@atlaskit/eslint-plugin-design-system 10.9.0 → 10.10.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 (101) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/README.md +1 -0
  3. package/dist/cjs/presets/all.codegen.js +2 -1
  4. package/dist/cjs/rules/ensure-design-token-usage/index.js +199 -227
  5. package/dist/cjs/rules/index.codegen.js +3 -1
  6. package/dist/cjs/rules/no-custom-icons/checks/has-prop.js +12 -0
  7. package/dist/cjs/rules/no-custom-icons/checks/is-from-import-source.js +42 -0
  8. package/dist/cjs/rules/no-custom-icons/checks/is-imported-jsx-element.js +10 -0
  9. package/dist/cjs/rules/no-custom-icons/index.js +67 -0
  10. package/dist/cjs/rules/no-legacy-icons/checks.js +63 -67
  11. package/dist/cjs/rules/no-legacy-icons/helpers.js +29 -24
  12. package/dist/cjs/rules/no-legacy-icons/index.js +15 -88
  13. package/dist/cjs/rules/use-tokens-typography/index.js +4 -8
  14. package/dist/cjs/rules/utils/error-boundary.js +58 -11
  15. package/dist/es2019/presets/all.codegen.js +2 -1
  16. package/dist/es2019/rules/ensure-design-token-usage/index.js +16 -30
  17. package/dist/es2019/rules/index.codegen.js +3 -1
  18. package/dist/es2019/rules/no-custom-icons/checks/has-prop.js +4 -0
  19. package/dist/es2019/rules/no-custom-icons/checks/is-from-import-source.js +23 -0
  20. package/dist/es2019/rules/no-custom-icons/checks/is-imported-jsx-element.js +4 -0
  21. package/dist/es2019/rules/no-custom-icons/index.js +61 -0
  22. package/dist/es2019/rules/no-legacy-icons/checks.js +20 -33
  23. package/dist/es2019/rules/no-legacy-icons/helpers.js +24 -21
  24. package/dist/es2019/rules/no-legacy-icons/index.js +15 -70
  25. package/dist/es2019/rules/use-tokens-typography/index.js +3 -7
  26. package/dist/es2019/rules/utils/error-boundary.js +55 -11
  27. package/dist/esm/presets/all.codegen.js +2 -1
  28. package/dist/esm/rules/ensure-design-token-usage/index.js +199 -227
  29. package/dist/esm/rules/index.codegen.js +3 -1
  30. package/dist/esm/rules/no-custom-icons/checks/has-prop.js +6 -0
  31. package/dist/esm/rules/no-custom-icons/checks/is-from-import-source.js +36 -0
  32. package/dist/esm/rules/no-custom-icons/checks/is-imported-jsx-element.js +4 -0
  33. package/dist/esm/rules/no-custom-icons/index.js +61 -0
  34. package/dist/esm/rules/no-legacy-icons/checks.js +64 -68
  35. package/dist/esm/rules/no-legacy-icons/helpers.js +28 -23
  36. package/dist/esm/rules/no-legacy-icons/index.js +15 -88
  37. package/dist/esm/rules/use-tokens-typography/index.js +4 -8
  38. package/dist/esm/rules/utils/error-boundary.js +56 -10
  39. package/dist/types/index.codegen.d.ts +1 -0
  40. package/dist/types/presets/all.codegen.d.ts +2 -1
  41. package/dist/types/rules/index.codegen.d.ts +1 -0
  42. package/dist/types/rules/no-custom-icons/checks/has-prop.d.ts +2 -0
  43. package/dist/types/rules/no-custom-icons/checks/is-from-import-source.d.ts +8 -0
  44. package/dist/types/rules/no-custom-icons/checks/is-imported-jsx-element.d.ts +8 -0
  45. package/dist/types/rules/no-custom-icons/index.d.ts +3 -0
  46. package/dist/types/rules/no-legacy-icons/helpers.d.ts +3 -13
  47. package/dist/types/rules/no-legacy-icons/index.d.ts +1 -2
  48. package/dist/types/rules/utils/error-boundary.d.ts +8 -5
  49. package/dist/types-ts4.5/index.codegen.d.ts +1 -0
  50. package/dist/types-ts4.5/presets/all.codegen.d.ts +2 -1
  51. package/dist/types-ts4.5/rules/index.codegen.d.ts +1 -0
  52. package/dist/types-ts4.5/rules/no-custom-icons/checks/has-prop.d.ts +2 -0
  53. package/dist/types-ts4.5/rules/no-custom-icons/checks/is-from-import-source.d.ts +8 -0
  54. package/dist/types-ts4.5/rules/no-custom-icons/checks/is-imported-jsx-element.d.ts +8 -0
  55. package/dist/types-ts4.5/rules/no-custom-icons/index.d.ts +3 -0
  56. package/dist/types-ts4.5/rules/no-legacy-icons/helpers.d.ts +3 -13
  57. package/dist/types-ts4.5/rules/no-legacy-icons/index.d.ts +1 -2
  58. package/dist/types-ts4.5/rules/utils/error-boundary.d.ts +8 -5
  59. package/package.json +2 -2
  60. package/constellation/consistent-css-prop-usage/usage.mdx +0 -210
  61. package/constellation/ensure-design-token-usage/usage.mdx +0 -77
  62. package/constellation/ensure-design-token-usage-preview/usage.mdx +0 -6
  63. package/constellation/icon-label/usage.mdx +0 -41
  64. package/constellation/index/props.mdx +0 -30
  65. package/constellation/index/usage.mdx +0 -56
  66. package/constellation/no-banned-imports/usage.mdx +0 -19
  67. package/constellation/no-css-tagged-template-expression/usage.mdx +0 -71
  68. package/constellation/no-deprecated-apis/usage.mdx +0 -82
  69. package/constellation/no-deprecated-design-token-usage/usage.mdx +0 -30
  70. package/constellation/no-deprecated-imports/usage.mdx +0 -66
  71. package/constellation/no-direct-use-of-web-platform-drag-and-drop/usage.mdx +0 -149
  72. package/constellation/no-empty-styled-expression/usage.mdx +0 -82
  73. package/constellation/no-exported-css/usage.mdx +0 -55
  74. package/constellation/no-exported-keyframes/usage.mdx +0 -55
  75. package/constellation/no-html-anchor/usage.mdx +0 -46
  76. package/constellation/no-html-button/usage.mdx +0 -52
  77. package/constellation/no-invalid-css-map/usage.mdx +0 -210
  78. package/constellation/no-keyframes-tagged-template-expression/usage.mdx +0 -80
  79. package/constellation/no-legacy-icons/usage.mdx +0 -42
  80. package/constellation/no-margin/usage.mdx +0 -21
  81. package/constellation/no-nested-styles/usage.mdx +0 -47
  82. package/constellation/no-physical-properties/usage.mdx +0 -53
  83. package/constellation/no-styled-tagged-template-expression/usage.mdx +0 -135
  84. package/constellation/no-unsafe-design-token-usage/usage.mdx +0 -50
  85. package/constellation/no-unsafe-style-overrides/usage.mdx +0 -52
  86. package/constellation/no-unsupported-drag-and-drop-libraries/usage.mdx +0 -23
  87. package/constellation/prefer-primitives/usage.mdx +0 -32
  88. package/constellation/use-button-group-label/usage.mdx +0 -60
  89. package/constellation/use-drawer-label/usage.mdx +0 -55
  90. package/constellation/use-heading/usage.mdx +0 -31
  91. package/constellation/use-heading-level-in-spotlight-card/usage.mdx +0 -21
  92. package/constellation/use-href-in-link-item/usage.mdx +0 -19
  93. package/constellation/use-latest-xcss-syntax/usage.mdx +0 -33
  94. package/constellation/use-latest-xcss-syntax-typography/usage.mdx +0 -34
  95. package/constellation/use-menu-section-title/usage.mdx +0 -55
  96. package/constellation/use-popup-label/usage.mdx +0 -56
  97. package/constellation/use-primitives/usage.mdx +0 -78
  98. package/constellation/use-primitives-text/usage.mdx +0 -35
  99. package/constellation/use-tokens-space/usage.mdx +0 -34
  100. package/constellation/use-tokens-typography/usage.mdx +0 -55
  101. package/constellation/use-visually-hidden/usage.mdx +0 -35
@@ -1,9 +1,18 @@
1
1
  "use strict";
2
2
 
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
3
4
  Object.defineProperty(exports, "__esModule", {
4
5
  value: true
5
6
  });
6
- exports.errorBoundary = void 0;
7
+ exports.errorBoundary = errorBoundary;
8
+ var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
9
+ var _typeof2 = _interopRequireDefault(require("@babel/runtime/helpers/typeof"));
10
+ // eslint-disable-next-line import/no-extraneous-dependencies
11
+
12
+ // Need to intersect type RuleListener with a generic function to allow use of Parameters<...> to be used
13
+
14
+ // Allow config to be to be easily passed from rules
15
+
7
16
  /**
8
17
  * ESLint rules should NEVER throw exceptions, because that breaks the VSCode ESLint server
9
18
  * (and probably the IntelliJ one too), which causes linting to fail in a file.
@@ -11,14 +20,52 @@ exports.errorBoundary = void 0;
11
20
  * It also breaks CI, which was the reason this error boundary was added. It's a final
12
21
  * catch all.
13
22
  */
14
- var errorBoundary = exports.errorBoundary = function errorBoundary(func, _ref) {
15
- var config = _ref.config;
16
- try {
17
- func();
18
- } catch (err) {
19
- if (!config.failSilently) {
20
- // eslint-disable-next-line no-console
21
- console.warn(err);
22
- }
23
+ function errorBoundary(ruleOrRules) {
24
+ var config = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
25
+ var failSilently = failSilentlyFromConfig(config);
26
+ if (isSingleRuleListener(ruleOrRules)) {
27
+ return wrapSingleRuleListener(ruleOrRules, failSilently);
28
+ }
29
+ return wrapRuleListener(ruleOrRules, failSilently);
30
+ }
31
+ function isSingleRuleListener(rule) {
32
+ return typeof rule === 'function';
33
+ }
34
+ function failSilentlyFromConfig(c) {
35
+ switch ((0, _typeof2.default)(c)) {
36
+ case 'undefined':
37
+ return false;
38
+ case 'boolean':
39
+ return c;
40
+ case 'object':
41
+ if ('failSilently' in c) {
42
+ var _c$failSilently;
43
+ return (_c$failSilently = c.failSilently) !== null && _c$failSilently !== void 0 ? _c$failSilently : false;
44
+ } else if ('config' in c) {
45
+ var _c$config$failSilentl;
46
+ return (_c$config$failSilentl = c.config.failSilently) !== null && _c$config$failSilentl !== void 0 ? _c$config$failSilentl : false;
47
+ }
48
+ return false;
49
+ default:
50
+ throw new Error('Invalid config');
23
51
  }
24
- };
52
+ }
53
+ function wrapSingleRuleListener(rule, failSilently) {
54
+ return function () {
55
+ try {
56
+ rule.apply(void 0, arguments);
57
+ } catch (err) {
58
+ if (!failSilently) {
59
+ // eslint-disable-next-line no-console
60
+ console.warn(err);
61
+ }
62
+ }
63
+ };
64
+ }
65
+ function wrapRuleListener(ruleListener, failSilently) {
66
+ return Object.entries(ruleListener).reduce(function (wrappedRuleListener, e) {
67
+ var ruleName = e[0];
68
+ var rule = e[1];
69
+ return Object.assign(wrappedRuleListener, (0, _defineProperty2.default)({}, ruleName, wrapSingleRuleListener(rule, failSilently)));
70
+ }, {});
71
+ }
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * THIS FILE WAS CREATED VIA CODEGEN DO NOT MODIFY {@see http://go/af-codegen}
3
- * @codegen <<SignedSource::9bb5329713677543f16019954484ae2f>>
3
+ * @codegen <<SignedSource::ab43b6e1a867d07b9a27eae78d48834a>>
4
4
  * @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
5
5
  */
6
6
  export default {
@@ -12,6 +12,7 @@ export default {
12
12
  '@atlaskit/design-system/icon-label': 'warn',
13
13
  '@atlaskit/design-system/no-banned-imports': 'error',
14
14
  '@atlaskit/design-system/no-css-tagged-template-expression': 'error',
15
+ '@atlaskit/design-system/no-custom-icons': 'warn',
15
16
  '@atlaskit/design-system/no-deprecated-apis': 'error',
16
17
  '@atlaskit/design-system/no-deprecated-design-token-usage': 'warn',
17
18
  '@atlaskit/design-system/no-deprecated-imports': 'error',
@@ -27,25 +27,21 @@ const createWithConfig = initialConfig => context => {
27
27
  failSilently: (userConfig === null || userConfig === void 0 ? void 0 : userConfig.failSilently) || defaultConfig.failSilently
28
28
  };
29
29
  let tokenNode = null;
30
- return {
31
- ImportDeclaration: node => errorBoundary(() => {
30
+ return errorBoundary({
31
+ ImportDeclaration: node => {
32
32
  if (node.source.value === '@atlaskit/tokens' && config.applyImport) {
33
33
  tokenNode = node;
34
34
  }
35
- }, {
36
- config
37
- }),
35
+ },
38
36
  // For expressions within template literals (e.g. `color: ${red}`) - color only
39
- 'TemplateLiteral > Identifier': node => errorBoundary(() => {
37
+ 'TemplateLiteral > Identifier': node => {
40
38
  if (config.domains.includes('color')) {
41
39
  return lintTemplateIdentifierForColor(node, context, config);
42
40
  }
43
41
  return;
44
- }, {
45
- config
46
- }),
42
+ },
47
43
  // const styles = css({ color: 'red', margin: '4px' }), styled.div({ color: 'red', margin: '4px' })
48
- ObjectExpression: parentNode => errorBoundary(() => {
44
+ ObjectExpression: parentNode => {
49
45
  const {
50
46
  references
51
47
  } = context.getScope();
@@ -115,13 +111,11 @@ const createWithConfig = initialConfig => context => {
115
111
  }
116
112
  }
117
113
  parentNode.properties.forEach(findObjectStyles);
118
- }, {
119
- config
120
- }),
114
+ },
121
115
  // CSSTemplateLiteral and StyledTemplateLiteral
122
116
  // const cssTemplateLiteral = css`color: red; padding: 12px`;
123
117
  // const styledTemplateLiteral = styled.p`color: red; padding: 8px`;
124
- 'TaggedTemplateExpression[tag.name="css"],TaggedTemplateExpression[tag.object.name="styled"],TaggedTemplateExpression[tag.callee.name="styled"]': node => errorBoundary(() => {
118
+ 'TaggedTemplateExpression[tag.name="css"],TaggedTemplateExpression[tag.object.name="styled"],TaggedTemplateExpression[tag.callee.name="styled"]': node => {
125
119
  // To force the correct node type
126
120
  if (!isNodeOfType(node, 'TaggedTemplateExpression')) {
127
121
  return;
@@ -249,37 +243,29 @@ const createWithConfig = initialConfig => context => {
249
243
  }
250
244
  });
251
245
  }
252
- }, {
253
- config
254
- }),
246
+ },
255
247
  // For inline JSX styles - literals (e.g. <Test color="red"/>) - color only
256
- 'JSXAttribute > Literal': node => errorBoundary(() => {
248
+ 'JSXAttribute > Literal': node => {
257
249
  if (config.domains.includes('color')) {
258
250
  return lintJSXLiteralForColor(node, context, config);
259
251
  }
260
252
  return;
261
- }, {
262
- config
263
- }),
253
+ },
264
254
  // For inline JSX styles - members (e.g. <Test color={color.red}/>) - color only
265
- 'JSXExpressionContainer > MemberExpression': node => errorBoundary(() => {
255
+ 'JSXExpressionContainer > MemberExpression': node => {
266
256
  if (config.domains.includes('color')) {
267
257
  return lintJSXMemberForColor(node, context, config);
268
258
  }
269
259
  return;
270
- }, {
271
- config
272
- }),
260
+ },
273
261
  // For inline JSX styles - identifiers (e.g. <Test color={red}/>) - color only
274
- 'JSXExpressionContainer > Identifier': node => errorBoundary(() => {
262
+ 'JSXExpressionContainer > Identifier': node => {
275
263
  if (config.domains.includes('color')) {
276
264
  return lintJSXIdentifierForColor(node, context, config);
277
265
  }
278
266
  return;
279
- }, {
280
- config
281
- })
282
- };
267
+ }
268
+ }, config);
283
269
  };
284
270
  const rule = createLintRule({
285
271
  meta: ruleMeta,
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * THIS FILE WAS CREATED VIA CODEGEN DO NOT MODIFY {@see http://go/af-codegen}
3
- * @codegen <<SignedSource::b359d356c482db0087ddfce5bd105403>>
3
+ * @codegen <<SignedSource::402e6eb5433560b1032e5ed926bb5564>>
4
4
  * @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
5
5
  */
6
6
  import consistentCssPropUsage from './consistent-css-prop-usage';
@@ -9,6 +9,7 @@ import ensureDesignTokenUsagePreview from './ensure-design-token-usage-preview';
9
9
  import iconLabel from './icon-label';
10
10
  import noBannedImports from './no-banned-imports';
11
11
  import noCssTaggedTemplateExpression from './no-css-tagged-template-expression';
12
+ import noCustomIcons from './no-custom-icons';
12
13
  import noDeprecatedApis from './no-deprecated-apis';
13
14
  import noDeprecatedDesignTokenUsage from './no-deprecated-design-token-usage';
14
15
  import noDeprecatedImports from './no-deprecated-imports';
@@ -50,6 +51,7 @@ export default {
50
51
  'icon-label': iconLabel,
51
52
  'no-banned-imports': noBannedImports,
52
53
  'no-css-tagged-template-expression': noCssTaggedTemplateExpression,
54
+ 'no-custom-icons': noCustomIcons,
53
55
  'no-deprecated-apis': noDeprecatedApis,
54
56
  'no-deprecated-design-token-usage': noDeprecatedDesignTokenUsage,
55
57
  'no-deprecated-imports': noDeprecatedImports,
@@ -0,0 +1,4 @@
1
+ import { isNodeOfType } from 'eslint-codemod-utils';
2
+ export function hasProp(node, propName) {
3
+ return isNodeOfType(node.openingElement, 'JSXOpeningElement') && node.openingElement.attributes.some(a => a.type === 'JSXAttribute' && a.name.name === propName);
4
+ }
@@ -0,0 +1,23 @@
1
+ import { isImportedJSXElement } from './is-imported-jsx-element';
2
+ export function createIsFromImportSourceFor(...importSources) {
3
+ const literalImportSources = importSources.filter(s => typeof s === 'string');
4
+ const matchImportSources = importSources.filter(s => s instanceof RegExp);
5
+ const varImportSourceMap = new Map();
6
+ function isFromImportSource(node) {
7
+ return isImportedJSXElement(node) && varImportSourceMap.has(node.openingElement.name.name);
8
+ }
9
+ isFromImportSource.importDeclarationHook = node => {
10
+ const source = node.source.value;
11
+ if (typeof source !== 'string' || !(literalImportSources.includes(source) || matchImportSources.some(r => r.test(source)))) {
12
+ return;
13
+ }
14
+ node.specifiers.filter(spec => ['ImportSpecifier', 'ImportDefaultSpecifier'].includes(spec.type)).forEach(spec => varImportSourceMap.set(spec.local.name, source));
15
+ };
16
+ isFromImportSource.getImportSource = node => {
17
+ if (!isFromImportSource(node)) {
18
+ throw new Error('Node is not an imported JSX element');
19
+ }
20
+ return varImportSourceMap.get(node.openingElement.name.name);
21
+ };
22
+ return isFromImportSource;
23
+ }
@@ -0,0 +1,4 @@
1
+ import { isNodeOfType } from 'eslint-codemod-utils';
2
+ export function isImportedJSXElement(node) {
3
+ return isNodeOfType(node, 'JSXElement') && isNodeOfType(node.openingElement.name, 'JSXIdentifier');
4
+ }
@@ -0,0 +1,61 @@
1
+ // eslint-disable-next-line import/no-extraneous-dependencies
2
+
3
+ import { createLintRule } from '../utils/create-rule';
4
+ import { errorBoundary } from '../utils/error-boundary';
5
+ import { hasProp } from './checks/has-prop';
6
+ import { createIsFromImportSourceFor } from './checks/is-from-import-source';
7
+ const rule = createLintRule({
8
+ meta: {
9
+ name: 'no-custom-icons',
10
+ type: 'problem',
11
+ docs: {
12
+ description: 'Enforces custom glyph icons are used.',
13
+ recommended: false,
14
+ severity: 'warn'
15
+ },
16
+ schema: [{
17
+ type: 'object',
18
+ properties: {
19
+ centralLocation: {
20
+ type: 'string'
21
+ },
22
+ failSilently: {
23
+ type: 'boolean'
24
+ }
25
+ },
26
+ additionalProperties: false
27
+ }],
28
+ messages: {
29
+ noCustomIcons: `Custom icons from {{importSource}} are no longer supported. Migrate to an icon from '@atlaskit/(icon-labs|icon/core|icon/utility)'{{locationMessage}}.
30
+ [Migration guide](https://hello.atlassian.net/wiki/spaces/DST/pages/3748692796/New+ADS+iconography+-+Code+migration+guide).`
31
+ }
32
+ },
33
+ create(context) {
34
+ var _context$options$;
35
+ const isIconBase = createIsFromImportSourceFor('@atlaskit/icon', '@atlaskit/icon/base');
36
+ const {
37
+ centralLocation = '',
38
+ failSilently = false
39
+ } = (_context$options$ = context.options[0]) !== null && _context$options$ !== void 0 ? _context$options$ : {};
40
+ const locationMessage = centralLocation ? ` or move the icon to '${centralLocation}'` : '';
41
+ return errorBoundary({
42
+ JSXElement(node) {
43
+ var _isIconBase$getImport;
44
+ if (!isIconBase(node) || !hasProp(node, 'glyph')) {
45
+ return;
46
+ }
47
+ const importSource = (_isIconBase$getImport = isIconBase.getImportSource(node)) !== null && _isIconBase$getImport !== void 0 ? _isIconBase$getImport : '';
48
+ context.report({
49
+ node: node.openingElement,
50
+ messageId: 'noCustomIcons',
51
+ data: {
52
+ importSource,
53
+ locationMessage
54
+ }
55
+ });
56
+ },
57
+ ImportDeclaration: isIconBase.importDeclarationHook
58
+ }, failSilently);
59
+ }
60
+ });
61
+ export default rule;
@@ -1,5 +1,5 @@
1
1
  import { isNodeOfType } from 'eslint-codemod-utils';
2
- import { canAutoMigrateNewIconBasedOnSize, canMigrateColor, createAutoMigrationError, createCantFindSuitableReplacementError, createCantMigrateColorError, createCantMigrateFunctionUnknownError, createCantMigrateIdentifierError, createCantMigrateReExportError, createCantMigrateSizeUnknown, createCantMigrateSpreadPropsError, createCantMigrateUnsafeProp, createGuidance, createHelpers, findUNSAFEProp, getIconKey, getMigrationMapObject, locToString } from './helpers';
2
+ import { canAutoMigrateNewIconBasedOnSize, canMigrateColor, createAutoMigrationError, createCantFindSuitableReplacementError, createCantMigrateColorError, createCantMigrateFunctionUnknownError, createCantMigrateIdentifierError, createCantMigrateReExportError, createCantMigrateSizeUnknown, createCantMigrateSpreadPropsError, createCantMigrateUnsafeProp, createGuidance, createHelpers, findUNSAFEProp, getMigrationMapObject, locToString } from './helpers';
3
3
  import { isSize } from './migration-map-temp';
4
4
  export const createChecks = context => {
5
5
  //create global variables to be shared by the checks
@@ -100,15 +100,17 @@ export const createChecks = context => {
100
100
  if (node.source && isNodeOfType(node.source, 'Literal') && 'value' in node.source) {
101
101
  const moduleSource = node.source.value;
102
102
  if (typeof moduleSource === 'string' && ['@atlaskit/icon/glyph/', '@atlaskit/icon-object/glyph/'].find(val => moduleSource.startsWith(val)) && node.specifiers.length) {
103
- createCantMigrateReExportError(node, moduleSource, node.specifiers[0].exported.name, errorsManual);
104
- guidance[locToString(node)] = createGuidance(moduleSource);
103
+ for (const spec of node.specifiers) {
104
+ createCantMigrateReExportError(spec, moduleSource, spec.exported.name, errorsManual);
105
+ guidance[locToString(spec)] = createGuidance(moduleSource);
106
+ }
105
107
  }
106
108
  } else if (node.declaration && isNodeOfType(node.declaration, 'VariableDeclaration')) {
107
109
  // export const Icon = AddIcon;
108
110
  for (const decl of node.declaration.declarations) {
109
111
  if (isNodeOfType(decl, 'VariableDeclarator') && 'init' in decl && decl.init && isNodeOfType(decl.init, 'Identifier') && decl.init.name in legacyIconImports) {
110
112
  createCantMigrateReExportError(node, legacyIconImports[decl.init.name].packageName, decl.init.name, errorsManual);
111
- guidance[locToString(node)] = createGuidance(createGuidance(legacyIconImports[decl.init.name].packageName));
113
+ guidance[locToString(node)] = createGuidance(legacyIconImports[decl.init.name].packageName);
112
114
  }
113
115
  }
114
116
  } else if (!node.source && node.specifiers && node.specifiers.length > 0) {
@@ -174,18 +176,17 @@ export const createChecks = context => {
174
176
  UNSAFE_propName = result.UNSAFE_propName;
175
177
  }
176
178
  if (newIcon && isInNewButton && isNewIconMigratable && UNSAFE_size !== 'large' && UNSAFE_size !== 'xlarge') {
177
- const iconKey = getIconKey(legacyIconImports[node.name].packageName);
178
- createAutoMigrationError(node, legacyIconImports[node.name].packageName, node.name, newIcon, iconKey, errorsAuto);
179
- guidance[locToString(node)] = createGuidance(legacyIconImports[node.name].packageName, 'medium');
179
+ createAutoMigrationError(node, legacyIconImports[node.name].packageName, node.name, errorsAuto);
180
+ guidance[locToString(node)] = createGuidance(legacyIconImports[node.name].packageName, isInNewButton, 'medium');
180
181
  } else if ((!newIcon || !isNewIconMigratable) && !UNSAFE_size) {
181
182
  createCantFindSuitableReplacementError(node, legacyIconImports[node.name].packageName, node.name, errorsManual);
182
- guidance[locToString(node)] = createGuidance(legacyIconImports[node.name].packageName);
183
+ guidance[locToString(node)] = createGuidance(legacyIconImports[node.name].packageName, isInNewButton);
183
184
  } else if ((UNSAFE_size === 'large' || UNSAFE_size === 'xlarge') && UNSAFE_propName) {
184
185
  createCantMigrateUnsafeProp(node, UNSAFE_propName, UNSAFE_size, legacyIconImports[node.name].packageName, node.name, errorsManual);
185
- guidance[locToString(node)] = createGuidance(legacyIconImports[node.name].packageName, UNSAFE_size);
186
+ guidance[locToString(node)] = createGuidance(legacyIconImports[node.name].packageName, isInNewButton, UNSAFE_size);
186
187
  } else if (!isInNewButton) {
187
188
  createCantMigrateFunctionUnknownError(node, legacyIconImports[node.name].packageName, node.name, errorsManual);
188
- guidance[locToString(node)] = createGuidance(legacyIconImports[node.name].packageName);
189
+ guidance[locToString(node)] = createGuidance(legacyIconImports[node.name].packageName, isInNewButton);
189
190
  }
190
191
  }
191
192
  };
@@ -272,7 +273,6 @@ export const createChecks = context => {
272
273
  createCantMigrateSizeUnknown(node, errorsManual, legacyIconImports[name].packageName, name);
273
274
  hasManualMigration = true;
274
275
  }
275
- guidance[locToString(node)] = createGuidance(legacyIconImports[name].packageName, size ? size : undefined);
276
276
  // Check for unsafe size
277
277
  if ((UNSAFE_size === 'large' || UNSAFE_size === 'xlarge') && (UNSAFE_propName === 'UNSAFE_iconAfter_size' || UNSAFE_propName === 'UNSAFE_iconBefore_size' || UNSAFE_propName === 'UNSAFE_size')) {
278
278
  createCantMigrateUnsafeProp(node, UNSAFE_propName, UNSAFE_size, legacyIconImports[name].packageName, name, errorsManual);
@@ -290,14 +290,14 @@ export const createChecks = context => {
290
290
  hasManualMigration = true;
291
291
  }
292
292
  const migrationMapObject = getMigrationMapObject(legacyIconImports[name].packageName);
293
- const iconKey = getIconKey(legacyIconImports[name].packageName);
294
293
  const newIcon = migrationMapObject === null || migrationMapObject === void 0 ? void 0 : migrationMapObject.newIcon;
295
294
  const isNewIconMigratable = canAutoMigrateNewIconBasedOnSize(migrationMapObject === null || migrationMapObject === void 0 ? void 0 : migrationMapObject.sizeGuidance[(_size = size) !== null && _size !== void 0 ? _size : 'medium']);
296
295
  if (!hasManualMigration && newIcon && isNewIconMigratable) {
297
- createAutoMigrationError(node, legacyIconImports[name].packageName, name, newIcon, iconKey, errorsAuto);
296
+ createAutoMigrationError(node, legacyIconImports[name].packageName, name, errorsAuto);
298
297
  } else if ((!newIcon || !isNewIconMigratable) && size) {
299
- createCantFindSuitableReplacementError(node, legacyIconImports[name].packageName, name, errorsManual);
298
+ createCantFindSuitableReplacementError(node, legacyIconImports[name].packageName, name, errorsManual, migrationMapObject ? true : false);
300
299
  }
300
+ guidance[locToString(node)] = createGuidance(legacyIconImports[name].packageName, insideNewButton, size && isSize(size) ? size : undefined);
301
301
  }
302
302
  };
303
303
 
@@ -324,28 +324,20 @@ export const createChecks = context => {
324
324
  for (const [key, errorList] of Object.entries(errorsManual)) {
325
325
  const node = 'node' in errorList.errors[0] ? errorList.errors[0].node : null;
326
326
  if (node) {
327
+ const guidanceMessage = key in guidance ? guidance[key] : '';
327
328
  context.report({
328
329
  node,
329
330
  messageId: 'noLegacyIconsManualMigration',
330
331
  data: {
331
332
  iconName: errorList.iconName,
332
333
  importSource: errorList.importSource,
333
- quietModeGuidance: isQuietMode ? 'For more information see the below errors:' : ''
334
+ guidance: isQuietMode ? guidanceMessage : `${guidanceMessage}For more information see the below errors.\n`
334
335
  }
335
336
  });
336
337
  if (!isQuietMode) {
337
338
  for (const error of errorList.errors) {
338
339
  context.report(error);
339
340
  }
340
- if (key in guidance) {
341
- context.report({
342
- node,
343
- messageId: 'guidance',
344
- data: {
345
- guidance: guidance[key]
346
- }
347
- });
348
- }
349
341
  }
350
342
  }
351
343
  }
@@ -360,16 +352,11 @@ export const createChecks = context => {
360
352
  }
361
353
  const node = 'node' in error ? error.node : null;
362
354
  if (node) {
363
- context.report(error);
364
- if (key in guidance && !isQuietMode) {
365
- context.report({
366
- node,
367
- messageId: 'guidance',
368
- data: {
369
- guidance: guidance[key]
370
- }
371
- });
355
+ const guidanceMessage = key in guidance ? guidance[key] : '';
356
+ if ('data' in error && error.data) {
357
+ error.data.guidance = guidanceMessage;
372
358
  }
359
+ context.report(error);
373
360
  }
374
361
  }
375
362
  }
@@ -19,7 +19,7 @@ export const getMigrationMapObject = iconPackage => {
19
19
  * @param iconPackage The name of the legacy icon package
20
20
  * @returns The unique identifier for the icon (the part after "@atlaskit/icon/glyph")
21
21
  */
22
- export const getIconKey = iconPackage => {
22
+ const getIconKey = iconPackage => {
23
23
  const key = iconPackage.replace(/^@atlaskit\/icon\/glyph\//, '');
24
24
  return key;
25
25
  };
@@ -34,36 +34,43 @@ export const canAutoMigrateNewIconBasedOnSize = guidance => {
34
34
  /**
35
35
  * Creates the written guidance for migrating a legacy icon to a new icon
36
36
  */
37
- export const createGuidance = (iconPackage, size) => {
37
+ export const createGuidance = (iconPackage, insideNewButton = false, size) => {
38
38
  const migrationMapObject = getMigrationMapObject(iconPackage);
39
39
  if (migrationMapObject) {
40
40
  const newIcon = migrationMapObject.newIcon;
41
41
  if (!newIcon) {
42
42
  return 'No equivalent icon in new set. An option is to contribute a custom icon into icon-labs package instead.\n';
43
43
  }
44
+ const buttonGuidanceStr = "Please set 'spacing' property of the new icon to 'none', to ensure appropriate spacing inside `@atlaskit/button`.\n";
45
+ let guidance = '';
44
46
  if (size) {
45
- let guidance = `Fix: Use ${newIcon.name} from @atlaskit/${newIcon.library}/${newIcon.type}/${newIcon.name} instead.`;
46
- if (migrationMapObject.sizeGuidance[size] in outcomeDescriptionMap) {
47
- guidance += ` Please: ${outcomeDescriptionMap[migrationMapObject.sizeGuidance[size]]}\n`;
47
+ if (migrationMapObject.sizeGuidance[size] && canAutoMigrateNewIconBasedOnSize(migrationMapObject.sizeGuidance[size])) {
48
+ guidance += `Fix: Use ${newIcon.name} from @atlaskit/${newIcon.library}/${newIcon.type}/${newIcon.name} instead.`;
48
49
  } else {
49
- guidance += ' No migration advice given.\n';
50
+ guidance += `No equivalent icon for this size, ${size}, in new set.`;
50
51
  }
51
- return guidance;
52
+ guidance += `${migrationMapObject.sizeGuidance[size] in outcomeDescriptionMap ? ` Please: ${outcomeDescriptionMap[migrationMapObject.sizeGuidance[size]]}` : ' No migration size advice given.'}\n`;
52
53
  } else {
53
- let guidance = `Use ${newIcon.name} from @atlaskit/${newIcon.library}/${newIcon.type}/${newIcon.name} instead.\nMigration suggestions, depending on the legacy icon size:\n`;
54
+ guidance = `Use ${newIcon.name} from @atlaskit/${newIcon.library}/${newIcon.type}/${newIcon.name} instead.\nMigration suggestions, depending on the legacy icon size:\n`;
54
55
  for (const [size, value] of Object.entries(migrationMapObject.sizeGuidance)) {
55
56
  guidance += `\t- ${size}: `;
56
57
  if (!(value in outcomeDescriptionMap)) {
57
58
  guidance += 'No migration advice given.\n';
58
59
  } else {
59
- guidance += `${outcomeDescriptionMap[value]}\n`;
60
+ guidance += `${outcomeDescriptionMap[value]}.\n`;
60
61
  }
61
- guidance;
62
62
  }
63
- return guidance;
64
63
  }
64
+ if (insideNewButton) {
65
+ guidance += buttonGuidanceStr;
66
+ } else if (size && size === 'medium') {
67
+ guidance += "Setting the spacing property to 'spacious' will maintain the icon's box dimensions - but consider setting spacing='none' as it allows for easier control of spacing by parent elements.\n";
68
+ } else if (size) {
69
+ guidance += "In the new icon, please use spacing='none'.\n";
70
+ }
71
+ return guidance;
65
72
  } else {
66
- return 'Migration suggestions not found\n';
73
+ return `Migration suggestions not found for "${iconPackage}".\n`;
67
74
  }
68
75
  };
69
76
 
@@ -139,13 +146,14 @@ export const createCantMigrateUnsafeProp = (node, propName, value, packageName,
139
146
  };
140
147
  pushManualError(locToString(node), errors, myError, packageName, iconName);
141
148
  };
142
- export const createCantFindSuitableReplacementError = (node, importSource, iconName, errors) => {
149
+ export const createCantFindSuitableReplacementError = (node, importSource, iconName, errors, sizeIssue) => {
143
150
  const myError = {
144
151
  node,
145
152
  messageId: 'cantFindSuitableReplacement',
146
153
  data: {
147
154
  importSource,
148
- iconName
155
+ iconName,
156
+ sizeGuidance: sizeIssue ? ' at the current size' : ''
149
157
  }
150
158
  };
151
159
  pushManualError(locToString(node), errors, myError, importSource, iconName);
@@ -188,18 +196,13 @@ export const createCantMigrateSizeUnknown = (node, errors, importSource, iconNam
188
196
  };
189
197
  pushManualError(locToString(node), errors, myError, importSource, iconName);
190
198
  };
191
- export const createAutoMigrationError = (node, importSource, iconName, newIcon, oldIconName, errors) => {
192
- const migrationKey = newIcon.name === oldIconName ? newIcon.name : `${newIcon.name}--${oldIconName}`;
193
- const newMigrationIconPackage = `@atlaskit/${newIcon.library}/${newIcon.type}/migration/${migrationKey}`;
199
+ export const createAutoMigrationError = (node, importSource, iconName, errors) => {
194
200
  const myError = {
195
201
  node,
196
202
  messageId: 'noLegacyIconsAutoMigration',
197
203
  data: {
198
204
  importSource,
199
- iconName,
200
- newIcon: newIcon.name,
201
- //TODO: provide size guidance
202
- newPackage: newMigrationIconPackage
205
+ iconName
203
206
  }
204
207
  };
205
208
  errors[locToString(node)] = myError;