You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
269 lines
8.6 KiB
269 lines
8.6 KiB
// 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) |
|
|
|
// FIXME: a temporary workaround to get accurate contenthash in `applyLegacy` |
|
// Should use a better fix per discussions at <https://github.com/jantimon/html-webpack-plugin/issues/1554#issuecomment-753653580> |
|
webpackConfig.optimization |
|
.set('realContentHash', false) |
|
|
|
// code splitting |
|
if (process.env.NODE_ENV !== 'test') { |
|
webpackConfig.optimization.splitChunks({ |
|
cacheGroups: { |
|
defaultVendors: { |
|
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') |
|
|
|
const htmlOptions = { |
|
title: api.service.pkg.name, |
|
scriptLoading: 'defer', |
|
templateParameters: (compilation, assets, assetTags, pluginOptions) => { |
|
// enhance html-webpack-plugin's built in template params |
|
return Object.assign({ |
|
compilation: compilation, |
|
webpackConfig: compilation.options, |
|
htmlWebpackPlugin: { |
|
tags: assetTags, |
|
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) |
|
]) |
|
} |
|
|
|
// 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(api.resolve(htmlOptions.template).replace(/\\/g, '/')) |
|
|
|
webpackConfig |
|
.plugin('html') |
|
.use(HTMLPlugin, [htmlOptions]) |
|
|
|
// FIXME: need to test out preload plugin's compatibility with html-webpack-plugin 4/5 |
|
// 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))) |
|
|
|
// trim inline loader |
|
// * See https://github.com/jantimon/html-webpack-plugin/blob/master/docs/template-option.md#2-setting-a-loader-directly-for-the-template |
|
const templateWithoutLoader = template.replace(/^.+!/, '').replace(/\?.+$/, '') |
|
|
|
// resolve page index template |
|
const hasDedicatedTemplate = fs.existsSync(api.resolve(templateWithoutLoader)) |
|
const templatePath = hasDedicatedTemplate |
|
? template |
|
: fs.existsSync(htmlPath) |
|
? htmlPath |
|
: defaultHtmlPath |
|
|
|
publicCopyIgnore.push(api.resolve(templateWithoutLoader).replace(/\\/g, '/')) |
|
|
|
// 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]) |
|
}) |
|
|
|
// FIXME: preload plugin is not compatible with webpack 5 / html-webpack-plugin 4 yet |
|
// 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') |
|
const CopyWebpackPlugin = require('copy-webpack-plugin') |
|
const PlaceholderPlugin = class PlaceholderPlugin { apply () {} } |
|
|
|
const copyOptions = { |
|
patterns: [{ |
|
from: publicDir, |
|
to: outputDir, |
|
toType: 'dir', |
|
noErrorOnMissing: true, |
|
globOptions: { |
|
ignore: publicCopyIgnore |
|
}, |
|
info: { |
|
minimized: true |
|
} |
|
}] |
|
} |
|
|
|
if (fs.existsSync(publicDir)) { |
|
if (isLegacyBundle) { |
|
webpackConfig.plugin('copy').use(PlaceholderPlugin, [copyOptions]) |
|
} else { |
|
webpackConfig.plugin('copy').use(CopyWebpackPlugin, [copyOptions]) |
|
} |
|
} |
|
}) |
|
}
|
|
|