diff --git a/packages/plugin-rsc/e2e/basic.test.ts b/packages/plugin-rsc/e2e/basic.test.ts index a4378638..99163871 100644 --- a/packages/plugin-rsc/e2e/basic.test.ts +++ b/packages/plugin-rsc/e2e/basic.test.ts @@ -48,36 +48,48 @@ test.describe('build-default', () => { const f = useFixture({ root: 'examples/basic', mode: 'build' }) defineTest(f) - test('custom client chunk', async () => { + test('server-chunk-based client chunks', async () => { const { chunks }: { chunks: Rollup.OutputChunk[] } = JSON.parse( f.createEditor('dist/client/.vite/test.json').read(), ) - const chunk = chunks.find((c) => c.name === 'custom-chunk') - const expected = [1, 2, 3].map((i) => - normalizePath(path.join(f.root, `src/routes/chunk/client${i}.tsx`)), - ) - expect(chunk?.moduleIds).toEqual(expect.arrayContaining(expected)) + const expectedGroups = { + 'facade:src/routes/chunk2/client1.tsx': ['src/routes/chunk2/client1.tsx'], + 'facade:src/routes/chunk2/server2.tsx': [ + 'src/routes/chunk2/client2.tsx', + 'src/routes/chunk2/client2b.tsx', + ], + 'shared:src/routes/chunk2/client3.tsx': ['src/routes/chunk2/client3.tsx'], + } + const actualGroups: Record = {} + for (const key in expectedGroups) { + const groupId = `\0virtual:vite-rsc/client-references/group/${key}` + const groupChunk = chunks.find((c) => c.facadeModuleId === groupId) + if (groupChunk) { + actualGroups[key] = groupChunk.moduleIds + .filter((id) => id !== groupId) + .map((id) => normalizePath(path.relative(f.root, id))) + } + } + expect(actualGroups).toEqual(expectedGroups) }) }) -test.describe('build-server-client-chunks', () => { +test.describe('custom-client-chunks', () => { const f = useFixture({ root: 'examples/basic', mode: 'build', cliOptions: { env: { - TEST_SERVER_CLIENT_CHUNKS: 'true', + TEST_CUSTOM_CLIENT_CHUNKS: 'true', }, }, }) - defineTest(f) - - test('custom client chunk', async () => { + test('basic', async () => { const { chunks }: { chunks: Rollup.OutputChunk[] } = JSON.parse( f.createEditor('dist/client/.vite/test.json').read(), ) - const chunk = chunks.find((c) => c.name === 'root') + const chunk = chunks.find((c) => c.name === 'custom-chunk') const expected = [1, 2, 3].map((i) => normalizePath(path.join(f.root, `src/routes/chunk/client${i}.tsx`)), ) @@ -181,7 +193,7 @@ test.describe('build-stable-chunks', () => { .sort() expect(newChunks).toEqual([ 'src/framework/entry.browser.tsx', - 'virtual:vite-rsc/client-references/group/src/routes/client.tsx', + 'virtual:vite-rsc/client-references/group/facade:src/routes/root.tsx', ]) expect(oldChunks).toEqual(newChunks) }) @@ -1434,4 +1446,12 @@ function defineTest(f: Fixture) { await testBackgroundImage('.test-assets-server-css') await testBackgroundImage('.test-assets-client-css') }) + + test('lazy', async ({ page }) => { + await page.goto(f.url()) + await waitForHydration(page) + await expect(page.getByTestId('test-chunk2')).toHaveText( + 'test-chunk1|test-chunk2|test-chunk2b|test-chunk3|test-chunk3', + ) + }) } diff --git a/packages/plugin-rsc/e2e/render-built-url.test.ts b/packages/plugin-rsc/e2e/render-built-url.test.ts index 565bb499..66721515 100644 --- a/packages/plugin-rsc/e2e/render-built-url.test.ts +++ b/packages/plugin-rsc/e2e/render-built-url.test.ts @@ -104,7 +104,9 @@ test.describe(() => { f.root + '/dist/ssr/__vite_rsc_assets_manifest.js', 'utf-8', ) - expect(manifestFileContent).toContain(`__dynamicBase + "assets/client-`) + expect(manifestFileContent).toContain( + `__dynamicBase + "assets/entry.rsc-`, + ) }) }) }) diff --git a/packages/plugin-rsc/e2e/starter.test.ts b/packages/plugin-rsc/e2e/starter.test.ts index 3c64d547..1caff94d 100644 --- a/packages/plugin-rsc/e2e/starter.test.ts +++ b/packages/plugin-rsc/e2e/starter.test.ts @@ -45,7 +45,7 @@ test.describe('build-development', () => { test('verify development', async ({ page }) => { let output!: string page.on('response', async (response) => { - if (response.url().match(/\/assets\/client-[\w-]+\.js$/)) { + if (response.url().match(/\/assets\/entry.rsc-[\w-]+\.js$/)) { output = await response.text() } }) diff --git a/packages/plugin-rsc/examples/basic/src/routes/chunk2/client1.tsx b/packages/plugin-rsc/examples/basic/src/routes/chunk2/client1.tsx new file mode 100644 index 00000000..0ffe9aad --- /dev/null +++ b/packages/plugin-rsc/examples/basic/src/routes/chunk2/client1.tsx @@ -0,0 +1,5 @@ +'use client' + +export default function TestChunkClient1() { + return test-chunk1 +} diff --git a/packages/plugin-rsc/examples/basic/src/routes/chunk2/client2.tsx b/packages/plugin-rsc/examples/basic/src/routes/chunk2/client2.tsx new file mode 100644 index 00000000..dc7dacf5 --- /dev/null +++ b/packages/plugin-rsc/examples/basic/src/routes/chunk2/client2.tsx @@ -0,0 +1,5 @@ +'use client' + +export default function TestChunkClient2() { + return test-chunk2 +} diff --git a/packages/plugin-rsc/examples/basic/src/routes/chunk2/client2b.tsx b/packages/plugin-rsc/examples/basic/src/routes/chunk2/client2b.tsx new file mode 100644 index 00000000..9d03f2a0 --- /dev/null +++ b/packages/plugin-rsc/examples/basic/src/routes/chunk2/client2b.tsx @@ -0,0 +1,5 @@ +'use client' + +export default function TestChunkClient2b() { + return test-chunk2b +} diff --git a/packages/plugin-rsc/examples/basic/src/routes/chunk2/client3.tsx b/packages/plugin-rsc/examples/basic/src/routes/chunk2/client3.tsx new file mode 100644 index 00000000..1baadec9 --- /dev/null +++ b/packages/plugin-rsc/examples/basic/src/routes/chunk2/client3.tsx @@ -0,0 +1,5 @@ +'use client' + +export default function TestChunkClient3() { + return test-chunk3 +} diff --git a/packages/plugin-rsc/examples/basic/src/routes/chunk2/server.tsx b/packages/plugin-rsc/examples/basic/src/routes/chunk2/server.tsx new file mode 100644 index 00000000..96039706 --- /dev/null +++ b/packages/plugin-rsc/examples/basic/src/routes/chunk2/server.tsx @@ -0,0 +1,17 @@ +import React from 'react' + +const TestChunkClient1 = React.lazy(() => import('./client1')) +const TestChunkServer2 = React.lazy(() => import('./server2')) +const TestChunkServer3 = React.lazy(() => import('./server3')) +const TestChunkServer4 = React.lazy(() => import('./server4')) + +export function TestChunk2() { + return ( +
+ | + | + | + +
+ ) +} diff --git a/packages/plugin-rsc/examples/basic/src/routes/chunk2/server2.tsx b/packages/plugin-rsc/examples/basic/src/routes/chunk2/server2.tsx new file mode 100644 index 00000000..693a828c --- /dev/null +++ b/packages/plugin-rsc/examples/basic/src/routes/chunk2/server2.tsx @@ -0,0 +1,11 @@ +import TestChunkClient2 from './client2' +import TestChunkClient2b from './client2b' + +export default function TestChunkServer2() { + return ( + <> + | + + + ) +} diff --git a/packages/plugin-rsc/examples/basic/src/routes/chunk2/server3.tsx b/packages/plugin-rsc/examples/basic/src/routes/chunk2/server3.tsx new file mode 100644 index 00000000..e8a829b0 --- /dev/null +++ b/packages/plugin-rsc/examples/basic/src/routes/chunk2/server3.tsx @@ -0,0 +1,5 @@ +import TestChunkClient3 from './client3' + +export default function TestChunkServer3() { + return +} diff --git a/packages/plugin-rsc/examples/basic/src/routes/chunk2/server4.tsx b/packages/plugin-rsc/examples/basic/src/routes/chunk2/server4.tsx new file mode 100644 index 00000000..69ee9e37 --- /dev/null +++ b/packages/plugin-rsc/examples/basic/src/routes/chunk2/server4.tsx @@ -0,0 +1,5 @@ +import TestChunkClient3 from './client3' + +export default function TestChunkServer4() { + return +} diff --git a/packages/plugin-rsc/examples/basic/src/routes/root.tsx b/packages/plugin-rsc/examples/basic/src/routes/root.tsx index cc266a54..59c633b7 100644 --- a/packages/plugin-rsc/examples/basic/src/routes/root.tsx +++ b/packages/plugin-rsc/examples/basic/src/routes/root.tsx @@ -43,6 +43,7 @@ import { TestClientChunkServer } from './chunk/server' import { TestTailwind } from './tailwind' import { TestHmrClientDep2 } from './hmr-client-dep2/client' import { TestHmrClientDep3 } from './hmr-client-dep3/server' +import { TestChunk2 } from './chunk2/server' export function Root(props: { url: URL }) { return ( @@ -99,6 +100,7 @@ export function Root(props: { url: URL }) { + ) diff --git a/packages/plugin-rsc/examples/basic/vite.config.ts b/packages/plugin-rsc/examples/basic/vite.config.ts index a3c76c7e..144117f2 100644 --- a/packages/plugin-rsc/examples/basic/vite.config.ts +++ b/packages/plugin-rsc/examples/basic/vite.config.ts @@ -32,11 +32,10 @@ export default defineConfig({ copyServerAssetsToClient: (fileName) => fileName !== '__server_secret.txt', clientChunks(meta) { - if (process.env.TEST_SERVER_CLIENT_CHUNKS) { - return meta.serverChunk - } - if (meta.id.includes('/src/routes/chunk/')) { - return 'custom-chunk' + if (process.env.TEST_CUSTOM_CLIENT_CHUNKS) { + if (meta.id.includes('/src/routes/chunk/')) { + return 'custom-chunk' + } } }, }), diff --git a/packages/plugin-rsc/src/plugin.ts b/packages/plugin-rsc/src/plugin.ts index 1710dff0..7544cc7c 100644 --- a/packages/plugin-rsc/src/plugin.ts +++ b/packages/plugin-rsc/src/plugin.ts @@ -192,10 +192,13 @@ export type RscPluginOptions = { * * This function allows you to group multiple client components into * custom chunks instead of having each module in its own chunk. + * By default, client chunks are grouped by `meta.serverChunk`. */ clientChunks?: (meta: { /** client reference module id */ id: string + /** normalized client reference module id */ + normalizedId: string /** server chunk which includes a corresponding client reference proxy module */ serverChunk: string }) => string | undefined @@ -1190,10 +1193,9 @@ function vitePluginUseClient( let name = useClientPluginOptions.clientChunks?.({ id: meta.importId, + normalizedId: manager.toRelativeId(meta.importId), serverChunk: meta.serverChunk!, - }) ?? - // use original module id as name by default - manager.toRelativeId(meta.importId) + }) ?? meta.serverChunk! // ensure clean virtual id to avoid interfering with other plugins name = cleanUrl(name.replaceAll('..', '__')) const group = (manager.clientReferenceGroups[name] ??= [])