270 lines
		
	
	
		
			8.6 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			270 lines
		
	
	
		
			8.6 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
// 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])
 | 
						|
      }
 | 
						|
    }
 | 
						|
  })
 | 
						|
}
 |