Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(ssr): avoid transforming json file in ssrTransform #6597

Merged
merged 15 commits into from May 10, 2022
23 changes: 23 additions & 0 deletions packages/playground/json/index.ssr.html
@@ -0,0 +1,23 @@
<div class="fetch-json-module">
json-module:
<pre></pre>
<code></code>
</div>
<div class="fetch-json-fs">
json-fs:
<pre></pre>
<code></code>
</div>

<script type="module">
const startModule = Date.now()
text('.fetch-json-module pre', await (await fetch('/json-module')).text())
text('.fetch-json-module code', Date.now() - startModule)

const startFs = Date.now()
text('.fetch-json-fs pre', await (await fetch('/json-fs')).text())
text('.fetch-json-fs code', Date.now() - startFs)
function text(sel, text) {
document.querySelector(sel).textContent = text
}
</script>
9 changes: 7 additions & 2 deletions packages/playground/json/package.json
Expand Up @@ -6,9 +6,14 @@
"dev": "vite",
"build": "vite build",
"debug": "node --inspect-brk ../../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": {
"vue": "^3.2.25"
"vue": "^3.2.25",
"cross-env": "^7.0.3",
"express": "^4.17.1"
}
}
88 changes: 88 additions & 0 deletions 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
7 changes: 6 additions & 1 deletion packages/vite/src/node/plugins/json.ts
Expand Up @@ -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 {
/**
Expand All @@ -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
Expand Down
6 changes: 5 additions & 1 deletion packages/vite/src/node/server/index.ts
Expand Up @@ -355,7 +355,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 }
})
},
transformWithEsbuild,
transformRequest(url, options) {
return transformRequest(url, server, options)
Expand Down
3 changes: 2 additions & 1 deletion packages/vite/src/node/server/transformRequest.ts
Expand Up @@ -183,7 +183,8 @@ async function doTransform(
return (mod.ssrTransformResult = await ssrTransform(
code,
map as SourceMap,
url
url,
{ json: { stringify: !!server.config.json?.stringify } }
))
} else {
return (mod.transformResult = {
Expand Down
1 change: 0 additions & 1 deletion packages/vite/src/node/ssr/ssrModuleLoader.ts
Expand Up @@ -72,7 +72,6 @@ async function instantiateModule(
if (mod.ssrModule) {
return mod.ssrModule
}

const result =
mod.ssrTransformResult ||
(await transformRequest(url, server, { ssr: true }))
Expand Down
31 changes: 31 additions & 0 deletions packages/vite/src/node/ssr/ssrTransform.ts
Expand Up @@ -13,19 +13,50 @@ import { extract_names as extractNames } from 'periscopic'
import { walk as eswalk } from 'estree-walker'
import { combineSourcemaps } from '../utils'
import type { RawSourceMap } from '@ampproject/remapping/dist/types/types'
import { isJSONRequest } from '../plugins/json'

type Node = _Node & {
start: number
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__`
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<TransformResult | null> {
if (options?.json?.stringify && isJSONRequest(url)) {
return ssrTransformJSON(code, inMap)
}
return ssrTransformScript(code, inMap, url)
}

async function ssrTransformJSON(
code: string,
inMap: SourceMap | null
): Promise<TransformResult> {
return {
code: code.replace('export default', `${ssrModuleExportsKey}.default =`),
map: inMap,
deps: [],
dynamicDeps: []
}
}

async function ssrTransformScript(
code: string,
inMap: SourceMap | null,
url: string
Expand Down
25 changes: 5 additions & 20 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.