Skip to content
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default function Dep() {
return <>test-import-meta-glob</>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export async function TestImportMetaGlob() {
const mod: any = await Object.values(import.meta.glob('./dep.tsx'))[0]()
return <mod.default />
}
2 changes: 2 additions & 0 deletions packages/plugin-rsc/examples/basic/src/routes/root.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import { TestHmrSharedServer } from './hmr-shared/server'
import { TestHmrSharedClient } from './hmr-shared/client'
import { TestHmrSharedAtomic } from './hmr-shared/atomic/server'
import { TestCssQueries } from './css-queries/server'
import { TestImportMetaGlob } from './import-meta-glob/server'

export function Root(props: { url: URL }) {
return (
Expand Down Expand Up @@ -85,6 +86,7 @@ export function Root(props: { url: URL }) {
<TestUseCache />
<TestReactCache url={props.url} />
<TestCssQueries />
<TestImportMetaGlob />
</body>
</html>
)
Expand Down
33 changes: 33 additions & 0 deletions packages/plugin-rsc/examples/basic/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ export default { fetch: handler };
}
},
},
testScanPlugin(),
],
build: {
minify: false,
Expand Down Expand Up @@ -158,6 +159,38 @@ export default { fetch: handler };
},
}) as any

function testScanPlugin(): Plugin[] {
const moduleIds: { name: string; ids: string[] }[] = []
return [
{
name: 'test-scan',
apply: 'build',
buildEnd() {
moduleIds.push({
name: this.environment.name,
ids: [...this.getModuleIds()],
})
},
buildApp: {
order: 'post',
async handler() {
// client scan build discovers additional modules for server references.
const [m1, m2] = moduleIds.filter((m) => m.name === 'rsc')
const diff = m2.ids.filter((id) => !m1.ids.includes(id))
assert(diff.length > 0)

// but make sure it's not due to import.meta.glob
// https://github.com/vitejs/rolldown-vite/issues/373
assert.equal(
diff.find((id) => id.includes('import-meta-glob/dep.tsx')),
undefined,
)
},
},
},
]
}

function vitePluginUseCache(): Plugin[] {
return [
{
Expand Down
12 changes: 5 additions & 7 deletions packages/plugin-rsc/src/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import {
import { cjsModuleRunnerPlugin } from './plugins/cjs'
import { evalValue, parseIdQuery } from './plugins/utils'
import { createDebug } from '@hiogawa/utils'
import { transformScanBuildStrip } from './plugins/scan'

// state for build orchestration
let serverReferences: Record<string, string> = {}
Expand Down Expand Up @@ -901,19 +902,16 @@ globalThis.AsyncLocalStorage = __viteRscAyncHooks.AsyncLocalStorage;
]
}

// During scan build, we strip all code but imports to
// traverse module graph faster and just discover client/server references.
function scanBuildStripPlugin(): Plugin {
return {
name: 'rsc:scan-strip',
apply: 'build',
enforce: 'post',
transform(code, _id, _options) {
async transform(code, _id, _options) {
if (!isScanBuild) return
// During server scan, we strip all code but imports to only discover client/server references.
const [imports] = esModuleLexer.parse(code)
const output = imports
.map((e) => e.n && `import ${JSON.stringify(e.n)};\n`)
.filter(Boolean)
.join('')
const output = await transformScanBuildStrip(code)
return { code: output, map: { mappings: '' } }
},
}
Expand Down
25 changes: 25 additions & 0 deletions packages/plugin-rsc/src/plugins/scan.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { describe, expect, it } from 'vitest'
import { transformScanBuildStrip } from './scan'

describe(transformScanBuildStrip, () => {
it('basic', async () => {
const input = `\
import { a } from "a";
import "b";
import(String("c"))
import.meta.glob("d", {
query: "?e",
})
import.meta.globee("d", { query: "?e" })
export default "foo";
`
expect(await transformScanBuildStrip(input)).toMatchInlineSnapshot(`
"import "a";
import "b";
console.log(import.meta.glob("d", {
query: "?e",
}));
"
`)
})
})
41 changes: 41 additions & 0 deletions packages/plugin-rsc/src/plugins/scan.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import * as esModuleLexer from 'es-module-lexer'
import { parseAstAsync } from 'vite'
import { walk } from 'estree-walker'

// https://github.com/vitejs/vite/blob/86d2e8be50be535494734f9f5f5236c61626b308/packages/vite/src/node/plugins/importMetaGlob.ts#L113
const importGlobRE = /\bimport\.meta\.glob(?:<\w+>)?\s*\(/g

export async function transformScanBuildStrip(code: string): Promise<string> {
const [imports] = esModuleLexer.parse(code)
let output = imports
.map((e) => e.n && `import ${JSON.stringify(e.n)};\n`)
.filter(Boolean)
.join('')

// preserve import.meta.glob for rolldown-vite
// https://github.com/vitejs/rolldown-vite/issues/373
if (importGlobRE.test(code)) {
const ast = await parseAstAsync(code)
walk(ast, {
enter(node) {
if (
node.type === 'CallExpression' &&
node.callee.type === 'MemberExpression' &&
node.callee.object.type === 'MetaProperty' &&
node.callee.object.meta.type === 'Identifier' &&
node.callee.object.meta.name === 'import' &&
node.callee.object.property.type === 'Identifier' &&
node.callee.object.property.name === 'meta' &&
node.callee.property.type === 'Identifier' &&
node.callee.property.name === 'glob'
) {
const importMetaGlob = code.slice(node.start, node.end)
output += `console.log(${importMetaGlob});\n`
}
},
})
output += ''
}

return output
}
Loading