Skip to content

Commit 048ef7e

Browse files
authored
Merge branch 'main' into renovate/es-module-lexer-2.x
2 parents e9119fc + fff018b commit 048ef7e

File tree

7 files changed

+118
-3
lines changed

7 files changed

+118
-3
lines changed

packages/plugin-rsc/CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
## <small>[0.5.6](https://github.com/vitejs/vite-plugin-react/compare/plugin-rsc@0.5.5...plugin-rsc@0.5.6) (2025-12-08)</small>
2+
### Bug Fixes
3+
4+
* **rsc:** validate reference id on dev ([#1010](https://github.com/vitejs/vite-plugin-react/issues/1010)) ([fe634b5](https://github.com/vitejs/vite-plugin-react/commit/fe634b58210d0a4a146a7faae56cd71af3bb9af4))
5+
16
## <small>[0.5.5](https://github.com/vitejs/vite-plugin-react/compare/plugin-rsc@0.5.4...plugin-rsc@0.5.5) (2025-12-04)</small>
27
### Features
38

packages/plugin-rsc/e2e/starter.test.ts

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,51 @@ import { x } from 'tinyexec'
77
test.describe('dev-default', () => {
88
const f = useFixture({ root: 'examples/starter', mode: 'dev' })
99
defineStarterTest(f)
10+
11+
test('validate reference 1', async () => {
12+
const requestUrl = f.url('_.rsc')
13+
const formData = new FormData()
14+
const payload = {
15+
'0': [1, '$F1'],
16+
'1': { id: '__invalid_reference__# ' },
17+
}
18+
for (const [k, v] of Object.entries(payload)) {
19+
formData.append(k, JSON.stringify(v))
20+
}
21+
const response = await fetch(requestUrl, {
22+
method: 'POST',
23+
body: formData,
24+
headers: {
25+
'x-rsc-action': '/src/action.tsx#updateServerCounter',
26+
},
27+
})
28+
expect(f.proc().stderr()).toContain(
29+
`invalid server reference '__invalid_reference__`,
30+
)
31+
expect(response.status).toBe(500)
32+
})
33+
34+
test('validate reference 2', async () => {
35+
const requestUrl = f.url('_.rsc')
36+
const formData = new FormData()
37+
const payload = {
38+
'0': [1],
39+
}
40+
for (const [k, v] of Object.entries(payload)) {
41+
formData.append(k, JSON.stringify(v))
42+
}
43+
const response = await fetch(requestUrl, {
44+
method: 'POST',
45+
body: formData,
46+
headers: {
47+
'x-rsc-action': `__invalid_reference__# `,
48+
},
49+
})
50+
expect(f.proc().stderr()).toContain(
51+
`invalid server reference '__invalid_reference__'`,
52+
)
53+
expect(response.status).toBe(500)
54+
})
1055
})
1156

1257
test.describe('build-default', () => {

packages/plugin-rsc/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@vitejs/plugin-rsc",
3-
"version": "0.5.5",
3+
"version": "0.5.6",
44
"description": "React Server Components (RSC) support for Vite.",
55
"keywords": [
66
"vite",

packages/plugin-rsc/src/plugin.ts

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,12 @@ import { createDebug } from '@hiogawa/utils'
5454
import { scanBuildStripPlugin } from './plugins/scan'
5555
import { validateImportPlugin } from './plugins/validate-import'
5656
import { vitePluginFindSourceMapURL } from './plugins/find-source-map-url'
57-
import { parseCssVirtual, toCssVirtual, parseIdQuery } from './plugins/shared'
57+
import {
58+
parseCssVirtual,
59+
toCssVirtual,
60+
parseIdQuery,
61+
parseReferenceValidationVirtual,
62+
} from './plugins/shared'
5863
import { stripLiteral } from 'strip-literal'
5964

6065
const isRolldownVite = 'rolldownVersion' in vite
@@ -261,6 +266,37 @@ export function vitePluginRscMinimal(
261266
...vitePluginUseClient(rscPluginOptions, manager),
262267
...vitePluginUseServer(rscPluginOptions, manager),
263268
...vitePluginDefineEncryptionKey(rscPluginOptions),
269+
{
270+
name: 'rsc:reference-validation',
271+
apply: 'serve',
272+
load: {
273+
handler(id, _options) {
274+
if (id.startsWith('\0virtual:vite-rsc/reference-validation?')) {
275+
const parsed = parseReferenceValidationVirtual(id)
276+
assert(parsed)
277+
if (parsed.type === 'client') {
278+
const meta = Object.values(manager.clientReferenceMetaMap).find(
279+
(meta) => meta.referenceKey === parsed.id,
280+
)
281+
if (meta) {
282+
return `export {}`
283+
}
284+
}
285+
if (parsed.type === 'server') {
286+
const meta = Object.values(manager.serverReferenceMetaMap).find(
287+
(meta) => meta.referenceKey === parsed.id,
288+
)
289+
if (meta) {
290+
return `export {}`
291+
}
292+
}
293+
this.error(
294+
`[vite-rsc] invalid ${parsed.type} reference '${parsed.id}'`,
295+
)
296+
}
297+
},
298+
},
299+
},
264300
scanBuildStripPlugin({ manager }),
265301
]
266302
}

packages/plugin-rsc/src/plugins/shared.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,23 @@ export function parseIdQuery(id: string): {
2727
const query = Object.fromEntries(new URLSearchParams(rawQuery))
2828
return { filename, query }
2929
}
30+
31+
export type ReferenceValidationVirtual = {
32+
id: string
33+
type: 'server' | 'client'
34+
}
35+
36+
export function toReferenceValidationVirtual({
37+
id,
38+
type,
39+
}: ReferenceValidationVirtual) {
40+
return `virtual:vite-rsc/reference-validation?type=${type}&id=${encodeURIComponent(id)}&lang.js`
41+
}
42+
43+
export function parseReferenceValidationVirtual(
44+
id: string,
45+
): ReferenceValidationVirtual | undefined {
46+
if (id.startsWith('\0virtual:vite-rsc/reference-validation?')) {
47+
return parseIdQuery(id).query as any
48+
}
49+
}

packages/plugin-rsc/src/rsc.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import serverReferences from 'virtual:vite-rsc/server-references'
22
import { setRequireModule } from './core/rsc'
3+
import { toReferenceValidationVirtual } from './plugins/shared'
34

45
export {
56
createClientManifest,
@@ -20,6 +21,10 @@ function initialize(): void {
2021
setRequireModule({
2122
load: async (id) => {
2223
if (!import.meta.env.__vite_rsc_build__) {
24+
await import(
25+
/* @vite-ignore */ '/@id/__x00__' +
26+
toReferenceValidationVirtual({ id, type: 'server' })
27+
)
2328
return import(/* @vite-ignore */ id)
2429
} else {
2530
const import_ = serverReferences[id]

packages/plugin-rsc/src/ssr.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import * as clientReferences from 'virtual:vite-rsc/client-references'
33
import * as ReactDOM from 'react-dom'
44
import { setRequireModule } from './core/ssr'
55
import type { ResolvedAssetDeps } from './plugin'
6-
import { toCssVirtual } from './plugins/shared'
6+
import { toCssVirtual, toReferenceValidationVirtual } from './plugins/shared'
77

88
export { createServerConsumerManifest } from './core/ssr'
99

@@ -15,6 +15,10 @@ function initialize(): void {
1515
setRequireModule({
1616
load: async (id) => {
1717
if (!import.meta.env.__vite_rsc_build__) {
18+
await import(
19+
/* @vite-ignore */ '/@id/__x00__' +
20+
toReferenceValidationVirtual({ id, type: 'client' })
21+
)
1822
const mod = await import(/* @vite-ignore */ id)
1923
const modCss = await import(
2024
/* @vite-ignore */ '/@id/__x00__' + toCssVirtual({ id, type: 'ssr' })

0 commit comments

Comments
 (0)