Skip to content

Commit 107e79e

Browse files
committedJan 18, 2021
feat: ssr manifest for preload inference
1 parent c6115e9 commit 107e79e

File tree

6 files changed

+82
-7
lines changed

6 files changed

+82
-7
lines changed
 

‎packages/plugin-vue/src/main.ts

+16-1
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ export async function transformMain(
9595
`_sfc_main.__scopeId = ${JSON.stringify(`data-v-${descriptor.id}`)}`
9696
)
9797
}
98-
if (devServer) {
98+
if (devServer && !isProduction) {
9999
// expose filename during serve for devtools to pickup
100100
output.push(`_sfc_main.__file = ${JSON.stringify(filename)}`)
101101
}
@@ -122,6 +122,21 @@ export async function transformMain(
122122
)
123123
}
124124

125+
// SSR module registration by wrapping user setup
126+
if (ssr) {
127+
output.push(
128+
`import { useSSRContext } from 'vue'`,
129+
`const _sfc_setup = _sfc_main.setup`,
130+
`_sfc_main.setup = (props, ctx) => {`,
131+
` const ssrContext = useSSRContext()`,
132+
` ;(ssrContext.modules || (ssrContext.modules = new Set())).add(${JSON.stringify(
133+
filename
134+
)})`,
135+
` return _sfc_setup ? _sfc_setup(props, ctx) : undefined`,
136+
`}`
137+
)
138+
}
139+
125140
// if the template is inlined into the main module (indicated by the presence
126141
// of templateMap, we need to concatenate the two source maps.
127142
let resolvedMap = map

‎packages/vite/src/node/build.ts

+16-4
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import { CleanCSS } from 'types/clean-css'
2929
import { dataURIPlugin } from './plugins/dataUri'
3030
import { buildImportAnalysisPlugin } from './plugins/importAnaysisBuild'
3131
import { resolveSSRExternal } from './ssr/ssrExternal'
32+
import { ssrManifestPlugin } from './ssr/ssrManifestPlugin'
3233

3334
export interface BuildOptions {
3435
/**
@@ -148,9 +149,15 @@ export interface BuildOptions {
148149
*/
149150
lib?: LibraryOptions | false
150151
/**
151-
* @internal for now
152+
* Produce SSR oriented build. Note this requires specifying SSR entry via
153+
* `rollupOptions.input`.
152154
*/
153155
ssr?: boolean
156+
/**
157+
* Generate SSR manifest for determining style links and asset preload
158+
* directives in production.
159+
*/
160+
ssrManifest?: boolean
154161
}
155162

156163
export interface LibraryOptions {
@@ -187,6 +194,7 @@ export function resolveBuildOptions(
187194
manifest: false,
188195
lib: false,
189196
ssr: false,
197+
ssrManifest: false,
190198
...raw
191199
}
192200

@@ -233,6 +241,7 @@ export function resolveBuildPlugins(
233241
? [terserPlugin(options.terserOptions)]
234242
: []),
235243
...(options.manifest ? [manifestPlugin()] : []),
244+
...(options.ssrManifest ? [ssrManifestPlugin(config)] : []),
236245
...(!config.logLevel || config.logLevel === 'info'
237246
? [buildReporterPlugin(config)]
238247
: [])
@@ -272,13 +281,15 @@ async function doBuild(
272281
inlineConfig: InlineConfig = {}
273282
): Promise<RollupOutput | RollupOutput[]> {
274283
const config = await resolveConfig(inlineConfig, 'build', 'production')
275-
config.logger.info(chalk.cyan(`building for ${config.mode}...`))
276-
277284
const options = config.build
278285
const ssr = !!options.ssr
279286
const libOptions = options.lib
280-
const resolve = (p: string) => path.resolve(config.root, p)
281287

288+
config.logger.info(
289+
chalk.cyan(`building ${ssr ? `SSR bundle ` : ``}for ${config.mode}...`)
290+
)
291+
292+
const resolve = (p: string) => path.resolve(config.root, p)
282293
const input = libOptions
283294
? libOptions.entry
284295
: options.rollupOptions?.input || resolve('index.html')
@@ -351,6 +362,7 @@ async function doBuild(
351362
// #764 add `Symbol.toStringTag` when build es module into cjs chunk
352363
// #1048 add `Symbol.toStringTag` for module default export
353364
namespaceToStringTag: true,
365+
inlineDynamicImports: ssr && typeof input === 'string',
354366
...output
355367
})
356368
}

‎packages/vite/src/node/cli.ts

+1
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ cli
120120
`or specify minifier to use (default: terser)`
121121
)
122122
.option('--manifest', `[boolean] emit build manifest json`)
123+
.option('--ssrManifest', `[boolean] emit ssr manifest json`)
123124
.option(
124125
'--emptyOutDir',
125126
`[boolean] force empty outDir when it's outside of root`

‎packages/vite/src/node/plugins/asset.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,10 @@ export function assetPlugin(config: ResolvedConfig): Plugin {
8080
// do not emit assets for SSR build
8181
if (config.command === 'build' && config.build.ssr) {
8282
for (const file in bundle) {
83-
if (bundle[file].type === 'asset') {
83+
if (
84+
bundle[file].type === 'asset' &&
85+
!file.includes('ssr-manifest.json')
86+
) {
8487
delete bundle[file]
8588
}
8689
}

‎packages/vite/src/node/plugins/manifest.ts

+7-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
11
import { Plugin } from '../plugin'
22

33
export function manifestPlugin(): Plugin {
4-
const manifest: Record<string, { file: string; imports?: string[] }> = {}
4+
const manifest: Record<
5+
string,
6+
{
7+
file: string
8+
imports?: string[]
9+
}
10+
> = {}
511

612
return {
713
name: 'vite:manifest',
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import { ResolvedConfig } from '..'
2+
import { Plugin } from '../plugin'
3+
import { chunkToEmittedCssFileMap } from '../plugins/css'
4+
5+
export function ssrManifestPlugin(config: ResolvedConfig): Plugin {
6+
// module id => preload assets mapping
7+
const ssrManifest: Record<string, string[]> = {}
8+
const base = config.build.base
9+
10+
return {
11+
name: 'vite:manifest',
12+
generateBundle(_options, bundle) {
13+
for (const file in bundle) {
14+
const chunk = bundle[file]
15+
if (chunk.type === 'chunk' && !chunk.isEntry) {
16+
// links for entry chunks are already generated in static HTML
17+
// so we only need to record info for non-entry chunks
18+
// TODO: also include non-CSS assets
19+
const cssFileHandle = chunkToEmittedCssFileMap.get(chunk)
20+
const cssFile = cssFileHandle && this.getFileName(cssFileHandle)
21+
for (const id in chunk.modules) {
22+
const mappedChunks = ssrManifest[id] || (ssrManifest[id] = [])
23+
mappedChunks.push(base + chunk.fileName)
24+
if (cssFile) {
25+
mappedChunks.push(base + cssFile)
26+
}
27+
}
28+
}
29+
}
30+
31+
this.emitFile({
32+
fileName: 'ssr-manifest.json',
33+
type: 'asset',
34+
source: JSON.stringify(ssrManifest, null, 2)
35+
})
36+
}
37+
}
38+
}

0 commit comments

Comments
 (0)
Please sign in to comment.