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.
234 lines
7.2 KiB
234 lines
7.2 KiB
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 |
|
}]) |
|
} |
|
}) |
|
}
|
|
|