fix(scripts): make compatible with all Node runtimes
This commit is contained in:
parent
ee8d5620a0
commit
f67c8db768
5 changed files with 105 additions and 64 deletions
10
scripts/_shared.ts
Normal file
10
scripts/_shared.ts
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
import { stat } from 'fs/promises'
|
||||
|
||||
export const exists = async (path: string) => {
|
||||
try {
|
||||
await stat(path)
|
||||
return true
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
|
@ -1,19 +1,23 @@
|
|||
import { $, main } from 'bun'
|
||||
import chalk from 'chalk'
|
||||
import { exists, mkdir, readdir, rm, writeFile } from 'fs/promises'
|
||||
import { parse } from 'path'
|
||||
import { execSync } from 'child_process'
|
||||
import { mkdir, readdir, rm, writeFile } from 'fs/promises'
|
||||
import { dirname, parse } from 'path'
|
||||
import { rolldown } from 'rolldown'
|
||||
import { importGlobPlugin } from 'rolldown/experimental'
|
||||
import pkg from '../package.json'
|
||||
import { fileURLToPath } from 'url'
|
||||
import pkg from '../package.json' with { type: 'json' }
|
||||
import { exists } from './_shared'
|
||||
import asRequire from './plugins/as-require'
|
||||
import hermesSwcPlugin from './plugins/hermes-swc'
|
||||
import hermesCPlugin from './plugins/hermesc'
|
||||
import importDefer from './plugins/import-defer'
|
||||
import shimAliases from './plugins/shim-aliases'
|
||||
|
||||
const ShimsDir = `${import.meta.dir}/../shims`
|
||||
const AssetsDir = `${import.meta.dir}/../src/assets`
|
||||
const GeneratedAssetsDir = `${import.meta.dir}/../dist/assets/generated`
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url))
|
||||
|
||||
const ShimsDir = `${__dirname}/../shims`
|
||||
const AssetsDir = `${__dirname}/../src/assets`
|
||||
const GeneratedAssetsDir = `${__dirname}/../dist/assets/generated`
|
||||
|
||||
await rm(GeneratedAssetsDir, { recursive: true, force: true })
|
||||
.then(() =>
|
||||
|
|
@ -25,7 +29,7 @@ const Dev =
|
|||
process.argv.includes('--dev') || process.env.NODE_ENV === 'development'
|
||||
|
||||
// If this file is being run directly, build the project
|
||||
if (main === import.meta.filename) build()
|
||||
if (import.meta.main) build()
|
||||
|
||||
export default async function build(dev = Dev, log = true) {
|
||||
const start = performance.now()
|
||||
|
|
@ -35,7 +39,10 @@ export default async function build(dev = Dev, log = true) {
|
|||
if (log) console.info(chalk.cyanBright('\u{1F5BB} Assets generated'))
|
||||
if (log) console.info(chalk.gray('\u{1F5CE} Compiling JS...'))
|
||||
|
||||
const COMMIT = (await $`git rev-parse HEAD`.text()).trim().substring(0, 7)
|
||||
const COMMIT = execSync('git rev-parse HEAD')
|
||||
.toString()
|
||||
.trim()
|
||||
.substring(0, 7)
|
||||
const REPO = 'revenge-mod/revenge-bundle-next'
|
||||
|
||||
const bundle = await rolldown({
|
||||
|
|
@ -67,7 +74,9 @@ export default async function build(dev = Dev, log = true) {
|
|||
__BUILD_VERSION__: JSON.stringify(pkg.version),
|
||||
__BUILD_COMMIT__: JSON.stringify(COMMIT),
|
||||
__BUILD_BRANCH__: JSON.stringify(
|
||||
(await $`git rev-parse --abbrev-ref HEAD`.text()).trim(),
|
||||
execSync('git rev-parse --abbrev-ref HEAD')
|
||||
.toString()
|
||||
.trim(),
|
||||
),
|
||||
__DEV__: String(dev),
|
||||
|
||||
|
|
@ -79,6 +88,11 @@ export default async function build(dev = Dev, log = true) {
|
|||
},
|
||||
},
|
||||
tsconfig: 'tsconfig.json',
|
||||
// propertyReadSideEffects: false works around a Rolldown bug where
|
||||
// property reads on tree-shaken import bindings are kept as "side
|
||||
// effects" while their imports are removed, producing free-variable
|
||||
// references (e.g. `FibonacciHeap.MinFibonacciHeap` from mnemonist's
|
||||
// barrel) that throw ReferenceError at runtime.
|
||||
treeshake: true,
|
||||
moduleTypes: {
|
||||
'.webp': 'dataurl',
|
||||
|
|
|
|||
|
|
@ -1,14 +1,20 @@
|
|||
import watcher from '@parcel/watcher'
|
||||
import { debounce } from '@revenge-mod/utils/callback'
|
||||
import chalk from 'chalk'
|
||||
import { readFile } from 'fs/promises'
|
||||
import { createServer } from 'http'
|
||||
import os from 'os'
|
||||
import { join } from 'path'
|
||||
import pkg from '../package.json'
|
||||
import { crc32 } from 'zlib'
|
||||
import pkg from '../package.json' with { type: 'json' }
|
||||
import build from './build'
|
||||
|
||||
const prod = process.argv.includes('--prod')
|
||||
const lanHost = process.argv.includes('--lan')
|
||||
|
||||
const Port = 4040
|
||||
const BundlePath = './dist/revenge.bundle'
|
||||
|
||||
console.info(chalk.redBright(`\nRevenge ${chalk.white(`v${pkg.version}`)}\n`))
|
||||
|
||||
const cwdify = (path: string) => join(process.cwd(), path)
|
||||
|
|
@ -43,51 +49,46 @@ watcher.subscribe(process.cwd(), (err, events) => {
|
|||
)
|
||||
})
|
||||
|
||||
const server = Bun.serve({
|
||||
hostname: lanHost ? '0.0.0.0' : '127.0.0.1',
|
||||
async fetch(req, srv) {
|
||||
try {
|
||||
if (needRebuild) await debouncedBuild()
|
||||
const server = createServer(async (req, res) => {
|
||||
try {
|
||||
if (needRebuild) await debouncedBuild()
|
||||
console.debug(
|
||||
chalk.gray(
|
||||
`\u{1F79B} Receiving request from ${req.socket.remoteAddress}`,
|
||||
),
|
||||
)
|
||||
|
||||
const bundle = await readFile(BundlePath).catch(() => null)
|
||||
if (!bundle)
|
||||
throw new Error('Could not serve the bundle! No file found.')
|
||||
|
||||
const hash = crc32(bundle).toString(16)
|
||||
|
||||
if (req.headers['if-none-match'] === hash) {
|
||||
console.debug(
|
||||
chalk.gray(
|
||||
`\u{1F79B} Receiving request from ${srv.requestIP(req)!.address}`,
|
||||
),
|
||||
chalk.gray('\u{1F4BE} ETag matched, responding with 304'),
|
||||
)
|
||||
|
||||
const file = Bun.file('./dist/revenge.bundle')
|
||||
|
||||
if (await file.exists()) {
|
||||
const hash = Bun.hash
|
||||
.crc32(await file.arrayBuffer())
|
||||
.toString(16)
|
||||
|
||||
if (req.headers.get('If-None-Match') === hash) {
|
||||
console.debug(
|
||||
chalk.gray(
|
||||
'\u{1F4BE} ETag matched, responding with 304',
|
||||
),
|
||||
)
|
||||
|
||||
return new Response(null, {
|
||||
status: 304,
|
||||
})
|
||||
}
|
||||
|
||||
return new Response(file, {
|
||||
status: 200,
|
||||
headers: {
|
||||
ETag: hash,
|
||||
},
|
||||
})
|
||||
} else throw new Error('Could not serve the bundle! No file found.')
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
throw new Error('Build failed. Check console for details.')
|
||||
res.writeHead(304)
|
||||
res.end()
|
||||
return
|
||||
}
|
||||
},
|
||||
port: 4040,
|
||||
|
||||
res.writeHead(200, {
|
||||
ETag: hash,
|
||||
'Content-Length': bundle.byteLength,
|
||||
})
|
||||
res.end(bundle)
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
|
||||
res.writeHead(500)
|
||||
res.end('Build failed. Check console for details.')
|
||||
}
|
||||
})
|
||||
|
||||
server.listen(Port, lanHost ? '0.0.0.0' : '127.0.0.1')
|
||||
|
||||
if (lanHost) console.info(chalk.gray('\u24D8 Listening on all interfaces...'))
|
||||
else
|
||||
console.info(
|
||||
|
|
@ -99,12 +100,12 @@ else
|
|||
if (!prod) console.info(chalk.gray('\u24D8 Use --prod to build for production'))
|
||||
else console.info(chalk.gray('\u24D8 Building for production...'))
|
||||
|
||||
console.info(chalk.cyanBright(`\u24D8 Serving on port ${server.port}`))
|
||||
console.info(chalk.cyanBright(`\u24D8 Serving on port ${Port}`))
|
||||
console.info(chalk.gray('\u24D8 Accessible on:'))
|
||||
|
||||
for (const int of Object.values(os.networkInterfaces()))
|
||||
if (int)
|
||||
for (const det of int) {
|
||||
if (det.family !== 'IPv4' || (!det.internal && !lanHost)) continue
|
||||
console.info(chalk.gray(`- http://${det.address}:${server.port}`))
|
||||
console.info(chalk.gray(`- http://${det.address}:${Port}`))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,8 +8,10 @@ import type { RolldownPlugin } from 'rolldown'
|
|||
* - Dynamic side-effect imports: `import('module')` and `await import('module')`
|
||||
*/
|
||||
export default function asRequire() {
|
||||
const name = 'as-require'
|
||||
|
||||
return {
|
||||
name: 'as-require',
|
||||
name,
|
||||
transform(code, id) {
|
||||
if (!code.includes('// @as-require')) return null
|
||||
|
||||
|
|
@ -45,7 +47,7 @@ export default function asRequire() {
|
|||
}
|
||||
|
||||
this.warn(
|
||||
`[${this.pluginName}] Warning: Could not transform statement in ${id}:\n${statement}\n` +
|
||||
`[${name}] Warning: Could not transform statement in ${id}:\n${statement}\n` +
|
||||
`The line after the comment must be a side-effect import or a dynamic import with .then().`,
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
import { spawnSync } from 'child_process'
|
||||
import { existsSync, readFileSync } from 'fs'
|
||||
import type { OutputChunk, RolldownPlugin } from 'rolldown'
|
||||
|
||||
|
|
@ -37,27 +38,40 @@ export default function hermesCPlugin({
|
|||
const file = bundle['revenge.js'] as OutputChunk
|
||||
if (!file) throw new Error('No code to compile')
|
||||
|
||||
const cmdlist = [binPath, '-emit-binary', ...(flags ?? [])]
|
||||
const argList = ['-emit-binary', ...(flags ?? [])]
|
||||
|
||||
const cmd = Bun.spawnSync<'pipe', 'pipe'>(cmdlist, {
|
||||
// @ts-expect-error: Types are incorrect, but this works
|
||||
stdin: new Blob([file.code]),
|
||||
stdout: 'pipe',
|
||||
})
|
||||
const cmd =
|
||||
// TODO: Move away from Bun: https://github.com/oven-sh/bun/issues/25498
|
||||
typeof Bun !== 'undefined'
|
||||
? (() => {
|
||||
const cmd = Bun.spawnSync([binPath, ...argList], {
|
||||
// @ts-expect-error: Works but types are incorrect
|
||||
stdio: [new Blob([file.code]), 'pipe'],
|
||||
})
|
||||
|
||||
if (cmd.exitCode) {
|
||||
return {
|
||||
status: cmd.exitCode,
|
||||
stdout: cmd.stdout,
|
||||
stderr: cmd.stderr,
|
||||
}
|
||||
})()
|
||||
: spawnSync(binPath, argList, {
|
||||
stdio: ['pipe', 'pipe'],
|
||||
input: file.code,
|
||||
})
|
||||
|
||||
if (cmd.status !== 0) {
|
||||
if (cmd.stderr.length)
|
||||
throw new Error(
|
||||
`Got error from hermesc: ${cmd.stderr.toString()}`,
|
||||
)
|
||||
else
|
||||
throw new Error(`hermesc exited with code: ${cmd.exitCode}`)
|
||||
else throw new Error(`hermesc exited with code: ${cmd.status}`)
|
||||
}
|
||||
|
||||
const buf = cmd.stdout
|
||||
if (!buf.length)
|
||||
throw new Error(
|
||||
`No output from hermesc. Probably a compilation error.\nTry running the command manually: ${cmdlist.join(' ')}`,
|
||||
`No output from hermesc. Probably a compilation error.\nTry running the command manually: ${binPath} ${argList.join(' ')}`,
|
||||
)
|
||||
|
||||
this.emitFile({
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue