poi
Version:
A zero-config bundler for JavaScript applications.
214 lines (188 loc) • 5.74 kB
JavaScript
const logger = require('@poi/logger')
exports.name = 'builtin:config-css'
exports.cli = ({ command, isProd }) => {
if (isProd) {
command.option('--no-extract-css', `Don't extract CSS files`)
} else {
command.option('--extract-css', 'Extract CSS files')
}
}
exports.apply = api => {
api.hook('createWebpackChain', (config, { type }) => {
const {
loaderOptions = {},
extract: shouldExtract,
sourceMap = api.config.output.sourceMap
} = api.config.css || {}
const isServer = type === 'server'
const hasPostCSSConfig = api.configLoader.resolve({
files: [
'postcss.config.js',
'package.json',
'.postcssrc',
'.postcssrc.js',
'.postcssrc.yaml',
'.postcssrc.json'
],
packageKey: 'postcss'
})
if (hasPostCSSConfig) {
logger.debug('Applying custom PostCSS config at ' + hasPostCSSConfig)
}
// 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 = api.config.output.minimize && !shouldExtract
const cssnanoOptions = {
safe: true,
autoprefixer: { disable: true },
mergeLonghand: false
}
if (sourceMap) {
cssnanoOptions.map = { inline: false }
}
const extractOptions = {
filename: api.config.output.fileNames.css,
chunkFilename: api.config.output.fileNames.css.replace(
/\.css$/,
'.chunk.css'
)
}
const createCSSRule = (lang, test, loader, options) => {
const applyLoaders = (rule, modules) => {
if (!isServer) {
if (shouldExtract) {
rule
.use('extract-css-loader')
.loader(require('extract-css-chunks-webpack-plugin').loader)
.options({
hmr: api.mode === 'development',
reloadAll: true
})
} else {
rule
.use('vue-style-loader')
.loader(require.resolve('vue-style-loader'))
.options({
sourceMap
})
}
}
const cssLoaderOptions = Object.assign(
{
sourceMap,
modules: modules
? {
localIdentName: '[name]_[local]_[hash:base64:5]'
}
: false,
importLoaders:
1 + // stylePostLoader injected by vue-loader
(hasPostCSSConfig ? 1 : 0) +
(needInlineMinification ? 1 : 0),
onlyLocals: isServer,
localsConvention: 'camelCase'
},
loaderOptions.css
)
rule
.use('css-loader')
.loader(require.resolve('css-loader'))
.options(cssLoaderOptions)
if (needInlineMinification) {
rule
.use('minify-inline-css')
.loader(require.resolve('postcss-loader'))
.options({
plugins: [require('cssnano')(cssnanoOptions)]
})
}
if (hasPostCSSConfig) {
rule
.use('postcss-loader')
.loader(require.resolve('postcss-loader'))
.options(Object.assign({ sourceMap }, loaderOptions.postcss))
}
if (loader) {
rule
.use(loader)
.loader(loader)
.options(Object.assign({ sourceMap }, options))
}
}
const baseRule = config.module.rule(lang).test(test)
// Prevent webpack from unexpectedly dropping local CSS files
// https://webpack.js.org/guides/tree-shaking/#mark-the-file-as-side-effect-free
// https://github.com/webpack/webpack/issues/6741
baseRule.sideEffects(true)
// 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, false)
}
if (shouldExtract) {
config
.plugin('extract-css')
.use(require('extract-css-chunks-webpack-plugin'), [extractOptions])
const OptimizeCSSPlugin = require('@intervolga/optimize-cssnano-plugin')
config.plugin('optimize-css').use(OptimizeCSSPlugin, [
{
sourceMap,
cssnanoOptions
}
])
}
createCSSRule('css', /\.css$/)
createCSSRule('postcss', /\.p(ost)?css$/)
const sassImplementation = api.hasDependency('sass')
? api.localRequire('sass')
: undefined
createCSSRule(
'scss',
/\.scss$/,
'sass-loader',
Object.assign(
{
implementation: sassImplementation
},
loaderOptions.sass
)
)
createCSSRule(
'sass',
/\.sass$/,
'sass-loader',
Object.assign(
{
indentedSyntax: true,
implementation: sassImplementation
},
loaderOptions.sass
)
)
createCSSRule('less', /\.less$/, 'less-loader', loaderOptions.less)
createCSSRule(
'stylus',
/\.styl(us)?$/,
'stylus-loader',
Object.assign(
{
preferPathResolver: 'webpack'
},
loaderOptions.stylus
)
)
})
}