@douyinfe/semi-rspack-plugin 0.0.1-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,169 @@
1
+ > A webpack plugin for Semi Design to custom theme、replace prefix and so on.
2
+
3
+ ## Introduction
4
+ The plugin is designed for Semi Design, support webpack4 and webpack5, provides two major abilities:
5
+ - Custom theme
6
+ - Replace prefix of CSS selector
7
+
8
+ ## Usage
9
+
10
+ ### Install
11
+ Install `@douyinfe/semi-webpack-plugin` as a development dependency:
12
+
13
+ ``` shell
14
+ npm install --save-dev @douyinfe/semi-webpack-plugin
15
+ # or
16
+ yarn add --dev @douyinfe/semi-webpack-plugin
17
+ ```
18
+
19
+ ### Custom theme
20
+ Semi Design uses the Scss variables to extract thousands of Design Tokens. You can replace Token through this plugin to achieve theme customization. [More info](https://semi.design/dsm/)
21
+
22
+ You can custom theme through three ways:
23
+ - npm package for custom theme
24
+ - Local Scss file in your project
25
+ - Pass key-value pair parameters to plugin
26
+ Priority from low to high.
27
+ #### Through npm package
28
+
29
+ In order to use the npm package, you need to customize the theme through [Semi Design System](https://semi.design/dsm/).After finishing the customization, Semi DSM will generate a npm package for you, and then you can use it like this.
30
+
31
+ ``` js
32
+ // webpack.config.js
33
+ const SemiPlugin = require('@douyinfe/semi-webpack-plugin').default;
34
+
35
+ module.exports = {
36
+ // ...
37
+ plugins: [
38
+ new SemiPlugin({
39
+ theme: '@douyinfe/semi-theme-default'
40
+ })
41
+ ]
42
+ // ...
43
+ };
44
+ ```
45
+
46
+ #### Through local Scss file
47
+
48
+ You can check which tokens can be customized on the [Semi WebSite](https://semi.design/zh-CN/basic/tokens).
49
+
50
+ - step1: add a local file
51
+ ``` scss
52
+ // local.scss
53
+ $font-size-small: 16px;
54
+
55
+ ```
56
+ - step2: config webpack
57
+ ``` js
58
+ // webpack.config.js
59
+ const path = require('path');
60
+ const SemiPlugin = require('@douyinfe/semi-webpack-plugin').default;
61
+
62
+ module.exports = {
63
+ // ...
64
+ plugins: [
65
+ new SemiPlugin({
66
+ include: path.join(__dirname, 'local.scss')
67
+ })
68
+ ]
69
+ };
70
+ ```
71
+
72
+ #### Through parameters
73
+ ``` js
74
+ // webpack.config.js
75
+ const SemiPlugin = require('@douyinfe/semi-webpack-plugin').default;
76
+
77
+ module.exports = {
78
+ // ...
79
+ plugins: [
80
+ new SemiPlugin({
81
+ variables: {
82
+ "$font-size-small": '16px'
83
+ }
84
+ })
85
+ ]
86
+ };
87
+ ```
88
+
89
+ ### Replace prefix of CSS selector
90
+ The CSS selectors used by Semi Design is prefixed with semi by default(e.g, `.semi-button`).You can replace the prefix through this plugin.
91
+
92
+ ``` js
93
+ // webpack.config.js
94
+ const SemiPlugin = require('@douyinfe/semi-webpack-plugin').default;
95
+
96
+ module.exports = {
97
+ // ...
98
+ plugins: [
99
+ new SemiPlugin({
100
+ prefixCls: 'custom'
101
+ })
102
+ ]
103
+ // ...
104
+ };
105
+ ```
106
+
107
+ Then you get the replaced CSS selectors(e.g, `.custom-button`).
108
+
109
+ ## Api
110
+ ### new SemiPlugin(options)
111
+
112
+ #### options.prefixCls
113
+
114
+ Type: `String`
115
+
116
+ The prefix of CSS selector.
117
+
118
+ #### options.theme
119
+
120
+ Type: `String` or `Object`
121
+
122
+ When the type is string, it represents the name of npm for custom theme.You can use [Semi Design System](https://semi.design) to custom theme.
123
+
124
+ ##### options.theme.name
125
+
126
+ Same performance as when the type of `options.theme` is string.
127
+
128
+ ##### options.include
129
+
130
+ Type: `String`
131
+
132
+ The absolute path of the local Scss file.
133
+
134
+ ##### options.variables
135
+
136
+ Type: `Object`
137
+
138
+ The key-value pair of Scss token.
139
+
140
+ ##### options.omitCss
141
+
142
+ Type: `Boolean`
143
+
144
+ In the compilation phase, whether to exclude css references.Used to solve the problem that Next.js does not support the global introduction of css in third-party code.See this [discussion](https://github.com/vercel/next.js/discussions/27953).
145
+
146
+ ##### options.webpackContext.NormalModule
147
+
148
+ Type: `webpack NormalModule`
149
+
150
+ ##### options.extractCssOptions.loader
151
+
152
+ Type: `String`
153
+
154
+ The path of webpack loader that extract css.
155
+
156
+ ##### options.extractCssOptions.loaderOptions
157
+
158
+ Type: `Object`
159
+
160
+ The options of webpack loader that extract css.
161
+
162
+ #### options.overrideStylesheetLoaders
163
+
164
+ Type: `(loaderList:any[])=>any[]`
165
+
166
+ You can customize how webpack process semi related styles by override the loader with this option. The function will receive the loader list of default loaders(include options.extractCssOptions) and you should return your new loader list. The best practice is just only add your loader to the list rather than delete or change the default loaders since some core logic is in there.
167
+
168
+
169
+ In webpack@5, some hooks need to be obtained through api `NormalModule.getCompilationHooks`. But in some scenarios, webpack will not be installed, such as Next.js. Therefore, the user is required to pass in NormalModule as a parameter.
@@ -0,0 +1,4 @@
1
+ export declare const SOURCE_SUFFIX_LOADER: string;
2
+ export declare const THEME_LOADER: string;
3
+ export declare const OMIT_CSS_LOADER: string;
4
+ export declare const PREFIX_LOADER: string;
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.PREFIX_LOADER = exports.OMIT_CSS_LOADER = exports.THEME_LOADER = exports.SOURCE_SUFFIX_LOADER = void 0;
7
+ const path_1 = __importDefault(require("path"));
8
+ exports.SOURCE_SUFFIX_LOADER = path_1.default.resolve(__dirname, './loaders/semi-source-suffix-loader');
9
+ exports.THEME_LOADER = path_1.default.resolve(__dirname, './loaders/semi-theme-loader');
10
+ exports.OMIT_CSS_LOADER = path_1.default.resolve(__dirname, './loaders/semi-omit-css-loader');
11
+ exports.PREFIX_LOADER = path_1.default.resolve(__dirname, './loaders/semi-prefix-loader');
package/lib/index.d.ts ADDED
@@ -0,0 +1,7 @@
1
+ export * from './types';
2
+ export * from './plugin';
3
+ export * from './rule';
4
+ export { default as semiThemeLoader } from './loaders/semi-theme-loader';
5
+ export { default as semiPrefixLoader } from './loaders/semi-prefix-loader';
6
+ export { default as semiSourceSuffixLoader } from './loaders/semi-source-suffix-loader';
7
+ export { default as semiOmitCssLoader } from './loaders/semi-omit-css-loader';
package/lib/index.js ADDED
@@ -0,0 +1,31 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ var __importDefault = (this && this.__importDefault) || function (mod) {
17
+ return (mod && mod.__esModule) ? mod : { "default": mod };
18
+ };
19
+ Object.defineProperty(exports, "__esModule", { value: true });
20
+ exports.semiOmitCssLoader = exports.semiSourceSuffixLoader = exports.semiPrefixLoader = exports.semiThemeLoader = void 0;
21
+ __exportStar(require("./types"), exports);
22
+ __exportStar(require("./plugin"), exports);
23
+ __exportStar(require("./rule"), exports);
24
+ var semi_theme_loader_1 = require("./loaders/semi-theme-loader");
25
+ Object.defineProperty(exports, "semiThemeLoader", { enumerable: true, get: function () { return __importDefault(semi_theme_loader_1).default; } });
26
+ var semi_prefix_loader_1 = require("./loaders/semi-prefix-loader");
27
+ Object.defineProperty(exports, "semiPrefixLoader", { enumerable: true, get: function () { return __importDefault(semi_prefix_loader_1).default; } });
28
+ var semi_source_suffix_loader_1 = require("./loaders/semi-source-suffix-loader");
29
+ Object.defineProperty(exports, "semiSourceSuffixLoader", { enumerable: true, get: function () { return __importDefault(semi_source_suffix_loader_1).default; } });
30
+ var semi_omit_css_loader_1 = require("./loaders/semi-omit-css-loader");
31
+ Object.defineProperty(exports, "semiOmitCssLoader", { enumerable: true, get: function () { return __importDefault(semi_omit_css_loader_1).default; } });
@@ -0,0 +1,2 @@
1
+ import { LoaderContext } from 'webpack';
2
+ export default function semiOmitCssLoader(this: LoaderContext<void>, source: string): string;
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ function semiOmitCssLoader(source) {
4
+ return source
5
+ .replace(/(import\s+['"][^'"]+\.css['"])/g, '// $1')
6
+ .replace(/(require\(['"][^'"]+\.css['"]\))/g, '// $1');
7
+ }
8
+ exports.default = semiOmitCssLoader;
@@ -0,0 +1,5 @@
1
+ import { LoaderContext } from 'webpack';
2
+ export interface SemiPrefixLoaderOptions {
3
+ replacers?: Record<string, string>;
4
+ }
5
+ export default function semiPrefixLoader(this: LoaderContext<SemiPrefixLoaderOptions>, source: string): string;
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const core_1 = require("@babel/core");
7
+ const assert_1 = __importDefault(require("assert"));
8
+ function semiPrefixLoader(source) {
9
+ const query = this.getOptions();
10
+ const transformer = {
11
+ visitor: {
12
+ VariableDeclarator(path) {
13
+ const { node } = path;
14
+ const replacerKeys = Object.keys(query.replacers || {});
15
+ if (replacerKeys.includes(node.id.name)) {
16
+ node.init.value = query.replacers[node.id.name];
17
+ }
18
+ },
19
+ },
20
+ };
21
+ const file = (0, core_1.transformSync)(source, {
22
+ sourceType: 'module',
23
+ plugins: [transformer],
24
+ });
25
+ const ret = file.code;
26
+ (0, assert_1.default)(ret, '');
27
+ return ret;
28
+ }
29
+ exports.default = semiPrefixLoader;
@@ -0,0 +1,2 @@
1
+ import { LoaderContext } from 'webpack';
2
+ export default function semiSourceSuffixLoader(this: LoaderContext<void>, source: string): string;
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ function semiSourceSuffixLoader(source) {
4
+ return source.replace(/(import\s+)['"]([^'"]+)(\.css)['"]/g, '$1\'$2.scss\'');
5
+ }
6
+ exports.default = semiSourceSuffixLoader;
@@ -0,0 +1,8 @@
1
+ import { LoaderContext } from 'webpack';
2
+ export interface SemiThemeLoaderOptions {
3
+ prefixCls: string;
4
+ variables: string;
5
+ include: string;
6
+ name?: string;
7
+ }
8
+ export default function SemiThemeLoader(this: LoaderContext<SemiThemeLoaderOptions>, source: string): string;
@@ -0,0 +1,59 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const enhanced_resolve_1 = __importDefault(require("enhanced-resolve"));
7
+ function SemiThemeLoader(source) {
8
+ const query = this.getOptions();
9
+ const theme = query.name || '@douyinfe/semi-theme-default';
10
+ // always inject
11
+ const scssVarStr = `@import "~${theme}/scss/index.scss";\n`;
12
+ // inject once
13
+ const cssVarStr = `@import "~${theme}/scss/global.scss";\n`;
14
+ let animationStr = `@import "~${theme}/scss/animation.scss";\n`;
15
+ try {
16
+ require.resolve(`${theme}/scss/animation.scss`);
17
+ }
18
+ catch (e) {
19
+ animationStr = ''; // fallback to empty string
20
+ }
21
+ const shouldInject = source.includes('semi-base');
22
+ let fileStr = source;
23
+ let componentVariables;
24
+ try {
25
+ componentVariables = enhanced_resolve_1.default.sync(this.context, `${theme}/scss/local.scss`);
26
+ }
27
+ catch (e) { }
28
+ if (query.include || query.variables || componentVariables) {
29
+ let localImport = '';
30
+ if (componentVariables) {
31
+ localImport += `\n@import "~${theme}/scss/local.scss";`;
32
+ }
33
+ if (query.include) {
34
+ localImport += `\n@import "${query.include}";`;
35
+ }
36
+ if (query.variables) {
37
+ localImport += `\n${query.variables}`;
38
+ }
39
+ try {
40
+ const regex = /(@import '.\/variables.scss';?|@import ".\/variables.scss";?)/g;
41
+ const fileSplit = source.split(regex).filter(item => Boolean(item));
42
+ if (fileSplit.length > 1) {
43
+ fileSplit.splice(fileSplit.length - 1, 0, localImport);
44
+ fileStr = fileSplit.join('');
45
+ }
46
+ }
47
+ catch (error) { }
48
+ }
49
+ // inject prefix
50
+ const prefixCls = query.prefixCls || 'semi';
51
+ const prefixClsStr = `$prefix: '${prefixCls}';\n`;
52
+ if (shouldInject) {
53
+ return `${animationStr}${cssVarStr}${scssVarStr}${prefixClsStr}${fileStr}`;
54
+ }
55
+ else {
56
+ return `${scssVarStr}${prefixClsStr}${fileStr}`;
57
+ }
58
+ }
59
+ exports.default = SemiThemeLoader;
@@ -0,0 +1,7 @@
1
+ import { Compiler } from 'webpack';
2
+ import { SemiWebpackPluginOptions } from './types';
3
+ export declare class SemiRspackPlugin {
4
+ opts: SemiWebpackPluginOptions;
5
+ constructor(options?: SemiWebpackPluginOptions);
6
+ apply(compiler: Compiler): void;
7
+ }
package/lib/plugin.js ADDED
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SemiRspackPlugin = void 0;
4
+ const rule_1 = require("./rule");
5
+ class SemiRspackPlugin {
6
+ constructor(options) {
7
+ this.opts = options;
8
+ }
9
+ apply(compiler) {
10
+ const rules = (0, rule_1.applySemiRules)(this.opts);
11
+ compiler.options.module.rules.push(...rules);
12
+ }
13
+ }
14
+ exports.SemiRspackPlugin = SemiRspackPlugin;
package/lib/rule.d.ts ADDED
@@ -0,0 +1,38 @@
1
+ import { RuleSetRule } from 'webpack';
2
+ import { SemiWebpackPluginOptions } from './types';
3
+ export declare function createSourceSuffixLoaderRule(_opts?: SemiWebpackPluginOptions): {
4
+ test: RegExp;
5
+ use: {
6
+ loader: string;
7
+ }[];
8
+ };
9
+ export declare function createThemeLoaderRule(opts?: SemiWebpackPluginOptions): {
10
+ test: RegExp;
11
+ use: {
12
+ loader: string;
13
+ options: {
14
+ prefixCls: string;
15
+ variables: string;
16
+ include: string;
17
+ name?: string;
18
+ };
19
+ }[];
20
+ };
21
+ export declare function createOmitCssLoaderRule(_opts?: SemiWebpackPluginOptions): {
22
+ test: RegExp;
23
+ use: {
24
+ loader: string;
25
+ }[];
26
+ };
27
+ export declare function createPrefixLoaderRule(opts?: SemiWebpackPluginOptions): {
28
+ test: RegExp;
29
+ use: {
30
+ loader: string;
31
+ options: {
32
+ replacers: {
33
+ BASE_CLASS_PREFIX: string;
34
+ };
35
+ };
36
+ }[];
37
+ };
38
+ export declare function applySemiRules(opts?: SemiWebpackPluginOptions): RuleSetRule[];
package/lib/rule.js ADDED
@@ -0,0 +1,58 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.applySemiRules = exports.createPrefixLoaderRule = exports.createOmitCssLoaderRule = exports.createThemeLoaderRule = exports.createSourceSuffixLoaderRule = void 0;
4
+ const constants_1 = require("./constants");
5
+ const utils_1 = require("./utils");
6
+ function createSourceSuffixLoaderRule(_opts) {
7
+ return {
8
+ test: /@douyinfe\/semi-(ui|icons)\/lib\/.+\.js$/,
9
+ use: [{ loader: constants_1.SOURCE_SUFFIX_LOADER }],
10
+ };
11
+ }
12
+ exports.createSourceSuffixLoaderRule = createSourceSuffixLoaderRule;
13
+ function createThemeLoaderRule(opts) {
14
+ const themeOptions = {};
15
+ if (typeof opts.theme === 'object') {
16
+ Object.assign(themeOptions, opts.theme);
17
+ }
18
+ else {
19
+ themeOptions.name = opts.theme;
20
+ }
21
+ const options = Object.assign(Object.assign({}, themeOptions), { prefixCls: opts.prefixCls, variables: (0, utils_1.stringifyVariableRecord)(opts.variables), include: opts.include });
22
+ return {
23
+ test: /@douyinfe\/semi-(ui|icons|foundation)\/lib\/.+\.scss$/,
24
+ use: [{ loader: constants_1.THEME_LOADER, options }],
25
+ };
26
+ }
27
+ exports.createThemeLoaderRule = createThemeLoaderRule;
28
+ function createOmitCssLoaderRule(_opts) {
29
+ return {
30
+ test: /@douyinfe\/semi-[^/]+\/.+env\.js$/,
31
+ use: [{ loader: constants_1.OMIT_CSS_LOADER }],
32
+ };
33
+ }
34
+ exports.createOmitCssLoaderRule = createOmitCssLoaderRule;
35
+ function createPrefixLoaderRule(opts) {
36
+ const options = {
37
+ replacers: { BASE_CLASS_PREFIX: opts.prefixCls },
38
+ };
39
+ return {
40
+ test: /@douyinfe\/semi-[^/]+\/.+env\.js$/,
41
+ use: [{ loader: constants_1.PREFIX_LOADER, options }],
42
+ };
43
+ }
44
+ exports.createPrefixLoaderRule = createPrefixLoaderRule;
45
+ function applySemiRules(opts) {
46
+ const rules = [];
47
+ if (opts.omitCss) {
48
+ rules.push(createOmitCssLoaderRule(opts));
49
+ return rules;
50
+ }
51
+ rules.push(createSourceSuffixLoaderRule(opts));
52
+ rules.push(createThemeLoaderRule(opts));
53
+ if (opts.prefixCls) {
54
+ rules.push(createPrefixLoaderRule(opts));
55
+ }
56
+ return rules;
57
+ }
58
+ exports.applySemiRules = applySemiRules;
package/lib/types.d.ts ADDED
@@ -0,0 +1,10 @@
1
+ export interface SemiWebpackPluginOptions {
2
+ theme?: SemiThemeOptions | SemiThemeOptions['name'];
3
+ prefixCls?: string;
4
+ variables?: Record<string, string | number>;
5
+ include?: string;
6
+ omitCss?: boolean;
7
+ }
8
+ export interface SemiThemeOptions {
9
+ name?: string;
10
+ }
package/lib/types.js ADDED
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
package/lib/utils.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ import { SemiWebpackPluginOptions } from './types';
2
+ export declare function stringifyVariableRecord(map?: SemiWebpackPluginOptions['variables']): string;
package/lib/utils.js ADDED
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.stringifyVariableRecord = void 0;
4
+ function stringifyVariableRecord(map = {}) {
5
+ let ret = '';
6
+ for (const [k, v] of Object.entries(map)) {
7
+ ret += `${k}: ${v};\n`;
8
+ }
9
+ return ret;
10
+ }
11
+ exports.stringifyVariableRecord = stringifyVariableRecord;
package/package.json ADDED
@@ -0,0 +1,34 @@
1
+ {
2
+ "name": "@douyinfe/semi-rspack-plugin",
3
+ "version": "0.0.1-alpha.0",
4
+ "description": "",
5
+ "author": "伍浩威 <wuhaowei.whw@bytedance.com>",
6
+ "homepage": "",
7
+ "license": "MIT",
8
+ "main": "lib/index.js",
9
+ "directories": {
10
+ "lib": "lib"
11
+ },
12
+ "files": [
13
+ "lib",
14
+ "README.md"
15
+ ],
16
+ "scripts": {
17
+ "build:lib": "rimraf lib && tsc",
18
+ "dev": "tsc -w --sourceMap"
19
+ },
20
+ "dependencies": {
21
+ "@babel/core": "^7.15.4",
22
+ "enhanced-resolve": "^5.8.3"
23
+ },
24
+ "peerDependencies": {
25
+ "webpack": "^5"
26
+ },
27
+ "devDependencies": {
28
+ "@types/loader-utils": "^2.0.3",
29
+ "rimraf": "^3.0.2",
30
+ "typescript": "^4",
31
+ "webpack": "^5.77.0"
32
+ },
33
+ "gitHead": "3ac52f8f6f711d525534036ec9be58e81a8eeebd"
34
+ }