UNPKG

5.31 kBJavaScriptView Raw
1const logger = require('@poi/logger')
2
3exports.name = 'builtin:config-css'
4
5exports.apply = api => {
6 api.hook('createCLI', ({ command }) => {
7 if (api.isProd) {
8 command.option('--no-extract-css', `Don't extract CSS files`)
9 } else {
10 command.option('--extract-css', 'Extract CSS files')
11 }
12 })
13
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(isServer ? 'css-loader/locals' : 'css-loader')
93 .options(cssLoaderOptions)
94
95 if (needInlineMinification) {
96 rule
97 .use('minify-inline-css')
98 .loader('postcss-loader')
99 .options({
100 plugins: [require('cssnano')(cssnanoOptions)]
101 })
102 }
103
104 if (hasPostCSSConfig) {
105 rule
106 .use('postcss-loader')
107 .loader('postcss-loader')
108 .options(Object.assign({ sourceMap }, loaderOptions.postcss))
109 }
110
111 if (loader) {
112 rule
113 .use(loader)
114 .loader(loader)
115 .options(Object.assign({ sourceMap }, options))
116 }
117 }
118
119 const baseRule = config.module.rule(lang).test(test)
120
121 // rules for <style lang="module">
122 const vueModulesRule = baseRule
123 .oneOf('vue-modules')
124 .resourceQuery(/module/)
125 applyLoaders(vueModulesRule, true)
126
127 // rules for <style>
128 const vueNormalRule = baseRule.oneOf('vue').resourceQuery(/\?vue/)
129 applyLoaders(vueNormalRule, false)
130
131 // rules for *.module.* files
132 const extModulesRule = baseRule
133 .oneOf('normal-modules')
134 .test(/\.module\.\w+$/)
135 applyLoaders(extModulesRule, true)
136
137 // rules for normal CSS imports
138 const normalRule = baseRule.oneOf('normal')
139 applyLoaders(normalRule, false)
140 }
141
142 if (shouldExtract) {
143 config
144 .plugin('extract-css')
145 .use(require('mini-css-extract-plugin'), [extractOptions])
146
147 const OptimizeCSSPlugin = require('@intervolga/optimize-cssnano-plugin')
148 config.plugin('optimize-css').use(OptimizeCSSPlugin, [
149 {
150 sourceMap,
151 cssnanoOptions
152 }
153 ])
154 }
155
156 createCSSRule('css', /\.css$/)
157 createCSSRule('postcss', /\.p(ost)?css$/)
158
159 const sassImplementation = api.hasDependency('sass')
160 ? api.localRequire('sass')
161 : undefined
162 createCSSRule(
163 'scss',
164 /\.scss$/,
165 'sass-loader',
166 Object.assign(
167 {
168 implementation: sassImplementation
169 },
170 loaderOptions.sass
171 )
172 )
173 createCSSRule(
174 'sass',
175 /\.sass$/,
176 'sass-loader',
177 Object.assign(
178 {
179 indentedSyntax: true,
180 implementation: sassImplementation
181 },
182 loaderOptions.sass
183 )
184 )
185
186 createCSSRule('less', /\.less$/, 'less-loader', loaderOptions.less)
187 createCSSRule(
188 'stylus',
189 /\.styl(us)?$/,
190 'stylus-loader',
191 Object.assign(
192 {
193 preferPathResolver: 'webpack'
194 },
195 loaderOptions.stylus
196 )
197 )
198 })
199}