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.
235 lines
7.2 KiB
235 lines
7.2 KiB
4 weeks ago
|
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
|
||
|
}])
|
||
|
}
|
||
|
})
|
||
|
}
|