@oceanbase/codemod 1.0.0-alpha.1 → 1.0.0-alpha.11
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 +158 -0
- package/bin/cli.js +196 -30
- package/package.json +7 -6
- package/transforms/__testfixtures__/less-to-cssvar/basic.input.less +16 -0
- package/transforms/__testfixtures__/less-to-cssvar/basic.output.less +14 -0
- package/transforms/__testfixtures__/less-to-cssvar/color-scales.input.less +23 -0
- package/transforms/__testfixtures__/less-to-cssvar/color-scales.output.less +21 -0
- package/transforms/__testfixtures__/less-to-cssvar/complex-values.input.less +22 -0
- package/transforms/__testfixtures__/less-to-cssvar/complex-values.output.less +20 -0
- package/transforms/__testfixtures__/less-to-cssvar/control-tokens.input.less +29 -0
- package/transforms/__testfixtures__/less-to-cssvar/control-tokens.output.less +27 -0
- package/transforms/__testfixtures__/less-to-cssvar/css-modules-global.input.less +21 -0
- package/transforms/__testfixtures__/less-to-cssvar/css-modules-global.output.less +19 -0
- package/transforms/__testfixtures__/less-to-cssvar/custom-prefix.input.less +9 -0
- package/transforms/__testfixtures__/less-to-cssvar/custom-prefix.output.less +7 -0
- package/transforms/__testfixtures__/less-to-cssvar/fill-tokens.input.less +36 -0
- package/transforms/__testfixtures__/less-to-cssvar/fill-tokens.output.less +34 -0
- package/transforms/__testfixtures__/less-to-cssvar/mixed-values.input.less +21 -0
- package/transforms/__testfixtures__/less-to-cssvar/mixed-values.output.less +19 -0
- package/transforms/__testfixtures__/less-to-cssvar/multiple-imports.input.less +9 -0
- package/transforms/__testfixtures__/less-to-cssvar/multiple-imports.output.less +8 -0
- package/transforms/__testfixtures__/less-to-cssvar/nested-selectors.input.less +24 -0
- package/transforms/__testfixtures__/less-to-cssvar/nested-selectors.output.less +22 -0
- package/transforms/__testfixtures__/less-to-cssvar/no-transform.input.less +8 -0
- package/transforms/__testfixtures__/less-to-cssvar/no-transform.output.less +8 -0
- package/transforms/__testfixtures__/less-to-cssvar/obui-import.input.less +7 -0
- package/transforms/__testfixtures__/less-to-cssvar/obui-import.output.less +5 -0
- package/transforms/__testfixtures__/less-to-cssvar/status-colors.input.less +25 -0
- package/transforms/__testfixtures__/less-to-cssvar/status-colors.output.less +23 -0
- package/transforms/__testfixtures__/less-to-token/antd-v4-less-to-token.input.less +2 -0
- package/transforms/__testfixtures__/less-to-token/antd-v4-less-to-token.output.less +2 -0
- package/transforms/__testfixtures__/less-to-token/case-insensitive.input.less +4 -0
- package/transforms/__testfixtures__/less-to-token/case-insensitive.output.less +4 -0
- package/transforms/__testfixtures__/less-to-token/exist-import-url.input.less +10 -0
- package/transforms/__testfixtures__/less-to-token/exist-import-url.output.less +10 -0
- package/transforms/__testfixtures__/less-to-token/exist-import.input.less +10 -0
- package/transforms/__testfixtures__/less-to-token/exist-import.output.less +10 -0
- package/transforms/__testfixtures__/sass-to-cssvar/basic.input.scss +18 -0
- package/transforms/__testfixtures__/sass-to-cssvar/basic.output.scss +18 -0
- package/transforms/__testfixtures__/sass-to-cssvar/custom-prefix.input.scss +5 -0
- package/transforms/__testfixtures__/sass-to-cssvar/custom-prefix.output.scss +5 -0
- package/transforms/__testfixtures__/sass-to-cssvar/no-transform.input.scss +6 -0
- package/transforms/__testfixtures__/sass-to-cssvar/no-transform.output.scss +6 -0
- package/transforms/__testfixtures__/style-to-token/antd-style.input.js +1 -0
- package/transforms/__testfixtures__/style-to-token/antd-style.output.js +1 -0
- package/transforms/__testfixtures__/style-to-token/function-component.input.js +2 -2
- package/transforms/__testfixtures__/style-to-token/function-component.output.js +2 -2
- package/transforms/__testfixtures__/style-to-token/nested-object.input.js +12 -0
- package/transforms/__testfixtures__/style-to-token/nested-object.output.js +13 -0
- package/transforms/__testfixtures__/style-to-token/single-function.output.js +1 -2
- package/transforms/__testfixtures__/style-to-token/template-string.input.js +23 -0
- package/transforms/__testfixtures__/style-to-token/template-string.output.js +25 -0
- package/transforms/__tests__/less-to-cssvar.test.ts +180 -0
- package/transforms/__tests__/less-to-token.test.ts +2 -0
- package/transforms/__tests__/sass-to-cssvar.test.ts +67 -0
- package/transforms/__tests__/style-to-token.test.ts +2 -0
- package/transforms/less-to-cssvar.js +505 -0
- package/transforms/less-to-token.js +47 -7
- package/transforms/obui-to-oceanbase-design-and-ui.js +0 -4
- package/transforms/sass-to-cssvar.js +194 -0
- package/transforms/style-to-token.js +183 -12
- package/transforms/utils/path-utils.js +40 -0
- package/transforms/utils/token.js +24 -2
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
const path = require('path');
|
|
2
|
+
const fs = require('fs');
|
|
3
|
+
const postcss = require('postcss');
|
|
4
|
+
const postcssScss = require('postcss-scss');
|
|
5
|
+
const isDirectory = require('is-directory');
|
|
6
|
+
const { shouldExcludePath } = require('./utils/path-utils');
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Get SASS/SCSS tokens from @oceanbase/design theme Less file
|
|
10
|
+
* Since we're converting SASS variables to CSS variables based on the same design tokens,
|
|
11
|
+
* we can reuse the Less token list
|
|
12
|
+
*/
|
|
13
|
+
function getSassTokensFromTheme() {
|
|
14
|
+
try {
|
|
15
|
+
// Try to find the Less theme file from @oceanbase/design
|
|
16
|
+
const themeLessPath = require.resolve('@oceanbase/design/es/theme/style/default.less');
|
|
17
|
+
const content = fs.readFileSync(themeLessPath, 'utf-8');
|
|
18
|
+
|
|
19
|
+
// Extract all @variableName: definitions
|
|
20
|
+
const tokenRegex = /@([a-zA-Z][a-zA-Z0-9]*):/g;
|
|
21
|
+
const tokens = new Set();
|
|
22
|
+
let match;
|
|
23
|
+
while ((match = tokenRegex.exec(content)) !== null) {
|
|
24
|
+
tokens.add(match[1]);
|
|
25
|
+
}
|
|
26
|
+
return Array.from(tokens);
|
|
27
|
+
} catch (e) {
|
|
28
|
+
// Fallback: try lib path
|
|
29
|
+
try {
|
|
30
|
+
const themeLessPath = require.resolve('@oceanbase/design/lib/theme/style/default.less');
|
|
31
|
+
const content = fs.readFileSync(themeLessPath, 'utf-8');
|
|
32
|
+
|
|
33
|
+
const tokenRegex = /@([a-zA-Z][a-zA-Z0-9]*):/g;
|
|
34
|
+
const tokens = new Set();
|
|
35
|
+
let match;
|
|
36
|
+
while ((match = tokenRegex.exec(content)) !== null) {
|
|
37
|
+
tokens.add(match[1]);
|
|
38
|
+
}
|
|
39
|
+
return Array.from(tokens);
|
|
40
|
+
} catch (e2) {
|
|
41
|
+
// Final fallback: use theme token object
|
|
42
|
+
try {
|
|
43
|
+
const { default: defaultTheme } = require('@oceanbase/design/lib/theme/default');
|
|
44
|
+
return Object.keys(defaultTheme.token || {});
|
|
45
|
+
} catch (e3) {
|
|
46
|
+
console.warn('Warning: Could not load tokens from @oceanbase/design, using empty list');
|
|
47
|
+
return [];
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Get SASS variable tokens dynamically from @oceanbase/design theme
|
|
54
|
+
const SASS_TOKENS = getSassTokensFromTheme();
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Convert camelCase to kebab-case
|
|
58
|
+
* @param {string} str - camelCase string
|
|
59
|
+
* @returns {string} - kebab-case string
|
|
60
|
+
*/
|
|
61
|
+
function camelToKebab(str) {
|
|
62
|
+
return str.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase();
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Find all sass/scss files in the directory
|
|
67
|
+
* @param {string} dir - Directory or file path
|
|
68
|
+
* @returns {string[]} - Array of file paths
|
|
69
|
+
*/
|
|
70
|
+
const findAllSassFiles = dir => {
|
|
71
|
+
const sassFiles = [];
|
|
72
|
+
const isDir = isDirectory.sync(dir);
|
|
73
|
+
if (isDir) {
|
|
74
|
+
const files = fs.readdirSync(dir);
|
|
75
|
+
files.forEach(file => {
|
|
76
|
+
const filePath = path.join(dir, file);
|
|
77
|
+
if (isDirectory.sync(filePath)) {
|
|
78
|
+
if (shouldExcludePath(filePath)) {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
sassFiles.push(...findAllSassFiles(filePath));
|
|
82
|
+
} else if (file.endsWith('.sass') || file.endsWith('.scss')) {
|
|
83
|
+
sassFiles.push(filePath);
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
} else if (dir.endsWith('.sass') || dir.endsWith('.scss')) {
|
|
87
|
+
sassFiles.push(dir);
|
|
88
|
+
}
|
|
89
|
+
return sassFiles;
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Transform SASS variable to CSS variable
|
|
94
|
+
* @param {string} value - CSS value that may contain SASS variables
|
|
95
|
+
* @param {string} prefix - CSS variable prefix (default: 'ant')
|
|
96
|
+
* @returns {string} - Transformed value with CSS variables
|
|
97
|
+
*/
|
|
98
|
+
function transformSassVarToCssVar(value, prefix = 'ant') {
|
|
99
|
+
if (!value || typeof value !== 'string') {
|
|
100
|
+
return value;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
let result = value;
|
|
104
|
+
|
|
105
|
+
// Match SASS variables like $colorPrimary, $fontSize, etc.
|
|
106
|
+
// Support both $tokenName and #{tokenName} syntax
|
|
107
|
+
const sassVarRegex = /\$([a-zA-Z][a-zA-Z0-9]*)/g;
|
|
108
|
+
|
|
109
|
+
result = result.replace(sassVarRegex, (match, varName) => {
|
|
110
|
+
// Check if this is a known token
|
|
111
|
+
if (SASS_TOKENS.includes(varName)) {
|
|
112
|
+
const kebabName = camelToKebab(varName);
|
|
113
|
+
return `var(--${prefix}-${kebabName})`;
|
|
114
|
+
}
|
|
115
|
+
// Return original if not a known token
|
|
116
|
+
return match;
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
return result;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Transform a single SASS/SCSS file to use CSS variables
|
|
124
|
+
* @param {string} file - File path
|
|
125
|
+
* @param {object} options - Transform options
|
|
126
|
+
* @param {string} options.prefix - CSS variable prefix (default: 'ant')
|
|
127
|
+
* @returns {Promise<{content: string, hasTransformations: boolean}>} - Transformed content
|
|
128
|
+
*/
|
|
129
|
+
async function transform(file, options = {}) {
|
|
130
|
+
const { prefix = 'ant' } = options;
|
|
131
|
+
const content = fs.readFileSync(file, 'utf-8');
|
|
132
|
+
const { root: ast } = await postcss([]).process(content, {
|
|
133
|
+
syntax: postcssScss,
|
|
134
|
+
from: file,
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
// Track whether any transformations were made
|
|
138
|
+
let hasTransformations = false;
|
|
139
|
+
|
|
140
|
+
// Traverse AST
|
|
141
|
+
ast.walk(node => {
|
|
142
|
+
if (node.type === 'decl') {
|
|
143
|
+
// Transform property values that contain SASS variables
|
|
144
|
+
const newValue = transformSassVarToCssVar(node.value, prefix);
|
|
145
|
+
if (newValue !== node.value) {
|
|
146
|
+
node.value = newValue;
|
|
147
|
+
hasTransformations = true;
|
|
148
|
+
}
|
|
149
|
+
} else if (node.type === 'atrule') {
|
|
150
|
+
// Transform SASS variables in at-rule params (e.g., media queries)
|
|
151
|
+
if (node.params) {
|
|
152
|
+
const newParams = transformSassVarToCssVar(node.params, prefix);
|
|
153
|
+
if (newParams !== node.params) {
|
|
154
|
+
node.params = newParams;
|
|
155
|
+
hasTransformations = true;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
return {
|
|
162
|
+
content: ast.toString(postcssScss.stringify),
|
|
163
|
+
hasTransformations,
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Run the sass-to-cssvar transformation
|
|
169
|
+
* @param {string} file - File or directory path
|
|
170
|
+
* @param {object} options - Transform options
|
|
171
|
+
* @param {string} options.prefix - CSS variable prefix (default: 'ant')
|
|
172
|
+
*/
|
|
173
|
+
async function sassToCssvar(file, options = {}) {
|
|
174
|
+
const { prefix = 'ant' } = options;
|
|
175
|
+
const allSassFiles = findAllSassFiles(file);
|
|
176
|
+
|
|
177
|
+
for (const item of allSassFiles) {
|
|
178
|
+
const { content, hasTransformations } = await transform(item, { prefix });
|
|
179
|
+
|
|
180
|
+
if (hasTransformations) {
|
|
181
|
+
fs.writeFileSync(item, content);
|
|
182
|
+
console.log(` Transformed: ${item}`);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
module.exports = {
|
|
188
|
+
transform,
|
|
189
|
+
sassToCssvar,
|
|
190
|
+
transformSassVarToCssVar,
|
|
191
|
+
getSassTokensFromTheme,
|
|
192
|
+
camelToKebab,
|
|
193
|
+
SASS_TOKENS,
|
|
194
|
+
};
|
|
@@ -65,8 +65,8 @@ function isReactComponentOrHook(functionName, path) {
|
|
|
65
65
|
// 检查 BlockStatement 中是否包含 token 使用
|
|
66
66
|
function hasTokenUsage(j, path) {
|
|
67
67
|
return (
|
|
68
|
-
j(path).find(j.
|
|
69
|
-
|
|
68
|
+
j(path).find(j.MemberExpression, {
|
|
69
|
+
object: { name: 'token' },
|
|
70
70
|
}).length > 0
|
|
71
71
|
);
|
|
72
72
|
}
|
|
@@ -156,7 +156,6 @@ function addTokenImportToBlockStatement(j, root, path) {
|
|
|
156
156
|
moduleName: '@oceanbase/design',
|
|
157
157
|
importedName: 'token',
|
|
158
158
|
importKind: 'value',
|
|
159
|
-
after: 'react',
|
|
160
159
|
});
|
|
161
160
|
}
|
|
162
161
|
}
|
|
@@ -261,12 +260,130 @@ function processStringLiterals(j, root) {
|
|
|
261
260
|
return hasChanged;
|
|
262
261
|
}
|
|
263
262
|
|
|
263
|
+
// 处理模板字符串中的颜色值
|
|
264
|
+
function processTemplateLiterals(j, root) {
|
|
265
|
+
let hasChanged = false;
|
|
266
|
+
|
|
267
|
+
const templateList = root.find(j.TemplateLiteral);
|
|
268
|
+
|
|
269
|
+
if (templateList.length > 0) {
|
|
270
|
+
templateList.forEach(path => {
|
|
271
|
+
const templateLiteral = path.value;
|
|
272
|
+
const quasis = templateLiteral.quasis;
|
|
273
|
+
const expressions = templateLiteral.expressions || [];
|
|
274
|
+
|
|
275
|
+
// 检查每个模板字符串片段是否包含需要转换的颜色值
|
|
276
|
+
let needsReconstruction = false;
|
|
277
|
+
const newQuasis = [];
|
|
278
|
+
const newExpressions = [];
|
|
279
|
+
|
|
280
|
+
for (let i = 0; i < quasis.length; i++) {
|
|
281
|
+
const quasi = quasis[i];
|
|
282
|
+
let value = quasi.value.raw;
|
|
283
|
+
|
|
284
|
+
// 查找需要转换的颜色值
|
|
285
|
+
const colorMatch = value.match(
|
|
286
|
+
/rgba?\([^)]+\)|#[0-9a-fA-F]{3,8}|rgb\([^)]+\)|hsl\([^)]+\)|hsla?\([^)]+\)/g
|
|
287
|
+
);
|
|
288
|
+
if (colorMatch) {
|
|
289
|
+
hasChanged = true;
|
|
290
|
+
needsReconstruction = true;
|
|
291
|
+
|
|
292
|
+
// 收集所有需要替换的匹配项及其 token
|
|
293
|
+
const replacements = [];
|
|
294
|
+
colorMatch.forEach(match => {
|
|
295
|
+
const { token } = tokenParse(match);
|
|
296
|
+
if (token) {
|
|
297
|
+
const index = value.indexOf(match);
|
|
298
|
+
replacements.push({ index, match, token });
|
|
299
|
+
}
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
// 按位置排序,从后往前处理以避免位置偏移
|
|
303
|
+
replacements.sort((a, b) => b.index - a.index);
|
|
304
|
+
|
|
305
|
+
let processedValue = value;
|
|
306
|
+
replacements.forEach(({ index, match, token }) => {
|
|
307
|
+
const before = processedValue.substring(0, index);
|
|
308
|
+
const after = processedValue.substring(index + match.length);
|
|
309
|
+
|
|
310
|
+
// 如果前面有内容,创建一个 quasi
|
|
311
|
+
if (before) {
|
|
312
|
+
newQuasis.push(j.templateElement({ raw: before, cooked: before }, false));
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// 创建 token 表达式
|
|
316
|
+
newExpressions.push(j.memberExpression(j.identifier('token'), j.identifier(token)));
|
|
317
|
+
|
|
318
|
+
// 剩余部分继续处理
|
|
319
|
+
processedValue = after;
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
// 如果有剩余部分,添加到 quasis
|
|
323
|
+
if (processedValue || newQuasis.length === 0) {
|
|
324
|
+
const isTail = i === quasis.length - 1 && expressions.length === 0;
|
|
325
|
+
newQuasis.push(
|
|
326
|
+
j.templateElement({ raw: processedValue || '', cooked: processedValue || '' }, isTail)
|
|
327
|
+
);
|
|
328
|
+
} else if (newQuasis.length > 0) {
|
|
329
|
+
// 确保最后一个 quasi 标记为 tail(如果这是最后一个 quasi 且没有更多表达式)
|
|
330
|
+
const isTail = i === quasis.length - 1 && expressions.length === 0;
|
|
331
|
+
if (isTail) {
|
|
332
|
+
const lastQuasi = newQuasis[newQuasis.length - 1];
|
|
333
|
+
lastQuasi.tail = true;
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
} else {
|
|
337
|
+
// 如果没有颜色值需要转换,保持原样
|
|
338
|
+
newQuasis.push(quasi);
|
|
339
|
+
if (i < expressions.length) {
|
|
340
|
+
newExpressions.push(expressions[i]);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
// 如果需要重构,替换整个模板字符串
|
|
346
|
+
if (needsReconstruction) {
|
|
347
|
+
// 确保最后一个 quasi 标记为 tail
|
|
348
|
+
if (newQuasis.length > 0) {
|
|
349
|
+
const lastQuasi = newQuasis[newQuasis.length - 1];
|
|
350
|
+
lastQuasi.tail = true;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
// 合并原有的表达式(如果有)
|
|
354
|
+
const allExpressions = [...newExpressions];
|
|
355
|
+
// 添加原有的表达式(在 quasis 之后)
|
|
356
|
+
for (let i = 0; i < expressions.length; i++) {
|
|
357
|
+
allExpressions.push(expressions[i]);
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
path.replace(j.templateLiteral(newQuasis, allExpressions));
|
|
361
|
+
}
|
|
362
|
+
});
|
|
363
|
+
|
|
364
|
+
// 为包含 token 使用的顶级 BlockStatement 添加导入
|
|
365
|
+
root
|
|
366
|
+
.find(j.BlockStatement)
|
|
367
|
+
.filter(path => isTopBlockStatement(path))
|
|
368
|
+
.forEach(path => {
|
|
369
|
+
if (hasTokenUsage(j, path) && !hasUseTokenStatement(j, path)) {
|
|
370
|
+
addTokenImportToBlockStatement(j, root, path);
|
|
371
|
+
}
|
|
372
|
+
});
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
return hasChanged;
|
|
376
|
+
}
|
|
377
|
+
|
|
264
378
|
function importComponent(j, root, options) {
|
|
265
379
|
let hasChanged = false;
|
|
266
380
|
|
|
267
381
|
// 处理字符串字面量
|
|
268
382
|
hasChanged = processStringLiterals(j, root) || hasChanged;
|
|
269
383
|
|
|
384
|
+
// 处理模板字符串中的颜色值
|
|
385
|
+
hasChanged = processTemplateLiterals(j, root) || hasChanged;
|
|
386
|
+
|
|
270
387
|
// 处理对象属性值(如 fontSize: 14)
|
|
271
388
|
const objectPropertyChanged = processObjectProperties(j, root);
|
|
272
389
|
hasChanged = objectPropertyChanged || hasChanged;
|
|
@@ -293,8 +410,11 @@ function processObjectProperties(j, root) {
|
|
|
293
410
|
if (tokenResult) {
|
|
294
411
|
hasChanged = true;
|
|
295
412
|
const isJSXAttribute = path.parentPath.value.type === 'JSXAttribute';
|
|
296
|
-
const
|
|
297
|
-
|
|
413
|
+
const memberExpression = j.memberExpression(
|
|
414
|
+
j.identifier('token'),
|
|
415
|
+
j.identifier(tokenResult.token)
|
|
416
|
+
);
|
|
417
|
+
return j.objectProperty(j.identifier(propertyName), memberExpression);
|
|
298
418
|
}
|
|
299
419
|
return path.value;
|
|
300
420
|
});
|
|
@@ -315,23 +435,22 @@ function processObjectProperties(j, root) {
|
|
|
315
435
|
if (tokenResult) {
|
|
316
436
|
hasChanged = true;
|
|
317
437
|
const isJSXAttribute = path.parentPath.value.type === 'JSXAttribute';
|
|
318
|
-
const
|
|
319
|
-
|
|
438
|
+
const memberExpression = j.memberExpression(
|
|
439
|
+
j.identifier('token'),
|
|
440
|
+
j.identifier(tokenResult.token)
|
|
441
|
+
);
|
|
442
|
+
return j.objectProperty(j.identifier(propertyName), memberExpression);
|
|
320
443
|
}
|
|
321
444
|
return path.value;
|
|
322
445
|
});
|
|
323
446
|
}
|
|
324
447
|
|
|
325
|
-
// 如果发生了替换,需要添加 token 导入
|
|
326
|
-
if (hasChanged) {
|
|
327
|
-
addTokenImportsForObjectProperties(j, root);
|
|
328
|
-
}
|
|
329
|
-
|
|
330
448
|
return hasChanged;
|
|
331
449
|
}
|
|
332
450
|
|
|
333
451
|
// 为对象属性添加 token 导入
|
|
334
452
|
function addTokenImportsForObjectProperties(j, root) {
|
|
453
|
+
// 处理 BlockStatement 中的 token 使用
|
|
335
454
|
root
|
|
336
455
|
.find(j.BlockStatement)
|
|
337
456
|
.filter(path => isTopBlockStatement(path))
|
|
@@ -344,6 +463,31 @@ function addTokenImportsForObjectProperties(j, root) {
|
|
|
344
463
|
}
|
|
345
464
|
}
|
|
346
465
|
});
|
|
466
|
+
|
|
467
|
+
// 处理顶层导出语句中的 token 使用
|
|
468
|
+
const hasTokenUsageInRoot =
|
|
469
|
+
root.find(j.MemberExpression, {
|
|
470
|
+
object: { name: 'token' },
|
|
471
|
+
}).length > 0;
|
|
472
|
+
|
|
473
|
+
if (hasTokenUsageInRoot) {
|
|
474
|
+
// 检查是否应该添加顶层 token 导入
|
|
475
|
+
if (shouldAddTopLevelTokenImport(j, root)) {
|
|
476
|
+
// 检查是否有 @oceanbase/design 的导入
|
|
477
|
+
const hasOceanbaseImport =
|
|
478
|
+
root.find(j.ImportDeclaration, {
|
|
479
|
+
source: { value: '@oceanbase/design' },
|
|
480
|
+
}).length > 0;
|
|
481
|
+
|
|
482
|
+
if (hasOceanbaseImport) {
|
|
483
|
+
// 如果有 @oceanbase/design 导入,添加到现有导入
|
|
484
|
+
addTokenToExistingImport(j, root);
|
|
485
|
+
} else {
|
|
486
|
+
// 如果没有 @oceanbase/design 导入,添加顶层 token 导入
|
|
487
|
+
addTopLevelTokenImport(j, root);
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
}
|
|
347
491
|
}
|
|
348
492
|
|
|
349
493
|
// 检查是否应该添加顶层 token 导入
|
|
@@ -425,6 +569,33 @@ module.exports = (file, api, options) => {
|
|
|
425
569
|
processCreateStylesParams(j, root);
|
|
426
570
|
}
|
|
427
571
|
|
|
572
|
+
// 为对象属性添加 token 导入(只在没有其他 token 导入逻辑时调用)
|
|
573
|
+
if (hasChanged) {
|
|
574
|
+
// 检查是否已经有其他 token 导入逻辑在处理
|
|
575
|
+
const hasOtherTokenLogic =
|
|
576
|
+
root.find(j.CallExpression, {
|
|
577
|
+
callee: { name: 'createStyles' },
|
|
578
|
+
}).length > 0 ||
|
|
579
|
+
root.find(j.CallExpression, {
|
|
580
|
+
callee: {
|
|
581
|
+
type: 'MemberExpression',
|
|
582
|
+
object: { name: 'theme' },
|
|
583
|
+
property: { name: 'useToken' },
|
|
584
|
+
},
|
|
585
|
+
}).length > 0 ||
|
|
586
|
+
root
|
|
587
|
+
.find(j.ImportDeclaration, {
|
|
588
|
+
source: { value: '@oceanbase/design' },
|
|
589
|
+
})
|
|
590
|
+
.find(j.ImportSpecifier, {
|
|
591
|
+
imported: { name: 'theme' },
|
|
592
|
+
}).length > 0;
|
|
593
|
+
|
|
594
|
+
if (!hasOtherTokenLogic) {
|
|
595
|
+
addTokenImportsForObjectProperties(j, root);
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
|
|
428
599
|
// 如果有变化,检查是否需要添加 token 导入
|
|
429
600
|
if (hasChanged) {
|
|
430
601
|
// 检查是否有 token 使用
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Common directories to exclude when traversing files
|
|
3
|
+
* These are typically build outputs, caches, dependencies, and temporary directories
|
|
4
|
+
*/
|
|
5
|
+
const EXCLUDED_DIRS = [
|
|
6
|
+
'node_modules',
|
|
7
|
+
'.umi',
|
|
8
|
+
'.umi-production',
|
|
9
|
+
'.git',
|
|
10
|
+
'dist',
|
|
11
|
+
'build',
|
|
12
|
+
'coverage',
|
|
13
|
+
'.cache',
|
|
14
|
+
'.next',
|
|
15
|
+
'.turbo',
|
|
16
|
+
'.vite',
|
|
17
|
+
'.nuxt',
|
|
18
|
+
'.output',
|
|
19
|
+
'.temp',
|
|
20
|
+
'.tmp',
|
|
21
|
+
'temp',
|
|
22
|
+
'tmp',
|
|
23
|
+
];
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Check if a path should be excluded
|
|
27
|
+
* @param {string} filePath - File path to check
|
|
28
|
+
* @returns {boolean} - True if should be excluded
|
|
29
|
+
*/
|
|
30
|
+
function shouldExcludePath(filePath) {
|
|
31
|
+
const normalizedPath = filePath.replace(/\\/g, '/');
|
|
32
|
+
return EXCLUDED_DIRS.some(
|
|
33
|
+
dir => normalizedPath.includes(`/${dir}/`) || normalizedPath.endsWith(`/${dir}`)
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
module.exports = {
|
|
38
|
+
EXCLUDED_DIRS,
|
|
39
|
+
shouldExcludePath,
|
|
40
|
+
};
|
|
@@ -36,17 +36,24 @@ const TOKEN_MAP = {
|
|
|
36
36
|
'#1890ff': 'colorInfo',
|
|
37
37
|
'#40a9ff': 'colorInfo',
|
|
38
38
|
'#006aff': 'colorInfo',
|
|
39
|
+
'#1843ff': 'colorInfo',
|
|
40
|
+
'#597ef7': 'colorInfo',
|
|
39
41
|
'#f7f9fb': 'colorInfoBg',
|
|
42
|
+
'#91a9f8': 'colorInfoBg',
|
|
40
43
|
'#e6f7ff': 'colorInfoBgHover',
|
|
41
44
|
'#f3f9ff': 'colorInfoBgHover',
|
|
42
45
|
'#e6f7ff': 'colorInfoBgHover',
|
|
43
46
|
'#73d13d': 'colorSuccess',
|
|
44
47
|
'#52c41a': 'colorSuccess',
|
|
45
48
|
'#faad14': 'colorWarning',
|
|
49
|
+
'#ffa940': 'colorWarning',
|
|
46
50
|
'#fef6e7': 'colorWarningBg',
|
|
51
|
+
'#fed59c': 'colorWarningBg',
|
|
47
52
|
'#ff4d4f': 'colorError',
|
|
48
53
|
'#f5222d': 'colorError',
|
|
49
54
|
'#f8636b': 'colorError',
|
|
55
|
+
'#f93939': 'colorError',
|
|
56
|
+
'#eb4444': 'colorError',
|
|
50
57
|
'#d9d9d9': 'colorBorder',
|
|
51
58
|
'#bfbfbf': 'colorBorder',
|
|
52
59
|
'#e8e8e8': 'colorBorder',
|
|
@@ -67,7 +74,9 @@ const TOKEN_MAP = {
|
|
|
67
74
|
'rgba(0,0,0,0.85)': 'colorText',
|
|
68
75
|
'rgba(0,0,0,0.65)': 'colorTextSecondary',
|
|
69
76
|
'rgba(0,0,0,0.45)': 'colorTextTertiary',
|
|
70
|
-
'#5c6b8a': '
|
|
77
|
+
'#5c6b8a': 'colorTextSecondary',
|
|
78
|
+
'#5C6B8A': 'colorTextSecondary',
|
|
79
|
+
'#ced5e3': '@colorTextPlaceholder',
|
|
71
80
|
'rgba(0,0,0,0.25)': 'colorTextQuaternary',
|
|
72
81
|
'rgba(0,0,0,.85)': 'colorText',
|
|
73
82
|
'rgba(0,0,0,.65)': 'colorTextSecondary',
|
|
@@ -97,6 +106,7 @@ const TOKEN_MAP = {
|
|
|
97
106
|
'rgba(0,0,0,0.06)': 'colorFillSecondary',
|
|
98
107
|
'rgba(0,0,0,0.04)': 'colorFillTertiary',
|
|
99
108
|
'rgba(0,0,0,0.02)': 'colorFillQuaternary',
|
|
109
|
+
'rgba(0,0,0,0.03)': 'colorFillQuaternary',
|
|
100
110
|
'#f5f6fa': 'colorBgLayout',
|
|
101
111
|
'#edeff2': 'colorBgLayout',
|
|
102
112
|
// obui legacy style => token
|
|
@@ -122,12 +132,12 @@ const TOKEN_MAP = {
|
|
|
122
132
|
'#cdd5e4': 'colorBorder',
|
|
123
133
|
'#f5f8fe': 'colorBgLayout',
|
|
124
134
|
'#f5f7fa': 'colorBgLayout',
|
|
135
|
+
'#f8fafe': 'colorBgLayout',
|
|
125
136
|
'rgba(140,140,140,0.1)': 'colorBgLayout',
|
|
126
137
|
'rgb(240,242,245)': 'colorBgLayout',
|
|
127
138
|
'#132039': 'colorText',
|
|
128
139
|
'#364563': 'colorTextSecondary',
|
|
129
140
|
'#8592ad': 'colorTextTertiary',
|
|
130
|
-
'#f8fafe': 'colorFillQuaternary',
|
|
131
141
|
};
|
|
132
142
|
|
|
133
143
|
const TOKEN_MAP_KEYS = Object.keys(TOKEN_MAP).map(key => formatValue(key));
|
|
@@ -170,6 +180,18 @@ const PROPERTY_TOKEN_MAP = {
|
|
|
170
180
|
15: 'fontSizeLG',
|
|
171
181
|
16: 'fontSizeLG',
|
|
172
182
|
},
|
|
183
|
+
fontWeight: {
|
|
184
|
+
300: 'fontWeightWeak',
|
|
185
|
+
400: 'fontWeight',
|
|
186
|
+
500: 'fontWeightStrong',
|
|
187
|
+
600: 'fontWeightStrong',
|
|
188
|
+
},
|
|
189
|
+
borderRadius: {
|
|
190
|
+
2: 'borderRadiusSM',
|
|
191
|
+
4: 'borderRadius',
|
|
192
|
+
6: 'borderRadiusMD',
|
|
193
|
+
8: 'borderRadiusLG',
|
|
194
|
+
},
|
|
173
195
|
};
|
|
174
196
|
|
|
175
197
|
function propertyTokenParse(propertyName, value) {
|