123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253 |
- 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
- }])
- }
- }
- })
- }
|