UNPKG

4.34 kBJavaScriptView Raw
1const path = require('path')
2const fs = require('fs')
3const merge = require('lodash.merge')
4
5exports.name = 'builtin:config-html'
6
7exports.apply = api => {
8 api.hook('createWebpackChain', config => {
9 // No need to generate HTML files for CJS format
10 // For UMD format it still might be useful since you can use to test if your library works
11 if (api.config.output.format === 'cjs') return
12
13 const HtmlPlugin = require('html-webpack-plugin')
14 HtmlPlugin.__expression = `require('html-webpack-plugin')`
15
16 // Split vendors and common chunks
17 if (api.mode === 'production' && api.config.output.format === 'iife') {
18 config.optimization.splitChunks({
19 cacheGroups: {
20 vendors: {
21 name: `chunk-vendors`,
22 test: /[\\/]node_modules[\\/]/,
23 priority: -10,
24 chunks: 'initial'
25 },
26 common: {
27 name: `chunk-common`,
28 minChunks: 2,
29 priority: -20,
30 chunks: 'initial',
31 reuseExistingChunk: true
32 }
33 }
34 })
35 // Keep the runtime chunk seperated to enable long term caching
36 // https://twitter.com/wSokra/status/969679223278505985
37 config.optimization.runtimeChunk(true)
38 }
39
40 const DEFAULT_TEMPLATE = path.join(
41 __dirname,
42 '../webpack/default-template.html'
43 )
44
45 const getDefaultTemplate = () => {
46 if (!api.config.publicFolder) {
47 return DEFAULT_TEMPLATE
48 }
49
50 const userTemplatePath = api.resolveCwd(
51 api.config.publicFolder,
52 'index.html'
53 )
54 return (
55 api.config.defaultHtmlTemplate ||
56 (fs.existsSync(userTemplatePath) ? userTemplatePath : DEFAULT_TEMPLATE)
57 )
58 }
59
60 const defaultHtmlOpts = {
61 title: api.pkg.data.productName || api.pkg.data.name || 'Poi App',
62 template: getDefaultTemplate(),
63 templateParameters: templateParametersGenerator({
64 pkg: api.pkg.data,
65 envs: api.webpackUtils.envs,
66 constants: api.webpackUtils.constants
67 }),
68 filename: 'index.html',
69 minify: api.isProd
70 ? {
71 removeComments: true,
72 collapseWhitespace: true,
73 removeRedundantAttributes: true,
74 useShortDoctype: true,
75 removeEmptyAttributes: true,
76 removeStyleLinkTypeAttributes: true,
77 keepClosingSlash: true,
78 minifyJS: true,
79 minifyCSS: true,
80 minifyURLs: true
81 }
82 : undefined
83 }
84
85 const { pages } = api.config
86 const { html } = api.config.output
87
88 if (html !== false) {
89 if (pages) {
90 for (const entryName of Object.keys(pages)) {
91 const page = merge(
92 {},
93 defaultHtmlOpts,
94 {
95 filename: `${entryName}.html`,
96 chunks: ['chunk-vendors', 'chunk-common', entryName]
97 },
98 typeof pages[entryName] === 'string' ? {} : pages[entryName]
99 )
100 page.template = api.resolveCwd(page.template)
101 config.plugin(`html-page-${entryName}`).use(HtmlPlugin, [page])
102 }
103 } else {
104 const page = merge({}, defaultHtmlOpts, html)
105 page.template = api.resolveCwd(page.template)
106 config.plugin('html').use(HtmlPlugin, [page])
107 }
108
109 config
110 .plugin('inline-runtime-chunk')
111 .use(require('@poi/dev-utils/InlineChunkHtmlPlugin'), [
112 require('html-webpack-plugin'),
113 [/runtime~.+[.]js/]
114 ])
115 }
116 })
117}
118
119function templateParametersGenerator(data) {
120 const { htmlTagObjectToString } = require('html-webpack-plugin/lib/html-tags')
121
122 return (compilation, assets, assetTags, options) => {
123 const { xhtml } = options
124 assetTags.headTags.toString = function() {
125 return this.map(assetTagObject =>
126 htmlTagObjectToString(assetTagObject, xhtml)
127 ).join('')
128 }
129 assetTags.bodyTags.toString = function() {
130 return this.map(assetTagObject =>
131 htmlTagObjectToString(assetTagObject, xhtml)
132 ).join('')
133 }
134 return Object.assign(
135 {
136 compilation,
137 webpackConfig: compilation.options,
138 htmlWebpackPlugin: {
139 tags: assetTags,
140 files: assets,
141 options
142 },
143 html: options
144 },
145 data
146 )
147 }
148}