UNPKG

4.55 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'
99 ? {
100 entry: pages[entryName]
101 }
102 : pages[entryName]
103 )
104 if (!page.template.startsWith('!')) {
105 page.template = api.resolveCwd(page.template)
106 }
107 config.plugin(`html-page-${entryName}`).use(HtmlPlugin, [page])
108 }
109 } else {
110 const page = merge({}, defaultHtmlOpts, html)
111 if (!page.template.startsWith('!')) {
112 page.template = api.resolveCwd(page.template)
113 }
114 config.plugin('html').use(HtmlPlugin, [page])
115 }
116
117 config
118 .plugin('inline-runtime-chunk')
119 .use(require('@poi/dev-utils/InlineChunkHtmlPlugin'), [
120 require('html-webpack-plugin'),
121 [/runtime~.+[.]js/]
122 ])
123 }
124 })
125}
126
127function templateParametersGenerator(data) {
128 const { htmlTagObjectToString } = require('html-webpack-plugin/lib/html-tags')
129
130 return (compilation, assets, assetTags, options) => {
131 const { xhtml } = options
132 assetTags.headTags.toString = function() {
133 return this.map(assetTagObject =>
134 htmlTagObjectToString(assetTagObject, xhtml)
135 ).join('')
136 }
137 assetTags.bodyTags.toString = function() {
138 return this.map(assetTagObject =>
139 htmlTagObjectToString(assetTagObject, xhtml)
140 ).join('')
141 }
142
143 return Object.assign(
144 {
145 compilation,
146 webpackConfig: compilation.options,
147 htmlWebpackPlugin: {
148 tags: assetTags,
149 files: assets,
150 options
151 },
152 html: options
153 },
154 data
155 )
156 }
157}