@atlaskit/codemod-cli 0.27.1 → 0.27.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.
Files changed (119) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/dist/cjs/cli.js +3 -3
  3. package/dist/cjs/filepath.js +4 -2
  4. package/dist/cjs/main.js +90 -38
  5. package/dist/cjs/presets/index.js +2 -1
  6. package/dist/cjs/presets/migrate-to-link/codemods/migrate-to-link.js +2 -2
  7. package/dist/cjs/presets/migrate-to-new-buttons/codemods/next-split-imports.js +1 -1
  8. package/dist/cjs/presets/migrate-to-new-buttons/migrate-to-new-buttons.js +2 -2
  9. package/dist/cjs/presets/migrate-to-new-buttons/utils/add-comment-for-custom-theme-buttons.js +1 -1
  10. package/dist/cjs/presets/remove-token-fallbacks/remove-token-fallbacks.js +225 -0
  11. package/dist/cjs/presets/remove-token-fallbacks/types.js +5 -0
  12. package/dist/cjs/presets/remove-token-fallbacks/utils/all-tokens.js +44 -0
  13. package/dist/cjs/presets/remove-token-fallbacks/utils/color-utils.js +93 -0
  14. package/dist/cjs/presets/remove-token-fallbacks/utils/get-team-info.js +51 -0
  15. package/dist/cjs/presets/remove-token-fallbacks/utils/normalize-values.js +113 -0
  16. package/dist/cjs/presets/remove-token-fallbacks/utils/remove-unused-imports.js +61 -0
  17. package/dist/cjs/presets/remove-token-fallbacks/utils/remove-unused-variables.js +37 -0
  18. package/dist/cjs/presets/remove-token-fallbacks/utils/reporter.js +310 -0
  19. package/dist/cjs/presets/remove-token-fallbacks/utils/token-processor.js +632 -0
  20. package/dist/cjs/presets/remove-token-fallbacks/utils/update-comments.js +58 -0
  21. package/dist/cjs/presets/upgrade-pragmatic-drag-and-drop-to-stable/upgrade-pragmatic-drag-and-drop-to-stable.js +3 -3
  22. package/dist/cjs/sinceRef.js +2 -1
  23. package/dist/cjs/transforms.js +3 -1
  24. package/dist/cjs/types.js +2 -1
  25. package/dist/es2019/cli.js +3 -3
  26. package/dist/es2019/filepath.js +4 -2
  27. package/dist/es2019/main.js +30 -6
  28. package/dist/es2019/presets/index.js +2 -1
  29. package/dist/es2019/presets/migrate-to-link/codemods/migrate-to-link.js +2 -3
  30. package/dist/es2019/presets/migrate-to-new-buttons/codemods/next-remove-unsafe-size.js +1 -1
  31. package/dist/es2019/presets/migrate-to-new-buttons/codemods/next-split-imports.js +2 -2
  32. package/dist/es2019/presets/migrate-to-new-buttons/migrate-to-new-buttons.js +2 -2
  33. package/dist/es2019/presets/migrate-to-new-buttons/utils/add-comment-for-custom-theme-buttons.js +1 -1
  34. package/dist/es2019/presets/migrate-to-new-buttons/utils/migrate-fit-container-icon-button.js +1 -1
  35. package/dist/es2019/presets/remove-token-fallbacks/remove-token-fallbacks.js +130 -0
  36. package/dist/es2019/presets/remove-token-fallbacks/types.js +1 -0
  37. package/dist/es2019/presets/remove-token-fallbacks/utils/all-tokens.js +30 -0
  38. package/dist/es2019/presets/remove-token-fallbacks/utils/color-utils.js +84 -0
  39. package/dist/es2019/presets/remove-token-fallbacks/utils/get-team-info.js +22 -0
  40. package/dist/es2019/presets/remove-token-fallbacks/utils/normalize-values.js +104 -0
  41. package/dist/es2019/presets/remove-token-fallbacks/utils/remove-unused-imports.js +51 -0
  42. package/dist/es2019/presets/remove-token-fallbacks/utils/remove-unused-variables.js +31 -0
  43. package/dist/es2019/presets/remove-token-fallbacks/utils/reporter.js +118 -0
  44. package/dist/es2019/presets/remove-token-fallbacks/utils/token-processor.js +377 -0
  45. package/dist/es2019/presets/remove-token-fallbacks/utils/update-comments.js +46 -0
  46. package/dist/es2019/presets/theme-remove-deprecated-mixins/theme-remove-deprecated-mixins.js +1 -1
  47. package/dist/es2019/presets/upgrade-pragmatic-drag-and-drop-to-stable/upgrade-pragmatic-drag-and-drop-to-stable.js +3 -3
  48. package/dist/es2019/sinceRef.js +2 -1
  49. package/dist/es2019/transforms.js +3 -1
  50. package/dist/es2019/types.js +2 -1
  51. package/dist/esm/cli.js +3 -3
  52. package/dist/esm/filepath.js +4 -2
  53. package/dist/esm/main.js +91 -39
  54. package/dist/esm/presets/index.js +2 -1
  55. package/dist/esm/presets/migrate-to-link/codemods/migrate-to-link.js +2 -3
  56. package/dist/esm/presets/migrate-to-new-buttons/codemods/next-remove-unsafe-size.js +1 -1
  57. package/dist/esm/presets/migrate-to-new-buttons/codemods/next-split-imports.js +2 -2
  58. package/dist/esm/presets/migrate-to-new-buttons/migrate-to-new-buttons.js +2 -2
  59. package/dist/esm/presets/migrate-to-new-buttons/utils/add-comment-for-custom-theme-buttons.js +1 -1
  60. package/dist/esm/presets/migrate-to-new-buttons/utils/migrate-fit-container-icon-button.js +1 -1
  61. package/dist/esm/presets/remove-token-fallbacks/remove-token-fallbacks.js +215 -0
  62. package/dist/esm/presets/remove-token-fallbacks/types.js +1 -0
  63. package/dist/esm/presets/remove-token-fallbacks/utils/all-tokens.js +38 -0
  64. package/dist/esm/presets/remove-token-fallbacks/utils/color-utils.js +86 -0
  65. package/dist/esm/presets/remove-token-fallbacks/utils/get-team-info.js +44 -0
  66. package/dist/esm/presets/remove-token-fallbacks/utils/normalize-values.js +107 -0
  67. package/dist/esm/presets/remove-token-fallbacks/utils/remove-unused-imports.js +55 -0
  68. package/dist/esm/presets/remove-token-fallbacks/utils/remove-unused-variables.js +31 -0
  69. package/dist/esm/presets/remove-token-fallbacks/utils/reporter.js +302 -0
  70. package/dist/esm/presets/remove-token-fallbacks/utils/token-processor.js +625 -0
  71. package/dist/esm/presets/remove-token-fallbacks/utils/update-comments.js +51 -0
  72. package/dist/esm/presets/theme-remove-deprecated-mixins/theme-remove-deprecated-mixins.js +1 -1
  73. package/dist/esm/presets/upgrade-pragmatic-drag-and-drop-to-stable/upgrade-pragmatic-drag-and-drop-to-stable.js +3 -3
  74. package/dist/esm/sinceRef.js +2 -1
  75. package/dist/esm/transforms.js +3 -1
  76. package/dist/esm/types.js +2 -1
  77. package/dist/types/filepath.d.ts +3 -1
  78. package/dist/types/main.d.ts +1 -1
  79. package/dist/types/presets/index.d.ts +1 -0
  80. package/dist/types/presets/migrate-to-new-buttons/utils/import-types-from-new-entry-point.d.ts +1 -1
  81. package/dist/types/presets/migrate-to-new-buttons/utils/move-icon-value-from-link-button-to-link-children.d.ts +1 -1
  82. package/dist/types/presets/remove-token-fallbacks/remove-token-fallbacks.d.ts +29 -0
  83. package/dist/types/presets/remove-token-fallbacks/types.d.ts +39 -0
  84. package/dist/types/presets/remove-token-fallbacks/utils/all-tokens.d.ts +1 -0
  85. package/dist/types/presets/remove-token-fallbacks/utils/color-utils.d.ts +3 -0
  86. package/dist/types/presets/remove-token-fallbacks/utils/get-team-info.d.ts +8 -0
  87. package/dist/types/presets/remove-token-fallbacks/utils/normalize-values.d.ts +8 -0
  88. package/dist/types/presets/remove-token-fallbacks/utils/remove-unused-imports.d.ts +2 -0
  89. package/dist/types/presets/remove-token-fallbacks/utils/remove-unused-variables.d.ts +2 -0
  90. package/dist/types/presets/remove-token-fallbacks/utils/reporter.d.ts +4 -0
  91. package/dist/types/presets/remove-token-fallbacks/utils/token-processor.d.ts +30 -0
  92. package/dist/types/presets/remove-token-fallbacks/utils/update-comments.d.ts +2 -0
  93. package/dist/types/presets/styled-to-emotion/styled-to-emotion.d.ts +1 -1
  94. package/dist/types/presets/upgrade-pragmatic-drag-and-drop-to-stable/upgrade-pragmatic-drag-and-drop-to-stable.d.ts +1 -1
  95. package/dist/types/sinceRef.d.ts +2 -1
  96. package/dist/types/transforms.d.ts +3 -1
  97. package/dist/types/types.d.ts +3 -2
  98. package/dist/types-ts4.5/filepath.d.ts +3 -1
  99. package/dist/types-ts4.5/main.d.ts +1 -1
  100. package/dist/types-ts4.5/presets/index.d.ts +1 -0
  101. package/dist/types-ts4.5/presets/migrate-to-new-buttons/utils/import-types-from-new-entry-point.d.ts +1 -1
  102. package/dist/types-ts4.5/presets/migrate-to-new-buttons/utils/move-icon-value-from-link-button-to-link-children.d.ts +1 -1
  103. package/dist/types-ts4.5/presets/remove-token-fallbacks/remove-token-fallbacks.d.ts +29 -0
  104. package/dist/types-ts4.5/presets/remove-token-fallbacks/types.d.ts +39 -0
  105. package/dist/types-ts4.5/presets/remove-token-fallbacks/utils/all-tokens.d.ts +1 -0
  106. package/dist/types-ts4.5/presets/remove-token-fallbacks/utils/color-utils.d.ts +3 -0
  107. package/dist/types-ts4.5/presets/remove-token-fallbacks/utils/get-team-info.d.ts +8 -0
  108. package/dist/types-ts4.5/presets/remove-token-fallbacks/utils/normalize-values.d.ts +8 -0
  109. package/dist/types-ts4.5/presets/remove-token-fallbacks/utils/remove-unused-imports.d.ts +2 -0
  110. package/dist/types-ts4.5/presets/remove-token-fallbacks/utils/remove-unused-variables.d.ts +2 -0
  111. package/dist/types-ts4.5/presets/remove-token-fallbacks/utils/reporter.d.ts +4 -0
  112. package/dist/types-ts4.5/presets/remove-token-fallbacks/utils/token-processor.d.ts +30 -0
  113. package/dist/types-ts4.5/presets/remove-token-fallbacks/utils/update-comments.d.ts +2 -0
  114. package/dist/types-ts4.5/presets/styled-to-emotion/styled-to-emotion.d.ts +1 -1
  115. package/dist/types-ts4.5/presets/upgrade-pragmatic-drag-and-drop-to-stable/upgrade-pragmatic-drag-and-drop-to-stable.d.ts +1 -1
  116. package/dist/types-ts4.5/sinceRef.d.ts +2 -1
  117. package/dist/types-ts4.5/transforms.d.ts +3 -1
  118. package/dist/types-ts4.5/types.d.ts +3 -2
  119. package/package.json +8 -3
@@ -0,0 +1,104 @@
1
+ import chalk from 'chalk';
2
+ import { colorToHex, compareHex, isValidColor } from './color-utils';
3
+
4
+ // so far allowing to remove only exact matches in values. Can be increased to auto-remove fallbacks with similar values
5
+ const ACCEPTABLE_COLOR_DIFFERENCE = 0;
6
+ const ACCEPTABLE_SPACE_DIFFERENCE = 0;
7
+ const ACCEPTABLE_NUMERIC_DIFFERENCE = 0;
8
+ export function normalizeValues(tokenKey, tokenValue, fallbackValue) {
9
+ let tokenLogValue;
10
+ let fallbackLogValue;
11
+ let normalizedTokenValue = tokenValue;
12
+ let normalizedFallbackValue = fallbackValue;
13
+ const lowerCaseTokenKey = tokenKey === null || tokenKey === void 0 ? void 0 : tokenKey.toLowerCase();
14
+ let difference;
15
+ let isAcceptableDifference;
16
+ if (lowerCaseTokenKey.startsWith('color') || lowerCaseTokenKey.startsWith('elevation')) {
17
+ if (tokenValue && isValidColor(tokenValue)) {
18
+ const normalizedHex = colorToHex(tokenValue);
19
+ tokenLogValue = chalk.bgHex(normalizedHex)(tokenValue);
20
+ normalizedTokenValue = normalizedHex;
21
+ }
22
+ if (fallbackValue && isValidColor(fallbackValue)) {
23
+ const normalizedHex = colorToHex(fallbackValue);
24
+ fallbackLogValue = chalk.bgHex(normalizedHex)(fallbackValue);
25
+ normalizedFallbackValue = normalizedHex;
26
+ }
27
+ if (normalizedTokenValue && normalizedFallbackValue) {
28
+ difference = compareHex(normalizedTokenValue, normalizedFallbackValue);
29
+ isAcceptableDifference = difference <= ACCEPTABLE_COLOR_DIFFERENCE;
30
+ }
31
+ } else if (lowerCaseTokenKey.startsWith('space')) {
32
+ const tokenValueInPx = tokenValue ? convertToPx(tokenValue) : undefined;
33
+ const fallbackValueInPx = fallbackValue ? convertToPx(fallbackValue) : undefined;
34
+ if (tokenValueInPx !== undefined && fallbackValueInPx !== undefined) {
35
+ const maxVal = Math.max(tokenValueInPx, fallbackValueInPx);
36
+ difference = Math.abs(tokenValueInPx - fallbackValueInPx) / maxVal * 100;
37
+ isAcceptableDifference = difference <= ACCEPTABLE_SPACE_DIFFERENCE;
38
+ }
39
+ // Log the normalized values
40
+ normalizedTokenValue = tokenValue;
41
+ normalizedFallbackValue = fallbackValue;
42
+ tokenLogValue = tokenValue;
43
+ fallbackLogValue = fallbackValue;
44
+ } else {
45
+ // Handle other numeric comparisons
46
+ const tokenValueNumber = parseFloat(tokenValue !== null && tokenValue !== void 0 ? tokenValue : '');
47
+ const fallbackValueNumber = parseFloat(fallbackValue !== null && fallbackValue !== void 0 ? fallbackValue : '');
48
+ if (!isNaN(tokenValueNumber) && !isNaN(fallbackValueNumber)) {
49
+ const maxVal = Math.max(tokenValueNumber, fallbackValueNumber);
50
+ difference = Math.abs(tokenValueNumber - fallbackValueNumber) / maxVal * 100;
51
+ isAcceptableDifference = difference <= ACCEPTABLE_NUMERIC_DIFFERENCE;
52
+ }
53
+ // Log the normalized values
54
+ normalizedTokenValue = tokenValue;
55
+ normalizedFallbackValue = fallbackValue;
56
+ tokenLogValue = tokenValue;
57
+ fallbackLogValue = fallbackValue;
58
+ }
59
+ if (tokenLogValue === undefined) {
60
+ tokenLogValue = chalk.magenta(tokenValue || '');
61
+ }
62
+ if (fallbackLogValue === undefined) {
63
+ fallbackLogValue = chalk.yellow(fallbackValue || '');
64
+ }
65
+ return {
66
+ difference,
67
+ isAcceptableDifference,
68
+ tokenLogValue,
69
+ fallbackLogValue,
70
+ normalizedTokenValue,
71
+ normalizedFallbackValue
72
+ };
73
+ }
74
+ function convertToPx(value) {
75
+ // If the value is a number, return it directly
76
+ if (typeof value === 'number') {
77
+ return value;
78
+ }
79
+ // Check if the string is a plain number (without units)
80
+ const plainNumberRegex = /^-?\d+(\.\d+)?$/;
81
+ if (plainNumberRegex.test(value)) {
82
+ return parseFloat(value);
83
+ }
84
+ // Regular expression to match CSS units
85
+ const unitRegex = /^(-?\d+(\.\d+)?)(px|rem|em|%)$/;
86
+ const match = value.match(unitRegex);
87
+ if (!match) {
88
+ return undefined;
89
+ }
90
+ const [, num,, unit] = match;
91
+ const numericValue = parseFloat(num);
92
+ switch (unit) {
93
+ case 'px':
94
+ return numericValue;
95
+ case 'rem':
96
+ return numericValue * 16;
97
+ // Assuming 1rem = 16px
98
+ case 'em':
99
+ return numericValue * 16;
100
+ // Assuming 1em = 16px
101
+ default:
102
+ return undefined;
103
+ }
104
+ }
@@ -0,0 +1,51 @@
1
+ export function removeUnusedImports(importDeclarations, j) {
2
+ const removeIfUnused = (importSpecifier, importDeclaration) => {
3
+ var _importSpecifier$valu;
4
+ const varName = (_importSpecifier$valu = importSpecifier.value.local) === null || _importSpecifier$valu === void 0 ? void 0 : _importSpecifier$valu.name;
5
+ if (varName === 'React' || varName === 'jsx') {
6
+ return false;
7
+ }
8
+ const isUsedInScopes = () => {
9
+ return j(importDeclaration).closestScope().find(j.Identifier, {
10
+ name: varName
11
+ }).filter(p => {
12
+ var _importSpecifier$valu2;
13
+ if (p.value.start === ((_importSpecifier$valu2 = importSpecifier.value.local) === null || _importSpecifier$valu2 === void 0 ? void 0 : _importSpecifier$valu2.start)) {
14
+ return false;
15
+ }
16
+ if (p.parentPath.value.type === 'Property' && p.name === 'key') {
17
+ return false;
18
+ }
19
+ if (p.name === 'property') {
20
+ return false;
21
+ }
22
+ return true;
23
+ }).size() > 0;
24
+ };
25
+ if (!isUsedInScopes()) {
26
+ j(importSpecifier).remove();
27
+ return true;
28
+ }
29
+ return false;
30
+ };
31
+ const removeUnusedDefaultImport = importDeclaration => {
32
+ return j(importDeclaration).find(j.ImportDefaultSpecifier).filter(s => removeIfUnused(s, importDeclaration)).size() > 0;
33
+ };
34
+ const removeUnusedNonDefaultImports = importDeclaration => {
35
+ return j(importDeclaration).find(j.ImportSpecifier).filter(s => removeIfUnused(s, importDeclaration)).size() > 0;
36
+ };
37
+ const processImportDeclaration = importDeclaration => {
38
+ var _importDeclaration$va, _importDeclaration$va2;
39
+ if (((_importDeclaration$va = importDeclaration.value.specifiers) === null || _importDeclaration$va === void 0 ? void 0 : _importDeclaration$va.length) === 0) {
40
+ return false;
41
+ }
42
+ const hadUnusedDefaultImport = removeUnusedDefaultImport(importDeclaration);
43
+ const hadUnusedNonDefaultImports = removeUnusedNonDefaultImports(importDeclaration);
44
+ if (((_importDeclaration$va2 = importDeclaration.value.specifiers) === null || _importDeclaration$va2 === void 0 ? void 0 : _importDeclaration$va2.length) === 0) {
45
+ j(importDeclaration).remove();
46
+ return true;
47
+ }
48
+ return hadUnusedDefaultImport || hadUnusedNonDefaultImports;
49
+ };
50
+ importDeclarations.forEach(processImportDeclaration);
51
+ }
@@ -0,0 +1,31 @@
1
+ export function removeUnusedVariables(variableDeclarations, j) {
2
+ const removeIfUnused = varDeclarator => {
3
+ var _varDeclarator$value;
4
+ if (((_varDeclarator$value = varDeclarator.value) === null || _varDeclarator$value === void 0 ? void 0 : _varDeclarator$value.id.type) !== 'Identifier') {
5
+ return false;
6
+ }
7
+ const varName = varDeclarator.value.id.name;
8
+ const isUsedInScopes = () => {
9
+ return j(varDeclarator).closestScope().find(j.Identifier, {
10
+ name: varName
11
+ }).filter(p => {
12
+ if (p.value.start === varDeclarator.value.id.start) {
13
+ return false;
14
+ }
15
+ if (p.parentPath.value.type === 'Property' && p.name === 'key') {
16
+ return false;
17
+ }
18
+ if (p.name === 'property') {
19
+ return false;
20
+ }
21
+ return true;
22
+ }).size() > 0;
23
+ };
24
+ if (!isUsedInScopes()) {
25
+ j(varDeclarator).remove();
26
+ return true;
27
+ }
28
+ return false;
29
+ };
30
+ variableDeclarations.forEach(removeIfUnused);
31
+ }
@@ -0,0 +1,118 @@
1
+ /* eslint-disable no-console */
2
+ import fs from 'fs/promises';
3
+ import path from 'path';
4
+ import { v4 as uuidv4 } from 'uuid';
5
+ async function writeToCsv(filePath, data) {
6
+ await fs.writeFile(filePath, data.join('\n'), 'utf-8');
7
+ }
8
+ async function readCsv(filePath) {
9
+ const data = await fs.readFile(filePath, 'utf-8');
10
+ return data.split('\n').filter(line => line.trim() !== '');
11
+ }
12
+ function escapeCsvValue(value) {
13
+ if (!value) {
14
+ return value;
15
+ }
16
+ if (value.includes('"')) {
17
+ // Escape double quotes by doubling them
18
+ value = value.replace(/"/g, '""');
19
+ }
20
+ if (value.includes(',') || value.includes('"') || value.includes('\n')) {
21
+ // Surround the value with double quotes if it contains a comma, double quotes, or newlines
22
+ value = `"${value}"`;
23
+ }
24
+ return value;
25
+ }
26
+ export async function clearFolder(reportFolder) {
27
+ console.log('Clearing report folder:', reportFolder);
28
+ const filesToDelete = await fs.readdir(reportFolder);
29
+ for (const file of filesToDelete) {
30
+ const filePath = path.join(reportFolder, file);
31
+ await fs.unlink(filePath);
32
+ }
33
+ }
34
+ async function saveFilePaths(reportFolder, files) {
35
+ const filesTxtPath = path.join(reportFolder, 'files.txt');
36
+ await fs.writeFile(filesTxtPath, Array.from(files).map(filePath => `"${filePath}"`).join(' '), 'utf-8');
37
+ }
38
+ export async function combineReports(reportFolder) {
39
+ console.log('Combining reports in folder:', reportFolder);
40
+ const files = await fs.readdir(reportFolder);
41
+ let totalReplaced = 0;
42
+ let totalNotReplaced = 0;
43
+ const combinedReplacements = [];
44
+ const combinedNonReplacements = [];
45
+ const affectedPaths = new Set();
46
+ for (const file of files) {
47
+ const filePath = path.join(reportFolder, file);
48
+ if (file.endsWith('_success.csv')) {
49
+ const replacements = await readCsv(filePath);
50
+ const codeFilePaths = replacements.map(x => x.split(',')[2]);
51
+ for (const codeFilePath of codeFilePaths) {
52
+ affectedPaths.add(codeFilePath);
53
+ }
54
+ totalReplaced += replacements.length;
55
+ combinedReplacements.push(...replacements);
56
+ } else if (file.endsWith('_failed.csv')) {
57
+ const nonReplacements = await readCsv(filePath);
58
+ totalNotReplaced += nonReplacements.length;
59
+ combinedNonReplacements.push(...nonReplacements);
60
+ }
61
+ }
62
+ const totalTokens = totalReplaced + totalNotReplaced;
63
+ const percentageReplaced = totalTokens > 0 ? totalReplaced / totalTokens * 100 : 0;
64
+ await clearFolder(reportFolder);
65
+
66
+ // Write combined summary as JSON
67
+ const combinedSummaryPath = path.join(reportFolder, 'summary.json');
68
+ const summaryData = {
69
+ totalReplaced,
70
+ totalNotReplaced,
71
+ percentageReplaced
72
+ };
73
+ await fs.writeFile(combinedSummaryPath, JSON.stringify(summaryData, null, 2), 'utf-8');
74
+
75
+ // Sort the combined arrays
76
+ const sortCsvLines = lines => {
77
+ return lines.sort((a, b) => {
78
+ const aCols = a.split(',');
79
+ const bCols = b.split(',');
80
+ for (let i = 0; i < 4; i++) {
81
+ const comparison = aCols[i].localeCompare(bCols[i]);
82
+ if (comparison !== 0) {
83
+ return comparison;
84
+ }
85
+ }
86
+ return 0;
87
+ });
88
+ };
89
+ const sortedReplacements = sortCsvLines(combinedReplacements);
90
+ const sortedNonReplacements = sortCsvLines(combinedNonReplacements);
91
+
92
+ // Write combined replacements
93
+ const combinedReplacementsPath = path.join(reportFolder, 'success.csv');
94
+ const header = 'Team,Package,File,Line Number,Raw Token Key,Raw Fallback Value,Resolved Token Value,Resolved Fallback Value,Difference %';
95
+ await fs.writeFile(combinedReplacementsPath, [header, ...sortedReplacements].join('\n'), 'utf-8');
96
+
97
+ // Write combined non-replacements
98
+ const combinedNonReplacementsPath = path.join(reportFolder, 'failed.csv');
99
+ await fs.writeFile(combinedNonReplacementsPath, [header, ...sortedNonReplacements].join('\n'), 'utf-8');
100
+ // Extract unique file paths
101
+ await saveFilePaths(reportFolder, affectedPaths);
102
+ }
103
+ function prepareCsvData(items) {
104
+ return items.map(item => {
105
+ var _item$difference$toFi, _item$difference;
106
+ return [escapeCsvValue(item.teamInfo.teamName), escapeCsvValue(item.teamInfo.packageName), escapeCsvValue(item.filePath), escapeCsvValue(String(item.lineNumber)), escapeCsvValue(item.tokenKey), escapeCsvValue(item.rawFallbackValue), escapeCsvValue(item.resolvedTokenValue), escapeCsvValue(item.resolvedFallbackValue), escapeCsvValue((_item$difference$toFi = (_item$difference = item.difference) === null || _item$difference === void 0 ? void 0 : _item$difference.toFixed(1)) !== null && _item$difference$toFi !== void 0 ? _item$difference$toFi : '')].join(',');
107
+ });
108
+ }
109
+ export async function writeReports(details, reportFolder) {
110
+ const replacementsFilePath = path.join(reportFolder, `${uuidv4()}_success.csv`);
111
+ const nonReplacementsFilePath = path.join(reportFolder, `${uuidv4()}_failed.csv`);
112
+ await fs.mkdir(reportFolder, {
113
+ recursive: true
114
+ });
115
+ const replacementData = prepareCsvData(details.replaced);
116
+ const nonReplacementData = prepareCsvData(details.notReplaced);
117
+ await Promise.all([writeToCsv(replacementsFilePath, replacementData), writeToCsv(nonReplacementsFilePath, nonReplacementData)]);
118
+ }
@@ -0,0 +1,377 @@
1
+ import _defineProperty from "@babel/runtime/helpers/defineProperty";
2
+ /* eslint-disable no-console */
3
+ import fs from 'fs/promises';
4
+ import path from 'path';
5
+ import chalk from 'chalk';
6
+ import { normalizeValues } from './normalize-values';
7
+ import { addOrUpdateEslintIgnoreComment } from './update-comments';
8
+ export class TokenProcessor {
9
+ constructor(j, options, fileInfo, source, rootDir, details, tokenMap, teamInfo) {
10
+ _defineProperty(this, "logMessages", []);
11
+ _defineProperty(this, "possibleExtensions", ['.ts', '.tsx', '.js', '.jsx']);
12
+ this.j = j;
13
+ this.options = options;
14
+ this.fileInfo = fileInfo;
15
+ this.source = source;
16
+ this.rootDir = rootDir;
17
+ this.details = details;
18
+ this.tokenMap = tokenMap;
19
+ this.teamInfo = teamInfo;
20
+ }
21
+ async processAndLogSingleToken(callPath) {
22
+ var _callPath$node$loc;
23
+ const line = (_callPath$node$loc = callPath.node.loc) === null || _callPath$node$loc === void 0 ? void 0 : _callPath$node$loc.start.line;
24
+ const {
25
+ shouldLog,
26
+ ...rest
27
+ } = await this.processSingleToken(callPath);
28
+ if (this.options.silent || !shouldLog) {
29
+ return rest;
30
+ }
31
+ const coloredPath = chalk.blue(this.fileInfo.path);
32
+ const coloredLine = line ? `: ${chalk.green(line)}` : '';
33
+ console.log(`${coloredPath}${coloredLine}: ${this.logMessages.join(' | ')}
34
+ ----------------------------------------`);
35
+ return rest;
36
+ }
37
+ logVerbose(message) {
38
+ if (this.options.verbose) {
39
+ this.log(message);
40
+ }
41
+ }
42
+ log(message) {
43
+ this.logMessages.push(message);
44
+ }
45
+ logError(message) {
46
+ this.log(chalk.red(message));
47
+ }
48
+ async processSingleToken(callPath) {
49
+ var _callPath$node$loc2;
50
+ const args = callPath.node.arguments;
51
+ const line = (_callPath$node$loc2 = callPath.node.loc) === null || _callPath$node$loc2 === void 0 ? void 0 : _callPath$node$loc2.start.line;
52
+ if (args.length < 2) {
53
+ this.logVerbose(chalk.yellow('Skipped token call without fallback'));
54
+ return {
55
+ shouldLog: false,
56
+ fallbackRemoved: false,
57
+ resolvedImportDeclaration: undefined,
58
+ resolvedLocalVarDeclaration: undefined
59
+ };
60
+ }
61
+ const tokenKey = this.getTokenKey(args[0]);
62
+ if (!tokenKey) {
63
+ return {
64
+ shouldLog: true,
65
+ fallbackRemoved: false,
66
+ resolvedImportDeclaration: undefined,
67
+ resolvedLocalVarDeclaration: undefined
68
+ };
69
+ }
70
+ const isSkipped = tokenKey.startsWith('elevation.shadow') || tokenKey.startsWith('font.body') || tokenKey.startsWith('font.heading');
71
+ const tokenValue = isSkipped ? '' : this.tokenMap[tokenKey];
72
+ this.logVerbose(`Token value from tokenMap: ${chalk.magenta(tokenValue)} for key: ${chalk.yellow(tokenKey)}`);
73
+ const {
74
+ rawFallbackValue,
75
+ fallbackValue,
76
+ resolvedImportDeclaration,
77
+ resolvedLocalVarDeclaration
78
+ } = isSkipped ? {
79
+ rawFallbackValue: 'N/A',
80
+ fallbackValue: undefined,
81
+ resolvedImportDeclaration: undefined,
82
+ resolvedLocalVarDeclaration: undefined
83
+ } : await this.getFallbackValue(args[1]);
84
+ const {
85
+ difference,
86
+ isAcceptableDifference,
87
+ tokenLogValue,
88
+ fallbackLogValue,
89
+ normalizedTokenValue,
90
+ normalizedFallbackValue
91
+ } = normalizeValues(tokenKey, tokenValue, fallbackValue);
92
+ const areEqual = normalizedTokenValue === normalizedFallbackValue;
93
+ const logData = {
94
+ teamInfo: this.teamInfo,
95
+ filePath: this.fileInfo.path,
96
+ lineNumber: line || -1,
97
+ tokenKey,
98
+ rawFallbackValue,
99
+ resolvedTokenValue: tokenValue,
100
+ resolvedFallbackValue: fallbackValue !== null && fallbackValue !== void 0 ? fallbackValue : '',
101
+ difference
102
+ };
103
+ let fallbackRemoved = false;
104
+ let importDeclaration;
105
+ let localVarDeclaration;
106
+ if (areEqual || isAcceptableDifference || this.options.forceUpdate) {
107
+ this.log(chalk.green(areEqual ? 'Token value and fallback value are equal, removing fallback' : 'Token value and fallback value are within acceptable difference threshold, removing fallback'));
108
+ args.pop();
109
+ this.details.replaced.push(logData);
110
+ fallbackRemoved = true;
111
+ importDeclaration = resolvedImportDeclaration;
112
+ localVarDeclaration = resolvedLocalVarDeclaration;
113
+ } else {
114
+ const message = normalizedFallbackValue === undefined ? `Fallback value could not be resolved` : `Values mismatched significantly`;
115
+ this.logError(message);
116
+ if (this.options.addEslintComments) {
117
+ addOrUpdateEslintIgnoreComment(this.j, tokenValue, fallbackValue, callPath);
118
+ }
119
+ this.details.notReplaced.push(logData);
120
+ }
121
+ this.log(`Token: ${chalk.yellow(tokenKey)}, Raw fallback: ${chalk.yellow(rawFallbackValue)}, Resolved token value: ${tokenLogValue}, Resolved fallback value: ${fallbackLogValue}`);
122
+ return {
123
+ shouldLog: true,
124
+ fallbackRemoved,
125
+ resolvedImportDeclaration: importDeclaration,
126
+ resolvedLocalVarDeclaration: localVarDeclaration
127
+ };
128
+ }
129
+ getTokenKey(arg) {
130
+ if (arg.type === 'StringLiteral') {
131
+ const tokenKey = arg.value;
132
+ this.logVerbose(`Determined token key as literal: ${chalk.yellow(tokenKey)}`);
133
+ return tokenKey;
134
+ } else {
135
+ this.logError(`The first argument of token function is not a string literal`);
136
+ return undefined;
137
+ }
138
+ }
139
+ async getFallbackValue(fallbackValueNode) {
140
+ switch (fallbackValueNode.type) {
141
+ case 'StringLiteral':
142
+ return this.processFallbackAsStringLiteral(fallbackValueNode);
143
+ case 'Identifier':
144
+ return this.processFallbackAsIdentifier(fallbackValueNode);
145
+ case 'MemberExpression':
146
+ return this.processFallbackAsMemberExpression(fallbackValueNode);
147
+ case 'TemplateLiteral':
148
+ return this.processFallbackAsTemplateLiteral(fallbackValueNode);
149
+ default:
150
+ return {
151
+ fallbackValue: undefined,
152
+ rawFallbackValue: '',
153
+ resolvedImportDeclaration: undefined,
154
+ resolvedLocalVarDeclaration: undefined
155
+ };
156
+ }
157
+ }
158
+ processFallbackAsStringLiteral(fallbackValueNode) {
159
+ const fallbackValue = fallbackValueNode.value;
160
+ this.logVerbose(`Fallback value is a literal: ${chalk.yellow(fallbackValue)}`);
161
+ return {
162
+ fallbackValue,
163
+ rawFallbackValue: fallbackValue,
164
+ resolvedImportDeclaration: undefined,
165
+ resolvedLocalVarDeclaration: undefined
166
+ };
167
+ }
168
+ async processFallbackAsIdentifier(fallbackValueNode) {
169
+ const variableName = fallbackValueNode.name;
170
+ const variableNameForLog = `${chalk.yellow(variableName)}`;
171
+ let fallbackValue;
172
+ this.logVerbose(`Fallback is an identifier: ${chalk.yellow(variableName)}, attempting to resolve...`);
173
+
174
+ // Check for local variable declaration
175
+ const localVarDeclaration = this.source.find(this.j.VariableDeclarator, {
176
+ id: {
177
+ name: variableName
178
+ }
179
+ }).at(0);
180
+ let resolvedImportDeclaration;
181
+ let resolvedLocalVarDeclaration;
182
+ if (localVarDeclaration.size()) {
183
+ const init = localVarDeclaration.get().value.init;
184
+ if (init.type === 'Literal' || init.type === 'StringLiteral') {
185
+ fallbackValue = init.value;
186
+ resolvedLocalVarDeclaration = localVarDeclaration.paths()[0];
187
+ this.logVerbose(`Resolved fallback value from local variable: ${chalk.yellow(fallbackValue)} for identifier: ${variableNameForLog}`);
188
+ }
189
+ } else {
190
+ // Check for named import
191
+ const importDeclaration = this.source.find(this.j.ImportDeclaration).filter(this.createImportFilter(variableName)).at(0);
192
+ if (importDeclaration.size()) {
193
+ const importSource = importDeclaration.get().value.source.value;
194
+ fallbackValue = await this.resolveValueFromImport(this.rootDir, importSource, undefined, variableName);
195
+ if (fallbackValue !== undefined) {
196
+ this.logVerbose(`Resolved fallback value from import: ${chalk.yellow(fallbackValue)} for identifier: ${variableNameForLog}`);
197
+ resolvedImportDeclaration = importDeclaration.paths()[0];
198
+ }
199
+ } else {
200
+ this.logVerbose(chalk.red(`Could not resolve fallback value for identifier: ${variableNameForLog}: it's neither a local variable nor an import`));
201
+ }
202
+ }
203
+ return {
204
+ rawFallbackValue: variableName,
205
+ fallbackValue,
206
+ resolvedImportDeclaration,
207
+ resolvedLocalVarDeclaration
208
+ };
209
+ }
210
+ async processFallbackAsMemberExpression(fallbackValueNode) {
211
+ let objectName;
212
+ let propertyName;
213
+ let fallbackValue;
214
+ if (fallbackValueNode.object.type === 'Identifier') {
215
+ objectName = fallbackValueNode.object.name;
216
+ }
217
+ if (fallbackValueNode.property.type === 'Identifier') {
218
+ propertyName = fallbackValueNode.property.name;
219
+ } else if (fallbackValueNode.property.type === 'Literal' && typeof fallbackValueNode.property.value === 'string') {
220
+ propertyName = fallbackValueNode.property.value;
221
+ }
222
+ if (!objectName || !propertyName) {
223
+ this.logError(`Could not determine object and property names from member expression: ${chalk.yellow(fallbackValueNode)}`);
224
+ return {
225
+ rawFallbackValue: '',
226
+ fallbackValue,
227
+ resolvedImportDeclaration: undefined,
228
+ resolvedLocalVarDeclaration: undefined
229
+ };
230
+ }
231
+ const rawFallbackValue = `${objectName}.${propertyName}`;
232
+ this.logVerbose(`Fallback is a member expression: ${chalk.yellow(rawFallbackValue)}, attempting to resolve...`);
233
+ let resolvedImportDeclaration;
234
+
235
+ // Find the import statement for the object
236
+ const importDeclaration = this.source.find(this.j.ImportDeclaration).filter(this.createImportFilter(objectName)).at(0);
237
+ if (importDeclaration.size()) {
238
+ const importSource = importDeclaration.get().value.source.value;
239
+ fallbackValue = await this.resolveValueFromImport(this.rootDir, importSource, objectName, propertyName);
240
+ if (fallbackValue !== undefined) {
241
+ resolvedImportDeclaration = importDeclaration.paths()[0];
242
+ this.logVerbose(`Resolved fallback value from member expression: ${chalk.yellow(fallbackValue)}`);
243
+ }
244
+ } else {
245
+ this.logError(`Could not find import for member expression: ${chalk.yellow(rawFallbackValue)}`);
246
+ }
247
+ return {
248
+ rawFallbackValue,
249
+ fallbackValue,
250
+ resolvedImportDeclaration,
251
+ resolvedLocalVarDeclaration: undefined
252
+ };
253
+ }
254
+ async processFallbackAsTemplateLiteral(fallbackValueNode) {
255
+ const expressions = fallbackValueNode.expressions;
256
+ let rawFallbackValue = '';
257
+ let fallbackValue;
258
+ const quasis = fallbackValueNode.quasis;
259
+ if (expressions.length !== 1 || quasis.length !== 2) {
260
+ this.logError(`Unsupported template literal structure`);
261
+ return {
262
+ rawFallbackValue,
263
+ fallbackValue,
264
+ resolvedImportDeclaration: undefined,
265
+ resolvedLocalVarDeclaration: undefined
266
+ };
267
+ }
268
+ let exprValue;
269
+ const expression = expressions[0];
270
+ let resolvedImportDeclaration;
271
+ let resolvedLocalVarDeclaration;
272
+ if (expression.type === 'Identifier') {
273
+ const result = await this.processFallbackAsIdentifier(expression);
274
+ exprValue = result.fallbackValue;
275
+ resolvedImportDeclaration = result.resolvedImportDeclaration;
276
+ resolvedLocalVarDeclaration = result.resolvedLocalVarDeclaration;
277
+ } else if (expression.type === 'MemberExpression') {
278
+ const result = await this.processFallbackAsMemberExpression(expression);
279
+ exprValue = result.fallbackValue;
280
+ resolvedImportDeclaration = result.resolvedImportDeclaration;
281
+ }
282
+ if (exprValue !== undefined) {
283
+ rawFallbackValue = `${quasis[0].value.raw}\${${exprValue}}${quasis[1].value.raw}`;
284
+ fallbackValue = `${quasis[0].value.cooked}${exprValue}${quasis[1].value.cooked}`;
285
+ this.logVerbose(`Resolved fallback value from template literal: ${chalk.yellow(fallbackValue)}`);
286
+ }
287
+ return {
288
+ rawFallbackValue,
289
+ fallbackValue,
290
+ resolvedImportDeclaration,
291
+ resolvedLocalVarDeclaration
292
+ };
293
+ }
294
+ tryResolveModulePath(moduleName) {
295
+ try {
296
+ const resolvedPath = require.resolve(moduleName, {
297
+ paths: [this.rootDir]
298
+ });
299
+ this.logVerbose(`Resolved module path: ${chalk.green(resolvedPath)} for ${chalk.cyan(moduleName)}`);
300
+ return resolvedPath;
301
+ } catch (error) {
302
+ return null;
303
+ }
304
+ }
305
+ async tryResolveLocalPath(currentDir, importPath) {
306
+ for (const ext of this.possibleExtensions) {
307
+ const potentialPath = path.resolve(currentDir, `${importPath}${ext}`);
308
+ try {
309
+ await fs.access(potentialPath);
310
+ this.logVerbose(`Resolved file path locally: ${chalk.green(potentialPath)}`);
311
+ return potentialPath;
312
+ } catch {
313
+ // Continue if the file is not found
314
+ }
315
+ }
316
+ return null;
317
+ }
318
+ async resolveValueFromImport(currentDir, importPath, objectName, propertyOrVariableName) {
319
+ let filePath = this.tryResolveModulePath(importPath);
320
+ if (!filePath) {
321
+ filePath = await this.tryResolveLocalPath(currentDir, importPath);
322
+ }
323
+ if (!filePath) {
324
+ this.logError(`File not found for import path: ${chalk.cyan(importPath)} in directory: ${chalk.blue(this.rootDir)}`);
325
+ return undefined;
326
+ }
327
+ this.logVerbose(`Reading file: ${chalk.green(filePath)}`);
328
+ const fileContent = await fs.readFile(filePath, 'utf-8');
329
+ const source = this.j(fileContent);
330
+ if (objectName) {
331
+ // Check if the object is imported from another module
332
+ const imports = source.find(this.j.ImportDeclaration);
333
+ const matchingImport = imports.filter(this.createImportFilter(objectName)).at(0);
334
+ if (matchingImport.size()) {
335
+ var _importDecl$source$va;
336
+ const importDecl = matchingImport.get().node;
337
+ const newImportPath = (_importDecl$source$va = importDecl.source.value) === null || _importDecl$source$va === void 0 ? void 0 : _importDecl$source$va.toString();
338
+ return this.resolveValueFromImport(path.dirname(filePath), newImportPath, objectName, propertyOrVariableName);
339
+ }
340
+ }
341
+ // If not imported, check for variable declaration
342
+ const varDeclaration = source.find(this.j.VariableDeclarator, {
343
+ id: {
344
+ name: propertyOrVariableName
345
+ }
346
+ }).at(0);
347
+ if (!varDeclaration.size()) {
348
+ this.logError(`Variable declaration not found for ${chalk.yellow(propertyOrVariableName)} in file: ${chalk.green(filePath)}`);
349
+ return undefined;
350
+ }
351
+ const init = varDeclaration.get().value.init;
352
+ if (init.type === 'Literal' || init.type === 'StringLiteral' || init.type === 'NumericLiteral') {
353
+ return init.value;
354
+ } else {
355
+ this.logError(`Unhandled init type ${init.type} for variable: ${chalk.yellow(propertyOrVariableName)} in file: ${chalk.green(filePath)}`);
356
+ return undefined;
357
+ }
358
+ }
359
+ createImportFilter(targetName) {
360
+ return path => {
361
+ var _path$node$specifiers;
362
+ return ((_path$node$specifiers = path.node.specifiers) === null || _path$node$specifiers === void 0 ? void 0 : _path$node$specifiers.some(specifier => {
363
+ var _specifier$local, _specifier$local2, _specifier$local3;
364
+ switch (specifier.type) {
365
+ case 'ImportNamespaceSpecifier':
366
+ return ((_specifier$local = specifier.local) === null || _specifier$local === void 0 ? void 0 : _specifier$local.name) === targetName;
367
+ case 'ImportDefaultSpecifier':
368
+ return ((_specifier$local2 = specifier.local) === null || _specifier$local2 === void 0 ? void 0 : _specifier$local2.name) === targetName;
369
+ case 'ImportSpecifier':
370
+ return ((_specifier$local3 = specifier.local) === null || _specifier$local3 === void 0 ? void 0 : _specifier$local3.name) === targetName || specifier.imported.name === targetName;
371
+ default:
372
+ return false;
373
+ }
374
+ })) === true;
375
+ };
376
+ }
377
+ }