Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/vite/rolldown.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ const bundledDevClientConfig = defineConfig({
transform: {
target: 'es2020',
},
// the runtime is served from the installed rolldown at dev time (`getRolldownDevRuntimeFiles`)
// the runtime is served from the installed rolldown at dev time (`getRolldownDevRuntime`)
external: ['@vite/env', 'rolldown/experimental/runtime'],
output: {
dir: path.resolve(dirname, 'dist'),
Expand Down
22 changes: 7 additions & 15 deletions packages/vite/src/node/__tests__/plugins/clientInjections.spec.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,10 @@
import path from 'node:path'
import { expect, test } from 'vitest'
import { getRolldownDevRuntimeFiles } from '../../plugins/clientInjections'
import { getRolldownDevRuntime } from '../../plugins/clientInjections'

// The runtime entry imports its helper file with a relative path. Catch a rolldown layout
// change here, instead of as a 404 in the browser.
test('serves every file the rolldown dev runtime imports', () => {
const files = getRolldownDevRuntimeFiles()
let checked = 0
for (const [urlPath, source] of files) {
for (const match of source.matchAll(/\bfrom\s*['"](\.\.?\/[^'"]+)['"]/g)) {
const imported = path.posix.join(path.posix.dirname(urlPath), match[1])
expect(files.has(imported), `${urlPath} imports ${match[1]}`).toBe(true)
checked++
}
}
expect(checked).toBeGreaterThan(0)
// Only this one file is served, so an import in it would 404 in the browser.
test('the rolldown dev runtime imports nothing', () => {
const source = getRolldownDevRuntime()
expect(source).toContain('DevRuntime')
expect(source).not.toMatch(/^\s*import\b/m)

Check failure on line 8 in packages/vite/src/node/__tests__/plugins/clientInjections.spec.ts

View workflow job for this annotation

GitHub Actions / Build&Test: node-24, ubuntu-latest

packages/vite/src/node/__tests__/plugins/clientInjections.spec.ts > the rolldown dev runtime imports nothing

AssertionError: expected 'import { __exportAll, __reExport, __t…' not to match /^\s*import\b/m - Expected: /^\s*import\b/m + Received: "import { __exportAll, __reExport, __toCommonJS, __toESM } from './experimental-runtime-base.mjs'; // @ts-check class Module { /** * @type {{ exports: any }} */ exportsHolder = { exports: null }; /** * @type {string} */ id; /** * @PARAM {string} id */ constructor(id) { this.id = id; } get exports() { return this.exportsHolder.exports; } } /** * Compiler-emitted module-graph delta — pure topology (static + dynamic edges). * `ids[0, localCount)` are the modules this payload carries; `ids[localCount, …)` are foreign edge targets. * `edges[i]` / `dynamicEdges[i]` are the static / dynamic-`import()` out-edges of `ids[i]`. * @typedef {{ ids: string[], localCount: number, edges: number[][], dynamicEdges?: number[][] }} ModuleGraphDelta * @typedef {{ createModuleHotContext(moduleId: string): any, onModuleCacheRemoval(moduleId: string): void }} DevRuntimeHooks */ export class MissingFactoryError extends Error { /** * @PARAM {string} id */ constructor(id) { super(`No factory registered for module ${id}`); this.id = id; } } export class DevRuntime { /** * Client ID generated at runtime initialization, used for lazy compilation requests. * @type {string} */ clientId; /** * @PARAM {string} clientId */ constructor(clientId) { this.clientId = clientId; } /** * Static import edges from `registerGraph` — entries persist across `removeModuleCache` * and change only by replacement from a newer payload (last write wins). * @type {Map<string, { edges: string[] }>} */ staticImports = new Map(); /** * Reverse index over the static imports. * @type {Map<string, Set<string>>} */ importers = new Map(); /** * Dynamic `import()` edges from `registerGraph`, keyed by importer — mirror of * `staticImports` for the dynamic reverse index. * @type {Map<string, { edges: string[] }>} */ dynamicImports = new Map(); /** * Reverse index over the dynamic imports. * @type {Map<string, Set<string>>} */ dynamicImporters = new Map(); /** * The module cache. Membership means \"this module's side effects ran in this tab\" — * registration is emitted ahead of every module body, and nothing un-registers on * unwind, so a factory that throws mid-body stays registered. A `Map` rather than a * plain object: HMR eviction deletes entries, and a `delete` on an object drops V8 * into dictionary mode, taxing every later lookup on the hottest read path. * @type {Map<string, Module>} */ moduleCache = new Map(); /** * Re-runnable factories from HMR patches and lazy chunks. The initial bundle stays * scope-hoisted and contributes none. * @type {Map<string, { kind: 'esm' | 'cjs', fn: (id: string) => void }>} */ factories = new Map(); /** * Installed by the dev client at boot. The runtime is a store + executor and makes * no HMR decisions; accepting, disposing, and reloading live behind these hooks. * @type {DevRuntimeHooks | null} */ hooks = null; /** * @PARAM {ModuleGraphDelta} delta */ registerGraph(delta) { for (let i = 0; i < delta.localCount; i++) { const id = delta.ids[i]; const edges = delta.edges[i].map((j) => delta.ids[j]); for (const target of this.staticImports.get(id)?.edges ?? []) { this.importers.get(target)?.delete(id); } for (const target of edges) { let importerSet = this.importers.get(target); if (!importerSet) { importerSet = new Set(); this.importers.set(target, importerSet); } importerSet.add(id); } this.staticImports.set(id, { edges }); // Dynamic `import()` edges are maintained in a parallel reverse index with the same // last-write-wins bookkeeping; `getImporters` unions the two. const dynamicEdges = (delta.dynamicEdges?.[i] ?? []).map((j) => delta.ids[j]);

Check failure on line 8 in packages/vite/src/node/__tests__/plugins/clientInjections.spec.ts

View workflow job for this annotation

GitHub Actions / Build&Test: node-22, ubuntu-latest

packages/vite/src/node/__tests__/plugins/clientInjections.spec.ts > the rolldown dev runtime imports nothing

AssertionError: expected 'import { __exportAll, __reExport, __t…' not to match /^\s*import\b/m - Expected: /^\s*import\b/m + Received: "import { __exportAll, __reExport, __toCommonJS, __toESM } from './experimental-runtime-base.mjs'; // @ts-check class Module { /** * @type {{ exports: any }} */ exportsHolder = { exports: null }; /** * @type {string} */ id; /** * @PARAM {string} id */ constructor(id) { this.id = id; } get exports() { return this.exportsHolder.exports; } } /** * Compiler-emitted module-graph delta — pure topology (static + dynamic edges). * `ids[0, localCount)` are the modules this payload carries; `ids[localCount, …)` are foreign edge targets. * `edges[i]` / `dynamicEdges[i]` are the static / dynamic-`import()` out-edges of `ids[i]`. * @typedef {{ ids: string[], localCount: number, edges: number[][], dynamicEdges?: number[][] }} ModuleGraphDelta * @typedef {{ createModuleHotContext(moduleId: string): any, onModuleCacheRemoval(moduleId: string): void }} DevRuntimeHooks */ export class MissingFactoryError extends Error { /** * @PARAM {string} id */ constructor(id) { super(`No factory registered for module ${id}`); this.id = id; } } export class DevRuntime { /** * Client ID generated at runtime initialization, used for lazy compilation requests. * @type {string} */ clientId; /** * @PARAM {string} clientId */ constructor(clientId) { this.clientId = clientId; } /** * Static import edges from `registerGraph` — entries persist across `removeModuleCache` * and change only by replacement from a newer payload (last write wins). * @type {Map<string, { edges: string[] }>} */ staticImports = new Map(); /** * Reverse index over the static imports. * @type {Map<string, Set<string>>} */ importers = new Map(); /** * Dynamic `import()` edges from `registerGraph`, keyed by importer — mirror of * `staticImports` for the dynamic reverse index. * @type {Map<string, { edges: string[] }>} */ dynamicImports = new Map(); /** * Reverse index over the dynamic imports. * @type {Map<string, Set<string>>} */ dynamicImporters = new Map(); /** * The module cache. Membership means \"this module's side effects ran in this tab\" — * registration is emitted ahead of every module body, and nothing un-registers on * unwind, so a factory that throws mid-body stays registered. A `Map` rather than a * plain object: HMR eviction deletes entries, and a `delete` on an object drops V8 * into dictionary mode, taxing every later lookup on the hottest read path. * @type {Map<string, Module>} */ moduleCache = new Map(); /** * Re-runnable factories from HMR patches and lazy chunks. The initial bundle stays * scope-hoisted and contributes none. * @type {Map<string, { kind: 'esm' | 'cjs', fn: (id: string) => void }>} */ factories = new Map(); /** * Installed by the dev client at boot. The runtime is a store + executor and makes * no HMR decisions; accepting, disposing, and reloading live behind these hooks. * @type {DevRuntimeHooks | null} */ hooks = null; /** * @PARAM {ModuleGraphDelta} delta */ registerGraph(delta) { for (let i = 0; i < delta.localCount; i++) { const id = delta.ids[i]; const edges = delta.edges[i].map((j) => delta.ids[j]); for (const target of this.staticImports.get(id)?.edges ?? []) { this.importers.get(target)?.delete(id); } for (const target of edges) { let importerSet = this.importers.get(target); if (!importerSet) { importerSet = new Set(); this.importers.set(target, importerSet); } importerSet.add(id); } this.staticImports.set(id, { edges }); // Dynamic `import()` edges are maintained in a parallel reverse index with the same // last-write-wins bookkeeping; `getImporters` unions the two. const dynamicEdges = (delta.dynamicEdges?.[i] ?? []).map((j) => delta.ids[j]);

Check failure on line 8 in packages/vite/src/node/__tests__/plugins/clientInjections.spec.ts

View workflow job for this annotation

GitHub Actions / Build&Test: node-24, windows-latest

packages/vite/src/node/__tests__/plugins/clientInjections.spec.ts > the rolldown dev runtime imports nothing

AssertionError: expected 'import { __exportAll, __reExport, __t…' not to match /^\s*import\b/m - Expected: /^\s*import\b/m + Received: "import { __exportAll, __reExport, __toCommonJS, __toESM } from './experimental-runtime-base.mjs'; // @ts-check class Module { /** * @type {{ exports: any }} */ exportsHolder = { exports: null }; /** * @type {string} */ id; /** * @PARAM {string} id */ constructor(id) { this.id = id; } get exports() { return this.exportsHolder.exports; } } /** * Compiler-emitted module-graph delta — pure topology (static + dynamic edges). * `ids[0, localCount)` are the modules this payload carries; `ids[localCount, …)` are foreign edge targets. * `edges[i]` / `dynamicEdges[i]` are the static / dynamic-`import()` out-edges of `ids[i]`. * @typedef {{ ids: string[], localCount: number, edges: number[][], dynamicEdges?: number[][] }} ModuleGraphDelta * @typedef {{ createModuleHotContext(moduleId: string): any, onModuleCacheRemoval(moduleId: string): void }} DevRuntimeHooks */ export class MissingFactoryError extends Error { /** * @PARAM {string} id */ constructor(id) { super(`No factory registered for module ${id}`); this.id = id; } } export class DevRuntime { /** * Client ID generated at runtime initialization, used for lazy compilation requests. * @type {string} */ clientId; /** * @PARAM {string} clientId */ constructor(clientId) { this.clientId = clientId; } /** * Static import edges from `registerGraph` — entries persist across `removeModuleCache` * and change only by replacement from a newer payload (last write wins). * @type {Map<string, { edges: string[] }>} */ staticImports = new Map(); /** * Reverse index over the static imports. * @type {Map<string, Set<string>>} */ importers = new Map(); /** * Dynamic `import()` edges from `registerGraph`, keyed by importer — mirror of * `staticImports` for the dynamic reverse index. * @type {Map<string, { edges: string[] }>} */ dynamicImports = new Map(); /** * Reverse index over the dynamic imports. * @type {Map<string, Set<string>>} */ dynamicImporters = new Map(); /** * The module cache. Membership means \"this module's side effects ran in this tab\" — * registration is emitted ahead of every module body, and nothing un-registers on * unwind, so a factory that throws mid-body stays registered. A `Map` rather than a * plain object: HMR eviction deletes entries, and a `delete` on an object drops V8 * into dictionary mode, taxing every later lookup on the hottest read path. * @type {Map<string, Module>} */ moduleCache = new Map(); /** * Re-runnable factories from HMR patches and lazy chunks. The initial bundle stays * scope-hoisted and contributes none. * @type {Map<string, { kind: 'esm' | 'cjs', fn: (id: string) => void }>} */ factories = new Map(); /** * Installed by the dev client at boot. The runtime is a store + executor and makes * no HMR decisions; accepting, disposing, and reloading live behind these hooks. * @type {DevRuntimeHooks | null} */ hooks = null; /** * @PARAM {ModuleGraphDelta} delta */ registerGraph(delta) { for (let i = 0; i < delta.localCount; i++) { const id = delta.ids[i]; const edges = delta.edges[i].map((j) => delta.ids[j]); for (const target of this.staticImports.get(id)?.edges ?? []) { this.importers.get(target)?.delete(id); } for (const target of edges) { let importerSet = this.importers.get(target); if (!importerSet) { importerSet = new Set(); this.importers.set(target, importerSet); } importerSet.add(id); } this.staticImports.set(id, { edges }); // Dynamic `import()` edges are maintained in a parallel reverse index with the same // last-write-wins bookkeeping; `getImporters` unions the two. const dynamicEdges = (delta.dynamicEdges?.[i] ?? []).map((j) => delta.ids[j]);

Check failure on line 8 in packages/vite/src/node/__tests__/plugins/clientInjections.spec.ts

View workflow job for this annotation

GitHub Actions / Build&Test: node-24, macos-latest

packages/vite/src/node/__tests__/plugins/clientInjections.spec.ts > the rolldown dev runtime imports nothing

AssertionError: expected 'import { __exportAll, __reExport, __t…' not to match /^\s*import\b/m - Expected: /^\s*import\b/m + Received: "import { __exportAll, __reExport, __toCommonJS, __toESM } from './experimental-runtime-base.mjs'; // @ts-check class Module { /** * @type {{ exports: any }} */ exportsHolder = { exports: null }; /** * @type {string} */ id; /** * @PARAM {string} id */ constructor(id) { this.id = id; } get exports() { return this.exportsHolder.exports; } } /** * Compiler-emitted module-graph delta — pure topology (static + dynamic edges). * `ids[0, localCount)` are the modules this payload carries; `ids[localCount, …)` are foreign edge targets. * `edges[i]` / `dynamicEdges[i]` are the static / dynamic-`import()` out-edges of `ids[i]`. * @typedef {{ ids: string[], localCount: number, edges: number[][], dynamicEdges?: number[][] }} ModuleGraphDelta * @typedef {{ createModuleHotContext(moduleId: string): any, onModuleCacheRemoval(moduleId: string): void }} DevRuntimeHooks */ export class MissingFactoryError extends Error { /** * @PARAM {string} id */ constructor(id) { super(`No factory registered for module ${id}`); this.id = id; } } export class DevRuntime { /** * Client ID generated at runtime initialization, used for lazy compilation requests. * @type {string} */ clientId; /** * @PARAM {string} clientId */ constructor(clientId) { this.clientId = clientId; } /** * Static import edges from `registerGraph` — entries persist across `removeModuleCache` * and change only by replacement from a newer payload (last write wins). * @type {Map<string, { edges: string[] }>} */ staticImports = new Map(); /** * Reverse index over the static imports. * @type {Map<string, Set<string>>} */ importers = new Map(); /** * Dynamic `import()` edges from `registerGraph`, keyed by importer — mirror of * `staticImports` for the dynamic reverse index. * @type {Map<string, { edges: string[] }>} */ dynamicImports = new Map(); /** * Reverse index over the dynamic imports. * @type {Map<string, Set<string>>} */ dynamicImporters = new Map(); /** * The module cache. Membership means \"this module's side effects ran in this tab\" — * registration is emitted ahead of every module body, and nothing un-registers on * unwind, so a factory that throws mid-body stays registered. A `Map` rather than a * plain object: HMR eviction deletes entries, and a `delete` on an object drops V8 * into dictionary mode, taxing every later lookup on the hottest read path. * @type {Map<string, Module>} */ moduleCache = new Map(); /** * Re-runnable factories from HMR patches and lazy chunks. The initial bundle stays * scope-hoisted and contributes none. * @type {Map<string, { kind: 'esm' | 'cjs', fn: (id: string) => void }>} */ factories = new Map(); /** * Installed by the dev client at boot. The runtime is a store + executor and makes * no HMR decisions; accepting, disposing, and reloading live behind these hooks. * @type {DevRuntimeHooks | null} */ hooks = null; /** * @PARAM {ModuleGraphDelta} delta */ registerGraph(delta) { for (let i = 0; i < delta.localCount; i++) { const id = delta.ids[i]; const edges = delta.edges[i].map((j) => delta.ids[j]); for (const target of this.staticImports.get(id)?.edges ?? []) { this.importers.get(target)?.delete(id); } for (const target of edges) { let importerSet = this.importers.get(target); if (!importerSet) { importerSet = new Set(); this.importers.set(target, importerSet); } importerSet.add(id); } this.staticImports.set(id, { edges }); // Dynamic `import()` edges are maintained in a parallel reverse index with the same // last-write-wins bookkeeping; `getImporters` unions the two. const dynamicEdges = (delta.dynamicEdges?.[i] ?? []).map((j) => delta.ids[j]);

Check failure on line 8 in packages/vite/src/node/__tests__/plugins/clientInjections.spec.ts

View workflow job for this annotation

GitHub Actions / Build&Test: node-20, ubuntu-latest

packages/vite/src/node/__tests__/plugins/clientInjections.spec.ts > the rolldown dev runtime imports nothing

AssertionError: expected 'import { __exportAll, __reExport, __t…' not to match /^\s*import\b/m - Expected: /^\s*import\b/m + Received: "import { __exportAll, __reExport, __toCommonJS, __toESM } from './experimental-runtime-base.mjs'; // @ts-check class Module { /** * @type {{ exports: any }} */ exportsHolder = { exports: null }; /** * @type {string} */ id; /** * @PARAM {string} id */ constructor(id) { this.id = id; } get exports() { return this.exportsHolder.exports; } } /** * Compiler-emitted module-graph delta — pure topology (static + dynamic edges). * `ids[0, localCount)` are the modules this payload carries; `ids[localCount, …)` are foreign edge targets. * `edges[i]` / `dynamicEdges[i]` are the static / dynamic-`import()` out-edges of `ids[i]`. * @typedef {{ ids: string[], localCount: number, edges: number[][], dynamicEdges?: number[][] }} ModuleGraphDelta * @typedef {{ createModuleHotContext(moduleId: string): any, onModuleCacheRemoval(moduleId: string): void }} DevRuntimeHooks */ export class MissingFactoryError extends Error { /** * @PARAM {string} id */ constructor(id) { super(`No factory registered for module ${id}`); this.id = id; } } export class DevRuntime { /** * Client ID generated at runtime initialization, used for lazy compilation requests. * @type {string} */ clientId; /** * @PARAM {string} clientId */ constructor(clientId) { this.clientId = clientId; } /** * Static import edges from `registerGraph` — entries persist across `removeModuleCache` * and change only by replacement from a newer payload (last write wins). * @type {Map<string, { edges: string[] }>} */ staticImports = new Map(); /** * Reverse index over the static imports. * @type {Map<string, Set<string>>} */ importers = new Map(); /** * Dynamic `import()` edges from `registerGraph`, keyed by importer — mirror of * `staticImports` for the dynamic reverse index. * @type {Map<string, { edges: string[] }>} */ dynamicImports = new Map(); /** * Reverse index over the dynamic imports. * @type {Map<string, Set<string>>} */ dynamicImporters = new Map(); /** * The module cache. Membership means \"this module's side effects ran in this tab\" — * registration is emitted ahead of every module body, and nothing un-registers on * unwind, so a factory that throws mid-body stays registered. A `Map` rather than a * plain object: HMR eviction deletes entries, and a `delete` on an object drops V8 * into dictionary mode, taxing every later lookup on the hottest read path. * @type {Map<string, Module>} */ moduleCache = new Map(); /** * Re-runnable factories from HMR patches and lazy chunks. The initial bundle stays * scope-hoisted and contributes none. * @type {Map<string, { kind: 'esm' | 'cjs', fn: (id: string) => void }>} */ factories = new Map(); /** * Installed by the dev client at boot. The runtime is a store + executor and makes * no HMR decisions; accepting, disposing, and reloading live behind these hooks. * @type {DevRuntimeHooks | null} */ hooks = null; /** * @PARAM {ModuleGraphDelta} delta */ registerGraph(delta) { for (let i = 0; i < delta.localCount; i++) { const id = delta.ids[i]; const edges = delta.edges[i].map((j) => delta.ids[j]); for (const target of this.staticImports.get(id)?.edges ?? []) { this.importers.get(target)?.delete(id); } for (const target of edges) { let importerSet = this.importers.get(target); if (!importerSet) { importerSet = new Set(); this.importers.set(target, importerSet); } importerSet.add(id); } this.staticImports.set(id, { edges }); // Dynamic `import()` edges are maintained in a parallel reverse index with the same // last-write-wins bookkeeping; `getImporters` unions the two. const dynamicEdges = (delta.dynamicEdges?.[i] ?? []).map((j) => delta.ids[j]);

Check failure on line 8 in packages/vite/src/node/__tests__/plugins/clientInjections.spec.ts

View workflow job for this annotation

GitHub Actions / Build&Test: node-26, ubuntu-latest

packages/vite/src/node/__tests__/plugins/clientInjections.spec.ts > the rolldown dev runtime imports nothing

AssertionError: expected 'import { __exportAll, __reExport, __t…' not to match /^\s*import\b/m - Expected: /^\s*import\b/m + Received: "import { __exportAll, __reExport, __toCommonJS, __toESM } from './experimental-runtime-base.mjs'; // @ts-check class Module { /** * @type {{ exports: any }} */ exportsHolder = { exports: null }; /** * @type {string} */ id; /** * @PARAM {string} id */ constructor(id) { this.id = id; } get exports() { return this.exportsHolder.exports; } } /** * Compiler-emitted module-graph delta — pure topology (static + dynamic edges). * `ids[0, localCount)` are the modules this payload carries; `ids[localCount, …)` are foreign edge targets. * `edges[i]` / `dynamicEdges[i]` are the static / dynamic-`import()` out-edges of `ids[i]`. * @typedef {{ ids: string[], localCount: number, edges: number[][], dynamicEdges?: number[][] }} ModuleGraphDelta * @typedef {{ createModuleHotContext(moduleId: string): any, onModuleCacheRemoval(moduleId: string): void }} DevRuntimeHooks */ export class MissingFactoryError extends Error { /** * @PARAM {string} id */ constructor(id) { super(`No factory registered for module ${id}`); this.id = id; } } export class DevRuntime { /** * Client ID generated at runtime initialization, used for lazy compilation requests. * @type {string} */ clientId; /** * @PARAM {string} clientId */ constructor(clientId) { this.clientId = clientId; } /** * Static import edges from `registerGraph` — entries persist across `removeModuleCache` * and change only by replacement from a newer payload (last write wins). * @type {Map<string, { edges: string[] }>} */ staticImports = new Map(); /** * Reverse index over the static imports. * @type {Map<string, Set<string>>} */ importers = new Map(); /** * Dynamic `import()` edges from `registerGraph`, keyed by importer — mirror of * `staticImports` for the dynamic reverse index. * @type {Map<string, { edges: string[] }>} */ dynamicImports = new Map(); /** * Reverse index over the dynamic imports. * @type {Map<string, Set<string>>} */ dynamicImporters = new Map(); /** * The module cache. Membership means \"this module's side effects ran in this tab\" — * registration is emitted ahead of every module body, and nothing un-registers on * unwind, so a factory that throws mid-body stays registered. A `Map` rather than a * plain object: HMR eviction deletes entries, and a `delete` on an object drops V8 * into dictionary mode, taxing every later lookup on the hottest read path. * @type {Map<string, Module>} */ moduleCache = new Map(); /** * Re-runnable factories from HMR patches and lazy chunks. The initial bundle stays * scope-hoisted and contributes none. * @type {Map<string, { kind: 'esm' | 'cjs', fn: (id: string) => void }>} */ factories = new Map(); /** * Installed by the dev client at boot. The runtime is a store + executor and makes * no HMR decisions; accepting, disposing, and reloading live behind these hooks. * @type {DevRuntimeHooks | null} */ hooks = null; /** * @PARAM {ModuleGraphDelta} delta */ registerGraph(delta) { for (let i = 0; i < delta.localCount; i++) { const id = delta.ids[i]; const edges = delta.edges[i].map((j) => delta.ids[j]); for (const target of this.staticImports.get(id)?.edges ?? []) { this.importers.get(target)?.delete(id); } for (const target of edges) { let importerSet = this.importers.get(target); if (!importerSet) { importerSet = new Set(); this.importers.set(target, importerSet); } importerSet.add(id); } this.staticImports.set(id, { edges }); // Dynamic `import()` edges are maintained in a parallel reverse index with the same // last-write-wins bookkeeping; `getImporters` unions the two. const dynamicEdges = (delta.dynamicEdges?.[i] ?? []).map((j) => delta.ids[j]);

Check failure on line 8 in packages/vite/src/node/__tests__/plugins/clientInjections.spec.ts

View workflow job for this annotation

GitHub Actions / Build&Test: node-24, windows-latest

packages/vite/src/node/__tests__/plugins/clientInjections.spec.ts > the rolldown dev runtime imports nothing

AssertionError: expected 'import { __exportAll, __reExport, __t…' not to match /^\s*import\b/m - Expected: /^\s*import\b/m + Received: "import { __exportAll, __reExport, __toCommonJS, __toESM } from './experimental-runtime-base.mjs'; // @ts-check class Module { /** * @type {{ exports: any }} */ exportsHolder = { exports: null }; /** * @type {string} */ id; /** * @PARAM {string} id */ constructor(id) { this.id = id; } get exports() { return this.exportsHolder.exports; } } /** * Compiler-emitted module-graph delta — pure topology (static + dynamic edges). * `ids[0, localCount)` are the modules this payload carries; `ids[localCount, …)` are foreign edge targets. * `edges[i]` / `dynamicEdges[i]` are the static / dynamic-`import()` out-edges of `ids[i]`. * @typedef {{ ids: string[], localCount: number, edges: number[][], dynamicEdges?: number[][] }} ModuleGraphDelta * @typedef {{ createModuleHotContext(moduleId: string): any, onModuleCacheRemoval(moduleId: string): void }} DevRuntimeHooks */ export class MissingFactoryError extends Error { /** * @PARAM {string} id */ constructor(id) { super(`No factory registered for module ${id}`); this.id = id; } } export class DevRuntime { /** * Client ID generated at runtime initialization, used for lazy compilation requests. * @type {string} */ clientId; /** * @PARAM {string} clientId */ constructor(clientId) { this.clientId = clientId; } /** * Static import edges from `registerGraph` — entries persist across `removeModuleCache` * and change only by replacement from a newer payload (last write wins). * @type {Map<string, { edges: string[] }>} */ staticImports = new Map(); /** * Reverse index over the static imports. * @type {Map<string, Set<string>>} */ importers = new Map(); /** * Dynamic `import()` edges from `registerGraph`, keyed by importer — mirror of * `staticImports` for the dynamic reverse index. * @type {Map<string, { edges: string[] }>} */ dynamicImports = new Map(); /** * Reverse index over the dynamic imports. * @type {Map<string, Set<string>>} */ dynamicImporters = new Map(); /** * The module cache. Membership means \"this module's side effects ran in this tab\" — * registration is emitted ahead of every module body, and nothing un-registers on * unwind, so a factory that throws mid-body stays registered. A `Map` rather than a * plain object: HMR eviction deletes entries, and a `delete` on an object drops V8 * into dictionary mode, taxing every later lookup on the hottest read path. * @type {Map<string, Module>} */ moduleCache = new Map(); /** * Re-runnable factories from HMR patches and lazy chunks. The initial bundle stays * scope-hoisted and contributes none. * @type {Map<string, { kind: 'esm' | 'cjs', fn: (id: string) => void }>} */ factories = new Map(); /** * Installed by the dev client at boot. The runtime is a store + executor and makes * no HMR decisions; accepting, disposing, and reloading live behind these hooks. * @type {DevRuntimeHooks | null} */ hooks = null; /** * @PARAM {ModuleGraphDelta} delta */ registerGraph(delta) { for (let i = 0; i < delta.localCount; i++) { const id = delta.ids[i]; const edges = delta.edges[i].map((j) => delta.ids[j]); for (const target of this.staticImports.get(id)?.edges ?? []) { this.importers.get(target)?.delete(id); } for (const target of edges) { let importerSet = this.importers.get(target); if (!importerSet) { importerSet = new Set(); this.importers.set(target, importerSet); } importerSet.add(id); } this.staticImports.set(id, { edges }); // Dynamic `import()` edges are maintained in a parallel reverse index with the same // last-write-wins bookkeeping; `getImporters` unions the two. const dynamicEdges = (delta.dynamicEdges?.[i] ?? []).map((j) => delta.ids[j]);

Check failure on line 8 in packages/vite/src/node/__tests__/plugins/clientInjections.spec.ts

View workflow job for this annotation

GitHub Actions / Build&Test: node-20, ubuntu-latest

packages/vite/src/node/__tests__/plugins/clientInjections.spec.ts > the rolldown dev runtime imports nothing

AssertionError: expected 'import { __exportAll, __reExport, __t…' not to match /^\s*import\b/m - Expected: /^\s*import\b/m + Received: "import { __exportAll, __reExport, __toCommonJS, __toESM } from './experimental-runtime-base.mjs'; // @ts-check class Module { /** * @type {{ exports: any }} */ exportsHolder = { exports: null }; /** * @type {string} */ id; /** * @PARAM {string} id */ constructor(id) { this.id = id; } get exports() { return this.exportsHolder.exports; } } /** * Compiler-emitted module-graph delta — pure topology (static + dynamic edges). * `ids[0, localCount)` are the modules this payload carries; `ids[localCount, …)` are foreign edge targets. * `edges[i]` / `dynamicEdges[i]` are the static / dynamic-`import()` out-edges of `ids[i]`. * @typedef {{ ids: string[], localCount: number, edges: number[][], dynamicEdges?: number[][] }} ModuleGraphDelta * @typedef {{ createModuleHotContext(moduleId: string): any, onModuleCacheRemoval(moduleId: string): void }} DevRuntimeHooks */ export class MissingFactoryError extends Error { /** * @PARAM {string} id */ constructor(id) { super(`No factory registered for module ${id}`); this.id = id; } } export class DevRuntime { /** * Client ID generated at runtime initialization, used for lazy compilation requests. * @type {string} */ clientId; /** * @PARAM {string} clientId */ constructor(clientId) { this.clientId = clientId; } /** * Static import edges from `registerGraph` — entries persist across `removeModuleCache` * and change only by replacement from a newer payload (last write wins). * @type {Map<string, { edges: string[] }>} */ staticImports = new Map(); /** * Reverse index over the static imports. * @type {Map<string, Set<string>>} */ importers = new Map(); /** * Dynamic `import()` edges from `registerGraph`, keyed by importer — mirror of * `staticImports` for the dynamic reverse index. * @type {Map<string, { edges: string[] }>} */ dynamicImports = new Map(); /** * Reverse index over the dynamic imports. * @type {Map<string, Set<string>>} */ dynamicImporters = new Map(); /** * The module cache. Membership means \"this module's side effects ran in this tab\" — * registration is emitted ahead of every module body, and nothing un-registers on * unwind, so a factory that throws mid-body stays registered. A `Map` rather than a * plain object: HMR eviction deletes entries, and a `delete` on an object drops V8 * into dictionary mode, taxing every later lookup on the hottest read path. * @type {Map<string, Module>} */ moduleCache = new Map(); /** * Re-runnable factories from HMR patches and lazy chunks. The initial bundle stays * scope-hoisted and contributes none. * @type {Map<string, { kind: 'esm' | 'cjs', fn: (id: string) => void }>} */ factories = new Map(); /** * Installed by the dev client at boot. The runtime is a store + executor and makes * no HMR decisions; accepting, disposing, and reloading live behind these hooks. * @type {DevRuntimeHooks | null} */ hooks = null; /** * @PARAM {ModuleGraphDelta} delta */ registerGraph(delta) { for (let i = 0; i < delta.localCount; i++) { const id = delta.ids[i]; const edges = delta.edges[i].map((j) => delta.ids[j]); for (const target of this.staticImports.get(id)?.edges ?? []) { this.importers.get(target)?.delete(id); } for (const target of edges) { let importerSet = this.importers.get(target); if (!importerSet) { importerSet = new Set(); this.importers.set(target, importerSet); } importerSet.add(id); } this.staticImports.set(id, { edges }); // Dynamic `import()` edges are maintained in a parallel reverse index with the same // last-write-wins bookkeeping; `getImporters` unions the two. const dynamicEdges = (delta.dynamicEdges?.[i] ?? []).map((j) => delta.ids[j]);

Check failure on line 8 in packages/vite/src/node/__tests__/plugins/clientInjections.spec.ts

View workflow job for this annotation

GitHub Actions / Build&Test: node-24, ubuntu-latest

packages/vite/src/node/__tests__/plugins/clientInjections.spec.ts > the rolldown dev runtime imports nothing

AssertionError: expected 'import { __exportAll, __reExport, __t…' not to match /^\s*import\b/m - Expected: /^\s*import\b/m + Received: "import { __exportAll, __reExport, __toCommonJS, __toESM } from './experimental-runtime-base.mjs'; // @ts-check class Module { /** * @type {{ exports: any }} */ exportsHolder = { exports: null }; /** * @type {string} */ id; /** * @PARAM {string} id */ constructor(id) { this.id = id; } get exports() { return this.exportsHolder.exports; } } /** * Compiler-emitted module-graph delta — pure topology (static + dynamic edges). * `ids[0, localCount)` are the modules this payload carries; `ids[localCount, …)` are foreign edge targets. * `edges[i]` / `dynamicEdges[i]` are the static / dynamic-`import()` out-edges of `ids[i]`. * @typedef {{ ids: string[], localCount: number, edges: number[][], dynamicEdges?: number[][] }} ModuleGraphDelta * @typedef {{ createModuleHotContext(moduleId: string): any, onModuleCacheRemoval(moduleId: string): void }} DevRuntimeHooks */ export class MissingFactoryError extends Error { /** * @PARAM {string} id */ constructor(id) { super(`No factory registered for module ${id}`); this.id = id; } } export class DevRuntime { /** * Client ID generated at runtime initialization, used for lazy compilation requests. * @type {string} */ clientId; /** * @PARAM {string} clientId */ constructor(clientId) { this.clientId = clientId; } /** * Static import edges from `registerGraph` — entries persist across `removeModuleCache` * and change only by replacement from a newer payload (last write wins). * @type {Map<string, { edges: string[] }>} */ staticImports = new Map(); /** * Reverse index over the static imports. * @type {Map<string, Set<string>>} */ importers = new Map(); /** * Dynamic `import()` edges from `registerGraph`, keyed by importer — mirror of * `staticImports` for the dynamic reverse index. * @type {Map<string, { edges: string[] }>} */ dynamicImports = new Map(); /** * Reverse index over the dynamic imports. * @type {Map<string, Set<string>>} */ dynamicImporters = new Map(); /** * The module cache. Membership means \"this module's side effects ran in this tab\" — * registration is emitted ahead of every module body, and nothing un-registers on * unwind, so a factory that throws mid-body stays registered. A `Map` rather than a * plain object: HMR eviction deletes entries, and a `delete` on an object drops V8 * into dictionary mode, taxing every later lookup on the hottest read path. * @type {Map<string, Module>} */ moduleCache = new Map(); /** * Re-runnable factories from HMR patches and lazy chunks. The initial bundle stays * scope-hoisted and contributes none. * @type {Map<string, { kind: 'esm' | 'cjs', fn: (id: string) => void }>} */ factories = new Map(); /** * Installed by the dev client at boot. The runtime is a store + executor and makes * no HMR decisions; accepting, disposing, and reloading live behind these hooks. * @type {DevRuntimeHooks | null} */ hooks = null; /** * @PARAM {ModuleGraphDelta} delta */ registerGraph(delta) { for (let i = 0; i < delta.localCount; i++) { const id = delta.ids[i]; const edges = delta.edges[i].map((j) => delta.ids[j]); for (const target of this.staticImports.get(id)?.edges ?? []) { this.importers.get(target)?.delete(id); } for (const target of edges) { let importerSet = this.importers.get(target); if (!importerSet) { importerSet = new Set(); this.importers.set(target, importerSet); } importerSet.add(id); } this.staticImports.set(id, { edges }); // Dynamic `import()` edges are maintained in a parallel reverse index with the same // last-write-wins bookkeeping; `getImporters` unions the two. const dynamicEdges = (delta.dynamicEdges?.[i] ?? []).map((j) => delta.ids[j]);

Check failure on line 8 in packages/vite/src/node/__tests__/plugins/clientInjections.spec.ts

View workflow job for this annotation

GitHub Actions / Build&Test: node-24, macos-latest

packages/vite/src/node/__tests__/plugins/clientInjections.spec.ts > the rolldown dev runtime imports nothing

AssertionError: expected 'import { __exportAll, __reExport, __t…' not to match /^\s*import\b/m - Expected: /^\s*import\b/m + Received: "import { __exportAll, __reExport, __toCommonJS, __toESM } from './experimental-runtime-base.mjs'; // @ts-check class Module { /** * @type {{ exports: any }} */ exportsHolder = { exports: null }; /** * @type {string} */ id; /** * @PARAM {string} id */ constructor(id) { this.id = id; } get exports() { return this.exportsHolder.exports; } } /** * Compiler-emitted module-graph delta — pure topology (static + dynamic edges). * `ids[0, localCount)` are the modules this payload carries; `ids[localCount, …)` are foreign edge targets. * `edges[i]` / `dynamicEdges[i]` are the static / dynamic-`import()` out-edges of `ids[i]`. * @typedef {{ ids: string[], localCount: number, edges: number[][], dynamicEdges?: number[][] }} ModuleGraphDelta * @typedef {{ createModuleHotContext(moduleId: string): any, onModuleCacheRemoval(moduleId: string): void }} DevRuntimeHooks */ export class MissingFactoryError extends Error { /** * @PARAM {string} id */ constructor(id) { super(`No factory registered for module ${id}`); this.id = id; } } export class DevRuntime { /** * Client ID generated at runtime initialization, used for lazy compilation requests. * @type {string} */ clientId; /** * @PARAM {string} clientId */ constructor(clientId) { this.clientId = clientId; } /** * Static import edges from `registerGraph` — entries persist across `removeModuleCache` * and change only by replacement from a newer payload (last write wins). * @type {Map<string, { edges: string[] }>} */ staticImports = new Map(); /** * Reverse index over the static imports. * @type {Map<string, Set<string>>} */ importers = new Map(); /** * Dynamic `import()` edges from `registerGraph`, keyed by importer — mirror of * `staticImports` for the dynamic reverse index. * @type {Map<string, { edges: string[] }>} */ dynamicImports = new Map(); /** * Reverse index over the dynamic imports. * @type {Map<string, Set<string>>} */ dynamicImporters = new Map(); /** * The module cache. Membership means \"this module's side effects ran in this tab\" — * registration is emitted ahead of every module body, and nothing un-registers on * unwind, so a factory that throws mid-body stays registered. A `Map` rather than a * plain object: HMR eviction deletes entries, and a `delete` on an object drops V8 * into dictionary mode, taxing every later lookup on the hottest read path. * @type {Map<string, Module>} */ moduleCache = new Map(); /** * Re-runnable factories from HMR patches and lazy chunks. The initial bundle stays * scope-hoisted and contributes none. * @type {Map<string, { kind: 'esm' | 'cjs', fn: (id: string) => void }>} */ factories = new Map(); /** * Installed by the dev client at boot. The runtime is a store + executor and makes * no HMR decisions; accepting, disposing, and reloading live behind these hooks. * @type {DevRuntimeHooks | null} */ hooks = null; /** * @PARAM {ModuleGraphDelta} delta */ registerGraph(delta) { for (let i = 0; i < delta.localCount; i++) { const id = delta.ids[i]; const edges = delta.edges[i].map((j) => delta.ids[j]); for (const target of this.staticImports.get(id)?.edges ?? []) { this.importers.get(target)?.delete(id); } for (const target of edges) { let importerSet = this.importers.get(target); if (!importerSet) { importerSet = new Set(); this.importers.set(target, importerSet); } importerSet.add(id); } this.staticImports.set(id, { edges }); // Dynamic `import()` edges are maintained in a parallel reverse index with the same // last-write-wins bookkeeping; `getImporters` unions the two. const dynamicEdges = (delta.dynamicEdges?.[i] ?? []).map((j) => delta.ids[j]);

Check failure on line 8 in packages/vite/src/node/__tests__/plugins/clientInjections.spec.ts

View workflow job for this annotation

GitHub Actions / Build&Test: node-26, ubuntu-latest

packages/vite/src/node/__tests__/plugins/clientInjections.spec.ts > the rolldown dev runtime imports nothing

AssertionError: expected 'import { __exportAll, __reExport, __t…' not to match /^\s*import\b/m - Expected: /^\s*import\b/m + Received: "import { __exportAll, __reExport, __toCommonJS, __toESM } from './experimental-runtime-base.mjs'; // @ts-check class Module { /** * @type {{ exports: any }} */ exportsHolder = { exports: null }; /** * @type {string} */ id; /** * @PARAM {string} id */ constructor(id) { this.id = id; } get exports() { return this.exportsHolder.exports; } } /** * Compiler-emitted module-graph delta — pure topology (static + dynamic edges). * `ids[0, localCount)` are the modules this payload carries; `ids[localCount, …)` are foreign edge targets. * `edges[i]` / `dynamicEdges[i]` are the static / dynamic-`import()` out-edges of `ids[i]`. * @typedef {{ ids: string[], localCount: number, edges: number[][], dynamicEdges?: number[][] }} ModuleGraphDelta * @typedef {{ createModuleHotContext(moduleId: string): any, onModuleCacheRemoval(moduleId: string): void }} DevRuntimeHooks */ export class MissingFactoryError extends Error { /** * @PARAM {string} id */ constructor(id) { super(`No factory registered for module ${id}`); this.id = id; } } export class DevRuntime { /** * Client ID generated at runtime initialization, used for lazy compilation requests. * @type {string} */ clientId; /** * @PARAM {string} clientId */ constructor(clientId) { this.clientId = clientId; } /** * Static import edges from `registerGraph` — entries persist across `removeModuleCache` * and change only by replacement from a newer payload (last write wins). * @type {Map<string, { edges: string[] }>} */ staticImports = new Map(); /** * Reverse index over the static imports. * @type {Map<string, Set<string>>} */ importers = new Map(); /** * Dynamic `import()` edges from `registerGraph`, keyed by importer — mirror of * `staticImports` for the dynamic reverse index. * @type {Map<string, { edges: string[] }>} */ dynamicImports = new Map(); /** * Reverse index over the dynamic imports. * @type {Map<string, Set<string>>} */ dynamicImporters = new Map(); /** * The module cache. Membership means \"this module's side effects ran in this tab\" — * registration is emitted ahead of every module body, and nothing un-registers on * unwind, so a factory that throws mid-body stays registered. A `Map` rather than a * plain object: HMR eviction deletes entries, and a `delete` on an object drops V8 * into dictionary mode, taxing every later lookup on the hottest read path. * @type {Map<string, Module>} */ moduleCache = new Map(); /** * Re-runnable factories from HMR patches and lazy chunks. The initial bundle stays * scope-hoisted and contributes none. * @type {Map<string, { kind: 'esm' | 'cjs', fn: (id: string) => void }>} */ factories = new Map(); /** * Installed by the dev client at boot. The runtime is a store + executor and makes * no HMR decisions; accepting, disposing, and reloading live behind these hooks. * @type {DevRuntimeHooks | null} */ hooks = null; /** * @PARAM {ModuleGraphDelta} delta */ registerGraph(delta) { for (let i = 0; i < delta.localCount; i++) { const id = delta.ids[i]; const edges = delta.edges[i].map((j) => delta.ids[j]); for (const target of this.staticImports.get(id)?.edges ?? []) { this.importers.get(target)?.delete(id); } for (const target of edges) { let importerSet = this.importers.get(target); if (!importerSet) { importerSet = new Set(); this.importers.set(target, importerSet); } importerSet.add(id); } this.staticImports.set(id, { edges }); // Dynamic `import()` edges are maintained in a parallel reverse index with the same // last-write-wins bookkeeping; `getImporters` unions the two. const dynamicEdges = (delta.dynamicEdges?.[i] ?? []).map((j) => delta.ids[j]);

Check failure on line 8 in packages/vite/src/node/__tests__/plugins/clientInjections.spec.ts

View workflow job for this annotation

GitHub Actions / Build&Test: node-22, ubuntu-latest

packages/vite/src/node/__tests__/plugins/clientInjections.spec.ts > the rolldown dev runtime imports nothing

AssertionError: expected 'import { __exportAll, __reExport, __t…' not to match /^\s*import\b/m - Expected: /^\s*import\b/m + Received: "import { __exportAll, __reExport, __toCommonJS, __toESM } from './experimental-runtime-base.mjs'; // @ts-check class Module { /** * @type {{ exports: any }} */ exportsHolder = { exports: null }; /** * @type {string} */ id; /** * @PARAM {string} id */ constructor(id) { this.id = id; } get exports() { return this.exportsHolder.exports; } } /** * Compiler-emitted module-graph delta — pure topology (static + dynamic edges). * `ids[0, localCount)` are the modules this payload carries; `ids[localCount, …)` are foreign edge targets. * `edges[i]` / `dynamicEdges[i]` are the static / dynamic-`import()` out-edges of `ids[i]`. * @typedef {{ ids: string[], localCount: number, edges: number[][], dynamicEdges?: number[][] }} ModuleGraphDelta * @typedef {{ createModuleHotContext(moduleId: string): any, onModuleCacheRemoval(moduleId: string): void }} DevRuntimeHooks */ export class MissingFactoryError extends Error { /** * @PARAM {string} id */ constructor(id) { super(`No factory registered for module ${id}`); this.id = id; } } export class DevRuntime { /** * Client ID generated at runtime initialization, used for lazy compilation requests. * @type {string} */ clientId; /** * @PARAM {string} clientId */ constructor(clientId) { this.clientId = clientId; } /** * Static import edges from `registerGraph` — entries persist across `removeModuleCache` * and change only by replacement from a newer payload (last write wins). * @type {Map<string, { edges: string[] }>} */ staticImports = new Map(); /** * Reverse index over the static imports. * @type {Map<string, Set<string>>} */ importers = new Map(); /** * Dynamic `import()` edges from `registerGraph`, keyed by importer — mirror of * `staticImports` for the dynamic reverse index. * @type {Map<string, { edges: string[] }>} */ dynamicImports = new Map(); /** * Reverse index over the dynamic imports. * @type {Map<string, Set<string>>} */ dynamicImporters = new Map(); /** * The module cache. Membership means \"this module's side effects ran in this tab\" — * registration is emitted ahead of every module body, and nothing un-registers on * unwind, so a factory that throws mid-body stays registered. A `Map` rather than a * plain object: HMR eviction deletes entries, and a `delete` on an object drops V8 * into dictionary mode, taxing every later lookup on the hottest read path. * @type {Map<string, Module>} */ moduleCache = new Map(); /** * Re-runnable factories from HMR patches and lazy chunks. The initial bundle stays * scope-hoisted and contributes none. * @type {Map<string, { kind: 'esm' | 'cjs', fn: (id: string) => void }>} */ factories = new Map(); /** * Installed by the dev client at boot. The runtime is a store + executor and makes * no HMR decisions; accepting, disposing, and reloading live behind these hooks. * @type {DevRuntimeHooks | null} */ hooks = null; /** * @PARAM {ModuleGraphDelta} delta */ registerGraph(delta) { for (let i = 0; i < delta.localCount; i++) { const id = delta.ids[i]; const edges = delta.edges[i].map((j) => delta.ids[j]); for (const target of this.staticImports.get(id)?.edges ?? []) { this.importers.get(target)?.delete(id); } for (const target of edges) { let importerSet = this.importers.get(target); if (!importerSet) { importerSet = new Set(); this.importers.set(target, importerSet); } importerSet.add(id); } this.staticImports.set(id, { edges }); // Dynamic `import()` edges are maintained in a parallel reverse index with the same // last-write-wins bookkeeping; `getImporters` unions the two. const dynamicEdges = (delta.dynamicEdges?.[i] ?? []).map((j) => delta.ids[j]);
expect(source).not.toMatch(/\bfrom\s*['"]/)
})
5 changes: 3 additions & 2 deletions packages/vite/src/node/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,8 +131,9 @@ export const BUNDLED_DEV_CLIENT_ENTRY: string = resolve(
)
/** URL filename the bundled-dev server serves the vite client under */
export const BUNDLED_DEV_CLIENT_FILENAME: string = 'bundledDevClient.mjs'
/** URL directory the bundled-dev server serves rolldown's dev runtime files under */
export const BUNDLED_DEV_ROLLDOWN_RUNTIME_DIR: string = '@rolldown'
/** URL path the bundled-dev server serves rolldown's dev runtime under */
export const BUNDLED_DEV_ROLLDOWN_RUNTIME_FILENAME: string =
'@rolldown/experimental-runtime.mjs'
export const ENV_ENTRY: string = resolve(
VITE_PACKAGE_DIR,
'dist/client/env.mjs',
Expand Down
38 changes: 8 additions & 30 deletions packages/vite/src/node/plugins/clientInjections.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { cleanUrl } from '../../shared/utils'
import type { ResolvedConfig } from '../config'
import {
BUNDLED_DEV_CLIENT_ENTRY,
BUNDLED_DEV_ROLLDOWN_RUNTIME_DIR,
BUNDLED_DEV_ROLLDOWN_RUNTIME_FILENAME,
CLIENT_ENTRY,
ENV_ENTRY,
} from '../constants'
Expand Down Expand Up @@ -159,40 +159,18 @@ export async function getHmrImplementation(
.replace(
new RegExp(`(from\\s*['"])${ROLLDOWN_DEV_RUNTIME_ENTRY}(['"])`),
(_, before, after) =>
`${before}${path.posix.join(
config.base,
BUNDLED_DEV_ROLLDOWN_RUNTIME_DIR,
path.basename(rolldownDevRuntimeEntryPath()),
)}${after}`,
`${before}${path.posix.join(config.base, BUNDLED_DEV_ROLLDOWN_RUNTIME_FILENAME)}${after}`,
)
)
}

function rolldownDevRuntimeEntryPath(): string {
return fileURLToPath(import.meta.resolve(ROLLDOWN_DEV_RUNTIME_ENTRY))
}

/**
* The dev runtime must match the rolldown that generates the bundle, so it is read from the
* installed package at serve time instead of being bundled into the client. The entry imports
* its helper file with a relative path, so both are served under the same directory.
* installed package at serve time instead of being bundled into the client.
*/
export function getRolldownDevRuntimeFiles(): Map<string, string> {
const entry = rolldownDevRuntimeEntryPath()
const dir = path.dirname(entry)
const files = new Map<string, string>()
for (const name of fs.readdirSync(dir)) {
if (name.startsWith('experimental-runtime') && name.endsWith('.mjs')) {
files.set(
`${BUNDLED_DEV_ROLLDOWN_RUNTIME_DIR}/${name}`,
fs.readFileSync(path.join(dir, name), 'utf-8'),
)
}
}
if (
!files.has(`${BUNDLED_DEV_ROLLDOWN_RUNTIME_DIR}/${path.basename(entry)}`)
) {
throw new Error(`rolldown dev runtime entry ${entry} was not found`)
}
return files
export function getRolldownDevRuntime(): string {
return fs.readFileSync(
fileURLToPath(import.meta.resolve(ROLLDOWN_DEV_RUNTIME_ENTRY)),
'utf-8',
)
}
9 changes: 6 additions & 3 deletions packages/vite/src/node/server/bundledDev.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,13 @@ import {
dev,
} from 'rolldown/experimental'
import { ChunkMetadataMap, resolveRolldownOptions } from '../build'
import { BUNDLED_DEV_CLIENT_FILENAME } from '../constants'
import {
BUNDLED_DEV_CLIENT_FILENAME,
BUNDLED_DEV_ROLLDOWN_RUNTIME_FILENAME,
} from '../constants'
import {
getHmrImplementation,
getRolldownDevRuntimeFiles,
getRolldownDevRuntime,
} from '../plugins/clientInjections'
import { createDebugger, formatAndTruncateFileList } from '../utils'
import { convertToDevWatchOptions } from '../watch'
Expand Down Expand Up @@ -376,7 +379,7 @@ export class BundledDev {
BUNDLED_DEV_CLIENT_FILENAME,
await getHmrImplementation(this.environment.getTopLevelConfig()),
],
...getRolldownDevRuntimeFiles(),
[BUNDLED_DEV_ROLLDOWN_RUNTIME_FILENAME, getRolldownDevRuntime()],
])
this.staticFiles.clear()
for (const [fileName, source] of sources) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ if (isBuild) {
expect(await client.text()).toContain(`from "${runtimeUrl.pathname}"`)
const runtime = await page.request.get(runtimeUrl.href)
expect(runtime.status()).toBe(200)
expect(await runtime.text()).toContain('class DevRuntime')
expect(await runtime.text()).toContain('DevRuntime')
const loaded: string[] = await page.evaluate(() =>
performance.getEntriesByType('resource').map((entry) => entry.name),
)
Expand Down
Loading