@oceanbase/codemod 1.0.0-alpha.1 → 1.0.0-alpha.3

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oceanbase/codemod",
3
- "version": "1.0.0-alpha.1",
3
+ "version": "1.0.0-alpha.3",
4
4
  "description": "Codemod for OceanBase Design upgrade",
5
5
  "keywords": [
6
6
  "oceanbase",
@@ -23,7 +23,7 @@
23
23
  "build": "father build"
24
24
  },
25
25
  "dependencies": {
26
- "@oceanbase/design": "^1.0.0-alpha.1",
26
+ "@oceanbase/design": "^1.0.0-alpha.3",
27
27
  "chalk": "^3.0.0",
28
28
  "command-exists": "^1.2.9",
29
29
  "execa": "^5.1.1",
@@ -37,7 +37,7 @@
37
37
  "postcss-less": "^6.0.0",
38
38
  "prettier": "^3.6.2",
39
39
  "read-pkg-up": "^10.1.0",
40
- "semver": "^7.7.2",
40
+ "semver": "^7.7.3",
41
41
  "update-check": "^1.5.4",
42
42
  "yargs-parser": "^21.1.1"
43
43
  },
@@ -47,5 +47,5 @@
47
47
  "enzyme": "^3.11.0",
48
48
  "enzyme-to-json": "^3.6.2"
49
49
  },
50
- "gitHead": "47b1ebfa75581585b83252085dddd33fc2c072a5"
50
+ "gitHead": "62c3cf2b25248d70e4f1826e69c019a38e03753e"
51
51
  }
@@ -5,6 +5,8 @@
5
5
  border-color: #ff4d4f;
6
6
  scrollbar-color: #ffffff;
7
7
  font-size: 14px;
8
+ box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.03),
9
+ 0 1px 6px -1px @colorFillQuaternary, 0 2px 4px 0 rgba(0, 0, 0, 0.02);
8
10
  .content {
9
11
  color: rgba(0, 0, 0, 0.85);
10
12
  background: rgba(0, 0, 0,0.65);
@@ -6,6 +6,8 @@
6
6
  border-color: @colorError;
7
7
  scrollbar-color: @colorBgContainer;
8
8
  font-size: @fontSize;
9
+ box-shadow: 0 1px 2px 0 @colorFillQuaternary,
10
+ 0 1px 6px -1px @colorFillQuaternary, 0 2px 4px 0 @colorFillQuaternary;
9
11
  .content {
10
12
  color: @colorText;
11
13
  background: @colorTextSecondary;
@@ -0,0 +1,10 @@
1
+ @import url('~@oceanbase/design/es/theme/index.less');
2
+
3
+ .container {
4
+ color: rgba(0, 0, 0, 0.85);
5
+ background: rgba(0, 0, 0,0.65);
6
+ background-color: rgba(0,0,0,0.45);
7
+ border-color: rgb(0 0 0 / 45%);
8
+ border: 1px solid #d9d9d9;
9
+ font-size: 14px;
10
+ }
@@ -0,0 +1,10 @@
1
+ @import url('~@oceanbase/design/es/theme/index.less');
2
+
3
+ .container {
4
+ color: @colorText;
5
+ background: @colorTextSecondary;
6
+ background-color: @colorTextTertiary;
7
+ border-color: @colorTextTertiary;
8
+ border: 1px solid @colorBorder;
9
+ font-size: @fontSize;
10
+ }
@@ -0,0 +1,10 @@
1
+ @import '~@oceanbase/design/es/theme/index.less';
2
+
3
+ .container {
4
+ color: rgba(0, 0, 0, 0.85);
5
+ background: rgba(0, 0, 0,0.65);
6
+ background-color: rgba(0,0,0,0.45);
7
+ border-color: rgb(0 0 0 / 45%);
8
+ border: 1px solid #d9d9d9;
9
+ font-size: 14px;
10
+ }
@@ -0,0 +1,10 @@
1
+ @import '~@oceanbase/design/es/theme/index.less';
2
+
3
+ .container {
4
+ color: @colorText;
5
+ background: @colorTextSecondary;
6
+ background-color: @colorTextTertiary;
7
+ border-color: @colorTextTertiary;
8
+ border: 1px solid @colorBorder;
9
+ font-size: @fontSize;
10
+ }
@@ -5,6 +5,7 @@ const useStyle1 = createStyles(() => {
5
5
  main: {
6
6
  background: '#1890ff',
7
7
  fontSize: 14,
8
+ boxShadow: `0 1px 2px 0 rgba(0, 0, 0, 0.03), 0 1px 6px -1px ${token.colorFillQuaternary}, 0 2px 4px 0 rgba(0, 0, 0, 0.02)`,
8
9
  },
9
10
  };
10
11
  });
@@ -9,6 +9,7 @@ const useStyle1 = createStyles((
9
9
  main: {
10
10
  background: token.colorInfo,
11
11
  fontSize: token.fontSize,
12
+ boxShadow: `0 1px 2px 0 ${token.colorFillQuaternary}, 0 1px 6px -1px ${token.colorFillQuaternary}, 0 2px 4px 0 ${token.colorFillQuaternary}`,
12
13
  },
13
14
  };
14
15
  });
@@ -0,0 +1,12 @@
1
+ export const DefaultLineConfig = {
2
+ annotations: [
3
+ {
4
+ text: {
5
+ style: {
6
+ fill: '#1890ff',
7
+ fontSize: 11,
8
+ },
9
+ },
10
+ },
11
+ ],
12
+ };
@@ -0,0 +1,13 @@
1
+ import { token } from '@oceanbase/design';
2
+ export const DefaultLineConfig = {
3
+ annotations: [
4
+ {
5
+ text: {
6
+ style: {
7
+ fill: token.colorInfo,
8
+ fontSize: token.fontSizeSM,
9
+ },
10
+ },
11
+ },
12
+ ],
13
+ };
@@ -9,6 +9,8 @@ const tests = [
9
9
  'case-insensitive',
10
10
  'mixin',
11
11
  'obui-less-token-to-token',
12
+ 'exist-import',
13
+ 'exist-import-url',
12
14
  ];
13
15
 
14
16
  describe(testUnit, () => {
@@ -12,6 +12,7 @@ const tests = [
12
12
  'nested-block-statement',
13
13
  'existed-useToken',
14
14
  'top-identifier',
15
+ 'nested-object',
15
16
  'case-insensitive',
16
17
  'antd-style',
17
18
  ];
@@ -6,7 +6,7 @@ const isDirectory = require('is-directory');
6
6
  const { tokenParse, propertyTokenParse } = require('./utils/token');
7
7
 
8
8
  /**
9
- * 搜索目录下所有的less文件
9
+ * Find all less files in the directory
10
10
  * @param dir
11
11
  * @returns
12
12
  */
@@ -36,6 +36,7 @@ async function transform(file) {
36
36
  const content = fs.readFileSync(file, 'utf-8');
37
37
  const { root: ast } = await postcss([]).process(content, {
38
38
  syntax: postcssLess,
39
+ from: file, // 添加 from 选项以避免警告
39
40
  });
40
41
  let hasToken = false;
41
42
  let tokenLessImported = false;
@@ -51,18 +52,56 @@ async function transform(file) {
51
52
  hasToken = true;
52
53
  } else {
53
54
  // 然后尝试基于值的 token 转换
54
- const { key, token, formattedValue } = tokenParse(node.value);
55
- if (token) {
56
- node.value = formattedValue.replace(key, `@${token}`);
55
+ let newValue = node.value;
56
+ let valueHasToken = false;
57
+
58
+ // 检查是否为复合值(包含多个值或颜色值)
59
+ const isCompositeValue =
60
+ node.value.includes(',') ||
61
+ /rgba?\([^)]+\)|#[0-9a-fA-F]{3,8}|rgb\([^)]+\)|hsl\([^)]+\)|hsla?\([^)]+\)/.test(
62
+ node.value
63
+ );
64
+
65
+ if (isCompositeValue) {
66
+ // 对于复合值,只替换其中的颜色值
67
+ const colorRegex =
68
+ /rgba?\([^)]+\)|#[0-9a-fA-F]{3,8}|rgb\([^)]+\)|hsl\([^)]+\)|hsla?\([^)]+\)/g;
69
+ const colorMatches = node.value.match(colorRegex);
70
+ if (colorMatches) {
71
+ colorMatches.forEach(match => {
72
+ const colorResult = tokenParse(match);
73
+ if (colorResult.token) {
74
+ newValue = newValue.replace(match, `@${colorResult.token}`);
75
+ valueHasToken = true;
76
+ }
77
+ });
78
+ }
79
+ } else {
80
+ // 对于简单值,尝试完整的 token 转换
81
+ const { key, token, formattedValue } = tokenParse(node.value);
82
+ if (token) {
83
+ newValue = formattedValue.replace(key, `@${token}`);
84
+ valueHasToken = true;
85
+ }
86
+ }
87
+
88
+ if (valueHasToken) {
89
+ node.value = newValue;
57
90
  hasToken = true;
58
91
  } else if (node.value?.includes('@')) {
59
92
  hasToken = true;
60
93
  }
61
94
  }
62
95
  } else if (node.type === 'atrule' && node.name === 'import') {
63
- if (node.params === "'~@oceanbase/design/es/theme/index.less'") {
96
+ if (
97
+ node.params?.includes("'~@oceanbase/design/es/theme/index.less'") ||
98
+ node.params?.includes('"~@oceanbase/design/es/theme/index.less"')
99
+ ) {
64
100
  tokenLessImported = true;
65
- } else if (node.params === "'~@alipay/ob-ui/es/theme/index.less'") {
101
+ } else if (
102
+ node.params?.includes("'~@alipay/ob-ui/es/theme/index.less'") ||
103
+ node.params?.includes('"~@alipay/ob-ui/es/theme/index.less"')
104
+ ) {
66
105
  node.remove();
67
106
  }
68
107
  }
@@ -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.Identifier, {
69
- name: name => name?.includes('token.'),
68
+ j(path).find(j.MemberExpression, {
69
+ object: { name: 'token' },
70
70
  }).length > 0
71
71
  );
72
72
  }
@@ -261,12 +261,71 @@ function processStringLiterals(j, root) {
261
261
  return hasChanged;
262
262
  }
263
263
 
264
+ // 处理模板字符串中的颜色值
265
+ function processTemplateLiterals(j, root) {
266
+ let hasChanged = false;
267
+
268
+ const templateList = root.find(j.TemplateLiteral);
269
+
270
+ if (templateList.length > 0) {
271
+ templateList.forEach(path => {
272
+ const templateLiteral = path.value;
273
+ const quasis = templateLiteral.quasis;
274
+
275
+ // 检查每个模板字符串片段是否包含需要转换的颜色值
276
+ for (let i = 0; i < quasis.length; i++) {
277
+ const quasi = quasis[i];
278
+ let value = quasi.value.raw;
279
+ let newValue = value;
280
+ let valueChanged = false;
281
+
282
+ // 查找需要转换的颜色值
283
+ const colorMatch = newValue.match(
284
+ /rgba?\([^)]+\)|#[0-9a-fA-F]{3,8}|rgb\([^)]+\)|hsl\([^)]+\)|hsla?\([^)]+\)/g
285
+ );
286
+ if (colorMatch) {
287
+ hasChanged = true;
288
+ valueChanged = true;
289
+
290
+ colorMatch.forEach(match => {
291
+ const { token } = tokenParse(match);
292
+ if (token) {
293
+ newValue = newValue.replace(match, `\${token.${token}}`);
294
+ }
295
+ });
296
+ }
297
+
298
+ // 如果值发生了变化,更新模板字符串片段
299
+ if (valueChanged) {
300
+ quasi.value.raw = newValue;
301
+ quasi.value.cooked = newValue;
302
+ }
303
+ }
304
+ });
305
+
306
+ // 为包含 token 使用的顶级 BlockStatement 添加导入
307
+ root
308
+ .find(j.BlockStatement)
309
+ .filter(path => isTopBlockStatement(path))
310
+ .forEach(path => {
311
+ if (hasTokenUsage(j, path) && !hasUseTokenStatement(j, path)) {
312
+ addTokenImportToBlockStatement(j, root, path);
313
+ }
314
+ });
315
+ }
316
+
317
+ return hasChanged;
318
+ }
319
+
264
320
  function importComponent(j, root, options) {
265
321
  let hasChanged = false;
266
322
 
267
323
  // 处理字符串字面量
268
324
  hasChanged = processStringLiterals(j, root) || hasChanged;
269
325
 
326
+ // 处理模板字符串中的颜色值
327
+ hasChanged = processTemplateLiterals(j, root) || hasChanged;
328
+
270
329
  // 处理对象属性值(如 fontSize: 14)
271
330
  const objectPropertyChanged = processObjectProperties(j, root);
272
331
  hasChanged = objectPropertyChanged || hasChanged;
@@ -293,8 +352,11 @@ function processObjectProperties(j, root) {
293
352
  if (tokenResult) {
294
353
  hasChanged = true;
295
354
  const isJSXAttribute = path.parentPath.value.type === 'JSXAttribute';
296
- const stringValue = wrapJSXValue(`token.${tokenResult.token}`, isJSXAttribute);
297
- return j.objectProperty(j.identifier(propertyName), j.identifier(stringValue));
355
+ const memberExpression = j.memberExpression(
356
+ j.identifier('token'),
357
+ j.identifier(tokenResult.token)
358
+ );
359
+ return j.objectProperty(j.identifier(propertyName), memberExpression);
298
360
  }
299
361
  return path.value;
300
362
  });
@@ -315,23 +377,22 @@ function processObjectProperties(j, root) {
315
377
  if (tokenResult) {
316
378
  hasChanged = true;
317
379
  const isJSXAttribute = path.parentPath.value.type === 'JSXAttribute';
318
- const stringValue = wrapJSXValue(`token.${tokenResult.token}`, isJSXAttribute);
319
- return j.objectProperty(j.identifier(propertyName), j.identifier(stringValue));
380
+ const memberExpression = j.memberExpression(
381
+ j.identifier('token'),
382
+ j.identifier(tokenResult.token)
383
+ );
384
+ return j.objectProperty(j.identifier(propertyName), memberExpression);
320
385
  }
321
386
  return path.value;
322
387
  });
323
388
  }
324
389
 
325
- // 如果发生了替换,需要添加 token 导入
326
- if (hasChanged) {
327
- addTokenImportsForObjectProperties(j, root);
328
- }
329
-
330
390
  return hasChanged;
331
391
  }
332
392
 
333
393
  // 为对象属性添加 token 导入
334
394
  function addTokenImportsForObjectProperties(j, root) {
395
+ // 处理 BlockStatement 中的 token 使用
335
396
  root
336
397
  .find(j.BlockStatement)
337
398
  .filter(path => isTopBlockStatement(path))
@@ -344,6 +405,31 @@ function addTokenImportsForObjectProperties(j, root) {
344
405
  }
345
406
  }
346
407
  });
408
+
409
+ // 处理顶层导出语句中的 token 使用
410
+ const hasTokenUsageInRoot =
411
+ root.find(j.MemberExpression, {
412
+ object: { name: 'token' },
413
+ }).length > 0;
414
+
415
+ if (hasTokenUsageInRoot) {
416
+ // 检查是否应该添加顶层 token 导入
417
+ if (shouldAddTopLevelTokenImport(j, root)) {
418
+ // 检查是否有 @oceanbase/design 的导入
419
+ const hasOceanbaseImport =
420
+ root.find(j.ImportDeclaration, {
421
+ source: { value: '@oceanbase/design' },
422
+ }).length > 0;
423
+
424
+ if (hasOceanbaseImport) {
425
+ // 如果有 @oceanbase/design 导入,添加到现有导入
426
+ addTokenToExistingImport(j, root);
427
+ } else {
428
+ // 如果没有 @oceanbase/design 导入,添加顶层 token 导入
429
+ addTopLevelTokenImport(j, root);
430
+ }
431
+ }
432
+ }
347
433
  }
348
434
 
349
435
  // 检查是否应该添加顶层 token 导入
@@ -425,6 +511,33 @@ module.exports = (file, api, options) => {
425
511
  processCreateStylesParams(j, root);
426
512
  }
427
513
 
514
+ // 为对象属性添加 token 导入(只在没有其他 token 导入逻辑时调用)
515
+ if (hasChanged) {
516
+ // 检查是否已经有其他 token 导入逻辑在处理
517
+ const hasOtherTokenLogic =
518
+ root.find(j.CallExpression, {
519
+ callee: { name: 'createStyles' },
520
+ }).length > 0 ||
521
+ root.find(j.CallExpression, {
522
+ callee: {
523
+ type: 'MemberExpression',
524
+ object: { name: 'theme' },
525
+ property: { name: 'useToken' },
526
+ },
527
+ }).length > 0 ||
528
+ root
529
+ .find(j.ImportDeclaration, {
530
+ source: { value: '@oceanbase/design' },
531
+ })
532
+ .find(j.ImportSpecifier, {
533
+ imported: { name: 'theme' },
534
+ }).length > 0;
535
+
536
+ if (!hasOtherTokenLogic) {
537
+ addTokenImportsForObjectProperties(j, root);
538
+ }
539
+ }
540
+
428
541
  // 如果有变化,检查是否需要添加 token 导入
429
542
  if (hasChanged) {
430
543
  // 检查是否有 token 使用
@@ -47,6 +47,7 @@ const TOKEN_MAP = {
47
47
  '#ff4d4f': 'colorError',
48
48
  '#f5222d': 'colorError',
49
49
  '#f8636b': 'colorError',
50
+ '#f93939': 'colorError',
50
51
  '#d9d9d9': 'colorBorder',
51
52
  '#bfbfbf': 'colorBorder',
52
53
  '#e8e8e8': 'colorBorder',
@@ -97,6 +98,7 @@ const TOKEN_MAP = {
97
98
  'rgba(0,0,0,0.06)': 'colorFillSecondary',
98
99
  'rgba(0,0,0,0.04)': 'colorFillTertiary',
99
100
  'rgba(0,0,0,0.02)': 'colorFillQuaternary',
101
+ 'rgba(0,0,0,0.03)': 'colorFillQuaternary',
100
102
  '#f5f6fa': 'colorBgLayout',
101
103
  '#edeff2': 'colorBgLayout',
102
104
  // obui legacy style => token
@@ -122,12 +124,12 @@ const TOKEN_MAP = {
122
124
  '#cdd5e4': 'colorBorder',
123
125
  '#f5f8fe': 'colorBgLayout',
124
126
  '#f5f7fa': 'colorBgLayout',
127
+ '#f8fafe': 'colorBgLayout',
125
128
  'rgba(140,140,140,0.1)': 'colorBgLayout',
126
129
  'rgb(240,242,245)': 'colorBgLayout',
127
130
  '#132039': 'colorText',
128
131
  '#364563': 'colorTextSecondary',
129
132
  '#8592ad': 'colorTextTertiary',
130
- '#f8fafe': 'colorFillQuaternary',
131
133
  };
132
134
 
133
135
  const TOKEN_MAP_KEYS = Object.keys(TOKEN_MAP).map(key => formatValue(key));