From ea18a738c701eac36d880235427ca210d1911cf8 Mon Sep 17 00:00:00 2001 From: Neo Nie Date: Mon, 20 Jul 2026 22:49:49 +0100 Subject: [PATCH 1/3] fix(rsc): handle client packages outside root --- packages/plugin-rsc/examples/basic/vite.config.ts | 15 +++++++++++++++ packages/plugin-rsc/src/plugin.ts | 9 ++++++++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/packages/plugin-rsc/examples/basic/vite.config.ts b/packages/plugin-rsc/examples/basic/vite.config.ts index d974b3ea3..1efe53415 100644 --- a/packages/plugin-rsc/examples/basic/vite.config.ts +++ b/packages/plugin-rsc/examples/basic/vite.config.ts @@ -32,6 +32,21 @@ export default defineConfig({ react(), vitePluginUseCache(), vitePluginVirtualModuleTest(), + { + name: 'test-root-package-resolution-importer', + enforce: 'pre', + resolveId(source, importer) { + // Root package probes need a concrete importer so ownership-sensitive + // resolvers keep the lookup anchored to the project. + if ( + this.environment.name === 'rsc' && + source === '@vitejs/test-dep-transitive-client/client' && + importer == null + ) { + throw new Error('root package resolution requires an importer') + } + }, + }, rsc({ entries: { client: './src/framework/entry.browser.tsx', diff --git a/packages/plugin-rsc/src/plugin.ts b/packages/plugin-rsc/src/plugin.ts index 86cbba631..c08a1ce09 100644 --- a/packages/plugin-rsc/src/plugin.ts +++ b/packages/plugin-rsc/src/plugin.ts @@ -1719,9 +1719,16 @@ function vitePluginUseClient( // transitive dependency reachable from `importer` but not installed // at the root (see #1247). In that case, skip virtualization and // let it fall back to referencing the fully resolved module id. + // Rolldown preserves an undefined importer, so use Vite's + // conventional root importer to keep importer-sensitive + // resolution anchored to the project root. + const rootImporter = path.join( + this.environment.config.root, + 'index.html', + ) const resolvedAtRoot = await this.resolve( source, - undefined, + rootImporter, options, ) if (!resolvedAtRoot || resolvedAtRoot.id !== resolved.id) { From e38b9b889ca0c1f9ec38ea5ac4c9dc6e7a4d45a4 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa <4232207+hi-ogawa@users.noreply.github.com> Date: Thu, 23 Jul 2026 09:22:23 +0900 Subject: [PATCH 2/3] chore(rsc): clean up root resolution test Co-authored-by: OpenCode --- .../plugin-rsc/examples/basic/vite.config.ts | 34 +++++++++++-------- packages/plugin-rsc/src/plugin.ts | 5 ++- 2 files changed, 21 insertions(+), 18 deletions(-) diff --git a/packages/plugin-rsc/examples/basic/vite.config.ts b/packages/plugin-rsc/examples/basic/vite.config.ts index 1efe53415..7211cb2bb 100644 --- a/packages/plugin-rsc/examples/basic/vite.config.ts +++ b/packages/plugin-rsc/examples/basic/vite.config.ts @@ -32,21 +32,7 @@ export default defineConfig({ react(), vitePluginUseCache(), vitePluginVirtualModuleTest(), - { - name: 'test-root-package-resolution-importer', - enforce: 'pre', - resolveId(source, importer) { - // Root package probes need a concrete importer so ownership-sensitive - // resolvers keep the lookup anchored to the project. - if ( - this.environment.name === 'rsc' && - source === '@vitejs/test-dep-transitive-client/client' && - importer == null - ) { - throw new Error('root package resolution requires an importer') - } - }, - }, + testRootPackageResolutionImporterPlugin(), rsc({ entries: { client: './src/framework/entry.browser.tsx', @@ -286,6 +272,24 @@ export default { fetch: handler }; }, }) as any +function testRootPackageResolutionImporterPlugin(): Plugin { + return { + name: 'test-root-package-resolution-importer', + enforce: 'pre', + resolveId(source, importer) { + // Root package probes need a concrete importer so ownership-sensitive + // resolvers keep the lookup anchored to the project. + if ( + this.environment.name === 'rsc' && + source === '@vitejs/test-dep-transitive-client/client' && + importer == null + ) { + throw new Error('root package resolution requires an importer') + } + }, + } +} + function testBuildPlugin(): Plugin[] { const moduleIds: { name: string; ids: string[] }[] = [] return [ diff --git a/packages/plugin-rsc/src/plugin.ts b/packages/plugin-rsc/src/plugin.ts index c08a1ce09..f8c300682 100644 --- a/packages/plugin-rsc/src/plugin.ts +++ b/packages/plugin-rsc/src/plugin.ts @@ -1719,9 +1719,8 @@ function vitePluginUseClient( // transitive dependency reachable from `importer` but not installed // at the root (see #1247). In that case, skip virtualization and // let it fall back to referencing the fully resolved module id. - // Rolldown preserves an undefined importer, so use Vite's - // conventional root importer to keep importer-sensitive - // resolution anchored to the project root. + // Use Vite's conventional root importer so every resolver in the + // plugin chain evaluates the package from the project root. const rootImporter = path.join( this.environment.config.root, 'index.html', From f3c5b3be28163d98759a1ddab2a61baccbca73f2 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa <4232207+hi-ogawa@users.noreply.github.com> Date: Thu, 23 Jul 2026 09:27:31 +0900 Subject: [PATCH 3/3] chore(rsc): clarify root importer guard Co-authored-by: OpenCode --- packages/plugin-rsc/examples/basic/vite.config.ts | 13 +++++++------ packages/plugin-rsc/src/plugin.ts | 4 ++-- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/packages/plugin-rsc/examples/basic/vite.config.ts b/packages/plugin-rsc/examples/basic/vite.config.ts index 7211cb2bb..72d42a074 100644 --- a/packages/plugin-rsc/examples/basic/vite.config.ts +++ b/packages/plugin-rsc/examples/basic/vite.config.ts @@ -32,7 +32,7 @@ export default defineConfig({ react(), vitePluginUseCache(), vitePluginVirtualModuleTest(), - testRootPackageResolutionImporterPlugin(), + testRscVirtualClientPackagePlugin(), rsc({ entries: { client: './src/framework/entry.browser.tsx', @@ -272,19 +272,20 @@ export default { fetch: handler }; }, }) as any -function testRootPackageResolutionImporterPlugin(): Plugin { +function testRscVirtualClientPackagePlugin(): Plugin { return { - name: 'test-root-package-resolution-importer', + name: 'test-rsc-virtual-client-package', enforce: 'pre', resolveId(source, importer) { - // Root package probes need a concrete importer so ownership-sensitive - // resolvers keep the lookup anchored to the project. + // `rsc:virtual-client-package` should pass an explicit root importer. if ( this.environment.name === 'rsc' && source === '@vitejs/test-dep-transitive-client/client' && importer == null ) { - throw new Error('root package resolution requires an importer') + throw new Error( + '`rsc:virtual-client-package` root resolution requires an importer', + ) } }, } diff --git a/packages/plugin-rsc/src/plugin.ts b/packages/plugin-rsc/src/plugin.ts index f8c300682..4504265e8 100644 --- a/packages/plugin-rsc/src/plugin.ts +++ b/packages/plugin-rsc/src/plugin.ts @@ -1719,8 +1719,8 @@ function vitePluginUseClient( // transitive dependency reachable from `importer` but not installed // at the root (see #1247). In that case, skip virtualization and // let it fall back to referencing the fully resolved module id. - // Use Vite's conventional root importer so every resolver in the - // plugin chain evaluates the package from the project root. + // Vite's resolver already treats an undefined importer as the project + // root, but other plugins may not, so pass the root importer explicitly. const rootImporter = path.join( this.environment.config.root, 'index.html',