235 lines
		
	
	
		
			7.2 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			235 lines
		
	
	
		
			7.2 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
const fs = require('fs')
 | 
						|
const path = require('path')
 | 
						|
const { chalk, semver, loadModule } = require('@vue/cli-shared-utils')
 | 
						|
const isAbsoluteUrl = require('../util/isAbsoluteUrl')
 | 
						|
 | 
						|
const findExisting = (context, files) => {
 | 
						|
  for (const file of files) {
 | 
						|
    if (fs.existsSync(path.join(context, file))) {
 | 
						|
      return file
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
module.exports = (api, rootOptions) => {
 | 
						|
  api.chainWebpack(webpackConfig => {
 | 
						|
    const getAssetPath = require('../util/getAssetPath')
 | 
						|
    const shadowMode = !!process.env.VUE_CLI_CSS_SHADOW_MODE
 | 
						|
    const isProd = process.env.NODE_ENV === 'production'
 | 
						|
 | 
						|
    const {
 | 
						|
      extract = isProd,
 | 
						|
      sourceMap = false,
 | 
						|
      loaderOptions = {}
 | 
						|
    } = rootOptions.css || {}
 | 
						|
 | 
						|
    const shouldExtract = extract !== false && !shadowMode
 | 
						|
    const filename = getAssetPath(
 | 
						|
      rootOptions,
 | 
						|
      `css/[name]${rootOptions.filenameHashing ? '.[contenthash:8]' : ''}.css`
 | 
						|
    )
 | 
						|
    const extractOptions = Object.assign({
 | 
						|
      filename,
 | 
						|
      chunkFilename: filename
 | 
						|
    }, extract && typeof extract === 'object' ? extract : {})
 | 
						|
 | 
						|
    // when project publicPath is a relative path
 | 
						|
    // use relative publicPath in extracted CSS based on extract location
 | 
						|
    const cssPublicPath = (isAbsoluteUrl(rootOptions.publicPath) || rootOptions.publicPath.startsWith('/'))
 | 
						|
      ? rootOptions.publicPath
 | 
						|
      : process.env.VUE_CLI_BUILD_TARGET === 'lib'
 | 
						|
        // in lib mode, CSS is extracted to dist root.
 | 
						|
        ? './'
 | 
						|
        : '../'.repeat(
 | 
						|
          extractOptions.filename
 | 
						|
            .replace(/^\.[/\\]/, '')
 | 
						|
            .split(/[/\\]/g)
 | 
						|
            .length - 1
 | 
						|
        )
 | 
						|
 | 
						|
    // check if the project has a valid postcss config
 | 
						|
    // if it doesn't, don't use postcss-loader for direct style imports
 | 
						|
    // because otherwise it would throw error when attempting to load postcss config
 | 
						|
    const hasPostCSSConfig = !!(loaderOptions.postcss || api.service.pkg.postcss || findExisting(api.resolve('.'), [
 | 
						|
      '.postcssrc',
 | 
						|
      '.postcssrc.js',
 | 
						|
      'postcss.config.js',
 | 
						|
      '.postcssrc.yaml',
 | 
						|
      '.postcssrc.json'
 | 
						|
    ]))
 | 
						|
 | 
						|
    if (!hasPostCSSConfig) {
 | 
						|
      // #6342
 | 
						|
      // NPM 6 may incorrectly hoist postcss 7 to the same level of autoprefixer
 | 
						|
      // So we have to run a preflight check to tell the users how to fix it
 | 
						|
      const autoprefixerDirectory = path.dirname(require.resolve('autoprefixer/package.json'))
 | 
						|
      const postcssPkg = loadModule('postcss/package.json', autoprefixerDirectory)
 | 
						|
      const postcssVersion = postcssPkg.version
 | 
						|
      if (!semver.satisfies(postcssVersion, '8.x')) {
 | 
						|
        throw new Error(
 | 
						|
          `The package manager has hoisted a wrong version of ${chalk.cyan('postcss')}, ` +
 | 
						|
          `please run ${chalk.cyan('npm i postcss@8 -D')} to fix it.`
 | 
						|
        )
 | 
						|
      }
 | 
						|
 | 
						|
      loaderOptions.postcss = {
 | 
						|
        postcssOptions: {
 | 
						|
          plugins: [
 | 
						|
            require('autoprefixer')
 | 
						|
          ]
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    // if building for production but not extracting CSS, we need to minimize
 | 
						|
    // the embbeded inline CSS as they will not be going through the optimizing
 | 
						|
    // plugin.
 | 
						|
    const needInlineMinification = isProd && !shouldExtract
 | 
						|
 | 
						|
    const cssnanoOptions = {
 | 
						|
      preset: ['default', {
 | 
						|
        mergeLonghand: false,
 | 
						|
        cssDeclarationSorter: false
 | 
						|
      }]
 | 
						|
    }
 | 
						|
    if (rootOptions.productionSourceMap && sourceMap) {
 | 
						|
      cssnanoOptions.map = { inline: false }
 | 
						|
    }
 | 
						|
 | 
						|
    function createCSSRule (lang, test, loader, options) {
 | 
						|
      const baseRule = webpackConfig.module.rule(lang).test(test)
 | 
						|
 | 
						|
      // rules for <style module>
 | 
						|
      const vueModulesRule = baseRule.oneOf('vue-modules').resourceQuery(/module/)
 | 
						|
      applyLoaders(vueModulesRule, true)
 | 
						|
 | 
						|
      // rules for <style>
 | 
						|
      const vueNormalRule = baseRule.oneOf('vue').resourceQuery(/\?vue/)
 | 
						|
      applyLoaders(vueNormalRule)
 | 
						|
 | 
						|
      // rules for *.module.* files
 | 
						|
      const extModulesRule = baseRule.oneOf('normal-modules').test(/\.module\.\w+$/)
 | 
						|
      applyLoaders(extModulesRule)
 | 
						|
 | 
						|
      // rules for normal CSS imports
 | 
						|
      const normalRule = baseRule.oneOf('normal')
 | 
						|
      applyLoaders(normalRule)
 | 
						|
 | 
						|
      function applyLoaders (rule, forceCssModule = false) {
 | 
						|
        if (shouldExtract) {
 | 
						|
          rule
 | 
						|
            .use('extract-css-loader')
 | 
						|
            .loader(require('mini-css-extract-plugin').loader)
 | 
						|
            .options({
 | 
						|
              publicPath: cssPublicPath
 | 
						|
            })
 | 
						|
        } else {
 | 
						|
          rule
 | 
						|
            .use('vue-style-loader')
 | 
						|
            .loader(require.resolve('vue-style-loader'))
 | 
						|
            .options({
 | 
						|
              sourceMap,
 | 
						|
              shadowMode
 | 
						|
            })
 | 
						|
        }
 | 
						|
 | 
						|
        const cssLoaderOptions = Object.assign({
 | 
						|
          sourceMap,
 | 
						|
          importLoaders: (
 | 
						|
            1 + // stylePostLoader injected by vue-loader
 | 
						|
            1 + // postcss-loader
 | 
						|
            (needInlineMinification ? 1 : 0)
 | 
						|
          )
 | 
						|
        }, loaderOptions.css)
 | 
						|
 | 
						|
        if (forceCssModule) {
 | 
						|
          cssLoaderOptions.modules = {
 | 
						|
            ...cssLoaderOptions.modules,
 | 
						|
            auto: () => true
 | 
						|
          }
 | 
						|
        }
 | 
						|
 | 
						|
        if (cssLoaderOptions.modules) {
 | 
						|
          cssLoaderOptions.modules = {
 | 
						|
            localIdentName: '[name]_[local]_[hash:base64:5]',
 | 
						|
            ...cssLoaderOptions.modules
 | 
						|
          }
 | 
						|
        }
 | 
						|
 | 
						|
        rule
 | 
						|
          .use('css-loader')
 | 
						|
          .loader(require.resolve('css-loader'))
 | 
						|
          .options(cssLoaderOptions)
 | 
						|
 | 
						|
        if (needInlineMinification) {
 | 
						|
          rule
 | 
						|
            .use('cssnano')
 | 
						|
            .loader(require.resolve('postcss-loader'))
 | 
						|
            .options({
 | 
						|
              sourceMap,
 | 
						|
              postcssOptions: {
 | 
						|
                plugins: [require('cssnano')(cssnanoOptions)]
 | 
						|
              }
 | 
						|
            })
 | 
						|
        }
 | 
						|
 | 
						|
        rule
 | 
						|
          .use('postcss-loader')
 | 
						|
          .loader(require.resolve('postcss-loader'))
 | 
						|
          .options(Object.assign({ sourceMap }, loaderOptions.postcss))
 | 
						|
 | 
						|
        if (loader) {
 | 
						|
          let resolvedLoader
 | 
						|
          try {
 | 
						|
            resolvedLoader = require.resolve(loader)
 | 
						|
          } catch (error) {
 | 
						|
            resolvedLoader = loader
 | 
						|
          }
 | 
						|
 | 
						|
          rule
 | 
						|
            .use(loader)
 | 
						|
            .loader(resolvedLoader)
 | 
						|
            .options(Object.assign({ sourceMap }, options))
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    createCSSRule('css', /\.css$/)
 | 
						|
    createCSSRule('postcss', /\.p(ost)?css$/)
 | 
						|
    createCSSRule('scss', /\.scss$/, 'sass-loader', Object.assign(
 | 
						|
      {},
 | 
						|
      loaderOptions.scss || loaderOptions.sass
 | 
						|
    ))
 | 
						|
    createCSSRule('sass', /\.sass$/, 'sass-loader', Object.assign(
 | 
						|
      {},
 | 
						|
      loaderOptions.sass,
 | 
						|
      {
 | 
						|
        sassOptions: Object.assign(
 | 
						|
          {},
 | 
						|
          loaderOptions.sass && loaderOptions.sass.sassOptions,
 | 
						|
          {
 | 
						|
            indentedSyntax: true
 | 
						|
          }
 | 
						|
        )
 | 
						|
      }
 | 
						|
    ))
 | 
						|
    createCSSRule('less', /\.less$/, 'less-loader', loaderOptions.less)
 | 
						|
    createCSSRule('stylus', /\.styl(us)?$/, 'stylus-loader', loaderOptions.stylus)
 | 
						|
 | 
						|
    // inject CSS extraction plugin
 | 
						|
    if (shouldExtract) {
 | 
						|
      webpackConfig
 | 
						|
        .plugin('extract-css')
 | 
						|
          .use(require('mini-css-extract-plugin'), [extractOptions])
 | 
						|
 | 
						|
      // minify extracted CSS
 | 
						|
      webpackConfig.optimization
 | 
						|
        .minimizer('css')
 | 
						|
          .use(require('css-minimizer-webpack-plugin'), [{
 | 
						|
            parallel: rootOptions.parallel,
 | 
						|
            minimizerOptions: cssnanoOptions
 | 
						|
          }])
 | 
						|
    }
 | 
						|
  })
 | 
						|
}
 |