Skip to content

Commit ccffead

Browse files
perf(module-runner): add client-side builtin module check (#20924)
Co-authored-by: sapphi-red <49056869+sapphi-red@users.noreply.github.com>
1 parent b42c3fb commit ccffead

File tree

7 files changed

+54
-15
lines changed

7 files changed

+54
-15
lines changed

packages/vite/src/module-runner/runner.ts

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
type NormalizedModuleRunnerTransport,
77
normalizeModuleRunnerTransport,
88
} from '../shared/moduleRunnerTransport'
9+
import { createIsBuiltin } from '../shared/builtin'
910
import type { EvaluatedModuleNode } from './evaluatedModules'
1011
import { EvaluatedModules } from './evaluatedModules'
1112
import type {
@@ -44,6 +45,8 @@ export class ModuleRunner {
4445
string,
4546
Promise<EvaluatedModuleNode>
4647
>()
48+
private isBuiltin?: (id: string) => boolean
49+
private builtinsPromise?: Promise<void>
4750

4851
private closed = false
4952

@@ -238,6 +241,23 @@ export class ModuleRunner {
238241
return cached
239242
}
240243

244+
private ensureBuiltins(): Promise<void> | undefined {
245+
if (this.isBuiltin) return
246+
247+
this.builtinsPromise ??= (async () => {
248+
try {
249+
this.debug?.('[module runner] fetching builtins from server')
250+
const builtins = await this.transport.invoke('getBuiltins', [])
251+
this.isBuiltin = createIsBuiltin(builtins)
252+
this.debug?.('[module runner] builtins loaded:', builtins)
253+
} finally {
254+
this.builtinsPromise = undefined
255+
}
256+
})()
257+
258+
return this.builtinsPromise
259+
}
260+
241261
private async getModuleInformation(
242262
url: string,
243263
importer: string | undefined,
@@ -247,13 +267,15 @@ export class ModuleRunner {
247267
throw new Error(`Vite module runner has been closed.`)
248268
}
249269

270+
await this.ensureBuiltins()
271+
250272
this.debug?.('[module runner] fetching', url)
251273

252274
const isCached = !!(typeof cachedModule === 'object' && cachedModule.meta)
253275

254276
const fetchedModule = // fast return for established externalized pattern
255277
(
256-
url.startsWith('data:')
278+
url.startsWith('data:') || this.isBuiltin?.(url)
257279
? { externalize: url, type: 'builtin' }
258280
: await this.transport.invoke('fetchModule', [
259281
url,

packages/vite/src/node/server/environment.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,9 @@ export class DevEnvironment extends BaseEnvironment {
133133
fetchModule: (id, importer, options) => {
134134
return this.fetchModule(id, importer, options)
135135
},
136+
getBuiltins: async () => {
137+
return this.config.resolve.builtins
138+
},
136139
})
137140

138141
this.hot.on(
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import { basename } from 'node:path'
2+
3+
export default basename('/foo/bar/baz.txt')

packages/vite/src/node/ssr/runtime/__tests__/server-worker-runner.invoke.spec.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,4 +98,13 @@ describe('running module runner inside a worker and using the ModuleRunnerTransp
9898
expect(output).not.toHaveProperty('result')
9999
expect(output.error).toContain('Error: Unknown invoke error')
100100
})
101+
102+
it('resolves builtin module without server round-trip', async () => {
103+
handleInvoke = (data: any) => server.environments.ssr.hot.handleInvoke(data)
104+
105+
const output = await run('./fixtures/builtin-import.ts')
106+
expect(output).toHaveProperty('result')
107+
expect(output.result).toBe('baz.txt')
108+
expect(output.error).toBeUndefined()
109+
})
101110
})

packages/vite/src/node/utils.ts

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import {
3030
withTrailingSlash,
3131
} from '../shared/utils'
3232
import { VALID_ID_PREFIX } from '../shared/constants'
33+
import { createIsBuiltin } from '../shared/builtin'
3334
import {
3435
CLIENT_ENTRY,
3536
CLIENT_PUBLIC_PATH,
@@ -117,20 +118,6 @@ export function isBuiltin(builtins: (string | RegExp)[], id: string): boolean {
117118
return isBuiltin(id)
118119
}
119120

120-
export function createIsBuiltin(
121-
builtins: (string | RegExp)[],
122-
): (id: string) => boolean {
123-
const plainBuiltinsSet = new Set(
124-
builtins.filter((builtin) => typeof builtin === 'string'),
125-
)
126-
const regexBuiltins = builtins.filter(
127-
(builtin) => typeof builtin !== 'string',
128-
)
129-
130-
return (id) =>
131-
plainBuiltinsSet.has(id) || regexBuiltins.some((regexp) => regexp.test(id))
132-
}
133-
134121
export const nodeLikeBuiltins: (string | RegExp)[] = [
135122
...nodeBuiltins,
136123
new RegExp(`^${NODE_BUILTIN_NAMESPACE}`),
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
export function createIsBuiltin(
2+
builtins: (string | RegExp)[],
3+
): (id: string) => boolean {
4+
const plainBuiltinsSet = new Set(
5+
builtins.filter((builtin) => typeof builtin === 'string'),
6+
)
7+
const regexBuiltins = builtins.filter(
8+
(builtin) => typeof builtin !== 'string',
9+
)
10+
11+
return (id: string) =>
12+
plainBuiltinsSet.has(id) || regexBuiltins.some((regexp) => regexp.test(id))
13+
}

packages/vite/src/shared/invokeMethods.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,4 +82,6 @@ export type InvokeMethods = {
8282
importer?: string,
8383
options?: FetchFunctionOptions,
8484
) => Promise<FetchResult>
85+
86+
getBuiltins: () => Promise<(string | RegExp)[]>
8587
}

0 commit comments

Comments
 (0)