From 9f78e8c8920f47078bf9417e6f6f6bc45ddc9dd3 Mon Sep 17 00:00:00 2001 From: irl231 <91590512+lazuee@users.noreply.github.com> Date: Thu, 19 Feb 2026 23:38:14 +0800 Subject: [PATCH 1/4] fix(node-protocol): respect alias and tsconfig paths when stripping node: prefix --- src/features/node-protocol.ts | 14 ++++++++++++-- tests/__snapshots__/issues/772.snap.md | 22 ++++++++++++++++++++++ tests/issues.test.ts | 26 ++++++++++++++++++++++++++ 3 files changed, 60 insertions(+), 2 deletions(-) create mode 100644 tests/__snapshots__/issues/772.snap.md diff --git a/src/features/node-protocol.ts b/src/features/node-protocol.ts index da44a798c..5e3bb34aa 100644 --- a/src/features/node-protocol.ts +++ b/src/features/node-protocol.ts @@ -22,10 +22,20 @@ export function NodeProtocolPlugin(nodeProtocolOption: 'strip' | true): Plugin { }, handler: nodeProtocolOption === 'strip' - ? (id) => { + ? async function (id, importer, extraOptions) { + const strippedId = id.slice(5) + // check if another resolver (e.g., tsconfig paths, alias) handles the stripped id + const resolved = await this.resolve( + strippedId, + importer, + extraOptions, + ) + if (resolved && !resolved.external) { + return resolved + } return { // strip the `node:` prefix - id: id.slice(5), + id: strippedId, external: true, moduleSideEffects: false, } diff --git a/tests/__snapshots__/issues/772.snap.md b/tests/__snapshots__/issues/772.snap.md new file mode 100644 index 000000000..7b9f4c3b8 --- /dev/null +++ b/tests/__snapshots__/issues/772.snap.md @@ -0,0 +1,22 @@ +## index.mjs + +```mjs +//#region crypto-polyfill.ts +function randomUUID() { + return "polyfill-uuid"; +} + +//#endregion +//#region path-polyfill.ts +function resolve(...args) { + return args.join("/"); +} + +//#endregion +//#region index.ts +const id = randomUUID(); +const dir = resolve("."); + +//#endregion +export { dir, id }; +``` diff --git a/tests/issues.test.ts b/tests/issues.test.ts index 8c1f65fe2..3a8dab4cb 100644 --- a/tests/issues.test.ts +++ b/tests/issues.test.ts @@ -174,6 +174,32 @@ describe('issues', () => { }) }) + test('#772', async (context) => { + const { fileMap, outputFiles } = await testBuild({ + context, + files: { + 'index.ts': `import { randomUUID } from 'node:crypto'\nimport { resolve } from 'node:path'\nexport const id = randomUUID()\nexport const dir = resolve('.')`, + 'crypto-polyfill.ts': `export function randomUUID() { return 'polyfill-uuid' }`, + 'path-polyfill.ts': `export function resolve(...args: string[]) { return args.join('/') }`, + 'tsconfig.json': JSON.stringify({ + compilerOptions: { + paths: { crypto: ['./crypto-polyfill'] }, + }, + }), + }, + options: { + nodeProtocol: 'strip', + alias: { path: './path-polyfill' }, + tsconfig: 'tsconfig.json', + }, + }) + expect(outputFiles).toContain('index.mjs') + expect(fileMap['index.mjs']).toContain('args.join') + expect(fileMap['index.mjs']).not.toContain('require("path")') + expect(fileMap['index.mjs']).toContain('polyfill-uuid') + expect(fileMap['index.mjs']).not.toContain('require("crypto")') + }) + test.fails('#668', async (context) => { const { outputFiles, fileMap } = await testBuild({ context, From f71579447293b4344076fed110aeb94a4a5a59da Mon Sep 17 00:00:00 2001 From: Kevin Deng Date: Fri, 20 Feb 2026 00:00:20 +0800 Subject: [PATCH 2/4] refactor --- src/features/node-protocol.ts | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/features/node-protocol.ts b/src/features/node-protocol.ts index 5e3bb34aa..47ccca5c8 100644 --- a/src/features/node-protocol.ts +++ b/src/features/node-protocol.ts @@ -22,19 +22,17 @@ export function NodeProtocolPlugin(nodeProtocolOption: 'strip' | true): Plugin { }, handler: nodeProtocolOption === 'strip' - ? async function (id, importer, extraOptions) { - const strippedId = id.slice(5) + ? async function (id, ...args) { + // strip the `node:` prefix + const strippedId = id.slice(5 /* "node:".length */) + // check if another resolver (e.g., tsconfig paths, alias) handles the stripped id - const resolved = await this.resolve( - strippedId, - importer, - extraOptions, - ) + const resolved = await this.resolve(strippedId, ...args) if (resolved && !resolved.external) { return resolved } + return { - // strip the `node:` prefix id: strippedId, external: true, moduleSideEffects: false, From 769cedcf521e645f6d88ab965c39445f78d38a0d Mon Sep 17 00:00:00 2001 From: irl231 <91590512+lazuee@users.noreply.github.com> Date: Fri, 20 Feb 2026 00:03:38 +0800 Subject: [PATCH 3/4] fix(tests): update assertions to use regex for path and crypto imports --- tests/issues.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/issues.test.ts b/tests/issues.test.ts index 3a8dab4cb..ac83fa768 100644 --- a/tests/issues.test.ts +++ b/tests/issues.test.ts @@ -195,9 +195,9 @@ describe('issues', () => { }) expect(outputFiles).toContain('index.mjs') expect(fileMap['index.mjs']).toContain('args.join') - expect(fileMap['index.mjs']).not.toContain('require("path")') + expect(fileMap['index.mjs']).not.toMatch(/from ['"]path['"]/) expect(fileMap['index.mjs']).toContain('polyfill-uuid') - expect(fileMap['index.mjs']).not.toContain('require("crypto")') + expect(fileMap['index.mjs']).not.toMatch(/from ['"]crypto['"]/) }) test.fails('#668', async (context) => { From c8aa040aa936677a7288651215691550f2b256c0 Mon Sep 17 00:00:00 2001 From: John Marlo Lapiz <91590512+lazuee@users.noreply.github.com> Date: Sun, 22 Feb 2026 01:05:27 +0800 Subject: [PATCH 4/4] refactor: reorder test cases --- tests/issues.test.ts | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/tests/issues.test.ts b/tests/issues.test.ts index ac83fa768..c8175fc76 100644 --- a/tests/issues.test.ts +++ b/tests/issues.test.ts @@ -174,6 +174,26 @@ describe('issues', () => { }) }) + test.fails('#668', async (context) => { + const { outputFiles, fileMap } = await testBuild({ + context, + files: { + 'shared.css': `.class-shared { color: red; }`, + 'entry1.css': `@import './shared.css'; .class-entry1 { color: red; }`, + 'entry2.css': `@import './shared.css'; .class-entry2 { color: red; }`, + }, + options: { + entry: ['entry1.css', 'entry2.css'], + }, + }) + expect(outputFiles).toContain('entry1.css') + expect(outputFiles).toContain('entry2.css') + expect(fileMap['entry1.css']).toContain('class-entry1') + expect(fileMap['entry2.css']).toContain('class-entry2') + expect(fileMap['entry1.css']).toContain('class-shared') + expect(fileMap['entry2.css']).toContain('class-shared') + }) + test('#772', async (context) => { const { fileMap, outputFiles } = await testBuild({ context, @@ -199,24 +219,4 @@ describe('issues', () => { expect(fileMap['index.mjs']).toContain('polyfill-uuid') expect(fileMap['index.mjs']).not.toMatch(/from ['"]crypto['"]/) }) - - test.fails('#668', async (context) => { - const { outputFiles, fileMap } = await testBuild({ - context, - files: { - 'shared.css': `.class-shared { color: red; }`, - 'entry1.css': `@import './shared.css'; .class-entry1 { color: red; }`, - 'entry2.css': `@import './shared.css'; .class-entry2 { color: red; }`, - }, - options: { - entry: ['entry1.css', 'entry2.css'], - }, - }) - expect(outputFiles).toContain('entry1.css') - expect(outputFiles).toContain('entry2.css') - expect(fileMap['entry1.css']).toContain('class-entry1') - expect(fileMap['entry2.css']).toContain('class-entry2') - expect(fileMap['entry1.css']).toContain('class-shared') - expect(fileMap['entry2.css']).toContain('class-shared') - }) })