1 | const logger = require('@poi/logger')
|
2 |
|
3 | exports.name = 'builtin:config-css'
|
4 |
|
5 | exports.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 |
|
13 | exports.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 |
|
36 |
|
37 |
|
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 +
|
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 |
|
124 |
|
125 |
|
126 | baseRule.sideEffects(true)
|
127 |
|
128 |
|
129 | const vueModulesRule = baseRule
|
130 | .oneOf('vue-modules')
|
131 | .resourceQuery(/module/)
|
132 | applyLoaders(vueModulesRule, true)
|
133 |
|
134 |
|
135 | const vueNormalRule = baseRule.oneOf('vue').resourceQuery(/\?vue/)
|
136 | applyLoaders(vueNormalRule, false)
|
137 |
|
138 |
|
139 | const extModulesRule = baseRule
|
140 | .oneOf('normal-modules')
|
141 | .test(/\.module\.\w+$/)
|
142 | applyLoaders(extModulesRule, true)
|
143 |
|
144 |
|
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 | }
|