├── .editorconfig
├── .eslintignore
├── .eslintrc.js
├── .gitattributes
├── .gitignore
├── .prettierignore
├── .prettierrc
├── LICENSE
├── README.md
├── babel.config.js
├── color-test.js
├── commitlint.config.js
├── husky.config.js
├── images
├── example.gif
├── includeStyles_p.png
└── includeStyles_r.png
├── lint-staged.config.js
├── package.json
├── src
├── arbitraryMode
│ ├── browser.js
│ ├── index.js
│ └── utils.js
├── bin
│ └── index.js
├── getLess.js
├── getSass.js
├── index.js
├── packPath.js
├── postcss-addScopeName.js
└── utils.js
└── yarn.lock
/.editorconfig:
--------------------------------------------------------------------------------
1 | # editorconfig.org
2 |
3 | [*]
4 | charset = utf-8
5 | indent_style = space
6 | indent_size = 2
7 | end_of_line = lf
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 |
11 | [*.md]
12 | trim_trailing_whitespace = false
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | /coverage
2 | /dist
3 | /node_modules
4 | /test/fixtures
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | root: true,
3 | extends: ["@webpack-contrib/eslint-config-webpack", "prettier"],
4 | };
5 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | * text=auto
2 | bin/* eol=lf
3 | yarn.lock -diff
4 | package-lock.json -diff
5 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | lib-cov
2 | *.seed
3 | *.log
4 | *.csv
5 | *.dat
6 | *.out
7 | *.pid
8 | *.gz
9 | pids
10 | logs
11 | results
12 | npm-debug.log
13 | /node_modules
14 | /coverage
15 | .idea
16 | .nyc_output
17 | npm-debug.log*
18 | yarn-debug.log*
19 | .eslintcache
20 | /coverage
21 | /dist
22 | /local
23 | /reports
24 | /test/fixtures/css
25 | /test/fixtures/generated-1.less
26 | /test/fixtures/generated-2.less
27 | /test/fixtures/generated-3.less
28 | /test/output
29 | .DS_Store
30 | Thumbs.db
31 | .vscode
32 | *.sublime-project
33 | *.sublime-workspace
34 | *.iml
35 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | /coverage
2 | /dist
3 | /node_modules
4 | /test/fixtures
5 | CHANGELOG.md
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "trailingComma": "es5",
3 | "tabWidth": 4,
4 | "semi": true,
5 | "singleQuote": true,
6 | "end-of-line": "lf"
7 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright JS Foundation and other contributors
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining
4 | a copy of this software and associated documentation files (the
5 | 'Software'), to deal in the Software without restriction, including
6 | without limitation the rights to use, copy, modify, merge, publish,
7 | distribute, sublicense, and/or sell copies of the Software, and to
8 | permit persons to whom the Software is furnished to do so, subject to
9 | the following conditions:
10 |
11 | The above copyright notice and this permission notice shall be
12 | included in all copies or substantial portions of the Software.
13 |
14 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
18 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
19 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
20 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # @zougt/some-loader-utils
2 |
3 | 提供了
4 |
5 | - [getLess](#getLess),本质上不是针对 less-loader 的扩展,而是[less 包](https://github.com/less/less.js)的扩展
6 | - [getSass](#getSass),本质上不是针对 sass-loader 的扩展,而是[sass 包](https://github.com/sass/dart-sass)的扩展
7 |
8 | 让你轻松实现基于`less`、`sass`的 web 应用在线动态主题切换。
9 |
10 | 有[动态主题模式](#动态主题模式)和[预设主题模式](#预设主题模式)
11 |
12 | 特点:
13 |
14 | - 使用成本很低
15 | - 不限 ui 框架,Element-ui、iview、Ant-design 等等等(只要基于 less/sass)
16 | - 不依赖 css3 vars
17 | - 浏览器兼容性良好(IE9+ ?)
18 |
19 | [demo repositories](https://github.com/GitOfZGT/dynamic-theme-demos)
20 |
21 | # 动态主题模式
22 |
23 | > v1.4.0+支持
24 |
25 | 可用颜色板选择任意颜色切换相关的梯度颜色,这里以 scss 为例
26 |
27 | [one inline demo](https://gitofzgt.github.io/dynamic-theme-demos/webpack-vuecli4-elementui-dynamic-theme/)
28 |
29 | [one demo repository](https://github.com/GitOfZGT/dynamic-theme-demos/tree/master/projects/webpack-vuecli4-elementui-dynamic-theme/)
30 |
31 | 
32 |
33 | ## 在 webpack 中使用
34 |
35 | ```bash
36 | # use npm or pnpm
37 | npm install color@3.2.1 @zougt/some-loader-utils @zougt/theme-css-extract-webpack-plugin -D
38 | # use yarn
39 | yarn add color@3.2.1 @zougt/some-loader-utils @zougt/theme-css-extract-webpack-plugin -D
40 | ```
41 |
42 | **webpack.config.js**
43 |
44 | ```js
45 | const path = require('path');
46 |
47 | const { getSass } = require('@zougt/some-loader-utils');
48 |
49 | const ThemeCssExtractWebpackPlugin = require('@zougt/theme-css-extract-webpack-plugin');
50 |
51 | const multipleScopeVars = [
52 | {
53 | // 必需,任意名称
54 | scopeName: 'theme-vars',
55 | // path和varsContent选一个
56 | path: path.resolve('src/theme/theme-vars.scss'),
57 | // varsContent:`@--color-primary:#9c26b;`
58 | },
59 | ];
60 |
61 | module.exports = {
62 | module: {
63 | rules: [
64 | {
65 | // 添加 setCustomTheme 的热更新loader
66 | test: /setCustomTheme\.js$/,
67 | enforce: 'pre',
68 | loader: require.resolve(
69 | '@zougt/theme-css-extract-webpack-plugin/dist/hot-loader/index.js'
70 | ),
71 | },
72 | {
73 | test: /\.(scss|sass)$/i,
74 | // 请确保支持 implementation 属性的 sass-loader版本,webpack4 => sass-loader v10.x,webpack5 => sass-loader v12.x,请安装sass, 非 node-sass
75 | loader: 'sass-loader',
76 | options: {
77 | implementation: getSass({
78 | // getMultipleScopeVars优先于 sassOptions.multipleScopeVars
79 | getMultipleScopeVars: (lessOptions) =>
80 | multipleScopeVars,
81 | // 可选项
82 | // implementation:sass
83 | }),
84 | },
85 | },
86 | ],
87 | },
88 | plugins: [
89 | new ThemeCssExtractWebpackPlugin({
90 | // 启用动态主题模式
91 | arbitraryMode: true,
92 | // 默认主题色,与"src/theme/theme-vars.scss"的@--color-primary主题色相同
93 | defaultPrimaryColor: '#512da7',
94 | multipleScopeVars,
95 | // 【注意】includeStyleWithColors作用: css中不是由主题色变量生成的颜色,也让它抽取到主题css内,可以提高权重
96 | includeStyleWithColors: [
97 | {
98 | // color也可以是array,如 ["#ffffff","#000"]
99 | color: '#ffffff',
100 | // 排除属性,如 不提取背景色的#ffffff
101 | // excludeCssProps:["background","background-color"]
102 | // 排除选择器,如 不提取以下选择器的 #ffffff
103 | // excludeSelectors: [
104 | // ".ant-btn-link:hover, .ant-btn-link:focus, .ant-btn-link:active",
105 | // ],
106 | },
107 | ],
108 | // 是否在html默认添加主题的style标签
109 | InjectDefaultStyleTagToHtml: true,
110 | // setCustomTheme.js的一个依赖的生成路径,默认是 @zougt/theme-css-extract-webpack-plugin/dist/hot-loader/setCustomThemeContent.js
111 | customThemeOutputPath: '',
112 | // 调整色相值偏差,某些颜色值是由主题色通过mix等函数转化后,两者色相值不相等,无法确认是梯度颜色,可以调整low和high,允许偏差范围, 例如 hueDiffControls:{low: 2,high:2}
113 | // hueDiffControls: {
114 | // low: 0,
115 | // high: 0,
116 | // },
117 | }),
118 | ],
119 | };
120 | ```
121 |
122 | **src/theme/theme-vars.scss**
123 |
124 | ```scss
125 | /*说明:此文件不应该被其他@import,此文件的变量并不是来设置项目的主题(当然,你可以作为加载时的默认主题),主要作用是,这里的变量值只要与项目的原变量值有差别,编译后就会抽取跟随主题色梯度变化的css*/
126 |
127 | /*注意(重要):此文件的内容一旦固定下来就不需要改,在线动态切换主题,调用setCustomTheme方法即可*/
128 |
129 | /*注意(强调):变量值改动会影响 gradientReplacer 和 targetValueReplacer 的可用属性的变化,所以内容一旦固定下来就不需要改(强调)*/
130 |
131 | /*主题色,通常与 插件的 defaultPrimaryColor 相同, 使用setCustomTheme({primaryColor})切换*/
132 |
133 | $--color-primary: #512da7;
134 |
135 | /*与此颜色对应的样式,默认情况也会跟主色变化的,要切换它对应的梯度颜色,使用setCustomTheme({gradientReplacer:{"#F7D06B":"#F7D06B"}})切换 */
136 | $--color-success: #f7d06b;
137 |
138 | // /*圆角值,尽量与原值差别大一点,方便分析 targetValueReplacer 的可用属性,非颜色值的切换,可以使用 setCustomTheme({targetValueReplacer:{"6px"}}) 精准替换*/
139 | // @border-radius-base:6px;
140 | ```
141 |
142 | **在线切换主题**
143 |
144 | 动态主题切换必须使用的 "setCustomTheme" 模块,会自动处理项目中包括组件库涉及的梯度颜色替换
145 |
146 | ```js
147 | // color@4 使用了Numeric separators,如需良好兼容性应该安装 color@3
148 | import Color from 'color';
149 | // setCustomTheme的参数必须提供Color模块,至于为什么不把 Color 直接依赖进去是有原因的
150 | import setCustomTheme from '@zougt/theme-css-extract-webpack-plugin/dist/setCustomTheme';
151 | // 设置任意主题色既可
152 | setCustomTheme({
153 | Color,
154 | primaryColor: '#FF005A',
155 | //gradientReplacer:{},
156 | //targetValueReplacer:{}
157 | });
158 | ```
159 |
160 | `setCustomTheme` 的可选参数 gradientReplacer 与 targetValueReplacer 的可用属性会跟随 .scss 内容变化的,所以整个项目动态主题的模型应该最开始固化下来
161 |
162 | ```shell
163 | # npm run dev 之后
164 | # 可以在终端使用 z-theme 命令查看 gradientReplacer 与 targetValueReplacer 的可用属性
165 | npx z-theme inspect
166 | ```
167 |
168 | ## 预设主题模式
169 |
170 | 只预设多种可选主题,这里以less为例
171 |
172 | [one inline demo](https://gitofzgt.github.io/dynamic-theme-demos/webpack-vuecli4-antdvue-preset-theme/)
173 |
174 | [one demo repository](https://github.com/GitOfZGT/dynamic-theme-demos/tree/master/projects/webpack-vuecli4-antdvue-preset-theme/)
175 |
176 | 
177 |
178 | ## 在 webpack 中使用
179 |
180 | ```bash
181 | # use npm or pnpm
182 | npm install @zougt/some-loader-utils @zougt/theme-css-extract-webpack-plugin -D
183 | # use yarn
184 | yarn add @zougt/some-loader-utils @zougt/theme-css-extract-webpack-plugin -D
185 | ```
186 |
187 | **webpack.config.js**
188 |
189 | ```js
190 | const path = require('path');
191 | const webpack = require('webpack');
192 |
193 | const { getLess } = require('@zougt/some-loader-utils');
194 |
195 | const ThemeCssExtractWebpackPlugin = require('@zougt/theme-css-extract-webpack-plugin');
196 |
197 | const multipleScopeVars = [
198 | {
199 | // 必需
200 | scopeName: 'theme-default',
201 | // path 和 varsContent 必选一个
202 | path: path.resolve('src/theme/theme-default.less'),
203 | // varsContent参数等效于 path文件的内容
204 | // varsContent:`@primary-color:${defaultPrimaryColor};`
205 | },
206 |
207 | {
208 | scopeName: 'theme-red',
209 | path: path.resolve('src/theme/theme-red.less'),
210 | },
211 | ];
212 | const extract = process.env.NODE_ENV === 'production';
213 | const publicPath = '/';
214 | const assetsDir = 'assets';
215 | const extractCssOutputDir = `${assetsDir}/css`;
216 |
217 | module.exports = {
218 | output: {
219 | publicPath,
220 | },
221 | module: {
222 | rules: [
223 | {
224 | test: /\.less$/i,
225 | // webpack4 => less-loader v7.x , webpack5 => less-loader v10.x
226 | loader: 'less-loader',
227 | options: {
228 | lessOptions: {
229 | javascriptEnabled: true,
230 | },
231 | implementation: getLess({
232 | // getMultipleScopeVars优先于 lessOptions.multipleScopeVars
233 | getMultipleScopeVars: (lessOptions) =>
234 | multipleScopeVars,
235 | // 可选项
236 | // implementation:less
237 | }),
238 | },
239 | },
240 | ],
241 | },
242 | plugins: [
243 | // 添加参数到浏览器端
244 | new webpack.DefinePlugin({
245 | 'env.themeConfig': {
246 | multipleScopeVars: JSON.stringify(multipleScopeVars),
247 | extract: JSON.stringify(extract),
248 | publicPath: JSON.stringify(publicPath),
249 | extractCssOutputDir: JSON.stringify(extractCssOutputDir),
250 | },
251 | }),
252 |
253 | new ThemeCssExtractWebpackPlugin({
254 | multipleScopeVars,
255 | // 【注意】includeStyleWithColors作用: css中不是由主题色变量生成的颜色,也让它抽取到主题css内,可以提高权重
256 | includeStyleWithColors: [
257 | {
258 | // color也可以是array,如 ["#ffffff","#000"]
259 | color: '#ffffff',
260 | // 排除属性,如 不提取背景色的#ffffff
261 | // excludeCssProps:["background","background-color"]
262 | // 排除选择器,如 不提取以下选择器的 #ffffff
263 | // excludeSelectors: [
264 | // ".ant-btn-link:hover, .ant-btn-link:focus, .ant-btn-link:active",
265 | // ],
266 | },
267 | {
268 | color: ['transparent', 'none'],
269 | },
270 | ],
271 | // 默认使用哪份主题,默认取 multipleScopeVars[0].scopeName
272 | defaultScopeName: '',
273 | // 在生产模式是否抽取独立的主题css文件,extract为true以下属性有效
274 | extract,
275 | // 独立主题css文件的输出路径
276 | outputDir: extractCssOutputDir,
277 | // 会选取defaultScopeName对应的主题css文件在html添加link
278 | themeLinkTagId: 'theme-link-tag',
279 | // 是否对抽取的css文件内对应scopeName的权重类名移除
280 | removeCssScopeName: false,
281 | }),
282 | ],
283 | };
284 | ```
285 |
286 | **在线切换主题**
287 |
288 | 预设主题切换,需要做的事情
289 |
290 | 1、开发时只需,html 标签的 calss 添加对应的 scopeName,移除上个 scopeName
291 | 2、打包后,如果开启 extract: true,需要切换对应的 link 标签的 href
292 |
293 | 可以选择使用如下封装好的方法
294 |
295 | ```js
296 | import { toggleTheme } from '@zougt/theme-css-extract-webpack-plugin/dist/toggleTheme';
297 | // env.themeConfig 来源 (webpack.DefinePlugin)
298 | const themeConfig = env.themeConfig;
299 | toggleTheme({
300 | scopeName,
301 | multipleScopeVars: themeConfig.multipleScopeVars,
302 | extract: themeConfig.extract,
303 | publicPath: themeConfig.publicPath,
304 | outputDir: themeConfig.extractCssOutputDir,
305 | // customLinkHref: (href) => href,
306 | // themeLinkTagId: "theme-link-tag",
307 | // removeCssScopeName: false,
308 | // loading: {
309 | // show: () => {},
310 | // hide: () => {},
311 | // },
312 | });
313 | ```
314 |
315 | **预设多主题编译原理示例(以 sass 为例)**
316 |
317 | **主题包含的可能不只是颜色部分**
318 |
319 | ```scss
320 | //src/theme/default-vars.scss
321 | /**
322 | *此scss变量文件作为multipleScopeVars去编译时,会自动移除!default以达到变量提升
323 | *同时此scss变量文件作为默认主题变量文件,被其他.scss通过 @import 时,必需 !default
324 | */
325 | $primary-color: #0081ff !default;
326 | $--border-radius-base: 4px !default;
327 | ```
328 |
329 | ```scss
330 | //src/theme/mauve-vars.scss
331 | $primary-color: #9c26b0 !default;
332 | $--border-radius-base: 8px !default;
333 | ```
334 |
335 | ```scss
336 | //src/components/Button/style.scss
337 | @import '../../theme/default-vars';
338 | .un-btn {
339 | position: relative;
340 | display: inline-block;
341 | font-weight: 400;
342 | white-space: nowrap;
343 | text-align: center;
344 | border: 1px solid transparent;
345 | background-color: $primary-color;
346 | border-radius: $--border-radius-base;
347 | .anticon {
348 | line-height: 1;
349 | }
350 | }
351 | ```
352 |
353 | 编译之后
354 |
355 | src/components/Button/style.css
356 |
357 | ```css
358 | .un-btn {
359 | position: relative;
360 | display: inline-block;
361 | font-weight: 400;
362 | white-space: nowrap;
363 | text-align: center;
364 | border: 1px solid transparent;
365 | }
366 | .theme-default .un-btn {
367 | background-color: #0081ff;
368 | border-radius: 4px;
369 | }
370 | .theme-mauve .un-btn {
371 | background-color: #9c26b0;
372 | border-radius: 8px;
373 | }
374 | .un-btn .anticon {
375 | line-height: 1;
376 | }
377 | ```
378 |
379 | 在`html`中改变 classname 切换主题,只作用于 html 标签 :
380 |
381 | ```html
382 |
383 |
384 |
385 |
386 | title
387 |
388 |
389 |
390 |
391 |
392 |
393 | ```
394 |
395 | ```js
396 | document.documentElement.className = 'theme-mauve';
397 | ```
398 |
399 | ### 使用 Css Modules
400 |
401 | 如果是模块化的 scss,得到的 css 类似:
402 |
403 | ```css
404 | .src-components-Button-style_theme-default-3CPvz
405 | .src-components-Button-style_un-btn-1n85E {
406 | background-color: #0081ff;
407 | }
408 | .src-components-Button-style_theme-mauve-3yajX
409 | .src-components-Button-style_un-btn-1n85E {
410 | background-color: #9c26b0;
411 | }
412 | ```
413 |
414 | 实际需要的结果应该是这样:
415 |
416 | ```css
417 | .theme-default .src-components-Button-style_un-btn-1n85E {
418 | background-color: #0081ff;
419 | }
420 | .theme-mauve .src-components-Button-style_un-btn-1n85E {
421 | background-color: #9c26b0;
422 | }
423 | ```
424 |
425 | 在 webpack.config.js 需要对`css-loader` (v4.0+) 的 modules 属性添加 getLocalIdent:
426 |
427 | ```js
428 | const path = require('path');
429 | // const sass = require("sass");
430 | const { getSass } = require('@zougt/some-loader-utils');
431 | const { interpolateName } = require('loader-utils');
432 | function normalizePath(file) {
433 | return path.sep === '\\' ? file.replace(/\\/g, '/') : file;
434 | }
435 | const multipleScopeVars = [
436 | {
437 | scopeName: 'theme-default',
438 | path: path.resolve('src/theme/default-vars.scss'),
439 | },
440 | {
441 | scopeName: 'theme-mauve',
442 | path: path.resolve('src/theme/mauve-vars.scss'),
443 | },
444 | ];
445 | module.exports = {
446 | module: {
447 | rules: [
448 | {
449 | test: /\.module.scss$/i,
450 | use: [
451 | {
452 | loader: 'css-loader',
453 | options: {
454 | importLoaders: 1,
455 | modules: {
456 | localIdentName:
457 | process.env.NODE_ENV === 'production'
458 | ? '[hash:base64:5]'
459 | : '[path][name]_[local]-[hash:base64:5]',
460 | //使用 getLocalIdent 自定义模块化名称 , css-loader v4.0+
461 | getLocalIdent: (
462 | loaderContext,
463 | localIdentName,
464 | localName,
465 | options
466 | ) => {
467 | if (
468 | multipleScopeVars.some(
469 | (item) =>
470 | item.scopeName === localName
471 | )
472 | ) {
473 | //localName 属于 multipleScopeVars 的不用模块化
474 | return localName;
475 | }
476 | const { context, hashPrefix } = options;
477 | const { resourcePath } = loaderContext;
478 | const request = normalizePath(
479 | path.relative(context, resourcePath)
480 | );
481 | // eslint-disable-next-line no-param-reassign
482 | options.content = `${
483 | hashPrefix + request
484 | }\x00${localName}`;
485 | const inname = interpolateName(
486 | loaderContext,
487 | localIdentName,
488 | options
489 | );
490 |
491 | return inname.replace(
492 | /\\?\[local\\?]/gi,
493 | localName
494 | );
495 | },
496 | },
497 | },
498 | },
499 | {
500 | loader: 'sass-loader',
501 | options: {
502 | implementation: getSass({
503 | // getMultipleScopeVars优先于 sassOptions.multipleScopeVars
504 | getMultipleScopeVars: (sassOptions) =>
505 | multipleScopeVars,
506 | // 可选项
507 | // implementation:sass
508 | }),
509 | },
510 | },
511 | ],
512 | },
513 | ],
514 | },
515 | };
516 | ```
517 |
518 | > 以上是基于 webpack 的多主题的编译方案实现,如需 vite 版本的请看 vite 插件[@zougt/vite-plugin-theme-preprocessor](https://github.com/GitOfZGT/vite-plugin-theme-preprocessor)
519 |
520 | ### multipleScopeVars
521 |
522 | 必需的
523 |
524 | > 当 multipleScopeVars 只有一项时, scopeName 就没有意义,但是 path 可以起到 变量提升的作用
525 |
526 | Type `object[]`
527 |
528 | #### multipleScopeVars[].scopeName
529 |
530 | Type `string`
531 |
532 | #### multipleScopeVars[].path
533 |
534 | 必需的,变量文件的绝对路径
535 |
536 | Type `string || string[]`
537 |
538 | ```js
539 | const multipleScopeVars = [
540 | {
541 | scopeName: 'theme-default',
542 | path: path.resolve('src/theme/default-vars.less'),
543 | },
544 | {
545 | scopeName: 'theme-mauve',
546 | path: path.resolve('src/theme/mauve-vars.less'),
547 | },
548 | ];
549 | ```
550 |
551 | ### multipleScopeVars[].includeStyles
552 |
553 | > v1.3.0 支持 includeStyles,只在预设主题模式有效
554 |
555 | Type: `Object`
556 |
557 | 当存在以下情况时,可以用这个属性处理
558 |
559 | ```css
560 | .theme-blue .el-button:focus,
561 | .theme-blue .el-button:hover {
562 | /*这里的color值由 $primary-color 编译得来的,所以选择器前面加了 .theme-blue 提高了权重*/
563 | color: #0281ff;
564 | border-color: #b3d9ff;
565 | background-color: #e6f2ff;
566 | }
567 | .el-button--primary:focus,
568 | .el-button--primary:hover {
569 | /*这里的color值不是由 变量 编译得来的,这时就会被上面那个 color 覆盖了, 实际上这里的color才是需要的效果*/
570 | color: #fff;
571 | }
572 | ```
573 |
574 | ```js
575 | const includeStyles = {
576 | '.el-button--primary:hover, .el-button--primary:focus': {
577 | color: '#FFFFFF',
578 | },
579 | };
580 | const multipleScopeVars = [
581 | {
582 | scopeName: 'theme-default',
583 | path: path.resolve('src/theme/default-vars.less'),
584 | includeStyles,
585 | },
586 | {
587 | scopeName: 'theme-mauve',
588 | path: path.resolve('src/theme/mauve-vars.less'),
589 | includeStyles,
590 | },
591 | ];
592 | ```
593 |
594 | 得到
595 |
596 | ```css
597 | .theme-blue .el-button:focus,
598 | .theme-blue .el-button:hover {
599 | /*这里的color值由 $primary-color 编译得来的,所以选择器前面加了 .theme-blue 提高了权重*/
600 | color: #0281ff;
601 | border-color: #b3d9ff;
602 | background-color: #e6f2ff;
603 | }
604 | .theme-blue .el-button--primary:focus,
605 | .theme-blue .el-button--primary:hover {
606 | /*这里的color值不是由 变量 编译得来的,通过includeStyles也提高了权重得到实际的效果*/
607 | color: #ffffff;
608 | }
609 | ```
610 |
611 | 出现权重问题效果图
612 |
613 | 
614 |
615 | 使用了 includeStyles 的效果图
616 |
617 | 
618 |
--------------------------------------------------------------------------------
/babel.config.js:
--------------------------------------------------------------------------------
1 | const MIN_BABEL_VERSION = 7;
2 |
3 | module.exports = (api) => {
4 | api.assertVersion(MIN_BABEL_VERSION);
5 | api.cache(true);
6 |
7 | return {
8 | presets: [
9 | [
10 | "@babel/preset-env",
11 | {
12 | targets: {
13 | node: "10.13.0",
14 | },
15 | },
16 | ],
17 | ],
18 | };
19 | };
20 |
--------------------------------------------------------------------------------
/color-test.js:
--------------------------------------------------------------------------------
1 | const Color = require('color');
2 | // const differenceAggMap1 = require('./differenceAggMap2')
3 | // // const differenceAggMap2 = require('./differenceAggMap2')
4 |
5 | // const agg= new Set();
6 | // for (const key in differenceAggMap1) {
7 | // if (Object.hasOwnProperty.call(differenceAggMap1, key)) {
8 | // const element = differenceAggMap1[key];
9 | // element.forEach(val=>{
10 | // agg.add(val)
11 | // })
12 | // }
13 | // }
14 | // console.log(Array.from(agg))
15 | // console.log(Color('#09A500').hsv().array())
16 | // console.log(Color('#CADCC9').hsv().array())
17 | // console.log(Color('hsla(120,0%,75%,0.3)').hsv())
18 |
19 | // 298 84 64
20 | // 298 2 86
21 | // 0 82 -12
22 |
23 | // const values = [
24 | // '#F4791E',
25 | // '#fef2e9',
26 | // '#fde4d2',
27 | // '#f4791e',
28 | // '#f6944b',
29 | // '#fbc9a5',
30 | // '#f8af78',
31 | // '#fcd7bc',
32 | // '#dc6d1b',
33 | // '#fabc8f',
34 | // '#fef4ed',
35 | // '#fffcfb',
36 | // '#fef8f4',
37 | // '#f6964f',
38 | // ];
39 | // const values =[
40 | // '#9D26B0', '#F4791E', '#f5e9f7',
41 | // '#ebd4ef', '#9d26b0', '#b151c0',
42 | // '#d8a8df', '#fef2e9', '#fde4d2',
43 | // '#f4791e', '#f6944b', '#fbc9a5',
44 | // '#c47dd0', '#f8af78', '#e2bee7',
45 | // '#8d229e', '#ce93d8', '#fcd7bc',
46 | // '#dc6d1b', '#fabc8f', '#f7eef9',
47 | // '#fef4ed', '#fdfbfd', '#fffcfb',
48 | // '#faf4fb', '#fef8f4', '#be36d3',
49 | // '#f6964f'
50 | // ]
51 | // const values = [
52 | // '#9D26B0',
53 | // '-1px 0 0 0 #c47dd0',
54 | // '5px 12px',
55 | // '#f5e9f7',
56 | // '#ebd4ef',
57 | // '#9d26b0',
58 | // '#b151c0',
59 | // '#d8a8df',
60 | // '#faf4fb',
61 | // '2px dashed #9D26B0',
62 | // '0 0 2px 2px #9D26B0',
63 | // '10px',
64 | // '#be36d3',
65 | // '10px 10px 0 20px',
66 | // '10px 10px 10px 20px',
67 | // '10px 20px',
68 | // '10px 20px 20px 20px',
69 | // '#e2bee7',
70 | // '#8d229e',
71 | // '#ce93d8',
72 | // '#c47dd0',
73 | // '5px',
74 | // '#fdfbfd',
75 | // '2px solid #9D26B0',
76 | // '0 0 2px 2px #9D26B0 inset',
77 | // '#f7eef9',
78 | // ].reduce((tol, curr) => {
79 | // const n = curr.split(/\s+/);
80 | // return [...tol, ...n.filter((c) => c.includes('#'))];
81 | // }, []);
82 | // const primaryArr = Color('#9D26B0').hsv().array();
83 | // values.forEach((val) => {
84 | // const carr = Color(val).hsv().array();
85 |
86 | // console.log(carr.map((v, i) => primaryArr[i] - v));
87 | // });
88 | // const jiezhi = [
89 | // [0, 0, 0],
90 | // [0.41382922996353955, 38.50524475524475, -12.549019607843135],
91 | // [0.3105590062112924, 72.74107471475892, -27.84313725490196],
92 | // [0.6280193236716514, 67.1120197793838, -24.705882352941174],
93 | // [0, 0, 0],
94 | // [-0.15276145710913624, 20.596590909090907, -6.274509803921575],
95 | // [-0.6245059288537504, 53.74541377904606, -18.431372549019613],
96 | // [0.3105590062114061, 75.62024628757698, -29.411764705882348],
97 | // [0, 0, 0],
98 | // [0, 0, 0],
99 | // [-0.2353918582108463, 4.001507970702292, -13.725490196078425],
100 | // [-0.9437963944856165, 60.66017316017316, -21.5686274509804],
101 | // [-0.03506311360433756, -0.07192174913694771, 7.058823529411761],
102 | // [0.43478260869574115, 46.464646464646464, -15.686274509803923],
103 | // [0.41382922996353955, 38.50524475524475, -12.549019607843135],
104 | // [-8.260869565217263, 77.6185770750988, -30.196078431372555],
105 | // [0, 0, 0],
106 | // [0, 0, 0],
107 | // [2.648221343873729, 73.99142022635999, -28.627450980392155],
108 | // ];
109 | // function outAllHex(color) {
110 | // const primaryArr = Color(color).hsv().array();
111 | // const outhex = jiezhi.map((item) =>
112 | // Color.hsv(
113 | // item.map((v, i) => {
114 | // const newv = primaryArr[i] - v;
115 | // const max = i === 0 ? 360 : 100;
116 | // return newv < 0 ? 0 : newv > max ? max : newv;
117 | // })
118 | // )
119 | // .hex()
120 | // .toString()
121 | // );
122 | // console.log(outhex);
123 | // }
124 | // outAllHex('#F4791E');
125 |
126 | function getHsvPercentGias(primaryColor, resultColor) {
127 | const primaryHsvArr = primaryColor.hsv().array();
128 | const resultHsvArr = resultColor.hsv().array();
129 | const getGiasPercent = (index) =>
130 | primaryHsvArr[index] !== 0
131 | ? (primaryHsvArr[index] - resultHsvArr[index]) /
132 | primaryHsvArr[index]
133 | : primaryHsvArr[index];
134 | return [getGiasPercent(0), getGiasPercent(1), getGiasPercent(2)];
135 | }
136 |
137 | function getTargetColor(newPrimaryColor, resultColor, percentGias) {
138 | const primaryHsvArr = newPrimaryColor.hsv().array();
139 | const getTargetVal = (index) =>
140 | primaryHsvArr[index] - percentGias[index] * primaryHsvArr[index];
141 | const targetColor = Color.hsv([
142 | getTargetVal(0),
143 | getTargetVal(1),
144 | getTargetVal(2),
145 | ]);
146 | targetColor.valpha = resultColor.valpha;
147 | return targetColor;
148 | }
149 |
150 | // console.log(Color('#CA359D').hsv().rgb());
151 |
152 | // const percentGias = getHsvPercentGias(Color('#CA359D'), Color('#B6E8E6'));
153 | // console.log(getTargetColor(Color('#296CDC'),Color('#B6E8E6'),percentGias).hex());
154 |
155 | function mix(color1, color2, weight) {
156 | const rgb1 = color1.rgb();
157 | const rgb2 = color2.rgb();
158 | weight = weight ? parseInt(weight, 10) : 50;
159 | const p = weight / 100.0;
160 | const w = p * 2 - 1;
161 | const a = rgb1.valpha - rgb2.valpha;
162 | const w1 = ((w * a === -1 ? w : (w + a) / (1 + w * a)) + 1) / 2.0;
163 | const alpha = rgb1.valpha * p + rgb2.valpha * (1 - p);
164 | const getRgbVal = (index) =>
165 | Math.round(rgb1.color[index] * w1 + rgb2.color[index] * (1 - w1));
166 | // const rgb = [getRgbVal(0), getRgbVal(1), getRgbVal(2)];
167 | return Color(
168 | `rgba(${getRgbVal(0)}, ${getRgbVal(1)}, ${getRgbVal(2)}, ${alpha})`
169 | );
170 | }
171 |
172 | function reverseMix(defaultPrimary, resultColor, weight) {
173 | const pRgb = defaultPrimary.rgb();
174 | const rRgb = resultColor.rgb();
175 | weight = weight ? parseInt(weight, 10) : 50;
176 | const p = weight / 100.0;
177 | const w = p * 2 - 1;
178 | const xalpha = (resultColor.valpha - pRgb.valpha * (1 - p)) / p;
179 | const a = xalpha - pRgb.valpha;
180 | const w1 = ((w * a === -1 ? w : (w + a) / (1 + w * a)) + 1) / 2.0;
181 | const getRgbVal = (index) =>
182 | Math.round((rRgb.color[index] - pRgb.color[index] * (1 - w1)) / w1);
183 | // const rgb = [getRgbVal(0), getRgbVal(1), getRgbVal(2)];
184 | return Color(
185 | `rgba(${getRgbVal(0)}, ${getRgbVal(1)}, ${getRgbVal(2)}, ${xalpha})`
186 | );
187 | }
188 | // const mixWeights = {
189 | // '#ea9fa3': '30%',
190 | // };
191 | // const resultColorString = '#ea9fa3';
192 | // const defaultPrimary = Color('#F6CD9F');
193 | // const resultColor = Color(resultColorString);
194 | // const xColor = reverseMix(
195 | // defaultPrimary,
196 | // resultColor,
197 | // mixWeights[resultColorString]
198 | // );
199 | // const primaryColor = Color('#34DC70');
200 | // const targetColor = mix(xColor, primaryColor, mixWeights[resultColorString]);
201 | // console.log(Color('blanchedalmond'));
202 |
203 | const values = [
204 | '#9D26B0',
205 | '-1px 0 0 0 #c47dd0',
206 | '5px 12px',
207 | '#f5e9f7',
208 | '#ebd4ef',
209 | '#9d26b0',
210 | '#b151c0',
211 | '#d8a8df',
212 | '#faf4fb',
213 | '2px dashed #9D26B0',
214 | '0 0 2px 2px #9D26B0',
215 | '10px',
216 | '#be36d3',
217 | '10px 10px 0 20px',
218 | '10px 10px 10px 20px',
219 | '10px 20px',
220 | '10px 20px 20px 20px',
221 | '#e2bee7',
222 | '#8d229e',
223 | '#ce93d8',
224 | '#c47dd0',
225 | '5px',
226 | '#fdfbfd',
227 | '2px solid #9D26B0',
228 | '0 0 2px 2px #9D26B0 inset',
229 | '#f7eef9',
230 | ];
231 |
232 | const colorValueReg = {
233 | hex: /#([0-9a-f]{8}|[0-9a-f]{6}|[0-9a-f]{3})/gi,
234 | rgb: /rgb\(\s*\d{1,3}\s*,\s*\d{1,3}\s*,\s*\d{1,3}\s*\)/gi,
235 | rgba: /rgba\(\s*\d{1,3}\s*,\s*\d{1,3}\s*,\s*\d{1,3}\s*,\s*(0|1|1.0|0?.[0-9])\s*\)/gi,
236 | hsl: /hsl\(\s*\d{1,3}\s*,\s*(0|\d{1,3}%)\s*,\s*(0|\d{1,3}%)\s*\)/gi,
237 | hsla: /hsla\(\s*\d{1,3}\s*,\s*(0|\d{1,3}%)\s*,\s*(0|\d{1,3}%)\s*,\s*(0|1|1.0|0?.[0-9])\s*\)/gi,
238 | };
239 |
240 | function separatValue(cssValues) {
241 | const cssColors = [];
242 | const hybridValueMap = {};
243 | const otherValues = [];
244 | cssValues.forEach((val) => {
245 | const hasSpace = /\s+/.test(val);
246 | const hex = val.match(colorValueReg.hex);
247 | const rgb = val.match(colorValueReg.rgb);
248 | const rgba = val.match(colorValueReg.rgba);
249 | const hsl = val.match(colorValueReg.hsl);
250 | const hsla = val.match(colorValueReg.hsla);
251 | if (!hex && !rgb && !rgba && !hsl && !hsla) {
252 | otherValues.push(val);
253 | } else {
254 | let residueVal = val;
255 | const getResidueVal = (colorArr, cssColors, residueVal) => {
256 | let newVal = residueVal;
257 | if (colorArr) {
258 | colorArr.forEach((cval) => {
259 | cssColors.push(cval);
260 | newVal = residueVal.replace(
261 | cval,
262 | hasSpace ? '#{color}' : ''
263 | );
264 | });
265 | }
266 | return newVal;
267 | };
268 | residueVal = getResidueVal(hex, cssColors, residueVal);
269 | residueVal = getResidueVal(rgb, cssColors, residueVal);
270 | residueVal = getResidueVal(rgba, cssColors, residueVal);
271 | residueVal = getResidueVal(hsl, cssColors, residueVal);
272 | residueVal = getResidueVal(hsla, cssColors, residueVal);
273 | if (residueVal) {
274 | hybridValueMap[residueVal] = val;
275 | }
276 | }
277 | });
278 | return {
279 | cssColors: Array.from(new Set(cssColors)),
280 | hybridValueMap,
281 | otherValues,
282 | };
283 | }
284 | const { cssColors, hybridValueMap, otherValues } = separatValue(values);
285 | const varsColorValues = ['#9D26B0'];
286 | const defaultPrimaryColor = '#9D26B0';
287 | function getResultColorReplaceMap({ varsColorValues, defaultPrimaryColor }) {
288 | const sourceColorMap = {};
289 | // const mixWeightsMap = {};
290 | cssColors.forEach((colorString) => {
291 | const resultColor = Color(colorString).hsv();
292 | let finded = false;
293 | for (let index = 0; index < varsColorValues.length; index++) {
294 | const varStr = varsColorValues[index];
295 | const varColor = Color(varStr).hsv();
296 | if (varColor.color[0] === resultColor.color[0]) {
297 | sourceColorMap[colorString] = {
298 | percentGias: getHsvPercentGias(varColor, resultColor),
299 | varColorString: varStr,
300 | };
301 | finded = true;
302 | break;
303 | }
304 | }
305 | if (!finded) {
306 | const varColor = Color(defaultPrimaryColor).hsv();
307 | sourceColorMap[colorString] = {
308 | percentGias: getHsvPercentGias(varColor, resultColor),
309 | varColorString: defaultPrimaryColor,
310 | };
311 | // if (varColor.color[0] > resultColor.color[0]) {
312 | // sourceColorMap[colorString].percentGias = getHsvPercentGias(
313 | // varColor,
314 | // resultColor
315 | // );
316 | // } else {
317 | // mixWeightsMap[colorString] = '50%';
318 | // }
319 | }
320 | });
321 | return sourceColorMap;
322 | }
323 | const sourceColorMap = getResultColorReplaceMap({
324 | varsColorValues,
325 | defaultPrimaryColor,
326 | });
327 | // console.log(mixWeightsMap);
328 |
329 | function getReplaceStyleValues({
330 | primaryColor = '',
331 | targetValueReplacer = {},
332 | sourceColorMap = {},
333 | hybridValueMap = {},
334 | otherValues = [],
335 | }) {
336 | const replaceColorMap = {};
337 | for (const key in sourceColorMap) {
338 | if (Object.hasOwnProperty.call(sourceColorMap, key)) {
339 | const item = sourceColorMap[key];
340 | replaceColorMap[key] = getTargetColor(
341 | Color(primaryColor),
342 | Color(key),
343 | item.percentGias
344 | )
345 | .hex()
346 | .toString();
347 | }
348 | }
349 | const replaceHybridValueMap = {};
350 | for (const sourceValue in hybridValueMap) {
351 | if (Object.hasOwnProperty.call(hybridValueMap, sourceValue)) {
352 | const temp = hybridValueMap[sourceValue];
353 | const sourceColors = Object.keys(sourceColorMap);
354 | const findColor = sourceColors.find((colorStr) =>
355 | sourceValue.includes(colorStr)
356 | );
357 | if (findColor) {
358 | replaceHybridValueMap[sourceValue] = (
359 | targetValueReplacer[temp] || temp
360 | ).replace('#{color}', replaceColorMap[findColor]);
361 | }
362 | }
363 | }
364 | const replaceOtherValueMap = {};
365 | otherValues.forEach((val) => {
366 | if (targetValueReplacer[val]) {
367 | replaceOtherValueMap[val] = targetValueReplacer[val];
368 | }
369 | });
370 | return { replaceColorMap, replaceHybridValueMap, replaceOtherValueMap };
371 | }
372 |
373 | function setNewThemeStyle({
374 | styleTagId,
375 | primaryColor,
376 | targetValueReplacer,
377 | sourceThemeStyle,
378 | sourceColorMap,
379 | hybridValueMap,
380 | otherValues,
381 | }) {
382 | const { replaceColorMap, replaceHybridValueMap, replaceOtherValueMap } =
383 | getReplaceStyleValues({
384 | primaryColor,
385 | targetValueReplacer,
386 | sourceColorMap,
387 | hybridValueMap,
388 | otherValues,
389 | });
390 |
391 | console.log(replaceColorMap, replaceHybridValueMap, replaceOtherValueMap);
392 | let newStyleContent = sourceThemeStyle;
393 | Object.keys(replaceOtherValueMap).forEach((sourceValue) => {
394 | newStyleContent = newStyleContent.replace(
395 | new RegExp(sourceValue, 'gi'),
396 | replaceOtherValueMap[sourceValue]
397 | );
398 | });
399 | Object.keys(replaceHybridValueMap).forEach((sourceValue) => {
400 | newStyleContent = newStyleContent.replace(
401 | new RegExp(sourceValue, 'gi'),
402 | replaceHybridValueMap[sourceValue]
403 | );
404 | });
405 | Object.keys(replaceColorMap).forEach((sourceValue) => {
406 | newStyleContent = newStyleContent.replace(
407 | new RegExp(sourceValue, 'gi'),
408 | replaceColorMap[sourceValue]
409 | );
410 | });
411 | console.log(newStyleContent)
412 | // const styleTag = document.getElementById(styleTagId);
413 | // if (styleTag) {
414 | // styleTag.content = newStyleContent;
415 | // }
416 | }
417 |
418 | const sourceThemeStyle = `.el-checkbox.is-bordered.is-checked { border-color: #9D26B0;}.el-checkbox__input.is-checked .el-checkbox__inner { background-color: #9D26B0; border-color: #9D26B0;}.el-checkbox__input.is-checked + .el-checkbox__label { color: #9D26B0;}.el-checkbox__input.is-focus .el-checkbox__inner { border-color: #9D26B0;}.el-checkbox__input.is-indeterminate .el-checkbox__inner { background-color: #9D26B0; border-color: #9D26B0;}.el-checkbox__inner:hover { border-color: #9D26B0;}.el-checkbox-button__inner:hover { color: #9D26B0;}.el-checkbox-button.is-checked .el-checkbox-button__inner { background-color: #9D26B0; border-color: #9D26B0; box-shadow: -1px 0 0 0 #c47dd0;}.el-checkbox-button.is-checked:first-child .el-checkbox-button__inner { border-left-color: #9D26B0;}.el-checkbox-button.is-focus .el-checkbox-button__inner { border-color: #9D26B0;}.el-checkbox-button--mini .el-checkbox-button__inner { padding: 5px 12px;}.el-checkbox-button--mini .el-checkbox-button__inner.is-round { padding: 5px 12px;}.el-tag { background-color: #f5e9f7; border-color: #ebd4ef; color: #9D26B0;}.el-tag.is-hit { border-color: #9D26B0;}.el-tag .el-tag__close { color: #9d26b0;}.el-tag .el-tag__close:hover { background-color: #9d26b0;}.el-tag--dark { background-color: #9d26b0; border-color: #9d26b0;}.el-tag--dark.is-hit { border-color: #9D26B0;}.el-tag--dark .el-tag__close:hover { background-color: #b151c0;}.el-tag--plain { border-color: #d8a8df; color: #9d26b0;}.el-tag--plain.is-hit { border-color: #9D26B0;}.el-tag--plain .el-tag__close { color: #9d26b0;}.el-tag--plain .el-tag__close:hover { background-color: #9d26b0;}.el-table-filter__list-item:hover { background-color: #f5e9f7; color: #b151c0;}.el-table-filter__list-item.is-active { background-color: #9D26B0;}.el-table-filter__bottom button:hover { color: #9D26B0;}.yb-main-aside .el-menu:not([class~=el-menu--collapse]) .el-menu-item.is-active::after { background-color: #f5e9f7;}.yb-main-aside-footer:hover { background-color: #faf4fb;}.yb-main-body-tab-center .router-tab__item.is-active,.yb-main-body-tab-center .router-tab__item:hover { color: #9D26B0;}.el-textarea__inner:focus { border-color: #9D26B0;}.el-input__inner:focus { border-color: #9D26B0;}.el-input.is-active .el-input__inner { border-color: #9D26B0;}.el-input-number__increase:hover, .el-input-number__decrease:hover { color: #9D26B0;}.el-input-number__increase:hover:not(.is-disabled) ~ .el-input .el-input__inner:not(.is-disabled), .el-input-number__decrease:hover:not(.is-disabled) ~ .el-input .el-input__inner:not(.is-disabled) { border-color: #9D26B0;}.el-tag { background-color: #f5e9f7; border-color: #ebd4ef; color: #9D26B0;}.el-tag.is-hit { border-color: #9D26B0;}.el-tag .el-tag__close { color: #9d26b0;}.el-tag .el-tag__close:hover { background-color: #9d26b0;}.el-tag--dark { background-color: #9d26b0; border-color: #9d26b0;}.el-tag--dark.is-hit { border-color: #9D26B0;}.el-tag--dark .el-tag__close:hover { background-color: #b151c0;}.el-tag--plain { border-color: #d8a8df; color: #9d26b0;}.el-tag--plain.is-hit { border-color: #9D26B0;}.el-tag--plain .el-tag__close { color: #9d26b0;}.el-tag--plain .el-tag__close:hover { background-color: #9d26b0;}.el-progress-bar__inner { background-color: #9D26B0;}.el-upload--picture-card:hover { border-color: #9D26B0; color: #9D26B0;}.el-upload:focus { border-color: #9D26B0; color: #9D26B0;}.el-upload:focus .el-upload-dragger { border-color: #9D26B0;}.el-upload-dragger .el-upload__text em { color: #9D26B0;}.el-upload-dragger:hover { border-color: #9D26B0;}.el-upload-dragger.is-dragover { border: 2px dashed #9D26B0;}.el-upload-list__item .el-icon-close-tip { color: #9D26B0;}.el-upload-list__item.is-success .el-upload-list__item-name:hover, .el-upload-list__item.is-success .el-upload-list__item-name:focus { color: #9D26B0;}.el-upload-list__item-delete:hover { color: #9D26B0;}.yb-page-404-content span { color: #9D26B0;}.el-textarea__inner:focus { border-color: #9D26B0;}.el-input__inner:focus { border-color: #9D26B0;}.el-input.is-active .el-input__inner { border-color: #9D26B0;}.el-tag { background-color: #f5e9f7; border-color: #ebd4ef; color: #9D26B0;}.el-tag.is-hit { border-color: #9D26B0;}.el-tag .el-tag__close { color: #9d26b0;}.el-tag .el-tag__close:hover { background-color: #9d26b0;}.el-tag--dark { background-color: #9d26b0; border-color: #9d26b0;}.el-tag--dark.is-hit { border-color: #9D26B0;}.el-tag--dark .el-tag__close:hover { background-color: #b151c0;}.el-tag--plain { border-color: #d8a8df; color: #9d26b0;}.el-tag--plain.is-hit { border-color: #9D26B0;}.el-tag--plain .el-tag__close { color: #9d26b0;}.el-tag--plain .el-tag__close:hover { background-color: #9d26b0;}.el-checkbox.is-bordered.is-checked { border-color: #9D26B0;}.el-checkbox__input.is-checked .el-checkbox__inner { background-color: #9D26B0; border-color: #9D26B0;}.el-checkbox__input.is-checked + .el-checkbox__label { color: #9D26B0;}.el-checkbox__input.is-focus .el-checkbox__inner { border-color: #9D26B0;}.el-checkbox__input.is-indeterminate .el-checkbox__inner { background-color: #9D26B0; border-color: #9D26B0;}.el-checkbox__inner:hover { border-color: #9D26B0;}.el-checkbox-button__inner:hover { color: #9D26B0;}.el-checkbox-button.is-checked .el-checkbox-button__inner { background-color: #9D26B0; border-color: #9D26B0; box-shadow: -1px 0 0 0 #c47dd0;}.el-checkbox-button.is-checked:first-child .el-checkbox-button__inner { border-left-color: #9D26B0;}.el-checkbox-button.is-focus .el-checkbox-button__inner { border-color: #9D26B0;}.el-checkbox-button--mini .el-checkbox-button__inner { padding: 5px 12px;}.el-checkbox-button--mini .el-checkbox-button__inner.is-round { padding: 5px 12px;}.el-radio.is-bordered.is-checked { border-color: #9D26B0;}.el-radio__input.is-checked .el-radio__inner { border-color: #9D26B0; background: #9D26B0;}.el-radio__input.is-checked + .el-radio__label { color: #9D26B0;}.el-radio__input.is-focus .el-radio__inner { border-color: #9D26B0;}.el-radio__inner:hover { border-color: #9D26B0;}.el-radio:focus:not(.is-focus):not(:active):not(.is-disabled) .el-radio__inner { box-shadow: 0 0 2px 2px #9D26B0;}.el-cascader-node.in-active-path, .el-cascader-node.is-selectable.in-checked-path, .el-cascader-node.is-active { color: #9D26B0;}.el-cascader .el-input .el-input__inner:focus { border-color: #9D26B0;}.el-cascader .el-input.is-focus .el-input__inner { border-color: #9D26B0;}.el-cascader__suggestion-item.is-checked { color: #9D26B0;}.el-drawer__header { padding: 10px;}.yb-ball-loading:not(:required) { background: #9D26B0;}.yb-ball-loading:not(:required)::after { background: #be36d3;}.yb-table-control-icon:hover { color: #9D26B0;}.type-radio::v-deep .el-radio.is-checked .el-radio__inner { color: #9D26B0;}.el-drawer__header { margin-bottom: 10px; padding: 10px 10px 0 20px;}.el-dialog__header { padding: 10px 10px 10px 20px;}.el-dialog__body { padding: 10px 20px;}.el-dialog__footer { padding: 10px 20px 20px 20px;}.el-loading-spinner .el-loading-text { color: #9D26B0;}.el-loading-spinner .path { stroke: #9D26B0;}.el-loading-spinner i { color: #9D26B0;}.el-button:hover, .el-button:focus { color: #9D26B0; border-color: #e2bee7; background-color: #f5e9f7;}.el-button:active { color: #8d229e; border-color: #8d229e;}.el-button.is-plain:hover, .el-button.is-plain:focus { border-color: #9D26B0; color: #9D26B0;}.el-button.is-plain:active { border-color: #8d229e; color: #8d229e;}.el-button.is-active { color: #8d229e; border-color: #8d229e;}.el-button--primary { background-color: #9D26B0; border-color: #9D26B0;}.el-button--primary:hover, .el-button--primary:focus { background: #b151c0; border-color: #b151c0;}.el-button--primary:active { background: #8d229e; border-color: #8d229e;}.el-button--primary.is-active { background: #8d229e; border-color: #8d229e;}.el-button--primary.is-disabled, .el-button--primary.is-disabled:hover, .el-button--primary.is-disabled:focus, .el-button--primary.is-disabled:active { background-color: #ce93d8; border-color: #ce93d8;}.el-button--primary.is-plain { color: #9D26B0; background: #f5e9f7; border-color: #d8a8df;}.el-button--primary.is-plain:hover, .el-button--primary.is-plain:focus { background: #9D26B0; border-color: #9D26B0;}.el-button--primary.is-plain:active { background: #8d229e; border-color: #8d229e;}.el-button--primary.is-plain.is-disabled, .el-button--primary.is-plain.is-disabled:hover, .el-button--primary.is-plain.is-disabled:focus, .el-button--primary.is-plain.is-disabled:active { color: #c47dd0; background-color: #f5e9f7; border-color: #ebd4ef;}.el-button--mini { padding: 5px 12px;}.el-button--mini.is-round { padding: 5px 12px;}.el-button--mini.is-circle { padding: 5px;}.el-button--text { color: #9D26B0;}.el-button--text:hover, .el-button--text:focus { color: #b151c0;}.el-button--text:active { color: #8d229e;}.el-progress-bar__inner { background-color: #9D26B0;}.el-drawer__header { margin-bottom: 10px; padding: 10px 10px 0 20px;}.el-dialog__header { padding: 10px 10px 10px 20px;}.el-dialog__body { padding: 10px 20px;}.el-dialog__footer { padding: 10px 20px 20px 20px;}.el-date-table td.today span { color: #9D26B0;}.el-date-table td.available:hover { color: #9D26B0;}.el-date-table td.current:not(.disabled) span { background-color: #9D26B0;}.el-date-table td.start-date span, .el-date-table td.end-date span { background-color: #9D26B0;}.el-date-table td.selected span { background-color: #9D26B0;}.el-month-table td.today .cell { color: #9D26B0;}.el-month-table td .cell:hover { color: #9D26B0;}.el-month-table td.start-date .cell, .el-month-table td.end-date .cell { background-color: #9D26B0;}.el-month-table td.current:not(.disabled) .cell { color: #9D26B0;}.el-year-table td.today .cell { color: #9D26B0;}.el-year-table td .cell:hover { color: #9D26B0;}.el-year-table td.current:not(.disabled) .cell { color: #9D26B0;}.el-time-spinner__arrow:hover { color: #9D26B0;}.el-range-editor.is-active { border-color: #9D26B0;}.el-range-editor.is-active:hover { border-color: #9D26B0;}.el-picker-panel__shortcut:hover { color: #9D26B0;}.el-picker-panel__shortcut.active { color: #9D26B0;}.el-picker-panel__icon-btn:hover { color: #9D26B0;}.el-date-picker__header-label:hover { color: #9D26B0;}.el-date-picker__header-label.active { color: #9D26B0;}.el-time-panel__btn.confirm { color: #9D26B0;}.el-textarea__inner:focus { border-color: #9D26B0;}.el-input__inner:focus { border-color: #9D26B0;}.el-input.is-active .el-input__inner { border-color: #9D26B0;}.el-radio.is-bordered.is-checked { border-color: #9D26B0;}.el-radio__input.is-checked .el-radio__inner { border-color: #9D26B0; background: #9D26B0;}.el-radio__input.is-checked + .el-radio__label { color: #9D26B0;}.el-radio__input.is-focus .el-radio__inner { border-color: #9D26B0;}.el-radio__inner:hover { border-color: #9D26B0;}.el-radio:focus:not(.is-focus):not(:active):not(.is-disabled) .el-radio__inner { box-shadow: 0 0 2px 2px #9D26B0;}.el-checkbox.is-bordered.is-checked { border-color: #9D26B0;}.el-checkbox__input.is-checked .el-checkbox__inner { background-color: #9D26B0; border-color: #9D26B0;}.el-checkbox__input.is-checked + .el-checkbox__label { color: #9D26B0;}.el-checkbox__input.is-focus .el-checkbox__inner { border-color: #9D26B0;}.el-checkbox__input.is-indeterminate .el-checkbox__inner { background-color: #9D26B0; border-color: #9D26B0;}.el-checkbox__inner:hover { border-color: #9D26B0;}.el-checkbox-button__inner:hover { color: #9D26B0;}.el-checkbox-button.is-checked .el-checkbox-button__inner { background-color: #9D26B0; border-color: #9D26B0; box-shadow: -1px 0 0 0 #c47dd0;}.el-checkbox-button.is-checked:first-child .el-checkbox-button__inner { border-left-color: #9D26B0;}.el-checkbox-button.is-focus .el-checkbox-button__inner { border-color: #9D26B0;}.el-checkbox-button--mini .el-checkbox-button__inner { padding: 5px 12px;}.el-checkbox-button--mini .el-checkbox-button__inner.is-round { padding: 5px 12px;}.el-tag { background-color: #f5e9f7; border-color: #ebd4ef; color: #9D26B0;}.el-tag.is-hit { border-color: #9D26B0;}.el-tag .el-tag__close { color: #9d26b0;}.el-tag .el-tag__close:hover { background-color: #9d26b0;}.el-tag--dark { background-color: #9d26b0; border-color: #9d26b0;}.el-tag--dark.is-hit { border-color: #9D26B0;}.el-tag--dark .el-tag__close:hover { background-color: #b151c0;}.el-tag--plain { border-color: #d8a8df; color: #9d26b0;}.el-tag--plain.is-hit { border-color: #9D26B0;}.el-tag--plain .el-tag__close { color: #9d26b0;}.el-tag--plain .el-tag__close:hover { background-color: #9d26b0;}.el-table th.el-table__cell > .cell.highlight { color: #9D26B0;}.el-table .ascending .sort-caret.ascending { border-bottom-color: #9D26B0;}.el-table .descending .sort-caret.descending { border-top-color: #9D26B0;}.el-table--striped .el-table__body tr.el-table__row--striped.current-row td.el-table__cell { background-color: #f5e9f7;}.el-table__body tr.current-row > td.el-table__cell { background-color: #f5e9f7;}.el-select-dropdown.is-multiple .el-select-dropdown__item.selected { color: #9D26B0;}.el-textarea__inner:focus { border-color: #9D26B0;}.el-input__inner:focus { border-color: #9D26B0;}.el-input.is-active .el-input__inner { border-color: #9D26B0;}.el-tag { background-color: #f5e9f7; border-color: #ebd4ef; color: #9D26B0;}.el-tag.is-hit { border-color: #9D26B0;}.el-tag .el-tag__close { color: #9d26b0;}.el-tag .el-tag__close:hover { background-color: #9d26b0;}.el-tag--dark { background-color: #9d26b0; border-color: #9d26b0;}.el-tag--dark.is-hit { border-color: #9D26B0;}.el-tag--dark .el-tag__close:hover { background-color: #b151c0;}.el-tag--plain { border-color: #d8a8df; color: #9d26b0;}.el-tag--plain.is-hit { border-color: #9D26B0;}.el-tag--plain .el-tag__close { color: #9d26b0;}.el-tag--plain .el-tag__close:hover { background-color: #9d26b0;}.el-select-dropdown__item.selected { color: #9D26B0;}.el-select .el-input__inner:focus { border-color: #9D26B0;}.el-select .el-input.is-focus .el-input__inner { border-color: #9D26B0;}.el-pagination button:hover { color: #9D26B0;}.el-pagination__sizes .el-input .el-input__inner:hover { border-color: #9D26B0;}.el-pagination.is-background .btn-prev,.el-pagination.is-background .btn-next,.el-pagination.is-background .el-pager li { background-color: #fdfbfd;}.el-pagination.is-background .el-pager li:not(.disabled):hover { color: #9D26B0;}.el-pagination.is-background .el-pager li:not(.disabled).active { background-color: #9D26B0;}.el-pager li:hover { color: #9D26B0;}.el-pager li.active { color: #9D26B0;}.el-select-dropdown.is-multiple .el-select-dropdown__item.selected { color: #9D26B0;}.el-textarea__inner:focus { border-color: #9D26B0;}.el-input__inner:focus { border-color: #9D26B0;}.el-input.is-active .el-input__inner { border-color: #9D26B0;}.el-tag { background-color: #f5e9f7; border-color: #ebd4ef; color: #9D26B0;}.el-tag.is-hit { border-color: #9D26B0;}.el-tag .el-tag__close { color: #9d26b0;}.el-tag .el-tag__close:hover { background-color: #9d26b0;}.el-tag--dark { background-color: #9d26b0; border-color: #9d26b0;}.el-tag--dark.is-hit { border-color: #9D26B0;}.el-tag--dark .el-tag__close:hover { background-color: #b151c0;}.el-tag--plain { border-color: #d8a8df; color: #9d26b0;}.el-tag--plain.is-hit { border-color: #9D26B0;}.el-tag--plain .el-tag__close { color: #9d26b0;}.el-tag--plain .el-tag__close:hover { background-color: #9d26b0;}.el-select-dropdown__item.selected { color: #9D26B0;}.el-select .el-input__inner:focus { border-color: #9D26B0;}.el-select .el-input.is-focus .el-input__inner { border-color: #9D26B0;}.el-textarea__inner:focus { border-color: #9D26B0;}.el-input__inner:focus { border-color: #9D26B0;}.el-input.is-active .el-input__inner { border-color: #9D26B0;}.el-menu--horizontal > .el-submenu.is-active .el-submenu__title { border-bottom: 2px solid #9D26B0;}.el-menu--horizontal > .el-menu-item.is-active { border-bottom: 2px solid #9D26B0;}.el-menu-item:hover, .el-menu-item:focus { background-color: #f5e9f7;}.el-menu-item.is-active { color: #9D26B0;}.el-submenu__title:hover, .el-submenu__title:focus { background-color: #f5e9f7;}.el-submenu__title:hover { background-color: #f5e9f7;}.el-submenu.is-active .el-submenu__title { border-bottom-color: #9D26B0;}.el-button:hover, .el-button:focus { color: #9D26B0; border-color: #e2bee7; background-color: #f5e9f7;}.el-button:active { color: #8d229e; border-color: #8d229e;}.el-button.is-plain:hover, .el-button.is-plain:focus { border-color: #9D26B0; color: #9D26B0;}.el-button.is-plain:active { border-color: #8d229e; color: #8d229e;}.el-button.is-active { color: #8d229e; border-color: #8d229e;}.el-button--primary { background-color: #9D26B0; border-color: #9D26B0;}.el-button--primary:hover, .el-button--primary:focus { background: #b151c0; border-color: #b151c0;}.el-button--primary:active { background: #8d229e; border-color: #8d229e;}.el-button--primary.is-active { background: #8d229e; border-color: #8d229e;}.el-button--primary.is-disabled, .el-button--primary.is-disabled:hover, .el-button--primary.is-disabled:focus, .el-button--primary.is-disabled:active { background-color: #ce93d8; border-color: #ce93d8;}.el-button--primary.is-plain { color: #9D26B0; background: #f5e9f7; border-color: #d8a8df;}.el-button--primary.is-plain:hover, .el-button--primary.is-plain:focus { background: #9D26B0; border-color: #9D26B0;}.el-button--primary.is-plain:active { background: #8d229e; border-color: #8d229e;}.el-button--primary.is-plain.is-disabled, .el-button--primary.is-plain.is-disabled:hover, .el-button--primary.is-plain.is-disabled:focus, .el-button--primary.is-plain.is-disabled:active { color: #c47dd0; background-color: #f5e9f7; border-color: #ebd4ef;}.el-button--mini { padding: 5px 12px;}.el-button--mini.is-round { padding: 5px 12px;}.el-button--mini.is-circle { padding: 5px;}.el-button--text { color: #9D26B0;}.el-button--text:hover, .el-button--text:focus { color: #b151c0;}.el-button--text:active { color: #8d229e;}.el-dropdown-menu__item:not(.is-disabled):hover, .el-dropdown-menu__item:focus { background-color: #f5e9f7; color: #b151c0;}.el-tabs__active-bar { background-color: #9D26B0;}.el-tabs__new-tab:hover { color: #9D26B0;}.el-tabs__item:focus.is-active.is-focus:not(:active) { box-shadow: 0 0 2px 2px #9D26B0 inset;}.el-tabs__item.is-active { color: #9D26B0;}.el-tabs__item:hover { color: #9D26B0;}.el-tabs--border-card > .el-tabs__header .el-tabs__item.is-active { color: #9D26B0;}.el-tabs--border-card > .el-tabs__header .el-tabs__item:not(.is-disabled):hover { color: #9D26B0;}.el-checkbox.is-bordered.is-checked { border-color: #9D26B0;}.el-checkbox__input.is-checked .el-checkbox__inner { background-color: #9D26B0; border-color: #9D26B0;}.el-checkbox__input.is-checked + .el-checkbox__label { color: #9D26B0;}.el-checkbox__input.is-focus .el-checkbox__inner { border-color: #9D26B0;}.el-checkbox__input.is-indeterminate .el-checkbox__inner { background-color: #9D26B0; border-color: #9D26B0;}.el-checkbox__inner:hover { border-color: #9D26B0;}.el-checkbox-button__inner:hover { color: #9D26B0;}.el-checkbox-button.is-checked .el-checkbox-button__inner { background-color: #9D26B0; border-color: #9D26B0; box-shadow: -1px 0 0 0 #c47dd0;}.el-checkbox-button.is-checked:first-child .el-checkbox-button__inner { border-left-color: #9D26B0;}.el-checkbox-button.is-focus .el-checkbox-button__inner { border-color: #9D26B0;}.el-checkbox-button--mini .el-checkbox-button__inner { padding: 5px 12px;}.el-checkbox-button--mini .el-checkbox-button__inner.is-round { padding: 5px 12px;}.el-radio.is-bordered.is-checked { border-color: #9D26B0;}.el-radio__input.is-checked .el-radio__inner { border-color: #9D26B0; background: #9D26B0;}.el-radio__input.is-checked + .el-radio__label { color: #9D26B0;}.el-radio__input.is-focus .el-radio__inner { border-color: #9D26B0;}.el-radio__inner:hover { border-color: #9D26B0;}.el-radio:focus:not(.is-focus):not(:active):not(.is-disabled) .el-radio__inner { box-shadow: 0 0 2px 2px #9D26B0;}.el-cascader-node.in-active-path, .el-cascader-node.is-selectable.in-checked-path, .el-cascader-node.is-active { color: #9D26B0;}.yb-main-header { background-color: #9D26B0;}.el-checkbox.is-bordered.is-checked { border-color: #9D26B0;}.el-checkbox__input.is-checked .el-checkbox__inner { background-color: #9D26B0; border-color: #9D26B0;}.el-checkbox__input.is-checked + .el-checkbox__label { color: #9D26B0;}.el-checkbox__input.is-focus .el-checkbox__inner { border-color: #9D26B0;}.el-checkbox__input.is-indeterminate .el-checkbox__inner { background-color: #9D26B0; border-color: #9D26B0;}.el-checkbox__inner:hover { border-color: #9D26B0;}.el-checkbox-button__inner:hover { color: #9D26B0;}.el-checkbox-button.is-checked .el-checkbox-button__inner { background-color: #9D26B0; border-color: #9D26B0; box-shadow: -1px 0 0 0 #c47dd0;}.el-checkbox-button.is-checked:first-child .el-checkbox-button__inner { border-left-color: #9D26B0;}.el-checkbox-button.is-focus .el-checkbox-button__inner { border-color: #9D26B0;}.el-checkbox-button--mini .el-checkbox-button__inner { padding: 5px 12px;}.el-checkbox-button--mini .el-checkbox-button__inner.is-round { padding: 5px 12px;}.el-button:hover, .el-button:focus { color: #9D26B0; border-color: #e2bee7; background-color: #f5e9f7;}.el-button:active { color: #8d229e; border-color: #8d229e;}.el-button.is-plain:hover, .el-button.is-plain:focus { border-color: #9D26B0; color: #9D26B0;}.el-button.is-plain:active { border-color: #8d229e; color: #8d229e;}.el-button.is-active { color: #8d229e; border-color: #8d229e;}.el-button--primary { background-color: #9D26B0; border-color: #9D26B0;}.el-button--primary:hover, .el-button--primary:focus { background: #b151c0; border-color: #b151c0;}.el-button--primary:active { background: #8d229e; border-color: #8d229e;}.el-button--primary.is-active { background: #8d229e; border-color: #8d229e;}.el-button--primary.is-disabled, .el-button--primary.is-disabled:hover, .el-button--primary.is-disabled:focus, .el-button--primary.is-disabled:active { background-color: #ce93d8; border-color: #ce93d8;}.el-button--primary.is-plain { color: #9D26B0; background: #f5e9f7; border-color: #d8a8df;}.el-button--primary.is-plain:hover, .el-button--primary.is-plain:focus { background: #9D26B0; border-color: #9D26B0;}.el-button--primary.is-plain:active { background: #8d229e; border-color: #8d229e;}.el-button--primary.is-plain.is-disabled, .el-button--primary.is-plain.is-disabled:hover, .el-button--primary.is-plain.is-disabled:focus, .el-button--primary.is-plain.is-disabled:active { color: #c47dd0; background-color: #f5e9f7; border-color: #ebd4ef;}.el-button--mini { padding: 5px 12px;}.el-button--mini.is-round { padding: 5px 12px;}.el-button--mini.is-circle { padding: 5px;}.el-button--text { color: #9D26B0;}.el-button--text:hover, .el-button--text:focus { color: #b151c0;}.el-button--text:active { color: #8d229e;}.el-textarea__inner:focus { border-color: #9D26B0;}.el-input__inner:focus { border-color: #9D26B0;}.el-input.is-active .el-input__inner { border-color: #9D26B0;}.el-message-box__headerbtn:focus .el-message-box__close, .el-message-box__headerbtn:hover .el-message-box__close { color: #9D26B0;}.el-checkbox.is-bordered.is-checked { border-color: #9D26B0;}.el-checkbox__input.is-checked .el-checkbox__inner { background-color: #9D26B0; border-color: #9D26B0;}.el-checkbox__input.is-checked + .el-checkbox__label { color: #9D26B0;}.el-checkbox__input.is-focus .el-checkbox__inner { border-color: #9D26B0;}.el-checkbox__input.is-indeterminate .el-checkbox__inner { background-color: #9D26B0; border-color: #9D26B0;}.el-checkbox__inner:hover { border-color: #9D26B0;}.el-checkbox-button__inner:hover { color: #9D26B0;}.el-checkbox-button.is-checked .el-checkbox-button__inner { background-color: #9D26B0; border-color: #9D26B0; box-shadow: -1px 0 0 0 #c47dd0;}.el-checkbox-button.is-checked:first-child .el-checkbox-button__inner { border-left-color: #9D26B0;}.el-checkbox-button.is-focus .el-checkbox-button__inner { border-color: #9D26B0;}.el-checkbox-button--mini .el-checkbox-button__inner { padding: 5px 12px;}.el-checkbox-button--mini .el-checkbox-button__inner.is-round { padding: 5px 12px;}.el-tree__drop-indicator { background-color: #9D26B0;}.el-tree-node.is-drop-inner > .el-tree-node__content .el-tree-node__label { background-color: #9D26B0;}.el-tree--highlight-current .el-tree-node.is-current > .el-tree-node__content { background-color: #f7eef9;}.el-select-dropdown__item.selected { color: #9D26B0;}.el-table__body tr.current-row::after { background-color: #9D26B0;}`;
419 | /**
420 | *
421 | * @param {string} options.primaryColor 新的主题色
422 | * @param {object} options.targetValueReplacer 可用于非颜色值的替换,如"padding:10px;" 中的 "10px"
423 | */
424 | function setCustomTheme({ primaryColor, targetValueReplacer }) {
425 | setNewThemeStyle({
426 | primaryColor,
427 | targetValueReplacer,
428 | styleTagId: 'coustom-theme-tagid',
429 | sourceThemeStyle,
430 | sourceColorMap,
431 | hybridValueMap,
432 | otherValues,
433 | });
434 | }
435 |
436 | setCustomTheme({ primaryColor: '#F4791E' ,targetValueReplacer:{"5px 12px":"6px 18px"}});
437 |
--------------------------------------------------------------------------------
/commitlint.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | extends: ["@commitlint/config-conventional"],
3 | };
4 |
--------------------------------------------------------------------------------
/husky.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | hooks: {
3 | "pre-commit": "lint-staged",
4 | "commit-msg": "commitlint -E HUSKY_GIT_PARAMS",
5 | },
6 | };
7 |
--------------------------------------------------------------------------------
/images/example.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GitOfZGT/some-loader-utils/659526602fffa75ef0c74bcd210840ab3bc9b829/images/example.gif
--------------------------------------------------------------------------------
/images/includeStyles_p.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GitOfZGT/some-loader-utils/659526602fffa75ef0c74bcd210840ab3bc9b829/images/includeStyles_p.png
--------------------------------------------------------------------------------
/images/includeStyles_r.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GitOfZGT/some-loader-utils/659526602fffa75ef0c74bcd210840ab3bc9b829/images/includeStyles_r.png
--------------------------------------------------------------------------------
/lint-staged.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | "*.js": ["eslint --fix", "prettier --write"],
3 | "*.{json,md,yml,css,ts}": ["prettier --write"],
4 | };
5 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "bin": {
3 | "z-theme": "dist/bin/index.js"
4 | },
5 | "name": "@zougt/some-loader-utils",
6 | "version": "1.4.3",
7 | "description": "implementation for less-loader or sass-loader. Compiles Less or sass to CSS.",
8 | "license": "MIT",
9 | "repository": "GitOfZGT/some-loader-utils",
10 | "author": "zougt ",
11 | "homepage": "https://github.com/GitOfZGT/some-loader-utils",
12 | "bugs": "https://github.com/GitOfZGT/some-loader-utils/issues",
13 | "main": "dist/index.js",
14 | "engines": {
15 | "node": ">= 10.13.0"
16 | },
17 | "scripts": {
18 | "start": "npm run build -- -w",
19 | "clean": "del-cli dist",
20 | "prebuild": "npm run clean",
21 | "build": "cross-env NODE_ENV=production babel src -d dist --ignore 'src/arbitraryMode/browser.js' --copy-files",
22 | "commitlint": "commitlint --from=master",
23 | "security": "npm audit",
24 | "lint:prettier": "prettier --list-different .",
25 | "lint:js": "eslint --cache .",
26 | "lint": "npm-run-all -l -p \"lint:**\"",
27 | "test:only": "cross-env NODE_ENV=test jest",
28 | "test:watch": "npm run test:only -- --watch",
29 | "test:coverage": "npm run test:only -- --collectCoverageFrom=\"src/**/*.js\" --coverage",
30 | "pretest": "npm run lint",
31 | "test": "npm run test:coverage",
32 | "prepare": "npm run build",
33 | "release": "standard-version",
34 | "defaults": "webpack-defaults"
35 | },
36 | "files": [
37 | "dist"
38 | ],
39 | "dependencies": {
40 | "cac": "^6.7.12",
41 | "color": "^4.0.1",
42 | "cssnano": "^5.0.11",
43 | "cssnano-preset-lite": "^2.0.1",
44 | "fs-extra": "^10.0.0",
45 | "postcss": "^8.2.9",
46 | "prettier": "^2.5.0",
47 | "uuid": "^8.3.2"
48 | },
49 | "devDependencies": {
50 | "@babel/cli": "^7.12.10",
51 | "@babel/core": "^7.12.10",
52 | "@babel/preset-env": "^7.12.11",
53 | "@commitlint/cli": "^11.0.0",
54 | "@commitlint/config-conventional": "^11.0.0",
55 | "@webpack-contrib/defaults": "^6.3.0",
56 | "@webpack-contrib/eslint-config-webpack": "^3.0.0",
57 | "babel-jest": "^26.6.3",
58 | "color": "^4.0.1",
59 | "cross-env": "^7.0.3",
60 | "del": "^6.0.0",
61 | "del-cli": "^3.0.1",
62 | "eslint": "^7.18.0",
63 | "eslint-config-prettier": "^7.2.0",
64 | "eslint-plugin-import": "^2.22.1",
65 | "eslint-plugin-prettier": "^3.3.0",
66 | "husky": "^4.3.8",
67 | "jest": "^26.6.3",
68 | "less": "^4.1.0",
69 | "lint-staged": "^10.5.3",
70 | "memfs": "^3.2.0",
71 | "npm-run-all": "^4.1.5",
72 | "standard-version": "^9.1.0",
73 | "strip-ansi": "^6.0.0",
74 | "webpack": "^5.16.0"
75 | },
76 | "keywords": [
77 | "loader",
78 | "less",
79 | "sass",
80 | "theme",
81 | "css",
82 | "preprocessor"
83 | ],
84 | "publishConfig": {
85 | "registry": "https://registry.npmjs.org"
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/src/arbitraryMode/browser.js:
--------------------------------------------------------------------------------
1 | /** 此文件内容,可能会不使用babel等工具编译,保证最好兼容性 */
2 | /* eslint-disable prefer-destructuring */
3 | /* eslint-disable prefer-arrow-callback */
4 | /* eslint-disable func-names */
5 | /* eslint-disable vars-on-top */
6 | /* eslint-disable object-shorthand */
7 | /* eslint-disable no-var */
8 | /* eslint-disable import/prefer-default-export */
9 | /* eslint-disable no-unused-vars */
10 | /**
11 | * @param {object} options
12 | * @param {function} options.Color see https://github.com/Qix-/color
13 | * */
14 | function getSetNewThemeMethod(options) {
15 | var Color = options.Color;
16 | function mix(color1, color2, weight) {
17 | var p = weight / 100.0;
18 | var w = p * 2 - 1;
19 | var a = color1.hsl().valpha - color2.hsl().valpha;
20 |
21 | var w1 = ((w * a === -1 ? w : (w + a) / (1 + w * a)) + 1) / 2.0;
22 | var w2 = 1 - w1;
23 | var alpha = color1.hsl().valpha * p + color2.hsl().valpha * (1 - p);
24 | var arr1 = color1.rgb().array();
25 | var arr2 = color2.rgb().array();
26 | var rgb = [
27 | arr1[0] * w1 + arr2[0] * w2,
28 | arr1[1] * w1 + arr2[1] * w2,
29 | arr1[2] * w1 + arr2[2] * w2,
30 | alpha,
31 | ];
32 |
33 | return Color.rgb(rgb);
34 | }
35 | function getTargetColor(newPrimaryColor, percentGias) {
36 | var primaryHsvArr = newPrimaryColor.hsv().array();
37 | var getTargetVal = function (index) {
38 | return (
39 | primaryHsvArr[index] - percentGias[index] * primaryHsvArr[index]
40 | );
41 | };
42 | var targetColor = Color.hsv([
43 | getTargetVal(0),
44 | getTargetVal(1),
45 | getTargetVal(2),
46 | newPrimaryColor.valpha - percentGias[3] * newPrimaryColor.valpha,
47 | ]);
48 | return targetColor;
49 | }
50 | /**
51 | *
52 | * @param {object} opt
53 | * @param {string} opt.primaryColor 切换主题色
54 | * @param {string} opt.defaultPrimaryColor 默认主题色
55 | * @param {string} opt.sourceThemeStyle 源主题样式
56 | * @param {object} opt.sourceColorMap 梯度映射
57 | * @param {object} opt.hybridValueMap 混合值的映射
58 | * @param {array} opt.otherValues 非颜色的值
59 | * @param {object} [opt.gradientReplacer] 存在多个梯度主色,可对应替换梯度主色
60 | * @param {object} [opt.targetValueReplacer] 可用于非颜色值的替换,如"padding:10px;" 中的 "10px",(如果是颜色值,则是精确替换颜色)
61 | */
62 | function getReplaceStyleValues(opt) {
63 | var primaryColor = opt.primaryColor || '';
64 | var gradientReplacer = opt.gradientReplacer || {};
65 | var targetValueReplacer = opt.targetValueReplacer || {};
66 | var sourceColorMap = opt.sourceColorMap || {};
67 | var hybridValueMap = opt.hybridValueMap || {};
68 | var otherValues = opt.otherValues || [];
69 |
70 | var replaceColorMap = {};
71 | for (var key in sourceColorMap) {
72 | if (Object.hasOwnProperty.call(sourceColorMap, key)) {
73 | if (targetValueReplacer[key]) {
74 | replaceColorMap[key] = targetValueReplacer[key];
75 | } else {
76 | var item = sourceColorMap[key];
77 |
78 | var replacer = item.sourceVarColorString
79 | ? gradientReplacer[item.sourceVarColorString]
80 | : '';
81 | var newColor = '';
82 | if (item.mixColorString) {
83 | newColor = mix(
84 | Color(item.mixColorString),
85 | Color(replacer || primaryColor),
86 | item.weight
87 | );
88 | } else {
89 | newColor = getTargetColor(
90 | Color(replacer || primaryColor),
91 | replacer ? item.sourcePercentGias : item.percentGias
92 | );
93 | }
94 | replaceColorMap[key] =
95 | newColor.valpha < 1
96 | ? newColor.rgb().toString()
97 | : newColor.hex().toString();
98 | }
99 | }
100 | }
101 | var replaceHybridValueMap = {};
102 | Object.keys(hybridValueMap).forEach(function (temp) {
103 | var sourceValue = hybridValueMap[temp];
104 | var sourceColors = Object.keys(sourceColorMap);
105 | var findColor = null;
106 | for (var i = 0; i < sourceColors.length; i++) {
107 | if (sourceValue.indexOf(sourceColors[i]) > -1) {
108 | findColor = sourceColors[i];
109 | break;
110 | }
111 | }
112 | if (findColor) {
113 | replaceHybridValueMap[sourceValue] = (
114 | targetValueReplacer[temp] || temp
115 | ).replace('#{color}', replaceColorMap[findColor]);
116 | }
117 | });
118 | var replaceOtherValueMap = {};
119 | otherValues.forEach(function (val) {
120 | if (targetValueReplacer[val]) {
121 | replaceOtherValueMap[val] = targetValueReplacer[val];
122 | }
123 | });
124 | return {
125 | replaceColorMap: replaceColorMap,
126 | replaceHybridValueMap: replaceHybridValueMap,
127 | replaceOtherValueMap: replaceOtherValueMap,
128 | };
129 | }
130 | /**
131 | *
132 | * @param {object} opt
133 | * @param {string} opt.primaryColor 主题色
134 | * @param {string} opt.styleTagId html中主题样式的style tag id
135 | * @param {string} opt.sourceThemeStyle 源主题样式
136 | * @param {object} opt.sourceColorMap 梯度映射
137 | * @param {object} opt.hybridValueMap 混合值的映射
138 | * @param {array} opt.otherValues 非颜色的值
139 | * @param {object} [opt.gradientReplacer] 存在多个梯度主色,可对应替换梯度主色
140 | * @param {object} [opt.targetValueReplacer] 可用于非颜色值的替换,如"padding:10px;" 中的 "10px",(如果是颜色值,则是精确替换颜色)
141 | */
142 | function setNewThemeStyle(opt) {
143 | var styleTagId = opt.styleTagId || '';
144 | var sourceThemeStyle = opt.sourceThemeStyle || '';
145 | var resultParam = getReplaceStyleValues(opt);
146 | var replaceColorMap = resultParam.replaceColorMap;
147 | var replaceHybridValueMap = resultParam.replaceHybridValueMap;
148 | var replaceOtherValueMap = resultParam.replaceOtherValueMap;
149 |
150 | var newStyleContent = sourceThemeStyle;
151 | Object.keys(replaceOtherValueMap).forEach(function (sourceValue) {
152 | newStyleContent = newStyleContent.replace(
153 | new RegExp(sourceValue.replace(/(\(|\)|\.)/g, '\\$1'), 'gi'),
154 | replaceOtherValueMap[sourceValue]
155 | );
156 | });
157 | Object.keys(replaceHybridValueMap).forEach(function (sourceValue) {
158 | newStyleContent = newStyleContent.replace(
159 | new RegExp(sourceValue.replace(/(\(|\)|\.)/g, '\\$1'), 'gi'),
160 | replaceHybridValueMap[sourceValue]
161 | );
162 | });
163 | Object.keys(replaceColorMap).forEach(function (sourceValue) {
164 | newStyleContent = newStyleContent.replace(
165 | new RegExp(sourceValue.replace(/(\(|\)|\.)/g, '\\$1'), 'gi'),
166 | replaceColorMap[sourceValue]
167 | );
168 | });
169 | // console.log(newStyleContent);
170 | // eslint-disable-next-line no-undef
171 | var styleTag = document.getElementById(styleTagId);
172 | if (styleTag) {
173 | styleTag.innerHTML = newStyleContent;
174 | } else {
175 | // eslint-disable-next-line no-undef
176 | styleTag = document.createElement('style');
177 | styleTag.id = styleTagId;
178 | styleTag.type = 'text/css';
179 | styleTag.innerHTML = newStyleContent;
180 | // eslint-disable-next-line no-undef
181 | document.body.appendChild(styleTag);
182 | }
183 | }
184 | return { setNewThemeStyle: setNewThemeStyle };
185 | }
186 |
187 | // 此行代码会在某些情况,会在不使用babel等编译时被移除掉
188 | export { getSetNewThemeMethod };
189 |
--------------------------------------------------------------------------------
/src/arbitraryMode/index.js:
--------------------------------------------------------------------------------
1 | import path from 'path';
2 |
3 | import Color from 'color';
4 |
5 | import fs from 'fs-extra';
6 |
7 | import prettier from 'prettier';
8 |
9 | import { extractThemeCss } from '../utils';
10 |
11 | import { getCurrentPackRequirePath } from '../packPath';
12 |
13 | import { colorValueReg, isSameColor } from './utils';
14 |
15 | function mix(color1, color2, weight) {
16 | const p = weight / 100.0;
17 | const w = p * 2 - 1;
18 | const a = color1.hsl().valpha - color2.hsl().valpha;
19 |
20 | const w1 = ((w * a === -1 ? w : (w + a) / (1 + w * a)) + 1) / 2.0;
21 | const w2 = 1 - w1;
22 | const alpha = color1.hsl().valpha * p + color2.hsl().valpha * (1 - p);
23 | const arr1 = color1.rgb().array();
24 | const arr2 = color2.rgb().array();
25 | const rgb = [
26 | arr1[0] * w1 + arr2[0] * w2,
27 | arr1[1] * w1 + arr2[1] * w2,
28 | arr1[2] * w1 + arr2[2] * w2,
29 | alpha,
30 | ];
31 |
32 | return Color.rgb(rgb);
33 | }
34 | function getMixColorAndWeight({ primaryColor, targetColor, otherMixColors }) {
35 | const arr = ['#ffffff', '#000000']
36 | .concat(otherMixColors || [])
37 | .map((cstr) => Color(cstr));
38 | const arr1 = targetColor.rgb().array();
39 | let weight = 0;
40 | let mixColor = null;
41 | for (let i = 0; i < arr.length; i++) {
42 | const color1 = arr[i];
43 | for (let j = 1; j <= 100; j++) {
44 | const sourceColor = mix(color1, primaryColor, j);
45 | const arr2 = sourceColor.rgb().array();
46 | if (
47 | Math.round(arr1[0]) === Math.round(arr2[0]) &&
48 | Math.round(arr1[1]) === Math.round(arr2[1]) &&
49 | Math.round(arr1[2]) === Math.round(arr2[2])
50 | ) {
51 | weight = j;
52 | mixColor = color1;
53 | break;
54 | }
55 | }
56 | if (mixColor) {
57 | break;
58 | }
59 | }
60 | return { mixColor, weight, valpha: targetColor.valpha };
61 | }
62 |
63 | function getHsvPercentGias(primaryColor, resultColor) {
64 | const primaryHsvArr = primaryColor.hsv().array();
65 | const resultHsvArr = resultColor.hsv().array();
66 | const getGiasPercent = (index) =>
67 | primaryHsvArr[index] !== 0
68 | ? (primaryHsvArr[index] - resultHsvArr[index]) /
69 | primaryHsvArr[index]
70 | : primaryHsvArr[index];
71 | return [
72 | getGiasPercent(0),
73 | getGiasPercent(1),
74 | getGiasPercent(2),
75 | primaryColor.valpha !== 0
76 | ? (primaryColor.valpha - resultColor.valpha) / primaryColor.valpha
77 | : primaryColor.valpha,
78 | ];
79 | }
80 |
81 | function separatValue(cssValues) {
82 | const cssColors = [];
83 | const hybridValueMap = {};
84 | const otherValues = [];
85 | cssValues.forEach((val) => {
86 | const hasSpace = /\s+/.test(val);
87 | const hex = val.match(colorValueReg.hex);
88 | const rgb = val.match(colorValueReg.rgb);
89 | const rgba = val.match(colorValueReg.rgba);
90 | const hsl = val.match(colorValueReg.hsl);
91 | const hsla = val.match(colorValueReg.hsla);
92 | if (!hex && !rgb && !rgba && !hsl && !hsla) {
93 | otherValues.push(val);
94 | } else {
95 | let residueVal = val;
96 | const getResidueVal = (colorArr, cssColors, residueVal) => {
97 | let newVal = residueVal;
98 | if (colorArr) {
99 | colorArr.forEach((cval) => {
100 | cssColors.push(cval);
101 | newVal = residueVal.replace(
102 | cval,
103 | hasSpace ? '#{color}' : ''
104 | );
105 | });
106 | }
107 | return newVal;
108 | };
109 | residueVal = getResidueVal(hex, cssColors, residueVal);
110 | residueVal = getResidueVal(rgb, cssColors, residueVal);
111 | residueVal = getResidueVal(rgba, cssColors, residueVal);
112 | residueVal = getResidueVal(hsl, cssColors, residueVal);
113 | residueVal = getResidueVal(hsla, cssColors, residueVal);
114 | if (residueVal) {
115 | hybridValueMap[residueVal] = val;
116 | }
117 | }
118 | });
119 | return {
120 | cssColors: Array.from(new Set(cssColors)),
121 | hybridValueMap,
122 | otherValues,
123 | };
124 | }
125 | function getResultColorReplaceMap({
126 | cssColors,
127 | varsColorValues,
128 | otherMixColors,
129 | defaultPrimaryColor,
130 | includeStyleWithColors,
131 | hueDiffControls,
132 | }) {
133 | // if (defaultPrimaryColor) {
134 |
135 | // const defaultColor = Color(defaultPrimaryColor).hsv();
136 | // if (
137 | // !varsColorValues.some((varStr) => {
138 | // const varColor = Color(varStr).hsv();
139 | // return (
140 | // defaultColor.color[0] === varColor.color[0] &&
141 | // defaultColor.color[1] === varColor.color[1] &&
142 | // defaultColor.color[2] === varColor.color[2] &&
143 | // defaultColor.valpha === varColor.valpha
144 | // );
145 | // })
146 | // ) {
147 | // console.warn(
148 | // `warning: defaultPrimaryColor:${defaultPrimaryColor} can not found in multipleScopeVars[].path`
149 | // );
150 | // }
151 | // }
152 | let primaryVarColor = null;
153 | try {
154 | primaryVarColor = Color(defaultPrimaryColor).hsv();
155 | } catch (e) {
156 | throw Error(
157 | `error:defaultPrimaryColor: ${defaultPrimaryColor} not a color value`
158 | );
159 | }
160 | const hueDiffControler = { low: 0, high: 0, ...(hueDiffControls || {}) };
161 | const sourceColorMap = {};
162 |
163 | cssColors.forEach((colorString) => {
164 | // 在 includeStyleWithColors 存在的颜色值就不会根据主色转换,除非启用了includeStyleWithColors[].inGradient:true
165 | if (
166 | includeStyleWithColors
167 | .filter((item) => !item.inGradient)
168 | .some((item) => {
169 | if (Array.isArray(item.color)) {
170 | return item.color.some((co) =>
171 | isSameColor(co, colorString)
172 | );
173 | }
174 | return isSameColor(item.color, colorString);
175 | })
176 | ) {
177 | return;
178 | }
179 | const resultColor = Color(colorString).hsv();
180 | let finded = false;
181 | for (let index = 0; index < varsColorValues.length; index++) {
182 | const varStr = varsColorValues[index];
183 | const varColor = Color(varStr).hsv();
184 | const varHue = Math.floor(varColor.color[0]);
185 | const hues = [varHue];
186 | for (let i = 0; i < hueDiffControler.low; i++) {
187 | const h = varHue - (i + 1);
188 | hues.push(h < 0 ? 0 : h);
189 | }
190 | for (let i = 0; i < hueDiffControler.high; i++) {
191 | const h = varHue + (i + 1);
192 | hues.push(h > 360 ? 360 : h);
193 | }
194 | const reHue = Math.floor(resultColor.color[0]);
195 | if (hues.some((h) => h === reHue)) {
196 | const { mixColor, weight, valpha } = getMixColorAndWeight({
197 | primaryColor: varColor,
198 | targetColor: resultColor,
199 | otherMixColors,
200 | });
201 | if (mixColor) {
202 | sourceColorMap[colorString] = {
203 | mixColorString: mixColor.hex(),
204 | weight,
205 | // varColorString: defaultPrimaryColor,
206 | sourceVarColorString: varStr,
207 | valpha,
208 | };
209 | } else {
210 | sourceColorMap[colorString] = {
211 | percentGias: getHsvPercentGias(
212 | primaryVarColor,
213 | resultColor
214 | ),
215 | // varColorString: defaultPrimaryColor,
216 | sourcePercentGias: getHsvPercentGias(
217 | varColor,
218 | resultColor
219 | ),
220 | sourceVarColorString: varStr,
221 | };
222 | }
223 | finded = true;
224 | break;
225 | }
226 | }
227 | if (!finded && defaultPrimaryColor) {
228 | const { mixColor, weight } = getMixColorAndWeight({
229 | primaryColor: primaryVarColor,
230 | targetColor: resultColor,
231 | otherMixColors,
232 | });
233 | if (mixColor) {
234 | sourceColorMap[colorString] = {
235 | mixColorString: mixColor.hex(),
236 | weight,
237 | // varColorString: defaultPrimaryColor,
238 | };
239 | } else {
240 | sourceColorMap[colorString] = {
241 | percentGias: getHsvPercentGias(
242 | primaryVarColor,
243 | resultColor
244 | ),
245 | // varColorString: defaultPrimaryColor,
246 | };
247 | }
248 | }
249 | });
250 | return sourceColorMap;
251 | }
252 | function getThemeStyleContent(scopeName, removeCssScopeName) {
253 | return extractThemeCss({ scopeName, removeCssScopeName }).then(
254 | ({ themeCss, themeRuleValues }) => {
255 | let styleContent = '';
256 | Object.values(themeCss).forEach((css) => {
257 | styleContent = `${styleContent}${css}`;
258 | });
259 | return { styleContent, themeRuleValues };
260 | }
261 | );
262 | }
263 |
264 | function createSetCustomThemeFile({
265 | defaultPrimaryColor,
266 | styleTagId = 'coustom-theme-tagid',
267 | includeStyleWithColors,
268 | styleContent,
269 | themeRuleValues,
270 | customThemeOutputPath,
271 | appendedContent,
272 | preAppendedContent,
273 | importUtils,
274 | hueDiffControls,
275 | otherMixColors,
276 | }) {
277 | if (typeof defaultPrimaryColor !== 'string' || !defaultPrimaryColor) {
278 | throw Error('defaultPrimaryColor can not found.');
279 | }
280 | const targetRsoleved = getCurrentPackRequirePath();
281 | let baseVarColorsJson = {};
282 | if (fs.existsSync(`${targetRsoleved}/baseVarColors.json`)) {
283 | baseVarColorsJson = JSON.parse(
284 | fs.readFileSync(`${targetRsoleved}/baseVarColors.json`).toString()
285 | );
286 | }
287 |
288 | const { cssColors, hybridValueMap, otherValues } =
289 | separatValue(themeRuleValues);
290 | const sourceColorMap = getResultColorReplaceMap({
291 | cssColors,
292 | varsColorValues: baseVarColorsJson.baseVarColors || [],
293 | defaultPrimaryColor,
294 | includeStyleWithColors,
295 | hueDiffControls,
296 | otherMixColors,
297 | });
298 |
299 | const targetValueReplacer = Object.keys(sourceColorMap)
300 | .concat(Object.keys(hybridValueMap))
301 | .concat(otherValues)
302 | .reduce((tol, curr) => {
303 | return { ...tol, [curr]: curr };
304 | }, {});
305 | const gradientReplacer = Object.keys(sourceColorMap)
306 | .map((key) => sourceColorMap[key].sourceVarColorString)
307 | .reduce((tol, curr) => {
308 | return { ...tol, [curr]: curr };
309 | }, {});
310 | const browerCodes = fs
311 | .readFileSync(path.join(__dirname, './browser.js'))
312 | .toString();
313 |
314 | fs.outputFile(
315 | `${targetRsoleved}/customThemeOptions.json`,
316 |
317 | JSON.stringify({
318 | Color: 'see https://github.com/Qix-/color',
319 | primaryColor: defaultPrimaryColor,
320 | gradientReplacer,
321 | targetValueReplacer,
322 | }),
323 | () => {}
324 | );
325 |
326 | const fileContent = `
327 | ${
328 | importUtils
329 | ? 'import { getSetNewThemeMethod } from "@zougt/some-loader-utils/dist/arbitraryMode/browser";'
330 | : ''
331 | }
332 | ${
333 | !importUtils
334 | ? browerCodes
335 | .replace(
336 | /import\s+([^\s]+|\{[^{}]+\})\s+from ['"][^'"]+['"];?/g,
337 | ''
338 | )
339 | .replace(/export\s+\{[^{}]+\};?/g, '')
340 | : ''
341 | }\n
342 | var saveThemeOptions = {
343 | defaultPrimaryColor:${JSON.stringify(defaultPrimaryColor)},
344 | primaryColor:${JSON.stringify(defaultPrimaryColor)},
345 | sourceThemeStyle:${JSON.stringify(styleContent)},
346 | hybridValueMap:${JSON.stringify(hybridValueMap)},
347 | otherValues:${JSON.stringify(otherValues)},
348 | sourceColorMap:${JSON.stringify(sourceColorMap)},
349 | styleTagId: "${styleTagId}"
350 | };
351 | function setSaveThemeOptions(opt){saveThemeOptions=Object.assign(saveThemeOptions,opt||{});};
352 | /**
353 | * 通常只需 primaryColor , gradientReplacer 和 targetValueReplacer 作为辅助使用
354 | *
355 | * @param {object} options
356 | * @param {function} options.Color required , 传入即可, 来源: https://github.com/Qix-/color
357 | * @param {string} options.primaryColor 新的主题色,替换所有颜色
358 | * @param {object} [options.gradientReplacer=${JSON.stringify(
359 | gradientReplacer
360 | )}] 存在多个梯度主色,可对应替换梯度主色
361 | * @param {object} [options.targetValueReplacer=${JSON.stringify(
362 | targetValueReplacer
363 | )}] 可用于非颜色值的替换,如"padding:10px;" 中的 "10px",(如果是颜色值,则是精确替换颜色)
364 | */
365 | function setCustomTheme(options) {
366 | setSaveThemeOptions(options);
367 | getSetNewThemeMethod({Color:options.Color}).setNewThemeStyle(saveThemeOptions);
368 | }`;
369 | return prettier.resolveConfig(process.cwd()).then((options) => {
370 | const setCustomThemeConent = prettier.format(
371 | `${preAppendedContent || ''}${fileContent}${appendedContent || ''}`,
372 | { ...(options || {}), parser: 'babel' }
373 | );
374 | if (customThemeOutputPath) {
375 | fs.outputFileSync(customThemeOutputPath, setCustomThemeConent);
376 | }
377 | return {
378 | styleContent,
379 | setCustomThemeConent,
380 | hybridValueMap,
381 | otherValues,
382 | sourceColorMap,
383 | };
384 | });
385 | }
386 |
387 | // eslint-disable-next-line import/prefer-default-export
388 | export { createSetCustomThemeFile, getThemeStyleContent };
389 |
--------------------------------------------------------------------------------
/src/arbitraryMode/utils.js:
--------------------------------------------------------------------------------
1 | import Color from 'color';
2 |
3 | export const colorValueReg = {
4 | hex: /#([0-9a-f]{8}|[0-9a-f]{6}|[0-9a-f]{3})/gi,
5 | rgb: /rgb\(\s*\d{1,3}\s*,\s*\d{1,3}\s*,\s*\d{1,3}\s*\)/gi,
6 | rgba: /rgba\(\s*\d{1,3}\s*,\s*\d{1,3}\s*,\s*\d{1,3}\s*,\s*(0|1|1.0|0?.[0-9]+)\s*\)/gi,
7 | hsl: /hsl\(\s*\d{1,3}\s*,\s*(0|\d{1,3}%)\s*,\s*(0|\d{1,3}%)\s*\)/gi,
8 | hsla: /hsla\(\s*\d{1,3}\s*,\s*(0|\d{1,3}%)\s*,\s*(0|\d{1,3}%)\s*,\s*(0|1|1.0|0?.[0-9]+)\s*\)/gi,
9 | };
10 |
11 | export function isSameColor(str1, str2) {
12 | if (str1 === str2) {
13 | return true;
14 | }
15 | let same = false;
16 | try {
17 | const color1 = Color(str1).rgb().array();
18 | const color2 = Color(str2).rgb().array();
19 | same =
20 | color1[0] === color2[0] &&
21 | color1[1] === color2[1] &&
22 | color1[2] === color2[2] &&
23 | color1[3] === color2[3];
24 | } catch (e) {
25 | same = false;
26 | }
27 | return same;
28 | }
29 |
30 | export function removeSpaceInColor(val = '') {
31 | let newVal = val;
32 | const rgb = val.match(colorValueReg.rgb) || [];
33 | const rgba = val.match(colorValueReg.rgba) || [];
34 | const hsl = val.match(colorValueReg.hsl) || [];
35 | const hsla = val.match(colorValueReg.hsla) || [];
36 | const removeSpace = (colors, value) => {
37 | let replacer = value;
38 | colors.forEach((c) => {
39 | replacer = replacer.replace(c, c.replace(/\s+/g, ''));
40 | });
41 | return replacer;
42 | };
43 | newVal = removeSpace(rgb, newVal);
44 | newVal = removeSpace(rgba, newVal);
45 | newVal = removeSpace(hsl, newVal);
46 | newVal = removeSpace(hsla, newVal);
47 | return newVal;
48 | }
49 |
50 | export default {};
51 |
--------------------------------------------------------------------------------
/src/bin/index.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 | import cac from 'cac';
3 |
4 | import fs from 'fs-extra';
5 |
6 | import pack from '../../package.json';
7 |
8 | import { getCurrentPackRequirePath } from '../packPath';
9 |
10 | const cli = cac();
11 | const init = (optionName) => {
12 | const targetRsoleved = getCurrentPackRequirePath();
13 | if (fs.existsSync(`${targetRsoleved}/customThemeOptions.json`)) {
14 | try {
15 | const opts = JSON.parse(
16 | fs
17 | .readFileSync(`${targetRsoleved}/customThemeOptions.json`)
18 | .toString()
19 | );
20 | console.log(
21 | !optionName ? opts : { [optionName]: opts[optionName] }
22 | );
23 | // eslint-disable-next-line no-empty
24 | } catch (e) {}
25 | }
26 | };
27 | cli.command('inspect [optionName]', 'inspect setCustomTheme options').action(
28 | init
29 | );
30 | cli.command('ins [optionName]', 'inspect setCustomTheme options').action(init);
31 | cli.help();
32 | cli.version(pack.version);
33 | cli.parse();
34 |
--------------------------------------------------------------------------------
/src/getLess.js:
--------------------------------------------------------------------------------
1 | import {
2 | getScopeProcessResult,
3 | getAllStyleVarFiles,
4 | getVarsContent,
5 | createArbitraryModeVarColors,
6 | getPluginParams,
7 | } from './utils';
8 | /**
9 | *
10 | * @param {Object} opt
11 | * @param {Object} opt.implementation
12 | * @param {Function} opt.getMultipleScopeVars
13 | * @returns less
14 | */
15 | export function getLess(opt = {}) {
16 | const packname = 'less';
17 | let less = opt.implementation;
18 | if (!less) {
19 | try {
20 | less = require(packname);
21 | } catch (e) {
22 | throw new Error(
23 | `Dependency "${packname}" not found. Did you install it?`
24 | );
25 | }
26 | }
27 | const { render } = less;
28 | // eslint-disable-next-line func-names
29 | less.render = function (input, options = {}, callback) {
30 | const renderOptions = { ...options };
31 | const defaultPluginOpt = getPluginParams(opt);
32 | const multipleScopeVars =
33 | typeof opt.getMultipleScopeVars === 'function'
34 | ? opt.getMultipleScopeVars(renderOptions)
35 | : renderOptions.multipleScopeVars;
36 |
37 | delete renderOptions.multipleScopeVars;
38 |
39 | const allStyleVarFiles = getAllStyleVarFiles(
40 | {
41 | emitError: (msg) => {
42 | throw new Error(msg);
43 | },
44 | },
45 | { multipleScopeVars, arbitraryMode: defaultPluginOpt.arbitraryMode }
46 | );
47 | const preProcessor = (code) => render.call(less, code, renderOptions);
48 | // 按allStyleVarFiles的个数对当前文件编译多次得到多个结果
49 | const rePromise = Promise.all(
50 | allStyleVarFiles.map((file) => {
51 | const varscontent = getVarsContent(file.path, packname);
52 | if (defaultPluginOpt.arbitraryMode) {
53 | createArbitraryModeVarColors(varscontent);
54 | }
55 |
56 | return preProcessor(
57 | `${input}\n${varscontent}\n${file.varsContent || ''}`
58 | );
59 | })
60 | )
61 | .then((prs) =>
62 | getScopeProcessResult(
63 | prs.map((item) => {
64 | return { ...item, code: item.css, deps: item.imports };
65 | }),
66 | allStyleVarFiles,
67 | renderOptions.filename,
68 | defaultPluginOpt.includeStyleWithColors,
69 | defaultPluginOpt.arbitraryMode,
70 | defaultPluginOpt.extract
71 | )
72 | )
73 | .then((result) => {
74 | const cssResult = {
75 | css: result.code,
76 | imports: result.deps,
77 | map: result.map,
78 | };
79 | if (callback) {
80 | callback(null, cssResult);
81 | return null;
82 | }
83 | return cssResult;
84 | });
85 | if (callback) {
86 | rePromise.catch(callback);
87 | }
88 | return rePromise;
89 | };
90 | return less;
91 | }
92 |
93 | export default getLess;
94 |
--------------------------------------------------------------------------------
/src/getSass.js:
--------------------------------------------------------------------------------
1 | import { v5 as uuidv5 } from 'uuid';
2 |
3 | import {
4 | getScopeProcessResult,
5 | getAllStyleVarFiles,
6 | getVarsContent,
7 | createArbitraryModeVarColors,
8 | getPluginParams,
9 | // eslint-disable-next-line import/no-extraneous-dependencies
10 | } from './utils';
11 |
12 | /**
13 | *
14 | * @param {Object} opt
15 | * @param {Object} opt.implementation
16 | * @param {Function} opt.getMultipleScopeVars
17 | * @returns sass
18 | */
19 | export function getSass(opt = {}) {
20 | const packname = 'sass';
21 |
22 | let sass = opt.implementation;
23 | if (!sass) {
24 | try {
25 | sass = require(packname);
26 | } catch (e) {
27 | throw new Error(
28 | `Dependency "${packname}" not found. Did you install it?`
29 | );
30 | }
31 | }
32 |
33 | const { render } = sass;
34 |
35 | // eslint-disable-next-line func-names
36 | sass.render = function (options = {}, callback) {
37 | const renderOptions = { ...options };
38 | const defaultPluginOpt = getPluginParams(opt);
39 | const multipleScopeVars =
40 | typeof opt.getMultipleScopeVars === 'function'
41 | ? opt.getMultipleScopeVars(renderOptions)
42 | : renderOptions.multipleScopeVars;
43 |
44 | delete renderOptions.multipleScopeVars;
45 |
46 | const allStyleVarFiles = getAllStyleVarFiles(
47 | {
48 | emitError: (msg) => {
49 | throw new Error(msg);
50 | },
51 | },
52 | { multipleScopeVars, arbitraryMode: defaultPluginOpt.arbitraryMode }
53 | );
54 | const preProcessor = (code) =>
55 | new Promise((resolve, reject) => {
56 | render.call(
57 | sass,
58 | { ...renderOptions, data: code },
59 | (err, res) => {
60 | if (err) {
61 | reject(err);
62 | } else {
63 | resolve(res);
64 | }
65 | }
66 | );
67 | });
68 | let cssResult = {};
69 | // 按allStyleVarFiles的个数对当前文件编译多次得到多个结果
70 | const rePromise = Promise.all(
71 | allStyleVarFiles.map((file) => {
72 | const varscontent = getVarsContent(file.path, packname);
73 | if (defaultPluginOpt.arbitraryMode) {
74 | createArbitraryModeVarColors(varscontent);
75 | }
76 | return preProcessor(
77 | `${file.varsContent || ''}${varscontent}\n${
78 | renderOptions.data
79 | }`
80 | );
81 | })
82 | )
83 | .then((prs) => {
84 | // eslint-disable-next-line prefer-destructuring
85 | cssResult = prs[0];
86 |
87 | return getScopeProcessResult(
88 | prs.map((item) => {
89 | return {
90 | ...item,
91 | code: item.css.toString(),
92 | deps: item.stats.includedFiles,
93 | };
94 | }),
95 | allStyleVarFiles,
96 | // sass-loader v8.x 没有传入renderOptions.file, 用uuid生成一个,防止报错
97 | renderOptions.file ||
98 | (renderOptions.data &&
99 | uuidv5(
100 | renderOptions.data,
101 | '4725327a-3250-4226-86cf-ae5ce775795b'
102 | )),
103 | defaultPluginOpt.includeStyleWithColors,
104 | defaultPluginOpt.arbitraryMode,
105 | defaultPluginOpt.extract
106 | );
107 | })
108 |
109 | .then((result) => {
110 | cssResult.css = result.code;
111 | cssResult.stats.includedFiles = result.deps;
112 |
113 | if (callback) {
114 | callback(null, cssResult);
115 | return null;
116 | }
117 |
118 | return cssResult;
119 | });
120 | if (callback) {
121 | rePromise.catch(callback);
122 | }
123 | return rePromise;
124 | };
125 | return sass;
126 | }
127 |
128 | export default getSass;
129 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | export * from './utils';
2 | export * from './getLess';
3 | export * from './getSass';
4 | export * from './arbitraryMode';
5 |
--------------------------------------------------------------------------------
/src/packPath.js:
--------------------------------------------------------------------------------
1 | import pack from '../package.json';
2 |
3 | export function getCurrentPackRequirePath() {
4 | const targetRsoleved = require
5 | .resolve(pack.name)
6 | .replace(/[\\/]dist[\\/]index\.js$/, '')
7 | .replace(/\\/g, '/');
8 | return targetRsoleved;
9 | }
10 |
11 | export default {};
12 |
--------------------------------------------------------------------------------
/src/postcss-addScopeName.js:
--------------------------------------------------------------------------------
1 | import postcss from 'postcss';
2 |
3 | import { isSameColor, removeSpaceInColor } from './arbitraryMode/utils';
4 |
5 | export default (
6 | opts = {
7 | allStyleVarFiles: [],
8 | allCssCodes: [],
9 | startIndex: 0,
10 | arbitraryMode: false,
11 | includeStyleWithColors: [],
12 | extract: false,
13 | },
14 | themeRuleValues = [],
15 | themeRuleMap = {}
16 | ) => {
17 | const { allStyleVarFiles, arbitraryMode, extract } = opts;
18 | function addScopeName(selector, scopeName) {
19 | if (/^(\.[^:]+)?:root/i.test(selector)) {
20 | return `.${scopeName}${selector}`;
21 | }
22 | if (/^html/i.test(selector)) {
23 | return selector.replace(
24 | /^html[^\s]*(?=\s*)/gi,
25 | (word) => `${word}.${scopeName}`
26 | );
27 | }
28 | return `.${scopeName}\x20${selector}`;
29 | }
30 | // allStyleVarFiles的个数与allCssCodes对应的
31 | // 除去startIndex的,其他的css转成ast
32 | const restCssAsts = opts.allCssCodes
33 | .filter((item, i) => i !== opts.startIndex)
34 | .map((code) => postcss.parse(code));
35 | return {
36 | postcssPlugin: 'postcss-addScopeName',
37 | Rule(rule, { Rule, Declaration }) {
38 | let blendRule = null;
39 | // 与当前样式规则不相同的其他主题样式规则
40 | const themeRules = [];
41 | // 当前规则中与其他主题规则中不相同的属性
42 | const currentThemeProps = {};
43 | // 当前规则中所有属性,按顺序去重
44 | const currentRuleNodeMap = {};
45 | rule.nodes.forEach((node) => {
46 | if (node.type === 'decl') {
47 | currentRuleNodeMap[node.prop] = node;
48 | }
49 | });
50 | const currentThemeKeyframes = [];
51 |
52 | const extractThemeKeyframesMap = {};
53 | const getThemeRules = (themeRule, varFile) => {
54 | const childNodes = [];
55 | // 先将主题规则属性按顺序去重
56 | const themeRuleNodeMap = {};
57 | themeRule.nodes.forEach((node) => {
58 | if (node.type === 'decl') {
59 | themeRuleNodeMap[node.prop] = node;
60 | }
61 | });
62 | Object.keys(currentRuleNodeMap).forEach((prop) => {
63 | if (!themeRuleNodeMap[prop]) {
64 | return;
65 | }
66 | // 比对属性值不相等的,或者存在 includeStyleWithColors 中对应的颜值,就进行分离
67 | if (
68 | currentRuleNodeMap[prop].value !==
69 | themeRuleNodeMap[prop].value ||
70 | (!varFile.arbitraryMode &&
71 | (opts.includeStyleWithColors || []).some((item) => {
72 | if (
73 | Array.isArray(item.excludeSelectors) &&
74 | themeRuleNodeMap[prop].parent &&
75 | themeRuleNodeMap[prop].parent.type ===
76 | 'rule'
77 | ) {
78 | const { selectors } =
79 | themeRuleNodeMap[prop].parent;
80 | if (
81 | item.excludeSelectors.some(
82 | (selectorStr) =>
83 | selectorStr.replace(
84 | /,\s+/g,
85 | ','
86 | ) === selectors.join(',')
87 | )
88 | ) {
89 | return false;
90 | }
91 | }
92 | const isExcludeProperty =
93 | Array.isArray(item.excludeCssProps) &&
94 | item.excludeCssProps.includes(prop);
95 | if (isExcludeProperty) {
96 | return false;
97 | }
98 | if (Array.isArray(item.color)) {
99 | return item.color.some((co) =>
100 | isSameColor(
101 | co,
102 | themeRuleNodeMap[prop].value
103 | )
104 | );
105 | }
106 | return isSameColor(
107 | item.color,
108 | themeRuleNodeMap[prop].value
109 | );
110 | }))
111 | ) {
112 | themeRuleValues.add(
113 | removeSpaceInColor(themeRuleNodeMap[prop].value)
114 | );
115 |
116 | childNodes.push(themeRuleNodeMap[prop]);
117 | currentThemeProps[prop] = currentRuleNodeMap[prop];
118 | }
119 | delete themeRuleNodeMap[prop];
120 | });
121 | // 假如比对后还有剩余,也纳入主题属性
122 | Object.keys(themeRuleNodeMap).forEach((prop) => {
123 | childNodes.push(themeRuleNodeMap[prop]);
124 | });
125 | themeRule.nodes = childNodes;
126 | if (!arbitraryMode && varFile.arbitraryMode) {
127 | blendRule = themeRule.clone();
128 | } else {
129 | themeRules.push(themeRule.clone());
130 | }
131 | themeRule.remove();
132 | };
133 | const restVarFiles = allStyleVarFiles.slice(0);
134 | restVarFiles.splice(opts.startIndex, 1);
135 | restCssAsts.forEach((root, i) => {
136 | for (
137 | let index = 0;
138 | index < (root.nodes || []).length;
139 | index++
140 | ) {
141 | const themeRule = root.nodes[index];
142 |
143 | /* ast第一层节点属于选择器类型的
144 | * 找到与rule选择器匹配的
145 | */
146 | const isSameRule =
147 | themeRule.type === 'rule' &&
148 | themeRule.selector === rule.selector;
149 | if (isSameRule) {
150 | getThemeRules(themeRule, restVarFiles[i]);
151 | break;
152 | }
153 | // 当这条规则在@media内
154 | const isInMedia =
155 | rule.parent.type === 'atrule' &&
156 | rule.parent.name === 'media';
157 | const isSameInMedia =
158 | themeRule.type === 'atrule' &&
159 | themeRule.name === 'media' &&
160 | themeRule.params === rule.parent.params;
161 | if (isInMedia && isSameInMedia) {
162 | // 刚好是同一个@media时,就往里面找到匹配的规则处理
163 | const atruleChild = (themeRule.nodes || []).find(
164 | (item) =>
165 | item.type === 'rule' &&
166 | item.selector === rule.selector
167 | );
168 | if (atruleChild) {
169 | getThemeRules(atruleChild, restVarFiles[i]);
170 | }
171 |
172 | break;
173 | }
174 | // 当这条规则在@keyframes内
175 | const isInKeyframes =
176 | rule.parent.type === 'atrule' &&
177 | rule.parent.name === 'keyframes';
178 | if (isInKeyframes) {
179 | // 又当@keyframes在@media内部时
180 | const isKeyframeInMedia =
181 | rule.parent.parent &&
182 | rule.parent.parent.type === 'atrule' &&
183 | rule.parent.parent.name === 'media';
184 | const isSameKeyframeInMedia =
185 | themeRule.type === 'atrule' &&
186 | themeRule.name === 'media' &&
187 | rule.parent.parent &&
188 | rule.parent.parent.params === themeRule.params;
189 | if (isKeyframeInMedia && isSameKeyframeInMedia) {
190 | // 当刚好是同一个@media,往里面找到匹配的@keyframes
191 | const atruleChild = (themeRule.nodes || []).find(
192 | (item) =>
193 | item.type === 'atrule' &&
194 | item.name === 'keyframes' &&
195 | item.params === rule.parent.params
196 | );
197 |
198 | if (atruleChild) {
199 | const childRules = atruleChild.nodes || [];
200 | const existsDiffValue = childRules.some(
201 | (item) => {
202 | const isExst =
203 | item.type === 'rule' &&
204 | item.selector === rule.selector &&
205 | item.nodes.some((node) => {
206 | const isDiffValue =
207 | node.type === 'decl' &&
208 | currentRuleNodeMap[
209 | node.prop
210 | ].value !== node.value;
211 | if (isDiffValue) {
212 | themeRuleValues.add(
213 | removeSpaceInColor(
214 | node.value
215 | )
216 | );
217 | }
218 | return isDiffValue;
219 | });
220 | return isExst;
221 | }
222 | );
223 | if (existsDiffValue) {
224 | if (
225 | !arbitraryMode &&
226 | !restVarFiles[i].arbitraryMode
227 | ) {
228 | currentThemeKeyframes.push(
229 | atruleChild.clone()
230 | );
231 | const currentMedia =
232 | rule.parent.parent.clone();
233 | currentMedia.removeAll();
234 | currentMedia.append(
235 | rule.parent.clone()
236 | );
237 | extractThemeKeyframesMap[
238 | allStyleVarFiles[
239 | opts.startIndex
240 | ].scopeName
241 | ] = currentMedia;
242 | rule.parent.remove();
243 | }
244 | const media = themeRule.clone();
245 | media.removeAll();
246 | media.append(atruleChild.clone());
247 | extractThemeKeyframesMap[
248 | restVarFiles[i].scopeName
249 | ] = media;
250 | atruleChild.remove();
251 | }
252 | }
253 |
254 | break;
255 | }
256 | const isSameKeyFrame =
257 | themeRule.type === 'atrule' &&
258 | themeRule.name === 'keyframes' &&
259 | themeRule.params === rule.parent.params;
260 | if (isSameKeyFrame) {
261 | const childRules = themeRule.nodes || [];
262 | const existsDiffValue = childRules.some((item) => {
263 | const isExst =
264 | item.type === 'rule' &&
265 | item.selector === rule.selector &&
266 | item.nodes.some((node) => {
267 | const isDiffValue =
268 | node.type === 'decl' &&
269 | currentRuleNodeMap[node.prop]
270 | .value !== node.value;
271 |
272 | if (isDiffValue) {
273 | themeRuleValues.add(
274 | removeSpaceInColor(node.value)
275 | );
276 | }
277 | return isDiffValue;
278 | });
279 | return isExst;
280 | });
281 | if (existsDiffValue) {
282 | if (
283 | !arbitraryMode &&
284 | !restVarFiles[i].arbitraryMode
285 | ) {
286 | currentThemeKeyframes.push(
287 | themeRule.clone()
288 | );
289 | extractThemeKeyframesMap[
290 | allStyleVarFiles[
291 | opts.startIndex
292 | ].scopeName
293 | ] = rule.parent.clone();
294 | rule.parent.remove();
295 | }
296 | extractThemeKeyframesMap[
297 | restVarFiles[i].scopeName
298 | ] = themeRule.clone();
299 | themeRule.remove();
300 | break;
301 | }
302 | }
303 | }
304 | }
305 | });
306 | const root = rule.parent;
307 | rule.nodes.forEach((node) => {
308 | if (
309 | node.type === 'decl' &&
310 | currentThemeProps[node.prop] === node
311 | ) {
312 | node.remove();
313 | }
314 | });
315 |
316 | if (themeRules.length) {
317 | const firstThemeRule = rule.clone();
318 | if (!arbitraryMode) {
319 | firstThemeRule.removeAll();
320 | }
321 | Object.keys(currentThemeProps).forEach((key) => {
322 | for (let index = 0; index < themeRules.length; index++) {
323 | const tRule = themeRules[index];
324 | // 如果当前规则的样式属性在其他主题规则中不存在,说明该样式属性是所有主题样式属性,但是在上面比对中发现值一致未被添加,这里要添加回去
325 | if (
326 | !tRule.nodes.some(
327 | (node) =>
328 | node.type === 'decl' && node.prop === key
329 | )
330 | ) {
331 | tRule.append(currentThemeProps[key].clone());
332 | }
333 | }
334 | if (!arbitraryMode) {
335 | firstThemeRule.append(currentThemeProps[key].clone());
336 | }
337 | });
338 | if (!arbitraryMode) {
339 | // 保持themeRules的顺序对应 opts.allStyleVarFiles的顺序,然后添加scopeName
340 | themeRules.splice(opts.startIndex, 0, firstThemeRule);
341 | }
342 | let arbitraryModeScopeItem = null;
343 | const scopeItems = (
344 | !arbitraryMode ? allStyleVarFiles : restVarFiles
345 | ).filter((item) => {
346 | if (item.arbitraryMode) {
347 | arbitraryModeScopeItem = item;
348 | }
349 | return !item.arbitraryMode;
350 | });
351 |
352 | themeRules.forEach((item, i) => {
353 | if (item && item.nodes.length) {
354 | const removeNodes = [];
355 | if (blendRule && blendRule.nodes.length) {
356 | item.nodes.forEach((node) => {
357 | if (
358 | node.type === 'decl' &&
359 | blendRule.nodes.some(
360 | (b) =>
361 | b.type === 'decl' &&
362 | b.prop === node.prop &&
363 | b.value === node.prop
364 | )
365 | ) {
366 | removeNodes.push(node);
367 | }
368 | });
369 | }
370 | removeNodes.forEach((node) => {
371 | item.removeChild(node);
372 | });
373 | if (!arbitraryMode && item.nodes.length) {
374 | // eslint-disable-next-line no-param-reassign
375 | item.selectors = item.selectors.map((selector) =>
376 | addScopeName(selector, scopeItems[i].scopeName)
377 | );
378 | if (!extract) {
379 | root.insertBefore(rule, item);
380 | }
381 | }
382 | let themeCssItem = item;
383 | if (
384 | themeCssItem.nodes.length &&
385 | rule.parent.type === 'atrule' &&
386 | rule.parent.name === 'media'
387 | ) {
388 | const pclone = rule.parent.clone();
389 | pclone.removeAll();
390 | pclone.append(item.clone());
391 | themeCssItem = pclone;
392 | }
393 | if (themeCssItem.nodes.length) {
394 | const scopeSet =
395 | themeRuleMap[scopeItems[i].scopeName] ||
396 | new Set();
397 | scopeSet.add(themeCssItem.toString());
398 | themeRuleMap[scopeItems[i].scopeName] = scopeSet;
399 | }
400 | }
401 | });
402 | if (!arbitraryMode && blendRule && arbitraryModeScopeItem) {
403 | let themeCssItem = blendRule;
404 | if (themeCssItem.nodes.length) {
405 | // eslint-disable-next-line no-param-reassign
406 | themeCssItem.selectors = themeCssItem.selectors.map(
407 | (selector) =>
408 | addScopeName(
409 | selector,
410 | arbitraryModeScopeItem.scopeName
411 | )
412 | );
413 | }
414 |
415 | if (
416 | themeCssItem.nodes.length &&
417 | rule.parent.type === 'atrule' &&
418 | rule.parent.name === 'media'
419 | ) {
420 | const pclone = rule.parent.clone();
421 | pclone.removeAll();
422 | pclone.append(themeCssItem.clone());
423 | themeCssItem = pclone;
424 | }
425 | if (themeCssItem.nodes.length) {
426 | const scopeSet =
427 | themeRuleMap[arbitraryModeScopeItem.scopeName] ||
428 | new Set();
429 | scopeSet.add(themeCssItem.toString());
430 | themeRuleMap[arbitraryModeScopeItem.scopeName] =
431 | scopeSet;
432 | }
433 | }
434 | if (!arbitraryMode) {
435 | const selectorMap = rule.selectors.reduce((tol, key) => {
436 | return { ...tol, [key]: true };
437 | }, {});
438 |
439 | scopeItems.forEach((item) => {
440 | if (
441 | item.includeStyles &&
442 | typeof item.includeStyles === 'object' &&
443 | !Array.isArray(item.includeStyles)
444 | ) {
445 | const includeKey = Object.keys(
446 | item.includeStyles
447 | ).find((key) => {
448 | const selectors = key
449 | .replace(/,\s+/g, ',')
450 | .split(',');
451 | return selectors.every((s) => selectorMap[s]);
452 | });
453 | if (includeKey) {
454 | const newRule = new Rule({
455 | selector: rule.selector,
456 | });
457 | newRule.selectors = newRule.selectors.map(
458 | (selector) =>
459 | addScopeName(selector, item.scopeName)
460 | );
461 | Object.keys(
462 | item.includeStyles[includeKey]
463 | ).forEach((prop) => {
464 | const decl = new Declaration({
465 | prop,
466 | value: item.includeStyles[includeKey][
467 | prop
468 | ],
469 | });
470 | newRule.append(decl);
471 | const currDecl = rule.nodes.find((node) => {
472 | if (node.prop === decl.prop) {
473 | return (
474 | node.value === decl.value ||
475 | isSameColor(
476 | node.value,
477 | decl.value
478 | )
479 | );
480 | }
481 | return false;
482 | });
483 |
484 | if (currDecl) {
485 | rule.removeChild(currDecl);
486 | }
487 | });
488 | const scopeSet =
489 | themeRuleMap[item.scopeName] || new Set();
490 | scopeSet.add(newRule.toString());
491 | themeRuleMap[item.scopeName] = scopeSet;
492 | if (!extract) {
493 | root.insertBefore(rule, newRule);
494 | }
495 | }
496 | }
497 | });
498 | }
499 | if (!rule.nodes.length) {
500 | rule.remove();
501 | }
502 | }
503 | if (!arbitraryMode && !extract) {
504 | currentThemeKeyframes.forEach((item) => {
505 | if (rule.parent && rule.parent.parent) {
506 | rule.parent.parent.insertBefore(rule.parent, item);
507 | }
508 | });
509 | }
510 |
511 | for (const key in extractThemeKeyframesMap) {
512 | if (Object.hasOwnProperty.call(extractThemeKeyframesMap, key)) {
513 | const keyframesRule = extractThemeKeyframesMap[key];
514 | const scopeSet = themeRuleMap[key] || new Set();
515 | scopeSet.add(keyframesRule.toString());
516 | themeRuleMap[key] = scopeSet;
517 | }
518 | }
519 | },
520 | };
521 | };
522 |
--------------------------------------------------------------------------------
/src/utils.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable import/no-dynamic-require */
2 | /* eslint-disable import/extensions */
3 | /* eslint-disable import/no-unresolved */
4 |
5 | import fs from 'fs-extra';
6 |
7 | import { v5 as uuidv5 } from 'uuid';
8 |
9 | import postcss from 'postcss';
10 |
11 | import cssnano from 'cssnano';
12 |
13 | import lite from 'cssnano-preset-lite';
14 |
15 | import postcssAddScopeName from './postcss-addScopeName';
16 |
17 | import { colorValueReg } from './arbitraryMode/utils';
18 |
19 | // import browerColorMap from './arbitraryMode/colors';
20 |
21 | import { getCurrentPackRequirePath } from './packPath';
22 |
23 | function getBlendVarFiles(allStyleVarFiles) {
24 | const allStyleVarFilesCopy = allStyleVarFiles.slice(0);
25 | let blendVarItem = null;
26 | for (let index = 0; index < allStyleVarFiles.length; index++) {
27 | const item = allStyleVarFiles[index];
28 | if (item.arbitraryMode) {
29 | blendVarItem = item;
30 | allStyleVarFilesCopy.splice(index, 1);
31 | break;
32 | }
33 | }
34 | allStyleVarFilesCopy.forEach((item) => {
35 | item.arbitraryMode = false;
36 | });
37 | if (blendVarItem) {
38 | allStyleVarFilesCopy.push(blendVarItem);
39 | }
40 | return {
41 | allStyleVarFiles: allStyleVarFilesCopy,
42 | blendVarFile: blendVarItem,
43 | };
44 | }
45 | const getAllStyleVarFiles = (loaderContext, options) => {
46 | const varsSet = {};
47 | (options.multipleScopeVars || []).forEach((item) => {
48 | varsSet[item.scopeName] = item;
49 | });
50 | const styleVarFiles = Object.keys(varsSet).reduce(
51 | (tol, key) => [...tol, varsSet[key]],
52 | []
53 | );
54 | let allStyleVarFiles = [{ scopeName: '', path: '' }];
55 | if (Array.isArray(styleVarFiles) && styleVarFiles.length) {
56 | if (styleVarFiles.length === 1) {
57 | allStyleVarFiles = styleVarFiles.map((item) => {
58 | if (Array.isArray(item.path)) {
59 | const exist = item.path.every((pathstr) => {
60 | const exists = pathstr && fs.existsSync(pathstr);
61 | if (!exists) {
62 | loaderContext.emitError(
63 | new Error(
64 | `Not found path: ${pathstr} in multipleScopeVars`
65 | )
66 | );
67 | }
68 | return exists;
69 | });
70 | if (!exist) {
71 | return { scopeName: '', path: '' };
72 | }
73 | } else if (
74 | (!item.path ||
75 | typeof item.path !== 'string' ||
76 | !fs.existsSync(item.path)) &&
77 | typeof item.varsContent !== 'string'
78 | ) {
79 | loaderContext.emitError(
80 | new Error(
81 | `Not found path or varsContent: ${item.path} in multipleScopeVars`
82 | )
83 | );
84 | return { scopeName: '', path: '' };
85 | }
86 | if (options.arbitraryMode) {
87 | if (!item.scopeName) {
88 | return { ...item, scopeName: 'theme-default' };
89 | }
90 | return item;
91 | }
92 | return { ...item, scopeName: '' };
93 | });
94 | return (
95 | options.arbitraryMode ? [{ scopeName: '', path: '' }] : []
96 | ).concat(
97 | allStyleVarFiles.filter(
98 | (item) =>
99 | !!item.path || typeof item.varsContent === 'string'
100 | )
101 | );
102 | }
103 | allStyleVarFiles = styleVarFiles.filter((item) => {
104 | if (!item.scopeName) {
105 | loaderContext.emitError(
106 | new Error('Not found scopeName in multipleScopeVars')
107 | );
108 | return false;
109 | }
110 | if (Array.isArray(item.path)) {
111 | return (
112 | item.path.every((pathstr) => {
113 | const exists = pathstr && fs.existsSync(pathstr);
114 | if (!exists) {
115 | loaderContext.emitError(
116 | new Error(
117 | `Not found path : ${pathstr} in multipleScopeVars`
118 | )
119 | );
120 | }
121 | return exists;
122 | }) || typeof item.varsContent === 'string'
123 | );
124 | }
125 | if (
126 | (!item.path ||
127 | typeof item.path !== 'string' ||
128 | !fs.existsSync(item.path)) &&
129 | typeof item.varsContent !== 'string'
130 | ) {
131 | loaderContext.emitError(
132 | new Error(
133 | `Not found path or varsContent: ${item.path} in multipleScopeVars`
134 | )
135 | );
136 | return false;
137 | }
138 | return true;
139 | });
140 | }
141 | return options.arbitraryMode
142 | ? allStyleVarFiles.slice(0, 2)
143 | : getBlendVarFiles(allStyleVarFiles).allStyleVarFiles;
144 | };
145 |
146 | // const cssFragReg = /[^{}/\\]+{[^{}]*?}/g;
147 | // const classNameFragReg = /[^{}/\\]+(?={)/;
148 |
149 | /**
150 | * 把多个 css 内容按 multipleScopeVars 对应顺序合并,并去重
151 | * @param {Array} cssResults [
152 | {
153 | map: sourceMap || null,
154 | code: `
155 | .un-btn {
156 | position: relative;
157 | background-color: #0081ff;
158 | }
159 | .un-btn .anticon {
160 | line-height: 1;
161 | }`,
162 | deps: ["E:\\sub\\panel1.less", "E:\\sub\\panel2.less"],
163 | },
164 | {
165 | map: sourceMap || null,
166 | code: `
167 | .un-btn {
168 | position: relative;
169 | background-color: #9c26b0;
170 | }
171 | .un-btn .anticon {
172 | line-height: 1;
173 | }`,
174 | deps: ["E:\\sub\\panel1.less", "E:\\sub\\panel2.less"],
175 | },
176 | ]
177 | * @param {Array} allStyleVarFiles
178 | [
179 | { scopeName: "theme-default", path: "E:\\sub\\default-vars.less" },
180 | { scopeName: "theme-mauve", path: "E:\\sub\\mauve-vars.less" },
181 | ]
182 | * @param {String} resourcePath "E:\\sub\\style.less"
183 | * @returns
184 | */
185 | const getScopeProcessResult = (
186 | cssResults = [],
187 | allStyleVarFiles = [],
188 | resourcePath,
189 | includeStyleWithColors,
190 | arbitraryMode,
191 | extract
192 | ) => {
193 | const preprocessResult = { deps: [], code: '', errors: [] };
194 | if (cssResults.length === 1) {
195 | preprocessResult.code = cssResults[0].code;
196 | preprocessResult.deps = cssResults[0].deps;
197 | return Promise.resolve(preprocessResult);
198 | }
199 | cssResults.forEach((item, i) => {
200 | preprocessResult.errors = [
201 | ...(preprocessResult.errors || []),
202 | ...(item.errors || []),
203 | ];
204 | const deps = Array.isArray(allStyleVarFiles[i].path)
205 | ? allStyleVarFiles[i].path
206 | : [allStyleVarFiles[i].path];
207 | deps.forEach((str) => {
208 | if (str) {
209 | preprocessResult.deps.push(str);
210 | }
211 | });
212 | });
213 | preprocessResult.deps = [
214 | ...preprocessResult.deps,
215 | ...(cssResults[0].deps || []),
216 | ];
217 | /**
218 | * 用cssResults的第一个css内容进入postcss
219 | */
220 | const startIndex = 0;
221 | const themeRuleValues = new Set();
222 | const themeRuleMap = {};
223 | return postcss([
224 | postcssAddScopeName(
225 | {
226 | allStyleVarFiles,
227 | allCssCodes: cssResults.map((r) => r.code),
228 | // 除去allCssCodes中的第几个
229 | startIndex,
230 | arbitraryMode,
231 | includeStyleWithColors,
232 | extract,
233 | },
234 | themeRuleValues,
235 | themeRuleMap
236 | ),
237 | ])
238 | .process(cssResults[startIndex].code, {
239 | from: resourcePath,
240 | to: resourcePath,
241 | })
242 | .then((postResult) => {
243 | const MY_NAMESPACE = '1b671a64-40d5-491e-99b0-da01ff1f3341';
244 | const filename = uuidv5(resourcePath, MY_NAMESPACE);
245 | const dirName = 'extractTheme';
246 | const targetRsoleved = getCurrentPackRequirePath();
247 | if (!fs.existsSync(`${targetRsoleved}/${dirName}`)) {
248 | fs.mkdirSync(`${targetRsoleved}/${dirName}`);
249 | }
250 | const cssRules = {};
251 | for (const key in themeRuleMap) {
252 | if (Object.hasOwnProperty.call(themeRuleMap, key)) {
253 | const ruleSet = themeRuleMap[key];
254 | const cssArr = Array.from(ruleSet);
255 | if (cssArr.length) {
256 | cssRules[key] = cssArr;
257 | }
258 | }
259 | }
260 | const themeRuleValuesArr = Array.from(themeRuleValues);
261 |
262 | const filecontent = {
263 | cssRules,
264 | ruleValues: themeRuleValuesArr,
265 | resourcePath,
266 | };
267 | fs.writeFileSync(
268 | `${targetRsoleved}/${dirName}/${filename}.json`,
269 | JSON.stringify(filecontent, null, 4)
270 | );
271 |
272 | preprocessResult.code = postResult.css;
273 | return preprocessResult;
274 | });
275 | };
276 |
277 | /**
278 | * getScropProcessResult 修正命名 getScopeProcessResult后的兼容
279 | */
280 | const getScropProcessResult = getScopeProcessResult;
281 | /**
282 | *
283 | * @param {String} url
284 | * @param {String} type "less" | "sass"
285 | * @returns code
286 | */
287 | const replaceFormSass = (url, type) => {
288 | let code = url ? fs.readFileSync(url).toString() : '';
289 | if (type === 'sass') {
290 | if (/\.less$/i.test(url)) {
291 | code = code.replace(/@/g, '$');
292 | }
293 | return code.replace(/!default/g, '');
294 | }
295 | if (/\.(scss|sass)$/i.test(url)) {
296 | code = code.replace(/\$/g, '@').replace(/!default/g, '');
297 | }
298 | return code;
299 | };
300 | /**
301 | *
302 | * @param {String} url
303 | * @param {String} type "less" | "sass"
304 | * @returns code
305 | */
306 | const getVarsContent = (url, type) => {
307 | let content = '';
308 | if (Array.isArray(url)) {
309 | url.forEach((p) => {
310 | content += replaceFormSass(p, type);
311 | });
312 | } else {
313 | content = replaceFormSass(url, type);
314 | }
315 | return content;
316 | };
317 | function removeThemeFiles() {
318 | const dirName = 'extractTheme';
319 | const targetRsoleved = getCurrentPackRequirePath();
320 | if (fs.existsSync(`${targetRsoleved}/${dirName}`)) {
321 | fs.removeSync(`${targetRsoleved}/${dirName}`);
322 | }
323 | }
324 | function getExtractThemeCode(opt) {
325 | const { scopeName = '', excludeScopeNames = [] } = opt || {};
326 | const targetRsoleved = getCurrentPackRequirePath();
327 | const dirName = 'extractTheme';
328 | if (fs.existsSync(`${targetRsoleved}/${dirName}`)) {
329 | const files = fs.readdirSync(`${targetRsoleved}/${dirName}`) || [];
330 | const themeRuleCodes = {};
331 | let themeRuleValues = [];
332 | files.forEach((file) => {
333 | const { cssRules, ruleValues } = JSON.parse(
334 | fs
335 | .readFileSync(`${targetRsoleved}/${dirName}/${file}`)
336 | .toString()
337 | );
338 | if (scopeName) {
339 | if (cssRules[scopeName]) {
340 | let scopeCssArr = themeRuleCodes[scopeName] || [];
341 | scopeCssArr = scopeCssArr.concat(cssRules[scopeName]);
342 | themeRuleCodes[scopeName] = scopeCssArr;
343 | }
344 | } else {
345 | Object.keys(cssRules)
346 | .filter((key) => !(excludeScopeNames || []).includes(key))
347 | .forEach((key) => {
348 | let scopeCssArr = themeRuleCodes[key] || [];
349 | scopeCssArr = scopeCssArr.concat(cssRules[key]);
350 | themeRuleCodes[key] = scopeCssArr;
351 | });
352 | }
353 | themeRuleValues = themeRuleValues.concat(ruleValues);
354 | });
355 | return {
356 | themeRuleCodes,
357 | themeRuleValues: Array.from(new Set(themeRuleValues)),
358 | };
359 | }
360 | return { themeRules: {}, themeRuleValues: [] };
361 | }
362 |
363 | /**
364 | *
365 | * @param {Object} options
366 | * @param {Boolean} options.removeCssScopeName 抽取的css是否移除scopeName
367 | * @returns { css: String, themeCss: Object , themeCommonCss: String }
368 | */
369 | const extractThemeCss = ({
370 | removeCssScopeName,
371 | scopeName,
372 | excludeScopeNames,
373 | }) => {
374 | const { themeRuleCodes, themeRuleValues } = getExtractThemeCode({
375 | scopeName,
376 | excludeScopeNames,
377 | });
378 |
379 | const allPro = Object.keys(themeRuleCodes || {}).map((key) => {
380 | const codes = (
381 | removeCssScopeName
382 | ? themeRuleCodes[key].map((frag) =>
383 | frag.replace(new RegExp(`\\.${key}`, 'g'), '')
384 | )
385 | : themeRuleCodes[key]
386 | ).join('');
387 | return postcss([
388 | cssnano({
389 | preset: lite({}),
390 | }),
391 | ])
392 | .process(codes)
393 | .then((postResult) => {
394 | return { key, css: postResult.css };
395 | });
396 | });
397 | return Promise.all(allPro).then((res) => {
398 | const themeCss = {};
399 | res.forEach((item) => {
400 | themeCss[item.key] = item.css;
401 | });
402 | return { themeCss, themeRuleValues };
403 | });
404 | };
405 |
406 | const addScopnameToHtmlClassname = (html, defaultScopeName) => {
407 | let newHtml = html;
408 | const htmlTagAttrStrings = html.match(/<\s*html[^<>]*>/gi) || [];
409 |
410 | htmlTagAttrStrings.forEach((attrstr) => {
411 | const classnameStrings = attrstr.match(/class\s*=['"].+['"]/g);
412 | if (classnameStrings) {
413 | classnameStrings.forEach((classstr) => {
414 | const classnamestr = classstr.replace(
415 | /(^class\s*=['"]|['"]$)/g,
416 | ''
417 | );
418 | const classnames = classnamestr.split(' ');
419 | if (!classnames.includes(defaultScopeName)) {
420 | classnames.push(defaultScopeName);
421 | newHtml = newHtml.replace(
422 | attrstr,
423 | attrstr.replace(
424 | classstr,
425 | classstr.replace(classnamestr, classnames.join(' '))
426 | )
427 | );
428 | }
429 | });
430 | } else {
431 | newHtml = newHtml.replace(
432 | attrstr,
433 | `${attrstr.replace(/>$/, '')} class="${defaultScopeName}">`
434 | );
435 | }
436 | });
437 | return newHtml;
438 | };
439 |
440 | function createArbitraryModeVarColors(filecontent) {
441 | if (filecontent) {
442 | const hex = (filecontent.match(colorValueReg.hex) || []).map((color) =>
443 | color.replace(/\s+/g, '')
444 | );
445 | const rgb = (filecontent.match(colorValueReg.rgb) || []).map((color) =>
446 | color.replace(/\s+/g, '')
447 | );
448 | const rgba = (filecontent.match(colorValueReg.rgba) || []).map(
449 | (color) => color.replace(/\s+/g, '')
450 | );
451 | const hsl = (filecontent.match(colorValueReg.hsl) || []).map((color) =>
452 | color.replace(/\s+/g, '').replace(/,0(?=,|\))/g, ',0%')
453 | );
454 | const hsla = (filecontent.match(colorValueReg.hsla) || []).map(
455 | (color) => color.replace(/\s+/g, '').replace(/,0(?=,)/g, ',0%')
456 | );
457 | // const browerColorReg = new RegExp(
458 | // `(?<=:\\s*)(${Object.keys(browerColorMap).join('|')})(?=\\s*)`,
459 | // 'ig'
460 | // );
461 | // const browerColors = filecontent.match(browerColorReg) || [];
462 | const colors = Array.from(
463 | new Set(
464 | [].concat(hex).concat(rgb).concat(rgba).concat(hsl).concat(hsla)
465 | )
466 | );
467 | const targetRsoleved = getCurrentPackRequirePath();
468 | fs.writeFileSync(
469 | `${targetRsoleved}/baseVarColors.json`,
470 | JSON.stringify({ baseVarColors: colors })
471 | );
472 | }
473 | }
474 |
475 | function createPulignParamsFile(options = {}) {
476 | const targetRsoleved = getCurrentPackRequirePath();
477 | fs.writeFileSync(
478 | `${targetRsoleved}/pulignParams.js`,
479 | `module.exports = ${JSON.stringify(options)};`
480 | );
481 | }
482 |
483 | function getPluginParams(opt) {
484 | const targetRsoleved = getCurrentPackRequirePath();
485 | // webpack等插件将与getSass相同的参数,打入到pulignParams.js,这里就获取到作为默认参数
486 | let defaultPluginOpt = {};
487 | const parmasPath = `${targetRsoleved}/pulignParams.js`;
488 | if (fs.existsSync(parmasPath)) {
489 | defaultPluginOpt = require(parmasPath);
490 | }
491 | defaultPluginOpt = { ...defaultPluginOpt, ...opt };
492 | return defaultPluginOpt;
493 | }
494 |
495 | export {
496 | getAllStyleVarFiles,
497 | getScopeProcessResult,
498 | getScropProcessResult,
499 | getVarsContent,
500 | extractThemeCss,
501 | addScopnameToHtmlClassname,
502 | removeThemeFiles,
503 | createArbitraryModeVarColors,
504 | getCurrentPackRequirePath,
505 | createPulignParamsFile,
506 | getPluginParams,
507 | getBlendVarFiles,
508 | };
509 |
--------------------------------------------------------------------------------