UNPKG

5.61 kBJavaScriptView Raw
1const logger = require('@poi/logger')
2
3exports.name = 'builtin:config-css'
4
5exports.cli = ({ command, isProd }) => {
6 if (isProd) {
7 command.option('--no-extract-css', `Don't extract CSS files`)
8 } else {
9 command.option('--extract-css', 'Extract CSS files')
10 }
11}
12
13exports.apply = api => {
14 api.hook('createWebpackChain', (config, { type }) => {
15 const { loaderOptions = {}, extract: shouldExtract } = api.config.css || {}
16 const sourceMap = Boolean(api.config.output.sourceMap)
17 const isServer = type === 'server'
18
19 const hasPostCSSConfig = api.configLoader.resolve({
20 files: [
21 'postcss.config.js',
22 'package.json',
23 '.postcssrc',
24 '.postcssrc.js',
25 '.postcssrc.yaml',
26 '.postcssrc.json'
27 ],
28 packageKey: 'postcss'
29 })
30
31 if (hasPostCSSConfig) {
32 logger.debug('Applying custom PostCSS config at ' + hasPostCSSConfig)
33 }
34
35 // if building for production but not extracting CSS, we need to minimize
36 // the embbeded inline CSS as they will not be going through the optimizing
37 // plugin.
38 const needInlineMinification = api.config.output.minimize && !shouldExtract
39
40 const cssnanoOptions = {
41 safe: true,
42 autoprefixer: { disable: true },
43 mergeLonghand: false
44 }
45 if (sourceMap) {
46 cssnanoOptions.map = { inline: false }
47 }
48
49 const extractOptions = {
50 filename: api.config.output.fileNames.css,
51 chunkFilename: api.config.output.fileNames.css.replace(
52 /\.css$/,
53 '.chunk.css'
54 )
55 }
56
57 const createCSSRule = (lang, test, loader, options) => {
58 const applyLoaders = (rule, modules) => {
59 if (!isServer) {
60 if (shouldExtract) {
61 rule
62 .use('extract-css-loader')
63 .loader(require('mini-css-extract-plugin').loader)
64 } else {
65 rule
66 .use('vue-style-loader')
67 .loader(require.resolve('vue-style-loader'))
68 .options({
69 sourceMap
70 })
71 }
72 }
73
74 const cssLoaderOptions = Object.assign(
75 {
76 sourceMap,
77 modules,
78 localIdentName:
79 api.mode === 'production'
80 ? '[local]_[hash:base64:5]'
81 : '[path][name]__[local]--[hash:base64:5]',
82 importLoaders:
83 1 + // stylePostLoader injected by vue-loader
84 (hasPostCSSConfig ? 1 : 0) +
85 (needInlineMinification ? 1 : 0)
86 },
87 loaderOptions.css
88 )
89
90 rule
91 .use('css-loader')
92 .loader(
93 require.resolve(isServer ? 'css-loader/locals' : 'css-loader')
94 )
95 .options(cssLoaderOptions)
96
97 if (needInlineMinification) {
98 rule
99 .use('minify-inline-css')
100 .loader(require.resolve('postcss-loader'))
101 .options({
102 plugins: [require('cssnano')(cssnanoOptions)]
103 })
104 }
105
106 if (hasPostCSSConfig) {
107 rule
108 .use('postcss-loader')
109 .loader(require.resolve('postcss-loader'))
110 .options(Object.assign({ sourceMap }, loaderOptions.postcss))
111 }
112
113 if (loader) {
114 rule
115 .use(loader)
116 .loader(loader)
117 .options(Object.assign({ sourceMap }, options))
118 }
119 }
120
121 const baseRule = config.module.rule(lang).test(test)
122
123 // Prevent webpack from unexpectedly dropping local CSS files
124 // https://webpack.js.org/guides/tree-shaking/#mark-the-file-as-side-effect-free
125 // https://github.com/webpack/webpack/issues/6741
126 baseRule.sideEffects(true)
127
128 // rules for <style lang="module">
129 const vueModulesRule = baseRule
130 .oneOf('vue-modules')
131 .resourceQuery(/module/)
132 applyLoaders(vueModulesRule, true)
133
134 // rules for <style>
135 const vueNormalRule = baseRule.oneOf('vue').resourceQuery(/\?vue/)
136 applyLoaders(vueNormalRule, false)
137
138 // rules for *.module.* files
139 const extModulesRule = baseRule
140 .oneOf('normal-modules')
141 .test(/\.module\.\w+$/)
142 applyLoaders(extModulesRule, true)
143
144 // rules for normal CSS imports
145 const normalRule = baseRule.oneOf('normal')
146 applyLoaders(normalRule, false)
147 }
148
149 if (shouldExtract) {
150 config
151 .plugin('extract-css')
152 .use(require('mini-css-extract-plugin'), [extractOptions])
153
154 const OptimizeCSSPlugin = require('@intervolga/optimize-cssnano-plugin')
155 config.plugin('optimize-css').use(OptimizeCSSPlugin, [
156 {
157 sourceMap,
158 cssnanoOptions
159 }
160 ])
161 }
162
163 createCSSRule('css', /\.css$/)
164 createCSSRule('postcss', /\.p(ost)?css$/)
165
166 const sassImplementation = api.hasDependency('sass')
167 ? api.localRequire('sass')
168 : undefined
169 createCSSRule(
170 'scss',
171 /\.scss$/,
172 'sass-loader',
173 Object.assign(
174 {
175 implementation: sassImplementation
176 },
177 loaderOptions.sass
178 )
179 )
180 createCSSRule(
181 'sass',
182 /\.sass$/,
183 'sass-loader',
184 Object.assign(
185 {
186 indentedSyntax: true,
187 implementation: sassImplementation
188 },
189 loaderOptions.sass
190 )
191 )
192
193 createCSSRule('less', /\.less$/, 'less-loader', loaderOptions.less)
194 createCSSRule(
195 'stylus',
196 /\.styl(us)?$/,
197 'stylus-loader',
198 Object.assign(
199 {
200 preferPathResolver: 'webpack'
201 },
202 loaderOptions.stylus
203 )
204 )
205 })
206}