@atlaskit/eslint-plugin-design-system 10.16.0 → 10.17.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.
@@ -5,7 +5,7 @@ var _typeof = require("@babel/runtime/helpers/typeof");
5
5
  Object.defineProperty(exports, "__esModule", {
6
6
  value: true
7
7
  });
8
- exports.locToString = exports.isSize = exports.isInRangeList = exports.getUpcomingIcons = exports.getMigrationMapObject = exports.getLiteralStringValue = exports.createHelpers = exports.createGuidance = exports.createCantMigrateSpreadPropsError = exports.createCantMigrateSizeUnknown = exports.createCantMigrateReExportError = exports.createCantMigrateIdentifierMapOrArrayError = exports.createCantMigrateIdentifierError = exports.createCantMigrateFunctionUnknownError = exports.createCantMigrateColorError = exports.createCantFindSuitableReplacementError = exports.createAutoMigrationError = exports.canMigrateColor = exports.canAutoMigrateNewIconBasedOnSize = exports.addToListOfRanges = void 0;
8
+ exports.locToString = exports.isSize = exports.isInsideNewButton = exports.isInsideLegacyButton = exports.isInRangeList = exports.getUpcomingIcons = exports.getNewIconNameAndImportPath = exports.getMigrationMapObject = exports.getLiteralStringValue = exports.createPropFixes = exports.createImportFix = exports.createHelpers = exports.createGuidance = exports.createCantMigrateSpreadPropsError = exports.createCantMigrateSizeUnknown = exports.createCantMigrateReExportError = exports.createCantMigrateIdentifierMapOrArrayError = exports.createCantMigrateIdentifierError = exports.createCantMigrateFunctionUnknownError = exports.createCantMigrateColorError = exports.createCantFindSuitableReplacementError = exports.createAutoMigrationError = exports.checkIfNewIconExist = exports.canMigrateColor = exports.canAutoMigrateNewIconBasedOnSize = exports.addToListOfRanges = void 0;
9
9
  var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
10
10
  var _eslintCodemodUtils = require("eslint-codemod-utils");
11
11
  var _UNSAFE_migrationMap = _interopRequireWildcard(require("@atlaskit/icon/UNSAFE_migration-map"));
@@ -63,12 +63,33 @@ var canAutoMigrateNewIconBasedOnSize = exports.canAutoMigrateNewIconBasedOnSize
63
63
  return ['swap', 'swap-slight-visual-change', 'swap-visual-change', 'swap-size-shift-utility'].includes(guidance || '');
64
64
  };
65
65
 
66
+ /**
67
+ *
68
+ * @param iconPackage string
69
+ * @returns object of new icon name and import path
70
+ */
71
+ var getNewIconNameAndImportPath = exports.getNewIconNameAndImportPath = function getNewIconNameAndImportPath(iconPackage, shouldUseMigrationPath) {
72
+ var legacyIconName = getIconKey(iconPackage);
73
+ var migrationMapObject = getMigrationMapObject(iconPackage);
74
+ if (!migrationMapObject || !migrationMapObject.newIcon) {
75
+ return {};
76
+ }
77
+ var newIcon = migrationMapObject.newIcon;
78
+ var migrationPath = newIcon.name === legacyIconName ? "".concat(newIcon.package, "/").concat(newIcon.type, "/migration/").concat(newIcon.name) : "".concat(newIcon.package, "/").concat(newIcon.type, "/migration/").concat(newIcon.name, "--").concat(legacyIconName.replaceAll('/', '-'));
79
+ return {
80
+ iconName: newIcon.name,
81
+ importPath: shouldUseMigrationPath ? migrationPath : "".concat(newIcon.package, "/").concat(newIcon.type, "/").concat(newIcon.name)
82
+ };
83
+ };
84
+
66
85
  /**
67
86
  * Creates the written guidance for migrating a legacy icon to a new icon
68
87
  */
69
- var createGuidance = exports.createGuidance = function createGuidance(iconPackage) {
70
- var insideNewButton = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
71
- var size = arguments.length > 2 ? arguments[2] : undefined;
88
+ var createGuidance = exports.createGuidance = function createGuidance(_ref) {
89
+ var iconPackage = _ref.iconPackage,
90
+ insideNewButton = _ref.insideNewButton,
91
+ size = _ref.size,
92
+ shouldUseMigrationPath = _ref.shouldUseMigrationPath;
72
93
  var migrationMapObject = getMigrationMapObject(iconPackage);
73
94
  var upcomingIcon = getUpcomingIcons(iconPackage);
74
95
  if (upcomingIcon) {
@@ -100,21 +121,24 @@ var createGuidance = exports.createGuidance = function createGuidance(iconPackag
100
121
  if (!newIcon) {
101
122
  return 'No equivalent icon in new set. An option is to contribute a custom icon into icon-labs package instead.\n';
102
123
  }
124
+ var _getNewIconNameAndImp = getNewIconNameAndImportPath(iconPackage, shouldUseMigrationPath),
125
+ iconName = _getNewIconNameAndImp.iconName,
126
+ importPath = _getNewIconNameAndImp.importPath;
103
127
  var buttonGuidanceStr = "Please set 'spacing' property of the new icon to 'none', to ensure appropriate spacing inside `@atlaskit/button`.\n";
104
128
  var _guidance = '';
105
129
  if (size) {
106
130
  if (migrationMapObject.sizeGuidance[size] && canAutoMigrateNewIconBasedOnSize(migrationMapObject.sizeGuidance[size])) {
107
- _guidance += "Fix: Use ".concat(newIcon.name, " from ").concat(newIcon.package, "/").concat(newIcon.type, "/").concat(newIcon.name, " instead.");
131
+ _guidance += "Fix: Use ".concat(iconName, " from ").concat(importPath, " instead.");
108
132
  } else {
109
133
  _guidance += "No equivalent icon for this size, ".concat(size, ", in new set.");
110
134
  }
111
135
  _guidance += "".concat(Object.keys(_UNSAFE_migrationMap.migrationOutcomeDescriptionMap).includes(migrationMapObject.sizeGuidance[size]) ? " Please: ".concat(_UNSAFE_migrationMap.migrationOutcomeDescriptionMap[migrationMapObject.sizeGuidance[size]]) : ' No migration size advice given.', "\n");
112
136
  } else {
113
- _guidance = "Use ".concat(newIcon.name, " from ").concat(newIcon.package, "/").concat(newIcon.type, "/").concat(newIcon.name, " instead.\nMigration suggestions, depending on the legacy icon size:\n");
114
- Object.entries(migrationMapObject.sizeGuidance).forEach(function (_ref) {
115
- var _ref2 = (0, _slicedToArray2.default)(_ref, 2),
116
- size = _ref2[0],
117
- value = _ref2[1];
137
+ _guidance = "Use ".concat(iconName, " from ").concat(importPath, " instead.\nMigration suggestions, depending on the legacy icon size:\n");
138
+ Object.entries(migrationMapObject.sizeGuidance).forEach(function (_ref2) {
139
+ var _ref3 = (0, _slicedToArray2.default)(_ref2, 2),
140
+ size = _ref3[0],
141
+ value = _ref3[1];
118
142
  _guidance += "\t- ".concat(size, ": ");
119
143
  if (!Object.keys(_UNSAFE_migrationMap.migrationOutcomeDescriptionMap).includes(value)) {
120
144
  _guidance += 'No migration advice given.\n';
@@ -244,13 +268,22 @@ var createCantMigrateSizeUnknown = exports.createCantMigrateSizeUnknown = functi
244
268
  };
245
269
  pushManualError(locToString(node), errors, myError, importSource, iconName);
246
270
  };
247
- var createAutoMigrationError = exports.createAutoMigrationError = function createAutoMigrationError(node, importSource, iconName, errors) {
271
+ var createAutoMigrationError = exports.createAutoMigrationError = function createAutoMigrationError(_ref4) {
272
+ var node = _ref4.node,
273
+ importSource = _ref4.importSource,
274
+ iconName = _ref4.iconName,
275
+ errors = _ref4.errors,
276
+ shouldAddSpaciousSpacing = _ref4.shouldAddSpaciousSpacing,
277
+ insideNewButton = _ref4.insideNewButton;
248
278
  var myError = {
249
279
  node: node,
250
280
  messageId: 'noLegacyIconsAutoMigration',
251
281
  data: {
252
282
  importSource: importSource,
253
- iconName: iconName
283
+ iconName: iconName,
284
+ spacing: shouldAddSpaciousSpacing ? 'spacious' : '',
285
+ // value type need to be a string in Rule.ReportDescriptor
286
+ insideNewButton: String(insideNewButton)
254
287
  }
255
288
  };
256
289
  errors[locToString(node)] = myError;
@@ -314,9 +347,9 @@ var createHelpers = exports.createHelpers = function createHelpers(context) {
314
347
  * Extracts the primaryColor value from a JSXAttribute
315
348
  */
316
349
  getPrimaryColor: function getPrimaryColor(attr) {
317
- var _ref3, _getLiteralStringValu;
350
+ var _ref5, _getLiteralStringValu;
318
351
  var value = attr.value;
319
- return (_ref3 = (_getLiteralStringValu = getLiteralStringValue(value)) !== null && _getLiteralStringValu !== void 0 ? _getLiteralStringValu : getTokenCallValue(value)) !== null && _ref3 !== void 0 ? _ref3 : null;
352
+ return (_ref5 = (_getLiteralStringValu = getLiteralStringValue(value)) !== null && _getLiteralStringValu !== void 0 ? _getLiteralStringValu : getTokenCallValue(value)) !== null && _ref5 !== void 0 ? _ref5 : null;
320
353
  },
321
354
  getTokenCallValue: getTokenCallValue,
322
355
  getConfigFlag: getConfigFlag
@@ -339,4 +372,171 @@ var isInRangeList = exports.isInRangeList = function isInRangeList(node, sortedL
339
372
  return range[0] >= currRange.start && range[1] <= currRange.end;
340
373
  });
341
374
  return !!found;
375
+ };
376
+
377
+ /**
378
+ *
379
+ * @param node Icon JSXelement
380
+ * @param newButtonImports list of new button import specifiers
381
+ * @returns if Icon is inside a new button
382
+ */
383
+ var isInsideNewButton = exports.isInsideNewButton = function isInsideNewButton(node, newButtonImports) {
384
+ var _node$parent, _node$parent2;
385
+ var insideNewButton = false;
386
+ if (node.parent && (0, _eslintCodemodUtils.isNodeOfType)(node.parent, 'ArrowFunctionExpression') && (_node$parent = node.parent) !== null && _node$parent !== void 0 && (_node$parent = _node$parent.parent) !== null && _node$parent !== void 0 && _node$parent.parent && (0, _eslintCodemodUtils.isNodeOfType)(node.parent.parent.parent, 'JSXAttribute') && (0, _eslintCodemodUtils.isNodeOfType)(node.parent.parent.parent.name, 'JSXIdentifier') && (_node$parent2 = node.parent) !== null && _node$parent2 !== void 0 && (_node$parent2 = _node$parent2.parent) !== null && _node$parent2 !== void 0 && (_node$parent2 = _node$parent2.parent) !== null && _node$parent2 !== void 0 && _node$parent2.parent && (0, _eslintCodemodUtils.isNodeOfType)(node.parent.parent.parent.parent, 'JSXOpeningElement') && (0, _eslintCodemodUtils.isNodeOfType)(node.parent.parent.parent.parent.name, 'JSXIdentifier') && newButtonImports.has(node.parent.parent.parent.parent.name.name)) {
387
+ insideNewButton = true;
388
+ }
389
+ return insideNewButton;
390
+ };
391
+
392
+ /**
393
+ *
394
+ * @param node Icon JSXelement
395
+ * @param newButtonImports list of legacy button import specifiers
396
+ * @returns if Icon is inside a legacy button
397
+ */
398
+ var isInsideLegacyButton = exports.isInsideLegacyButton = function isInsideLegacyButton(node, legacyButtonImports) {
399
+ var _node$parent3, _node$parent4, _node$parent5, _node$parent6;
400
+ var insideLegacyButton = false;
401
+ if (node.parent && (0, _eslintCodemodUtils.isNodeOfType)(node.parent, 'JSXExpressionContainer') && (_node$parent3 = node.parent) !== null && _node$parent3 !== void 0 && _node$parent3.parent && (0, _eslintCodemodUtils.isNodeOfType)(node.parent.parent, 'JSXAttribute') && (node.parent.parent.name.name === 'iconBefore' || node.parent.parent.name.name === 'iconAfter') && (0, _eslintCodemodUtils.isNodeOfType)((_node$parent4 = node.parent) === null || _node$parent4 === void 0 || (_node$parent4 = _node$parent4.parent) === null || _node$parent4 === void 0 ? void 0 : _node$parent4.parent, 'JSXOpeningElement') && (0, _eslintCodemodUtils.isNodeOfType)((_node$parent5 = node.parent) === null || _node$parent5 === void 0 || (_node$parent5 = _node$parent5.parent) === null || _node$parent5 === void 0 ? void 0 : _node$parent5.parent.name, 'JSXIdentifier') && legacyButtonImports.has((_node$parent6 = node.parent) === null || _node$parent6 === void 0 || (_node$parent6 = _node$parent6.parent) === null || _node$parent6 === void 0 ? void 0 : _node$parent6.parent.name.name)) {
402
+ insideLegacyButton = true;
403
+ }
404
+ return insideLegacyButton;
405
+ };
406
+ var findProp = function findProp(attributes, propName) {
407
+ return attributes.find(function (attr) {
408
+ return attr.type === 'JSXAttribute' && attr.name.name === propName;
409
+ });
410
+ };
411
+
412
+ /**
413
+ *
414
+ * Creates a list of fixers to update the icon import path
415
+ * @param metadata Metadata including the import source and spacing
416
+ * @param fixer The original fix function
417
+ * @param legacyImportNode The import declaration node to replace
418
+ * @param shouldUseMigrationPath The eslint rule config, whether to use migration entrypoint or not
419
+ * @param migrationImportNode The migration import declaration node to replace, only present if shouldUseMigrationPath is false
420
+ * @returns A list of fixers to migrate the icon
421
+ */
422
+ var createImportFix = exports.createImportFix = function createImportFix(_ref6) {
423
+ var fixer = _ref6.fixer,
424
+ legacyImportNode = _ref6.legacyImportNode,
425
+ metadata = _ref6.metadata,
426
+ shouldUseMigrationPath = _ref6.shouldUseMigrationPath,
427
+ migrationImportNode = _ref6.migrationImportNode;
428
+ var fixes = [];
429
+ var importSource = metadata.importSource;
430
+ var importPath = migrationImportNode ? importSource.replace('/migration', '').split('--')[0] : getNewIconNameAndImportPath(importSource, shouldUseMigrationPath).importPath;
431
+
432
+ // replace old icon import with icon import
433
+ if (legacyImportNode && importPath) {
434
+ fixes.push(fixer.replaceText(legacyImportNode.source, "'".concat((0, _eslintCodemodUtils.literal)(importPath), "'")));
435
+ }
436
+ if (migrationImportNode && !shouldUseMigrationPath && importPath) {
437
+ fixes.push(fixer.replaceText(migrationImportNode.source, "'".concat((0, _eslintCodemodUtils.literal)(importPath), "'")));
438
+ }
439
+ return fixes;
440
+ };
441
+
442
+ /**
443
+ * Creates a list of fixers to update the icon props
444
+ * @param node The Icon element to migrate
445
+ * @param metadata Metadata including the import source and spacing
446
+ * @param fixer The original fix function
447
+ * @param legacyImportNode The import declaration node to replace
448
+ * @param shouldUseMigrationPath The eslint rule config, whether to use migration entrypoint or not
449
+ * @param migrationImportNode The migration import declaration node to replace, only present if shouldUseMigrationPath is false
450
+ * @returns A list of fixers to migrate the icon
451
+ */
452
+ var createPropFixes = exports.createPropFixes = function createPropFixes(_ref7) {
453
+ var node = _ref7.node,
454
+ fixer = _ref7.fixer,
455
+ legacyImportNode = _ref7.legacyImportNode,
456
+ metadata = _ref7.metadata,
457
+ shouldUseMigrationPath = _ref7.shouldUseMigrationPath,
458
+ migrationImportNode = _ref7.migrationImportNode;
459
+ var fixes = [];
460
+ var importSource = metadata.importSource,
461
+ spacing = metadata.spacing,
462
+ insideNewButton = metadata.insideNewButton;
463
+ if (shouldUseMigrationPath && !legacyImportNode) {
464
+ return fixes;
465
+ }
466
+ var importPath = migrationImportNode ? importSource.replace('/migration', '').split('--')[0] : getNewIconNameAndImportPath(importSource, shouldUseMigrationPath).importPath;
467
+ var iconType = importPath !== null && importPath !== void 0 && importPath.startsWith('@atlaskit/icon/core') ? 'core' : 'utility';
468
+ if (node.type === 'JSXElement') {
469
+ var openingElement = node.openingElement;
470
+ var attributes = openingElement.attributes;
471
+
472
+ // replace primaryColor prop with color
473
+ var primaryColor = findProp(attributes, 'primaryColor');
474
+ if (primaryColor && primaryColor.type === 'JSXAttribute') {
475
+ fixes.push(fixer.replaceText(primaryColor.name, 'color'));
476
+ }
477
+
478
+ // add color="currentColor" if
479
+ // 1. primaryColor prop is not set
480
+ // 2. icon is not imported from migration entrypoint
481
+ // 3. icon element is not inside a new button
482
+ if (legacyImportNode && !primaryColor && !migrationImportNode &&
483
+ // value type need to be a string in Rule.ReportDescriptor
484
+ insideNewButton !== 'true') {
485
+ fixes.push(fixer.insertTextAfter(openingElement.name, " color=\"currentColor\""));
486
+ }
487
+
488
+ // rename or remove size prop based on shouldUseMigrationPath,
489
+ // add spacing="spacious" if
490
+ // 1. it's in error metadata, which means size is medium
491
+ // 2. no existing spacing prop
492
+ // 3. iconType is "core"
493
+ // 4. icon is not imported from migration entrypoint
494
+ var sizeProp = findProp(attributes, 'size');
495
+ var spacingProp = findProp(attributes, 'spacing');
496
+ if (spacing && !spacingProp && iconType === 'core' && !migrationImportNode) {
497
+ fixes.push(fixer.insertTextAfter(sizeProp || openingElement.name, " spacing=\"".concat(spacing, "\"")));
498
+ }
499
+ if (sizeProp && sizeProp.type === 'JSXAttribute') {
500
+ fixes.push(shouldUseMigrationPath ?
501
+ // replace size prop with LEGACY_size,
502
+ fixer.replaceText(sizeProp.name, 'LEGACY_size') :
503
+ // remove size prop if shouldUseMigrationPath is false
504
+ fixer.remove(sizeProp));
505
+ }
506
+
507
+ // rename or remove secondaryColor prop based on shouldUseMigrationPath
508
+ var secondaryColorProp = findProp(attributes, 'secondaryColor');
509
+ if (secondaryColorProp && secondaryColorProp.type === 'JSXAttribute') {
510
+ fixes.push(shouldUseMigrationPath ?
511
+ // replace secondaryColor prop with LEGACY_secondaryColor
512
+ fixer.replaceText(secondaryColorProp.name, 'LEGACY_secondaryColor') :
513
+ // remove secondaryColor prop if shouldUseMigrationPath is false
514
+ fixer.remove(secondaryColorProp));
515
+ }
516
+
517
+ // remove LEGACY props
518
+ if (!shouldUseMigrationPath) {
519
+ ['LEGACY_size', 'LEGACY_margin', 'LEGACY_fallbackIcon', 'LEGACY_secondaryColor'].forEach(function (propName) {
520
+ var legacyProp = findProp(attributes, propName);
521
+ if (legacyProp && legacyProp.type === 'JSXAttribute') {
522
+ fixes.push(fixer.remove(legacyProp));
523
+ }
524
+ });
525
+ }
526
+ }
527
+ return fixes;
528
+ };
529
+
530
+ /**
531
+ * Check if the new icon exists in the migration map
532
+ */
533
+ var checkIfNewIconExist = exports.checkIfNewIconExist = function checkIfNewIconExist(error) {
534
+ var _error$data;
535
+ if (!((_error$data = error.data) !== null && _error$data !== void 0 && _error$data.importSource)) {
536
+ return false;
537
+ }
538
+ var iconKey = getIconKey(error.data.importSource);
539
+ var _ref8 = _UNSAFE_migrationMap.default[iconKey] || {},
540
+ newIcon = _ref8.newIcon;
541
+ return Boolean(newIcon);
342
542
  };
@@ -11,6 +11,7 @@ var _helpers = require("./helpers");
11
11
  var rule = (0, _createRule.createLintRule)({
12
12
  meta: {
13
13
  name: 'no-legacy-icons',
14
+ fixable: 'code',
14
15
  type: 'problem',
15
16
  docs: {
16
17
  description: 'Enforces no legacy icons are used.',
@@ -28,6 +29,9 @@ var rule = (0, _createRule.createLintRule)({
28
29
  },
29
30
  quiet: {
30
31
  type: 'boolean'
32
+ },
33
+ shouldUseMigrationPath: {
34
+ type: 'boolean'
31
35
  }
32
36
  },
33
37
  additionalProperties: false
@@ -4,4 +4,4 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.upcomingIcons = void 0;
7
- var upcomingIcons = exports.upcomingIcons = ['app-switcher', 'check-circle-outline', 'export', 'file', 'flag-filled', 'lightbulb-filled', 'lock-filled', 'menu-expand', 'menu', 'notification-direct', 'notification', 'people-group', 'people', 'refresh', 'switcher', 'editor/align-image-center', 'editor/align-image-left', 'editor/align-image-right', 'editor/background-color', 'editor/bold', 'editor/indent', 'editor/italic', 'editor/horizontal-rule', 'editor-layout-three-equal', 'editor-layout-three-with-sidebars', 'editor/layout-two-equal', 'editor/layout-two-left-sidebars', 'editor/layout-two-right-sidebar', 'editor/media-center', 'editor/media-full-width', 'editor/media-wide', 'editor/number-list', 'emoji/activity', 'editor/file', 'emoji/flags', 'emoji/food', 'emoji/nature', 'emoji/people', 'emoji/productivity', 'emoji/travel', 'bitbucket/repos'];
7
+ var upcomingIcons = exports.upcomingIcons = [];
@@ -1,5 +1,5 @@
1
1
  import { isNodeOfType } from 'eslint-codemod-utils';
2
- import { addToListOfRanges, canAutoMigrateNewIconBasedOnSize, canMigrateColor, createAutoMigrationError, createCantFindSuitableReplacementError, createCantMigrateColorError, createCantMigrateFunctionUnknownError, createCantMigrateIdentifierError, createCantMigrateIdentifierMapOrArrayError, createCantMigrateReExportError, createCantMigrateSizeUnknown, createCantMigrateSpreadPropsError, createGuidance, createHelpers, getMigrationMapObject, getUpcomingIcons, isInRangeList, isSize, locToString } from './helpers';
2
+ import { addToListOfRanges, canAutoMigrateNewIconBasedOnSize, canMigrateColor, checkIfNewIconExist, createAutoMigrationError, createCantFindSuitableReplacementError, createCantMigrateColorError, createCantMigrateFunctionUnknownError, createCantMigrateIdentifierError, createCantMigrateIdentifierMapOrArrayError, createCantMigrateReExportError, createCantMigrateSizeUnknown, createCantMigrateSpreadPropsError, createGuidance, createHelpers, createImportFix, createPropFixes, getMigrationMapObject, getUpcomingIcons, isInRangeList, isInsideLegacyButton, isInsideNewButton, isSize, locToString } from './helpers';
3
3
  export const createChecks = context => {
4
4
  //create global variables to be shared by the checks
5
5
  const {
@@ -7,15 +7,19 @@ export const createChecks = context => {
7
7
  getConfigFlag
8
8
  } = createHelpers(context);
9
9
  const legacyIconImports = {};
10
+ const migrationIconImports = {};
10
11
  const newButtonImports = new Set();
12
+ const legacyButtonImports = new Set();
11
13
  const errorsManual = {};
12
14
  const errorsAuto = {};
13
15
  let guidance = {};
16
+ let autoIconJSXElementOccurrenceCount = 0;
14
17
 
15
18
  // Extract parameters
16
19
  const shouldErrorForManualMigration = getConfigFlag('shouldErrorForManualMigration', true);
17
20
  const shouldErrorForAutoMigration = getConfigFlag('shouldErrorForAutoMigration', true);
18
21
  const isQuietMode = getConfigFlag('quiet', false);
22
+ const shouldUseMigrationPath = getConfigFlag('shouldUseMigrationPath', true);
19
23
 
20
24
  // Sorted list of ranges
21
25
  let errorRanges = [];
@@ -33,12 +37,26 @@ export const createChecks = context => {
33
37
  if (spec.local.name) {
34
38
  legacyIconImports[spec.local.name] = {
35
39
  packageName: moduleSource,
36
- exported: false
40
+ exported: false,
41
+ importNode: node
37
42
  };
38
43
  }
39
44
  }
40
45
  }
41
46
 
47
+ // Find the imports for icons that import from migration path
48
+ if (moduleSource && typeof moduleSource === 'string' && (moduleSource.startsWith('@atlaskit/icon/core/migration/') || moduleSource.startsWith('@atlaskit/icon/utility/migration/')) && node.specifiers.length) {
49
+ node.specifiers.forEach(spec => {
50
+ if (spec.local.name) {
51
+ migrationIconImports[spec.local.name] = {
52
+ packageName: moduleSource,
53
+ exported: false,
54
+ importNode: node
55
+ };
56
+ }
57
+ });
58
+ }
59
+
42
60
  // Find the imports for new button and IconButton
43
61
  if (typeof moduleSource === 'string' && moduleSource.startsWith('@atlaskit/button/new') && node.specifiers.length) {
44
62
  for (const spec of node.specifiers) {
@@ -49,6 +67,15 @@ export const createChecks = context => {
49
67
  }
50
68
  }
51
69
  }
70
+
71
+ // Find the imports for legacy default button
72
+ if (typeof moduleSource === 'string' && (moduleSource === '@atlaskit/button' || moduleSource === '@atlaskit/button/standard-button' || moduleSource === '@atlaskit/button/loading-button' || moduleSource === '@atlaskit/button/custom-theme-button') && node.specifiers.length) {
73
+ for (const spec of node.specifiers) {
74
+ if (spec.type === 'ImportDefaultSpecifier') {
75
+ legacyButtonImports.add(spec.local.name);
76
+ }
77
+ }
78
+ }
52
79
  };
53
80
 
54
81
  /**
@@ -91,7 +118,10 @@ export const createChecks = context => {
91
118
  }
92
119
  createCantMigrateReExportError(node, packageName, exportName, errorsManual);
93
120
  addToListOfRanges(node, errorRanges);
94
- guidance[locToString(node)] = createGuidance(packageName);
121
+ guidance[locToString(node)] = createGuidance({
122
+ iconPackage: packageName,
123
+ shouldUseMigrationPath
124
+ });
95
125
  };
96
126
 
97
127
  /**
@@ -106,7 +136,10 @@ export const createChecks = context => {
106
136
  for (const spec of node.specifiers) {
107
137
  createCantMigrateReExportError(spec, moduleSource, spec.exported.name, errorsManual);
108
138
  addToListOfRanges(spec, errorRanges);
109
- guidance[locToString(spec)] = createGuidance(moduleSource);
139
+ guidance[locToString(spec)] = createGuidance({
140
+ iconPackage: moduleSource,
141
+ shouldUseMigrationPath
142
+ });
110
143
  }
111
144
  }
112
145
  } else if (node.declaration && isNodeOfType(node.declaration, 'VariableDeclaration')) {
@@ -115,7 +148,10 @@ export const createChecks = context => {
115
148
  if (isNodeOfType(decl, 'VariableDeclarator') && Object.keys(decl).includes('init') && decl.init && isNodeOfType(decl.init, 'Identifier') && Object.keys(legacyIconImports).includes(decl.init.name)) {
116
149
  createCantMigrateReExportError(node, legacyIconImports[decl.init.name].packageName, decl.init.name, errorsManual);
117
150
  addToListOfRanges(node, errorRanges);
118
- guidance[locToString(node)] = createGuidance(legacyIconImports[decl.init.name].packageName);
151
+ guidance[locToString(node)] = createGuidance({
152
+ iconPackage: legacyIconImports[decl.init.name].packageName,
153
+ shouldUseMigrationPath
154
+ });
119
155
  }
120
156
  }
121
157
  } else if (!node.source && node.specifiers && node.specifiers.length > 0) {
@@ -134,7 +170,10 @@ export const createChecks = context => {
134
170
  };
135
171
  createCantMigrateReExportError(spec, legacyIconImports[spec.local.name].packageName, spec.exported.name, errorsManual);
136
172
  addToListOfRanges(spec, errorRanges);
137
- guidance[locToString(spec)] = createGuidance(legacyIconImports[spec.local.name].packageName);
173
+ guidance[locToString(spec)] = createGuidance({
174
+ iconPackage: legacyIconImports[spec.local.name].packageName,
175
+ shouldUseMigrationPath
176
+ });
138
177
  }
139
178
  }
140
179
  }
@@ -151,7 +190,10 @@ export const createChecks = context => {
151
190
  if (node.name && Object.keys(legacyIconImports).includes(node.name) && legacyIconImports[node.name].packageName) {
152
191
  createCantMigrateIdentifierMapOrArrayError(node, legacyIconImports[node.name].packageName, node.name, errorsManual);
153
192
  addToListOfRanges(node, errorRanges);
154
- guidance[locToString(node)] = createGuidance(legacyIconImports[node.name].packageName);
193
+ guidance[locToString(node)] = createGuidance({
194
+ iconPackage: legacyIconImports[node.name].packageName,
195
+ shouldUseMigrationPath
196
+ });
155
197
  }
156
198
  };
157
199
 
@@ -177,29 +219,59 @@ export const createChecks = context => {
177
219
  const isNewIconMigratable = canAutoMigrateNewIconBasedOnSize(upcomingIcon ? upcomingIcon.sizeGuidance.medium : migrationMapObject === null || migrationMapObject === void 0 ? void 0 : (_migrationMapObject$s = migrationMapObject.sizeGuidance) === null || _migrationMapObject$s === void 0 ? void 0 : _migrationMapObject$s.medium);
178
220
  const isInNewButton = isNodeOfType(node.parent.parent.parent.name, 'JSXIdentifier') && newButtonImports.has(node.parent.parent.parent.name.name);
179
221
  if (newIcon && isInNewButton && isNewIconMigratable || upcomingIcon && isInNewButton && isNewIconMigratable) {
180
- createAutoMigrationError(node, legacyIconImports[node.name].packageName, node.name, errorsAuto);
222
+ createAutoMigrationError({
223
+ node,
224
+ importSource: legacyIconImports[node.name].packageName,
225
+ iconName: node.name,
226
+ errors: errorsAuto
227
+ });
181
228
  addToListOfRanges(node, errorRanges);
182
- guidance[locToString(node)] = createGuidance(legacyIconImports[node.name].packageName, isInNewButton, 'medium');
229
+ guidance[locToString(node)] = createGuidance({
230
+ iconPackage: legacyIconImports[node.name].packageName,
231
+ insideNewButton: true,
232
+ size: 'medium',
233
+ shouldUseMigrationPath
234
+ });
183
235
  } else if (!newIcon && !upcomingIcon || !isNewIconMigratable) {
184
236
  createCantFindSuitableReplacementError(node, legacyIconImports[node.name].packageName, node.name, errorsManual);
185
237
  addToListOfRanges(node, errorRanges);
186
- guidance[locToString(node)] = createGuidance(legacyIconImports[node.name].packageName, isInNewButton);
238
+ guidance[locToString(node)] = createGuidance({
239
+ iconPackage: legacyIconImports[node.name].packageName,
240
+ insideNewButton: isInNewButton,
241
+ shouldUseMigrationPath
242
+ });
187
243
  } else if (!isInNewButton) {
188
244
  createCantMigrateFunctionUnknownError(node, legacyIconImports[node.name].packageName, node.name, errorsManual);
189
245
  addToListOfRanges(node, errorRanges);
190
- guidance[locToString(node)] = createGuidance(legacyIconImports[node.name].packageName, isInNewButton);
246
+ guidance[locToString(node)] = createGuidance({
247
+ iconPackage: legacyIconImports[node.name].packageName,
248
+ insideNewButton: false,
249
+ shouldUseMigrationPath
250
+ });
191
251
  }
192
252
  }
193
253
  };
194
254
  const checkIconReference = node => {
195
- //check the reference to see if it's a legacy icon, if not exit early
196
- if (!Object.keys(legacyIconImports).includes(node.name)) {
197
- return;
198
- }
199
255
  //if this is an import statement then exit early
200
256
  if (node.parent && (isNodeOfType(node.parent, 'ImportSpecifier') || isNodeOfType(node.parent, 'ImportDefaultSpecifier'))) {
201
257
  return;
202
258
  }
259
+
260
+ // Flag icons imported from migration path
261
+ if (!shouldUseMigrationPath && Object.keys(migrationIconImports).includes(node.name)) {
262
+ createAutoMigrationError({
263
+ node,
264
+ importSource: migrationIconImports[node.name].packageName,
265
+ iconName: node.name,
266
+ errors: errorsAuto
267
+ });
268
+ }
269
+
270
+ //check the reference to see if it's a legacy icon, if not exit early
271
+ if (!Object.keys(legacyIconImports).includes(node.name)) {
272
+ return;
273
+ }
274
+
203
275
  //if in Fallback prop, do not error
204
276
  if (node.parent && node.parent.parent && isNodeOfType(node.parent.parent, 'JSXAttribute') && isNodeOfType(node.parent.parent.name, 'JSXIdentifier') && node.parent.parent.name.name === 'LEGACY_fallbackIcon') {
205
277
  return;
@@ -223,15 +295,36 @@ export const createChecks = context => {
223
295
  return;
224
296
  }
225
297
  const name = node.openingElement.name.name;
298
+
299
+ // Flag icons imported from migration path
300
+ if (!shouldUseMigrationPath && Object.keys(migrationIconImports).includes(name)) {
301
+ var _sizeProp$value;
302
+ const sizeProp = node.openingElement.attributes.find(attribute => attribute.type === 'JSXAttribute' && (attribute.name.name === 'size' || attribute.name.name === 'LEGACY_size'));
303
+ const insideNewButton = isInsideNewButton(node, newButtonImports);
304
+ // Add spacious spacing if:
305
+ // 1. size is medium, or not set (default is medium)
306
+ // 2. not inside a new or legacy button
307
+ const shouldAddSpaciousSpacing = (sizeProp && sizeProp.type === 'JSXAttribute' && ((_sizeProp$value = sizeProp.value) === null || _sizeProp$value === void 0 ? void 0 : _sizeProp$value.type) === 'Literal' && sizeProp.value.value === 'medium' || !sizeProp) && !isInsideNewButton(node, newButtonImports) && !isInsideLegacyButton(node, legacyButtonImports);
308
+ createAutoMigrationError({
309
+ node,
310
+ importSource: migrationIconImports[name].packageName,
311
+ iconName: name,
312
+ errors: errorsAuto,
313
+ shouldAddSpaciousSpacing,
314
+ insideNewButton
315
+ });
316
+ }
317
+
226
318
  // Legacy icons rendered as JSX elements
227
319
  if (Object.keys(legacyIconImports).includes(name)) {
228
- var _node$parent2, _node$parent2$parent, _node$parent3, _node$parent3$parent, _node$parent3$parent$, _size, _size2;
320
+ var _size, _size2, _sizeProp$value2;
229
321
  // Determine if inside a new button - if so:
230
322
  // - Assume spread props are safe - still error if props explicitly set to unmigratable values
231
- let insideNewButton = false;
232
- if (node.parent && isNodeOfType(node.parent, 'ArrowFunctionExpression') && (_node$parent2 = node.parent) !== null && _node$parent2 !== void 0 && (_node$parent2$parent = _node$parent2.parent) !== null && _node$parent2$parent !== void 0 && _node$parent2$parent.parent && isNodeOfType(node.parent.parent.parent, 'JSXAttribute') && isNodeOfType(node.parent.parent.parent.name, 'JSXIdentifier') && (_node$parent3 = node.parent) !== null && _node$parent3 !== void 0 && (_node$parent3$parent = _node$parent3.parent) !== null && _node$parent3$parent !== void 0 && (_node$parent3$parent$ = _node$parent3$parent.parent) !== null && _node$parent3$parent$ !== void 0 && _node$parent3$parent$.parent && isNodeOfType(node.parent.parent.parent.parent, 'JSXOpeningElement') && isNodeOfType(node.parent.parent.parent.parent.name, 'JSXIdentifier') && newButtonImports.has(node.parent.parent.parent.parent.name.name)) {
233
- insideNewButton = true;
234
- }
323
+ const insideNewButton = isInsideNewButton(node, newButtonImports);
324
+
325
+ // Determine if inside a legacy default button - if so:
326
+ // the auto fixer will add spacing prop to the medium size icon
327
+ const insideLegacyButton = isInsideLegacyButton(node, legacyButtonImports);
235
328
 
236
329
  // Find size prop on node
237
330
  let size = 'medium';
@@ -299,13 +392,32 @@ export const createChecks = context => {
299
392
  const upcomingIcon = getUpcomingIcons(legacyIconImports[name].packageName);
300
393
  const newIcon = migrationMapObject === null || migrationMapObject === void 0 ? void 0 : migrationMapObject.newIcon;
301
394
  const isNewIconMigratable = canAutoMigrateNewIconBasedOnSize(upcomingIcon ? upcomingIcon.sizeGuidance[(_size = size) !== null && _size !== void 0 ? _size : 'medium'] : migrationMapObject === null || migrationMapObject === void 0 ? void 0 : migrationMapObject.sizeGuidance[(_size2 = size) !== null && _size2 !== void 0 ? _size2 : 'medium']);
395
+
396
+ // Add spacious spacing if:
397
+ // 1. size is medium, or not set (default is medium)
398
+ // 2. not inside a new or legacy button
399
+ const sizeProp = node.openingElement.attributes.find(attribute => attribute.type === 'JSXAttribute' && (attribute.name.name === 'size' || attribute.name.name === 'LEGACY_size'));
400
+ const shouldAddSpaciousSpacing = (sizeProp && sizeProp.type === 'JSXAttribute' && ((_sizeProp$value2 = sizeProp.value) === null || _sizeProp$value2 === void 0 ? void 0 : _sizeProp$value2.type) === 'Literal' && sizeProp.value.value === 'medium' || !sizeProp) && !insideNewButton && !insideLegacyButton;
302
401
  if (!hasManualMigration && (newIcon || upcomingIcon) && isNewIconMigratable) {
303
- createAutoMigrationError(node, legacyIconImports[name].packageName, name, errorsAuto);
402
+ autoIconJSXElementOccurrenceCount++;
403
+ createAutoMigrationError({
404
+ node,
405
+ importSource: legacyIconImports[name].packageName,
406
+ iconName: name,
407
+ errors: errorsAuto,
408
+ shouldAddSpaciousSpacing,
409
+ insideNewButton
410
+ });
304
411
  } else if ((!newIcon && !upcomingIcon || !isNewIconMigratable) && size) {
305
412
  createCantFindSuitableReplacementError(node, legacyIconImports[name].packageName, name, errorsManual, upcomingIcon ? true : migrationMapObject ? true : false);
306
413
  }
307
414
  addToListOfRanges(node, errorRanges);
308
- guidance[locToString(node)] = createGuidance(legacyIconImports[name].packageName, insideNewButton, size && isSize(size) ? size : undefined);
415
+ guidance[locToString(node)] = createGuidance({
416
+ iconPackage: legacyIconImports[name].packageName,
417
+ insideNewButton,
418
+ size: size && isSize(size) ? size : undefined,
419
+ shouldUseMigrationPath
420
+ });
309
421
  }
310
422
  };
311
423
 
@@ -319,7 +431,10 @@ export const createChecks = context => {
319
431
  if (isNodeOfType(arg, 'Identifier') && Object.keys(legacyIconImports).includes(arg.name) && legacyIconImports[arg.name].packageName) {
320
432
  createCantMigrateFunctionUnknownError(node, legacyIconImports[arg.name].packageName, arg.name, errorsManual);
321
433
  addToListOfRanges(node, errorRanges);
322
- guidance[locToString(node)] = createGuidance(legacyIconImports[arg.name].packageName);
434
+ guidance[locToString(node)] = createGuidance({
435
+ iconPackage: legacyIconImports[arg.name].packageName,
436
+ shouldUseMigrationPath
437
+ });
323
438
  }
324
439
  }
325
440
  }
@@ -377,7 +492,58 @@ export const createChecks = context => {
377
492
  if (Object.keys(error).includes('data') && error.data) {
378
493
  error.data.guidance = guidanceMessage;
379
494
  }
380
- context.report(error);
495
+ context.report({
496
+ ...error,
497
+ fix: fixer => {
498
+ var _legacyIconImports$er, _migrationIconImports;
499
+ // don't migration if the new icon is not available
500
+ if (!error.data || shouldUseMigrationPath && !checkIfNewIconExist(error)) {
501
+ return [];
502
+ }
503
+ const fixArguments = {
504
+ metadata: error.data,
505
+ legacyImportNode: (_legacyIconImports$er = legacyIconImports[error.data.iconName]) === null || _legacyIconImports$er === void 0 ? void 0 : _legacyIconImports$er.importNode,
506
+ migrationImportNode: (_migrationIconImports = migrationIconImports[error.data.iconName]) === null || _migrationIconImports === void 0 ? void 0 : _migrationIconImports.importNode,
507
+ shouldUseMigrationPath
508
+ };
509
+ const propsFixes = createPropFixes({
510
+ ...fixArguments,
511
+ node,
512
+ fixer
513
+ });
514
+ let importFixes = [];
515
+ // Otherwise if there are multiple occurrences of the icon, import path will be handled after the prop fix
516
+ if (autoIconJSXElementOccurrenceCount <= 1) {
517
+ importFixes = createImportFix({
518
+ ...fixArguments,
519
+ fixer
520
+ });
521
+ }
522
+ return [...propsFixes, ...importFixes];
523
+ }
524
+ });
525
+ }
526
+ }
527
+
528
+ // Update import path at the end if there are multiple occurrences of the icon
529
+ if (autoIconJSXElementOccurrenceCount > 1) {
530
+ for (const [_, error] of Object.entries(errorsAuto)) {
531
+ context.report({
532
+ ...error,
533
+ fix: fixer => {
534
+ var _legacyIconImports$er2, _migrationIconImports2;
535
+ if (!error.data || shouldUseMigrationPath && !checkIfNewIconExist(error)) {
536
+ return [];
537
+ }
538
+ return createImportFix({
539
+ metadata: error.data,
540
+ fixer,
541
+ legacyImportNode: (_legacyIconImports$er2 = legacyIconImports[error.data.iconName]) === null || _legacyIconImports$er2 === void 0 ? void 0 : _legacyIconImports$er2.importNode,
542
+ migrationImportNode: (_migrationIconImports2 = migrationIconImports[error.data.iconName]) === null || _migrationIconImports2 === void 0 ? void 0 : _migrationIconImports2.importNode,
543
+ shouldUseMigrationPath
544
+ });
545
+ }
546
+ });
381
547
  }
382
548
  }
383
549
  }