1 | const path = require('path')
|
2 | const fs = require('fs-extra')
|
3 | const { createBundleRenderer } = require('vue-server-renderer')
|
4 | const { minify } = require('html-minifier')
|
5 |
|
6 | module.exports = async (
|
7 | api,
|
8 | { staticRoutes, serverBundle, clientManifest, htmlSkeletion, resourceHints }
|
9 | ) => {
|
10 | const routes = [...new Set(['/'].concat(staticRoutes || []))]
|
11 | const renderer = createBundleRenderer(serverBundle, {
|
12 | clientManifest,
|
13 | runInNewContext: false,
|
14 | inject: false,
|
15 | basedir: api.resolveCwd()
|
16 | })
|
17 |
|
18 | const generatePage = async route => {
|
19 | const context = { url: route }
|
20 | const app = await renderer.renderToString(context)
|
21 | const {
|
22 | title,
|
23 | htmlAttrs,
|
24 | headAttrs,
|
25 | bodyAttrs,
|
26 | link,
|
27 | style,
|
28 | script,
|
29 | noscript,
|
30 | meta
|
31 | } = context.meta.inject()
|
32 |
|
33 | const html = htmlSkeletion
|
34 | .replace(
|
35 | /<title>.*<\/title>/,
|
36 | () => `
|
37 | ${meta.text()}
|
38 | ${title.text()}
|
39 | ${link.text()}
|
40 | ${context.renderStyles()}
|
41 | ${style.text()}
|
42 | ${script.text()}
|
43 | ${noscript.text()}
|
44 | ${resourceHints ? context.renderResourceHints() : ''}
|
45 | `
|
46 | )
|
47 | .replace(
|
48 | /<html(\s+)?(.*)>/,
|
49 | `<html data-poi-ssr ${htmlAttrs.text()}$1$2>`
|
50 | )
|
51 | .replace(/<head(\s+)?(.*)>/, `<head ${headAttrs.text()}$1$2>`)
|
52 | .replace(/<body(\s+)?(.*)>/, `<body ${bodyAttrs.text()}$1$2>`)
|
53 | .replace(
|
54 | '</body>',
|
55 | `${context.renderState()}${context.renderScripts()}</body>`
|
56 | )
|
57 | .replace(`<div id="app"></div>`, app)
|
58 | const outPath = getFilePath(api.resolveOutDir(), route)
|
59 | console.log(`> Rendering ${route}`)
|
60 | await fs.outputFile(
|
61 | outPath,
|
62 | minify(html, {
|
63 | removeComments: true,
|
64 | collapseWhitespace: true,
|
65 | removeRedundantAttributes: true,
|
66 | useShortDoctype: true,
|
67 | removeEmptyAttributes: true,
|
68 | removeStyleLinkTypeAttributes: true,
|
69 | keepClosingSlash: true,
|
70 | minifyJS: true,
|
71 | minifyCSS: true,
|
72 | minifyURLs: true
|
73 | }),
|
74 | 'utf8'
|
75 | )
|
76 | }
|
77 |
|
78 | await Promise.all(routes.map(route => generatePage(route)))
|
79 | }
|
80 |
|
81 | function getFilePath(outDir, route) {
|
82 | if (!route.endsWith('.html')) {
|
83 | route = route.replace(/(\/)?$/, '/index.html')
|
84 | }
|
85 |
|
86 | return path.join(outDir, route)
|
87 | }
|