Skip to content

Commit 2845b9f

Browse files
fix(rsc): include bundled server CSS when cssCodeSplit is false (#1192)
Co-authored-by: Hiroshi Ogawa <hi.ogawa.zz@gmail.com>
1 parent 323ccd7 commit 2845b9f

2 files changed

Lines changed: 108 additions & 3 deletions

File tree

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import { expect, test } from '@playwright/test'
2+
import { setupInlineFixture, useFixture } from './fixture'
3+
import { defineStarterTest } from './starter'
4+
5+
test.describe('cssCodeSplit-false', () => {
6+
const root = 'examples/e2e/temp/cssCodeSplit-false'
7+
8+
test.beforeAll(async () => {
9+
await setupInlineFixture({
10+
src: 'examples/starter',
11+
dest: root,
12+
files: {
13+
'vite.config.base.ts': { cp: 'vite.config.ts' },
14+
'vite.config.ts': /* js */ `
15+
import { defineConfig, mergeConfig } from 'vite'
16+
import baseConfig from './vite.config.base.ts'
17+
18+
const overrideConfig = defineConfig({
19+
build: {
20+
cssCodeSplit: false,
21+
},
22+
})
23+
24+
export default mergeConfig(baseConfig, overrideConfig)
25+
`,
26+
// test server css module too
27+
// (starter example already tests normal server css)
28+
'src/server-only.module.css': /* css */ `
29+
.serverOnly {
30+
color: rgb(123, 45, 67);
31+
}
32+
`,
33+
'src/server-only.tsx': /* js */ `
34+
import styles from './server-only.module.css'
35+
export function ServerOnly() {
36+
return (
37+
<button data-testid="server-only" className={styles.serverOnly}>
38+
server-only
39+
</button>
40+
)
41+
}
42+
`,
43+
'src/root.tsx': {
44+
edit: (s) =>
45+
s
46+
.replace(
47+
`import { ClientCounter } from './client.tsx'`,
48+
`import { ClientCounter } from './client.tsx';
49+
import { ServerOnly } from './server-only.tsx'`,
50+
)
51+
.replace(`<ClientCounter />`, `<ClientCounter /><ServerOnly />`),
52+
},
53+
},
54+
})
55+
})
56+
57+
test.describe('build', () => {
58+
const f = useFixture({ root, mode: 'build' })
59+
defineStarterTest(f)
60+
61+
test('server css module', async ({ page }) => {
62+
await page.goto(f.url())
63+
await expect(page.getByTestId('server-only')).toHaveCSS(
64+
'color',
65+
'rgb(123, 45, 67)',
66+
)
67+
})
68+
})
69+
})

packages/plugin-rsc/src/plugin.ts

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1032,6 +1032,21 @@ export function createRpcClient(params) {
10321032
},
10331033
},
10341034
},
1035+
{
1036+
// keep track of build output for each environment build to generate
1037+
// assets manaifest and environment imports manifest.
1038+
// use `order: "post"` to include all chunks/assets
1039+
// generated by other plugins
1040+
// (e.g. `styles.css` generated by `vite:css-post` when `cssCodeSplit: false`).
1041+
// https://github.com/vitejs/vite/blob/a19003516951a3710aab0f2646d78c48b2e5d2ad/packages/vite/src/node/plugins/css.ts#L1040
1042+
name: 'rsc:virtual:vite-rsc/track-bundles',
1043+
generateBundle: {
1044+
order: 'post',
1045+
handler(_options, bundle) {
1046+
manager.bundles[this.environment.name] = bundle
1047+
},
1048+
},
1049+
},
10351050
{
10361051
name: 'rsc:virtual:vite-rsc/assets-manifest',
10371052
resolveId: {
@@ -1067,10 +1082,31 @@ export function createRpcClient(params) {
10671082
// client build
10681083
generateBundle(_options, bundle) {
10691084
// copy assets from rsc build to client build
1070-
manager.bundles[this.environment.name] = bundle
1071-
10721085
if (this.environment.name === 'client') {
10731086
const rscBundle = manager.bundles['rsc']!
1087+
1088+
// when css code split is disabled, treat vite's single css bundle `style.css`
1089+
// as dependency of all server chunks
1090+
if (!manager.config.environments.rsc!.build.cssCodeSplit) {
1091+
let cssBundleName: string | undefined
1092+
for (const output of Object.values(rscBundle)) {
1093+
if (
1094+
output.type === 'asset' &&
1095+
output.names.includes('style.css')
1096+
) {
1097+
cssBundleName = output.fileName
1098+
break
1099+
}
1100+
}
1101+
if (cssBundleName) {
1102+
for (const output of Object.values(rscBundle)) {
1103+
if (output.type === 'chunk') {
1104+
output.viteMetadata!.importedCss.add(cssBundleName)
1105+
}
1106+
}
1107+
}
1108+
}
1109+
10741110
const assets = new Set(
10751111
Object.values(rscBundle).flatMap((output) =>
10761112
output.type === 'chunk'
@@ -1092,7 +1128,7 @@ export function createRpcClient(params) {
10921128
}
10931129

10941130
const serverResources: Record<string, AssetDeps> = {}
1095-
const rscAssetDeps = collectAssetDeps(manager.bundles['rsc']!)
1131+
const rscAssetDeps = collectAssetDeps(rscBundle)
10961132
for (const [id, meta] of Object.entries(
10971133
manager.serverResourcesMetaMap,
10981134
)) {

0 commit comments

Comments
 (0)