From 82e9fef6ec11493cfc4b27cbe2812bd31f000236 Mon Sep 17 00:00:00 2001 From: Hana Date: Fri, 25 Sep 2026 18:50:48 +0800 Subject: [PATCH] refactor(bundled-dev): serve the rolldown dev runtime as a single file --- packages/vite/rolldown.config.ts | 2 +- .../plugins/clientInjections.spec.ts | 22 ++++------- packages/vite/src/node/constants.ts | 5 ++- .../vite/src/node/plugins/clientInjections.ts | 38 ++++--------------- packages/vite/src/node/server/bundledDev.ts | 9 +++-- .../__tests__/hmr-full-bundle-mode.spec.ts | 2 +- 6 files changed, 26 insertions(+), 52 deletions(-) diff --git a/packages/vite/rolldown.config.ts b/packages/vite/rolldown.config.ts index 52a0f0526c60b9..336664b661847d 100644 --- a/packages/vite/rolldown.config.ts +++ b/packages/vite/rolldown.config.ts @@ -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'), diff --git a/packages/vite/src/node/__tests__/plugins/clientInjections.spec.ts b/packages/vite/src/node/__tests__/plugins/clientInjections.spec.ts index 172d3cba589fdf..253eb639e1660c 100644 --- a/packages/vite/src/node/__tests__/plugins/clientInjections.spec.ts +++ b/packages/vite/src/node/__tests__/plugins/clientInjections.spec.ts @@ -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) + expect(source).not.toMatch(/\bfrom\s*['"]/) }) diff --git a/packages/vite/src/node/constants.ts b/packages/vite/src/node/constants.ts index 751deab5e64bcb..2e34e42340b457 100644 --- a/packages/vite/src/node/constants.ts +++ b/packages/vite/src/node/constants.ts @@ -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', diff --git a/packages/vite/src/node/plugins/clientInjections.ts b/packages/vite/src/node/plugins/clientInjections.ts index e55e1e4a778052..5232ce9b581a28 100644 --- a/packages/vite/src/node/plugins/clientInjections.ts +++ b/packages/vite/src/node/plugins/clientInjections.ts @@ -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' @@ -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 { - const entry = rolldownDevRuntimeEntryPath() - const dir = path.dirname(entry) - const files = new Map() - 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', + ) } diff --git a/packages/vite/src/node/server/bundledDev.ts b/packages/vite/src/node/server/bundledDev.ts index e5415d62fadc5d..18d6849edbaa0d 100644 --- a/packages/vite/src/node/server/bundledDev.ts +++ b/packages/vite/src/node/server/bundledDev.ts @@ -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' @@ -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) { diff --git a/playground/hmr-full-bundle-mode/__tests__/hmr-full-bundle-mode.spec.ts b/playground/hmr-full-bundle-mode/__tests__/hmr-full-bundle-mode.spec.ts index 45dcaee5172327..447031b45841a8 100644 --- a/playground/hmr-full-bundle-mode/__tests__/hmr-full-bundle-mode.spec.ts +++ b/playground/hmr-full-bundle-mode/__tests__/hmr-full-bundle-mode.spec.ts @@ -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), )