Spaces:
Sleeping
Sleeping
| // config that are specific to --target app | |
| const fs = require('fs') | |
| const path = require('path') | |
| // ensure the filename passed to html-webpack-plugin is a relative path | |
| // because it cannot correctly handle absolute paths | |
| function ensureRelative (outputDir, _path) { | |
| if (path.isAbsolute(_path)) { | |
| return path.relative(outputDir, _path) | |
| } else { | |
| return _path | |
| } | |
| } | |
| module.exports = (api, options) => { | |
| api.chainWebpack(webpackConfig => { | |
| // only apply when there's no alternative target | |
| if (process.env.VUE_CLI_BUILD_TARGET && process.env.VUE_CLI_BUILD_TARGET !== 'app') { | |
| return | |
| } | |
| const isProd = process.env.NODE_ENV === 'production' | |
| const isLegacyBundle = process.env.VUE_CLI_MODERN_MODE && !process.env.VUE_CLI_MODERN_BUILD | |
| const outputDir = api.resolve(options.outputDir) | |
| const getAssetPath = require('../util/getAssetPath') | |
| const outputFilename = getAssetPath( | |
| options, | |
| `js/[name]${isLegacyBundle ? `-legacy` : ``}${isProd && options.filenameHashing ? '.[contenthash:8]' : ''}.js` | |
| ) | |
| webpackConfig | |
| .output | |
| .filename(outputFilename) | |
| .chunkFilename(outputFilename) | |
| // code splitting | |
| if (process.env.NODE_ENV !== 'test') { | |
| webpackConfig | |
| .optimization.splitChunks({ | |
| cacheGroups: { | |
| vendors: { | |
| name: `chunk-vendors`, | |
| test: /[\\/]node_modules[\\/]/, | |
| priority: -10, | |
| chunks: 'initial' | |
| }, | |
| common: { | |
| name: `chunk-common`, | |
| minChunks: 2, | |
| priority: -20, | |
| chunks: 'initial', | |
| reuseExistingChunk: true | |
| } | |
| } | |
| }) | |
| } | |
| // HTML plugin | |
| const resolveClientEnv = require('../util/resolveClientEnv') | |
| // #1669 html-webpack-plugin's default sort uses toposort which cannot | |
| // handle cyclic deps in certain cases. Monkey patch it to handle the case | |
| // before we can upgrade to its 4.0 version (incompatible with preload atm) | |
| const chunkSorters = require('html-webpack-plugin/lib/chunksorter') | |
| const depSort = chunkSorters.dependency | |
| chunkSorters.auto = chunkSorters.dependency = (chunks, ...args) => { | |
| try { | |
| return depSort(chunks, ...args) | |
| } catch (e) { | |
| // fallback to a manual sort if that happens... | |
| return chunks.sort((a, b) => { | |
| // make sure user entry is loaded last so user CSS can override | |
| // vendor CSS | |
| if (a.id === 'app') { | |
| return 1 | |
| } else if (b.id === 'app') { | |
| return -1 | |
| } else if (a.entry !== b.entry) { | |
| return b.entry ? -1 : 1 | |
| } | |
| return 0 | |
| }) | |
| } | |
| } | |
| const htmlOptions = { | |
| title: api.service.pkg.name, | |
| templateParameters: (compilation, assets, pluginOptions) => { | |
| // enhance html-webpack-plugin's built in template params | |
| let stats | |
| return Object.assign({ | |
| // make stats lazy as it is expensive | |
| get webpack () { | |
| return stats || (stats = compilation.getStats().toJson()) | |
| }, | |
| compilation: compilation, | |
| webpackConfig: compilation.options, | |
| htmlWebpackPlugin: { | |
| files: assets, | |
| options: pluginOptions | |
| } | |
| }, resolveClientEnv(options, true /* raw */)) | |
| } | |
| } | |
| // handle indexPath | |
| if (options.indexPath !== 'index.html') { | |
| // why not set filename for html-webpack-plugin? | |
| // 1. It cannot handle absolute paths | |
| // 2. Relative paths causes incorrect SW manifest to be generated (#2007) | |
| webpackConfig | |
| .plugin('move-index') | |
| .use(require('../webpack/MovePlugin'), [ | |
| path.resolve(outputDir, 'index.html'), | |
| path.resolve(outputDir, options.indexPath) | |
| ]) | |
| } | |
| if (isProd) { | |
| Object.assign(htmlOptions, { | |
| minify: { | |
| removeComments: true, | |
| collapseWhitespace: true, | |
| collapseBooleanAttributes: true, | |
| removeScriptTypeAttributes: true | |
| // more options: | |
| // https://github.com/kangax/html-minifier#options-quick-reference | |
| } | |
| }) | |
| // keep chunk ids stable so async chunks have consistent hash (#1916) | |
| webpackConfig | |
| .plugin('named-chunks') | |
| .use(require('webpack/lib/NamedChunksPlugin'), [chunk => { | |
| if (chunk.name) { | |
| return chunk.name | |
| } | |
| const hash = require('hash-sum') | |
| const joinedHash = hash( | |
| Array.from(chunk.modulesIterable, m => m.id).join('_') | |
| ) | |
| return `chunk-` + joinedHash | |
| }]) | |
| } | |
| // resolve HTML file(s) | |
| const HTMLPlugin = require('html-webpack-plugin') | |
| const PreloadPlugin = require('@vue/preload-webpack-plugin') | |
| const multiPageConfig = options.pages | |
| const htmlPath = api.resolve('public/index.html') | |
| const defaultHtmlPath = path.resolve(__dirname, 'index-default.html') | |
| const publicCopyIgnore = ['.DS_Store'] | |
| if (!multiPageConfig) { | |
| // default, single page setup. | |
| htmlOptions.template = fs.existsSync(htmlPath) | |
| ? htmlPath | |
| : defaultHtmlPath | |
| publicCopyIgnore.push({ | |
| glob: path.relative(api.resolve('public'), api.resolve(htmlOptions.template)), | |
| matchBase: false | |
| }) | |
| webpackConfig | |
| .plugin('html') | |
| .use(HTMLPlugin, [htmlOptions]) | |
| if (!isLegacyBundle) { | |
| // inject preload/prefetch to HTML | |
| webpackConfig | |
| .plugin('preload') | |
| .use(PreloadPlugin, [{ | |
| rel: 'preload', | |
| include: 'initial', | |
| fileBlacklist: [/\.map$/, /hot-update\.js$/] | |
| }]) | |
| webpackConfig | |
| .plugin('prefetch') | |
| .use(PreloadPlugin, [{ | |
| rel: 'prefetch', | |
| include: 'asyncChunks' | |
| }]) | |
| } | |
| } else { | |
| // multi-page setup | |
| webpackConfig.entryPoints.clear() | |
| const pages = Object.keys(multiPageConfig) | |
| const normalizePageConfig = c => typeof c === 'string' ? { entry: c } : c | |
| pages.forEach(name => { | |
| const pageConfig = normalizePageConfig(multiPageConfig[name]) | |
| const { | |
| entry, | |
| template = `public/${name}.html`, | |
| filename = `${name}.html`, | |
| chunks = ['chunk-vendors', 'chunk-common', name] | |
| } = pageConfig | |
| // Currently Cypress v3.1.0 comes with a very old version of Node, | |
| // which does not support object rest syntax. | |
| // (https://github.com/cypress-io/cypress/issues/2253) | |
| // So here we have to extract the customHtmlOptions manually. | |
| const customHtmlOptions = {} | |
| for (const key in pageConfig) { | |
| if ( | |
| !['entry', 'template', 'filename', 'chunks'].includes(key) | |
| ) { | |
| customHtmlOptions[key] = pageConfig[key] | |
| } | |
| } | |
| // inject entry | |
| const entries = Array.isArray(entry) ? entry : [entry] | |
| webpackConfig.entry(name).merge(entries.map(e => api.resolve(e))) | |
| // resolve page index template | |
| const hasDedicatedTemplate = fs.existsSync(api.resolve(template)) | |
| const templatePath = hasDedicatedTemplate | |
| ? template | |
| : fs.existsSync(htmlPath) | |
| ? htmlPath | |
| : defaultHtmlPath | |
| publicCopyIgnore.push({ | |
| glob: path.relative(api.resolve('public'), api.resolve(templatePath)), | |
| matchBase: false | |
| }) | |
| // inject html plugin for the page | |
| const pageHtmlOptions = Object.assign( | |
| {}, | |
| htmlOptions, | |
| { | |
| chunks, | |
| template: templatePath, | |
| filename: ensureRelative(outputDir, filename) | |
| }, | |
| customHtmlOptions | |
| ) | |
| webpackConfig | |
| .plugin(`html-${name}`) | |
| .use(HTMLPlugin, [pageHtmlOptions]) | |
| }) | |
| if (!isLegacyBundle) { | |
| pages.forEach(name => { | |
| const filename = ensureRelative( | |
| outputDir, | |
| normalizePageConfig(multiPageConfig[name]).filename || `${name}.html` | |
| ) | |
| webpackConfig | |
| .plugin(`preload-${name}`) | |
| .use(PreloadPlugin, [{ | |
| rel: 'preload', | |
| includeHtmlNames: [filename], | |
| include: { | |
| type: 'initial', | |
| entries: [name] | |
| }, | |
| fileBlacklist: [/\.map$/, /hot-update\.js$/] | |
| }]) | |
| webpackConfig | |
| .plugin(`prefetch-${name}`) | |
| .use(PreloadPlugin, [{ | |
| rel: 'prefetch', | |
| includeHtmlNames: [filename], | |
| include: { | |
| type: 'asyncChunks', | |
| entries: [name] | |
| } | |
| }]) | |
| }) | |
| } | |
| } | |
| // CORS and Subresource Integrity | |
| if (options.crossorigin != null || options.integrity) { | |
| webpackConfig | |
| .plugin('cors') | |
| .use(require('../webpack/CorsPlugin'), [{ | |
| crossorigin: options.crossorigin, | |
| integrity: options.integrity, | |
| publicPath: options.publicPath | |
| }]) | |
| } | |
| // copy static assets in public/ | |
| const publicDir = api.resolve('public') | |
| if (!isLegacyBundle && fs.existsSync(publicDir)) { | |
| webpackConfig | |
| .plugin('copy') | |
| .use(require('copy-webpack-plugin'), [[{ | |
| from: publicDir, | |
| to: outputDir, | |
| toType: 'dir', | |
| ignore: publicCopyIgnore | |
| }]]) | |
| } | |
| }) | |
| } | |