From be852d4e9bd2adb4f1d317bc1f488533e1a5a62a Mon Sep 17 00:00:00 2001 From: imccyu <276526105+imccyu@users.noreply.github.com> Date: Mon, 24 Aug 2026 01:01:19 +0800 Subject: [PATCH] fix(webworker): scope Linux-only CI checks --- .../node/builtin_modules/implemented/fs.ts | 57 ++++++++++++------- .../tests/node/fs-watch-stream.spec.ts | 2 +- vitest.config.ts | 17 +++++- 3 files changed, 53 insertions(+), 23 deletions(-) diff --git a/packages/experimental/webworker-runtime/src/node/builtin_modules/implemented/fs.ts b/packages/experimental/webworker-runtime/src/node/builtin_modules/implemented/fs.ts index 4e83d52752..e4d7099f3e 100644 --- a/packages/experimental/webworker-runtime/src/node/builtin_modules/implemented/fs.ts +++ b/packages/experimental/webworker-runtime/src/node/builtin_modules/implemented/fs.ts @@ -481,6 +481,40 @@ export interface WriteStreamOptions { /** Node implements file-stream `autoClose` through the stream's `autoDestroy` state. */ const streamAutoDestroy = (autoClose: boolean | undefined): boolean => autoClose ?? true +interface FileStreamState { + fd: number | null + pending: boolean +} + +/** Release the descriptor and abort listener shared by both file-stream directions. */ +function destroyFileStream( + stream: FileStreamState, + signal: AbortSignal | undefined, + onAbort: (() => void) | undefined, + error: Error | null, + callback: (error: Error | null) => void, +): void { + signal?.removeEventListener('abort', onAbort as () => void) + if (stream.fd !== null) closeSync(stream.fd) + stream.fd = null + stream.pending = false + callback(error) +} + +interface ClosableFileStream { + once(event: string, listener: () => void): unknown + destroy(): unknown +} + +/** Register an optional completion callback and explicitly destroy a file stream. */ +function closeFileStream( + stream: ClosableFileStream, + callback?: (error?: NodeJS.ErrnoException | null) => void, +): void { + if (callback !== undefined) stream.once('close', () => { callback(null) }) + stream.destroy() +} + /** Read stream over one VFS file. */ export class ReadStream extends Readable { /** Resolved path opened by this stream. */ @@ -560,11 +594,7 @@ export class ReadStream extends Readable { } override _destroy(error: Error | null, callback: (error?: Error | null) => void): void { - this.signal?.removeEventListener('abort', this.onAbort as () => void) - if (this.fd !== null) closeSync(this.fd) - this.fd = null - this.pending = false - callback(error) + destroyFileStream(this, this.signal, this.onAbort, error, callback) } /** @@ -572,8 +602,7 @@ export class ReadStream extends Readable { * @param callback - Optional completion callback after `close`. */ close(callback?: (error?: NodeJS.ErrnoException | null) => void): void { - if (callback !== undefined) this.once('close', () => { callback(null) }) - this.destroy() + closeFileStream(this, callback) } } @@ -647,11 +676,7 @@ export class WriteStream extends Writable { } override _destroy(error: Error | null, callback: (error: Error | null) => void): void { - this.signal?.removeEventListener('abort', this.onAbort as () => void) - closeDescriptor(this.fd) - this.fd = null - this.pending = false - callback(error) + destroyFileStream(this, this.signal, this.onAbort, error, callback) } /** @@ -659,16 +684,10 @@ export class WriteStream extends Writable { * @param callback - Optional completion callback after `close`. */ close(callback?: (error?: NodeJS.ErrnoException | null) => void): void { - if (callback !== undefined) this.once('close', () => { callback(null) }) - this.destroy() + closeFileStream(this, callback) } } -/** Close a stream-owned descriptor when it has opened successfully. */ -function closeDescriptor(fd: number | null): void { - if (fd !== null) closeSync(fd) -} - /** * Create a Node-compatible readable file stream over the VFS. * @param path - File path. diff --git a/packages/experimental/webworker-runtime/tests/node/fs-watch-stream.spec.ts b/packages/experimental/webworker-runtime/tests/node/fs-watch-stream.spec.ts index c5759dd4d7..df8c31e0b5 100644 --- a/packages/experimental/webworker-runtime/tests/node/fs-watch-stream.spec.ts +++ b/packages/experimental/webworker-runtime/tests/node/fs-watch-stream.spec.ts @@ -279,7 +279,7 @@ describe('file streams', () => { expect(workerStream.default._isArrayBufferView(new Uint8Array())).toBe(true) }) - it('matches Node file-stream defaults and abort error identity', async () => { + it('uses Node 22 Linux file-stream defaults and abort error identity', async () => { const nativeRoot = mkdtempSync(join(tmpdir(), 'dsh-stream-diff-')) nativeRoots.push(nativeRoot) const nativePath = join(nativeRoot, 'input.txt') diff --git a/vitest.config.ts b/vitest.config.ts index f24c8c552e..6e870fcbe4 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -57,6 +57,17 @@ const windowsUnsupportedTests = process.platform === 'win32' ] : [] +// These suites compare against or assemble the Worker's fixed Linux platform. +// Host-native Windows and macOS behavior is not their oracle. +const nonLinuxWebWorkerTests = process.platform === 'linux' + ? [] + : [ + 'packages/experimental/webworker-runtime/tests/node/fs-watch-stream.spec.ts', + 'packages/experimental/webworker-runtime/tests/node/sandbox-stack.spec.ts', + ] + +const platformUnsupportedTests = [...windowsUnsupportedTests, ...nonLinuxWebWorkerTests] + const windowsUnsupportedCoveragePackages = process.platform === 'win32' ? [...windowsUnsupportedPackages, 'packages/subprocess/*'] : [] @@ -142,7 +153,7 @@ export default defineConfig({ setupFiles: ['./scripts/test-invariants.ts'], // .tsx: client component specs (jsdom via per-file @vitest-environment pragma). include: testIncludes, - exclude: windowsUnsupportedTests, + exclude: platformUnsupportedTests, // One coverage invocation aggregates both projects. Every suite forks for // Node stability; process-bound suites stay separate for inventory control. projects: [ @@ -158,7 +169,7 @@ export default defineConfig({ setupFiles: ['./scripts/test-invariants.ts'], include: testIncludes, exclude: [ - ...windowsUnsupportedTests, + ...platformUnsupportedTests, ...processBoundTests, ...coverageExemptExcludes, ], @@ -173,7 +184,7 @@ export default defineConfig({ setupFiles: ['./scripts/test-invariants.ts'], include: processBoundTests, exclude: [ - ...windowsUnsupportedTests, + ...platformUnsupportedTests, ...coverageExemptExcludes, ], },