Skip to content

Commit 74ec0e0

Browse files
authored
fix(rsc): keep import.meta.glob during scan build for rolldown-vite (#721)
1 parent cc1bcdf commit 74ec0e0

File tree

7 files changed

+113
-7
lines changed

7 files changed

+113
-7
lines changed
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export default function Dep() {
2+
return <>test-import-meta-glob</>
3+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export async function TestImportMetaGlob() {
2+
const mod: any = await Object.values(import.meta.glob('./dep.tsx'))[0]()
3+
return <mod.default />
4+
}

packages/plugin-rsc/examples/basic/src/routes/root.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import { TestHmrSharedServer } from './hmr-shared/server'
3636
import { TestHmrSharedClient } from './hmr-shared/client'
3737
import { TestHmrSharedAtomic } from './hmr-shared/atomic/server'
3838
import { TestCssQueries } from './css-queries/server'
39+
import { TestImportMetaGlob } from './import-meta-glob/server'
3940

4041
export function Root(props: { url: URL }) {
4142
return (
@@ -85,6 +86,7 @@ export function Root(props: { url: URL }) {
8586
<TestUseCache />
8687
<TestReactCache url={props.url} />
8788
<TestCssQueries />
89+
<TestImportMetaGlob />
8890
</body>
8991
</html>
9092
)

packages/plugin-rsc/examples/basic/vite.config.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ export default { fetch: handler };
131131
}
132132
},
133133
},
134+
testScanPlugin(),
134135
],
135136
build: {
136137
minify: false,
@@ -158,6 +159,38 @@ export default { fetch: handler };
158159
},
159160
}) as any
160161

162+
function testScanPlugin(): Plugin[] {
163+
const moduleIds: { name: string; ids: string[] }[] = []
164+
return [
165+
{
166+
name: 'test-scan',
167+
apply: 'build',
168+
buildEnd() {
169+
moduleIds.push({
170+
name: this.environment.name,
171+
ids: [...this.getModuleIds()],
172+
})
173+
},
174+
buildApp: {
175+
order: 'post',
176+
async handler() {
177+
// client scan build discovers additional modules for server references.
178+
const [m1, m2] = moduleIds.filter((m) => m.name === 'rsc')
179+
const diff = m2.ids.filter((id) => !m1.ids.includes(id))
180+
assert(diff.length > 0)
181+
182+
// but make sure it's not due to import.meta.glob
183+
// https://github.com/vitejs/rolldown-vite/issues/373
184+
assert.equal(
185+
diff.find((id) => id.includes('import-meta-glob/dep.tsx')),
186+
undefined,
187+
)
188+
},
189+
},
190+
},
191+
]
192+
}
193+
161194
function vitePluginUseCache(): Plugin[] {
162195
return [
163196
{

packages/plugin-rsc/src/plugin.ts

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ import {
4040
import { cjsModuleRunnerPlugin } from './plugins/cjs'
4141
import { evalValue, parseIdQuery } from './plugins/utils'
4242
import { createDebug } from '@hiogawa/utils'
43+
import { transformScanBuildStrip } from './plugins/scan'
4344

4445
// state for build orchestration
4546
let serverReferences: Record<string, string> = {}
@@ -901,19 +902,16 @@ globalThis.AsyncLocalStorage = __viteRscAyncHooks.AsyncLocalStorage;
901902
]
902903
}
903904

905+
// During scan build, we strip all code but imports to
906+
// traverse module graph faster and just discover client/server references.
904907
function scanBuildStripPlugin(): Plugin {
905908
return {
906909
name: 'rsc:scan-strip',
907910
apply: 'build',
908911
enforce: 'post',
909-
transform(code, _id, _options) {
912+
async transform(code, _id, _options) {
910913
if (!isScanBuild) return
911-
// During server scan, we strip all code but imports to only discover client/server references.
912-
const [imports] = esModuleLexer.parse(code)
913-
const output = imports
914-
.map((e) => e.n && `import ${JSON.stringify(e.n)};\n`)
915-
.filter(Boolean)
916-
.join('')
914+
const output = await transformScanBuildStrip(code)
917915
return { code: output, map: { mappings: '' } }
918916
},
919917
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { describe, expect, it } from 'vitest'
2+
import { transformScanBuildStrip } from './scan'
3+
4+
describe(transformScanBuildStrip, () => {
5+
it('basic', async () => {
6+
const input = `\
7+
import { a } from "a";
8+
import "b";
9+
import(String("c"))
10+
import.meta.glob("d", {
11+
query: "?e",
12+
})
13+
import.meta.globee("d", { query: "?e" })
14+
export default "foo";
15+
`
16+
expect(await transformScanBuildStrip(input)).toMatchInlineSnapshot(`
17+
"import "a";
18+
import "b";
19+
console.log(import.meta.glob("d", {
20+
query: "?e",
21+
}));
22+
"
23+
`)
24+
})
25+
})
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import * as esModuleLexer from 'es-module-lexer'
2+
import { parseAstAsync } from 'vite'
3+
import { walk } from 'estree-walker'
4+
5+
// https://github.com/vitejs/vite/blob/86d2e8be50be535494734f9f5f5236c61626b308/packages/vite/src/node/plugins/importMetaGlob.ts#L113
6+
const importGlobRE = /\bimport\.meta\.glob(?:<\w+>)?\s*\(/g
7+
8+
export async function transformScanBuildStrip(code: string): Promise<string> {
9+
const [imports] = esModuleLexer.parse(code)
10+
let output = imports
11+
.map((e) => e.n && `import ${JSON.stringify(e.n)};\n`)
12+
.filter(Boolean)
13+
.join('')
14+
15+
// preserve import.meta.glob for rolldown-vite
16+
// https://github.com/vitejs/rolldown-vite/issues/373
17+
if (importGlobRE.test(code)) {
18+
const ast = await parseAstAsync(code)
19+
walk(ast, {
20+
enter(node) {
21+
if (
22+
node.type === 'CallExpression' &&
23+
node.callee.type === 'MemberExpression' &&
24+
node.callee.object.type === 'MetaProperty' &&
25+
node.callee.object.meta.type === 'Identifier' &&
26+
node.callee.object.meta.name === 'import' &&
27+
node.callee.object.property.type === 'Identifier' &&
28+
node.callee.object.property.name === 'meta' &&
29+
node.callee.property.type === 'Identifier' &&
30+
node.callee.property.name === 'glob'
31+
) {
32+
const importMetaGlob = code.slice(node.start, node.end)
33+
output += `console.log(${importMetaGlob});\n`
34+
}
35+
},
36+
})
37+
output += ''
38+
}
39+
40+
return output
41+
}

0 commit comments

Comments
 (0)