|
1 | 1 | const mix = require("laravel-mix"); |
2 | 2 |
|
3 | | -class VueCssModule { |
| 3 | +class VueCssModules { |
4 | 4 | /** |
5 | 5 | * Register the component. |
6 | 6 | * |
7 | | - * When your component is called, all user parameters |
8 | | - * will be passed to this method. |
9 | | - * |
10 | | - * Ex: register(src, output) {} |
11 | | - * Ex: mix.yourPlugin('src/path', 'output/path'); |
12 | | - * |
13 | | - * @param {*} ...params |
14 | | - * @return {void} |
15 | | - * |
| 7 | + * @param {Object} options |
| 8 | + * @param {Boolean} [options.oneOf] |
| 9 | + * @param {Boolean} [options.preProcessor] |
| 10 | + * @param {String} [options.localIdentNameType] |
| 11 | + * @param {Object} [options.cssLoaderOptions] |
16 | 12 | */ |
17 | | - register({ |
18 | | - localIdentName = "[local]_[hash:base64:8]", |
19 | | - mode = "global", |
20 | | - oneOf = false, |
21 | | - sass = false, |
22 | | - } = {}) { |
23 | | - this.localIdentName = localIdentName; |
24 | | - this.mode = mode; |
25 | | - this.oneOf = oneOf; |
26 | | - this.sass = sass; |
| 13 | + register(options = {}) { |
| 14 | + const config = { |
| 15 | + modules: true, // {Boolean\|String\|Object} |
| 16 | + sourceMap: false, // {Boolean} |
| 17 | + importLoaders: 1, // {Number} // webpackDefault: 0 // laravel-mix default: 1 |
| 18 | + esModule: true, // {Boolean}, |
| 19 | + localIdentName: options.cssLoaderOptions.localIdentName |
| 20 | + ? options.cssLoaderOptions.localIdentName |
| 21 | + : this.defaultLocalIdentName(options.localIdentNameType), // {String} |
| 22 | + }; |
| 23 | + |
| 24 | + const cssLoaderOptions = { |
| 25 | + ...config, |
| 26 | + ...options.cssLoaderOptions, |
| 27 | + }; |
| 28 | + |
| 29 | + delete options.cssLoaderOptions; |
| 30 | + |
| 31 | + this.options = Object.assign( |
| 32 | + { |
| 33 | + oneOf: false, |
| 34 | + preProcessor: true, |
| 35 | + exclude: [], |
| 36 | + cssLoaderOptions: cssLoaderOptions, |
| 37 | + }, |
| 38 | + options |
| 39 | + ); |
| 40 | + |
| 41 | + console.log(this.options); |
27 | 42 | } |
28 | 43 |
|
29 | 44 | /** |
30 | 45 | * Override the generated webpack configuration. |
31 | 46 | * |
32 | | - * @param {Object} webpackConfig |
33 | | - * @return {void} |
| 47 | + * @param {Object} config |
34 | 48 | */ |
35 | | - webpackConfig(webpackConfig) { |
36 | | - webpackConfig.module.rules = webpackConfig.module.rules.map((rule) => { |
37 | | - if (!rule.loaders) { |
38 | | - return rule; |
39 | | - } |
| 49 | + webpackConfig(config) { |
| 50 | + // for css-loader |
| 51 | + const cssLoaders = config.module.rules.find( |
| 52 | + (rule) => rule.test.toString() === "/\\.css$/" |
| 53 | + ); |
| 54 | + |
| 55 | + if (this.options.oneOf) this.handleOneOfCss(cssLoaders); |
| 56 | + else this.handleCss(cssLoaders); |
40 | 57 |
|
41 | | - const sass = rule.loaders.find( |
42 | | - (loader) => loader.loader === "sass-loader" |
| 58 | + // only if pre-processor activated || default is active |
| 59 | + if (this.options.preProcessor) { |
| 60 | + // for sass-loader |
| 61 | + const sassLoaders = config.module.rules.find( |
| 62 | + (rule) => rule.test.toString() === "/\\.scss$/" |
43 | 63 | ); |
44 | | - const css = rule.loaders.find((loader) => loader.loader === "css-loader"); |
45 | | - |
46 | | - if (css != undefined) { |
47 | | - if (this.oneOf) { |
48 | | - const postCssConfig = rule.loaders.find( |
49 | | - (loader) => loader.loader === "postcss-loader" |
50 | | - ); |
51 | | - |
52 | | - delete rule.loaders; |
53 | | - Object.assign(rule, { |
54 | | - test: /\.css$/, |
55 | | - oneOf: [ |
56 | | - { |
57 | | - resourceQuery: /module/, |
58 | | - use: [ |
59 | | - "style-loader", |
60 | | - { |
61 | | - loader: "css-loader", |
62 | | - options: this[this.mode](), |
63 | | - }, |
64 | | - ], |
65 | | - }, |
66 | | - { |
67 | | - use: ["style-loader", postCssConfig], |
68 | | - }, |
69 | | - ], |
70 | | - }); |
71 | | - } else { |
72 | | - Object.assign(css.options, this[this.mode]()); |
73 | | - } |
74 | | - } |
75 | 64 |
|
76 | | - if ( |
77 | | - this.sass && |
78 | | - sass != undefined && |
79 | | - rule.test.toString() === /\.scss$/.toString() |
80 | | - ) { |
81 | | - const postCssLoader = rule.loaders.find( |
82 | | - (l) => l.loader === "postcss-loader" |
83 | | - ); |
84 | | - const sassLoader = rule.loaders.find((l) => l.loader === "sass-loader"); |
85 | | - delete rule.loaders; |
86 | | - |
87 | | - Object.assign(rule, { |
88 | | - test: /\.scss$/, |
89 | | - use: [ |
90 | | - "style-loader", |
91 | | - { |
92 | | - loader: "css-loader", |
93 | | - options: this[this.mode](), |
94 | | - }, |
95 | | - postCssLoader, |
96 | | - sassLoader, |
97 | | - ], |
| 65 | + if (this.options.oneOf) this.handleOneOfPreProcessor(sassLoaders); |
| 66 | + else this.handlePreProcessor(sassLoaders); |
| 67 | + } |
| 68 | + } |
| 69 | + |
| 70 | + /** |
| 71 | + * handle normal css-module |
| 72 | + * |
| 73 | + * @param {*} cssLoaders |
| 74 | + * @returns |
| 75 | + * @memberof VueCssModule |
| 76 | + * |
| 77 | + */ |
| 78 | + handleCss(cssLoaders) { |
| 79 | + this.handleExclude(cssLoaders); |
| 80 | + |
| 81 | + cssLoaders.loaders.forEach((cssLoader) => { |
| 82 | + if (cssLoader.loader === "css-loader") { |
| 83 | + Object.assign(cssLoader, { |
| 84 | + options: this.options.cssLoaderOptions, |
98 | 85 | }); |
99 | 86 | } |
100 | | - |
101 | | - return rule; |
102 | 87 | }); |
103 | 88 |
|
104 | | - return webpackConfig; |
| 89 | + return cssLoaders; |
105 | 90 | } |
106 | 91 |
|
107 | 92 | /** |
108 | | - * Return default mode |
| 93 | + * handle oneOf css-module |
109 | 94 | * |
110 | | - * @return {object} |
| 95 | + * @param {*} cssLoaders |
| 96 | + * @returns |
| 97 | + * @memberof VueCssModule |
111 | 98 | */ |
112 | | - global() { |
113 | | - return { |
114 | | - modules: true, |
115 | | - localIdentName: this.localIdentName, |
116 | | - }; |
| 99 | + handleOneOfCss(cssLoaders) { |
| 100 | + this.handleExclude(cssLoaders); |
| 101 | + |
| 102 | + // keep default config for postcss-loader |
| 103 | + const postCssLoader = cssLoaders.loaders.find( |
| 104 | + (cssLoader) => cssLoader.loader === "postcss-loader" |
| 105 | + ); |
| 106 | + |
| 107 | + // reset loaders change with use |
| 108 | + delete cssLoaders.loaders; |
| 109 | + |
| 110 | + cssLoaders.oneOf = [ |
| 111 | + { |
| 112 | + resourceQuery: /module/, |
| 113 | + use: [ |
| 114 | + "style-loader", |
| 115 | + { |
| 116 | + loader: "css-loader", |
| 117 | + options: this.options.cssLoaderOptions, |
| 118 | + }, |
| 119 | + ], |
| 120 | + }, |
| 121 | + { |
| 122 | + use: ["style-loader", postCssLoader], |
| 123 | + }, |
| 124 | + ]; |
| 125 | + |
| 126 | + return cssLoaders; |
117 | 127 | } |
118 | 128 |
|
119 | 129 | /** |
120 | | - * Return local mode |
| 130 | + * handle normal css-module for pre-processcor |
121 | 131 | * |
122 | | - * @return {object} |
| 132 | + * @param {*} sassLoaders |
| 133 | + * @returns |
| 134 | + * @memberof VueCssModule |
123 | 135 | */ |
124 | | - local() { |
125 | | - return { |
126 | | - modules: { |
127 | | - mode: this.mode, |
128 | | - localIdentName: this.localIdentName, |
| 136 | + handlePreProcessor(sassLoaders) { |
| 137 | + this.handleExclude(sassLoaders); |
| 138 | + |
| 139 | + const [postCssLoader, sassLoader] = this.getDefaultPreProcessorConfig( |
| 140 | + sassLoaders |
| 141 | + ); |
| 142 | + |
| 143 | + // re-create config & add custom css-loader for .scss |
| 144 | + sassLoaders.loaders = [ |
| 145 | + "style-loader", |
| 146 | + { |
| 147 | + loader: "css-loader", |
| 148 | + options: this.options.cssLoaderOptions, |
129 | 149 | }, |
130 | | - }; |
| 150 | + postCssLoader, |
| 151 | + sassLoader, |
| 152 | + ]; |
| 153 | + |
| 154 | + return sassLoaders; |
| 155 | + } |
| 156 | + |
| 157 | + /** |
| 158 | + * handle oneOf css-module for pre-processcor |
| 159 | + * |
| 160 | + * @param {*} sassLoaders |
| 161 | + * @returns |
| 162 | + * @memberof VueCssModule |
| 163 | + */ |
| 164 | + handleOneOfPreProcessor(sassLoaders) { |
| 165 | + this.handleExclude(sassLoaders); |
| 166 | + |
| 167 | + const [postCssLoader, sassLoader] = this.getDefaultPreProcessorConfig( |
| 168 | + sassLoaders |
| 169 | + ); |
| 170 | + |
| 171 | + delete sassLoaders.loaders; |
| 172 | + |
| 173 | + sassLoaders.oneOf = [ |
| 174 | + { |
| 175 | + resourceQuery: /module/, |
| 176 | + use: [ |
| 177 | + "style-loader", |
| 178 | + { |
| 179 | + loader: "css-loader", |
| 180 | + options: this.options.cssLoaderOptions, |
| 181 | + }, |
| 182 | + ], |
| 183 | + }, |
| 184 | + { |
| 185 | + use: ["style-loader", "css-loader", postCssLoader, sassLoader], |
| 186 | + }, |
| 187 | + ]; |
| 188 | + |
| 189 | + return sassLoaders; |
| 190 | + } |
| 191 | + |
| 192 | + /** |
| 193 | + * get default config from laravel-mix |
| 194 | + * |
| 195 | + * @param {*} sassLoaders |
| 196 | + * @returns |
| 197 | + * @memberof VueCssModule |
| 198 | + */ |
| 199 | + getDefaultPreProcessorConfig(sassLoaders) { |
| 200 | + // keep default config for postcss-loader |
| 201 | + const postCssLoader = sassLoaders.loaders.find( |
| 202 | + (sassLoader) => sassLoader.loader === "postcss-loader" |
| 203 | + ); |
| 204 | + |
| 205 | + // keep default config for sass-loader |
| 206 | + const sassLoader = sassLoaders.loaders.find( |
| 207 | + (sassLoader) => sassLoader.loader === "sass-loader" |
| 208 | + ); |
| 209 | + |
| 210 | + return [postCssLoader, sassLoader]; |
| 211 | + } |
| 212 | + |
| 213 | + /** |
| 214 | + * handle exclude |
| 215 | + * |
| 216 | + * @param {*} loaders |
| 217 | + * @returns |
| 218 | + * @memberof VueCssModule |
| 219 | + */ |
| 220 | + handleExclude(loaders) { |
| 221 | + if (this.options.exclude.length > 0) { |
| 222 | + if (loaders.exclude === undefined) { |
| 223 | + loaders.exclude = this.options.exclude; |
| 224 | + } else { |
| 225 | + this.options.exclude.forEach((e) => loaders.exclude.push(e)); |
| 226 | + } |
| 227 | + } |
| 228 | + |
| 229 | + return loaders; |
| 230 | + } |
| 231 | + |
| 232 | + /** |
| 233 | + * get default type for localIdentName |
| 234 | + * |
| 235 | + * @returns |
| 236 | + * @memberof VueCssModule |
| 237 | + */ |
| 238 | + defaultLocalIdentName(type) { |
| 239 | + if (type === "react") { |
| 240 | + return this.reactLocalIdentName(); |
| 241 | + } |
| 242 | + |
| 243 | + if (type === "discord") { |
| 244 | + return this.discordLocalIdentName(); |
| 245 | + } |
| 246 | + |
| 247 | + return Mix.inProduction() ? "[hash:base64]" : "[path][name]__[local]"; |
| 248 | + } |
| 249 | + |
| 250 | + /** |
| 251 | + * Example localIdentName like react |
| 252 | + */ |
| 253 | + reactLocalIdentName() { |
| 254 | + return "[name]___[local]___[hash:base64:5]"; |
| 255 | + } |
| 256 | + |
| 257 | + /** |
| 258 | + * Example localIdentName like discord |
| 259 | + */ |
| 260 | + discordLocalIdentName() { |
| 261 | + return "[local]-[hash:base64:5]"; |
131 | 262 | } |
132 | 263 | } |
133 | 264 |
|
134 | | -mix.extend("vueCssModules", new VueCssModule()); |
| 265 | +mix.extend("vueCssModules", new VueCssModules()); |
0 commit comments