From e21fabe1936049602e2359418791c6c2fee73aae Mon Sep 17 00:00:00 2001 From: Manuel Schiller Date: Fri, 17 Apr 2026 00:19:46 +0200 Subject: [PATCH 1/5] fix(rsc): include bundled server CSS when cssCodeSplit is false --- .../plugin-rsc/e2e/css-code-split.test.ts | 91 +++++++++ packages/plugin-rsc/src/plugin.ts | 188 +++++++++++------- 2 files changed, 203 insertions(+), 76 deletions(-) create mode 100644 packages/plugin-rsc/e2e/css-code-split.test.ts diff --git a/packages/plugin-rsc/e2e/css-code-split.test.ts b/packages/plugin-rsc/e2e/css-code-split.test.ts new file mode 100644 index 000000000..97c98a07f --- /dev/null +++ b/packages/plugin-rsc/e2e/css-code-split.test.ts @@ -0,0 +1,91 @@ +import fs from 'node:fs' +import path from 'node:path' +import { expect, test } from '@playwright/test' +import { setupInlineFixture, useFixture } from './fixture' +import { waitForHydration } from './helper' +import { defineStarterTest } from './starter' + +// When `build.cssCodeSplit: false`, Vite emits a single consolidated CSS +// bundle asset and no `importedCss` metadata on chunks. The rsc plugin must +// still copy the rsc environment's CSS asset into the client output and +// reference it from server-component resources, otherwise RSC-only CSS +// (e.g. from a server component module) goes missing from the client build. + +test.describe('cssCodeSplit-false', () => { + const root = 'examples/e2e/temp/cssCodeSplit-false' + + test.beforeAll(async () => { + await setupInlineFixture({ + src: 'examples/starter', + dest: root, + files: { + 'vite.config.ts': /* js */ ` + import rsc from '@vitejs/plugin-rsc' + import react from '@vitejs/plugin-react' + import { defineConfig } from 'vite' + + export default defineConfig({ + build: { cssCodeSplit: false }, + plugins: [ + react(), + rsc({ + entries: { + client: './src/framework/entry.browser.tsx', + ssr: './src/framework/entry.ssr.tsx', + rsc: './src/framework/entry.rsc.tsx', + } + }), + ], + }) + `, + // CSS module imported exclusively by a server component module, so its + // styles only reach the client via plugin-rsc's server-resources + // path. + 'src/server-only.module.css': /* css */ ` + .serverOnly { + color: rgb(123, 45, 67); + } + `, + 'src/server-only.tsx': /* js */ ` + import styles from './server-only.module.css' + export function ServerOnly() { + return
rsc-css-only
+ } + `, + 'src/root.tsx': { + edit: (s) => + s + .replace( + `import { ClientCounter } from './client.tsx'`, + `import { ClientCounter } from './client.tsx'\nimport { ServerOnly } from './server-only.tsx'`, + ) + .replace(``, ``), + }, + }, + }) + }) + + test.describe('build', () => { + const f = useFixture({ root, mode: 'build' }) + defineStarterTest(f) + + test('rsc-only css is present in the client output', () => { + const dir = path.join(f.root, 'dist/client/assets') + const cssFiles = fs.readdirSync(dir).filter((n) => n.endsWith('.css')) + const combined = cssFiles + .map((n) => fs.readFileSync(path.join(dir, n), 'utf-8')) + .join('\n') + // minifier may hex-encode; accept either form + expect(combined).toMatch(/rgb\(123,\s*45,\s*67\)|#7b2d43/i) + }) + + test('rsc-only css is applied at runtime', async ({ page }) => { + await page.goto(f.url()) + await waitForHydration(page) + await expect(page.getByTestId('server-only')).toHaveCSS( + 'color', + 'rgb(123, 45, 67)', + ) + }) + }) +}) diff --git a/packages/plugin-rsc/src/plugin.ts b/packages/plugin-rsc/src/plugin.ts index 9141febba..6d8e7d77d 100644 --- a/packages/plugin-rsc/src/plugin.ts +++ b/packages/plugin-rsc/src/plugin.ts @@ -1065,93 +1065,117 @@ export function createRpcClient(params) { }, }, // client build - generateBundle(_options, bundle) { - // copy assets from rsc build to client build - manager.bundles[this.environment.name] = bundle - - if (this.environment.name === 'client') { - const rscBundle = manager.bundles['rsc']! - const assets = new Set( - Object.values(rscBundle).flatMap((output) => - output.type === 'chunk' - ? [ - ...(output.viteMetadata?.importedCss ?? []), - ...(output.viteMetadata?.importedAssets ?? []), - ] - : [], - ), - ) - for (const fileName of assets) { - const asset = rscBundle[fileName] - assert(asset?.type === 'asset') - this.emitFile({ - type: 'asset', - fileName: asset.fileName, - source: asset.source, - }) - } + generateBundle: { + // run after vite's css plugin emits the consolidated stylesheet + // (relevant when `build.cssCodeSplit: false`). + order: 'post', + handler(_options, bundle) { + // copy assets from rsc build to client build + manager.bundles[this.environment.name] = bundle - const serverResources: Record = {} - const rscAssetDeps = collectAssetDeps(manager.bundles['rsc']!) - for (const [id, meta] of Object.entries( - manager.serverResourcesMetaMap, - )) { - serverResources[meta.key] = assetsURLOfDeps( - { - js: [], - css: rscAssetDeps[id]?.deps.css ?? [], - }, - manager, + if (this.environment.name === 'client') { + const rscBundle = manager.bundles['rsc']! + const assets = new Set( + Object.values(rscBundle).flatMap((output) => + output.type === 'chunk' + ? [ + ...(output.viteMetadata?.importedCss ?? []), + ...(output.viteMetadata?.importedAssets ?? []), + ] + : [], + ), ) - } - - const assetDeps = collectAssetDeps(bundle) - let bootstrapScriptContent: string | RuntimeAsset = '' - - const clientReferenceDeps: Record = {} - for (const meta of Object.values(manager.clientReferenceMetaMap)) { - const deps: AssetDeps = assetDeps[meta.groupChunkId!]?.deps ?? { - js: [], - css: [], + // when the rsc environment has `cssCodeSplit: false`, Vite emits a + // single bundled CSS asset and chunks carry no `importedCss` metadata. + // Pick the bundled CSS asset(s) directly so they get copied and + // referenced by server resources. + const rscCssCodeSplit = + manager.config.environments.rsc?.build.cssCodeSplit + const rscBundledCssFileNames = + rscCssCodeSplit === false + ? collectBundledCssAssetFileNames(rscBundle) + : [] + for (const fileName of rscBundledCssFileNames) { + assets.add(fileName) + } + for (const fileName of assets) { + const asset = rscBundle[fileName] + assert(asset?.type === 'asset') + this.emitFile({ + type: 'asset', + fileName: asset.fileName, + source: asset.source, + }) } - clientReferenceDeps[meta.referenceKey] = assetsURLOfDeps( - deps, - manager, - ) - } - // When customClientEntry is enabled, don't require "index" entry - // and don't merge entry deps into client references - if (!rscPluginOptions.customClientEntry) { - const entry = Object.values(assetDeps).find( - (v) => v.chunk.name === 'index' && v.chunk.isEntry, - ) - if (!entry) { - throw new Error( - `[vite-rsc] Client build must have an entry chunk named "index". Use 'customClientEntry' option to disable this requirement.`, + const serverResources: Record = {} + const rscAssetDeps = collectAssetDeps(manager.bundles['rsc']!) + for (const [id, meta] of Object.entries( + manager.serverResourcesMetaMap, + )) { + const cssDeps = new Set(rscAssetDeps[id]?.deps.css ?? []) + if (rscCssCodeSplit === false) { + for (const fileName of rscBundledCssFileNames) { + cssDeps.add(fileName) + } + } + serverResources[meta.key] = assetsURLOfDeps( + { + js: [], + css: [...cssDeps], + }, + manager, ) } - const entryDeps = assetsURLOfDeps(entry.deps, manager) - for (const [key, deps] of Object.entries(clientReferenceDeps)) { - clientReferenceDeps[key] = mergeAssetDeps(deps, entryDeps) + + const assetDeps = collectAssetDeps(bundle) + let bootstrapScriptContent: string | RuntimeAsset = '' + + const clientReferenceDeps: Record = {} + for (const meta of Object.values(manager.clientReferenceMetaMap)) { + const deps: AssetDeps = assetDeps[meta.groupChunkId!]?.deps ?? { + js: [], + css: [], + } + clientReferenceDeps[meta.referenceKey] = assetsURLOfDeps( + deps, + manager, + ) } - const entryUrl = assetsURL(entry.chunk.fileName, manager) - if (typeof entryUrl === 'string') { - bootstrapScriptContent = `import(${JSON.stringify(entryUrl)})` - } else { - bootstrapScriptContent = new RuntimeAsset( - `"import(" + JSON.stringify(${entryUrl.runtime}) + ")"`, + + // When customClientEntry is enabled, don't require "index" entry + // and don't merge entry deps into client references + if (!rscPluginOptions.customClientEntry) { + const entry = Object.values(assetDeps).find( + (v) => v.chunk.name === 'index' && v.chunk.isEntry, ) + if (!entry) { + throw new Error( + `[vite-rsc] Client build must have an entry chunk named "index". Use 'customClientEntry' option to disable this requirement.`, + ) + } + const entryDeps = assetsURLOfDeps(entry.deps, manager) + for (const [key, deps] of Object.entries(clientReferenceDeps)) { + clientReferenceDeps[key] = mergeAssetDeps(deps, entryDeps) + } + const entryUrl = assetsURL(entry.chunk.fileName, manager) + if (typeof entryUrl === 'string') { + bootstrapScriptContent = `import(${JSON.stringify(entryUrl)})` + } else { + bootstrapScriptContent = new RuntimeAsset( + `"import(" + JSON.stringify(${entryUrl.runtime}) + ")"`, + ) + } } - } - manager.buildAssetsManifest = { - bootstrapScriptContent, - clientReferenceDeps, - serverResources, - cssLinkPrecedence: rscPluginOptions.cssLinkPrecedence, + manager.buildAssetsManifest = { + bootstrapScriptContent, + clientReferenceDeps, + serverResources, + cssLinkPrecedence: rscPluginOptions.cssLinkPrecedence, + } } - } + }, }, // non-client builds can load assets manifest as external renderChunk(code, chunk) { @@ -2172,6 +2196,18 @@ function collectAssetDepsInner( } } +function collectBundledCssAssetFileNames( + bundle: Rollup.OutputBundle, +): string[] { + return Object.values(bundle) + .filter( + (output): output is Rollup.OutputAsset => + output.type === 'asset' && + output.originalFileNames?.includes('style.css'), + ) + .map((output) => output.fileName) +} + // // css support // From 36f0bd2f90bd65c4b32e5f9069fe271bc825f071 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Thu, 23 Apr 2026 18:16:31 +0900 Subject: [PATCH 2/5] refactor: simplify --- packages/plugin-rsc/src/plugin.ts | 220 +++++++++++++++--------------- 1 file changed, 110 insertions(+), 110 deletions(-) diff --git a/packages/plugin-rsc/src/plugin.ts b/packages/plugin-rsc/src/plugin.ts index 6d8e7d77d..ba546297a 100644 --- a/packages/plugin-rsc/src/plugin.ts +++ b/packages/plugin-rsc/src/plugin.ts @@ -1032,6 +1032,21 @@ export function createRpcClient(params) { }, }, }, + { + // keep track of build output for each environment build to generate + // assets manaifest and environment imports manifest. + // use `order: "post"` to include all chunks/assets + // generated by other plugins + // (e.g. `styles.css` generated by `vite:css-post` when `cssCodeSplit: false`). + // https://github.com/vitejs/vite/blob/a19003516951a3710aab0f2646d78c48b2e5d2ad/packages/vite/src/node/plugins/css.ts#L1040 + name: 'rsc:virtual:vite-rsc/track-bundles', + generateBundle: { + order: 'post', + handler(_options, bundle) { + manager.bundles[this.environment.name] = bundle + }, + }, + }, { name: 'rsc:virtual:vite-rsc/assets-manifest', resolveId: { @@ -1065,117 +1080,114 @@ export function createRpcClient(params) { }, }, // client build - generateBundle: { - // run after vite's css plugin emits the consolidated stylesheet - // (relevant when `build.cssCodeSplit: false`). - order: 'post', - handler(_options, bundle) { - // copy assets from rsc build to client build - manager.bundles[this.environment.name] = bundle - - if (this.environment.name === 'client') { - const rscBundle = manager.bundles['rsc']! - const assets = new Set( - Object.values(rscBundle).flatMap((output) => - output.type === 'chunk' - ? [ - ...(output.viteMetadata?.importedCss ?? []), - ...(output.viteMetadata?.importedAssets ?? []), - ] - : [], - ), - ) - // when the rsc environment has `cssCodeSplit: false`, Vite emits a - // single bundled CSS asset and chunks carry no `importedCss` metadata. - // Pick the bundled CSS asset(s) directly so they get copied and - // referenced by server resources. - const rscCssCodeSplit = - manager.config.environments.rsc?.build.cssCodeSplit - const rscBundledCssFileNames = - rscCssCodeSplit === false - ? collectBundledCssAssetFileNames(rscBundle) - : [] - for (const fileName of rscBundledCssFileNames) { - assets.add(fileName) - } - for (const fileName of assets) { - const asset = rscBundle[fileName] - assert(asset?.type === 'asset') - this.emitFile({ - type: 'asset', - fileName: asset.fileName, - source: asset.source, - }) + generateBundle(_options, bundle) { + // copy assets from rsc build to client build + if (this.environment.name === 'client') { + const rscBundle = manager.bundles['rsc']! + + // when css code split is disabled, treat vite's single css bundle `style.css` + // as dependency of all server chunks + if (!manager.config.environments.rsc!.build.cssCodeSplit) { + let cssBundleName: string | undefined + for (const output of Object.values(rscBundle)) { + if ( + output.type === 'asset' && + output.names.includes('style.css') + ) { + cssBundleName = output.fileName + break + } } - - const serverResources: Record = {} - const rscAssetDeps = collectAssetDeps(manager.bundles['rsc']!) - for (const [id, meta] of Object.entries( - manager.serverResourcesMetaMap, - )) { - const cssDeps = new Set(rscAssetDeps[id]?.deps.css ?? []) - if (rscCssCodeSplit === false) { - for (const fileName of rscBundledCssFileNames) { - cssDeps.add(fileName) + if (cssBundleName) { + for (const output of Object.values(rscBundle)) { + if (output.type === 'chunk') { + output.viteMetadata!.importedCss.add(cssBundleName) } } - serverResources[meta.key] = assetsURLOfDeps( - { - js: [], - css: [...cssDeps], - }, - manager, - ) } + } - const assetDeps = collectAssetDeps(bundle) - let bootstrapScriptContent: string | RuntimeAsset = '' + const assets = new Set( + Object.values(rscBundle).flatMap((output) => + output.type === 'chunk' + ? [ + ...(output.viteMetadata?.importedCss ?? []), + ...(output.viteMetadata?.importedAssets ?? []), + ] + : [], + ), + ) + for (const fileName of assets) { + const asset = rscBundle[fileName] + assert(asset?.type === 'asset') + this.emitFile({ + type: 'asset', + fileName: asset.fileName, + source: asset.source, + }) + } - const clientReferenceDeps: Record = {} - for (const meta of Object.values(manager.clientReferenceMetaMap)) { - const deps: AssetDeps = assetDeps[meta.groupChunkId!]?.deps ?? { + const serverResources: Record = {} + const rscAssetDeps = collectAssetDeps(rscBundle) + for (const [id, meta] of Object.entries( + manager.serverResourcesMetaMap, + )) { + serverResources[meta.key] = assetsURLOfDeps( + { js: [], - css: [], - } - clientReferenceDeps[meta.referenceKey] = assetsURLOfDeps( - deps, - manager, - ) + css: rscAssetDeps[id]?.deps.css ?? [], + }, + manager, + ) + } + + const assetDeps = collectAssetDeps(bundle) + let bootstrapScriptContent: string | RuntimeAsset = '' + + const clientReferenceDeps: Record = {} + for (const meta of Object.values(manager.clientReferenceMetaMap)) { + const deps: AssetDeps = assetDeps[meta.groupChunkId!]?.deps ?? { + js: [], + css: [], } + clientReferenceDeps[meta.referenceKey] = assetsURLOfDeps( + deps, + manager, + ) + } - // When customClientEntry is enabled, don't require "index" entry - // and don't merge entry deps into client references - if (!rscPluginOptions.customClientEntry) { - const entry = Object.values(assetDeps).find( - (v) => v.chunk.name === 'index' && v.chunk.isEntry, + // When customClientEntry is enabled, don't require "index" entry + // and don't merge entry deps into client references + if (!rscPluginOptions.customClientEntry) { + const entry = Object.values(assetDeps).find( + (v) => v.chunk.name === 'index' && v.chunk.isEntry, + ) + if (!entry) { + throw new Error( + `[vite-rsc] Client build must have an entry chunk named "index". Use 'customClientEntry' option to disable this requirement.`, ) - if (!entry) { - throw new Error( - `[vite-rsc] Client build must have an entry chunk named "index". Use 'customClientEntry' option to disable this requirement.`, - ) - } - const entryDeps = assetsURLOfDeps(entry.deps, manager) - for (const [key, deps] of Object.entries(clientReferenceDeps)) { - clientReferenceDeps[key] = mergeAssetDeps(deps, entryDeps) - } - const entryUrl = assetsURL(entry.chunk.fileName, manager) - if (typeof entryUrl === 'string') { - bootstrapScriptContent = `import(${JSON.stringify(entryUrl)})` - } else { - bootstrapScriptContent = new RuntimeAsset( - `"import(" + JSON.stringify(${entryUrl.runtime}) + ")"`, - ) - } } - - manager.buildAssetsManifest = { - bootstrapScriptContent, - clientReferenceDeps, - serverResources, - cssLinkPrecedence: rscPluginOptions.cssLinkPrecedence, + const entryDeps = assetsURLOfDeps(entry.deps, manager) + for (const [key, deps] of Object.entries(clientReferenceDeps)) { + clientReferenceDeps[key] = mergeAssetDeps(deps, entryDeps) + } + const entryUrl = assetsURL(entry.chunk.fileName, manager) + if (typeof entryUrl === 'string') { + bootstrapScriptContent = `import(${JSON.stringify(entryUrl)})` + } else { + bootstrapScriptContent = new RuntimeAsset( + `"import(" + JSON.stringify(${entryUrl.runtime}) + ")"`, + ) } } - }, + + manager.buildAssetsManifest = { + bootstrapScriptContent, + clientReferenceDeps, + serverResources, + cssLinkPrecedence: rscPluginOptions.cssLinkPrecedence, + } + } }, // non-client builds can load assets manifest as external renderChunk(code, chunk) { @@ -2196,18 +2208,6 @@ function collectAssetDepsInner( } } -function collectBundledCssAssetFileNames( - bundle: Rollup.OutputBundle, -): string[] { - return Object.values(bundle) - .filter( - (output): output is Rollup.OutputAsset => - output.type === 'asset' && - output.originalFileNames?.includes('style.css'), - ) - .map((output) => output.fileName) -} - // // css support // From 42abb9cbc09c9b236d006766cf1d25c8dd2cfebb Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Thu, 23 Apr 2026 18:25:17 +0900 Subject: [PATCH 3/5] test: simplify --- .../plugin-rsc/e2e/css-code-split.test.ts | 58 ++++++------------- 1 file changed, 18 insertions(+), 40 deletions(-) diff --git a/packages/plugin-rsc/e2e/css-code-split.test.ts b/packages/plugin-rsc/e2e/css-code-split.test.ts index 97c98a07f..ffe097db0 100644 --- a/packages/plugin-rsc/e2e/css-code-split.test.ts +++ b/packages/plugin-rsc/e2e/css-code-split.test.ts @@ -1,16 +1,8 @@ -import fs from 'node:fs' -import path from 'node:path' import { expect, test } from '@playwright/test' import { setupInlineFixture, useFixture } from './fixture' import { waitForHydration } from './helper' import { defineStarterTest } from './starter' -// When `build.cssCodeSplit: false`, Vite emits a single consolidated CSS -// bundle asset and no `importedCss` metadata on chunks. The rsc plugin must -// still copy the rsc environment's CSS asset into the client output and -// reference it from server-component resources, otherwise RSC-only CSS -// (e.g. from a server component module) goes missing from the client build. - test.describe('cssCodeSplit-false', () => { const root = 'examples/e2e/temp/cssCodeSplit-false' @@ -19,28 +11,20 @@ test.describe('cssCodeSplit-false', () => { src: 'examples/starter', dest: root, files: { + 'vite.config.base.ts': { cp: 'vite.config.ts' }, 'vite.config.ts': /* js */ ` - import rsc from '@vitejs/plugin-rsc' - import react from '@vitejs/plugin-react' - import { defineConfig } from 'vite' + import { defineConfig, mergeConfig } from 'vite' + import baseConfig from './vite.config.base.ts' - export default defineConfig({ - build: { cssCodeSplit: false }, - plugins: [ - react(), - rsc({ - entries: { - client: './src/framework/entry.browser.tsx', - ssr: './src/framework/entry.ssr.tsx', - rsc: './src/framework/entry.rsc.tsx', - } - }), - ], + const overrideConfig = defineConfig({ + build: { + cssCodeSplit: false, + }, }) + + export default mergeConfig(baseConfig, overrideConfig) `, - // CSS module imported exclusively by a server component module, so its - // styles only reach the client via plugin-rsc's server-resources - // path. + // test server css module 'src/server-only.module.css': /* css */ ` .serverOnly { color: rgb(123, 45, 67); @@ -49,7 +33,11 @@ test.describe('cssCodeSplit-false', () => { 'src/server-only.tsx': /* js */ ` import styles from './server-only.module.css' export function ServerOnly() { - return
rsc-css-only
+ return ( + + ) } `, 'src/root.tsx': { @@ -57,7 +45,8 @@ test.describe('cssCodeSplit-false', () => { s .replace( `import { ClientCounter } from './client.tsx'`, - `import { ClientCounter } from './client.tsx'\nimport { ServerOnly } from './server-only.tsx'`, + `import { ClientCounter } from './client.tsx'; + import { ServerOnly } from './server-only.tsx'`, ) .replace(``, ``), }, @@ -69,19 +58,8 @@ test.describe('cssCodeSplit-false', () => { const f = useFixture({ root, mode: 'build' }) defineStarterTest(f) - test('rsc-only css is present in the client output', () => { - const dir = path.join(f.root, 'dist/client/assets') - const cssFiles = fs.readdirSync(dir).filter((n) => n.endsWith('.css')) - const combined = cssFiles - .map((n) => fs.readFileSync(path.join(dir, n), 'utf-8')) - .join('\n') - // minifier may hex-encode; accept either form - expect(combined).toMatch(/rgb\(123,\s*45,\s*67\)|#7b2d43/i) - }) - - test('rsc-only css is applied at runtime', async ({ page }) => { + test('server css module', async ({ page }) => { await page.goto(f.url()) - await waitForHydration(page) await expect(page.getByTestId('server-only')).toHaveCSS( 'color', 'rgb(123, 45, 67)', From cf2d15dd0e670646f844511b5797420d5a7bdaf8 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Thu, 23 Apr 2026 18:25:31 +0900 Subject: [PATCH 4/5] chore: lint --- packages/plugin-rsc/e2e/css-code-split.test.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/plugin-rsc/e2e/css-code-split.test.ts b/packages/plugin-rsc/e2e/css-code-split.test.ts index ffe097db0..9f933f6c3 100644 --- a/packages/plugin-rsc/e2e/css-code-split.test.ts +++ b/packages/plugin-rsc/e2e/css-code-split.test.ts @@ -1,6 +1,5 @@ import { expect, test } from '@playwright/test' import { setupInlineFixture, useFixture } from './fixture' -import { waitForHydration } from './helper' import { defineStarterTest } from './starter' test.describe('cssCodeSplit-false', () => { From fa9949ce6a3b967b3d5e802a27044902f695bf99 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Thu, 23 Apr 2026 18:32:08 +0900 Subject: [PATCH 5/5] chore: comment --- packages/plugin-rsc/e2e/css-code-split.test.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/plugin-rsc/e2e/css-code-split.test.ts b/packages/plugin-rsc/e2e/css-code-split.test.ts index 9f933f6c3..09dbe4842 100644 --- a/packages/plugin-rsc/e2e/css-code-split.test.ts +++ b/packages/plugin-rsc/e2e/css-code-split.test.ts @@ -23,7 +23,8 @@ test.describe('cssCodeSplit-false', () => { export default mergeConfig(baseConfig, overrideConfig) `, - // test server css module + // test server css module too + // (starter example already tests normal server css) 'src/server-only.module.css': /* css */ ` .serverOnly { color: rgb(123, 45, 67);