123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306 |
- // 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
- }]])
- }
- })
- }
|