@atlaskit/codemod-cli 0.28.0 → 0.28.1

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 (21) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/dist/cjs/main.js +3 -3
  3. package/dist/cjs/presets/remove-token-fallbacks/remove-token-fallbacks.js +69 -13
  4. package/dist/cjs/presets/remove-token-fallbacks/utils/normalize-values.js +14 -9
  5. package/dist/cjs/presets/remove-token-fallbacks/utils/token-processor.js +55 -33
  6. package/dist/es2019/presets/remove-token-fallbacks/remove-token-fallbacks.js +44 -2
  7. package/dist/es2019/presets/remove-token-fallbacks/utils/normalize-values.js +14 -9
  8. package/dist/es2019/presets/remove-token-fallbacks/utils/token-processor.js +43 -15
  9. package/dist/esm/main.js +3 -3
  10. package/dist/esm/presets/remove-token-fallbacks/remove-token-fallbacks.js +69 -13
  11. package/dist/esm/presets/remove-token-fallbacks/utils/normalize-values.js +14 -9
  12. package/dist/esm/presets/remove-token-fallbacks/utils/token-processor.js +55 -33
  13. package/dist/types/presets/remove-token-fallbacks/remove-token-fallbacks.d.ts +8 -0
  14. package/dist/types/presets/remove-token-fallbacks/types.d.ts +8 -0
  15. package/dist/types/presets/remove-token-fallbacks/utils/normalize-values.d.ts +2 -1
  16. package/dist/types/presets/remove-token-fallbacks/utils/token-processor.d.ts +6 -0
  17. package/dist/types-ts4.5/presets/remove-token-fallbacks/remove-token-fallbacks.d.ts +8 -0
  18. package/dist/types-ts4.5/presets/remove-token-fallbacks/types.d.ts +8 -0
  19. package/dist/types-ts4.5/presets/remove-token-fallbacks/utils/normalize-values.d.ts +2 -1
  20. package/dist/types-ts4.5/presets/remove-token-fallbacks/utils/token-processor.d.ts +6 -0
  21. package/package.json +3 -3
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # @atlaskit/codemod-cli
2
2
 
3
+ ## 0.28.1
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies
8
+
3
9
  ## 0.28.0
4
10
 
5
11
  ### Minor Changes
package/dist/cjs/main.js CHANGED
@@ -314,7 +314,7 @@ var defaultFlags = {
314
314
  ignorePattern: 'node_modules',
315
315
  logger: console
316
316
  };
317
- function processLifecycleHook(_x9, _x10, _x11, _x12, _x13) {
317
+ function processLifecycleHook(_x9, _x0, _x1, _x10, _x11) {
318
318
  return _processLifecycleHook.apply(this, arguments);
319
319
  }
320
320
  function _processLifecycleHook() {
@@ -346,7 +346,7 @@ function _processLifecycleHook() {
346
346
  }));
347
347
  return _processLifecycleHook.apply(this, arguments);
348
348
  }
349
- function main(_x14, _x15) {
349
+ function main(_x12, _x13) {
350
350
  return _main.apply(this, arguments);
351
351
  }
352
352
  function _main() {
@@ -362,7 +362,7 @@ function _main() {
362
362
  case 4:
363
363
  _yield$parseArgs = _context6.sent;
364
364
  packages = _yield$parseArgs.packages;
365
- _process$env$_PACKAGE = "0.28.0", _PACKAGE_VERSION_ = _process$env$_PACKAGE === void 0 ? '0.0.0-dev' : _process$env$_PACKAGE;
365
+ _process$env$_PACKAGE = "0.28.1", _PACKAGE_VERSION_ = _process$env$_PACKAGE === void 0 ? '0.0.0-dev' : _process$env$_PACKAGE;
366
366
  logger.log(_chalk.default.bgBlue(_chalk.default.black("\uD83D\uDCDA Atlassian-Frontend codemod library @ ".concat(_PACKAGE_VERSION_, " \uD83D\uDCDA"))));
367
367
  if (packages && packages.length > 0) {
368
368
  logger.log(_chalk.default.gray("Searching for codemods for newer versions of the following packages: ".concat(packages.map(function (pkg) {
@@ -44,6 +44,14 @@ var execAsync = (0, _util.promisify)(_child_process.exec);
44
44
  * @param {boolean} [options.useLegacyColorTheme] - If true, uses the legacy theme for color token mapping.
45
45
  * @param {string} [options.reportFolder] - Directory path to output transformation reports. Reports will be generated only if this option is provided.
46
46
  * @param {boolean} [options.dry] - If true, performs a dry run without modifying the files.
47
+ * @param {string} [options.skipTokens] - A comma-separated list of token prefixes to exempt from automatic fallback removal. By default, 'border' tokens are always included in this list. Whether fallbacks for these tokens are removed when they exactly match depends on the preserveSkippedFallbacks option.
48
+ * @param {boolean} [options.preserveSkippedFallbacks] - If true, fallbacks for skipped tokens will never be removed, even if they exactly match the token value. If false (default), fallbacks for skipped tokens will be removed if they exactly match.
49
+ * @param {boolean} [options.skipEslint] - If true, skips running ESLint on modified files after transformation.
50
+ * @param {boolean} [options.skipPrettier] - If true, skips running Prettier on modified files after transformation.
51
+ * @param {number} [options.colorDifferenceThreshold] - The maximum allowed difference for color tokens to be considered acceptable for removal. Default is 15.
52
+ * @param {number} [options.spaceDifferenceThreshold] - The maximum allowed percentage difference for space tokens to be considered acceptable for removal. Default is 0.
53
+ * @param {number} [options.numericDifferenceThreshold] - The maximum allowed percentage difference for numeric tokens to be considered acceptable for removal. Default is 0.
54
+ * @param {number} [options.borderDifferenceThreshold] - The maximum allowed percentage difference for border tokens to be considered acceptable for removal. Default is 0.
47
55
  *
48
56
  * @returns {Promise<string>} A promise that resolves to the transformed source code as a string.
49
57
  */
@@ -75,6 +83,32 @@ function _transformer() {
75
83
  };
76
84
  if (options.verbose) {
77
85
  console.log(_chalk.default.yellow("Using ".concat(options.useLegacyColorTheme ? 'legacy light' : 'light', " theme.")));
86
+ if (options.skipTokens) {
87
+ console.log(_chalk.default.yellow("Auto fallback exemptions active for: ".concat(options.skipTokens)));
88
+ }
89
+ if (options.preserveSkippedFallbacks) {
90
+ console.log(_chalk.default.yellow("Preserving all fallbacks for skipped tokens, even if they match exactly."));
91
+ }
92
+ if (options.skipEslint) {
93
+ console.log(_chalk.default.yellow("Skipping ESLint post-processing."));
94
+ }
95
+ if (options.skipPrettier) {
96
+ console.log(_chalk.default.yellow("Skipping Prettier post-processing."));
97
+ }
98
+
99
+ // Log threshold values if they are set
100
+ if (options.colorDifferenceThreshold !== undefined) {
101
+ console.log(_chalk.default.yellow("Color difference threshold set to: ".concat(options.colorDifferenceThreshold)));
102
+ }
103
+ if (options.spaceDifferenceThreshold !== undefined) {
104
+ console.log(_chalk.default.yellow("Space difference threshold set to: ".concat(options.spaceDifferenceThreshold, "%")));
105
+ }
106
+ if (options.numericDifferenceThreshold !== undefined) {
107
+ console.log(_chalk.default.yellow("Numeric difference threshold set to: ".concat(options.numericDifferenceThreshold, "%")));
108
+ }
109
+ if (options.borderDifferenceThreshold !== undefined) {
110
+ console.log(_chalk.default.yellow("Border difference threshold set to: ".concat(options.borderDifferenceThreshold, "%")));
111
+ }
78
112
  }
79
113
  tokenMap = (0, _allTokens.getTokenMap)((_options$useLegacyCol = options.useLegacyColorTheme) !== null && _options$useLegacyCol !== void 0 ? _options$useLegacyCol : false);
80
114
  _context.next = 12;
@@ -198,7 +232,7 @@ function _afterAll() {
198
232
  return (0, _reporter.combineReports)(options.reportFolder);
199
233
  case 3:
200
234
  if (!(options.reportFolder && !options.dry)) {
201
- _context3.next = 27;
235
+ _context3.next = 35;
202
236
  break;
203
237
  }
204
238
  _context3.prev = 4;
@@ -208,7 +242,7 @@ function _afterAll() {
208
242
  case 8:
209
243
  fileContent = _context3.sent;
210
244
  if (!(fileContent.length > 0)) {
211
- _context3.next = 22;
245
+ _context3.next = 30;
212
246
  break;
213
247
  }
214
248
  filePaths = fileContent.split(/\r?\n/).filter(Boolean); // Get the first file path and strip any quotes
@@ -221,27 +255,49 @@ function _afterAll() {
221
255
  _context3.next = 18;
222
256
  return gitStage(filePaths, rootDir);
223
257
  case 18:
224
- _context3.next = 20;
258
+ if (options.skipEslint) {
259
+ _context3.next = 23;
260
+ break;
261
+ }
262
+ _context3.next = 21;
225
263
  return runEslint(filePaths, rootDir);
226
- case 20:
227
- _context3.next = 22;
228
- return runPrettier(filePaths, rootDir);
229
- case 22:
230
- _context3.next = 27;
264
+ case 21:
265
+ _context3.next = 24;
231
266
  break;
267
+ case 23:
268
+ if (options.verbose) {
269
+ console.log(_chalk.default.blue('Skipping ESLint post-processing as requested.'));
270
+ }
232
271
  case 24:
233
- _context3.prev = 24;
272
+ if (options.skipPrettier) {
273
+ _context3.next = 29;
274
+ break;
275
+ }
276
+ _context3.next = 27;
277
+ return runPrettier(filePaths, rootDir);
278
+ case 27:
279
+ _context3.next = 30;
280
+ break;
281
+ case 29:
282
+ if (options.verbose) {
283
+ console.log(_chalk.default.blue('Skipping Prettier post-processing as requested.'));
284
+ }
285
+ case 30:
286
+ _context3.next = 35;
287
+ break;
288
+ case 32:
289
+ _context3.prev = 32;
234
290
  _context3.t0 = _context3["catch"](4);
235
291
  if (_context3.t0 instanceof Error) {
236
292
  console.error(_chalk.default.red("Unexpected error: ".concat(_context3.t0.message)));
237
293
  } else {
238
294
  console.error(_chalk.default.red('An unknown error occurred.'));
239
295
  }
240
- case 27:
296
+ case 35:
241
297
  case "end":
242
298
  return _context3.stop();
243
299
  }
244
- }, _callee3, null, [[4, 24]]);
300
+ }, _callee3, null, [[4, 32]]);
245
301
  }));
246
302
  return _afterAll.apply(this, arguments);
247
303
  }
@@ -313,7 +369,7 @@ function _runPrettier() {
313
369
  }));
314
370
  return _runPrettier.apply(this, arguments);
315
371
  }
316
- function runEslint(_x10, _x11) {
372
+ function runEslint(_x0, _x1) {
317
373
  return _runEslint.apply(this, arguments);
318
374
  }
319
375
  function _runEslint() {
@@ -404,7 +460,7 @@ function _runEslint() {
404
460
  }
405
461
  }, _callee6, null, [[0, 11]]);
406
462
  }));
407
- return function (_x12) {
463
+ return function (_x10) {
408
464
  return _ref2.apply(this, arguments);
409
465
  };
410
466
  }()));
@@ -8,12 +8,17 @@ exports.normalizeValues = normalizeValues;
8
8
  var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
9
9
  var _chalk = _interopRequireDefault(require("chalk"));
10
10
  var _colorUtils = require("./color-utils");
11
- // so far allowing to remove only exact matches in values. Can be increased to auto-remove fallbacks with similar values
12
- var ACCEPTABLE_COLOR_DIFFERENCE = 15;
13
- var ACCEPTABLE_SPACE_DIFFERENCE = 0;
14
- var ACCEPTABLE_NUMERIC_DIFFERENCE = 0;
15
- var ACCEPTABLE_BORDER_DIFFERENCE = 0;
16
- function normalizeValues(tokenKey, tokenValue, fallbackValue) {
11
+ // Default threshold values
12
+ var DEFAULT_COLOR_DIFFERENCE = 15;
13
+ var DEFAULT_SPACE_DIFFERENCE = 0;
14
+ var DEFAULT_NUMERIC_DIFFERENCE = 0;
15
+ var DEFAULT_BORDER_DIFFERENCE = 0;
16
+ function normalizeValues(tokenKey, tokenValue, fallbackValue, options) {
17
+ // Use options thresholds or defaults
18
+ var colorDifference = (options === null || options === void 0 ? void 0 : options.colorDifferenceThreshold) !== undefined ? options.colorDifferenceThreshold : DEFAULT_COLOR_DIFFERENCE;
19
+ var spaceDifference = (options === null || options === void 0 ? void 0 : options.spaceDifferenceThreshold) !== undefined ? options.spaceDifferenceThreshold : DEFAULT_SPACE_DIFFERENCE;
20
+ var numericDifference = (options === null || options === void 0 ? void 0 : options.numericDifferenceThreshold) !== undefined ? options.numericDifferenceThreshold : DEFAULT_NUMERIC_DIFFERENCE;
21
+ var borderDifference = (options === null || options === void 0 ? void 0 : options.borderDifferenceThreshold) !== undefined ? options.borderDifferenceThreshold : DEFAULT_BORDER_DIFFERENCE;
17
22
  var tokenLogValue;
18
23
  var fallbackLogValue;
19
24
  var normalizedTokenValue = tokenValue;
@@ -34,7 +39,7 @@ function normalizeValues(tokenKey, tokenValue, fallbackValue) {
34
39
  }
35
40
  if (normalizedTokenValue && normalizedFallbackValue) {
36
41
  difference = (0, _colorUtils.compareHex)(normalizedTokenValue, normalizedFallbackValue);
37
- isAcceptableDifference = difference <= ACCEPTABLE_COLOR_DIFFERENCE;
42
+ isAcceptableDifference = difference <= colorDifference;
38
43
  }
39
44
  } else if (lowerCaseTokenKey.startsWith('space') || lowerCaseTokenKey.startsWith('border')) {
40
45
  var tokenValueInPx = tokenValue ? convertToPx(tokenValue) : undefined;
@@ -42,7 +47,7 @@ function normalizeValues(tokenKey, tokenValue, fallbackValue) {
42
47
  if (tokenValueInPx !== undefined && fallbackValueInPx !== undefined) {
43
48
  var maxVal = Math.max(tokenValueInPx, fallbackValueInPx);
44
49
  difference = Math.abs(tokenValueInPx - fallbackValueInPx) / maxVal * 100;
45
- isAcceptableDifference = difference <= (lowerCaseTokenKey.startsWith('space') ? ACCEPTABLE_SPACE_DIFFERENCE : ACCEPTABLE_BORDER_DIFFERENCE);
50
+ isAcceptableDifference = difference <= (lowerCaseTokenKey.startsWith('space') ? spaceDifference : borderDifference);
46
51
  }
47
52
  // Log the normalized values
48
53
  normalizedTokenValue = tokenValue;
@@ -56,7 +61,7 @@ function normalizeValues(tokenKey, tokenValue, fallbackValue) {
56
61
  if (!isNaN(tokenValueNumber) && !isNaN(fallbackValueNumber)) {
57
62
  var _maxVal = Math.max(tokenValueNumber, fallbackValueNumber);
58
63
  difference = Math.abs(tokenValueNumber - fallbackValueNumber) / _maxVal * 100;
59
- isAcceptableDifference = difference <= ACCEPTABLE_NUMERIC_DIFFERENCE;
64
+ isAcceptableDifference = difference <= numericDifference;
60
65
  }
61
66
  // Log the normalized values
62
67
  normalizedTokenValue = tokenValue;
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", {
6
6
  });
7
7
  exports.TokenProcessor = void 0;
8
8
  var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));
9
+ var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
9
10
  var _objectWithoutProperties2 = _interopRequireDefault(require("@babel/runtime/helpers/objectWithoutProperties"));
10
11
  var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));
11
12
  var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
@@ -89,12 +90,43 @@ var TokenProcessor = exports.TokenProcessor = /*#__PURE__*/function () {
89
90
  value: function logError(message) {
90
91
  this.log(_chalk.default.red(message));
91
92
  }
93
+
94
+ /**
95
+ * Checks if a token should be exempted from automatic fallback removal
96
+ * @param tokenKey The token key to check
97
+ * @returns An object containing whether the token should be exempted and related information
98
+ */
99
+ }, {
100
+ key: "checkTokenExemption",
101
+ value: function checkTokenExemption(tokenKey) {
102
+ // Create exemption list from user-provided skipTokens, and always include 'border'
103
+ var userExemptions = this.options.skipTokens ? this.options.skipTokens.split(',').map(function (item) {
104
+ return item.trim();
105
+ }) : [];
106
+
107
+ // Always include 'border' in the exemption list
108
+ var exemptionList = (0, _toConsumableArray2.default)(userExemptions);
109
+ if (!exemptionList.includes('border')) {
110
+ exemptionList.push('border');
111
+ }
112
+ var isExemptedToken = exemptionList.some(function (prefix) {
113
+ return tokenKey.startsWith(prefix);
114
+ });
115
+ var exemptedPrefix = isExemptedToken ? exemptionList.find(function (prefix) {
116
+ return tokenKey.startsWith(prefix);
117
+ }) || null : null;
118
+ return {
119
+ shouldBeExempted: isExemptedToken,
120
+ exemptedPrefix: exemptedPrefix,
121
+ exemptionList: exemptionList
122
+ };
123
+ }
92
124
  }, {
93
125
  key: "processSingleToken",
94
126
  value: function () {
95
127
  var _processSingleToken = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee2(callPath) {
96
128
  var _callPath$node$loc2;
97
- var args, line, tokenKey, isSkipped, tokenValue, _ref, rawFallbackValue, fallbackValue, resolvedImportDeclaration, resolvedLocalVarDeclaration, _normalizeValues, difference, isAcceptableDifference, tokenLogValue, fallbackLogValue, normalizedTokenValue, normalizedFallbackValue, areEqual, logData, fallbackRemoved, importDeclaration, localVarDeclaration, isBorderToken, message;
129
+ var args, line, tokenKey, tokenValue, _yield$this$getFallba, rawFallbackValue, fallbackValue, resolvedImportDeclaration, resolvedLocalVarDeclaration, _normalizeValues, difference, isAcceptableDifference, tokenLogValue, fallbackLogValue, normalizedTokenValue, normalizedFallbackValue, areEqual, logData, fallbackRemoved, importDeclaration, localVarDeclaration, _this$checkTokenExemp, shouldBeExempted, exemptedPrefix, shouldModifyToken, message;
98
130
  return _regenerator.default.wrap(function _callee2$(_context2) {
99
131
  while (1) switch (_context2.prev = _context2.next) {
100
132
  case 0:
@@ -124,35 +156,17 @@ var TokenProcessor = exports.TokenProcessor = /*#__PURE__*/function () {
124
156
  resolvedLocalVarDeclaration: undefined
125
157
  });
126
158
  case 8:
127
- isSkipped = false; // tokenKey.startsWith('elevation.shadow') ||
128
- // tokenKey.startsWith('font.body') ||
129
- // tokenKey.startsWith('font.heading');
130
- tokenValue = isSkipped ? '' : this.tokenMap[tokenKey];
159
+ tokenValue = this.tokenMap[tokenKey];
131
160
  this.logVerbose("Token value from tokenMap: ".concat(_chalk.default.magenta(tokenValue), " for key: ").concat(_chalk.default.yellow(tokenKey)));
132
- if (!isSkipped) {
133
- _context2.next = 15;
134
- break;
135
- }
136
- _context2.t0 = {
137
- rawFallbackValue: 'N/A',
138
- fallbackValue: undefined,
139
- resolvedImportDeclaration: undefined,
140
- resolvedLocalVarDeclaration: undefined
141
- };
142
- _context2.next = 18;
143
- break;
144
- case 15:
145
- _context2.next = 17;
161
+ _context2.next = 12;
146
162
  return this.getFallbackValue(args[1]);
147
- case 17:
148
- _context2.t0 = _context2.sent;
149
- case 18:
150
- _ref = _context2.t0;
151
- rawFallbackValue = _ref.rawFallbackValue;
152
- fallbackValue = _ref.fallbackValue;
153
- resolvedImportDeclaration = _ref.resolvedImportDeclaration;
154
- resolvedLocalVarDeclaration = _ref.resolvedLocalVarDeclaration;
155
- _normalizeValues = (0, _normalizeValues2.normalizeValues)(tokenKey, tokenValue, fallbackValue), difference = _normalizeValues.difference, isAcceptableDifference = _normalizeValues.isAcceptableDifference, tokenLogValue = _normalizeValues.tokenLogValue, fallbackLogValue = _normalizeValues.fallbackLogValue, normalizedTokenValue = _normalizeValues.normalizedTokenValue, normalizedFallbackValue = _normalizeValues.normalizedFallbackValue;
163
+ case 12:
164
+ _yield$this$getFallba = _context2.sent;
165
+ rawFallbackValue = _yield$this$getFallba.rawFallbackValue;
166
+ fallbackValue = _yield$this$getFallba.fallbackValue;
167
+ resolvedImportDeclaration = _yield$this$getFallba.resolvedImportDeclaration;
168
+ resolvedLocalVarDeclaration = _yield$this$getFallba.resolvedLocalVarDeclaration;
169
+ _normalizeValues = (0, _normalizeValues2.normalizeValues)(tokenKey, tokenValue, fallbackValue, this.options), difference = _normalizeValues.difference, isAcceptableDifference = _normalizeValues.isAcceptableDifference, tokenLogValue = _normalizeValues.tokenLogValue, fallbackLogValue = _normalizeValues.fallbackLogValue, normalizedTokenValue = _normalizeValues.normalizedTokenValue, normalizedFallbackValue = _normalizeValues.normalizedFallbackValue;
156
170
  areEqual = normalizedTokenValue === normalizedFallbackValue;
157
171
  logData = {
158
172
  teamInfo: this.teamInfo,
@@ -165,8 +179,16 @@ var TokenProcessor = exports.TokenProcessor = /*#__PURE__*/function () {
165
179
  difference: difference
166
180
  };
167
181
  fallbackRemoved = false;
168
- isBorderToken = tokenKey.startsWith('border');
169
- if (areEqual || (isAcceptableDifference || this.options.forceUpdate) && !isBorderToken) {
182
+ // Check if token should be exempted
183
+ _this$checkTokenExemp = this.checkTokenExemption(tokenKey), shouldBeExempted = _this$checkTokenExemp.shouldBeExempted, exemptedPrefix = _this$checkTokenExemp.exemptedPrefix; // Determine if we should modify this token based on the exemption status and settings
184
+ shouldModifyToken =
185
+ // Always modify if not exempted and values match
186
+ areEqual && !shouldBeExempted ||
187
+ // Or if values don't exactly match but are acceptable to modify and not exempted
188
+ (isAcceptableDifference || this.options.forceUpdate) && !shouldBeExempted ||
189
+ // Or if exempted but values match exactly and we're not preserving skipped fallbacks
190
+ areEqual && shouldBeExempted && !this.options.preserveSkippedFallbacks;
191
+ if (shouldModifyToken) {
170
192
  this.log(_chalk.default.green(areEqual ? 'Token value and fallback value are equal, removing fallback' : 'Token value and fallback value are within acceptable difference threshold, removing fallback'));
171
193
  args.pop();
172
194
  this.details.replaced.push(logData);
@@ -174,7 +196,7 @@ var TokenProcessor = exports.TokenProcessor = /*#__PURE__*/function () {
174
196
  importDeclaration = resolvedImportDeclaration;
175
197
  localVarDeclaration = resolvedLocalVarDeclaration;
176
198
  } else {
177
- message = isBorderToken ? 'Skip modifying border token' : normalizedFallbackValue === undefined ? "Fallback value could not be resolved" : "Values mismatched significantly";
199
+ message = shouldBeExempted ? this.options.preserveSkippedFallbacks && areEqual ? "Preserving fallback for exempted token '".concat(tokenKey, "' (matches exemption '").concat(exemptedPrefix, "')") : "Skip modifying exempted token '".concat(tokenKey, "' (matches exemption '").concat(exemptedPrefix, "')") : normalizedFallbackValue === undefined ? "Fallback value could not be resolved" : "Values mismatched significantly";
178
200
  this.logError(message);
179
201
  if (this.options.addEslintComments) {
180
202
  (0, _updateComments.addOrUpdateEslintIgnoreComment)(this.j, tokenValue, fallbackValue, callPath);
@@ -188,7 +210,7 @@ var TokenProcessor = exports.TokenProcessor = /*#__PURE__*/function () {
188
210
  resolvedImportDeclaration: importDeclaration,
189
211
  resolvedLocalVarDeclaration: localVarDeclaration
190
212
  });
191
- case 31:
213
+ case 26:
192
214
  case "end":
193
215
  return _context2.stop();
194
216
  }
@@ -711,7 +733,7 @@ var TokenProcessor = exports.TokenProcessor = /*#__PURE__*/function () {
711
733
  }
712
734
  }, _callee8, this);
713
735
  }));
714
- function resolveValueFromImport(_x9, _x10, _x11, _x12) {
736
+ function resolveValueFromImport(_x9, _x0, _x1, _x10) {
715
737
  return _resolveValueFromImport.apply(this, arguments);
716
738
  }
717
739
  return resolveValueFromImport;
@@ -28,6 +28,14 @@ const execAsync = promisify(exec);
28
28
  * @param {boolean} [options.useLegacyColorTheme] - If true, uses the legacy theme for color token mapping.
29
29
  * @param {string} [options.reportFolder] - Directory path to output transformation reports. Reports will be generated only if this option is provided.
30
30
  * @param {boolean} [options.dry] - If true, performs a dry run without modifying the files.
31
+ * @param {string} [options.skipTokens] - A comma-separated list of token prefixes to exempt from automatic fallback removal. By default, 'border' tokens are always included in this list. Whether fallbacks for these tokens are removed when they exactly match depends on the preserveSkippedFallbacks option.
32
+ * @param {boolean} [options.preserveSkippedFallbacks] - If true, fallbacks for skipped tokens will never be removed, even if they exactly match the token value. If false (default), fallbacks for skipped tokens will be removed if they exactly match.
33
+ * @param {boolean} [options.skipEslint] - If true, skips running ESLint on modified files after transformation.
34
+ * @param {boolean} [options.skipPrettier] - If true, skips running Prettier on modified files after transformation.
35
+ * @param {number} [options.colorDifferenceThreshold] - The maximum allowed difference for color tokens to be considered acceptable for removal. Default is 15.
36
+ * @param {number} [options.spaceDifferenceThreshold] - The maximum allowed percentage difference for space tokens to be considered acceptable for removal. Default is 0.
37
+ * @param {number} [options.numericDifferenceThreshold] - The maximum allowed percentage difference for numeric tokens to be considered acceptable for removal. Default is 0.
38
+ * @param {number} [options.borderDifferenceThreshold] - The maximum allowed percentage difference for border tokens to be considered acceptable for removal. Default is 0.
31
39
  *
32
40
  * @returns {Promise<string>} A promise that resolves to the transformed source code as a string.
33
41
  */
@@ -46,6 +54,32 @@ export default async function transformer(fileInfo, {
46
54
  };
47
55
  if (options.verbose) {
48
56
  console.log(chalk.yellow(`Using ${options.useLegacyColorTheme ? 'legacy light' : 'light'} theme.`));
57
+ if (options.skipTokens) {
58
+ console.log(chalk.yellow(`Auto fallback exemptions active for: ${options.skipTokens}`));
59
+ }
60
+ if (options.preserveSkippedFallbacks) {
61
+ console.log(chalk.yellow(`Preserving all fallbacks for skipped tokens, even if they match exactly.`));
62
+ }
63
+ if (options.skipEslint) {
64
+ console.log(chalk.yellow(`Skipping ESLint post-processing.`));
65
+ }
66
+ if (options.skipPrettier) {
67
+ console.log(chalk.yellow(`Skipping Prettier post-processing.`));
68
+ }
69
+
70
+ // Log threshold values if they are set
71
+ if (options.colorDifferenceThreshold !== undefined) {
72
+ console.log(chalk.yellow(`Color difference threshold set to: ${options.colorDifferenceThreshold}`));
73
+ }
74
+ if (options.spaceDifferenceThreshold !== undefined) {
75
+ console.log(chalk.yellow(`Space difference threshold set to: ${options.spaceDifferenceThreshold}%`));
76
+ }
77
+ if (options.numericDifferenceThreshold !== undefined) {
78
+ console.log(chalk.yellow(`Numeric difference threshold set to: ${options.numericDifferenceThreshold}%`));
79
+ }
80
+ if (options.borderDifferenceThreshold !== undefined) {
81
+ console.log(chalk.yellow(`Border difference threshold set to: ${options.borderDifferenceThreshold}%`));
82
+ }
49
83
  }
50
84
  const tokenMap = getTokenMap((_options$useLegacyCol = options.useLegacyColorTheme) !== null && _options$useLegacyCol !== void 0 ? _options$useLegacyCol : false);
51
85
  const teamInfo = await getTeamInfo(fileInfo.path);
@@ -130,8 +164,16 @@ export async function afterAll(options) {
130
164
  const rootDir = await findRoot(path.dirname(firstFilePath));
131
165
  console.log('Root directory:', rootDir);
132
166
  await gitStage(filePaths, rootDir);
133
- await runEslint(filePaths, rootDir);
134
- await runPrettier(filePaths, rootDir);
167
+ if (!options.skipEslint) {
168
+ await runEslint(filePaths, rootDir);
169
+ } else if (options.verbose) {
170
+ console.log(chalk.blue('Skipping ESLint post-processing as requested.'));
171
+ }
172
+ if (!options.skipPrettier) {
173
+ await runPrettier(filePaths, rootDir);
174
+ } else if (options.verbose) {
175
+ console.log(chalk.blue('Skipping Prettier post-processing as requested.'));
176
+ }
135
177
  }
136
178
  } catch (error) {
137
179
  if (error instanceof Error) {
@@ -1,12 +1,17 @@
1
1
  import chalk from 'chalk';
2
2
  import { colorToHex, compareHex, isValidColor } from './color-utils';
3
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 = 15;
6
- const ACCEPTABLE_SPACE_DIFFERENCE = 0;
7
- const ACCEPTABLE_NUMERIC_DIFFERENCE = 0;
8
- const ACCEPTABLE_BORDER_DIFFERENCE = 0;
9
- export function normalizeValues(tokenKey, tokenValue, fallbackValue) {
4
+ // Default threshold values
5
+ const DEFAULT_COLOR_DIFFERENCE = 15;
6
+ const DEFAULT_SPACE_DIFFERENCE = 0;
7
+ const DEFAULT_NUMERIC_DIFFERENCE = 0;
8
+ const DEFAULT_BORDER_DIFFERENCE = 0;
9
+ export function normalizeValues(tokenKey, tokenValue, fallbackValue, options) {
10
+ // Use options thresholds or defaults
11
+ const colorDifference = (options === null || options === void 0 ? void 0 : options.colorDifferenceThreshold) !== undefined ? options.colorDifferenceThreshold : DEFAULT_COLOR_DIFFERENCE;
12
+ const spaceDifference = (options === null || options === void 0 ? void 0 : options.spaceDifferenceThreshold) !== undefined ? options.spaceDifferenceThreshold : DEFAULT_SPACE_DIFFERENCE;
13
+ const numericDifference = (options === null || options === void 0 ? void 0 : options.numericDifferenceThreshold) !== undefined ? options.numericDifferenceThreshold : DEFAULT_NUMERIC_DIFFERENCE;
14
+ const borderDifference = (options === null || options === void 0 ? void 0 : options.borderDifferenceThreshold) !== undefined ? options.borderDifferenceThreshold : DEFAULT_BORDER_DIFFERENCE;
10
15
  let tokenLogValue;
11
16
  let fallbackLogValue;
12
17
  let normalizedTokenValue = tokenValue;
@@ -27,7 +32,7 @@ export function normalizeValues(tokenKey, tokenValue, fallbackValue) {
27
32
  }
28
33
  if (normalizedTokenValue && normalizedFallbackValue) {
29
34
  difference = compareHex(normalizedTokenValue, normalizedFallbackValue);
30
- isAcceptableDifference = difference <= ACCEPTABLE_COLOR_DIFFERENCE;
35
+ isAcceptableDifference = difference <= colorDifference;
31
36
  }
32
37
  } else if (lowerCaseTokenKey.startsWith('space') || lowerCaseTokenKey.startsWith('border')) {
33
38
  const tokenValueInPx = tokenValue ? convertToPx(tokenValue) : undefined;
@@ -35,7 +40,7 @@ export function normalizeValues(tokenKey, tokenValue, fallbackValue) {
35
40
  if (tokenValueInPx !== undefined && fallbackValueInPx !== undefined) {
36
41
  const maxVal = Math.max(tokenValueInPx, fallbackValueInPx);
37
42
  difference = Math.abs(tokenValueInPx - fallbackValueInPx) / maxVal * 100;
38
- isAcceptableDifference = difference <= (lowerCaseTokenKey.startsWith('space') ? ACCEPTABLE_SPACE_DIFFERENCE : ACCEPTABLE_BORDER_DIFFERENCE);
43
+ isAcceptableDifference = difference <= (lowerCaseTokenKey.startsWith('space') ? spaceDifference : borderDifference);
39
44
  }
40
45
  // Log the normalized values
41
46
  normalizedTokenValue = tokenValue;
@@ -49,7 +54,7 @@ export function normalizeValues(tokenKey, tokenValue, fallbackValue) {
49
54
  if (!isNaN(tokenValueNumber) && !isNaN(fallbackValueNumber)) {
50
55
  const maxVal = Math.max(tokenValueNumber, fallbackValueNumber);
51
56
  difference = Math.abs(tokenValueNumber - fallbackValueNumber) / maxVal * 100;
52
- isAcceptableDifference = difference <= ACCEPTABLE_NUMERIC_DIFFERENCE;
57
+ isAcceptableDifference = difference <= numericDifference;
53
58
  }
54
59
  // Log the normalized values
55
60
  normalizedTokenValue = tokenValue;
@@ -45,6 +45,29 @@ export class TokenProcessor {
45
45
  logError(message) {
46
46
  this.log(chalk.red(message));
47
47
  }
48
+
49
+ /**
50
+ * Checks if a token should be exempted from automatic fallback removal
51
+ * @param tokenKey The token key to check
52
+ * @returns An object containing whether the token should be exempted and related information
53
+ */
54
+ checkTokenExemption(tokenKey) {
55
+ // Create exemption list from user-provided skipTokens, and always include 'border'
56
+ const userExemptions = this.options.skipTokens ? this.options.skipTokens.split(',').map(item => item.trim()) : [];
57
+
58
+ // Always include 'border' in the exemption list
59
+ const exemptionList = [...userExemptions];
60
+ if (!exemptionList.includes('border')) {
61
+ exemptionList.push('border');
62
+ }
63
+ const isExemptedToken = exemptionList.some(prefix => tokenKey.startsWith(prefix));
64
+ const exemptedPrefix = isExemptedToken ? exemptionList.find(prefix => tokenKey.startsWith(prefix)) || null : null;
65
+ return {
66
+ shouldBeExempted: isExemptedToken,
67
+ exemptedPrefix,
68
+ exemptionList
69
+ };
70
+ }
48
71
  async processSingleToken(callPath) {
49
72
  var _callPath$node$loc2;
50
73
  const args = callPath.node.arguments;
@@ -67,23 +90,14 @@ export class TokenProcessor {
67
90
  resolvedLocalVarDeclaration: undefined
68
91
  };
69
92
  }
70
- const isSkipped = false;
71
- // tokenKey.startsWith('elevation.shadow') ||
72
- // tokenKey.startsWith('font.body') ||
73
- // tokenKey.startsWith('font.heading');
74
- const tokenValue = isSkipped ? '' : this.tokenMap[tokenKey];
93
+ const tokenValue = this.tokenMap[tokenKey];
75
94
  this.logVerbose(`Token value from tokenMap: ${chalk.magenta(tokenValue)} for key: ${chalk.yellow(tokenKey)}`);
76
95
  const {
77
96
  rawFallbackValue,
78
97
  fallbackValue,
79
98
  resolvedImportDeclaration,
80
99
  resolvedLocalVarDeclaration
81
- } = isSkipped ? {
82
- rawFallbackValue: 'N/A',
83
- fallbackValue: undefined,
84
- resolvedImportDeclaration: undefined,
85
- resolvedLocalVarDeclaration: undefined
86
- } : await this.getFallbackValue(args[1]);
100
+ } = await this.getFallbackValue(args[1]);
87
101
  const {
88
102
  difference,
89
103
  isAcceptableDifference,
@@ -91,7 +105,7 @@ export class TokenProcessor {
91
105
  fallbackLogValue,
92
106
  normalizedTokenValue,
93
107
  normalizedFallbackValue
94
- } = normalizeValues(tokenKey, tokenValue, fallbackValue);
108
+ } = normalizeValues(tokenKey, tokenValue, fallbackValue, this.options);
95
109
  const areEqual = normalizedTokenValue === normalizedFallbackValue;
96
110
  const logData = {
97
111
  teamInfo: this.teamInfo,
@@ -106,8 +120,22 @@ export class TokenProcessor {
106
120
  let fallbackRemoved = false;
107
121
  let importDeclaration;
108
122
  let localVarDeclaration;
109
- const isBorderToken = tokenKey.startsWith('border');
110
- if (areEqual || (isAcceptableDifference || this.options.forceUpdate) && !isBorderToken) {
123
+
124
+ // Check if token should be exempted
125
+ const {
126
+ shouldBeExempted,
127
+ exemptedPrefix
128
+ } = this.checkTokenExemption(tokenKey);
129
+
130
+ // Determine if we should modify this token based on the exemption status and settings
131
+ const shouldModifyToken =
132
+ // Always modify if not exempted and values match
133
+ areEqual && !shouldBeExempted ||
134
+ // Or if values don't exactly match but are acceptable to modify and not exempted
135
+ (isAcceptableDifference || this.options.forceUpdate) && !shouldBeExempted ||
136
+ // Or if exempted but values match exactly and we're not preserving skipped fallbacks
137
+ areEqual && shouldBeExempted && !this.options.preserveSkippedFallbacks;
138
+ if (shouldModifyToken) {
111
139
  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'));
112
140
  args.pop();
113
141
  this.details.replaced.push(logData);
@@ -115,7 +143,7 @@ export class TokenProcessor {
115
143
  importDeclaration = resolvedImportDeclaration;
116
144
  localVarDeclaration = resolvedLocalVarDeclaration;
117
145
  } else {
118
- const message = isBorderToken ? 'Skip modifying border token' : normalizedFallbackValue === undefined ? `Fallback value could not be resolved` : `Values mismatched significantly`;
146
+ const message = shouldBeExempted ? this.options.preserveSkippedFallbacks && areEqual ? `Preserving fallback for exempted token '${tokenKey}' (matches exemption '${exemptedPrefix}')` : `Skip modifying exempted token '${tokenKey}' (matches exemption '${exemptedPrefix}')` : normalizedFallbackValue === undefined ? `Fallback value could not be resolved` : `Values mismatched significantly`;
119
147
  this.logError(message);
120
148
  if (this.options.addEslintComments) {
121
149
  addOrUpdateEslintIgnoreComment(this.j, tokenValue, fallbackValue, callPath);
package/dist/esm/main.js CHANGED
@@ -307,7 +307,7 @@ var defaultFlags = {
307
307
  ignorePattern: 'node_modules',
308
308
  logger: console
309
309
  };
310
- function processLifecycleHook(_x9, _x10, _x11, _x12, _x13) {
310
+ function processLifecycleHook(_x9, _x0, _x1, _x10, _x11) {
311
311
  return _processLifecycleHook.apply(this, arguments);
312
312
  }
313
313
  function _processLifecycleHook() {
@@ -339,7 +339,7 @@ function _processLifecycleHook() {
339
339
  }));
340
340
  return _processLifecycleHook.apply(this, arguments);
341
341
  }
342
- export default function main(_x14, _x15) {
342
+ export default function main(_x12, _x13) {
343
343
  return _main.apply(this, arguments);
344
344
  }
345
345
  function _main() {
@@ -355,7 +355,7 @@ function _main() {
355
355
  case 4:
356
356
  _yield$parseArgs = _context6.sent;
357
357
  packages = _yield$parseArgs.packages;
358
- _process$env$_PACKAGE = "0.28.0", _PACKAGE_VERSION_ = _process$env$_PACKAGE === void 0 ? '0.0.0-dev' : _process$env$_PACKAGE;
358
+ _process$env$_PACKAGE = "0.28.1", _PACKAGE_VERSION_ = _process$env$_PACKAGE === void 0 ? '0.0.0-dev' : _process$env$_PACKAGE;
359
359
  logger.log(chalk.bgBlue(chalk.black("\uD83D\uDCDA Atlassian-Frontend codemod library @ ".concat(_PACKAGE_VERSION_, " \uD83D\uDCDA"))));
360
360
  if (packages && packages.length > 0) {
361
361
  logger.log(chalk.gray("Searching for codemods for newer versions of the following packages: ".concat(packages.map(function (pkg) {
@@ -35,6 +35,14 @@ var execAsync = promisify(exec);
35
35
  * @param {boolean} [options.useLegacyColorTheme] - If true, uses the legacy theme for color token mapping.
36
36
  * @param {string} [options.reportFolder] - Directory path to output transformation reports. Reports will be generated only if this option is provided.
37
37
  * @param {boolean} [options.dry] - If true, performs a dry run without modifying the files.
38
+ * @param {string} [options.skipTokens] - A comma-separated list of token prefixes to exempt from automatic fallback removal. By default, 'border' tokens are always included in this list. Whether fallbacks for these tokens are removed when they exactly match depends on the preserveSkippedFallbacks option.
39
+ * @param {boolean} [options.preserveSkippedFallbacks] - If true, fallbacks for skipped tokens will never be removed, even if they exactly match the token value. If false (default), fallbacks for skipped tokens will be removed if they exactly match.
40
+ * @param {boolean} [options.skipEslint] - If true, skips running ESLint on modified files after transformation.
41
+ * @param {boolean} [options.skipPrettier] - If true, skips running Prettier on modified files after transformation.
42
+ * @param {number} [options.colorDifferenceThreshold] - The maximum allowed difference for color tokens to be considered acceptable for removal. Default is 15.
43
+ * @param {number} [options.spaceDifferenceThreshold] - The maximum allowed percentage difference for space tokens to be considered acceptable for removal. Default is 0.
44
+ * @param {number} [options.numericDifferenceThreshold] - The maximum allowed percentage difference for numeric tokens to be considered acceptable for removal. Default is 0.
45
+ * @param {number} [options.borderDifferenceThreshold] - The maximum allowed percentage difference for border tokens to be considered acceptable for removal. Default is 0.
38
46
  *
39
47
  * @returns {Promise<string>} A promise that resolves to the transformed source code as a string.
40
48
  */
@@ -66,6 +74,32 @@ function _transformer() {
66
74
  };
67
75
  if (options.verbose) {
68
76
  console.log(chalk.yellow("Using ".concat(options.useLegacyColorTheme ? 'legacy light' : 'light', " theme.")));
77
+ if (options.skipTokens) {
78
+ console.log(chalk.yellow("Auto fallback exemptions active for: ".concat(options.skipTokens)));
79
+ }
80
+ if (options.preserveSkippedFallbacks) {
81
+ console.log(chalk.yellow("Preserving all fallbacks for skipped tokens, even if they match exactly."));
82
+ }
83
+ if (options.skipEslint) {
84
+ console.log(chalk.yellow("Skipping ESLint post-processing."));
85
+ }
86
+ if (options.skipPrettier) {
87
+ console.log(chalk.yellow("Skipping Prettier post-processing."));
88
+ }
89
+
90
+ // Log threshold values if they are set
91
+ if (options.colorDifferenceThreshold !== undefined) {
92
+ console.log(chalk.yellow("Color difference threshold set to: ".concat(options.colorDifferenceThreshold)));
93
+ }
94
+ if (options.spaceDifferenceThreshold !== undefined) {
95
+ console.log(chalk.yellow("Space difference threshold set to: ".concat(options.spaceDifferenceThreshold, "%")));
96
+ }
97
+ if (options.numericDifferenceThreshold !== undefined) {
98
+ console.log(chalk.yellow("Numeric difference threshold set to: ".concat(options.numericDifferenceThreshold, "%")));
99
+ }
100
+ if (options.borderDifferenceThreshold !== undefined) {
101
+ console.log(chalk.yellow("Border difference threshold set to: ".concat(options.borderDifferenceThreshold, "%")));
102
+ }
69
103
  }
70
104
  tokenMap = getTokenMap((_options$useLegacyCol = options.useLegacyColorTheme) !== null && _options$useLegacyCol !== void 0 ? _options$useLegacyCol : false);
71
105
  _context.next = 12;
@@ -190,7 +224,7 @@ function _afterAll() {
190
224
  return combineReports(options.reportFolder);
191
225
  case 3:
192
226
  if (!(options.reportFolder && !options.dry)) {
193
- _context3.next = 27;
227
+ _context3.next = 35;
194
228
  break;
195
229
  }
196
230
  _context3.prev = 4;
@@ -200,7 +234,7 @@ function _afterAll() {
200
234
  case 8:
201
235
  fileContent = _context3.sent;
202
236
  if (!(fileContent.length > 0)) {
203
- _context3.next = 22;
237
+ _context3.next = 30;
204
238
  break;
205
239
  }
206
240
  filePaths = fileContent.split(/\r?\n/).filter(Boolean); // Get the first file path and strip any quotes
@@ -213,27 +247,49 @@ function _afterAll() {
213
247
  _context3.next = 18;
214
248
  return gitStage(filePaths, rootDir);
215
249
  case 18:
216
- _context3.next = 20;
250
+ if (options.skipEslint) {
251
+ _context3.next = 23;
252
+ break;
253
+ }
254
+ _context3.next = 21;
217
255
  return runEslint(filePaths, rootDir);
218
- case 20:
219
- _context3.next = 22;
220
- return runPrettier(filePaths, rootDir);
221
- case 22:
222
- _context3.next = 27;
256
+ case 21:
257
+ _context3.next = 24;
223
258
  break;
259
+ case 23:
260
+ if (options.verbose) {
261
+ console.log(chalk.blue('Skipping ESLint post-processing as requested.'));
262
+ }
224
263
  case 24:
225
- _context3.prev = 24;
264
+ if (options.skipPrettier) {
265
+ _context3.next = 29;
266
+ break;
267
+ }
268
+ _context3.next = 27;
269
+ return runPrettier(filePaths, rootDir);
270
+ case 27:
271
+ _context3.next = 30;
272
+ break;
273
+ case 29:
274
+ if (options.verbose) {
275
+ console.log(chalk.blue('Skipping Prettier post-processing as requested.'));
276
+ }
277
+ case 30:
278
+ _context3.next = 35;
279
+ break;
280
+ case 32:
281
+ _context3.prev = 32;
226
282
  _context3.t0 = _context3["catch"](4);
227
283
  if (_context3.t0 instanceof Error) {
228
284
  console.error(chalk.red("Unexpected error: ".concat(_context3.t0.message)));
229
285
  } else {
230
286
  console.error(chalk.red('An unknown error occurred.'));
231
287
  }
232
- case 27:
288
+ case 35:
233
289
  case "end":
234
290
  return _context3.stop();
235
291
  }
236
- }, _callee3, null, [[4, 24]]);
292
+ }, _callee3, null, [[4, 32]]);
237
293
  }));
238
294
  return _afterAll.apply(this, arguments);
239
295
  }
@@ -305,7 +361,7 @@ function _runPrettier() {
305
361
  }));
306
362
  return _runPrettier.apply(this, arguments);
307
363
  }
308
- function runEslint(_x10, _x11) {
364
+ function runEslint(_x0, _x1) {
309
365
  return _runEslint.apply(this, arguments);
310
366
  }
311
367
  function _runEslint() {
@@ -396,7 +452,7 @@ function _runEslint() {
396
452
  }
397
453
  }, _callee6, null, [[0, 11]]);
398
454
  }));
399
- return function (_x12) {
455
+ return function (_x10) {
400
456
  return _ref2.apply(this, arguments);
401
457
  };
402
458
  }()));
@@ -2,12 +2,17 @@ import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
2
2
  import chalk from 'chalk';
3
3
  import { colorToHex, compareHex, isValidColor } from './color-utils';
4
4
 
5
- // so far allowing to remove only exact matches in values. Can be increased to auto-remove fallbacks with similar values
6
- var ACCEPTABLE_COLOR_DIFFERENCE = 15;
7
- var ACCEPTABLE_SPACE_DIFFERENCE = 0;
8
- var ACCEPTABLE_NUMERIC_DIFFERENCE = 0;
9
- var ACCEPTABLE_BORDER_DIFFERENCE = 0;
10
- export function normalizeValues(tokenKey, tokenValue, fallbackValue) {
5
+ // Default threshold values
6
+ var DEFAULT_COLOR_DIFFERENCE = 15;
7
+ var DEFAULT_SPACE_DIFFERENCE = 0;
8
+ var DEFAULT_NUMERIC_DIFFERENCE = 0;
9
+ var DEFAULT_BORDER_DIFFERENCE = 0;
10
+ export function normalizeValues(tokenKey, tokenValue, fallbackValue, options) {
11
+ // Use options thresholds or defaults
12
+ var colorDifference = (options === null || options === void 0 ? void 0 : options.colorDifferenceThreshold) !== undefined ? options.colorDifferenceThreshold : DEFAULT_COLOR_DIFFERENCE;
13
+ var spaceDifference = (options === null || options === void 0 ? void 0 : options.spaceDifferenceThreshold) !== undefined ? options.spaceDifferenceThreshold : DEFAULT_SPACE_DIFFERENCE;
14
+ var numericDifference = (options === null || options === void 0 ? void 0 : options.numericDifferenceThreshold) !== undefined ? options.numericDifferenceThreshold : DEFAULT_NUMERIC_DIFFERENCE;
15
+ var borderDifference = (options === null || options === void 0 ? void 0 : options.borderDifferenceThreshold) !== undefined ? options.borderDifferenceThreshold : DEFAULT_BORDER_DIFFERENCE;
11
16
  var tokenLogValue;
12
17
  var fallbackLogValue;
13
18
  var normalizedTokenValue = tokenValue;
@@ -28,7 +33,7 @@ export function normalizeValues(tokenKey, tokenValue, fallbackValue) {
28
33
  }
29
34
  if (normalizedTokenValue && normalizedFallbackValue) {
30
35
  difference = compareHex(normalizedTokenValue, normalizedFallbackValue);
31
- isAcceptableDifference = difference <= ACCEPTABLE_COLOR_DIFFERENCE;
36
+ isAcceptableDifference = difference <= colorDifference;
32
37
  }
33
38
  } else if (lowerCaseTokenKey.startsWith('space') || lowerCaseTokenKey.startsWith('border')) {
34
39
  var tokenValueInPx = tokenValue ? convertToPx(tokenValue) : undefined;
@@ -36,7 +41,7 @@ export function normalizeValues(tokenKey, tokenValue, fallbackValue) {
36
41
  if (tokenValueInPx !== undefined && fallbackValueInPx !== undefined) {
37
42
  var maxVal = Math.max(tokenValueInPx, fallbackValueInPx);
38
43
  difference = Math.abs(tokenValueInPx - fallbackValueInPx) / maxVal * 100;
39
- isAcceptableDifference = difference <= (lowerCaseTokenKey.startsWith('space') ? ACCEPTABLE_SPACE_DIFFERENCE : ACCEPTABLE_BORDER_DIFFERENCE);
44
+ isAcceptableDifference = difference <= (lowerCaseTokenKey.startsWith('space') ? spaceDifference : borderDifference);
40
45
  }
41
46
  // Log the normalized values
42
47
  normalizedTokenValue = tokenValue;
@@ -50,7 +55,7 @@ export function normalizeValues(tokenKey, tokenValue, fallbackValue) {
50
55
  if (!isNaN(tokenValueNumber) && !isNaN(fallbackValueNumber)) {
51
56
  var _maxVal = Math.max(tokenValueNumber, fallbackValueNumber);
52
57
  difference = Math.abs(tokenValueNumber - fallbackValueNumber) / _maxVal * 100;
53
- isAcceptableDifference = difference <= ACCEPTABLE_NUMERIC_DIFFERENCE;
58
+ isAcceptableDifference = difference <= numericDifference;
54
59
  }
55
60
  // Log the normalized values
56
61
  normalizedTokenValue = tokenValue;
@@ -1,3 +1,4 @@
1
+ import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray";
1
2
  import _objectWithoutProperties from "@babel/runtime/helpers/objectWithoutProperties";
2
3
  import _asyncToGenerator from "@babel/runtime/helpers/asyncToGenerator";
3
4
  import _classCallCheck from "@babel/runtime/helpers/classCallCheck";
@@ -82,12 +83,43 @@ export var TokenProcessor = /*#__PURE__*/function () {
82
83
  value: function logError(message) {
83
84
  this.log(chalk.red(message));
84
85
  }
86
+
87
+ /**
88
+ * Checks if a token should be exempted from automatic fallback removal
89
+ * @param tokenKey The token key to check
90
+ * @returns An object containing whether the token should be exempted and related information
91
+ */
92
+ }, {
93
+ key: "checkTokenExemption",
94
+ value: function checkTokenExemption(tokenKey) {
95
+ // Create exemption list from user-provided skipTokens, and always include 'border'
96
+ var userExemptions = this.options.skipTokens ? this.options.skipTokens.split(',').map(function (item) {
97
+ return item.trim();
98
+ }) : [];
99
+
100
+ // Always include 'border' in the exemption list
101
+ var exemptionList = _toConsumableArray(userExemptions);
102
+ if (!exemptionList.includes('border')) {
103
+ exemptionList.push('border');
104
+ }
105
+ var isExemptedToken = exemptionList.some(function (prefix) {
106
+ return tokenKey.startsWith(prefix);
107
+ });
108
+ var exemptedPrefix = isExemptedToken ? exemptionList.find(function (prefix) {
109
+ return tokenKey.startsWith(prefix);
110
+ }) || null : null;
111
+ return {
112
+ shouldBeExempted: isExemptedToken,
113
+ exemptedPrefix: exemptedPrefix,
114
+ exemptionList: exemptionList
115
+ };
116
+ }
85
117
  }, {
86
118
  key: "processSingleToken",
87
119
  value: function () {
88
120
  var _processSingleToken = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee2(callPath) {
89
121
  var _callPath$node$loc2;
90
- var args, line, tokenKey, isSkipped, tokenValue, _ref, rawFallbackValue, fallbackValue, resolvedImportDeclaration, resolvedLocalVarDeclaration, _normalizeValues, difference, isAcceptableDifference, tokenLogValue, fallbackLogValue, normalizedTokenValue, normalizedFallbackValue, areEqual, logData, fallbackRemoved, importDeclaration, localVarDeclaration, isBorderToken, message;
122
+ var args, line, tokenKey, tokenValue, _yield$this$getFallba, rawFallbackValue, fallbackValue, resolvedImportDeclaration, resolvedLocalVarDeclaration, _normalizeValues, difference, isAcceptableDifference, tokenLogValue, fallbackLogValue, normalizedTokenValue, normalizedFallbackValue, areEqual, logData, fallbackRemoved, importDeclaration, localVarDeclaration, _this$checkTokenExemp, shouldBeExempted, exemptedPrefix, shouldModifyToken, message;
91
123
  return _regeneratorRuntime.wrap(function _callee2$(_context2) {
92
124
  while (1) switch (_context2.prev = _context2.next) {
93
125
  case 0:
@@ -117,35 +149,17 @@ export var TokenProcessor = /*#__PURE__*/function () {
117
149
  resolvedLocalVarDeclaration: undefined
118
150
  });
119
151
  case 8:
120
- isSkipped = false; // tokenKey.startsWith('elevation.shadow') ||
121
- // tokenKey.startsWith('font.body') ||
122
- // tokenKey.startsWith('font.heading');
123
- tokenValue = isSkipped ? '' : this.tokenMap[tokenKey];
152
+ tokenValue = this.tokenMap[tokenKey];
124
153
  this.logVerbose("Token value from tokenMap: ".concat(chalk.magenta(tokenValue), " for key: ").concat(chalk.yellow(tokenKey)));
125
- if (!isSkipped) {
126
- _context2.next = 15;
127
- break;
128
- }
129
- _context2.t0 = {
130
- rawFallbackValue: 'N/A',
131
- fallbackValue: undefined,
132
- resolvedImportDeclaration: undefined,
133
- resolvedLocalVarDeclaration: undefined
134
- };
135
- _context2.next = 18;
136
- break;
137
- case 15:
138
- _context2.next = 17;
154
+ _context2.next = 12;
139
155
  return this.getFallbackValue(args[1]);
140
- case 17:
141
- _context2.t0 = _context2.sent;
142
- case 18:
143
- _ref = _context2.t0;
144
- rawFallbackValue = _ref.rawFallbackValue;
145
- fallbackValue = _ref.fallbackValue;
146
- resolvedImportDeclaration = _ref.resolvedImportDeclaration;
147
- resolvedLocalVarDeclaration = _ref.resolvedLocalVarDeclaration;
148
- _normalizeValues = normalizeValues(tokenKey, tokenValue, fallbackValue), difference = _normalizeValues.difference, isAcceptableDifference = _normalizeValues.isAcceptableDifference, tokenLogValue = _normalizeValues.tokenLogValue, fallbackLogValue = _normalizeValues.fallbackLogValue, normalizedTokenValue = _normalizeValues.normalizedTokenValue, normalizedFallbackValue = _normalizeValues.normalizedFallbackValue;
156
+ case 12:
157
+ _yield$this$getFallba = _context2.sent;
158
+ rawFallbackValue = _yield$this$getFallba.rawFallbackValue;
159
+ fallbackValue = _yield$this$getFallba.fallbackValue;
160
+ resolvedImportDeclaration = _yield$this$getFallba.resolvedImportDeclaration;
161
+ resolvedLocalVarDeclaration = _yield$this$getFallba.resolvedLocalVarDeclaration;
162
+ _normalizeValues = normalizeValues(tokenKey, tokenValue, fallbackValue, this.options), difference = _normalizeValues.difference, isAcceptableDifference = _normalizeValues.isAcceptableDifference, tokenLogValue = _normalizeValues.tokenLogValue, fallbackLogValue = _normalizeValues.fallbackLogValue, normalizedTokenValue = _normalizeValues.normalizedTokenValue, normalizedFallbackValue = _normalizeValues.normalizedFallbackValue;
149
163
  areEqual = normalizedTokenValue === normalizedFallbackValue;
150
164
  logData = {
151
165
  teamInfo: this.teamInfo,
@@ -158,8 +172,16 @@ export var TokenProcessor = /*#__PURE__*/function () {
158
172
  difference: difference
159
173
  };
160
174
  fallbackRemoved = false;
161
- isBorderToken = tokenKey.startsWith('border');
162
- if (areEqual || (isAcceptableDifference || this.options.forceUpdate) && !isBorderToken) {
175
+ // Check if token should be exempted
176
+ _this$checkTokenExemp = this.checkTokenExemption(tokenKey), shouldBeExempted = _this$checkTokenExemp.shouldBeExempted, exemptedPrefix = _this$checkTokenExemp.exemptedPrefix; // Determine if we should modify this token based on the exemption status and settings
177
+ shouldModifyToken =
178
+ // Always modify if not exempted and values match
179
+ areEqual && !shouldBeExempted ||
180
+ // Or if values don't exactly match but are acceptable to modify and not exempted
181
+ (isAcceptableDifference || this.options.forceUpdate) && !shouldBeExempted ||
182
+ // Or if exempted but values match exactly and we're not preserving skipped fallbacks
183
+ areEqual && shouldBeExempted && !this.options.preserveSkippedFallbacks;
184
+ if (shouldModifyToken) {
163
185
  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'));
164
186
  args.pop();
165
187
  this.details.replaced.push(logData);
@@ -167,7 +189,7 @@ export var TokenProcessor = /*#__PURE__*/function () {
167
189
  importDeclaration = resolvedImportDeclaration;
168
190
  localVarDeclaration = resolvedLocalVarDeclaration;
169
191
  } else {
170
- message = isBorderToken ? 'Skip modifying border token' : normalizedFallbackValue === undefined ? "Fallback value could not be resolved" : "Values mismatched significantly";
192
+ message = shouldBeExempted ? this.options.preserveSkippedFallbacks && areEqual ? "Preserving fallback for exempted token '".concat(tokenKey, "' (matches exemption '").concat(exemptedPrefix, "')") : "Skip modifying exempted token '".concat(tokenKey, "' (matches exemption '").concat(exemptedPrefix, "')") : normalizedFallbackValue === undefined ? "Fallback value could not be resolved" : "Values mismatched significantly";
171
193
  this.logError(message);
172
194
  if (this.options.addEslintComments) {
173
195
  addOrUpdateEslintIgnoreComment(this.j, tokenValue, fallbackValue, callPath);
@@ -181,7 +203,7 @@ export var TokenProcessor = /*#__PURE__*/function () {
181
203
  resolvedImportDeclaration: importDeclaration,
182
204
  resolvedLocalVarDeclaration: localVarDeclaration
183
205
  });
184
- case 31:
206
+ case 26:
185
207
  case "end":
186
208
  return _context2.stop();
187
209
  }
@@ -704,7 +726,7 @@ export var TokenProcessor = /*#__PURE__*/function () {
704
726
  }
705
727
  }, _callee8, this);
706
728
  }));
707
- function resolveValueFromImport(_x9, _x10, _x11, _x12) {
729
+ function resolveValueFromImport(_x9, _x0, _x1, _x10) {
708
730
  return _resolveValueFromImport.apply(this, arguments);
709
731
  }
710
732
  return resolveValueFromImport;
@@ -13,6 +13,14 @@ import { RemoveTokenFallbackOptions } from './types';
13
13
  * @param {boolean} [options.useLegacyColorTheme] - If true, uses the legacy theme for color token mapping.
14
14
  * @param {string} [options.reportFolder] - Directory path to output transformation reports. Reports will be generated only if this option is provided.
15
15
  * @param {boolean} [options.dry] - If true, performs a dry run without modifying the files.
16
+ * @param {string} [options.skipTokens] - A comma-separated list of token prefixes to exempt from automatic fallback removal. By default, 'border' tokens are always included in this list. Whether fallbacks for these tokens are removed when they exactly match depends on the preserveSkippedFallbacks option.
17
+ * @param {boolean} [options.preserveSkippedFallbacks] - If true, fallbacks for skipped tokens will never be removed, even if they exactly match the token value. If false (default), fallbacks for skipped tokens will be removed if they exactly match.
18
+ * @param {boolean} [options.skipEslint] - If true, skips running ESLint on modified files after transformation.
19
+ * @param {boolean} [options.skipPrettier] - If true, skips running Prettier on modified files after transformation.
20
+ * @param {number} [options.colorDifferenceThreshold] - The maximum allowed difference for color tokens to be considered acceptable for removal. Default is 15.
21
+ * @param {number} [options.spaceDifferenceThreshold] - The maximum allowed percentage difference for space tokens to be considered acceptable for removal. Default is 0.
22
+ * @param {number} [options.numericDifferenceThreshold] - The maximum allowed percentage difference for numeric tokens to be considered acceptable for removal. Default is 0.
23
+ * @param {number} [options.borderDifferenceThreshold] - The maximum allowed percentage difference for border tokens to be considered acceptable for removal. Default is 0.
16
24
  *
17
25
  * @returns {Promise<string>} A promise that resolves to the transformed source code as a string.
18
26
  */
@@ -22,6 +22,14 @@ export type RemoveTokenFallbackOptions = Options & {
22
22
  reportFolder?: string;
23
23
  addEslintComments?: boolean;
24
24
  forceUpdate?: boolean;
25
+ skipTokens?: string;
26
+ preserveSkippedFallbacks?: boolean;
27
+ skipEslint?: boolean;
28
+ skipPrettier?: boolean;
29
+ colorDifferenceThreshold?: number;
30
+ spaceDifferenceThreshold?: number;
31
+ numericDifferenceThreshold?: number;
32
+ borderDifferenceThreshold?: number;
25
33
  };
26
34
  export type WithResolvedDeclarations = {
27
35
  resolvedImportDeclaration?: ASTPath<ImportDeclaration>;
@@ -1,4 +1,5 @@
1
- export declare function normalizeValues(tokenKey: string, tokenValue: string | undefined, fallbackValue: string | undefined): {
1
+ import { RemoveTokenFallbackOptions } from '../types';
2
+ export declare function normalizeValues(tokenKey: string, tokenValue: string | undefined, fallbackValue: string | undefined, options?: RemoveTokenFallbackOptions): {
2
3
  difference?: number;
3
4
  isAcceptableDifference?: boolean;
4
5
  tokenLogValue: string;
@@ -16,6 +16,12 @@ export declare class TokenProcessor {
16
16
  private logVerbose;
17
17
  private log;
18
18
  private logError;
19
+ /**
20
+ * Checks if a token should be exempted from automatic fallback removal
21
+ * @param tokenKey The token key to check
22
+ * @returns An object containing whether the token should be exempted and related information
23
+ */
24
+ private checkTokenExemption;
19
25
  private processSingleToken;
20
26
  private getTokenKey;
21
27
  private getFallbackValue;
@@ -13,6 +13,14 @@ import { RemoveTokenFallbackOptions } from './types';
13
13
  * @param {boolean} [options.useLegacyColorTheme] - If true, uses the legacy theme for color token mapping.
14
14
  * @param {string} [options.reportFolder] - Directory path to output transformation reports. Reports will be generated only if this option is provided.
15
15
  * @param {boolean} [options.dry] - If true, performs a dry run without modifying the files.
16
+ * @param {string} [options.skipTokens] - A comma-separated list of token prefixes to exempt from automatic fallback removal. By default, 'border' tokens are always included in this list. Whether fallbacks for these tokens are removed when they exactly match depends on the preserveSkippedFallbacks option.
17
+ * @param {boolean} [options.preserveSkippedFallbacks] - If true, fallbacks for skipped tokens will never be removed, even if they exactly match the token value. If false (default), fallbacks for skipped tokens will be removed if they exactly match.
18
+ * @param {boolean} [options.skipEslint] - If true, skips running ESLint on modified files after transformation.
19
+ * @param {boolean} [options.skipPrettier] - If true, skips running Prettier on modified files after transformation.
20
+ * @param {number} [options.colorDifferenceThreshold] - The maximum allowed difference for color tokens to be considered acceptable for removal. Default is 15.
21
+ * @param {number} [options.spaceDifferenceThreshold] - The maximum allowed percentage difference for space tokens to be considered acceptable for removal. Default is 0.
22
+ * @param {number} [options.numericDifferenceThreshold] - The maximum allowed percentage difference for numeric tokens to be considered acceptable for removal. Default is 0.
23
+ * @param {number} [options.borderDifferenceThreshold] - The maximum allowed percentage difference for border tokens to be considered acceptable for removal. Default is 0.
16
24
  *
17
25
  * @returns {Promise<string>} A promise that resolves to the transformed source code as a string.
18
26
  */
@@ -22,6 +22,14 @@ export type RemoveTokenFallbackOptions = Options & {
22
22
  reportFolder?: string;
23
23
  addEslintComments?: boolean;
24
24
  forceUpdate?: boolean;
25
+ skipTokens?: string;
26
+ preserveSkippedFallbacks?: boolean;
27
+ skipEslint?: boolean;
28
+ skipPrettier?: boolean;
29
+ colorDifferenceThreshold?: number;
30
+ spaceDifferenceThreshold?: number;
31
+ numericDifferenceThreshold?: number;
32
+ borderDifferenceThreshold?: number;
25
33
  };
26
34
  export type WithResolvedDeclarations = {
27
35
  resolvedImportDeclaration?: ASTPath<ImportDeclaration>;
@@ -1,4 +1,5 @@
1
- export declare function normalizeValues(tokenKey: string, tokenValue: string | undefined, fallbackValue: string | undefined): {
1
+ import { RemoveTokenFallbackOptions } from '../types';
2
+ export declare function normalizeValues(tokenKey: string, tokenValue: string | undefined, fallbackValue: string | undefined, options?: RemoveTokenFallbackOptions): {
2
3
  difference?: number;
3
4
  isAcceptableDifference?: boolean;
4
5
  tokenLogValue: string;
@@ -16,6 +16,12 @@ export declare class TokenProcessor {
16
16
  private logVerbose;
17
17
  private log;
18
18
  private logError;
19
+ /**
20
+ * Checks if a token should be exempted from automatic fallback removal
21
+ * @param tokenKey The token key to check
22
+ * @returns An object containing whether the token should be exempted and related information
23
+ */
24
+ private checkTokenExemption;
19
25
  private processSingleToken;
20
26
  private getTokenKey;
21
27
  private getFallbackValue;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/codemod-cli",
3
- "version": "0.28.0",
3
+ "version": "0.28.1",
4
4
  "description": "A cli for distributing codemods for atlassian-frontend components and services",
5
5
  "publishConfig": {
6
6
  "registry": "https://registry.npmjs.org/"
@@ -39,7 +39,7 @@
39
39
  "bin": "./bin/codemod-cli.js",
40
40
  "dependencies": {
41
41
  "@atlaskit/codemod-utils": "^4.2.0",
42
- "@atlaskit/tokens": "^4.6.0",
42
+ "@atlaskit/tokens": "^5.0.0",
43
43
  "@babel/runtime": "^7.0.0",
44
44
  "@codeshift/utils": "^0.2.4",
45
45
  "@hypermod/utils": "^0.4.2",
@@ -58,7 +58,7 @@
58
58
  "uuid": "^3.1.0"
59
59
  },
60
60
  "devDependencies": {
61
- "@af/formatting": "^0.0.4",
61
+ "@af/formatting": "workspace:^",
62
62
  "@types/color-diff": "^1.2.1",
63
63
  "prettier": "^3.2.5",
64
64
  "ts-node": "^10.9.1",