Skip to content

Commit a709440

Browse files
authored
fix(ssr): avoid transforming json file in ssrTransform (#6597)
1 parent 86b29e7 commit a709440

File tree

9 files changed

+166
-5
lines changed

9 files changed

+166
-5
lines changed
+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<div class="fetch-json-module">
2+
json-module:
3+
<pre></pre>
4+
<code></code>
5+
</div>
6+
<div class="fetch-json-fs">
7+
json-fs:
8+
<pre></pre>
9+
<code></code>
10+
</div>
11+
12+
<script type="module">
13+
const startModule = Date.now()
14+
text('.fetch-json-module pre', await (await fetch('/json-module')).text())
15+
text('.fetch-json-module code', Date.now() - startModule)
16+
17+
const startFs = Date.now()
18+
text('.fetch-json-fs pre', await (await fetch('/json-fs')).text())
19+
text('.fetch-json-fs code', Date.now() - startFs)
20+
function text(sel, text) {
21+
document.querySelector(sel).textContent = text
22+
}
23+
</script>

packages/playground/json/server.js

+88
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
// @ts-check
2+
const fs = require('fs')
3+
const path = require('path')
4+
const express = require('express')
5+
6+
const isTest = process.env.NODE_ENV === 'test' || !!process.env.VITE_TEST_BUILD
7+
8+
async function createServer(
9+
root = process.cwd(),
10+
isProd = process.env.NODE_ENV === 'production'
11+
) {
12+
const resolve = (p) => path.resolve(__dirname, p)
13+
const app = express()
14+
15+
/**
16+
* @type {import('vite').ViteDevServer}
17+
*/
18+
let vite
19+
vite = await require('vite').createServer({
20+
root,
21+
logLevel: isTest ? 'error' : 'info',
22+
server: {
23+
middlewareMode: 'ssr',
24+
watch: {
25+
// During tests we edit the files too fast and sometimes chokidar
26+
// misses change events, so enforce polling for consistency
27+
usePolling: true,
28+
interval: 100
29+
}
30+
},
31+
json: {
32+
stringify: true
33+
}
34+
})
35+
// use vite's connect instance as middleware
36+
app.use(vite.middlewares)
37+
38+
app.use('*', async (req, res) => {
39+
try {
40+
let [url] = req.originalUrl.split('?')
41+
if (url.endsWith('/')) url += 'index.ssr.html'
42+
43+
if (url === '/json-module') {
44+
console.time('load module')
45+
const json = JSON.stringify(await vite.ssrLoadModule('/test.json'))
46+
console.timeEnd('load module')
47+
res.status(200).end('' + json.length)
48+
return
49+
}
50+
51+
if (url === '/json-fs') {
52+
console.time('transform module')
53+
const source = fs.readFileSync('./test.json', { encoding: 'utf-8' })
54+
const json = await vite.ssrTransform(
55+
`export default ${source}`,
56+
null,
57+
'./output.json'
58+
)
59+
console.timeEnd('transform module')
60+
res.status(200).end(String(json.code.length))
61+
return
62+
}
63+
64+
const htmlLoc = resolve(`.${url}`)
65+
let html = fs.readFileSync(htmlLoc, 'utf8')
66+
html = await vite.transformIndexHtml(url, html)
67+
68+
res.status(200).set({ 'Content-Type': 'text/html' }).end(html)
69+
} catch (e) {
70+
vite && vite.ssrFixStacktrace(e)
71+
console.log(e.stack)
72+
res.status(500).end(e.stack)
73+
}
74+
})
75+
76+
return { app, vite }
77+
}
78+
79+
if (!isTest) {
80+
createServer().then(({ app }) =>
81+
app.listen(3000, () => {
82+
console.log('http://localhost:3000')
83+
})
84+
)
85+
}
86+
87+
// for test use
88+
exports.createServer = createServer

packages/vite/src/node/plugins/json.ts

+6-1
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@
77
*/
88

99
import { dataToEsm } from '@rollup/pluginutils'
10-
import type { Plugin } from 'rollup'
1110
import { SPECIAL_QUERY_RE } from '../constants'
11+
import type { Plugin } from '../plugin'
1212

1313
export interface JsonOptions {
1414
/**
@@ -27,6 +27,11 @@ export interface JsonOptions {
2727
// Custom json filter for vite
2828
const jsonExtRE = /\.json($|\?)(?!commonjs-(proxy|external))/
2929

30+
const jsonLangs = `\\.(json|json5)($|\\?)`
31+
const jsonLangRE = new RegExp(jsonLangs)
32+
export const isJSONRequest = (request: string): boolean =>
33+
jsonLangRE.test(request)
34+
3035
export function jsonPlugin(
3136
options: JsonOptions = {},
3237
isBuild: boolean

packages/vite/src/node/server/index.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -321,7 +321,11 @@ export async function createServer(
321321
pluginContainer: container,
322322
ws,
323323
moduleGraph,
324-
ssrTransform,
324+
ssrTransform(code: string, inMap: SourceMap | null, url: string) {
325+
return ssrTransform(code, inMap, url, {
326+
json: { stringify: server.config.json?.stringify }
327+
})
328+
},
325329
transformRequest(url, options) {
326330
return transformRequest(url, server, options)
327331
},

packages/vite/src/node/server/transformRequest.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,9 @@ async function doTransform(
238238
}
239239

240240
const result = ssr
241-
? await ssrTransform(code, map as SourceMap, url)
241+
? await ssrTransform(code, map as SourceMap, url, {
242+
json: { stringify: !!server.config.json?.stringify }
243+
})
242244
: ({
243245
code,
244246
map,

packages/vite/src/node/ssr/ssrModuleLoader.ts

-1
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,6 @@ async function instantiateModule(
8484
if (mod.ssrModule) {
8585
return mod.ssrModule
8686
}
87-
8887
const result =
8988
mod.ssrTransformResult ||
9089
(await transformRequest(url, server, { ssr: true }))

packages/vite/src/node/ssr/ssrTransform.ts

+31
Original file line numberDiff line numberDiff line change
@@ -12,20 +12,51 @@ import type {
1212
import { extract_names as extractNames } from 'periscopic'
1313
import { walk as eswalk } from 'estree-walker'
1414
import { combineSourcemaps } from '../utils'
15+
import { isJSONRequest } from '../plugins/json'
1516
import type { RawSourceMap } from '@ampproject/remapping'
1617

1718
type Node = _Node & {
1819
start: number
1920
end: number
2021
}
2122

23+
interface TransformOptions {
24+
json?: {
25+
stringify?: boolean
26+
}
27+
}
28+
2229
export const ssrModuleExportsKey = `__vite_ssr_exports__`
2330
export const ssrImportKey = `__vite_ssr_import__`
2431
export const ssrDynamicImportKey = `__vite_ssr_dynamic_import__`
2532
export const ssrExportAllKey = `__vite_ssr_exportAll__`
2633
export const ssrImportMetaKey = `__vite_ssr_import_meta__`
2734

2835
export async function ssrTransform(
36+
code: string,
37+
inMap: SourceMap | null,
38+
url: string,
39+
options?: TransformOptions
40+
): Promise<TransformResult | null> {
41+
if (options?.json?.stringify && isJSONRequest(url)) {
42+
return ssrTransformJSON(code, inMap)
43+
}
44+
return ssrTransformScript(code, inMap, url)
45+
}
46+
47+
async function ssrTransformJSON(
48+
code: string,
49+
inMap: SourceMap | null
50+
): Promise<TransformResult> {
51+
return {
52+
code: code.replace('export default', `${ssrModuleExportsKey}.default =`),
53+
map: inMap,
54+
deps: [],
55+
dynamicDeps: []
56+
}
57+
}
58+
59+
async function ssrTransformScript(
2960
code: string,
3061
inMap: SourceMap | null,
3162
url: string

playground/json/package.json

+6-1
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,14 @@
66
"dev": "vite",
77
"build": "vite build",
88
"debug": "node --inspect-brk ../../packages/vite/bin/vite",
9-
"preview": "vite preview"
9+
"preview": "vite preview",
10+
"dev:ssr": "node server",
11+
"serve:ssr": "cross-env NODE_ENV=production node server",
12+
"debug:ssr": "node --inspect-brk server"
1013
},
1114
"devDependencies": {
15+
"cross-env": "^7.0.3",
16+
"express": "^4.17.1",
1217
"json-module": "file:./json-module",
1318
"vue": "^3.2.33"
1419
}

pnpm-lock.yaml

+4
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)