From 8b1578eb7d8ad04506c2c2973a6d3e76fe279934 Mon Sep 17 00:00:00 2001 From: Luca Casonato Date: Sun, 5 Mar 2023 16:22:41 +0100 Subject: [PATCH 1/5] Support native system access from wasm This commit adds support for native system access for WASM. This is used through a new `wasmSystemAccess` option on the `esbuild.initialize` options bag. This options bag can contain either a specifier or namespace for a Node.js compatible `fs` and `process` module. During setup these are injected into `globalThis.fs` and `globalThis.process`, where Go's WASM runtime uses them to back the regular Go FS APIs. Finally, if this option is set, we set the `hasFS` option on `createChannel`, and everything just works :) Example for Deno: ```ts import * as esbuild from "./deno/wasm.js" await esbuild.initialize({ wasmSystemAccess: { fsSpecifier: "node:fs", processSpecifier: "node:process" } }) await esbuild.build({ format: "esm", entryPoints: ["./lib/deno/mod.ts"], target: "esnext", outfile: "./out.js" }) ``` --- lib/deno/wasm.ts | 47 ++++++++++++++++------- lib/npm/browser.ts | 45 ++++++++++++++++------ lib/shared/common.ts | 35 +++++++++++++++++ lib/shared/types.ts | 41 ++++++++++++++++++++ lib/shared/worker.ts | 87 ++++++++++++++++++++++++++++--------------- scripts/deno-tests.js | 70 +++++++++++++++++++++++++++++++++- scripts/esbuild.js | 2 +- 7 files changed, 269 insertions(+), 58 deletions(-) diff --git a/lib/deno/wasm.ts b/lib/deno/wasm.ts index 2c78593e4bb..9c768678a6d 100644 --- a/lib/deno/wasm.ts +++ b/lib/deno/wasm.ts @@ -60,7 +60,7 @@ let initializePromise: Promise | undefined let stopService: (() => void) | undefined let ensureServiceIsRunning = (): Promise => { - return initializePromise || startRunningService('esbuild.wasm', undefined, true) + return initializePromise || startRunningService('esbuild.wasm', undefined, true, undefined) } export const initialize: typeof types.initialize = async (options) => { @@ -68,8 +68,9 @@ export const initialize: typeof types.initialize = async (options) => { let wasmURL = options.wasmURL let wasmModule = options.wasmModule let useWorker = options.worker !== false + let wasmSystemAccess = options.wasmSystemAccess if (initializePromise) throw new Error('Cannot call "initialize" more than once') - initializePromise = startRunningService(wasmURL || 'esbuild.wasm', wasmModule, useWorker) + initializePromise = startRunningService(wasmURL || 'esbuild.wasm', wasmModule, useWorker, wasmSystemAccess) initializePromise.catch(() => { // Let the caller try again if this fails initializePromise = void 0 @@ -77,7 +78,10 @@ export const initialize: typeof types.initialize = async (options) => { await initializePromise } -const startRunningService = async (wasmURL: string | URL, wasmModule: WebAssembly.Module | undefined, useWorker: boolean): Promise => { +const startRunningService = async ( + wasmURL: string | URL, wasmModule: WebAssembly.Module | undefined, + useWorker: boolean, wasmSystemAccess: types.WasmSystemAccess | undefined, +): Promise => { let worker: { onmessage: ((event: any) => void) | null postMessage: (data: Uint8Array | ArrayBuffer | WebAssembly.Module) => void @@ -86,10 +90,27 @@ const startRunningService = async (wasmURL: string | URL, wasmModule: WebAssembl if (useWorker) { // Run esbuild off the main thread - let blob = new Blob([`onmessage=${WEB_WORKER_SOURCE_CODE}(postMessage)`], { type: 'text/javascript' }) + let script = `onmessage=${WEB_WORKER_SOURCE_CODE}(postMessage)`; + if (wasmSystemAccess?.fsSpecifier) { + script = `import fs from "${wasmSystemAccess.fsSpecifier}";globalThis.fs=fs;${script}` + } + if (wasmSystemAccess?.processSpecifier) { + script = `import process from "${wasmSystemAccess.processSpecifier}";globalThis.process=process;${script}` + } + let blob = new Blob([script], { type: 'text/javascript' }) worker = new Worker(URL.createObjectURL(blob), { type: 'module' }) } else { // Run esbuild on the main thread + if (wasmSystemAccess?.fsSpecifier) { + (globalThis as any).fs = await import(wasmSystemAccess.fsSpecifier) + } else if (wasmSystemAccess?.fsNamespace) { + (globalThis as any).fs = wasmSystemAccess.fsNamespace + } + if (wasmSystemAccess?.processSpecifier) { + (globalThis as any).process = await import(wasmSystemAccess.processSpecifier) + } else if (wasmSystemAccess?.processNamespace) { + (globalThis as any).process = wasmSystemAccess?.processNamespace + } let onmessage = WEB_WORKER_FUNCTION((data: Uint8Array) => worker.onmessage!({ data })) worker = { onmessage: null, @@ -99,18 +120,18 @@ const startRunningService = async (wasmURL: string | URL, wasmModule: WebAssembl } } - let firstMessageResolve: (value: void) => void + let firstMessageResolve: (value: string) => void let firstMessageReject: (error: any) => void - const firstMessagePromise = new Promise((resolve, reject) => { + const firstMessagePromise = new Promise((resolve, reject) => { firstMessageResolve = resolve firstMessageReject = reject }) - worker.onmessage = ({ data: error }) => { + worker.onmessage = ({ data }) => { worker.onmessage = ({ data }) => readFromStdout(data) - if (error) firstMessageReject(error) - else firstMessageResolve() + if (data.error) firstMessageReject(data.error) + else firstMessageResolve(data.ok) } worker.postMessage(wasmModule || new URL(wasmURL, import.meta.url).toString()) @@ -120,12 +141,12 @@ const startRunningService = async (wasmURL: string | URL, wasmModule: WebAssembl worker.postMessage(bytes) }, isSync: false, - hasFS: false, + hasFS: wasmSystemAccess !== undefined, esbuild: ourselves, }) // This will throw if WebAssembly module instantiation fails - await firstMessagePromise + const defaultWD = await firstMessagePromise stopService = () => { worker.terminate() @@ -141,7 +162,7 @@ const startRunningService = async (wasmURL: string | URL, wasmModule: WebAssembl refs: null, options, isTTY: false, - defaultWD: '/', + defaultWD, callback: (err, res) => err ? reject(err) : resolve(res as types.BuildResult), })), @@ -152,7 +173,7 @@ const startRunningService = async (wasmURL: string | URL, wasmModule: WebAssembl refs: null, options, isTTY: false, - defaultWD: '/', + defaultWD, callback: (err, res) => err ? reject(err) : resolve(res as types.BuildContext), })), diff --git a/lib/npm/browser.ts b/lib/npm/browser.ts index ef8580297f9..578a5120106 100644 --- a/lib/npm/browser.ts +++ b/lib/npm/browser.ts @@ -61,9 +61,10 @@ export const initialize: typeof types.initialize = options => { let wasmURL = options.wasmURL let wasmModule = options.wasmModule let useWorker = options.worker !== false + let wasmSystemAccess = options.wasmSystemAccess if (!wasmURL && !wasmModule) throw new Error('Must provide either the "wasmURL" option or the "wasmModule" option') if (initializePromise) throw new Error('Cannot call "initialize" more than once') - initializePromise = startRunningService(wasmURL || '', wasmModule, useWorker) + initializePromise = startRunningService(wasmURL || '', wasmModule, useWorker, wasmSystemAccess) initializePromise.catch(() => { // Let the caller try again if this fails initializePromise = void 0 @@ -71,7 +72,10 @@ export const initialize: typeof types.initialize = options => { return initializePromise } -const startRunningService = async (wasmURL: string | URL, wasmModule: WebAssembly.Module | undefined, useWorker: boolean): Promise => { +const startRunningService = async ( + wasmURL: string | URL, wasmModule: WebAssembly.Module | undefined, + useWorker: boolean, wasmSystemAccess: types.WasmSystemAccess | undefined, +): Promise => { let worker: { onmessage: ((event: any) => void) | null postMessage: (data: Uint8Array | ArrayBuffer | WebAssembly.Module) => void @@ -80,10 +84,27 @@ const startRunningService = async (wasmURL: string | URL, wasmModule: WebAssembl if (useWorker) { // Run esbuild off the main thread - let blob = new Blob([`onmessage=${WEB_WORKER_SOURCE_CODE}(postMessage)`], { type: 'text/javascript' }) + let script = `onmessage=${WEB_WORKER_SOURCE_CODE}(postMessage)`; + if (wasmSystemAccess?.fsSpecifier) { + script = `import fs from "${wasmSystemAccess.fsSpecifier}";globalThis.fs=fs;${script}` + } + if (wasmSystemAccess?.processSpecifier) { + script = `import process from "${wasmSystemAccess.processSpecifier}";globalThis.process=process;${script}` + } + let blob = new Blob([script], { type: 'text/javascript' }) worker = new Worker(URL.createObjectURL(blob)) } else { // Run esbuild on the main thread + if (wasmSystemAccess?.fsSpecifier) { + (globalThis as any).fs = await import(wasmSystemAccess.fsSpecifier) + } else if (wasmSystemAccess?.fsNamespace) { + (globalThis as any).fs = wasmSystemAccess.fsNamespace + } + if (wasmSystemAccess?.processSpecifier) { + (globalThis as any).process = await import(wasmSystemAccess.processSpecifier) + } else if (wasmSystemAccess?.processNamespace) { + (globalThis as any).process = wasmSystemAccess?.processNamespace + } let onmessage = WEB_WORKER_FUNCTION((data: Uint8Array) => worker.onmessage!({ data })) worker = { onmessage: null, @@ -93,18 +114,18 @@ const startRunningService = async (wasmURL: string | URL, wasmModule: WebAssembl } } - let firstMessageResolve: (value: void) => void + let firstMessageResolve: (value: string) => void let firstMessageReject: (error: any) => void - const firstMessagePromise = new Promise((resolve, reject) => { + const firstMessagePromise = new Promise((resolve, reject) => { firstMessageResolve = resolve firstMessageReject = reject }) - worker.onmessage = ({ data: error }) => { + worker.onmessage = ({ data }) => { worker.onmessage = ({ data }) => readFromStdout(data) - if (error) firstMessageReject(error) - else firstMessageResolve() + if (data.error) firstMessageReject(data.error) + else firstMessageResolve(data.ok) } worker.postMessage(wasmModule || new URL(wasmURL, location.href).toString()) @@ -114,12 +135,12 @@ const startRunningService = async (wasmURL: string | URL, wasmModule: WebAssembl worker.postMessage(bytes) }, isSync: false, - hasFS: false, + hasFS: wasmSystemAccess !== undefined, esbuild: ourselves, }) // This will throw if WebAssembly module instantiation fails - await firstMessagePromise + const defaultWD: string = await firstMessagePromise longLivedService = { build: (options: types.BuildOptions) => @@ -129,7 +150,7 @@ const startRunningService = async (wasmURL: string | URL, wasmModule: WebAssembl refs: null, options, isTTY: false, - defaultWD: '/', + defaultWD, callback: (err, res) => err ? reject(err) : resolve(res as types.BuildResult), })), @@ -140,7 +161,7 @@ const startRunningService = async (wasmURL: string | URL, wasmModule: WebAssembl refs: null, options, isTTY: false, - defaultWD: '/', + defaultWD, callback: (err, res) => err ? reject(err) : resolve(res as types.BuildContext), })), diff --git a/lib/shared/common.ts b/lib/shared/common.ts index dc98695e4f0..7623723121a 100644 --- a/lib/shared/common.ts +++ b/lib/shared/common.ts @@ -61,6 +61,9 @@ let mustBeStringOrUint8Array = (value: string | Uint8Array | undefined): string let mustBeStringOrURL = (value: string | URL | undefined): string | null => typeof value === 'string' || value instanceof URL ? null : 'a string or a URL' +let mustBeObjectOrBoolean = (value: Object | boolean | undefined): string | null => + typeof value === 'boolean' || (typeof value === 'object' && value !== null && !Array.isArray(value)) ? null : 'a boolean or an object' + type OptionKeys = { [key: string]: boolean } function getFlag(object: T, keys: OptionKeys, key: K, mustBeFn: (value: T[K]) => string | null): T[K] | undefined { @@ -85,11 +88,43 @@ export function validateInitializeOptions(options: types.InitializeOptions): typ let wasmURL = getFlag(options, keys, 'wasmURL', mustBeStringOrURL) let wasmModule = getFlag(options, keys, 'wasmModule', mustBeWebAssemblyModule) let worker = getFlag(options, keys, 'worker', mustBeBoolean) + let wasmSystemAccess = getFlag(options, keys, 'wasmSystemAccess', mustBeObjectOrBoolean) + if (typeof wasmSystemAccess === "object") { + let fsSpecifier = getFlag(wasmSystemAccess, keys, 'fsSpecifier', mustBeString) + let fsNamespace = getFlag(wasmSystemAccess, keys, 'fsNamespace', mustBeObject) + if (fsSpecifier && fsNamespace) { + throw new Error(`The "wasmSystemAccess.fsSpecifier option is mutually exclusive with the "wasmSystemAccess.fsNamespace option.`) + } + if (!fsNamespace && !fsSpecifier) { + throw new Error(`Must provide either of "fsNamespace" or "fsSpecifier" when the "wasmSystemAccess" option is specified.`) + } + let processSpecifier = getFlag(wasmSystemAccess, keys, 'processSpecifier', mustBeString) + let processNamespace = getFlag(wasmSystemAccess, keys, 'processNamespace', mustBeObject) + if (processSpecifier && processNamespace) { + throw new Error(`The "wasmSystemAccess.processSpecifier" option is mutually exclusive with the "wasmSystemAccess.processNamespace" option.`) + } + if (!processNamespace && !processSpecifier) { + throw new Error(`Must provide either of "processNamespace" or "processSpecifier" when the "wasmSystemAccess" option is specified.`) + } + if (fsNamespace && worker === true) { + throw new Error(`The "wasmSystemAccess.fsNamespace" option is not compatible with the "worker" option.`) + } + if (processNamespace && worker === true) { + throw new Error(`The "wasmSystemAccess.processNamespace" option is not compatible with the "worker" option.`) + } + wasmSystemAccess = { + fsSpecifier, + fsNamespace, + processSpecifier, + processNamespace + } + } checkForInvalidFlags(options, keys, 'in initialize() call') return { wasmURL, wasmModule, worker, + wasmSystemAccess, } } diff --git a/lib/shared/types.ts b/lib/shared/types.ts index 215e98d27d5..a917e8808ba 100644 --- a/lib/shared/types.ts +++ b/lib/shared/types.ts @@ -631,6 +631,18 @@ export interface InitializeOptions { */ wasmModule?: WebAssembly.Module + /** + * Enable system access (fs / process) when using WebAssembly. This requires + * that the host environment provide a namespace providing all APIs in + * Node.js' "fs" and "process" built-in modules. + * + * By default this feature is disabled. To enable it, implementations for the + * "fs" and "process" namespaces must be provided by specifying either a + * namespace containing for "fs" and "process", or specifiers to an ES module that exports these bindings + * as a default export. The former is unsupported when using "worker: true". + */ + wasmSystemAccess?: WasmSystemAccess + /** * By default esbuild runs the WebAssembly-based browser API in a web worker * to avoid blocking the UI thread. This can be disabled by setting "worker" @@ -639,4 +651,33 @@ export interface InitializeOptions { worker?: boolean } +export interface WasmSystemAccess { + /** + * A module specifier to an ES module exporting an object that has a "node:fs" + * compatible signature. + * + * Mutually exclusive with the "fsNamespace" option. + */ + fsSpecifier?: string, + /** + * An object with a "node:fs" compatible signature. + * + * Mutually exclusive with the "fsSpecifier" option. + */ + fsNamespace?: any + /** + * A module specifier to an ES module exporting an object that has a + * "node:process" compatible signature. + * + * Mutually exclusive with the "processNamespace" option. + */ + processSpecifier?: string + /** + * An object with a "node:process" compatible signature. + * + * Mutually exclusive with the "processSpecifier" option. + */ + processNamespace?: any +} + export let version: string diff --git a/lib/shared/worker.ts b/lib/shared/worker.ts index 9773e56f06c..c827c48336f 100644 --- a/lib/shared/worker.ts +++ b/lib/shared/worker.ts @@ -10,22 +10,29 @@ declare const ESBUILD_VERSION: string declare function postMessage(message: any): void onmessage = ({ data: wasm }: { data: WebAssembly.Module | string }) => { - let decoder = new TextDecoder() - let fs = (globalThis as any).fs + let originalFs = (globalThis as any).fs - let stderr = '' - fs.writeSync = (fd: number, buffer: Uint8Array) => { + const writeSync = (fd: number, buffer: Uint8Array) => { if (fd === 1) { postMessage(buffer) - } else if (fd === 2) { - stderr += decoder.decode(buffer) - let parts = stderr.split('\n') - if (parts.length > 1) console.log(parts.slice(0, -1).join('\n')) - stderr = parts[parts.length - 1] - } else { - throw new Error('Bad write') + return + } + return originalFs.writeSync(fd, buffer) + } + + const write = ( + fd: number, buffer: Uint8Array, offset: number, length: number, + position: null, callback: (err: Error | null, count?: number) => void + ) => { + if (fd === 1) { + if (offset !== 0 || length !== buffer.length || position !== null) { + throw new Error('Bad write') + } + postMessage(buffer) + callback(null, buffer.length) + return } - return buffer.length + return originalFs.write(fd, buffer, offset, length, position, callback) } let stdin: Uint8Array[] = [] @@ -39,28 +46,48 @@ onmessage = ({ data: wasm }: { data: WebAssembly.Module | string }) => { } } - fs.read = ( + const read = ( fd: number, buffer: Uint8Array, offset: number, length: number, position: null, callback: (err: Error | null, count?: number) => void, ) => { - if (fd !== 0 || offset !== 0 || length !== buffer.length || position !== null) { - throw new Error('Bad read') - } - - if (stdin.length === 0) { - resumeStdin = () => fs.read(fd, buffer, offset, length, position, callback) + if (fd === 0) { + if (offset !== 0 || length !== buffer.length || position !== null) { + throw new Error('Bad read') + } + + if (stdin.length === 0) { + resumeStdin = () => read(fd, buffer, offset, length, position, callback) + return + } + + let first = stdin[0] + let count = Math.max(0, Math.min(length, first.length - stdinPos)) + buffer.set(first.subarray(stdinPos, stdinPos + count), offset) + stdinPos += count + if (stdinPos === first.length) { + stdin.shift() + stdinPos = 0 + } + callback(null, count) return } - let first = stdin[0] - let count = Math.max(0, Math.min(length, first.length - stdinPos)) - buffer.set(first.subarray(stdinPos, stdinPos + count), offset) - stdinPos += count - if (stdinPos === first.length) { - stdin.shift() - stdinPos = 0 - } - callback(null, count) + return originalFs.read(fd, buffer, offset, length, position, callback) + } + + (globalThis as any).fs = { + ...originalFs, + writeSync, + write, + read, + } + + let process = (globalThis as any).process; + let defaultWD: string; + try { + defaultWD = process.cwd() + } catch { + defaultWD = "/" } let go: Go = new (globalThis as any).Go() @@ -69,11 +96,11 @@ onmessage = ({ data: wasm }: { data: WebAssembly.Module | string }) => { // Try to instantiate the module in the worker, then report back to the main thread tryToInstantiateModule(wasm, go).then( instance => { - postMessage(null) + postMessage({ ok: defaultWD }) go.run(instance) }, error => { - postMessage(error) + postMessage({ error }) }, ) } diff --git a/scripts/deno-tests.js b/scripts/deno-tests.js index fe08bdaaea3..c986f5bd1a9 100644 --- a/scripts/deno-tests.js +++ b/scripts/deno-tests.js @@ -73,6 +73,71 @@ function test(name, backends, fn) { } }) break + + case 'wasm-main-sys': + singleTest(name + '-wasm-main-sys', async () => { + let testDir = path.join(rootTestDir, name + '-wasm-main-sys') + await esbuildWASM.initialize({ + wasmModule, + worker: false, + wasmSystemAccess: { + fsSpecifier: "node:fs", + processSpecifier: "node:process", + } + }) + await Deno.mkdir(testDir, { recursive: true }) + try { + await fn({ esbuild: esbuildWASM, testDir }) + await Deno.remove(testDir, { recursive: true }).catch(() => null) + } finally { + esbuildWASM.stop() + } + }) + break + + + case 'wasm-main-sys-namespace': + singleTest(name + '-wasm-main-sys-namespace', async () => { + let testDir = path.join(rootTestDir, name + '-wasm-main-sys-namespace') + await esbuildWASM.initialize({ + wasmModule, + worker: false, + wasmSystemAccess: { + fsNamespace: await import("node:fs"), + processNamespace: await import("node:process"), + } + }) + await Deno.mkdir(testDir, { recursive: true }) + try { + await fn({ esbuild: esbuildWASM, testDir }) + await Deno.remove(testDir, { recursive: true }).catch(() => null) + } finally { + esbuildWASM.stop() + } + }) + break + + case 'wasm-worker-sys': + singleTest(name + '-wasm-worker-sys', async () => { + let testDir = path.join(rootTestDir, name + '-wasm-worker-sys') + await esbuildWASM.initialize({ + wasmModule, + worker: true, + wasmSystemAccess: { + fsSpecifier: "node:fs", + processSpecifier: "node:process", + } + }) + await Deno.mkdir(testDir, { recursive: true }) + try { + await fn({ esbuild: esbuildWASM, testDir }) + await Deno.remove(testDir, { recursive: true }).catch(() => null) + } finally { + esbuildWASM.stop() + } + }) + break + } } } @@ -86,7 +151,7 @@ window.addEventListener("unload", (e) => { }) // This test doesn't run in WebAssembly because it requires file system access -test("basicBuild", ['native'], async ({ esbuild, testDir }) => { +test("basicBuild", ['native', 'wasm-main-sys', 'wasm-main-sys-namespace', 'wasm-worker-sys'], async ({ esbuild, testDir }) => { const input = path.join(testDir, 'in.ts') const dep = path.join(testDir, 'dep.ts') const output = path.join(testDir, 'out.ts') @@ -96,13 +161,14 @@ test("basicBuild", ['native'], async ({ esbuild, testDir }) => { entryPoints: [input], bundle: true, outfile: output, + write: true, format: 'esm', }) const result = await import(path.toFileUrl(output)) asserts.assertStrictEquals(result.default, true) }) -test("basicContext", ['native'], async ({ esbuild, testDir }) => { +test("basicContext", ['native', 'wasm-main-sys', 'wasm-main-sys-namespace', 'wasm-worker-sys'], async ({ esbuild, testDir }) => { const input = path.join(testDir, 'in.ts') const dep = path.join(testDir, 'dep.ts') const output = path.join(testDir, 'out.ts') diff --git a/scripts/esbuild.js b/scripts/esbuild.js index 07b3514d74b..5f4348f3cf7 100644 --- a/scripts/esbuild.js +++ b/scripts/esbuild.js @@ -92,7 +92,7 @@ async function generateWorkerCode({ esbuildPath, wasm_exec_js, minify, target }) for (let o = self; o; o = Object.getPrototypeOf(o)) for (let k of Object.getOwnPropertyNames(o)) if (!(k in globalThis)) - Object.defineProperty(globalThis, k, { get: () => self[k] }) + Object.defineProperty(globalThis, k, { get: () => self[k], set: (value) => Object.defineProperty(globalThis, k, { value }), configurable: true }) ${wasm_exec_js.replace(/\bfs\./g, 'globalThis.fs.')} ${fs.readFileSync(path.join(repoDir, 'lib', 'shared', 'worker.ts'), 'utf8')} return m => onmessage(m) From f1ed573f2ff2df755570c5fc9aa5918b502267bd Mon Sep 17 00:00:00 2001 From: Luca Casonato Date: Sun, 5 Mar 2023 16:42:22 +0100 Subject: [PATCH 2/5] update deno --- .github/workflows/ci.yml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8bef2f401a6..97dfd38f1ae 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -58,12 +58,10 @@ jobs: with: node-version: 16 - # The version of Deno is pinned because version 1.25.1 was causing test - # flakes due to random segfaults. - - name: Setup Deno 1.24.0 + - name: Setup Deno 1.31.1 uses: denoland/setup-deno@main with: - deno-version: v1.24.0 + deno-version: v1.31.1 - name: Check out code into the Go module directory uses: actions/checkout@v3 From 6f47ff2d69de0702ae31fc6e35aa7625c1480884 Mon Sep 17 00:00:00 2001 From: Luca Casonato Date: Thu, 16 Mar 2023 15:15:55 +0100 Subject: [PATCH 3/5] deno canary --- .github/workflows/ci.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 97dfd38f1ae..e4c8d77e5c5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,12 +2,12 @@ name: CI on: push: - branches: ['*'] + branches: ["*"] pull_request: - branches: ['*'] + branches: ["*"] permissions: - contents: read # to fetch code (actions/checkout) + contents: read # to fetch code (actions/checkout) jobs: esbuild-slow: @@ -61,7 +61,7 @@ jobs: - name: Setup Deno 1.31.1 uses: denoland/setup-deno@main with: - deno-version: v1.31.1 + deno-version: canary - name: Check out code into the Go module directory uses: actions/checkout@v3 From 29691e9a84bfd541335d34288fba16fab5982a19 Mon Sep 17 00:00:00 2001 From: Luca Casonato Date: Thu, 16 Mar 2023 15:27:45 +0100 Subject: [PATCH 4/5] disable wasm-sys on windows --- .github/workflows/ci.yml | 4 ++-- scripts/deno-tests.js | 15 +++++++++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e4c8d77e5c5..f67252df747 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -58,10 +58,10 @@ jobs: with: node-version: 16 - - name: Setup Deno 1.31.1 + - name: Setup Deno 1.31.3 uses: denoland/setup-deno@main with: - deno-version: canary + deno-version: v1.31.3 - name: Check out code into the Go module directory uses: actions/checkout@v3 diff --git a/scripts/deno-tests.js b/scripts/deno-tests.js index c986f5bd1a9..d9874387950 100644 --- a/scripts/deno-tests.js +++ b/scripts/deno-tests.js @@ -75,6 +75,11 @@ function test(name, backends, fn) { break case 'wasm-main-sys': + // WASM with sys access not supported on Windows because Go assumes that + // inside of WASM all FS operations operate on Unix-style paths, but + // Windows uses backslashes. This is a limitation of Go's WASM support + // and unlikely to be fixable in esbuild right now. + if (Deno.build.os === "windows") break; singleTest(name + '-wasm-main-sys', async () => { let testDir = path.join(rootTestDir, name + '-wasm-main-sys') await esbuildWASM.initialize({ @@ -97,6 +102,11 @@ function test(name, backends, fn) { case 'wasm-main-sys-namespace': + // WASM with sys access not supported on Windows because Go assumes that + // inside of WASM all FS operations operate on Unix-style paths, but + // Windows uses backslashes. This is a limitation of Go's WASM support + // and unlikely to be fixable in esbuild right now. + if (Deno.build.os === "windows") break; singleTest(name + '-wasm-main-sys-namespace', async () => { let testDir = path.join(rootTestDir, name + '-wasm-main-sys-namespace') await esbuildWASM.initialize({ @@ -118,6 +128,11 @@ function test(name, backends, fn) { break case 'wasm-worker-sys': + // WASM with sys access not supported on Windows because Go assumes that + // inside of WASM all FS operations operate on Unix-style paths, but + // Windows uses backslashes. This is a limitation of Go's WASM support + // and unlikely to be fixable in esbuild right now. + if (Deno.build.os === "windows") break; singleTest(name + '-wasm-worker-sys', async () => { let testDir = path.join(rootTestDir, name + '-wasm-worker-sys') await esbuildWASM.initialize({ From c41dc56d73a41de19f89722709073950b06092c6 Mon Sep 17 00:00:00 2001 From: Luca Casonato Date: Thu, 16 Mar 2023 15:36:34 +0100 Subject: [PATCH 5/5] no windows support --- lib/shared/types.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/shared/types.ts b/lib/shared/types.ts index a917e8808ba..906e8af4230 100644 --- a/lib/shared/types.ts +++ b/lib/shared/types.ts @@ -640,6 +640,10 @@ export interface InitializeOptions { * "fs" and "process" namespaces must be provided by specifying either a * namespace containing for "fs" and "process", or specifiers to an ES module that exports these bindings * as a default export. The former is unsupported when using "worker: true". + * + * The passed "fs" namespace must operate on Unix-style paths. One can thus + * not use this feature to access the local file system on Windows when using + * Node.js' built-in "fs" module. */ wasmSystemAccess?: WasmSystemAccess