Skip to content

Commit 2bd2792

Browse files
authored
fix(test): use platform neutral for vendor leaf dep bundling to fix browser tests (#635)
The leaf dependency bundling step used `platform: 'node'`, which caused rolldown to generate a shared runtime chunk with `createRequire` from `node:module`. Since vendor files (pathe, chai, tinyrainbow, etc.) are loaded by @vitest/runner and @vitest/expect in the browser during vitest browser mode, the browser hit the externalized `node:module` and threw: Module "node:module" has been externalized for browser compatibility. Cannot access "node:module.createRequire" in client code. This caused browser tests to hang indefinitely. Changes: - Switch `platform: 'node'` to `platform: 'neutral'` so rolldown emits pure JS CJS interop helpers without Node.js built-in imports - Add `mainFields: ['module', 'main']` so CJS-only packages like expect-type can still be resolved under neutral platform - Fix `fixCjsNamedExports()` to handle the case where rolldown emits `export default require_xxx()` without an accompanying `export {}` block (the previous regex required both patterns to match) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Medium Risk** > Changes the bundling configuration and post-processing of generated vendor code; risk is moderate because it can affect module resolution and runtime behavior across both Node and browser test environments. > > **Overview** > Fixes vitest browser-mode hangs by changing the leaf-dependency bundling step to emit browser-safe output. > > `bundleLeafDeps()` now builds with `platform: 'neutral'` (avoiding Node-only helpers like `node:module.createRequire`) and adds `resolve.mainFields: ['module','main']` so CJS-only packages still resolve correctly. The `fixCjsNamedExports()` post-processing was broadened to also patch outputs that contain `export default require_xxx()` without an `export {}` block, ensuring named exports like `expectTypeOf` are consistently re-exported. > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit ace4c36. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY -->
1 parent 5844065 commit 2bd2792

1 file changed

Lines changed: 17 additions & 8 deletions

File tree

packages/test/build.ts

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -720,7 +720,7 @@ async function bundleLeafDeps(leafDeps: Set<string>): Promise<Map<string, string
720720
entryFileNames: '[name].mjs',
721721
chunkFileNames: 'shared-[hash].mjs',
722722
},
723-
platform: 'node',
723+
platform: 'neutral',
724724
treeshake: false,
725725
external: [
726726
// Keep node built-ins external
@@ -736,6 +736,7 @@ async function bundleLeafDeps(leafDeps: Set<string>): Promise<Map<string, string
736736
],
737737
resolve: {
738738
conditionNames: ['node', 'import', 'default'],
739+
mainFields: ['module', 'main'],
739740
},
740741
logLevel: 'warn',
741742
});
@@ -1150,17 +1151,25 @@ async function fixCjsNamedExports(vendorFilePath: string, specifier: string) {
11501151
// Match pattern like: export default require_xxx();
11511152
// and: export { };
11521153
const defaultExportMatch = content.match(/export default (require_\w+)\(\);/);
1153-
const emptyExportMatch = content.match(/export \{\s*\};/);
11541154

1155-
if (defaultExportMatch && emptyExportMatch) {
1155+
if (defaultExportMatch) {
11561156
const requireFn = defaultExportMatch[1];
11571157
console.log(` Fixing CJS named exports for ${specifier}...`);
11581158

1159-
// Replace empty export with named exports from the default
1160-
content = content.replace(
1161-
/export default (require_\w+)\(\);\s*\nexport \{\s*\};/,
1162-
`const __cjs_export__ = ${requireFn}();\nexport const { expectTypeOf } = __cjs_export__;\nexport default __cjs_export__;`,
1163-
);
1159+
const emptyExportMatch = content.match(/export \{\s*\};/);
1160+
if (emptyExportMatch) {
1161+
// Pattern: export default require_xxx();\nexport { };
1162+
content = content.replace(
1163+
/export default (require_\w+)\(\);\s*\nexport \{\s*\};/,
1164+
`const __cjs_export__ = ${requireFn}();\nexport const { expectTypeOf } = __cjs_export__;\nexport default __cjs_export__;`,
1165+
);
1166+
} else {
1167+
// Pattern: export default require_xxx(); (no empty export block)
1168+
content = content.replace(
1169+
/export default (require_\w+)\(\);/,
1170+
`const __cjs_export__ = ${requireFn}();\nexport const { expectTypeOf } = __cjs_export__;\nexport default __cjs_export__;`,
1171+
);
1172+
}
11641173

11651174
await writeFile(vendorFilePath, content, 'utf-8');
11661175
}

0 commit comments

Comments
 (0)