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