254 lines
		
	
	
		
			7.5 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			254 lines
		
	
	
		
			7.5 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| const fs = require('fs')
 | |
| const path = require('path')
 | |
| const { semver, warn, pauseSpinner, resumeSpinner } = require('@vue/cli-shared-utils')
 | |
| 
 | |
| 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'
 | |
| 
 | |
|     let sassLoaderVersion
 | |
|     try {
 | |
|       sassLoaderVersion = semver.major(require('sass-loader/package.json').version)
 | |
|     } catch (e) {}
 | |
|     if (sassLoaderVersion < 8) {
 | |
|       pauseSpinner()
 | |
|       warn('A new version of sass-loader is available. Please upgrade for best experience.')
 | |
|       resumeSpinner()
 | |
|     }
 | |
| 
 | |
|     const defaultSassLoaderOptions = {}
 | |
|     try {
 | |
|       defaultSassLoaderOptions.implementation = require('sass')
 | |
|       // since sass-loader 8, fibers will be automatically detected and used
 | |
|       if (sassLoaderVersion < 8) {
 | |
|         defaultSassLoaderOptions.fiber = require('fibers')
 | |
|       }
 | |
|     } catch (e) {}
 | |
| 
 | |
|     const {
 | |
|       extract = isProd,
 | |
|       sourceMap = false,
 | |
|       loaderOptions = {}
 | |
|     } = rootOptions.css || {}
 | |
| 
 | |
|     let { requireModuleExtension } = rootOptions.css || {}
 | |
|     if (typeof requireModuleExtension === 'undefined') {
 | |
|       if (loaderOptions.css && loaderOptions.css.modules) {
 | |
|         throw new Error('`css.requireModuleExtension` is required when custom css modules options provided')
 | |
|       }
 | |
|       requireModuleExtension = true
 | |
|     }
 | |
| 
 | |
|     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 : {})
 | |
| 
 | |
|     // use relative publicPath in extracted CSS based on extract location
 | |
|     const cssPublicPath = 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) {
 | |
|       loaderOptions.postcss = {
 | |
|         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 lang="module">
 | |
|       const vueModulesRule = baseRule.oneOf('vue-modules').resourceQuery(/module/)
 | |
|       applyLoaders(vueModulesRule, true)
 | |
| 
 | |
|       // rules for <style>
 | |
|       const vueNormalRule = baseRule.oneOf('vue').resourceQuery(/\?vue/)
 | |
|       applyLoaders(vueNormalRule, false)
 | |
| 
 | |
|       // rules for *.module.* files
 | |
|       const extModulesRule = baseRule.oneOf('normal-modules').test(/\.module\.\w+$/)
 | |
|       applyLoaders(extModulesRule, true)
 | |
| 
 | |
|       // rules for normal CSS imports
 | |
|       const normalRule = baseRule.oneOf('normal')
 | |
|       applyLoaders(normalRule, !requireModuleExtension)
 | |
| 
 | |
|       function applyLoaders (rule, isCssModule) {
 | |
|         if (shouldExtract) {
 | |
|           rule
 | |
|             .use('extract-css-loader')
 | |
|             .loader(require('mini-css-extract-plugin').loader)
 | |
|             .options({
 | |
|               hmr: !isProd,
 | |
|               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 (isCssModule) {
 | |
|           cssLoaderOptions.modules = {
 | |
|             localIdentName: '[name]_[local]_[hash:base64:5]',
 | |
|             ...cssLoaderOptions.modules
 | |
|           }
 | |
|         } else {
 | |
|           delete 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,
 | |
|               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(
 | |
|       {},
 | |
|       defaultSassLoaderOptions,
 | |
|       loaderOptions.scss || loaderOptions.sass
 | |
|     ))
 | |
|     if (sassLoaderVersion < 8) {
 | |
|       createCSSRule('sass', /\.sass$/, 'sass-loader', Object.assign(
 | |
|         {},
 | |
|         defaultSassLoaderOptions,
 | |
|         {
 | |
|           indentedSyntax: true
 | |
|         },
 | |
|         loaderOptions.sass
 | |
|       ))
 | |
|     } else {
 | |
|       createCSSRule('sass', /\.sass$/, 'sass-loader', Object.assign(
 | |
|         {},
 | |
|         defaultSassLoaderOptions,
 | |
|         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', Object.assign({
 | |
|       preferPathResolver: 'webpack'
 | |
|     }, loaderOptions.stylus))
 | |
| 
 | |
|     // inject CSS extraction plugin
 | |
|     if (shouldExtract) {
 | |
|       webpackConfig
 | |
|         .plugin('extract-css')
 | |
|           .use(require('mini-css-extract-plugin'), [extractOptions])
 | |
| 
 | |
|       // minify extracted CSS
 | |
|       if (isProd) {
 | |
|         webpackConfig
 | |
|           .plugin('optimize-css')
 | |
|             .use(require('@intervolga/optimize-cssnano-plugin'), [{
 | |
|               sourceMap: rootOptions.productionSourceMap && sourceMap,
 | |
|               cssnanoOptions
 | |
|             }])
 | |
|       }
 | |
|     }
 | |
|   })
 | |
| }
 | 
