@atlaskit/eslint-plugin-design-system 4.0.0 → 4.2.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 (65) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/dist/cjs/index.js +14 -3
  3. package/dist/cjs/rules/ensure-design-token-usage/index.js +3 -0
  4. package/dist/cjs/rules/no-deprecated-apis/index.js +91 -0
  5. package/dist/cjs/rules/no-deprecated-design-token-usage/index.js +76 -0
  6. package/dist/cjs/rules/no-deprecated-imports/index.js +45 -33
  7. package/dist/cjs/rules/no-deprecated-imports/paths.js +43 -15
  8. package/dist/cjs/rules/no-unsafe-design-token-usage/index.js +19 -5
  9. package/dist/cjs/rules/use-visually-hidden/constants.js +12 -0
  10. package/dist/cjs/rules/use-visually-hidden/fix-jsx.js +39 -0
  11. package/dist/cjs/rules/use-visually-hidden/fix-vanilla.js +35 -0
  12. package/dist/cjs/rules/use-visually-hidden/index.js +194 -0
  13. package/dist/cjs/rules/use-visually-hidden/utils.js +91 -0
  14. package/dist/cjs/rules/utils/get-import-node-by-source.js +23 -0
  15. package/dist/cjs/rules/utils/is-color.js +3 -2
  16. package/dist/cjs/rules/utils/is-node.js +33 -3
  17. package/dist/cjs/rules/utils/remove-named-import.js +29 -0
  18. package/dist/cjs/version.json +1 -1
  19. package/dist/es2019/index.js +10 -2
  20. package/dist/es2019/rules/ensure-design-token-usage/index.js +3 -0
  21. package/dist/es2019/rules/no-deprecated-apis/index.js +72 -0
  22. package/dist/es2019/rules/no-deprecated-design-token-usage/index.js +58 -0
  23. package/dist/es2019/rules/no-deprecated-imports/index.js +45 -32
  24. package/dist/es2019/rules/no-deprecated-imports/paths.js +41 -14
  25. package/dist/es2019/rules/no-unsafe-design-token-usage/index.js +12 -5
  26. package/dist/es2019/rules/use-visually-hidden/constants.js +3 -0
  27. package/dist/es2019/rules/use-visually-hidden/fix-jsx.js +24 -0
  28. package/dist/es2019/rules/use-visually-hidden/fix-vanilla.js +21 -0
  29. package/dist/es2019/rules/use-visually-hidden/index.js +169 -0
  30. package/dist/es2019/rules/use-visually-hidden/utils.js +62 -0
  31. package/dist/es2019/rules/utils/get-import-node-by-source.js +10 -0
  32. package/dist/es2019/rules/utils/is-color.js +2 -1
  33. package/dist/es2019/rules/utils/is-node.js +19 -2
  34. package/dist/es2019/rules/utils/remove-named-import.js +16 -0
  35. package/dist/es2019/version.json +1 -1
  36. package/dist/esm/index.js +10 -2
  37. package/dist/esm/rules/ensure-design-token-usage/index.js +3 -0
  38. package/dist/esm/rules/no-deprecated-apis/index.js +81 -0
  39. package/dist/esm/rules/no-deprecated-design-token-usage/index.js +66 -0
  40. package/dist/esm/rules/no-deprecated-imports/index.js +45 -32
  41. package/dist/esm/rules/no-deprecated-imports/paths.js +41 -14
  42. package/dist/esm/rules/no-unsafe-design-token-usage/index.js +20 -5
  43. package/dist/esm/rules/use-visually-hidden/constants.js +3 -0
  44. package/dist/esm/rules/use-visually-hidden/fix-jsx.js +26 -0
  45. package/dist/esm/rules/use-visually-hidden/fix-vanilla.js +23 -0
  46. package/dist/esm/rules/use-visually-hidden/index.js +180 -0
  47. package/dist/esm/rules/use-visually-hidden/utils.js +74 -0
  48. package/dist/esm/rules/utils/get-import-node-by-source.js +14 -0
  49. package/dist/esm/rules/utils/is-color.js +2 -1
  50. package/dist/esm/rules/utils/is-node.js +22 -1
  51. package/dist/esm/rules/utils/remove-named-import.js +20 -0
  52. package/dist/esm/version.json +1 -1
  53. package/dist/types/index.d.ts +5 -0
  54. package/dist/types/rules/no-deprecated-apis/index.d.ts +3 -0
  55. package/dist/types/rules/no-deprecated-design-token-usage/index.d.ts +3 -0
  56. package/dist/types/rules/no-deprecated-imports/paths.d.ts +33 -14
  57. package/dist/types/rules/use-visually-hidden/constants.d.ts +3 -0
  58. package/dist/types/rules/use-visually-hidden/fix-jsx.d.ts +3 -0
  59. package/dist/types/rules/use-visually-hidden/fix-vanilla.d.ts +3 -0
  60. package/dist/types/rules/use-visually-hidden/index.d.ts +3 -0
  61. package/dist/types/rules/use-visually-hidden/utils.d.ts +35 -0
  62. package/dist/types/rules/utils/get-import-node-by-source.d.ts +8 -0
  63. package/dist/types/rules/utils/is-node.d.ts +7 -0
  64. package/dist/types/rules/utils/remove-named-import.d.ts +8 -0
  65. package/package.json +6 -2
@@ -0,0 +1,194 @@
1
+ "use strict";
2
+
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
+
5
+ Object.defineProperty(exports, "__esModule", {
6
+ value: true
7
+ });
8
+ exports.default = void 0;
9
+
10
+ var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
11
+
12
+ var _isNode = require("../utils/is-node");
13
+
14
+ var _fixJsx = _interopRequireDefault(require("./fix-jsx"));
15
+
16
+ var _fixVanilla = _interopRequireDefault(require("./fix-vanilla"));
17
+
18
+ var _utils = require("./utils");
19
+
20
+ /* eslint-disable @atlaskit/design-system/use-visually-hidden */
21
+ var THEME_IMPORT_NAMES = ['visuallyHidden', 'assistive'];
22
+ var rule = {
23
+ meta: {
24
+ type: 'suggestion',
25
+ fixable: 'code',
26
+ docs: {
27
+ description: 'Suggest usage of the `@atlaskit/visually-hidden` component instead of either the `@atlaskit/theme` mixins or just something the user has rolled themselves.',
28
+ recommended: true
29
+ },
30
+ messages: {
31
+ noDeprecatedUsage: 'Using the export `{{local}}` from `{{import}}` as a mixin is discouraged. Please use `@atlaskit/visually-hidden` instead.',
32
+ noDeprecated: 'The export `{{local}}` from `{{import}}` is deprecated. Please use `@atlaskit/visually-hidden` instead.',
33
+ suggestion: 'This CSS closely matches the implementation of a visually hidden element. You should consider using the `@atlaskit/visually-hidden` component instead.'
34
+ }
35
+ },
36
+ create: function create(context) {
37
+ var source = context.getSourceCode();
38
+ return {
39
+ ImportDeclaration: function ImportDeclaration(node) {
40
+ var isThemeNode = node.source.value === '@atlaskit/theme' || node.source.value === '@atlaskit/theme/constants';
41
+
42
+ if (!isThemeNode) {
43
+ return;
44
+ }
45
+
46
+ var visuallyHiddenOrAssistive = node.specifiers.filter(function (specifier) {
47
+ return specifier.type === 'ImportSpecifier';
48
+ }).find(function (specifier) {
49
+ return THEME_IMPORT_NAMES.includes(specifier.imported.name);
50
+ });
51
+
52
+ if (!visuallyHiddenOrAssistive) {
53
+ return;
54
+ }
55
+
56
+ context.getDeclaredVariables(visuallyHiddenOrAssistive).forEach(function (someNode) {
57
+ someNode.references.map(function (innerNode) {
58
+ return innerNode.identifier;
59
+ }).forEach(function (idNode) {
60
+ // @ts-ignore JSX is not typed correctly in eslint
61
+ if ((idNode === null || idNode === void 0 ? void 0 : idNode.parent.type) === 'JSXExpressionContainer') {
62
+ context.report({
63
+ node: idNode.parent,
64
+ messageId: 'noDeprecatedUsage',
65
+ data: {
66
+ import: "".concat(node.source.value),
67
+ local: visuallyHiddenOrAssistive.local.name
68
+ },
69
+ fix: (0, _fixJsx.default)(source, idNode)
70
+ }); // this is either a styled usage OR mixin usage in a styled usage
71
+ } else if (idNode.parent.type === 'CallExpression') {
72
+ if ((0, _isNode.isStyledObjectNode)(idNode.parent) || (0, _isNode.isStyledTemplateNode)(idNode.parent)) {
73
+ context.report({
74
+ node: idNode.parent,
75
+ messageId: 'noDeprecatedUsage',
76
+ data: {
77
+ import: "".concat(node.source.value),
78
+ local: visuallyHiddenOrAssistive.local.name
79
+ },
80
+ fix: (0, _fixVanilla.default)(source, idNode.parent)
81
+ });
82
+ }
83
+
84
+ if (idNode.parent.callee === idNode) {
85
+ context.report({
86
+ node: idNode.parent,
87
+ messageId: 'noDeprecatedUsage',
88
+ data: {
89
+ import: "".concat(node.source.value),
90
+ local: visuallyHiddenOrAssistive.local.name
91
+ },
92
+ fix: (0, _fixVanilla.default)(source, (0, _isNode.getClosestNodeOfType)(idNode.parent, 'TaggedTemplateExpression'))
93
+ });
94
+ }
95
+ }
96
+ });
97
+ });
98
+ return context.report({
99
+ node: visuallyHiddenOrAssistive,
100
+ messageId: 'noDeprecated',
101
+ data: {
102
+ import: "".concat(node.source.value),
103
+ local: visuallyHiddenOrAssistive.local.name
104
+ }
105
+ });
106
+ },
107
+ CallExpression: function CallExpression(node) {
108
+ var _node$arguments$;
109
+
110
+ if (node.type !== 'CallExpression') {
111
+ return;
112
+ }
113
+
114
+ if (!(node.callee.type === 'MemberExpression' || node.callee.type === 'Identifier')) {
115
+ return;
116
+ }
117
+
118
+ var isStyled = (0, _isNode.isStyledObjectNode)(node);
119
+
120
+ if (node.callee.type === 'MemberExpression' && node.callee.object.type === 'Identifier' && node.callee.object.name !== 'styled') {
121
+ return;
122
+ }
123
+
124
+ if (node.callee.type === 'Identifier' && node.callee.name !== 'css') {
125
+ return;
126
+ } // This is an object style (probably)
127
+
128
+
129
+ if (node.arguments && ((_node$arguments$ = node.arguments[0]) === null || _node$arguments$ === void 0 ? void 0 : _node$arguments$.type) === 'ObjectExpression') {
130
+ var matchingScore = (0, _utils.getObjectLikeness)(node.arguments[0]);
131
+
132
+ if (matchingScore > 0.8) {
133
+ return context.report({
134
+ node: node.parent,
135
+ messageId: 'suggestion',
136
+ fix: isStyled ? (0, _fixVanilla.default)(source, node) : undefined
137
+ });
138
+ }
139
+ }
140
+
141
+ return null;
142
+ },
143
+ ObjectExpression: function ObjectExpression(node) {
144
+ if (node.parent.type === 'CallExpression') {
145
+ return;
146
+ }
147
+
148
+ var matchingScore = (0, _utils.getObjectLikeness)(node);
149
+
150
+ if (matchingScore > 0.8) {
151
+ return context.report({
152
+ node: node,
153
+ messageId: 'suggestion'
154
+ });
155
+ }
156
+ },
157
+ 'TaggedTemplateExpression[tag.name="css"],TaggedTemplateExpression[tag.object.name="styled"]': function TaggedTemplateExpressionTagNameCssTaggedTemplateExpressionTagObjectNameStyled(node) {
158
+ if (node.type !== 'TaggedTemplateExpression') {
159
+ return;
160
+ }
161
+
162
+ var templateString = node.quasi.quasis.map(function (q) {
163
+ return q.value.raw;
164
+ }).join('');
165
+ var styleEntries = (0, _utils.makeTemplateLiteralIntoEntries)(templateString);
166
+
167
+ if (!styleEntries) {
168
+ return;
169
+ }
170
+
171
+ var count = (0, _utils.countMatchingKeyValues)(styleEntries.map(function (_ref) {
172
+ var _ref2 = (0, _slicedToArray2.default)(_ref, 2),
173
+ key = _ref2[0],
174
+ value = _ref2[1];
175
+
176
+ return {
177
+ key: key,
178
+ value: value
179
+ };
180
+ }));
181
+
182
+ if (count > 0.8) {
183
+ return context.report({
184
+ node: node,
185
+ messageId: 'suggestion',
186
+ fix: node.tag.type !== 'Identifier' ? (0, _fixVanilla.default)(source, node) : undefined
187
+ });
188
+ }
189
+ }
190
+ };
191
+ }
192
+ };
193
+ var _default = rule;
194
+ exports.default = _default;
@@ -0,0 +1,91 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.makeTemplateLiteralIntoEntries = exports.getObjectLikeness = exports.getFirstImport = exports.countMatchingKeyValues = void 0;
7
+ // eslint-disable-next-line import/no-unresolved
8
+ // eslint-disable-next-line @atlaskit/design-system/use-visually-hidden
9
+ var referenceObject = {
10
+ width: '1px',
11
+ height: '1px',
12
+ padding: '0',
13
+ position: 'absolute',
14
+ border: '0',
15
+ clip: 'rect(1px, 1px, 1px, 1px)',
16
+ overflow: 'hidden',
17
+ whiteSpace: 'nowrap'
18
+ };
19
+
20
+ /**
21
+ * Returns the first import in the esprima AST.
22
+ */
23
+ var getFirstImport = function getFirstImport(source) {
24
+ return source.ast.body.find(function (node) {
25
+ return node.type === 'ImportDeclaration';
26
+ });
27
+ };
28
+ /**
29
+ * Takes a template literal and returns [key, value] array of the css properties
30
+ */
31
+
32
+
33
+ exports.getFirstImport = getFirstImport;
34
+
35
+ var makeTemplateLiteralIntoEntries = function makeTemplateLiteralIntoEntries(templateString) {
36
+ return templateString.replace(/\n/g, '').split(/;|{|}/).filter(function (el) {
37
+ return !el.match(/\@/);
38
+ }).map(function (el) {
39
+ return el.trim().split(':').map(function (e) {
40
+ return e.trim();
41
+ });
42
+ });
43
+ };
44
+ /**
45
+ * Given a node, translate the node into css key-value pairs and
46
+ * compare the output to the reference styles required to make a
47
+ * visually hidden element.
48
+ *
49
+ * @returns {number} A fraction between 0-1 depending on the object's likeness.
50
+ */
51
+
52
+
53
+ exports.makeTemplateLiteralIntoEntries = makeTemplateLiteralIntoEntries;
54
+
55
+ var getObjectLikeness = function getObjectLikeness(node) {
56
+ var styleEntries = node.properties.map(function (_ref) {
57
+ var type = _ref.type,
58
+ key = _ref.key,
59
+ value = _ref.value;
60
+
61
+ if (type === 'Property' && key.type === 'Identifier') {
62
+ return {
63
+ key: key.name,
64
+ value: value.type === 'Literal' && value.value
65
+ };
66
+ }
67
+
68
+ return null;
69
+ }).filter(function (node) {
70
+ return Boolean(node);
71
+ });
72
+ return countMatchingKeyValues(styleEntries);
73
+ };
74
+
75
+ exports.getObjectLikeness = getObjectLikeness;
76
+
77
+ var countMatchingKeyValues = function countMatchingKeyValues(styleEntries) {
78
+ var matchingStyleEntries = styleEntries.filter(function (entry) {
79
+ return entry.key in referenceObject;
80
+ });
81
+
82
+ if (styleEntries.length < 5) {
83
+ return 0;
84
+ }
85
+
86
+ return matchingStyleEntries.reduce(function (acc, curr) {
87
+ return acc + (referenceObject[curr === null || curr === void 0 ? void 0 : curr.key] === (curr === null || curr === void 0 ? void 0 : curr.value) ? 1.5 : 0.75);
88
+ }, 0) / styleEntries.length;
89
+ };
90
+
91
+ exports.countMatchingKeyValues = countMatchingKeyValues;
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.getImportedNodeBySource = void 0;
7
+
8
+ /* eslint-disable import/no-unresolved */
9
+
10
+ /**
11
+ * @param {SourceCode} source The eslint source
12
+ * @param {string} path The path specified to find
13
+ * @returns {ImportDeclaration}
14
+ */
15
+ var getImportedNodeBySource = function getImportedNodeBySource(source, path) {
16
+ return source.ast.body.filter(function (node) {
17
+ return node.type === 'ImportDeclaration';
18
+ }).find(function (node) {
19
+ return node.source.value === path;
20
+ });
21
+ };
22
+
23
+ exports.getImportedNodeBySource = getImportedNodeBySource;
@@ -3,7 +3,7 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.isHardCodedColor = exports.includesHardCodedColor = exports.isLegacyNamedColor = exports.isLegacyColor = void 0;
6
+ exports.isLegacyNamedColor = exports.isLegacyColor = exports.isHardCodedColor = exports.includesHardCodedColor = void 0;
7
7
  var namedColors = ['black', 'silver', 'gray', 'white', 'maroon', 'red', 'purple', 'fuchsia', 'green', 'lime', 'olive', 'yellow', 'navy', 'blue', 'teal', 'aqua', 'orange', 'aliceblue', 'antiquewhite', 'aquamarine', 'azure', 'beige', 'bisque', 'blanchedalmond', 'blueviolet', 'brown', 'burlywood', 'cadetblue', 'chartreuse', 'chocolate', 'coral', 'cornflowerblue', 'cornsilk', 'crimson', 'cyan', 'darkblue', 'darkcyan', 'darkgoldenrod', 'darkgray', 'darkgreen', 'darkgrey', 'darkkhaki', 'darkmagenta', 'darkolivegreen', 'darkorange', 'darkorchid', 'darkred', 'darksalmon', 'darkseagreen', 'darkslateblue', 'darkslategray', 'darkslategrey', 'darkturquoise', 'darkviolet', 'deeppink', 'deepskyblue', 'dimgray', 'dimgrey', 'dodgerblue', 'firebrick', 'floralwhite', 'forestgreen', 'gainsboro', 'ghostwhite', 'gold', 'goldenrod', 'greenyellow', 'grey', 'honeydew', 'hotpink', 'indianred', 'indigo', 'ivory', 'khaki', 'lavender', 'lavenderblush', 'lawngreen', 'lemonchiffon', 'lightblue', 'lightcoral', 'lightcyan', 'lightgoldenrodyellow', 'lightgray', 'lightgreen', 'lightgrey', 'lightpink', 'lightsalmon', 'lightseagreen', 'lightskyblue', 'lightslategray', 'lightslategrey', 'lightsteelblue', 'lightyellow', 'limegreen', 'linen', 'magenta', 'mediumaquamarine', 'mediumblue', 'mediumorchid', 'mediumpurple', 'mediumseagreen', 'mediumslateblue', 'mediumspringgreen', 'mediumturquoise', 'mediumvioletred', 'midnightblue', 'mintcream', 'mistyrose', 'moccasin', 'navajowhite', 'oldlace', 'olivedrab', 'orangered', 'orchid', 'palegoldenrod', 'palegreen', 'paleturquoise', 'palevioletred', 'papayawhip', 'peachpuff', 'peru', 'pink', 'plum', 'powderblue', 'rosybrown', 'royalblue', 'saddlebrown', 'salmon', 'sandybrown', 'seagreen', 'seashell', 'sienna', 'skyblue', 'slateblue', 'slategray', 'slategrey', 'snow', 'springgreen', 'steelblue', 'tan', 'thistle', 'tomato', 'turquoise', 'violet', 'wheat', 'whitesmoke', 'yellowgreen', 'rebeccapurple'];
8
8
  var legacyColors = ['R50', 'R75', 'R100', 'R200', 'R300', 'R400', 'R500', 'Y50', 'Y75', 'Y100', 'Y200', 'Y300', 'Y400', 'Y500', 'G50', 'G75', 'G100', 'G200', 'G300', 'G400', 'G500', 'B50', 'B75', 'B100', 'B200', 'B300', 'B400', 'B500', 'P50', 'P75', 'P100', 'P200', 'P300', 'P400', 'P500', 'T50', 'T75', 'T100', 'T200', 'T300', 'T400', 'T500', 'N0', 'N10', 'N20', 'N30', 'N40', 'N50', 'N60', 'N70', 'N80', 'N90', 'N100', 'N200', 'N300', 'N400', 'N500', 'N600', 'N700', 'N800', 'N900', 'N10A', 'N20A', 'N30A', 'N40A', 'N50A', 'N60A', 'N70A', 'N80A', 'N90A', 'N100A', 'N200A', 'N300A', 'N400A', 'N500A', 'N600A', 'N700A', 'N800A', 'DN900', 'DN800', 'DN700', 'DN600', 'DN500', 'DN400', 'DN300', 'DN200', 'DN100', 'DN90', 'DN80', 'DN70', 'DN60', 'DN50', 'DN40', 'DN30', 'DN20', 'DN10', 'DN0', 'DN800A', 'DN700A', 'DN600A', 'DN500A', 'DN400A', 'DN300A', 'DN200A', 'DN100A', 'DN90A', 'DN80A', 'DN70A', 'DN60A', 'DN50A', 'DN40A', 'DN30A', 'DN20A', 'DN10A'];
9
9
  var legacyColorMixins = ['background', 'backgroundActive', 'backgroundHover', 'backgroundOnLayer', 'text', 'textHover', 'textActive', 'subtleText', 'placeholderText', 'heading', 'subtleHeading', 'codeBlock', 'link', 'linkHover', 'linkActive', 'linkOutline', 'primary', 'blue', 'teal', 'purple', 'red', 'yellow', 'green', 'skeleton'];
@@ -56,7 +56,8 @@ var isHardCodedColor = function isHardCodedColor(value) {
56
56
  return true;
57
57
  }
58
58
 
59
- if (value.startsWith('#') && (value.length === 4 || value.length === 7 || value.length === 9)) {
59
+ if (value.startsWith('#') && ( // short hex, hex, or hex with alpha
60
+ value.length === 4 || value.length === 7 || value.length === 9)) {
60
61
  return true;
61
62
  }
62
63
 
@@ -3,7 +3,7 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.isChildOfType = exports.isDecendantOfStyleBlock = exports.isDecendantOfStyleJsxAttribute = exports.isDecendantOfType = exports.isDecendantOfGlobalToken = void 0;
6
+ exports.isStyledTemplateNode = exports.isStyledObjectNode = exports.isDecendantOfType = exports.isDecendantOfStyleJsxAttribute = exports.isDecendantOfStyleBlock = exports.isDecendantOfGlobalToken = exports.isChildOfType = exports.getClosestNodeOfType = void 0;
7
7
 
8
8
  var isDecendantOfGlobalToken = function isDecendantOfGlobalToken(node) {
9
9
  if (node.type === 'CallExpression' && node.callee.type === 'Identifier' && node.callee.name === 'token') {
@@ -50,6 +50,18 @@ var isDecendantOfStyleJsxAttribute = function isDecendantOfStyleJsxAttribute(nod
50
50
 
51
51
  exports.isDecendantOfStyleJsxAttribute = isDecendantOfStyleJsxAttribute;
52
52
 
53
+ var isStyledTemplateNode = function isStyledTemplateNode(node) {
54
+ return (node === null || node === void 0 ? void 0 : node.type) === 'TaggedTemplateExpression' && node.tag.type === 'MemberExpression' && node.tag.object.type === 'Identifier' && node.tag.object.name === 'styled';
55
+ };
56
+
57
+ exports.isStyledTemplateNode = isStyledTemplateNode;
58
+
59
+ var isStyledObjectNode = function isStyledObjectNode(node) {
60
+ return (node === null || node === void 0 ? void 0 : node.type) === 'CallExpression' && node.callee.type === 'MemberExpression' && node.callee.object.type === 'Identifier' && node.callee.object.name === 'styled';
61
+ };
62
+
63
+ exports.isStyledObjectNode = isStyledObjectNode;
64
+
53
65
  var isDecendantOfStyleBlock = function isDecendantOfStyleBlock(node) {
54
66
  if (node.type === 'VariableDeclarator') {
55
67
  if (node.id.type !== 'Identifier') {
@@ -81,7 +93,7 @@ var isDecendantOfStyleBlock = function isDecendantOfStyleBlock(node) {
81
93
  return true;
82
94
  }
83
95
 
84
- if (node.type === 'TaggedTemplateExpression' && node.tag.type === 'MemberExpression' && node.tag.object.type === 'Identifier' && node.tag.object.name === 'styled') {
96
+ if (isStyledTemplateNode(node)) {
85
97
  return true;
86
98
  }
87
99
 
@@ -101,5 +113,23 @@ exports.isDecendantOfStyleBlock = isDecendantOfStyleBlock;
101
113
  var isChildOfType = function isChildOfType(node, type) {
102
114
  return node.parent.type === type;
103
115
  };
116
+ /**
117
+ * Given a node, walk up the tree until there is no parent OR a common ancestor of the correct type is found
118
+ */
119
+
120
+
121
+ exports.isChildOfType = isChildOfType;
122
+
123
+ var getClosestNodeOfType = function getClosestNodeOfType(node, type) {
124
+ if (!node) {
125
+ return node;
126
+ }
127
+
128
+ if (node.type === type) {
129
+ return node;
130
+ }
131
+
132
+ return getClosestNodeOfType(node.parent, type);
133
+ };
104
134
 
105
- exports.isChildOfType = isChildOfType;
135
+ exports.getClosestNodeOfType = getClosestNodeOfType;
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.removeNamedImport = void 0;
7
+
8
+ /* eslint-disable import/no-unresolved */
9
+
10
+ /**
11
+ * @param {SourceCode} source The eslint source
12
+ * @param {string} path The path specified to find
13
+ * @returns FixerObject
14
+ */
15
+ var removeNamedImport = function removeNamedImport(fixer, importNode, name) {
16
+ var filteredSpecifers = importNode.specifiers.filter(function (node) {
17
+ return node.type === 'ImportSpecifier' && node.imported.name !== name;
18
+ });
19
+
20
+ if (filteredSpecifers.length) {
21
+ return fixer.remove(importNode.specifiers.find(function (node) {
22
+ return node.type === 'ImportSpecifier' && node.imported.name === name;
23
+ }));
24
+ }
25
+
26
+ return fixer.remove(importNode);
27
+ };
28
+
29
+ exports.removeNamedImport = removeNamedImport;
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "@atlaskit/eslint-plugin-design-system",
3
- "version": "4.0.0",
3
+ "version": "4.2.0",
4
4
  "sideEffects": false
5
5
  }
@@ -1,16 +1,24 @@
1
1
  import ensureTokenUsage from './rules/ensure-design-token-usage';
2
+ import noDeprecatedAPIs from './rules/no-deprecated-apis';
3
+ import noDeprecatedUsage from './rules/no-deprecated-design-token-usage';
2
4
  import noDeprecatedImports from './rules/no-deprecated-imports';
3
5
  import noUnsafeUsage from './rules/no-unsafe-design-token-usage';
6
+ import useVisuallyHidden from './rules/use-visually-hidden';
4
7
  export const rules = {
5
8
  'ensure-design-token-usage': ensureTokenUsage,
6
9
  'no-unsafe-design-token-usage': noUnsafeUsage,
7
- 'no-deprecated-imports': noDeprecatedImports
10
+ 'no-deprecated-design-token-usage': noDeprecatedUsage,
11
+ 'no-deprecated-imports': noDeprecatedImports,
12
+ 'no-deprecated-apis': noDeprecatedAPIs,
13
+ 'use-visually-hidden': useVisuallyHidden
8
14
  };
9
15
  export const configs = {
10
16
  recommended: {
11
17
  plugins: ['@atlaskit/design-system'],
12
18
  rules: {
13
- '@atlaskit/design-system/no-deprecated-imports': 'error'
19
+ '@atlaskit/design-system/no-deprecated-imports': 'error',
20
+ '@atlaskit/design-system/use-visually-hidden': 'error',
21
+ '@atlaskit/design-system/no-deprecated-apis': 'warn'
14
22
  }
15
23
  }
16
24
  };
@@ -24,6 +24,9 @@ const filterSuggestion = ({
24
24
 
25
25
  const rule = {
26
26
  meta: {
27
+ // We need to upgrade the version of ESLint.
28
+ // @ts-expect-error
29
+ hasSuggestions: true,
27
30
  docs: {
28
31
  recommended: true
29
32
  },
@@ -0,0 +1,72 @@
1
+ // eslint-disable-next-line import/no-unresolved
2
+ import { getImportedNodeBySource } from '../utils/get-import-node-by-source';
3
+ import { getClosestNodeOfType } from '../utils/is-node';
4
+ const unsafeOverridesConfig = {
5
+ cssFn: ['@atlaskit/menu', '@atlaskit/side-navigation'],
6
+ overrides: ['@atlaskit/drawer', '@atlaskit/menu', '@atlaskit/side-navigation']
7
+ };
8
+ const unsafeOverrides = ['cssFn', 'overrides'];
9
+ const rule = {
10
+ meta: {
11
+ type: 'suggestion',
12
+ docs: {
13
+ description: 'Disallow specified APIs that have been marked as deprecated and/or discouraged.',
14
+ recommended: true
15
+ },
16
+ messages: {
17
+ noDeprecatedApis: `The prop "{{propName}}" has been deprecated. It will be removed in the next major release.`
18
+ }
19
+ },
20
+
21
+ create(context) {
22
+ return {
23
+ // find JSX atribute - find name of attribute - get source and find relevant identifiers.
24
+ JSXAttribute(node) {
25
+ var _node$name, _closesetJSXElement$n;
26
+
27
+ if (!unsafeOverrides.includes(node === null || node === void 0 ? void 0 : (_node$name = node.name) === null || _node$name === void 0 ? void 0 : _node$name.name)) {
28
+ return;
29
+ }
30
+
31
+ const source = context.getSourceCode();
32
+ const bannedApi = node.name.name; // traverse the tree to the nearest JSX Element and get its name
33
+
34
+ const closesetJSXElement = getClosestNodeOfType(node, 'JSXOpeningElement'); // @ts-ignore
35
+
36
+ const jsxElementName = closesetJSXElement === null || closesetJSXElement === void 0 ? void 0 : (_closesetJSXElement$n = closesetJSXElement.name) === null || _closesetJSXElement$n === void 0 ? void 0 : _closesetJSXElement$n.name;
37
+
38
+ if (!jsxElementName) {
39
+ return;
40
+ } // find an import for the path of the banned api
41
+
42
+
43
+ unsafeOverridesConfig[bannedApi].forEach(path => {
44
+ const importNode = getImportedNodeBySource(source, path);
45
+
46
+ if (!importNode) {
47
+ return;
48
+ } // find an import that matches our JSX element
49
+
50
+
51
+ const hasTargetNode = importNode.specifiers.some(node => node.local.name === jsxElementName);
52
+
53
+ if (!hasTargetNode) {
54
+ return;
55
+ } // if we're here the import exists and there is a valid lint error.
56
+
57
+
58
+ context.report({
59
+ node: node,
60
+ messageId: 'noDeprecatedApis',
61
+ data: {
62
+ propName: bannedApi
63
+ }
64
+ });
65
+ });
66
+ }
67
+
68
+ };
69
+ }
70
+
71
+ };
72
+ export default rule;
@@ -0,0 +1,58 @@
1
+ import renameMapping from '@atlaskit/tokens/rename-mapping';
2
+
3
+ const getCleanPathId = path => path.split('.').filter(el => el !== '[default]').join('.');
4
+
5
+ const rule = {
6
+ meta: {
7
+ docs: {
8
+ recommended: true
9
+ },
10
+ fixable: 'code',
11
+ type: 'problem',
12
+ messages: {
13
+ tokenRenamed: 'The token "{{name}}" is deprecated in favour of "{{replacement}}".'
14
+ }
15
+ },
16
+
17
+ create(context) {
18
+ return {
19
+ 'CallExpression[callee.name="token"]': node => {
20
+ if (node.type !== 'CallExpression') {
21
+ return;
22
+ }
23
+
24
+ if (node.arguments[0].type !== 'Literal') {
25
+ return;
26
+ }
27
+
28
+ const tokenKey = node.arguments[0].value;
29
+
30
+ if (!tokenKey) {
31
+ return;
32
+ }
33
+
34
+ if (typeof tokenKey !== 'string') {
35
+ return;
36
+ }
37
+
38
+ const migrationMeta = renameMapping.filter(t => t.state === 'deprecated').find(t => t.path === tokenKey);
39
+
40
+ if (migrationMeta) {
41
+ const cleanTokenKey = getCleanPathId(migrationMeta.replacement);
42
+ context.report({
43
+ messageId: 'tokenRenamed',
44
+ node,
45
+ data: {
46
+ name: tokenKey,
47
+ replacement: cleanTokenKey
48
+ },
49
+ fix: fixer => fixer.replaceText(node.arguments[0], `'${cleanTokenKey}'`)
50
+ });
51
+ return;
52
+ }
53
+ }
54
+ };
55
+ }
56
+
57
+ };
58
+ export default rule;