@atlaskit/eslint-plugin-design-system 10.8.2 → 10.10.0

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 (59) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/README.md +1 -0
  3. package/constellation/consistent-css-prop-usage/usage.mdx +9 -0
  4. package/constellation/index/usage.mdx +1 -0
  5. package/constellation/no-custom-icons/usage.mdx +36 -0
  6. package/dist/cjs/presets/all.codegen.js +2 -1
  7. package/dist/cjs/rules/consistent-css-prop-usage/index.js +19 -2
  8. package/dist/cjs/rules/ensure-design-token-usage/index.js +199 -227
  9. package/dist/cjs/rules/index.codegen.js +3 -1
  10. package/dist/cjs/rules/no-custom-icons/checks/has-prop.js +12 -0
  11. package/dist/cjs/rules/no-custom-icons/checks/is-from-import-source.js +42 -0
  12. package/dist/cjs/rules/no-custom-icons/checks/is-imported-jsx-element.js +10 -0
  13. package/dist/cjs/rules/no-custom-icons/index.js +67 -0
  14. package/dist/cjs/rules/no-legacy-icons/index.js +11 -83
  15. package/dist/cjs/rules/use-tokens-typography/index.js +4 -8
  16. package/dist/cjs/rules/utils/error-boundary.js +58 -11
  17. package/dist/es2019/presets/all.codegen.js +2 -1
  18. package/dist/es2019/rules/consistent-css-prop-usage/index.js +19 -2
  19. package/dist/es2019/rules/ensure-design-token-usage/index.js +16 -30
  20. package/dist/es2019/rules/index.codegen.js +3 -1
  21. package/dist/es2019/rules/no-custom-icons/checks/has-prop.js +4 -0
  22. package/dist/es2019/rules/no-custom-icons/checks/is-from-import-source.js +23 -0
  23. package/dist/es2019/rules/no-custom-icons/checks/is-imported-jsx-element.js +4 -0
  24. package/dist/es2019/rules/no-custom-icons/index.js +61 -0
  25. package/dist/es2019/rules/no-legacy-icons/index.js +11 -65
  26. package/dist/es2019/rules/use-tokens-typography/index.js +3 -7
  27. package/dist/es2019/rules/utils/error-boundary.js +55 -11
  28. package/dist/esm/presets/all.codegen.js +2 -1
  29. package/dist/esm/rules/consistent-css-prop-usage/index.js +19 -2
  30. package/dist/esm/rules/ensure-design-token-usage/index.js +199 -227
  31. package/dist/esm/rules/index.codegen.js +3 -1
  32. package/dist/esm/rules/no-custom-icons/checks/has-prop.js +6 -0
  33. package/dist/esm/rules/no-custom-icons/checks/is-from-import-source.js +36 -0
  34. package/dist/esm/rules/no-custom-icons/checks/is-imported-jsx-element.js +4 -0
  35. package/dist/esm/rules/no-custom-icons/index.js +61 -0
  36. package/dist/esm/rules/no-legacy-icons/index.js +11 -83
  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/consistent-css-prop-usage/types.d.ts +1 -0
  42. package/dist/types/rules/index.codegen.d.ts +1 -0
  43. package/dist/types/rules/no-custom-icons/checks/has-prop.d.ts +2 -0
  44. package/dist/types/rules/no-custom-icons/checks/is-from-import-source.d.ts +8 -0
  45. package/dist/types/rules/no-custom-icons/checks/is-imported-jsx-element.d.ts +8 -0
  46. package/dist/types/rules/no-custom-icons/index.d.ts +3 -0
  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/consistent-css-prop-usage/types.d.ts +1 -0
  52. package/dist/types-ts4.5/rules/index.codegen.d.ts +1 -0
  53. package/dist/types-ts4.5/rules/no-custom-icons/checks/has-prop.d.ts +2 -0
  54. package/dist/types-ts4.5/rules/no-custom-icons/checks/is-from-import-source.d.ts +8 -0
  55. package/dist/types-ts4.5/rules/no-custom-icons/checks/is-imported-jsx-element.d.ts +8 -0
  56. package/dist/types-ts4.5/rules/no-custom-icons/index.d.ts +3 -0
  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 +1 -1
@@ -0,0 +1,67 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = void 0;
7
+ var _createRule = require("../utils/create-rule");
8
+ var _errorBoundary = require("../utils/error-boundary");
9
+ var _hasProp = require("./checks/has-prop");
10
+ var _isFromImportSource = require("./checks/is-from-import-source");
11
+ // eslint-disable-next-line import/no-extraneous-dependencies
12
+
13
+ var rule = (0, _createRule.createLintRule)({
14
+ meta: {
15
+ name: 'no-custom-icons',
16
+ type: 'problem',
17
+ docs: {
18
+ description: 'Enforces custom glyph icons are used.',
19
+ recommended: false,
20
+ severity: 'warn'
21
+ },
22
+ schema: [{
23
+ type: 'object',
24
+ properties: {
25
+ centralLocation: {
26
+ type: 'string'
27
+ },
28
+ failSilently: {
29
+ type: 'boolean'
30
+ }
31
+ },
32
+ additionalProperties: false
33
+ }],
34
+ messages: {
35
+ noCustomIcons: "Custom icons from {{importSource}} are no longer supported. Migrate to an icon from '@atlaskit/(icon-labs|icon/core|icon/utility)'{{locationMessage}}.\n[Migration guide](https://hello.atlassian.net/wiki/spaces/DST/pages/3748692796/New+ADS+iconography+-+Code+migration+guide)."
36
+ }
37
+ },
38
+ create: function create(context) {
39
+ var _context$options$;
40
+ var isIconBase = (0, _isFromImportSource.createIsFromImportSourceFor)('@atlaskit/icon', '@atlaskit/icon/base');
41
+ var _ref = (_context$options$ = context.options[0]) !== null && _context$options$ !== void 0 ? _context$options$ : {},
42
+ _ref$centralLocation = _ref.centralLocation,
43
+ centralLocation = _ref$centralLocation === void 0 ? '' : _ref$centralLocation,
44
+ _ref$failSilently = _ref.failSilently,
45
+ failSilently = _ref$failSilently === void 0 ? false : _ref$failSilently;
46
+ var locationMessage = centralLocation ? " or move the icon to '".concat(centralLocation, "'") : '';
47
+ return (0, _errorBoundary.errorBoundary)({
48
+ JSXElement: function JSXElement(node) {
49
+ var _isIconBase$getImport;
50
+ if (!isIconBase(node) || !(0, _hasProp.hasProp)(node, 'glyph')) {
51
+ return;
52
+ }
53
+ var importSource = (_isIconBase$getImport = isIconBase.getImportSource(node)) !== null && _isIconBase$getImport !== void 0 ? _isIconBase$getImport : '';
54
+ context.report({
55
+ node: node.openingElement,
56
+ messageId: 'noCustomIcons',
57
+ data: {
58
+ importSource: importSource,
59
+ locationMessage: locationMessage
60
+ }
61
+ });
62
+ },
63
+ ImportDeclaration: isIconBase.importDeclarationHook
64
+ }, failSilently);
65
+ }
66
+ });
67
+ var _default = exports.default = rule;
@@ -60,95 +60,23 @@ var rule = (0, _createRule.createLintRule)({
60
60
  checkJSXElement = _createChecks.checkJSXElement,
61
61
  checkCallExpression = _createChecks.checkCallExpression,
62
62
  throwErrors = _createChecks.throwErrors;
63
- return {
63
+ return (0, _errorBoundary.errorBoundary)({
64
64
  // Track imports of relevant components
65
- ImportDeclaration: function ImportDeclaration(node) {
66
- (0, _errorBoundary.errorBoundary)(function () {
67
- return checkImportDeclarations(node);
68
- }, {
69
- config: {
70
- failSilently: failSilently
71
- }
72
- });
73
- },
65
+ ImportDeclaration: checkImportDeclarations,
74
66
  // Keep track of the relevant variable declarations and renames
75
- VariableDeclaration: function VariableDeclaration(node) {
76
- (0, _errorBoundary.errorBoundary)(function () {
77
- return checkVariableDeclarations(node);
78
- }, {
79
- config: {
80
- failSilently: failSilently
81
- }
82
- });
83
- },
67
+ VariableDeclaration: checkVariableDeclarations,
84
68
  // Case: default re-exports. Can't be auto-migrated
85
- ExportDefaultDeclaration: function ExportDefaultDeclaration(node) {
86
- (0, _errorBoundary.errorBoundary)(function () {
87
- return checkExportDefaultDeclaration(node);
88
- }, {
89
- config: {
90
- failSilently: failSilently
91
- }
92
- });
93
- },
94
- ExportNamedDeclaration: function ExportNamedDeclaration(node) {
95
- (0, _errorBoundary.errorBoundary)(function () {
96
- return checkExportNamedVariables(node);
97
- }, {
98
- config: {
99
- failSilently: failSilently
100
- }
101
- });
102
- },
69
+ ExportDefaultDeclaration: checkExportDefaultDeclaration,
70
+ ExportNamedDeclaration: checkExportNamedVariables,
103
71
  // Legacy icons found in arrays/objects
104
- 'ObjectExpression > Property > Identifier, ArrayExpression > Identifier ': function ObjectExpressionPropertyIdentifierArrayExpressionIdentifier(node) {
105
- (0, _errorBoundary.errorBoundary)(function () {
106
- return checkArrayOrMap(node);
107
- }, {
108
- config: {
109
- failSilently: failSilently
110
- }
111
- });
112
- },
72
+ 'ObjectExpression > Property > Identifier, ArrayExpression > Identifier ': checkArrayOrMap,
113
73
  // Legacy icons passed in via props, as JSX identifier (i.e. icon={AddIcon})
114
- 'JSXOpeningElement > JSXAttribute > JSXExpressionContainer > Identifier': function JSXOpeningElementJSXAttributeJSXExpressionContainerIdentifier(node) {
115
- (0, _errorBoundary.errorBoundary)(function () {
116
- return checkIconAsProp(node);
117
- }, {
118
- config: {
119
- failSilently: failSilently
120
- }
121
- });
122
- },
123
- JSXElement: function JSXElement(node) {
124
- (0, _errorBoundary.errorBoundary)(function () {
125
- return checkJSXElement(node);
126
- }, {
127
- config: {
128
- failSilently: failSilently
129
- }
130
- });
131
- },
74
+ 'JSXOpeningElement > JSXAttribute > JSXExpressionContainer > Identifier': checkIconAsProp,
75
+ JSXElement: checkJSXElement,
132
76
  // Icons called as an argument of a function (i.e. icon={DefaultIcon(AddIcon)})
133
- CallExpression: function CallExpression(node) {
134
- (0, _errorBoundary.errorBoundary)(function () {
135
- return checkCallExpression(node);
136
- }, {
137
- config: {
138
- failSilently: failSilently
139
- }
140
- });
141
- },
142
- 'Program:exit': function ProgramExit() {
143
- (0, _errorBoundary.errorBoundary)(function () {
144
- return throwErrors();
145
- }, {
146
- config: {
147
- failSilently: failSilently
148
- }
149
- });
150
- }
151
- };
77
+ CallExpression: checkCallExpression,
78
+ 'Program:exit': throwErrors
79
+ }, failSilently);
152
80
  }
153
81
  });
154
82
  var _default = exports.default = rule;
@@ -12,16 +12,12 @@ var create = function create(context) {
12
12
  var config = (0, _config.getConfig)(context.options[0]);
13
13
  return {
14
14
  // const styles = css({ fontSize: '14px, ... }), styled.div({ fontSize: 14, ... })
15
- ObjectExpression: function ObjectExpression(node) {
16
- return (0, _errorBoundary.errorBoundary)(function () {
17
- return _styleObject.StyleObject.lint(node, {
18
- context: context,
19
- config: config
20
- });
21
- }, {
15
+ ObjectExpression: (0, _errorBoundary.errorBoundary)(function (node) {
16
+ return _styleObject.StyleObject.lint(node, {
17
+ context: context,
22
18
  config: config
23
19
  });
24
- }
20
+ }, config)
25
21
  };
26
22
  };
27
23
  var rule = (0, _createRule.createLintRule)({
@@ -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',
@@ -449,7 +449,8 @@ const defaultConfig = {
449
449
  cssImportSource: CSS_IN_JS_IMPORTS.compiled,
450
450
  xcssImportSource: CSS_IN_JS_IMPORTS.atlaskitPrimitives,
451
451
  excludeReactComponents: false,
452
- autoFix: true
452
+ autoFix: true,
453
+ shouldAlwaysCheckXcss: false
453
454
  };
454
455
  const rule = createLintRule({
455
456
  meta: {
@@ -492,6 +493,9 @@ const rule = createLintRule({
492
493
  excludeReactComponents: {
493
494
  type: 'boolean'
494
495
  },
496
+ shouldAlwaysCheckXcss: {
497
+ type: 'boolean'
498
+ },
495
499
  autoFix: {
496
500
  type: 'boolean'
497
501
  }
@@ -508,7 +512,20 @@ const rule = createLintRule({
508
512
  name,
509
513
  value
510
514
  } = node;
511
- if (mergedConfig.excludeReactComponents && node.parent.type === 'JSXOpeningElement') {
515
+
516
+ /**
517
+ * We skip linting `xcss` attributes if:
518
+ *
519
+ * - excludeReactComponents === true
520
+ * - shouldAlwaysCheckXcss === false
521
+ *
522
+ * In the future we may want to remove `shouldAlwaysCheckXcss`
523
+ * and just always lint `xcss`, regardless of `excludeReactComponents`
524
+ */
525
+ if (mergedConfig.excludeReactComponents && name.name === 'xcss' && !mergedConfig.shouldAlwaysCheckXcss) {
526
+ return;
527
+ }
528
+ if (mergedConfig.excludeReactComponents && node.parent.type === 'JSXOpeningElement' && name.name === 'css') {
512
529
  // e.g. <item.before />
513
530
  if (node.parent.name.type === 'JSXMemberExpression') {
514
531
  return;
@@ -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;
@@ -56,77 +56,23 @@ const rule = createLintRule({
56
56
  checkCallExpression,
57
57
  throwErrors
58
58
  } = createChecks(context);
59
- return {
59
+ return errorBoundary({
60
60
  // Track imports of relevant components
61
- ImportDeclaration(node) {
62
- errorBoundary(() => checkImportDeclarations(node), {
63
- config: {
64
- failSilently
65
- }
66
- });
67
- },
61
+ ImportDeclaration: checkImportDeclarations,
68
62
  // Keep track of the relevant variable declarations and renames
69
- VariableDeclaration(node) {
70
- errorBoundary(() => checkVariableDeclarations(node), {
71
- config: {
72
- failSilently
73
- }
74
- });
75
- },
63
+ VariableDeclaration: checkVariableDeclarations,
76
64
  // Case: default re-exports. Can't be auto-migrated
77
- ExportDefaultDeclaration(node) {
78
- errorBoundary(() => checkExportDefaultDeclaration(node), {
79
- config: {
80
- failSilently
81
- }
82
- });
83
- },
84
- ExportNamedDeclaration(node) {
85
- errorBoundary(() => checkExportNamedVariables(node), {
86
- config: {
87
- failSilently
88
- }
89
- });
90
- },
65
+ ExportDefaultDeclaration: checkExportDefaultDeclaration,
66
+ ExportNamedDeclaration: checkExportNamedVariables,
91
67
  // Legacy icons found in arrays/objects
92
- 'ObjectExpression > Property > Identifier, ArrayExpression > Identifier ': node => {
93
- errorBoundary(() => checkArrayOrMap(node), {
94
- config: {
95
- failSilently
96
- }
97
- });
98
- },
68
+ 'ObjectExpression > Property > Identifier, ArrayExpression > Identifier ': checkArrayOrMap,
99
69
  // Legacy icons passed in via props, as JSX identifier (i.e. icon={AddIcon})
100
- 'JSXOpeningElement > JSXAttribute > JSXExpressionContainer > Identifier': node => {
101
- errorBoundary(() => checkIconAsProp(node), {
102
- config: {
103
- failSilently
104
- }
105
- });
106
- },
107
- JSXElement(node) {
108
- errorBoundary(() => checkJSXElement(node), {
109
- config: {
110
- failSilently
111
- }
112
- });
113
- },
70
+ 'JSXOpeningElement > JSXAttribute > JSXExpressionContainer > Identifier': checkIconAsProp,
71
+ JSXElement: checkJSXElement,
114
72
  // Icons called as an argument of a function (i.e. icon={DefaultIcon(AddIcon)})
115
- CallExpression: node => {
116
- errorBoundary(() => checkCallExpression(node), {
117
- config: {
118
- failSilently
119
- }
120
- });
121
- },
122
- 'Program:exit': () => {
123
- errorBoundary(() => throwErrors(), {
124
- config: {
125
- failSilently
126
- }
127
- });
128
- }
129
- };
73
+ CallExpression: checkCallExpression,
74
+ 'Program:exit': throwErrors
75
+ }, failSilently);
130
76
  }
131
77
  });
132
78
  export default rule;
@@ -6,14 +6,10 @@ const create = context => {
6
6
  const config = getConfig(context.options[0]);
7
7
  return {
8
8
  // const styles = css({ fontSize: '14px, ... }), styled.div({ fontSize: 14, ... })
9
- ObjectExpression: node => errorBoundary(() => {
10
- return StyleObject.lint(node, {
11
- context,
12
- config
13
- });
14
- }, {
9
+ ObjectExpression: errorBoundary(node => StyleObject.lint(node, {
10
+ context,
15
11
  config
16
- })
12
+ }), config)
17
13
  };
18
14
  };
19
15
  const rule = createLintRule({