Skip to content

Commit d5d3779

Browse files
committed
fix(rsc/cjs): add __filename and __dirname
1 parent 876a30a commit d5d3779

File tree

3 files changed

+25
-8
lines changed

3 files changed

+25
-8
lines changed

packages/plugin-rsc/src/plugins/cjs.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import * as esModuleLexer from 'es-module-lexer'
66
import { transformCjsToEsm } from '../transforms/cjs'
77
import { createDebug } from '@hiogawa/utils'
88
import { parseIdQuery } from './shared'
9+
import { fileURLToPath, pathToFileURL } from 'node:url'
910

1011
const debug = createDebug('vite-rsc:cjs')
1112

@@ -52,7 +53,7 @@ export function cjsModuleRunnerPlugin(): Plugin[] {
5253
}
5354

5455
const ast = await parseAstAsync(code)
55-
const result = transformCjsToEsm(code, ast)
56+
const result = transformCjsToEsm(code, ast, { id })
5657
const output = result.output
5758
return {
5859
code: output.toString(),

packages/plugin-rsc/src/transforms/cjs.test.ts

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import path from 'node:path'
77
describe(transformCjsToEsm, () => {
88
async function testTransform(input: string) {
99
const ast = await parseAstAsync(input)
10-
const { output } = transformCjsToEsm(input, ast)
10+
const { output } = transformCjsToEsm(input, ast, { id: '/test.js' })
1111
if (!output.hasChanged()) {
1212
return
1313
}
@@ -22,7 +22,8 @@ describe(transformCjsToEsm, () => {
2222
exports.ok = true;
2323
`
2424
expect(await testTransform(input)).toMatchInlineSnapshot(`
25-
"let exports = {}; const module = { exports };
25+
"let __filename = "/test.js"; let __dirname = "/";
26+
let exports = {}; const module = { exports };
2627
exports.ok = true;
2728
2829
;__vite_ssr_exportAll__(module.exports);
@@ -41,7 +42,8 @@ if (true) {
4142
}
4243
`
4344
expect(await testTransform(input)).toMatchInlineSnapshot(`
44-
"let exports = {}; const module = { exports };
45+
"let __filename = "/test.js"; let __dirname = "/";
46+
let exports = {}; const module = { exports };
4547
function __cjs_interop__(m) { return m.__cjs_module_runner_transform ? m.default : m; }
4648
if (true) {
4749
module.exports = (__cjs_interop__(await import('./cjs/use-sync-external-store.production.js')));
@@ -65,7 +67,8 @@ if (true) {
6567
})()
6668
`
6769
expect(await testTransform(input)).toMatchInlineSnapshot(`
68-
"let exports = {}; const module = { exports };
70+
"let __filename = "/test.js"; let __dirname = "/";
71+
let exports = {}; const module = { exports };
6972
function __cjs_interop__(m) { return m.__cjs_module_runner_transform ? m.default : m; }
7073
const __cjs_to_esm_hoist_0 = __cjs_interop__(await import("react"));
7174
const __cjs_to_esm_hoist_1 = __cjs_interop__(await import("react-dom"));
@@ -95,7 +98,8 @@ function test() {
9598
}
9699
`
97100
expect(await testTransform(input)).toMatchInlineSnapshot(`
98-
"let exports = {}; const module = { exports };
101+
"let __filename = "/test.js"; let __dirname = "/";
102+
let exports = {}; const module = { exports };
99103
function __cjs_interop__(m) { return m.__cjs_module_runner_transform ? m.default : m; }
100104
const __cjs_to_esm_hoist_0 = __cjs_interop__(await import("te" + "st"));
101105
const __cjs_to_esm_hoist_1 = __cjs_interop__(await import("test"));
@@ -125,7 +129,8 @@ function test() {
125129
}
126130
`
127131
expect(await testTransform(input)).toMatchInlineSnapshot(`
128-
"let exports = {}; const module = { exports };
132+
"let __filename = "/test.js"; let __dirname = "/";
133+
let exports = {}; const module = { exports };
129134
{
130135
const require = () => {};
131136
require("test");
@@ -149,7 +154,7 @@ function test() {
149154
async transform(code, id) {
150155
if (id.endsWith('.cjs')) {
151156
const ast = await parseAstAsync(code)
152-
const { output } = transformCjsToEsm(code, ast)
157+
const { output } = transformCjsToEsm(code, ast, { id })
153158
return {
154159
code: output.toString(),
155160
map: output.generateMap({ hires: 'boundary' }),

packages/plugin-rsc/src/transforms/cjs.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import type { Program, Node } from 'estree'
22
import MagicString from 'magic-string'
33
import { analyze } from 'periscopic'
44
import { walk } from 'estree-walker'
5+
import { fileURLToPath, pathToFileURL } from 'node:url'
6+
import path from 'node:path'
57

68
// TODO:
79
// replacing require("xxx") into import("xxx") affects Vite's resolution.
@@ -14,6 +16,7 @@ const CJS_INTEROP_HELPER = `function __cjs_interop__(m) { return m.__cjs_module_
1416
export function transformCjsToEsm(
1517
code: string,
1618
ast: Program,
19+
options: { id: string },
1720
): { output: MagicString } {
1821
const output = new MagicString(code)
1922
const analyzed = analyze(ast)
@@ -85,6 +88,14 @@ export function transformCjsToEsm(
8588
// https://nodejs.org/docs/v22.19.0/api/modules.html#exports-shortcut
8689
output.prepend(`let exports = {}; const module = { exports };\n`)
8790

91+
// https://nodejs.org/docs/v22.19.0/api/modules.html#the-module-scope
92+
// https://github.com/vitest-dev/vitest/blob/965cefc19722a6c899cd1d3decb3cc33e72af696/packages/vite-node/src/client.ts#L548-L554
93+
const __filename = fileURLToPath(pathToFileURL(options.id).href)
94+
const __dirname = path.dirname(__filename)
95+
output.prepend(
96+
`let __filename = ${JSON.stringify(__filename)}; let __dirname = ${JSON.stringify(__dirname)};\n`,
97+
)
98+
8899
// TODO: can we use cjs-module-lexer to properly define named exports?
89100
// for re-exports, we need to eagerly transform dependencies though.
90101
// https://github.com/nodejs/node/blob/f3adc11e37b8bfaaa026ea85c1cf22e3a0e29ae9/lib/internal/modules/esm/translators.js#L382-L409

0 commit comments

Comments
 (0)