@atlaskit/eslint-plugin-design-system 10.4.4 → 10.5.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 (28) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/README.md +1 -0
  3. package/constellation/index/usage.mdx +1 -0
  4. package/constellation/no-legacy-icons/usage.mdx +42 -0
  5. package/dist/cjs/presets/all.codegen.js +2 -1
  6. package/dist/cjs/rules/index.codegen.js +3 -1
  7. package/dist/cjs/rules/no-html-anchor/node-types/styled-component/index.js +4 -0
  8. package/dist/cjs/rules/no-html-button/node-types/styled-component/index.js +4 -0
  9. package/dist/cjs/rules/no-legacy-icons/index.js +174 -0
  10. package/dist/es2019/presets/all.codegen.js +2 -1
  11. package/dist/es2019/rules/index.codegen.js +3 -1
  12. package/dist/es2019/rules/no-html-anchor/node-types/styled-component/index.js +4 -0
  13. package/dist/es2019/rules/no-html-button/node-types/styled-component/index.js +4 -0
  14. package/dist/es2019/rules/no-legacy-icons/index.js +133 -0
  15. package/dist/esm/presets/all.codegen.js +2 -1
  16. package/dist/esm/rules/index.codegen.js +3 -1
  17. package/dist/esm/rules/no-html-anchor/node-types/styled-component/index.js +4 -0
  18. package/dist/esm/rules/no-html-button/node-types/styled-component/index.js +4 -0
  19. package/dist/esm/rules/no-legacy-icons/index.js +168 -0
  20. package/dist/types/index.codegen.d.ts +1 -0
  21. package/dist/types/presets/all.codegen.d.ts +2 -1
  22. package/dist/types/rules/index.codegen.d.ts +1 -0
  23. package/dist/types/rules/no-legacy-icons/index.d.ts +2 -0
  24. package/dist/types-ts4.5/index.codegen.d.ts +1 -0
  25. package/dist/types-ts4.5/presets/all.codegen.d.ts +2 -1
  26. package/dist/types-ts4.5/rules/index.codegen.d.ts +1 -0
  27. package/dist/types-ts4.5/rules/no-legacy-icons/index.d.ts +2 -0
  28. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,22 @@
1
1
  # @atlaskit/eslint-plugin-design-system
2
2
 
3
+ ## 10.5.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#105106](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/pull-requests/105106)
8
+ [`c1ef7e00be9d`](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/commits/c1ef7e00be9d) -
9
+ Introduces new ESLint rule to identify and discourage use of legacy icons.
10
+
11
+ ## 10.4.5
12
+
13
+ ### Patch Changes
14
+
15
+ - [#107585](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/pull-requests/107585)
16
+ [`c7428760443b`](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/commits/c7428760443b) -
17
+ Fixes bugs with `no-html-button` and `no-html-anchor` rules that was falsely reporting for styled
18
+ components not used in the same file they are defined in.
19
+
3
20
  ## 10.4.4
4
21
 
5
22
  ### Patch Changes
package/README.md CHANGED
@@ -66,6 +66,7 @@ module.exports = {
66
66
  | <a href="./src/rules/no-html-button/README.md">no-html-button</a> | Discourage direct usage of HTML button elements in favor of Atlassian Design System button components. | Yes | | |
67
67
  | <a href="./src/rules/no-invalid-css-map/README.md">no-invalid-css-map</a> | Checks the validity of a CSS map created through cssMap. This is intended to be used alongside TypeScript's type-checking. | Yes | | |
68
68
  | <a href="./src/rules/no-keyframes-tagged-template-expression/README.md">no-keyframes-tagged-template-expression</a> | Disallows any `keyframe` tagged template expressions that originate from Emotion, Styled Components or Compiled | | Yes | |
69
+ | <a href="./src/rules/no-legacy-icons/README.md">no-legacy-icons</a> | Enforces no legacy icons are used. | | | |
69
70
  | <a href="./src/rules/no-margin/README.md">no-margin</a> | Disallow using the margin CSS property. | | | |
70
71
  | <a href="./src/rules/no-nested-styles/README.md">no-nested-styles</a> | Disallows use of nested styles in `css` functions. | Yes | | |
71
72
  | <a href="./src/rules/no-physical-properties/README.md">no-physical-properties</a> | Disallow physical properties and values in `css` function calls. | | Yes | |
@@ -29,6 +29,7 @@ This plugin contains rules that should be used when working with the
29
29
  | <a href="/components/eslint-plugin-design-system/no-html-button/usage">no-html-button</a> | Discourage direct usage of HTML button elements in favor of Atlassian Design System button components. | Yes | | |
30
30
  | <a href="/components/eslint-plugin-design-system/no-invalid-css-map/usage">no-invalid-css-map</a> | Checks the validity of a CSS map created through cssMap. This is intended to be used alongside TypeScript's type-checking. | Yes | | |
31
31
  | <a href="/components/eslint-plugin-design-system/no-keyframes-tagged-template-expression/usage">no-keyframes-tagged-template-expression</a> | Disallows any `keyframe` tagged template expressions that originate from Emotion, Styled Components or Compiled | | Yes | |
32
+ | <a href="/components/eslint-plugin-design-system/no-legacy-icons/usage">no-legacy-icons</a> | Enforces no legacy icons are used. | | | |
32
33
  | <a href="/components/eslint-plugin-design-system/no-margin/usage">no-margin</a> | Disallow using the margin CSS property. | | | |
33
34
  | <a href="/components/eslint-plugin-design-system/no-nested-styles/usage">no-nested-styles</a> | Disallows use of nested styles in `css` functions. | Yes | | |
34
35
  | <a href="/components/eslint-plugin-design-system/no-physical-properties/usage">no-physical-properties</a> | Disallow physical properties and values in `css` function calls. | | Yes | |
@@ -0,0 +1,42 @@
1
+ # no-legacy-icons
2
+
3
+ Icons are being updated with new designs, a simplified set of available icons and sizes, as well as
4
+ more consistent padding and spacing.
5
+
6
+ The new icon components allows your components to align with the new visual language - either by
7
+ default, or when enabled via a feature flag.
8
+
9
+ ## Examples
10
+
11
+ This rule identifies usages of legacy icons from `@atlaskit/icon/glyph` and `@atlaskit/icon-object`,
12
+ that aren't yet migrated to the new icon API. Legacy icons are only permitted when passed into a new
13
+ "core" or "utility" icon from `@atlaskit/icon` or `@atlassian/icon-lab`, via the
14
+ `LEGACY_fallbackIcon` prop.
15
+
16
+ ### Incorrect
17
+
18
+ ```js
19
+ import ActivityIcon from '@atlaskit/icon/glyph/activity'
20
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ legacy icon import
21
+
22
+ import { IconButton } from '@atlaskit/button/new'
23
+
24
+ <ActivityIcon label="Activity">
25
+ ^^^^^^^^^^^^^^^^^^^^^^^ legacy icon
26
+
27
+ <IconButton icon={ActivityIcon} label="Activity"/>
28
+ ^^^^^^^^^^^^^ legacy icon
29
+ ```
30
+
31
+ ### Correct
32
+
33
+ ```js
34
+ import AddIcon from '@atlaskit/icon/core/add';
35
+ import { IconButton } from '@atlaskit/button/new';
36
+
37
+ <AddIcon label="" />;
38
+ <IconButton
39
+ icon={(iconProps) => <AddIcon LEGACY_fallbackIcon={AddIconLegacy} {...iconProps} />}
40
+ label="Add to Cart"
41
+ />;
42
+ ```
@@ -6,7 +6,7 @@ Object.defineProperty(exports, "__esModule", {
6
6
  exports.default = void 0;
7
7
  /**
8
8
  * THIS FILE WAS CREATED VIA CODEGEN DO NOT MODIFY {@see http://go/af-codegen}
9
- * @codegen <<SignedSource::ed09a41b825250124299d53a17b16b74>>
9
+ * @codegen <<SignedSource::45da5525f92249d3a5b5049a95fc2fc5>>
10
10
  * @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
11
11
  */
12
12
  var _default = exports.default = {
@@ -29,6 +29,7 @@ var _default = exports.default = {
29
29
  '@atlaskit/design-system/no-html-button': 'warn',
30
30
  '@atlaskit/design-system/no-invalid-css-map': 'error',
31
31
  '@atlaskit/design-system/no-keyframes-tagged-template-expression': 'error',
32
+ '@atlaskit/design-system/no-legacy-icons': 'warn',
32
33
  '@atlaskit/design-system/no-margin': 'warn',
33
34
  '@atlaskit/design-system/no-nested-styles': 'error',
34
35
  '@atlaskit/design-system/no-physical-properties': 'error',
@@ -22,6 +22,7 @@ var _noHtmlAnchor = _interopRequireDefault(require("./no-html-anchor"));
22
22
  var _noHtmlButton = _interopRequireDefault(require("./no-html-button"));
23
23
  var _noInvalidCssMap = _interopRequireDefault(require("./no-invalid-css-map"));
24
24
  var _noKeyframesTaggedTemplateExpression = _interopRequireDefault(require("./no-keyframes-tagged-template-expression"));
25
+ var _noLegacyIcons = _interopRequireDefault(require("./no-legacy-icons"));
25
26
  var _noMargin = _interopRequireDefault(require("./no-margin"));
26
27
  var _noNestedStyles = _interopRequireDefault(require("./no-nested-styles"));
27
28
  var _noPhysicalProperties = _interopRequireDefault(require("./no-physical-properties"));
@@ -44,7 +45,7 @@ var _useTokensTypography = _interopRequireDefault(require("./use-tokens-typograp
44
45
  var _useVisuallyHidden = _interopRequireDefault(require("./use-visually-hidden"));
45
46
  /**
46
47
  * THIS FILE WAS CREATED VIA CODEGEN DO NOT MODIFY {@see http://go/af-codegen}
47
- * @codegen <<SignedSource::920113eedc2218488bff48b528364867>>
48
+ * @codegen <<SignedSource::37cb0f5dfe6f5b8391203fdb1cf7cabe>>
48
49
  * @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
49
50
  */
50
51
  var _default = exports.default = {
@@ -65,6 +66,7 @@ var _default = exports.default = {
65
66
  'no-html-button': _noHtmlButton.default,
66
67
  'no-invalid-css-map': _noInvalidCssMap.default,
67
68
  'no-keyframes-tagged-template-expression': _noKeyframesTaggedTemplateExpression.default,
69
+ 'no-legacy-icons': _noLegacyIcons.default,
68
70
  'no-margin': _noMargin.default,
69
71
  'no-nested-styles': _noNestedStyles.default,
70
72
  'no-physical-properties': _noPhysicalProperties.default,
@@ -23,6 +23,10 @@ var StyledComponent = exports.StyledComponent = {
23
23
  return;
24
24
  }
25
25
  var jsxElement = (_getJsxElementByName = (0, _getJsxElementByName2.getJsxElementByName)(styles.id.name, context.getScope())) === null || _getJsxElementByName === void 0 ? void 0 : _getJsxElementByName.parent;
26
+ if (!jsxElement) {
27
+ // If there's no JSX element, we can't determine if it's being used as an anchor or not
28
+ return;
29
+ }
26
30
  if (jsxElement && !(0, _supported.isSupportedForLint)(jsxElement, elementName)) {
27
31
  return;
28
32
  }
@@ -23,6 +23,10 @@ var StyledComponent = exports.StyledComponent = {
23
23
  return;
24
24
  }
25
25
  var jsxElement = (_getJsxElementByName = (0, _getJsxElementByName2.getJsxElementByName)(styles.id.name, context.getScope())) === null || _getJsxElementByName === void 0 ? void 0 : _getJsxElementByName.parent;
26
+ if (!jsxElement) {
27
+ // If there's no JSX element, we can't determine if it's being used as a button or not
28
+ return;
29
+ }
26
30
  if (jsxElement && !(0, _supported.isSupportedForLint)(jsxElement, elementName)) {
27
31
  return;
28
32
  }
@@ -0,0 +1,174 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = void 0;
7
+ var _eslintCodemodUtils = require("eslint-codemod-utils");
8
+ var _createRule = require("../utils/create-rule");
9
+ function _createForOfIteratorHelper(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it.return != null) it.return(); } finally { if (didErr) throw err; } } }; }
10
+ function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
11
+ function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; }
12
+ var rule = (0, _createRule.createLintRule)({
13
+ meta: {
14
+ name: 'no-legacy-icons',
15
+ type: 'problem',
16
+ docs: {
17
+ description: 'Enforces no legacy icons are used.',
18
+ recommended: false,
19
+ severity: 'warn'
20
+ },
21
+ messages: {
22
+ noLegacyIcons: "Legacy icon '{{iconName}}', is being rendered from import '{{importSource}}'. Migrate to an icon from '@atlaskit/icon/(core|utility)', or '@atlaskit/icon-labs/(core|utility)'.\nLearn more in our [code migration guide](https://hello.atlassian.net/wiki/spaces/DST/pages/3748692796/New+ADS+iconography+-+Code+migration+guide)."
23
+ }
24
+ },
25
+ create: function create(context) {
26
+ var legacyIconImports = {};
27
+ return {
28
+ ImportDeclaration: function ImportDeclaration(node) {
29
+ var moduleSource = node.source.value;
30
+ if (typeof moduleSource === 'string' && ['@atlaskit/icon/glyph/', '@atlaskit/icon-object/glyph/'].find(function (val) {
31
+ return moduleSource.startsWith(val);
32
+ }) && node.specifiers.length) {
33
+ var defaultImport = node.specifiers.find(function (spec) {
34
+ return spec.type === 'ImportDefaultSpecifier';
35
+ });
36
+ if (!defaultImport) {
37
+ return;
38
+ }
39
+ var defaultImportName = defaultImport.local.name;
40
+ legacyIconImports[defaultImportName] = moduleSource;
41
+ }
42
+ },
43
+ VariableDeclaration: function VariableDeclaration(node) {
44
+ if ((0, _eslintCodemodUtils.isNodeOfType)(node, 'VariableDeclaration')) {
45
+ var _iterator = _createForOfIteratorHelper(node.declarations),
46
+ _step;
47
+ try {
48
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
49
+ var decl = _step.value;
50
+ if ((0, _eslintCodemodUtils.isNodeOfType)(decl, 'VariableDeclarator') && 'init' in decl && 'id' in decl && decl.init && decl.id && 'name' in decl.id && decl.id.name && (0, _eslintCodemodUtils.isNodeOfType)(decl.init, 'Identifier') && decl.init.name in legacyIconImports) {
51
+ legacyIconImports[decl.id.name] = legacyIconImports[decl.init.name];
52
+ }
53
+ }
54
+ } catch (err) {
55
+ _iterator.e(err);
56
+ } finally {
57
+ _iterator.f();
58
+ }
59
+ }
60
+ },
61
+ ExportDefaultDeclaration: function ExportDefaultDeclaration(node) {
62
+ if ('declaration' in node && node.declaration && (0, _eslintCodemodUtils.isNodeOfType)(node.declaration, 'Identifier') && node.declaration.name in legacyIconImports) {
63
+ context.report({
64
+ node: node,
65
+ messageId: 'noLegacyIcons',
66
+ data: {
67
+ importSource: legacyIconImports[node.declaration.name],
68
+ iconName: node.declaration.name
69
+ }
70
+ });
71
+ }
72
+ },
73
+ ExportNamedDeclaration: function ExportNamedDeclaration(node) {
74
+ if ('source' in node && node.source && (0, _eslintCodemodUtils.isNodeOfType)(node.source, 'Literal') && 'value' in node.source) {
75
+ var moduleSource = node.source.value;
76
+ if (typeof moduleSource === 'string' && ['@atlaskit/icon/glyph/', '@atlaskit/icon-object/glyph/'].find(function (val) {
77
+ return moduleSource.startsWith(val);
78
+ }) && node.specifiers.length) {
79
+ context.report({
80
+ node: node,
81
+ messageId: 'noLegacyIcons',
82
+ data: {
83
+ importSource: moduleSource,
84
+ iconName: node.specifiers[0].exported.name
85
+ }
86
+ });
87
+ }
88
+ } else if ('declaration' in node && node.declaration && (0, _eslintCodemodUtils.isNodeOfType)(node.declaration, 'VariableDeclaration')) {
89
+ var _iterator2 = _createForOfIteratorHelper(node.declaration.declarations),
90
+ _step2;
91
+ try {
92
+ for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
93
+ var decl = _step2.value;
94
+ if ((0, _eslintCodemodUtils.isNodeOfType)(decl, 'VariableDeclarator') && 'init' in decl && decl.init && (0, _eslintCodemodUtils.isNodeOfType)(decl.init, 'Identifier') && decl.init.name in legacyIconImports) {
95
+ context.report({
96
+ node: node,
97
+ messageId: 'noLegacyIcons',
98
+ data: {
99
+ importSource: legacyIconImports[decl.init.name],
100
+ iconName: decl.init.name
101
+ }
102
+ });
103
+ }
104
+ }
105
+ } catch (err) {
106
+ _iterator2.e(err);
107
+ } finally {
108
+ _iterator2.f();
109
+ }
110
+ }
111
+ },
112
+ JSXAttribute: function JSXAttribute(node) {
113
+ if (!(0, _eslintCodemodUtils.isNodeOfType)(node.value, 'JSXExpressionContainer')) {
114
+ return;
115
+ }
116
+ if ((0, _eslintCodemodUtils.isNodeOfType)(node.value.expression, 'Identifier') && node.value.expression.name in legacyIconImports && (0, _eslintCodemodUtils.isNodeOfType)(node.name, 'JSXIdentifier') && !node.name.name.startsWith('LEGACY_')) {
117
+ context.report({
118
+ node: node,
119
+ messageId: 'noLegacyIcons',
120
+ data: {
121
+ importSource: legacyIconImports[node.value.expression.name],
122
+ iconName: node.value.expression.name
123
+ }
124
+ });
125
+ }
126
+ },
127
+ JSXElement: function JSXElement(node) {
128
+ if (!(0, _eslintCodemodUtils.isNodeOfType)(node, 'JSXElement')) {
129
+ return;
130
+ }
131
+ if (!(0, _eslintCodemodUtils.isNodeOfType)(node.openingElement.name, 'JSXIdentifier')) {
132
+ return;
133
+ }
134
+ var name = node.openingElement.name.name;
135
+ if (name in legacyIconImports) {
136
+ context.report({
137
+ node: node,
138
+ messageId: 'noLegacyIcons',
139
+ data: {
140
+ importSource: legacyIconImports[name],
141
+ iconName: name
142
+ }
143
+ });
144
+ }
145
+ },
146
+ CallExpression: function CallExpression(node) {
147
+ if ('arguments' in node && node.arguments.length) {
148
+ var _iterator3 = _createForOfIteratorHelper(node.arguments),
149
+ _step3;
150
+ try {
151
+ for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
152
+ var arg = _step3.value;
153
+ if ((0, _eslintCodemodUtils.isNodeOfType)(arg, 'Identifier') && arg.name in legacyIconImports) {
154
+ context.report({
155
+ node: arg,
156
+ messageId: 'noLegacyIcons',
157
+ data: {
158
+ importSource: legacyIconImports[arg.name],
159
+ iconName: arg.name
160
+ }
161
+ });
162
+ }
163
+ }
164
+ } catch (err) {
165
+ _iterator3.e(err);
166
+ } finally {
167
+ _iterator3.f();
168
+ }
169
+ }
170
+ }
171
+ };
172
+ }
173
+ });
174
+ var _default = exports.default = rule;
@@ -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::ed09a41b825250124299d53a17b16b74>>
3
+ * @codegen <<SignedSource::45da5525f92249d3a5b5049a95fc2fc5>>
4
4
  * @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
5
5
  */
6
6
  export default {
@@ -23,6 +23,7 @@ export default {
23
23
  '@atlaskit/design-system/no-html-button': 'warn',
24
24
  '@atlaskit/design-system/no-invalid-css-map': 'error',
25
25
  '@atlaskit/design-system/no-keyframes-tagged-template-expression': 'error',
26
+ '@atlaskit/design-system/no-legacy-icons': 'warn',
26
27
  '@atlaskit/design-system/no-margin': 'warn',
27
28
  '@atlaskit/design-system/no-nested-styles': 'error',
28
29
  '@atlaskit/design-system/no-physical-properties': 'error',
@@ -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::920113eedc2218488bff48b528364867>>
3
+ * @codegen <<SignedSource::37cb0f5dfe6f5b8391203fdb1cf7cabe>>
4
4
  * @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
5
5
  */
6
6
  import consistentCssPropUsage from './consistent-css-prop-usage';
@@ -20,6 +20,7 @@ import noHtmlAnchor from './no-html-anchor';
20
20
  import noHtmlButton from './no-html-button';
21
21
  import noInvalidCssMap from './no-invalid-css-map';
22
22
  import noKeyframesTaggedTemplateExpression from './no-keyframes-tagged-template-expression';
23
+ import noLegacyIcons from './no-legacy-icons';
23
24
  import noMargin from './no-margin';
24
25
  import noNestedStyles from './no-nested-styles';
25
26
  import noPhysicalProperties from './no-physical-properties';
@@ -58,6 +59,7 @@ export default {
58
59
  'no-html-button': noHtmlButton,
59
60
  'no-invalid-css-map': noInvalidCssMap,
60
61
  'no-keyframes-tagged-template-expression': noKeyframesTaggedTemplateExpression,
62
+ 'no-legacy-icons': noLegacyIcons,
61
63
  'no-margin': noMargin,
62
64
  'no-nested-styles': noNestedStyles,
63
65
  'no-physical-properties': noPhysicalProperties,
@@ -18,6 +18,10 @@ export const StyledComponent = {
18
18
  return;
19
19
  }
20
20
  const jsxElement = (_getJsxElementByName = getJsxElementByName(styles.id.name, context.getScope())) === null || _getJsxElementByName === void 0 ? void 0 : _getJsxElementByName.parent;
21
+ if (!jsxElement) {
22
+ // If there's no JSX element, we can't determine if it's being used as an anchor or not
23
+ return;
24
+ }
21
25
  if (jsxElement && !isSupportedForLint(jsxElement, elementName)) {
22
26
  return;
23
27
  }
@@ -18,6 +18,10 @@ export const StyledComponent = {
18
18
  return;
19
19
  }
20
20
  const jsxElement = (_getJsxElementByName = getJsxElementByName(styles.id.name, context.getScope())) === null || _getJsxElementByName === void 0 ? void 0 : _getJsxElementByName.parent;
21
+ if (!jsxElement) {
22
+ // If there's no JSX element, we can't determine if it's being used as a button or not
23
+ return;
24
+ }
21
25
  if (jsxElement && !isSupportedForLint(jsxElement, elementName)) {
22
26
  return;
23
27
  }
@@ -0,0 +1,133 @@
1
+ import { isNodeOfType } from 'eslint-codemod-utils';
2
+ import { createLintRule } from '../utils/create-rule';
3
+ const rule = createLintRule({
4
+ meta: {
5
+ name: 'no-legacy-icons',
6
+ type: 'problem',
7
+ docs: {
8
+ description: 'Enforces no legacy icons are used.',
9
+ recommended: false,
10
+ severity: 'warn'
11
+ },
12
+ messages: {
13
+ noLegacyIcons: `Legacy icon '{{iconName}}', is being rendered from import '{{importSource}}'. Migrate to an icon from '@atlaskit/icon/(core|utility)', or '@atlaskit/icon-labs/(core|utility)'.
14
+ Learn more in our [code migration guide](https://hello.atlassian.net/wiki/spaces/DST/pages/3748692796/New+ADS+iconography+-+Code+migration+guide).`
15
+ }
16
+ },
17
+ create(context) {
18
+ const legacyIconImports = {};
19
+ return {
20
+ ImportDeclaration(node) {
21
+ const moduleSource = node.source.value;
22
+ if (typeof moduleSource === 'string' && ['@atlaskit/icon/glyph/', '@atlaskit/icon-object/glyph/'].find(val => moduleSource.startsWith(val)) && node.specifiers.length) {
23
+ const defaultImport = node.specifiers.find(spec => spec.type === 'ImportDefaultSpecifier');
24
+ if (!defaultImport) {
25
+ return;
26
+ }
27
+ const defaultImportName = defaultImport.local.name;
28
+ legacyIconImports[defaultImportName] = moduleSource;
29
+ }
30
+ },
31
+ VariableDeclaration(node) {
32
+ if (isNodeOfType(node, 'VariableDeclaration')) {
33
+ for (const decl of node.declarations) {
34
+ if (isNodeOfType(decl, 'VariableDeclarator') && 'init' in decl && 'id' in decl && decl.init && decl.id && 'name' in decl.id && decl.id.name && isNodeOfType(decl.init, 'Identifier') && decl.init.name in legacyIconImports) {
35
+ legacyIconImports[decl.id.name] = legacyIconImports[decl.init.name];
36
+ }
37
+ }
38
+ }
39
+ },
40
+ ExportDefaultDeclaration(node) {
41
+ if ('declaration' in node && node.declaration && isNodeOfType(node.declaration, 'Identifier') && node.declaration.name in legacyIconImports) {
42
+ context.report({
43
+ node,
44
+ messageId: 'noLegacyIcons',
45
+ data: {
46
+ importSource: legacyIconImports[node.declaration.name],
47
+ iconName: node.declaration.name
48
+ }
49
+ });
50
+ }
51
+ },
52
+ ExportNamedDeclaration(node) {
53
+ if ('source' in node && node.source && isNodeOfType(node.source, 'Literal') && 'value' in node.source) {
54
+ const moduleSource = node.source.value;
55
+ if (typeof moduleSource === 'string' && ['@atlaskit/icon/glyph/', '@atlaskit/icon-object/glyph/'].find(val => moduleSource.startsWith(val)) && node.specifiers.length) {
56
+ context.report({
57
+ node,
58
+ messageId: 'noLegacyIcons',
59
+ data: {
60
+ importSource: moduleSource,
61
+ iconName: node.specifiers[0].exported.name
62
+ }
63
+ });
64
+ }
65
+ } else if ('declaration' in node && node.declaration && isNodeOfType(node.declaration, 'VariableDeclaration')) {
66
+ for (const decl of node.declaration.declarations) {
67
+ if (isNodeOfType(decl, 'VariableDeclarator') && 'init' in decl && decl.init && isNodeOfType(decl.init, 'Identifier') && decl.init.name in legacyIconImports) {
68
+ context.report({
69
+ node,
70
+ messageId: 'noLegacyIcons',
71
+ data: {
72
+ importSource: legacyIconImports[decl.init.name],
73
+ iconName: decl.init.name
74
+ }
75
+ });
76
+ }
77
+ }
78
+ }
79
+ },
80
+ JSXAttribute(node) {
81
+ if (!isNodeOfType(node.value, 'JSXExpressionContainer')) {
82
+ return;
83
+ }
84
+ if (isNodeOfType(node.value.expression, 'Identifier') && node.value.expression.name in legacyIconImports && isNodeOfType(node.name, 'JSXIdentifier') && !node.name.name.startsWith('LEGACY_')) {
85
+ context.report({
86
+ node,
87
+ messageId: 'noLegacyIcons',
88
+ data: {
89
+ importSource: legacyIconImports[node.value.expression.name],
90
+ iconName: node.value.expression.name
91
+ }
92
+ });
93
+ }
94
+ },
95
+ JSXElement(node) {
96
+ if (!isNodeOfType(node, 'JSXElement')) {
97
+ return;
98
+ }
99
+ if (!isNodeOfType(node.openingElement.name, 'JSXIdentifier')) {
100
+ return;
101
+ }
102
+ const name = node.openingElement.name.name;
103
+ if (name in legacyIconImports) {
104
+ context.report({
105
+ node,
106
+ messageId: 'noLegacyIcons',
107
+ data: {
108
+ importSource: legacyIconImports[name],
109
+ iconName: name
110
+ }
111
+ });
112
+ }
113
+ },
114
+ CallExpression(node) {
115
+ if ('arguments' in node && node.arguments.length) {
116
+ for (const arg of node.arguments) {
117
+ if (isNodeOfType(arg, 'Identifier') && arg.name in legacyIconImports) {
118
+ context.report({
119
+ node: arg,
120
+ messageId: 'noLegacyIcons',
121
+ data: {
122
+ importSource: legacyIconImports[arg.name],
123
+ iconName: arg.name
124
+ }
125
+ });
126
+ }
127
+ }
128
+ }
129
+ }
130
+ };
131
+ }
132
+ });
133
+ export default rule;
@@ -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::ed09a41b825250124299d53a17b16b74>>
3
+ * @codegen <<SignedSource::45da5525f92249d3a5b5049a95fc2fc5>>
4
4
  * @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
5
5
  */
6
6
  export default {
@@ -23,6 +23,7 @@ export default {
23
23
  '@atlaskit/design-system/no-html-button': 'warn',
24
24
  '@atlaskit/design-system/no-invalid-css-map': 'error',
25
25
  '@atlaskit/design-system/no-keyframes-tagged-template-expression': 'error',
26
+ '@atlaskit/design-system/no-legacy-icons': 'warn',
26
27
  '@atlaskit/design-system/no-margin': 'warn',
27
28
  '@atlaskit/design-system/no-nested-styles': 'error',
28
29
  '@atlaskit/design-system/no-physical-properties': 'error',
@@ -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::920113eedc2218488bff48b528364867>>
3
+ * @codegen <<SignedSource::37cb0f5dfe6f5b8391203fdb1cf7cabe>>
4
4
  * @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
5
5
  */
6
6
  import consistentCssPropUsage from './consistent-css-prop-usage';
@@ -20,6 +20,7 @@ import noHtmlAnchor from './no-html-anchor';
20
20
  import noHtmlButton from './no-html-button';
21
21
  import noInvalidCssMap from './no-invalid-css-map';
22
22
  import noKeyframesTaggedTemplateExpression from './no-keyframes-tagged-template-expression';
23
+ import noLegacyIcons from './no-legacy-icons';
23
24
  import noMargin from './no-margin';
24
25
  import noNestedStyles from './no-nested-styles';
25
26
  import noPhysicalProperties from './no-physical-properties';
@@ -58,6 +59,7 @@ export default {
58
59
  'no-html-button': noHtmlButton,
59
60
  'no-invalid-css-map': noInvalidCssMap,
60
61
  'no-keyframes-tagged-template-expression': noKeyframesTaggedTemplateExpression,
62
+ 'no-legacy-icons': noLegacyIcons,
61
63
  'no-margin': noMargin,
62
64
  'no-nested-styles': noNestedStyles,
63
65
  'no-physical-properties': noPhysicalProperties,
@@ -17,6 +17,10 @@ export var StyledComponent = {
17
17
  return;
18
18
  }
19
19
  var jsxElement = (_getJsxElementByName = getJsxElementByName(styles.id.name, context.getScope())) === null || _getJsxElementByName === void 0 ? void 0 : _getJsxElementByName.parent;
20
+ if (!jsxElement) {
21
+ // If there's no JSX element, we can't determine if it's being used as an anchor or not
22
+ return;
23
+ }
20
24
  if (jsxElement && !isSupportedForLint(jsxElement, elementName)) {
21
25
  return;
22
26
  }
@@ -17,6 +17,10 @@ export var StyledComponent = {
17
17
  return;
18
18
  }
19
19
  var jsxElement = (_getJsxElementByName = getJsxElementByName(styles.id.name, context.getScope())) === null || _getJsxElementByName === void 0 ? void 0 : _getJsxElementByName.parent;
20
+ if (!jsxElement) {
21
+ // If there's no JSX element, we can't determine if it's being used as a button or not
22
+ return;
23
+ }
20
24
  if (jsxElement && !isSupportedForLint(jsxElement, elementName)) {
21
25
  return;
22
26
  }
@@ -0,0 +1,168 @@
1
+ function _createForOfIteratorHelper(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it.return != null) it.return(); } finally { if (didErr) throw err; } } }; }
2
+ function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
3
+ function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; }
4
+ import { isNodeOfType } from 'eslint-codemod-utils';
5
+ import { createLintRule } from '../utils/create-rule';
6
+ var rule = createLintRule({
7
+ meta: {
8
+ name: 'no-legacy-icons',
9
+ type: 'problem',
10
+ docs: {
11
+ description: 'Enforces no legacy icons are used.',
12
+ recommended: false,
13
+ severity: 'warn'
14
+ },
15
+ messages: {
16
+ noLegacyIcons: "Legacy icon '{{iconName}}', is being rendered from import '{{importSource}}'. Migrate to an icon from '@atlaskit/icon/(core|utility)', or '@atlaskit/icon-labs/(core|utility)'.\nLearn more in our [code migration guide](https://hello.atlassian.net/wiki/spaces/DST/pages/3748692796/New+ADS+iconography+-+Code+migration+guide)."
17
+ }
18
+ },
19
+ create: function create(context) {
20
+ var legacyIconImports = {};
21
+ return {
22
+ ImportDeclaration: function ImportDeclaration(node) {
23
+ var moduleSource = node.source.value;
24
+ if (typeof moduleSource === 'string' && ['@atlaskit/icon/glyph/', '@atlaskit/icon-object/glyph/'].find(function (val) {
25
+ return moduleSource.startsWith(val);
26
+ }) && node.specifiers.length) {
27
+ var defaultImport = node.specifiers.find(function (spec) {
28
+ return spec.type === 'ImportDefaultSpecifier';
29
+ });
30
+ if (!defaultImport) {
31
+ return;
32
+ }
33
+ var defaultImportName = defaultImport.local.name;
34
+ legacyIconImports[defaultImportName] = moduleSource;
35
+ }
36
+ },
37
+ VariableDeclaration: function VariableDeclaration(node) {
38
+ if (isNodeOfType(node, 'VariableDeclaration')) {
39
+ var _iterator = _createForOfIteratorHelper(node.declarations),
40
+ _step;
41
+ try {
42
+ for (_iterator.s(); !(_step = _iterator.n()).done;) {
43
+ var decl = _step.value;
44
+ if (isNodeOfType(decl, 'VariableDeclarator') && 'init' in decl && 'id' in decl && decl.init && decl.id && 'name' in decl.id && decl.id.name && isNodeOfType(decl.init, 'Identifier') && decl.init.name in legacyIconImports) {
45
+ legacyIconImports[decl.id.name] = legacyIconImports[decl.init.name];
46
+ }
47
+ }
48
+ } catch (err) {
49
+ _iterator.e(err);
50
+ } finally {
51
+ _iterator.f();
52
+ }
53
+ }
54
+ },
55
+ ExportDefaultDeclaration: function ExportDefaultDeclaration(node) {
56
+ if ('declaration' in node && node.declaration && isNodeOfType(node.declaration, 'Identifier') && node.declaration.name in legacyIconImports) {
57
+ context.report({
58
+ node: node,
59
+ messageId: 'noLegacyIcons',
60
+ data: {
61
+ importSource: legacyIconImports[node.declaration.name],
62
+ iconName: node.declaration.name
63
+ }
64
+ });
65
+ }
66
+ },
67
+ ExportNamedDeclaration: function ExportNamedDeclaration(node) {
68
+ if ('source' in node && node.source && isNodeOfType(node.source, 'Literal') && 'value' in node.source) {
69
+ var moduleSource = node.source.value;
70
+ if (typeof moduleSource === 'string' && ['@atlaskit/icon/glyph/', '@atlaskit/icon-object/glyph/'].find(function (val) {
71
+ return moduleSource.startsWith(val);
72
+ }) && node.specifiers.length) {
73
+ context.report({
74
+ node: node,
75
+ messageId: 'noLegacyIcons',
76
+ data: {
77
+ importSource: moduleSource,
78
+ iconName: node.specifiers[0].exported.name
79
+ }
80
+ });
81
+ }
82
+ } else if ('declaration' in node && node.declaration && isNodeOfType(node.declaration, 'VariableDeclaration')) {
83
+ var _iterator2 = _createForOfIteratorHelper(node.declaration.declarations),
84
+ _step2;
85
+ try {
86
+ for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
87
+ var decl = _step2.value;
88
+ if (isNodeOfType(decl, 'VariableDeclarator') && 'init' in decl && decl.init && isNodeOfType(decl.init, 'Identifier') && decl.init.name in legacyIconImports) {
89
+ context.report({
90
+ node: node,
91
+ messageId: 'noLegacyIcons',
92
+ data: {
93
+ importSource: legacyIconImports[decl.init.name],
94
+ iconName: decl.init.name
95
+ }
96
+ });
97
+ }
98
+ }
99
+ } catch (err) {
100
+ _iterator2.e(err);
101
+ } finally {
102
+ _iterator2.f();
103
+ }
104
+ }
105
+ },
106
+ JSXAttribute: function JSXAttribute(node) {
107
+ if (!isNodeOfType(node.value, 'JSXExpressionContainer')) {
108
+ return;
109
+ }
110
+ if (isNodeOfType(node.value.expression, 'Identifier') && node.value.expression.name in legacyIconImports && isNodeOfType(node.name, 'JSXIdentifier') && !node.name.name.startsWith('LEGACY_')) {
111
+ context.report({
112
+ node: node,
113
+ messageId: 'noLegacyIcons',
114
+ data: {
115
+ importSource: legacyIconImports[node.value.expression.name],
116
+ iconName: node.value.expression.name
117
+ }
118
+ });
119
+ }
120
+ },
121
+ JSXElement: function JSXElement(node) {
122
+ if (!isNodeOfType(node, 'JSXElement')) {
123
+ return;
124
+ }
125
+ if (!isNodeOfType(node.openingElement.name, 'JSXIdentifier')) {
126
+ return;
127
+ }
128
+ var name = node.openingElement.name.name;
129
+ if (name in legacyIconImports) {
130
+ context.report({
131
+ node: node,
132
+ messageId: 'noLegacyIcons',
133
+ data: {
134
+ importSource: legacyIconImports[name],
135
+ iconName: name
136
+ }
137
+ });
138
+ }
139
+ },
140
+ CallExpression: function CallExpression(node) {
141
+ if ('arguments' in node && node.arguments.length) {
142
+ var _iterator3 = _createForOfIteratorHelper(node.arguments),
143
+ _step3;
144
+ try {
145
+ for (_iterator3.s(); !(_step3 = _iterator3.n()).done;) {
146
+ var arg = _step3.value;
147
+ if (isNodeOfType(arg, 'Identifier') && arg.name in legacyIconImports) {
148
+ context.report({
149
+ node: arg,
150
+ messageId: 'noLegacyIcons',
151
+ data: {
152
+ importSource: legacyIconImports[arg.name],
153
+ iconName: arg.name
154
+ }
155
+ });
156
+ }
157
+ }
158
+ } catch (err) {
159
+ _iterator3.e(err);
160
+ } finally {
161
+ _iterator3.f();
162
+ }
163
+ }
164
+ }
165
+ };
166
+ }
167
+ });
168
+ export default rule;
@@ -20,6 +20,7 @@ export declare const configs: {
20
20
  '@atlaskit/design-system/no-html-button': string;
21
21
  '@atlaskit/design-system/no-invalid-css-map': string;
22
22
  '@atlaskit/design-system/no-keyframes-tagged-template-expression': string;
23
+ '@atlaskit/design-system/no-legacy-icons': string;
23
24
  '@atlaskit/design-system/no-margin': string;
24
25
  '@atlaskit/design-system/no-nested-styles': string;
25
26
  '@atlaskit/design-system/no-physical-properties': string;
@@ -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::ed09a41b825250124299d53a17b16b74>>
3
+ * @codegen <<SignedSource::45da5525f92249d3a5b5049a95fc2fc5>>
4
4
  * @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
5
5
  */
6
6
  declare const _default: {
@@ -23,6 +23,7 @@ declare const _default: {
23
23
  '@atlaskit/design-system/no-html-button': string;
24
24
  '@atlaskit/design-system/no-invalid-css-map': string;
25
25
  '@atlaskit/design-system/no-keyframes-tagged-template-expression': string;
26
+ '@atlaskit/design-system/no-legacy-icons': string;
26
27
  '@atlaskit/design-system/no-margin': string;
27
28
  '@atlaskit/design-system/no-nested-styles': string;
28
29
  '@atlaskit/design-system/no-physical-properties': string;
@@ -20,6 +20,7 @@ declare const _default: {
20
20
  'no-html-button': import("eslint").Rule.RuleModule;
21
21
  'no-invalid-css-map': import("eslint").Rule.RuleModule;
22
22
  'no-keyframes-tagged-template-expression': import("eslint").Rule.RuleModule;
23
+ 'no-legacy-icons': import("eslint").Rule.RuleModule;
23
24
  'no-margin': import("eslint").Rule.RuleModule;
24
25
  'no-nested-styles': import("eslint").Rule.RuleModule;
25
26
  'no-physical-properties': import("eslint").Rule.RuleModule;
@@ -0,0 +1,2 @@
1
+ declare const rule: import("eslint").Rule.RuleModule;
2
+ export default rule;
@@ -20,6 +20,7 @@ export declare const configs: {
20
20
  '@atlaskit/design-system/no-html-button': string;
21
21
  '@atlaskit/design-system/no-invalid-css-map': string;
22
22
  '@atlaskit/design-system/no-keyframes-tagged-template-expression': string;
23
+ '@atlaskit/design-system/no-legacy-icons': string;
23
24
  '@atlaskit/design-system/no-margin': string;
24
25
  '@atlaskit/design-system/no-nested-styles': string;
25
26
  '@atlaskit/design-system/no-physical-properties': string;
@@ -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::ed09a41b825250124299d53a17b16b74>>
3
+ * @codegen <<SignedSource::45da5525f92249d3a5b5049a95fc2fc5>>
4
4
  * @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
5
5
  */
6
6
  declare const _default: {
@@ -23,6 +23,7 @@ declare const _default: {
23
23
  '@atlaskit/design-system/no-html-button': string;
24
24
  '@atlaskit/design-system/no-invalid-css-map': string;
25
25
  '@atlaskit/design-system/no-keyframes-tagged-template-expression': string;
26
+ '@atlaskit/design-system/no-legacy-icons': string;
26
27
  '@atlaskit/design-system/no-margin': string;
27
28
  '@atlaskit/design-system/no-nested-styles': string;
28
29
  '@atlaskit/design-system/no-physical-properties': string;
@@ -24,6 +24,7 @@ declare const _default: {
24
24
  'no-html-button': import("eslint").Rule.RuleModule;
25
25
  'no-invalid-css-map': import("eslint").Rule.RuleModule;
26
26
  'no-keyframes-tagged-template-expression': import("eslint").Rule.RuleModule;
27
+ 'no-legacy-icons': import("eslint").Rule.RuleModule;
27
28
  'no-margin': import("eslint").Rule.RuleModule;
28
29
  'no-nested-styles': import("eslint").Rule.RuleModule;
29
30
  'no-physical-properties': import("eslint").Rule.RuleModule;
@@ -0,0 +1,2 @@
1
+ declare const rule: import("eslint").Rule.RuleModule;
2
+ export default rule;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@atlaskit/eslint-plugin-design-system",
3
3
  "description": "The essential plugin for use with the Atlassian Design System.",
4
- "version": "10.4.4",
4
+ "version": "10.5.0",
5
5
  "author": "Atlassian Pty Ltd",
6
6
  "license": "Apache-2.0",
7
7
  "publishConfig": {