diff --git a/packages/playground/json/index.ssr.html b/packages/playground/json/index.ssr.html
new file mode 100644
index 00000000000000..8a4c4551a90817
--- /dev/null
+++ b/packages/playground/json/index.ssr.html
@@ -0,0 +1,23 @@
+
+
+
+
diff --git a/packages/playground/json/server.js b/packages/playground/json/server.js
new file mode 100644
index 00000000000000..d7803b94f4b81a
--- /dev/null
+++ b/packages/playground/json/server.js
@@ -0,0 +1,88 @@
+// @ts-check
+const fs = require('fs')
+const path = require('path')
+const express = require('express')
+
+const isTest = process.env.NODE_ENV === 'test' || !!process.env.VITE_TEST_BUILD
+
+async function createServer(
+ root = process.cwd(),
+ isProd = process.env.NODE_ENV === 'production'
+) {
+ const resolve = (p) => path.resolve(__dirname, p)
+ const app = express()
+
+ /**
+ * @type {import('vite').ViteDevServer}
+ */
+ let vite
+ vite = await require('vite').createServer({
+ root,
+ logLevel: isTest ? 'error' : 'info',
+ server: {
+ middlewareMode: 'ssr',
+ watch: {
+ // During tests we edit the files too fast and sometimes chokidar
+ // misses change events, so enforce polling for consistency
+ usePolling: true,
+ interval: 100
+ }
+ },
+ json: {
+ stringify: true
+ }
+ })
+ // use vite's connect instance as middleware
+ app.use(vite.middlewares)
+
+ app.use('*', async (req, res) => {
+ try {
+ let [url] = req.originalUrl.split('?')
+ if (url.endsWith('/')) url += 'index.ssr.html'
+
+ if (url === '/json-module') {
+ console.time('load module')
+ const json = JSON.stringify(await vite.ssrLoadModule('/test.json'))
+ console.timeEnd('load module')
+ res.status(200).end('' + json.length)
+ return
+ }
+
+ if (url === '/json-fs') {
+ console.time('transform module')
+ const source = fs.readFileSync('./test.json', { encoding: 'utf-8' })
+ const json = await vite.ssrTransform(
+ `export default ${source}`,
+ null,
+ './output.json'
+ )
+ console.timeEnd('transform module')
+ res.status(200).end(String(json.code.length))
+ return
+ }
+
+ const htmlLoc = resolve(`.${url}`)
+ let html = fs.readFileSync(htmlLoc, 'utf8')
+ html = await vite.transformIndexHtml(url, html)
+
+ res.status(200).set({ 'Content-Type': 'text/html' }).end(html)
+ } catch (e) {
+ vite && vite.ssrFixStacktrace(e)
+ console.log(e.stack)
+ res.status(500).end(e.stack)
+ }
+ })
+
+ return { app, vite }
+}
+
+if (!isTest) {
+ createServer().then(({ app }) =>
+ app.listen(3000, () => {
+ console.log('http://localhost:3000')
+ })
+ )
+}
+
+// for test use
+exports.createServer = createServer
diff --git a/packages/vite/src/node/plugins/json.ts b/packages/vite/src/node/plugins/json.ts
index f8419c40f36174..9c142501ff651e 100644
--- a/packages/vite/src/node/plugins/json.ts
+++ b/packages/vite/src/node/plugins/json.ts
@@ -7,8 +7,8 @@
*/
import { dataToEsm } from '@rollup/pluginutils'
-import type { Plugin } from 'rollup'
import { SPECIAL_QUERY_RE } from '../constants'
+import type { Plugin } from '../plugin'
export interface JsonOptions {
/**
@@ -27,6 +27,11 @@ export interface JsonOptions {
// Custom json filter for vite
const jsonExtRE = /\.json($|\?)(?!commonjs-(proxy|external))/
+const jsonLangs = `\\.(json|json5)($|\\?)`
+const jsonLangRE = new RegExp(jsonLangs)
+export const isJSONRequest = (request: string): boolean =>
+ jsonLangRE.test(request)
+
export function jsonPlugin(
options: JsonOptions = {},
isBuild: boolean
diff --git a/packages/vite/src/node/server/index.ts b/packages/vite/src/node/server/index.ts
index 0b499b42c2d457..50ca05e17a748e 100644
--- a/packages/vite/src/node/server/index.ts
+++ b/packages/vite/src/node/server/index.ts
@@ -321,7 +321,11 @@ export async function createServer(
pluginContainer: container,
ws,
moduleGraph,
- ssrTransform,
+ ssrTransform(code: string, inMap: SourceMap | null, url: string) {
+ return ssrTransform(code, inMap, url, {
+ json: { stringify: server.config.json?.stringify }
+ })
+ },
transformRequest(url, options) {
return transformRequest(url, server, options)
},
diff --git a/packages/vite/src/node/server/transformRequest.ts b/packages/vite/src/node/server/transformRequest.ts
index baafe95e8416f3..f877925f491479 100644
--- a/packages/vite/src/node/server/transformRequest.ts
+++ b/packages/vite/src/node/server/transformRequest.ts
@@ -238,7 +238,9 @@ async function doTransform(
}
const result = ssr
- ? await ssrTransform(code, map as SourceMap, url)
+ ? await ssrTransform(code, map as SourceMap, url, {
+ json: { stringify: !!server.config.json?.stringify }
+ })
: ({
code,
map,
diff --git a/packages/vite/src/node/ssr/ssrModuleLoader.ts b/packages/vite/src/node/ssr/ssrModuleLoader.ts
index 8b3a423f58aeab..9f75641c84e513 100644
--- a/packages/vite/src/node/ssr/ssrModuleLoader.ts
+++ b/packages/vite/src/node/ssr/ssrModuleLoader.ts
@@ -84,7 +84,6 @@ async function instantiateModule(
if (mod.ssrModule) {
return mod.ssrModule
}
-
const result =
mod.ssrTransformResult ||
(await transformRequest(url, server, { ssr: true }))
diff --git a/packages/vite/src/node/ssr/ssrTransform.ts b/packages/vite/src/node/ssr/ssrTransform.ts
index c1aa572864776a..4eeb34ad7e102c 100644
--- a/packages/vite/src/node/ssr/ssrTransform.ts
+++ b/packages/vite/src/node/ssr/ssrTransform.ts
@@ -12,6 +12,7 @@ import type {
import { extract_names as extractNames } from 'periscopic'
import { walk as eswalk } from 'estree-walker'
import { combineSourcemaps } from '../utils'
+import { isJSONRequest } from '../plugins/json'
import type { RawSourceMap } from '@ampproject/remapping'
type Node = _Node & {
@@ -19,6 +20,12 @@ type Node = _Node & {
end: number
}
+interface TransformOptions {
+ json?: {
+ stringify?: boolean
+ }
+}
+
export const ssrModuleExportsKey = `__vite_ssr_exports__`
export const ssrImportKey = `__vite_ssr_import__`
export const ssrDynamicImportKey = `__vite_ssr_dynamic_import__`
@@ -26,6 +33,30 @@ export const ssrExportAllKey = `__vite_ssr_exportAll__`
export const ssrImportMetaKey = `__vite_ssr_import_meta__`
export async function ssrTransform(
+ code: string,
+ inMap: SourceMap | null,
+ url: string,
+ options?: TransformOptions
+): Promise {
+ if (options?.json?.stringify && isJSONRequest(url)) {
+ return ssrTransformJSON(code, inMap)
+ }
+ return ssrTransformScript(code, inMap, url)
+}
+
+async function ssrTransformJSON(
+ code: string,
+ inMap: SourceMap | null
+): Promise {
+ return {
+ code: code.replace('export default', `${ssrModuleExportsKey}.default =`),
+ map: inMap,
+ deps: [],
+ dynamicDeps: []
+ }
+}
+
+async function ssrTransformScript(
code: string,
inMap: SourceMap | null,
url: string
diff --git a/playground/json/package.json b/playground/json/package.json
index a32140c1220d1a..b919c42eb6ef5d 100644
--- a/playground/json/package.json
+++ b/playground/json/package.json
@@ -6,9 +6,14 @@
"dev": "vite",
"build": "vite build",
"debug": "node --inspect-brk ../../packages/vite/bin/vite",
- "preview": "vite preview"
+ "preview": "vite preview",
+ "dev:ssr": "node server",
+ "serve:ssr": "cross-env NODE_ENV=production node server",
+ "debug:ssr": "node --inspect-brk server"
},
"devDependencies": {
+ "cross-env": "^7.0.3",
+ "express": "^4.17.1",
"json-module": "file:./json-module",
"vue": "^3.2.33"
}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 655b3273279f9e..1a06a82b104080 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -458,9 +458,13 @@ importers:
playground/json:
specifiers:
+ cross-env: ^7.0.3
+ express: ^4.17.1
json-module: file:./json-module
vue: ^3.2.33
devDependencies:
+ cross-env: 7.0.3
+ express: 4.18.1
json-module: link:json-module
vue: 3.2.33