From 58711b64de6d2afb7bffb789614a99fc23b4e54d Mon Sep 17 00:00:00 2001 From: sapphi-red <49056869+sapphi-red@users.noreply.github.com> Date: Thu, 12 Mar 2026 12:46:10 +0900 Subject: [PATCH 1/2] fix: always use ESM Oxc runtime --- packages/vite/src/node/plugins/oxc.ts | 23 ++++++++++++++----- packages/vite/src/node/plugins/resolve.ts | 5 +++- .../__tests__/fixtures/oxc-runtime-helper.ts | 6 +++++ .../runtime/__tests__/server-runtime.spec.ts | 6 +++++ 4 files changed, 33 insertions(+), 7 deletions(-) create mode 100644 packages/vite/src/node/ssr/runtime/__tests__/fixtures/oxc-runtime-helper.ts diff --git a/packages/vite/src/node/plugins/oxc.ts b/packages/vite/src/node/plugins/oxc.ts index e0e30e8224d7b9..cea5f7778a343b 100644 --- a/packages/vite/src/node/plugins/oxc.ts +++ b/packages/vite/src/node/plugins/oxc.ts @@ -18,6 +18,7 @@ import type { ViteDevServer } from '../server' import { JS_TYPES_RE, VITE_PACKAGE_DIR } from '../constants' import type { Logger } from '../logger' import { type ESBuildOptions, getTSConfigResolutionCache } from './esbuild' +import { getRealPath } from './resolve' // IIFE content looks like `var MyLib = (function() {` or `this.nested.myLib = (function() {`. export const IIFE_BEGIN_RE: RegExp = @@ -299,8 +300,9 @@ export function oxcPlugin(config: ResolvedConfig): Plugin { return result } - const runtimeResolveBase = normalizePath( - path.join(VITE_PACKAGE_DIR, 'package.json'), + const runtimePackageDir = getRealPath( + path.join(VITE_PACKAGE_DIR, 'node_modules', '@oxc-project', 'runtime'), + config.resolve.preserveSymlinks, ) let server: ViteDevServer @@ -315,13 +317,22 @@ export function oxcPlugin(config: ResolvedConfig): Plugin { ? { resolveId: { filter: { - id: prefixRegex('@oxc-project/runtime/'), + id: prefixRegex('@oxc-project/runtime/helpers/'), }, - async handler(id, _importer, opts) { + async handler(id, _importer, _opts) { // @oxc-project/runtime imports will be injected by Oxc transform // since it's injected by the transform, @oxc-project/runtime should be resolved to the one Vite depends on - const resolved = await this.resolve(id, runtimeResolveBase, opts) - return resolved + const helperName = id.slice( + '@oxc-project/runtime/helpers/'.length, + ) + // Rolldown always uses the esm version of @oxc-project/runtime + // https://github.com/rolldown/rolldown/blob/v1.0.0-rc.9/crates/rolldown_plugin_oxc_runtime/src/lib.rs#L27 + const helperId = path.posix.join( + runtimePackageDir, + 'src/helpers/esm', + helperName + (id.endsWith('.js') ? '' : '.js'), + ) + return helperId }, order: 'pre', }, diff --git a/packages/vite/src/node/plugins/resolve.ts b/packages/vite/src/node/plugins/resolve.ts index 89ed17efaac477..26a233ed635d98 100644 --- a/packages/vite/src/node/plugins/resolve.ts +++ b/packages/vite/src/node/plugins/resolve.ts @@ -1218,7 +1218,10 @@ function tryResolveRealFileOrType( return } -function getRealPath(resolved: string, preserveSymlinks?: boolean): string { +export function getRealPath( + resolved: string, + preserveSymlinks?: boolean, +): string { if (!preserveSymlinks) { resolved = safeRealpathSync(resolved) } diff --git a/packages/vite/src/node/ssr/runtime/__tests__/fixtures/oxc-runtime-helper.ts b/packages/vite/src/node/ssr/runtime/__tests__/fixtures/oxc-runtime-helper.ts new file mode 100644 index 00000000000000..c4e5d4d23c5fec --- /dev/null +++ b/packages/vite/src/node/ssr/runtime/__tests__/fixtures/oxc-runtime-helper.ts @@ -0,0 +1,6 @@ +function html(strings: TemplateStringsArray, ...values: unknown[]) { + return strings.join('') +} +export const result = html`` diff --git a/packages/vite/src/node/ssr/runtime/__tests__/server-runtime.spec.ts b/packages/vite/src/node/ssr/runtime/__tests__/server-runtime.spec.ts index 5114dab3b593bc..6cab0c63ed37c0 100644 --- a/packages/vite/src/node/ssr/runtime/__tests__/server-runtime.spec.ts +++ b/packages/vite/src/node/ssr/runtime/__tests__/server-runtime.spec.ts @@ -12,6 +12,7 @@ describe('module runner initialization', async () => { const it = await createModuleRunnerTester({ resolve: { external: ['tinyglobby'], + noExternal: ['@oxc-project/runtime'], }, }) @@ -396,6 +397,11 @@ describe('module runner initialization', async () => { ) }) + it('oxc runtime helpers are loadable', async ({ runner }) => { + const mod = await runner.import('/fixtures/oxc-runtime-helper.ts') + expect(mod.result).toBe("") + }) + it(`handle Object variable`, async ({ runner }) => { const mod = await runner.import('/fixtures/top-level-object.js') expect(mod).toMatchInlineSnapshot(` From 9d664a5e3a1fd480895989731592ecb9be4811a4 Mon Sep 17 00:00:00 2001 From: sapphi-red <49056869+sapphi-red@users.noreply.github.com> Date: Thu, 12 Mar 2026 12:55:54 +0900 Subject: [PATCH 2/2] chore: update --- .../src/node/ssr/runtime/__tests__/server-runtime.spec.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/vite/src/node/ssr/runtime/__tests__/server-runtime.spec.ts b/packages/vite/src/node/ssr/runtime/__tests__/server-runtime.spec.ts index 6cab0c63ed37c0..b659dd67f9f6c3 100644 --- a/packages/vite/src/node/ssr/runtime/__tests__/server-runtime.spec.ts +++ b/packages/vite/src/node/ssr/runtime/__tests__/server-runtime.spec.ts @@ -399,7 +399,11 @@ describe('module runner initialization', async () => { it('oxc runtime helpers are loadable', async ({ runner }) => { const mod = await runner.import('/fixtures/oxc-runtime-helper.ts') - expect(mod.result).toBe("") + expect(mod.result).toMatchInlineSnapshot(` + "" + `) }) it(`handle Object variable`, async ({ runner }) => {