diff --git a/.gitignore b/.gitignore index 0df77e94361..4d1cdc65628 100644 --- a/.gitignore +++ b/.gitignore @@ -8,12 +8,14 @@ /github/ /npm/esbuild-android-64/esbuild.wasm /npm/esbuild-android-64/exit0.js +/npm/esbuild-android-64/wasm_exec_node.js /npm/esbuild-android-64/wasm_exec.js /npm/esbuild-wasm/browser.js /npm/esbuild-wasm/esbuild.wasm /npm/esbuild-wasm/esm/ /npm/esbuild-wasm/exit0.js /npm/esbuild-wasm/lib/ +/npm/esbuild-wasm/wasm_exec_node.js /npm/esbuild-wasm/wasm_exec.js /npm/esbuild/install.js /npm/esbuild/lib/ diff --git a/lib/npm/worker.ts b/lib/npm/worker.ts index 3772bc49a7b..cf0653dd0b8 100644 --- a/lib/npm/worker.ts +++ b/lib/npm/worker.ts @@ -5,7 +5,7 @@ declare function postMessage(message: any): void; onmessage = ({ data: wasm }) => { let decoder = new TextDecoder() - let fs = (global as any).fs + let fs = (globalThis as any).fs let stderr = '' fs.writeSync = (fd: number, buffer: Uint8Array) => { @@ -57,7 +57,7 @@ onmessage = ({ data: wasm }) => { callback(null, count) } - let go = new (global as any).Go() + let go = new (globalThis as any).Go() go.argv = ['', `--service=${ESBUILD_VERSION}`] WebAssembly.instantiate(wasm, go.importObject) diff --git a/npm/esbuild-wasm/bin/esbuild b/npm/esbuild-wasm/bin/esbuild index cc2529da53a..7587ceaea4e 100755 --- a/npm/esbuild-wasm/bin/esbuild +++ b/npm/esbuild-wasm/bin/esbuild @@ -2,17 +2,18 @@ // Forward to the automatically-generated WebAssembly loader from the Go compiler +const module_ = require('module'); const crypto = require('crypto'); const path = require('path'); const zlib = require('zlib'); const fs = require('fs'); const os = require('os'); -const wasm_exec = path.join(__dirname, '..', 'wasm_exec.js'); +const wasm_exec_node = path.join(__dirname, '..', 'wasm_exec_node.js'); const esbuild_wasm = path.join(__dirname, '..', 'esbuild.wasm'); -const code = fs.readFileSync(wasm_exec, 'utf8'); -const wrapper = new Function('require', 'module', 'process', 'WebAssembly', code); +const code = fs.readFileSync(wasm_exec_node, 'utf8'); +const wrapper = new Function('require', 'WebAssembly', code); function instantiate(bytes, importObject) { // Using this API causes "./esbuild --version" to run around 1 second faster @@ -80,6 +81,37 @@ fs.read = function () { return read.apply(this, arguments); }; +// Hack around a Unicode bug in node: https://github.com/nodejs/node/issues/24550. +// See this for the matching Go issue: https://github.com/golang/go/issues/43917. +const write = fs.write; +fs.write = function (fd, buf, offset, length, position, callback) { + if (offset === 0 && length === buf.length && position === null) { + if (fd === process.stdout.fd) { + try { + process.stdout.write(buf, err => err ? callback(err, 0, null) : callback(null, length, buf)); + } catch (err) { + callback(err, 0, null); + } + return; + } + if (fd === process.stderr.fd) { + try { + process.stderr.write(buf, err => err ? callback(err, 0, null) : callback(null, length, buf)); + } catch (err) { + callback(err, 0, null); + } + return; + } + } + return write.apply(this, arguments); +}; +const writeSync = fs.writeSync; +fs.writeSync = function (fd, buf) { + if (fd === process.stdout.fd) return process.stdout.write(buf), buf.length; + if (fd === process.stderr.fd) return process.stderr.write(buf), buf.length; + return writeSync.apply(this, arguments); +}; + // WASM code generated with Go 1.17.2+ will crash when run in a situation with // many environment variables: https://github.com/golang/go/issues/49011. An // example of this situation is running a Go-compiled WASM executable in GitHub @@ -97,5 +129,5 @@ for (let key in process.env) { } } -const argv = ['node', wasm_exec, esbuild_wasm].concat(process.argv.slice(2)); -wrapper(require, require.main, Object.assign(Object.create(process), { argv }), Object.assign(Object.create(WebAssembly), { instantiate })); +process.argv.splice(2, 0, esbuild_wasm); +wrapper(module_.createRequire(wasm_exec_node), Object.assign(Object.create(WebAssembly), { instantiate })); diff --git a/scripts/esbuild.js b/scripts/esbuild.js index e6287d194e3..3bee037e98f 100644 --- a/scripts/esbuild.js +++ b/scripts/esbuild.js @@ -106,41 +106,9 @@ exports.buildWasmLib = async (esbuildPath) => { // Generate "npm/esbuild-wasm/wasm_exec.js" const GOROOT = childProcess.execFileSync('go', ['env', 'GOROOT']).toString().trim(); let wasm_exec_js = fs.readFileSync(path.join(GOROOT, 'misc', 'wasm', 'wasm_exec.js'), 'utf8'); - const replace = (toReplace, replacement) => { - if (wasm_exec_js.indexOf(toReplace) === -1) throw new Error(`Failed to find ${JSON.stringify(toReplace)} in Go JS shim code`); - wasm_exec_js = wasm_exec_js.replace(toReplace, replacement); - } - replace('global.fs = fs;', ` - global.fs = Object.assign({}, fs, { - // Hack around a Unicode bug in node: https://github.com/nodejs/node/issues/24550 - write(fd, buf, offset, length, position, callback) { - if (offset === 0 && length === buf.length && position === null) { - if (fd === process.stdout.fd) { - try { - process.stdout.write(buf, err => err ? callback(err, 0, null) : callback(null, length, buf)); - } catch (err) { - callback(err, 0, null); - } - return; - } - if (fd === process.stderr.fd) { - try { - process.stderr.write(buf, err => err ? callback(err, 0, null) : callback(null, length, buf)); - } catch (err) { - callback(err, 0, null); - } - return; - } - } - fs.write(fd, buf, offset, length, position, callback); - }, - }); - `); - replace('// End of polyfills for common API.', ` - // Make sure Go sees the shadowed "fs" global - const { fs } = global; - `); + let wasm_exec_node_js = fs.readFileSync(path.join(GOROOT, 'misc', 'wasm', 'wasm_exec_node.js'), 'utf8'); fs.writeFileSync(path.join(npmWasmDir, 'wasm_exec.js'), wasm_exec_js); + fs.writeFileSync(path.join(npmWasmDir, 'wasm_exec_node.js'), wasm_exec_node_js); // Generate "npm/esbuild-wasm/lib/main.js" childProcess.execFileSync(esbuildPath, [ @@ -170,11 +138,11 @@ exports.buildWasmLib = async (esbuildPath) => { // Process "npm/esbuild-wasm/wasm_exec.js" and "lib/worker.ts" const input = ` let onmessage; - let global = {}; + let globalThis = {}; for (let o = self; o; o = Object.getPrototypeOf(o)) for (let k of Object.getOwnPropertyNames(o)) - if (!(k in global)) - Object.defineProperty(global, k, { get: () => self[k] }); + if (!(k in globalThis)) + Object.defineProperty(globalThis, k, { get: () => self[k] }); ${wasm_exec_js} ${fs.readFileSync(path.join(repoDir, 'lib', 'npm', 'worker.ts'), 'utf8')} return m => onmessage(m) @@ -248,6 +216,7 @@ module.exports = ${JSON.stringify(exit0Map, null, 2)}; for (const dir of npmWasmShimDirs) { fs.mkdirSync(path.join(dir, 'bin'), { recursive: true }) fs.writeFileSync(path.join(dir, 'wasm_exec.js'), wasm_exec_js); + fs.writeFileSync(path.join(dir, 'wasm_exec_node.js'), wasm_exec_node_js); fs.writeFileSync(path.join(dir, 'exit0.js'), exit0Code); fs.copyFileSync(path.join(npmWasmDir, 'bin', 'esbuild'), path.join(dir, 'bin', 'esbuild')); fs.copyFileSync(path.join(npmWasmDir, 'esbuild.wasm'), path.join(dir, 'esbuild.wasm'));