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.
		
		
		
		
			
				
					308 lines
				
				9.5 KiB
			
		
		
			
		
	
	
					308 lines
				
				9.5 KiB
			| 
								 
											4 years ago
										 
									 | 
							
								// 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,
							 | 
						||
| 
								 | 
							
								          removeAttributeQuotes: 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
							 | 
						||
| 
								 | 
							
								          }]])
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  })
							 | 
						||
| 
								 | 
							
								}
							 |