@atlaskit/codemod-cli 0.27.2 → 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.
- package/CHANGELOG.md +6 -0
- package/dist/cjs/main.js +85 -33
- package/dist/cjs/presets/index.js +2 -1
- package/dist/cjs/presets/remove-token-fallbacks/remove-token-fallbacks.js +225 -0
- package/dist/cjs/presets/remove-token-fallbacks/types.js +5 -0
- package/dist/cjs/presets/remove-token-fallbacks/utils/all-tokens.js +44 -0
- package/dist/cjs/presets/remove-token-fallbacks/utils/color-utils.js +93 -0
- package/dist/cjs/presets/remove-token-fallbacks/utils/get-team-info.js +51 -0
- package/dist/cjs/presets/remove-token-fallbacks/utils/normalize-values.js +113 -0
- package/dist/cjs/presets/remove-token-fallbacks/utils/remove-unused-imports.js +61 -0
- package/dist/cjs/presets/remove-token-fallbacks/utils/remove-unused-variables.js +37 -0
- package/dist/cjs/presets/remove-token-fallbacks/utils/reporter.js +310 -0
- package/dist/cjs/presets/remove-token-fallbacks/utils/token-processor.js +632 -0
- package/dist/cjs/presets/remove-token-fallbacks/utils/update-comments.js +58 -0
- package/dist/es2019/main.js +24 -0
- package/dist/es2019/presets/index.js +2 -1
- package/dist/es2019/presets/remove-token-fallbacks/remove-token-fallbacks.js +130 -0
- package/dist/es2019/presets/remove-token-fallbacks/types.js +1 -0
- package/dist/es2019/presets/remove-token-fallbacks/utils/all-tokens.js +30 -0
- package/dist/es2019/presets/remove-token-fallbacks/utils/color-utils.js +84 -0
- package/dist/es2019/presets/remove-token-fallbacks/utils/get-team-info.js +22 -0
- package/dist/es2019/presets/remove-token-fallbacks/utils/normalize-values.js +104 -0
- package/dist/es2019/presets/remove-token-fallbacks/utils/remove-unused-imports.js +51 -0
- package/dist/es2019/presets/remove-token-fallbacks/utils/remove-unused-variables.js +31 -0
- package/dist/es2019/presets/remove-token-fallbacks/utils/reporter.js +118 -0
- package/dist/es2019/presets/remove-token-fallbacks/utils/token-processor.js +377 -0
- package/dist/es2019/presets/remove-token-fallbacks/utils/update-comments.js +46 -0
- package/dist/esm/main.js +85 -33
- package/dist/esm/presets/index.js +2 -1
- package/dist/esm/presets/remove-token-fallbacks/remove-token-fallbacks.js +215 -0
- package/dist/esm/presets/remove-token-fallbacks/types.js +1 -0
- package/dist/esm/presets/remove-token-fallbacks/utils/all-tokens.js +38 -0
- package/dist/esm/presets/remove-token-fallbacks/utils/color-utils.js +86 -0
- package/dist/esm/presets/remove-token-fallbacks/utils/get-team-info.js +44 -0
- package/dist/esm/presets/remove-token-fallbacks/utils/normalize-values.js +107 -0
- package/dist/esm/presets/remove-token-fallbacks/utils/remove-unused-imports.js +55 -0
- package/dist/esm/presets/remove-token-fallbacks/utils/remove-unused-variables.js +31 -0
- package/dist/esm/presets/remove-token-fallbacks/utils/reporter.js +302 -0
- package/dist/esm/presets/remove-token-fallbacks/utils/token-processor.js +625 -0
- package/dist/esm/presets/remove-token-fallbacks/utils/update-comments.js +51 -0
- package/dist/types/presets/index.d.ts +1 -0
- package/dist/types/presets/remove-token-fallbacks/remove-token-fallbacks.d.ts +29 -0
- package/dist/types/presets/remove-token-fallbacks/types.d.ts +39 -0
- package/dist/types/presets/remove-token-fallbacks/utils/all-tokens.d.ts +1 -0
- package/dist/types/presets/remove-token-fallbacks/utils/color-utils.d.ts +3 -0
- package/dist/types/presets/remove-token-fallbacks/utils/get-team-info.d.ts +8 -0
- package/dist/types/presets/remove-token-fallbacks/utils/normalize-values.d.ts +8 -0
- package/dist/types/presets/remove-token-fallbacks/utils/remove-unused-imports.d.ts +2 -0
- package/dist/types/presets/remove-token-fallbacks/utils/remove-unused-variables.d.ts +2 -0
- package/dist/types/presets/remove-token-fallbacks/utils/reporter.d.ts +4 -0
- package/dist/types/presets/remove-token-fallbacks/utils/token-processor.d.ts +30 -0
- package/dist/types/presets/remove-token-fallbacks/utils/update-comments.d.ts +2 -0
- package/dist/types-ts4.5/presets/index.d.ts +1 -0
- package/dist/types-ts4.5/presets/remove-token-fallbacks/remove-token-fallbacks.d.ts +29 -0
- package/dist/types-ts4.5/presets/remove-token-fallbacks/types.d.ts +39 -0
- package/dist/types-ts4.5/presets/remove-token-fallbacks/utils/all-tokens.d.ts +1 -0
- package/dist/types-ts4.5/presets/remove-token-fallbacks/utils/color-utils.d.ts +3 -0
- package/dist/types-ts4.5/presets/remove-token-fallbacks/utils/get-team-info.d.ts +8 -0
- package/dist/types-ts4.5/presets/remove-token-fallbacks/utils/normalize-values.d.ts +8 -0
- package/dist/types-ts4.5/presets/remove-token-fallbacks/utils/remove-unused-imports.d.ts +2 -0
- package/dist/types-ts4.5/presets/remove-token-fallbacks/utils/remove-unused-variables.d.ts +2 -0
- package/dist/types-ts4.5/presets/remove-token-fallbacks/utils/reporter.d.ts +4 -0
- package/dist/types-ts4.5/presets/remove-token-fallbacks/utils/token-processor.d.ts +30 -0
- package/dist/types-ts4.5/presets/remove-token-fallbacks/utils/update-comments.d.ts +2 -0
- package/package.json +8 -3
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
4
|
+
Object.defineProperty(exports, "__esModule", {
|
|
5
|
+
value: true
|
|
6
|
+
});
|
|
7
|
+
exports.addOrUpdateEslintIgnoreComment = addOrUpdateEslintIgnoreComment;
|
|
8
|
+
var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
|
|
9
|
+
function addOrUpdateEslintIgnoreComment(j, tokenValue, fallbackValue, callPath) {
|
|
10
|
+
var commentText = "eslint-disable-next-line @atlaskit/design-system/no-unsafe-design-token-usage -- The token value \"".concat(tokenValue, "\" and fallback \"").concat(fallbackValue, "\" do not match and can't be replaced automatically.");
|
|
11
|
+
// first see if we can add the comment to the parent object property
|
|
12
|
+
var updatedCommentInObjectExpression = addOrUpdateEslintIgnoreCommentForParentInObjectExpression(j, commentText, callPath);
|
|
13
|
+
// if the comment was not added to the parent object property, add it to the node itself
|
|
14
|
+
if (!updatedCommentInObjectExpression) {
|
|
15
|
+
addOrUpdateEslintIgnoreCommentForNodeItself(j, commentText, callPath);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
function addOrUpdateEslintIgnoreCommentForNodeItself(j, commentText, callPath) {
|
|
19
|
+
var leadingComments = callPath.node.leadingComments || [];
|
|
20
|
+
var existingCommentIndex = leadingComments.findIndex(function (comment) {
|
|
21
|
+
return comment.value.includes('eslint-disable-next-line @atlaskit/design-system/no-unsafe-design-token-usage');
|
|
22
|
+
});
|
|
23
|
+
var commentLine = j.commentLine(commentText, true);
|
|
24
|
+
if (existingCommentIndex !== -1) {
|
|
25
|
+
// Replace the existing comment
|
|
26
|
+
// Note: in order to modify the comment, it's fine to update it in the `leadingComments` array
|
|
27
|
+
leadingComments[existingCommentIndex] = commentLine;
|
|
28
|
+
} else {
|
|
29
|
+
// Add a new comment if none exists
|
|
30
|
+
// Note: Adding new comment to 'leadingComments' doesn't affect anything, we need to add it to 'comments' property
|
|
31
|
+
callPath.node.comments = [commentLine];
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
function addOrUpdateEslintIgnoreCommentForParentInObjectExpression(j, commentText, callPath) {
|
|
35
|
+
var parent = callPath.parentPath;
|
|
36
|
+
// Check if the parent node is an ObjectProperty
|
|
37
|
+
if (parent && parent.node.type === 'ObjectProperty') {
|
|
38
|
+
var grandparent = parent.parentPath;
|
|
39
|
+
// Check if the grandparent is an ObjectExpression
|
|
40
|
+
if (grandparent && grandparent.node.type === 'ObjectExpression') {
|
|
41
|
+
// Check for existing leading comments
|
|
42
|
+
var leadingComments = parent.node.leadingComments || [];
|
|
43
|
+
var existingCommentIndex = leadingComments.findIndex(function (comment) {
|
|
44
|
+
return comment.value.includes('eslint-disable-next-line @atlaskit/design-system/no-unsafe-design-token-usage');
|
|
45
|
+
});
|
|
46
|
+
var commentLine = j.commentLine(commentText, true);
|
|
47
|
+
if (existingCommentIndex !== -1) {
|
|
48
|
+
// Replace the existing comment
|
|
49
|
+
leadingComments[existingCommentIndex] = commentLine;
|
|
50
|
+
} else {
|
|
51
|
+
// Add a new comment if none exists
|
|
52
|
+
parent.node.comments = [commentLine].concat((0, _toConsumableArray2.default)(parent.node.comments || []));
|
|
53
|
+
}
|
|
54
|
+
return true;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return false;
|
|
58
|
+
}
|
package/dist/es2019/main.js
CHANGED
|
@@ -106,9 +106,22 @@ const runTransform = async (filePaths, transform, flags) => {
|
|
|
106
106
|
const jscodeshiftContent = fs.readFileSync(jscodeshift, 'utf8');
|
|
107
107
|
const jscodeshiftContentNew = fixLineEnding(jscodeshiftContent, 'LF');
|
|
108
108
|
fs.writeFileSync(jscodeshift, jscodeshiftContentNew);
|
|
109
|
+
let transformModule;
|
|
110
|
+
try {
|
|
111
|
+
transformModule = require(transformPath);
|
|
112
|
+
} catch (error) {
|
|
113
|
+
// eslint-disable-next-line no-console
|
|
114
|
+
console.warn(`Error loading transform module: ${transformPath}. Skipping lifecycle hooks.`);
|
|
115
|
+
}
|
|
116
|
+
if (transformModule) {
|
|
117
|
+
await processLifecycleHook('beforeAll', transformModule, logger, transform, flags);
|
|
118
|
+
}
|
|
109
119
|
await spawn(jscodeshift, args, {
|
|
110
120
|
stdio: 'inherit'
|
|
111
121
|
});
|
|
122
|
+
if (transformModule) {
|
|
123
|
+
await processLifecycleHook('afterAll', transformModule, logger, transform, flags);
|
|
124
|
+
}
|
|
112
125
|
};
|
|
113
126
|
const parsePkg = pkg => {
|
|
114
127
|
if (!pkg.startsWith('@')) {
|
|
@@ -179,6 +192,17 @@ const defaultFlags = {
|
|
|
179
192
|
ignorePattern: 'node_modules',
|
|
180
193
|
logger: console
|
|
181
194
|
};
|
|
195
|
+
async function processLifecycleHook(hookName, transformModule, logger, transform, flags) {
|
|
196
|
+
if (typeof transformModule[hookName] === 'function') {
|
|
197
|
+
try {
|
|
198
|
+
logger.log(chalk.green(`Executing ${hookName} for transform '${transform.name}'...`));
|
|
199
|
+
await transformModule[hookName](flags);
|
|
200
|
+
} catch (error) {
|
|
201
|
+
logger.log(chalk.red(`Error in ${hookName} for transform '${transform.name}': ${error}`));
|
|
202
|
+
throw error; // Re-throw error for beforeAll to halt execution
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
182
206
|
export default async function main(input, userFlags) {
|
|
183
207
|
const flags = {
|
|
184
208
|
...defaultFlags,
|
|
@@ -10,5 +10,6 @@ import './migrate-to-link/migrate-to-link';
|
|
|
10
10
|
import './migrate-to-new-buttons/migrate-to-new-buttons';
|
|
11
11
|
import './upgrade-pragmatic-drag-and-drop-to-stable/upgrade-pragmatic-drag-and-drop-to-stable';
|
|
12
12
|
import './remove-dark-theme-vr-options/remove-dark-theme-vr-options';
|
|
13
|
-
|
|
13
|
+
import './remove-token-fallbacks/remove-token-fallbacks';
|
|
14
|
+
const presets = ['styled-to-emotion', 'theme-remove-deprecated-mixins', 'migrate-to-link', 'migrate-to-new-buttons', 'upgrade-pragmatic-drag-and-drop-to-stable', 'remove-dark-theme-vr-options', 'remove-token-fallbacks'].map(preset => path.join(__dirname, preset, `${preset}.@(ts|js|tsx)`));
|
|
14
15
|
export default presets;
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/* eslint-disable no-console */
|
|
2
|
+
import { exec } from 'child_process';
|
|
3
|
+
import fs from 'fs/promises';
|
|
4
|
+
import path from 'path';
|
|
5
|
+
import { promisify } from 'util';
|
|
6
|
+
import { hasImportDeclaration } from '@hypermod/utils';
|
|
7
|
+
import { findRoot } from '@manypkg/find-root';
|
|
8
|
+
import chalk from 'chalk';
|
|
9
|
+
import { getTokenMap } from './utils/all-tokens';
|
|
10
|
+
import { getTeamInfo } from './utils/get-team-info';
|
|
11
|
+
import { removeUnusedImports } from './utils/remove-unused-imports';
|
|
12
|
+
import { removeUnusedVariables } from './utils/remove-unused-variables';
|
|
13
|
+
import { clearFolder, combineReports, writeReports } from './utils/reporter';
|
|
14
|
+
import { TokenProcessor } from './utils/token-processor';
|
|
15
|
+
const execAsync = promisify(exec);
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Transforms the source code of a file by removing fallback values from the @atlaskit/tokens/token functions.
|
|
19
|
+
* By default removes only the fallbacks that have the same values as the tokens.
|
|
20
|
+
*
|
|
21
|
+
* @param {FileInfo} fileInfo - Information about the file to be transformed.
|
|
22
|
+
* @param {API} api - The jscodeshift API, providing utilities for AST transformations.
|
|
23
|
+
* @param {RemoveTokenFallbackOptions} options - Options for the transformation, including:
|
|
24
|
+
* @param {boolean} [options.verbose] - If true, enables verbose logging.
|
|
25
|
+
* @param {boolean} [options.forceUpdate] - If true, removes the fallbacks regardless of the difference between token and fallback. Otherwise removes only the fallbacks that are equal to the token values.
|
|
26
|
+
* @param {boolean} [options.addEslintComments] - If true, adds the eslint ignore comment for the rule @atlaskit/design-system/no-unsafe-design-token-usage for the fallbacks that weren't removed.
|
|
27
|
+
* @param {boolean} [options.useLegacyColorTheme] - If true, uses the legacy theme for color token mapping.
|
|
28
|
+
* @param {string} [options.reportFolder] - Directory path to output transformation reports. Reports will be generated only if this option is provided.
|
|
29
|
+
* @param {boolean} [options.dry] - If true, performs a dry run without modifying the files.
|
|
30
|
+
*
|
|
31
|
+
* @returns {Promise<string>} A promise that resolves to the transformed source code as a string.
|
|
32
|
+
*/
|
|
33
|
+
export default async function transformer(fileInfo, {
|
|
34
|
+
jscodeshift: j
|
|
35
|
+
}, options) {
|
|
36
|
+
var _options$useLegacyCol;
|
|
37
|
+
const rootDir = await findRoot(path.dirname(fileInfo.path));
|
|
38
|
+
const source = j(fileInfo.source);
|
|
39
|
+
if (!hasImportDeclaration(j, source, '@atlaskit/tokens')) {
|
|
40
|
+
return fileInfo.source;
|
|
41
|
+
}
|
|
42
|
+
const details = {
|
|
43
|
+
replaced: [],
|
|
44
|
+
notReplaced: []
|
|
45
|
+
};
|
|
46
|
+
if (options.verbose) {
|
|
47
|
+
console.log(chalk.yellow(`Using ${options.useLegacyColorTheme ? 'legacy light' : 'light'} theme.`));
|
|
48
|
+
}
|
|
49
|
+
const tokenMap = getTokenMap((_options$useLegacyCol = options.useLegacyColorTheme) !== null && _options$useLegacyCol !== void 0 ? _options$useLegacyCol : false);
|
|
50
|
+
const teamInfo = await getTeamInfo(fileInfo.path);
|
|
51
|
+
const transformPromises = source.find(j.CallExpression, {
|
|
52
|
+
callee: {
|
|
53
|
+
type: 'Identifier',
|
|
54
|
+
name: 'token'
|
|
55
|
+
}
|
|
56
|
+
}).paths().map(callPath => {
|
|
57
|
+
const processor = new TokenProcessor(j, options, fileInfo, source, rootDir, details, tokenMap, teamInfo);
|
|
58
|
+
return processor.processAndLogSingleToken(callPath);
|
|
59
|
+
});
|
|
60
|
+
const results = await Promise.all(transformPromises);
|
|
61
|
+
const unusedVars = [];
|
|
62
|
+
if (results.some(result => result.fallbackRemoved)) {
|
|
63
|
+
const allImports = results.flatMap(result => {
|
|
64
|
+
var _result$resolvedImpor;
|
|
65
|
+
return (_result$resolvedImpor = result.resolvedImportDeclaration) !== null && _result$resolvedImpor !== void 0 ? _result$resolvedImpor : [];
|
|
66
|
+
});
|
|
67
|
+
const allVars = results.flatMap(result => {
|
|
68
|
+
var _result$resolvedLocal;
|
|
69
|
+
return (_result$resolvedLocal = result.resolvedLocalVarDeclaration) !== null && _result$resolvedLocal !== void 0 ? _result$resolvedLocal : [];
|
|
70
|
+
});
|
|
71
|
+
unusedVars.push(...allVars);
|
|
72
|
+
if (allImports.length) {
|
|
73
|
+
if (options.verbose) {
|
|
74
|
+
console.log(chalk.green(`${fileInfo.path}: Some fallbacks were removed. Cleaning up ${allImports.length} imports.`));
|
|
75
|
+
}
|
|
76
|
+
removeUnusedImports(allImports, j);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
removeUnusedVariables(unusedVars, j);
|
|
80
|
+
if (options.reportFolder) {
|
|
81
|
+
await writeReports(details, options.reportFolder);
|
|
82
|
+
}
|
|
83
|
+
if (options.dry) {
|
|
84
|
+
if (options.verbose) {
|
|
85
|
+
console.log(chalk.cyan(`${fileInfo.path}: dry run mode active. Source was not modified.`));
|
|
86
|
+
}
|
|
87
|
+
// Return the unmodified source if dryRun is true
|
|
88
|
+
return fileInfo.source;
|
|
89
|
+
} else {
|
|
90
|
+
// Return the transformed source
|
|
91
|
+
return source.toSource();
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
export const parser = 'tsx';
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Function executed before all transformations to prepare the environment by clearing the report folder.
|
|
98
|
+
*/
|
|
99
|
+
export async function beforeAll(options) {
|
|
100
|
+
if (options.reportFolder) {
|
|
101
|
+
await clearFolder(options.reportFolder);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Function executed after all transformations to combine individual file reports into a comprehensive transformation report.
|
|
107
|
+
* It also applies prettier to the affected files.
|
|
108
|
+
*/
|
|
109
|
+
export async function afterAll(options) {
|
|
110
|
+
if (options.reportFolder) {
|
|
111
|
+
await combineReports(options.reportFolder);
|
|
112
|
+
}
|
|
113
|
+
if (options.reportFolder && !options.dry) {
|
|
114
|
+
try {
|
|
115
|
+
const filesTxtPath = path.join(options.reportFolder, 'files.txt');
|
|
116
|
+
const fileContent = await fs.readFile(filesTxtPath, 'utf-8');
|
|
117
|
+
if (fileContent.length > 0) {
|
|
118
|
+
console.log(`Running prettier on files: ${chalk.magenta(fileContent)}`);
|
|
119
|
+
await execAsync(`yarn prettier --write ${fileContent}`);
|
|
120
|
+
console.log(chalk.green(`Prettier was run successfully`));
|
|
121
|
+
}
|
|
122
|
+
} catch (error) {
|
|
123
|
+
if (error instanceof Error) {
|
|
124
|
+
console.error(chalk.red(`Unexpected error running Prettier: ${error.message}`));
|
|
125
|
+
} else {
|
|
126
|
+
console.error(chalk.red('An unknown error occurred while running Prettier.'));
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { legacyLightTokens as legacyLightTheme, light as lightTheme, shape as shapeTheme, spacing as spacingTheme, typographyAdg3 as typographyAdg3Theme } from '@atlaskit/tokens/tokens-raw';
|
|
2
|
+
const typographyGroups = ['typography', 'fontWeight', 'fontFamily'];
|
|
3
|
+
function buildCombinedMap(...arrays) {
|
|
4
|
+
const combinedMap = {};
|
|
5
|
+
arrays.forEach(array => {
|
|
6
|
+
array.forEach(token => {
|
|
7
|
+
combinedMap[token.cleanName] = token.value.toString();
|
|
8
|
+
});
|
|
9
|
+
});
|
|
10
|
+
return combinedMap;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// Filter the typography tokens based on predefined groups and exclusions
|
|
14
|
+
const typographyAdg3ThemeFiltered = typographyAdg3Theme.filter(token => typographyGroups.includes(token.attributes.group)).filter(token => token.cleanName !== 'font.body.UNSAFE_small');
|
|
15
|
+
|
|
16
|
+
// Cache array: [0] for light theme, [1] for legacy light theme
|
|
17
|
+
const tokenMapCache = [null, null];
|
|
18
|
+
|
|
19
|
+
// Function to get the token map with the desired theme.
|
|
20
|
+
// This should be used with the same value as the @atlaskit/tokens/babel-plugin is cofigured with for the given path.
|
|
21
|
+
// When there is no theme, the babel plugin would use the given theme to provide default token fallbacks. Therefore it's safe to remove fallbacks only when they match the fallbacks that would be generated by the babel plugin with the given theme.
|
|
22
|
+
// The default value is set to use the Light theme to match the behavior of the babel plugin (packages/design-system/tokens/src/babel-plugin/plugin.tsx).
|
|
23
|
+
export function getTokenMap(useLegacyTheme = false) {
|
|
24
|
+
const themeIndex = useLegacyTheme ? 1 : 0;
|
|
25
|
+
if (!tokenMapCache[themeIndex]) {
|
|
26
|
+
const selectedTheme = useLegacyTheme ? legacyLightTheme : lightTheme;
|
|
27
|
+
tokenMapCache[themeIndex] = buildCombinedMap(selectedTheme, spacingTheme, shapeTheme, typographyAdg3ThemeFiltered);
|
|
28
|
+
}
|
|
29
|
+
return tokenMapCache[themeIndex];
|
|
30
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { diff, rgb_to_lab } from 'color-diff';
|
|
2
|
+
|
|
3
|
+
// Compare hex values using a CIEDE2000 color difference algorithm
|
|
4
|
+
export const compareHex = (hex, hex2) => diff(rgb_to_lab(hexToRgbA(hex)), rgb_to_lab(hexToRgbA(hex2)));
|
|
5
|
+
function hexToRgbA(hex) {
|
|
6
|
+
// Remove the leading '#' if present
|
|
7
|
+
hex = hex.replace(/^#/, '');
|
|
8
|
+
// Parse the hex string
|
|
9
|
+
const r = parseInt(hex.substring(0, 2), 16);
|
|
10
|
+
const g = parseInt(hex.substring(2, 4), 16);
|
|
11
|
+
const b = parseInt(hex.substring(4, 6), 16);
|
|
12
|
+
const a = parseInt(hex.substring(6, 8), 16) / 255;
|
|
13
|
+
return {
|
|
14
|
+
R: r,
|
|
15
|
+
G: g,
|
|
16
|
+
B: b,
|
|
17
|
+
A: a
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
const namedColors = {
|
|
21
|
+
black: '#000000',
|
|
22
|
+
silver: '#C0C0C0',
|
|
23
|
+
gray: '#808080',
|
|
24
|
+
grey: '#808080',
|
|
25
|
+
pink: '#FFC0CB',
|
|
26
|
+
white: '#FFFFFF',
|
|
27
|
+
maroon: '#800000',
|
|
28
|
+
red: '#FF0000',
|
|
29
|
+
purple: '#800080',
|
|
30
|
+
fuchsia: '#FF00FF',
|
|
31
|
+
green: '#008000',
|
|
32
|
+
lime: '#00FF00',
|
|
33
|
+
olive: '#808000',
|
|
34
|
+
yellow: '#FFFF00',
|
|
35
|
+
navy: '#000080',
|
|
36
|
+
blue: '#0000FF',
|
|
37
|
+
teal: '#008080',
|
|
38
|
+
aqua: '#00FFFF'
|
|
39
|
+
};
|
|
40
|
+
export function isValidColor(color) {
|
|
41
|
+
// Check if it's a named color
|
|
42
|
+
if (namedColors[color.toLowerCase()]) {
|
|
43
|
+
return true;
|
|
44
|
+
}
|
|
45
|
+
// Check for hex colors (including those with alpha)
|
|
46
|
+
if (/^#([0-9A-F]{3}){1,2}([0-9A-F]{2})?$/i.test(color)) {
|
|
47
|
+
return true;
|
|
48
|
+
}
|
|
49
|
+
// Check for rgba() values
|
|
50
|
+
if (/^rgba?\(\s*\d+\s*,\s*\d+\s*,\s*\d+\s*(?:,\s*(?:0?\.)?\d+\s*)?\)$/i.test(color)) {
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
export function colorToHex(color) {
|
|
56
|
+
// Handle named colors
|
|
57
|
+
if (namedColors[color.toLowerCase()]) {
|
|
58
|
+
return namedColors[color.toLowerCase()].toUpperCase() + 'FF'; // Add full opacity
|
|
59
|
+
}
|
|
60
|
+
if (color.startsWith('#')) {
|
|
61
|
+
// If it's already a hex color
|
|
62
|
+
if (color.length === 7) {
|
|
63
|
+
// #RRGGBB format, add full opacity
|
|
64
|
+
return (color + 'FF').toUpperCase();
|
|
65
|
+
} else if (color.length === 9) {
|
|
66
|
+
// #RRGGBBAA format, return as is
|
|
67
|
+
return color.toUpperCase();
|
|
68
|
+
} else if (color.length === 4) {
|
|
69
|
+
// #RGB format, expand to #RRGGBBFF
|
|
70
|
+
return ('#' + color[1] + color[1] + color[2] + color[2] + color[3] + color[3] + 'FF').toUpperCase();
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
// For rgb() and rgba(), convert to hex
|
|
74
|
+
const match = color.match(/^rgba?\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*(?:,\s*(\d*\.?\d+)\s*)?\)$/i);
|
|
75
|
+
if (match) {
|
|
76
|
+
const r = parseInt(match[1], 10);
|
|
77
|
+
const g = parseInt(match[2], 10);
|
|
78
|
+
const b = parseInt(match[3], 10);
|
|
79
|
+
const a = match[4] !== undefined ? Math.round(parseFloat(match[4]) * 255) : 255;
|
|
80
|
+
return `#${((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1).padStart(6, '0')}${a.toString(16).padStart(2, '0')}`.toUpperCase();
|
|
81
|
+
}
|
|
82
|
+
// If conversion is not possible, return the original color
|
|
83
|
+
return color;
|
|
84
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import readPkgUp from 'read-pkg-up';
|
|
2
|
+
/**
|
|
3
|
+
* Looks for the closest package.json and gets team and package information from it.
|
|
4
|
+
*
|
|
5
|
+
* @param {string} filePath
|
|
6
|
+
* @returns {Promise<AtlassianTeamInfo>}
|
|
7
|
+
*/
|
|
8
|
+
export const getTeamInfo = async filePath => {
|
|
9
|
+
var _pkgJson$packageJson$;
|
|
10
|
+
const pkgJson = await readPkgUp({
|
|
11
|
+
cwd: filePath
|
|
12
|
+
});
|
|
13
|
+
if (!pkgJson || !pkgJson.packageJson) {
|
|
14
|
+
throw new Error(`Closest package.json file could not be found or is invalid for ${filePath}.`);
|
|
15
|
+
}
|
|
16
|
+
const packageName = pkgJson.packageJson.name || '';
|
|
17
|
+
const teamName = ((_pkgJson$packageJson$ = pkgJson.packageJson.atlassian) === null || _pkgJson$packageJson$ === void 0 ? void 0 : _pkgJson$packageJson$.team) || '';
|
|
18
|
+
return {
|
|
19
|
+
packageName,
|
|
20
|
+
teamName
|
|
21
|
+
};
|
|
22
|
+
};
|
|
@@ -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
|
+
}
|