@atlaskit/eslint-plugin-design-system 8.15.5 → 8.16.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 (99) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/constellation/index/usage.mdx +62 -16
  3. package/dist/cjs/rules/prefer-primitives/index.js +2 -3
  4. package/dist/cjs/rules/prefer-primitives/utils.js +16 -5
  5. package/dist/cjs/rules/use-primitives/index.js +89 -58
  6. package/dist/cjs/rules/use-primitives/transformers/css-to-xcss.js +95 -0
  7. package/dist/cjs/rules/use-primitives/transformers/index.js +31 -0
  8. package/dist/cjs/rules/use-primitives/transformers/jsx-element-to-box.js +26 -0
  9. package/dist/cjs/rules/use-primitives/utils/contains-only-supported-attrs.js +19 -0
  10. package/dist/cjs/rules/use-primitives/utils/convert-ast-object-expression-to-js-object.js +31 -0
  11. package/dist/cjs/rules/use-primitives/utils/get-attribute-value-identifier.js +37 -0
  12. package/dist/cjs/rules/use-primitives/utils/get-function-argument-at-pos.js +10 -0
  13. package/dist/cjs/rules/use-primitives/utils/get-jsx-attribute-by-name.js +16 -0
  14. package/dist/cjs/rules/use-primitives/utils/get-variable-definition-value.js +29 -0
  15. package/dist/cjs/rules/use-primitives/utils/get-variable-usage-count.js +21 -0
  16. package/dist/cjs/rules/use-primitives/utils/index.js +89 -0
  17. package/dist/cjs/rules/use-primitives/utils/is-function-named.js +19 -0
  18. package/dist/cjs/rules/use-primitives/utils/is-valid-tag-name.js +13 -0
  19. package/dist/cjs/rules/use-primitives/utils/update-jsx-attribute-by-name.js +31 -0
  20. package/dist/cjs/rules/use-primitives/utils/update-jsx-element-name.js +16 -0
  21. package/dist/cjs/rules/use-primitives/utils/upsert-import-declaration.js +80 -0
  22. package/dist/es2019/rules/prefer-primitives/index.js +1 -2
  23. package/dist/es2019/rules/prefer-primitives/utils.js +11 -2
  24. package/dist/es2019/rules/use-primitives/index.js +91 -60
  25. package/dist/es2019/rules/use-primitives/transformers/css-to-xcss.js +91 -0
  26. package/dist/es2019/rules/use-primitives/transformers/index.js +2 -0
  27. package/dist/es2019/rules/use-primitives/transformers/jsx-element-to-box.js +16 -0
  28. package/dist/es2019/rules/use-primitives/utils/contains-only-supported-attrs.js +13 -0
  29. package/dist/es2019/rules/use-primitives/utils/convert-ast-object-expression-to-js-object.js +26 -0
  30. package/dist/es2019/rules/use-primitives/utils/get-attribute-value-identifier.js +32 -0
  31. package/dist/es2019/rules/use-primitives/utils/get-function-argument-at-pos.js +4 -0
  32. package/dist/es2019/rules/use-primitives/utils/get-jsx-attribute-by-name.js +10 -0
  33. package/dist/es2019/rules/use-primitives/utils/get-variable-definition-value.js +23 -0
  34. package/dist/es2019/rules/use-primitives/utils/get-variable-usage-count.js +15 -0
  35. package/dist/es2019/rules/use-primitives/utils/index.js +12 -0
  36. package/dist/es2019/rules/use-primitives/utils/is-function-named.js +13 -0
  37. package/dist/es2019/rules/use-primitives/utils/is-valid-tag-name.js +7 -0
  38. package/dist/es2019/rules/use-primitives/utils/update-jsx-attribute-by-name.js +26 -0
  39. package/dist/es2019/rules/use-primitives/utils/update-jsx-element-name.js +12 -0
  40. package/dist/es2019/rules/use-primitives/utils/upsert-import-declaration.js +76 -0
  41. package/dist/esm/rules/prefer-primitives/index.js +1 -2
  42. package/dist/esm/rules/prefer-primitives/utils.js +13 -2
  43. package/dist/esm/rules/use-primitives/index.js +91 -60
  44. package/dist/esm/rules/use-primitives/transformers/css-to-xcss.js +88 -0
  45. package/dist/esm/rules/use-primitives/transformers/index.js +2 -0
  46. package/dist/esm/rules/use-primitives/transformers/jsx-element-to-box.js +19 -0
  47. package/dist/esm/rules/use-primitives/utils/contains-only-supported-attrs.js +13 -0
  48. package/dist/esm/rules/use-primitives/utils/convert-ast-object-expression-to-js-object.js +26 -0
  49. package/dist/esm/rules/use-primitives/utils/get-attribute-value-identifier.js +32 -0
  50. package/dist/esm/rules/use-primitives/utils/get-function-argument-at-pos.js +4 -0
  51. package/dist/esm/rules/use-primitives/utils/get-jsx-attribute-by-name.js +10 -0
  52. package/dist/esm/rules/use-primitives/utils/get-variable-definition-value.js +23 -0
  53. package/dist/esm/rules/use-primitives/utils/get-variable-usage-count.js +15 -0
  54. package/dist/esm/rules/use-primitives/utils/index.js +12 -0
  55. package/dist/esm/rules/use-primitives/utils/is-function-named.js +13 -0
  56. package/dist/esm/rules/use-primitives/utils/is-valid-tag-name.js +7 -0
  57. package/dist/esm/rules/use-primitives/utils/update-jsx-attribute-by-name.js +24 -0
  58. package/dist/esm/rules/use-primitives/utils/update-jsx-element-name.js +10 -0
  59. package/dist/esm/rules/use-primitives/utils/upsert-import-declaration.js +75 -0
  60. package/dist/types/rules/prefer-primitives/utils.d.ts +2 -1
  61. package/dist/types/rules/use-primitives/transformers/css-to-xcss.d.ts +9 -0
  62. package/dist/types/rules/use-primitives/transformers/index.d.ts +2 -0
  63. package/dist/types/rules/use-primitives/transformers/jsx-element-to-box.d.ts +3 -0
  64. package/dist/types/rules/use-primitives/utils/contains-only-supported-attrs.d.ts +2 -0
  65. package/dist/types/rules/use-primitives/utils/convert-ast-object-expression-to-js-object.d.ts +9 -0
  66. package/dist/types/rules/use-primitives/utils/get-attribute-value-identifier.d.ts +14 -0
  67. package/dist/types/rules/use-primitives/utils/get-function-argument-at-pos.d.ts +2 -0
  68. package/dist/types/rules/use-primitives/utils/get-jsx-attribute-by-name.d.ts +2 -0
  69. package/dist/types/rules/use-primitives/utils/get-variable-definition-value.d.ts +2 -0
  70. package/dist/types/rules/use-primitives/utils/get-variable-usage-count.d.ts +6 -0
  71. package/dist/types/rules/use-primitives/utils/index.d.ts +12 -0
  72. package/dist/types/rules/use-primitives/utils/is-function-named.d.ts +2 -0
  73. package/dist/types/rules/use-primitives/utils/is-valid-tag-name.d.ts +2 -0
  74. package/dist/types/rules/use-primitives/utils/update-jsx-attribute-by-name.d.ts +3 -0
  75. package/dist/types/rules/use-primitives/utils/update-jsx-element-name.d.ts +3 -0
  76. package/dist/types/rules/use-primitives/utils/upsert-import-declaration.d.ts +11 -0
  77. package/dist/types-ts4.5/rules/prefer-primitives/utils.d.ts +2 -1
  78. package/dist/types-ts4.5/rules/use-primitives/transformers/css-to-xcss.d.ts +9 -0
  79. package/dist/types-ts4.5/rules/use-primitives/transformers/index.d.ts +2 -0
  80. package/dist/types-ts4.5/rules/use-primitives/transformers/jsx-element-to-box.d.ts +3 -0
  81. package/dist/types-ts4.5/rules/use-primitives/utils/contains-only-supported-attrs.d.ts +2 -0
  82. package/dist/types-ts4.5/rules/use-primitives/utils/convert-ast-object-expression-to-js-object.d.ts +9 -0
  83. package/dist/types-ts4.5/rules/use-primitives/utils/get-attribute-value-identifier.d.ts +14 -0
  84. package/dist/types-ts4.5/rules/use-primitives/utils/get-function-argument-at-pos.d.ts +2 -0
  85. package/dist/types-ts4.5/rules/use-primitives/utils/get-jsx-attribute-by-name.d.ts +2 -0
  86. package/dist/types-ts4.5/rules/use-primitives/utils/get-variable-definition-value.d.ts +2 -0
  87. package/dist/types-ts4.5/rules/use-primitives/utils/get-variable-usage-count.d.ts +6 -0
  88. package/dist/types-ts4.5/rules/use-primitives/utils/index.d.ts +12 -0
  89. package/dist/types-ts4.5/rules/use-primitives/utils/is-function-named.d.ts +2 -0
  90. package/dist/types-ts4.5/rules/use-primitives/utils/is-valid-tag-name.d.ts +2 -0
  91. package/dist/types-ts4.5/rules/use-primitives/utils/update-jsx-attribute-by-name.d.ts +3 -0
  92. package/dist/types-ts4.5/rules/use-primitives/utils/update-jsx-element-name.d.ts +3 -0
  93. package/dist/types-ts4.5/rules/use-primitives/utils/upsert-import-declaration.d.ts +11 -0
  94. package/package.json +3 -1
  95. package/dist/cjs/rules/use-primitives/utils.js +0 -364
  96. package/dist/es2019/rules/use-primitives/utils.js +0 -353
  97. package/dist/esm/rules/use-primitives/utils.js +0 -359
  98. package/dist/types/rules/use-primitives/utils.d.ts +0 -13
  99. package/dist/types-ts4.5/rules/use-primitives/utils.d.ts +0 -13
@@ -1,353 +0,0 @@
1
- // eslint-disable-next-line import/no-extraneous-dependencies
2
-
3
- import { getIdentifierInParentScope, hasImportDeclaration, insertImportDeclaration, insertImportSpecifier, isNodeOfType, jsxAttribute, jsxIdentifier, jsxOpeningElement, literal } from 'eslint-codemod-utils';
4
- export const shouldSuggestBox = (node
5
- // scope: Scope.Scope,
6
- ) => {
7
- if (!node) {
8
- return false;
9
- }
10
- if (!isValidPrimitiveElement(node)) {
11
- return false;
12
- }
13
-
14
- /**
15
- * Check for empty elements: `<div></div>` || `<span></span>`
16
- */
17
- if (node.children.length === 0) {
18
- return true;
19
- }
20
-
21
- /**
22
- * Check for elements containing only whitespace. e.g. `<div> </div>` || `<span> </span>`
23
- */
24
- if (containsOnlyWhitespace(node)) {
25
- return true;
26
- }
27
-
28
- /**
29
- * If an element contains more than one JSX child, then we don't want to convert it.
30
- */
31
- const JSXChildren = getChildrenByType(node, ['JSXElement']);
32
- if (JSXChildren.length > 1) {
33
- return false;
34
- }
35
-
36
- /**
37
- * Check for things like:
38
- * ```
39
- * <div>
40
- * <h2>heading</h2>
41
- * subheading <= rejected because of standalone piece of text
42
- * </div>
43
- * ```
44
- */
45
- const nonWhiteSpaceTextChildren = getChildrenByType(node, ['JSXText']).filter(child => !isWhiteSpace(child.value));
46
- if (nonWhiteSpaceTextChildren.length > 0) {
47
- return false;
48
- }
49
-
50
- // Possible since we now know the div only has one child
51
- // const singleChild = node.children[0];
52
-
53
- // // let's narrow down the type inside of the expression
54
- // if (isNodeOfType(singleChild, 'JSXExpressionContainer')) {
55
- // const expression = singleChild.expression;
56
-
57
- // // If an Identifier that is just a string then should be a <Text>, not <Box>
58
- // const identifier =
59
- // isNodeOfType(expression, 'Identifier') &&
60
- // getIdentifierInParentScope(scope, expression.name);
61
-
62
- // if (
63
- // identifier &&
64
- // (isVariableAnnotatedWithType(identifier, 'StringTypeAnnotation') ||
65
- // isVariableAnnotatedWithType(identifier, 'TSStringKeyword'))
66
- // ) {
67
- // return false;
68
- // }
69
- // }
70
-
71
- return true;
72
- };
73
- export const shouldSuggestInline = (node, context) => {
74
- if (!node) {
75
- return false;
76
- }
77
- if (!isValidPrimitiveElement(node)) {
78
- return false;
79
- }
80
- const cssStyleObject = getCSSPropStyleObject(node, context);
81
- if (!cssStyleObject) {
82
- return false;
83
- }
84
- if (!hasInlineCompatibleStyles(cssStyleObject)) {
85
- return false;
86
- }
87
- const JSXChildren = getChildrenByType(node, ['JSXElement']);
88
- if (JSXChildren.length < 2) {
89
- return false;
90
- }
91
- return true;
92
- };
93
- export const shouldSuggestStack = (node, context) => {
94
- if (!node) {
95
- return false;
96
- }
97
- if (!isValidPrimitiveElement(node)) {
98
- return false;
99
- }
100
- const cssStyleObject = getCSSPropStyleObject(node, context);
101
- if (!cssStyleObject) {
102
- return false;
103
- }
104
- if (!hasStackCompatibleStyles(cssStyleObject)) {
105
- return false;
106
- }
107
- const JSXChildren = getChildrenByType(node, ['JSXElement']);
108
- if (JSXChildren.length < 2) {
109
- return false;
110
- }
111
- return true;
112
- };
113
- export const shouldSuggestText = (node, scope) => {
114
- if (!node) {
115
- return true;
116
- }
117
-
118
- // node doesn't have children then it should be a <Box>
119
- if (!node.children || node.children.length === 0) {
120
- return false;
121
- }
122
- const containsOnlyText = node.children.filter(child => {
123
- return isNodeOfType(child, 'Literal') || isNodeOfType(child, 'JSXText');
124
- });
125
- if (containsOnlyText) {
126
- return true;
127
- }
128
-
129
- // if the element contains strictly a single child... let's see what we can discover
130
- if (node.children.length === 1) {
131
- const singleChild = node.children[0];
132
-
133
- // let's narrow down the type inside of the expression
134
- if (isNodeOfType(singleChild, 'JSXExpressionContainer')) {
135
- const expression = singleChild.expression;
136
-
137
- // A direct Literal is another case for only <Text>
138
- if (isNodeOfType(expression, 'Literal')) {
139
- return true;
140
- }
141
-
142
- // If an Identifier then we want to confirm it's just string to suggest only <Text>
143
- const identifier = isNodeOfType(expression, 'Identifier') && getIdentifierInParentScope(scope, expression.name);
144
- if (identifier && (isVariableAnnotatedWithType(identifier, 'StringTypeAnnotation') || isVariableAnnotatedWithType(identifier, 'TSStringKeyword'))) {
145
- return true;
146
- }
147
- }
148
- }
149
- return true;
150
- };
151
- export const primitiveFixer = (node, nodeName, context) => {
152
- return fixer => {
153
- if (!isNodeOfType(node, 'JSXOpeningElement')) {
154
- return [];
155
- }
156
- const parent = node.parent;
157
- if (!isNodeOfType(parent, 'JSXElement')) {
158
- return [];
159
- }
160
- const fixes = [];
161
- const body = context.getSourceCode().ast.body;
162
- const imports = body.filter(node => isNodeOfType(node, 'ImportDeclaration'));
163
- let primitivesNode = imports.find(node => hasImportDeclaration(node, '@atlaskit/primitives'));
164
- if (!primitivesNode) {
165
- fixes.push(fixer.insertTextBefore(body[0], `${insertImportDeclaration('@atlaskit/primitives', [nodeName])};\n`));
166
- } else {
167
- fixes.push(fixer.replaceText(primitivesNode, `${insertImportSpecifier(primitivesNode, nodeName)};\n`));
168
- }
169
- const {
170
- closingElement
171
- } = parent;
172
- const jsxId = jsxIdentifier(nodeName);
173
- const attributes = [];
174
-
175
- // Box defaults to div. We only need to add an as prop if it's not a div
176
- if (node.name.name !== 'div' && nodeName === 'Box') {
177
- const asProp = jsxAttribute({
178
- name: jsxIdentifier('as'),
179
- value: literal({
180
- value: `${node.name.name}`,
181
- raw: `\"${node.name.name}\"`
182
- })
183
- });
184
- attributes.push(asProp);
185
- }
186
- const hasStylingProps = getJSXAttributeByName(node, 'style') || getJSXAttributeByName(node, 'css') || getJSXAttributeByName(node, 'class') || getJSXAttributeByName(node, 'className');
187
- if (hasStylingProps) {
188
- fixes.push(fixer.insertTextBefore(node, '// TODO: Manually convert styling into props\n'));
189
- }
190
- const candidateAttributes = node.attributes.map(attr => {
191
- // pull out any named attributes we can re-map
192
- if (isNodeOfType(attr, 'JSXAttribute')) {
193
- if (attr.name.name === 'data-testid') {
194
- return jsxAttribute({
195
- ...attr,
196
- name: jsxIdentifier('testId')
197
- });
198
- }
199
- }
200
- return attr;
201
- }).filter(Boolean);
202
- fixes.push(fixer.replaceText(node, jsxOpeningElement({
203
- ...node,
204
- name: jsxId,
205
- attributes: candidateAttributes.concat(attributes)
206
- }).toString()));
207
- if (closingElement && closingElement.name) {
208
- fixes.push(fixer.replaceText(closingElement.name, `${jsxId}`));
209
- }
210
- return fixes;
211
- };
212
- };
213
- const isVariableAnnotatedWithType = (identifier, typeAnnotation) => {
214
- var _identifiers$, _identifiers$$typeAnn;
215
- if (!identifier || identifier.identifiers.length === 0) {
216
- return false;
217
- }
218
- const typeAnnotationObject = (_identifiers$ = identifier.identifiers[0]) === null || _identifiers$ === void 0 ? void 0 : (_identifiers$$typeAnn = _identifiers$.typeAnnotation) === null || _identifiers$$typeAnn === void 0 ? void 0 : _identifiers$$typeAnn.typeAnnotation;
219
-
220
- // variable declaration lacks type definitions
221
- if (!typeAnnotationObject) {
222
- return false;
223
- }
224
-
225
- // single type annotation
226
- if (typeAnnotationObject.type === typeAnnotation) {
227
- return true;
228
- }
229
- return false;
230
- };
231
- const getJSXAttributeByName = (node, attrName) => {
232
- return node.attributes.find(attr => {
233
- // Ignore JSXSpreadAttributes
234
- if (!isNodeOfType(attr, 'JSXAttribute')) {
235
- return false;
236
- }
237
- return attr.name.name === attrName;
238
- });
239
- };
240
- export const isWhiteSpace = value => value.trim() === '';
241
- function containsOnlyWhitespace(node) {
242
- return node.children.every(child => {
243
- if (!isNodeOfType(child, 'JSXText')) {
244
- return false;
245
- }
246
- return isWhiteSpace(child.value);
247
- });
248
- }
249
- export const getChildrenByType = (node, types) => {
250
- return node.children.filter(child => {
251
- return types.find(type => isNodeOfType(child, type));
252
- });
253
- };
254
-
255
- // FIXME: This not correctly typed
256
-
257
- const getCSSPropStyleObject = (node, context) => {
258
- const cssAttr = getJSXAttributeByName(node.openingElement, 'css');
259
- const styleObj = {};
260
- if (cssAttr && cssAttr.value) {
261
- const scope = context.getScope();
262
- const {
263
- expression
264
- } = cssAttr.value;
265
- const variableDeclarator = getIdentifierInParentScope(scope, expression.name);
266
- const defNode = variableDeclarator === null || variableDeclarator === void 0 ? void 0 : variableDeclarator.defs[0];
267
- if (!defNode) {
268
- return undefined;
269
- }
270
-
271
- // check if the variable declaration has a call expression init
272
- // eg we're looking for css() type inits
273
- if (!isNodeOfType(defNode.node, 'VariableDeclarator') && !isNodeOfType(defNode.node.init, 'CallExpression')) {
274
- return undefined;
275
- }
276
- const {
277
- init
278
- } = defNode.node;
279
-
280
- // make the sure the init is called 'css'
281
- if (!(isNodeOfType(init.callee, 'Identifier') && init.callee.name === 'css')) {
282
- return undefined;
283
- }
284
- const styles = init.arguments[0];
285
- if (!styles) {
286
- return undefined;
287
- }
288
-
289
- // make sure the styles are object styles (they should be but let's just double check)
290
- if (!isNodeOfType(styles, 'ObjectExpression')) {
291
- return undefined;
292
- }
293
- styles.properties.forEach(prop => {
294
- if (!isNodeOfType(prop, 'Property')) {
295
- return;
296
- }
297
- if (!isNodeOfType(prop.key, 'Identifier')) {
298
- return;
299
- }
300
- if (!isNodeOfType(prop.value, 'Literal')) {
301
- return;
302
- }
303
- styleObj[prop.key.name] = prop.value.value;
304
- });
305
- }
306
- return styleObj;
307
- };
308
- export const validPrimitiveElements = new Set(['div', 'span', 'article', 'aside', 'dialog', 'footer', 'header', 'li', 'main', 'nav', 'ol', 'section', 'ul']);
309
- export const isValidPrimitiveElement = node => {
310
- return validPrimitiveElements.has(node.openingElement.name.name);
311
- };
312
- const hasInlineCompatibleStyles = cssStyleObject => {
313
- if (!['flex', 'inline-flex'].includes(cssStyleObject['display'])) {
314
- return false;
315
- }
316
-
317
- /**
318
- * Default `flexDirection` is `'row'`, so we can recommend an Inline if
319
- * it's `undefined`, or if it's explicitly `'row'` or `'row-reverse'`.
320
- *
321
- * Note: Currently we are including `'row-reverse'` even though Inline doesn't support it
322
- * as an attempt to educate makers about accessibility concerns. *
323
- */
324
- const flexDirection = cssStyleObject['flex-direction'] || cssStyleObject['flexDirection'];
325
- const validFlexDirectionValue = ['row', 'row-reverse', undefined].includes(flexDirection);
326
-
327
- /**
328
- * Note: Currently we are including `'wrap-reverse'` even though Inline doesn't support it
329
- * as an attempt to educate makers about accessibility concerns.
330
- */
331
- const flexWrap = cssStyleObject['flex-wrap'] || cssStyleObject['flexWrap'];
332
- const validFlexWrapValue = ['wrap', 'wrap-reverse'].includes(flexWrap);
333
- if (validFlexDirectionValue || validFlexWrapValue) {
334
- return true;
335
- }
336
- return false;
337
- };
338
- const hasStackCompatibleStyles = cssStyleObject => {
339
- if (!['flex', 'inline-flex'].includes(cssStyleObject['display'])) {
340
- return false;
341
- }
342
-
343
- /**
344
- * Note: Currently we are including `'column-reverse'` even though Stack doesn't support it
345
- * as an attempt to educate makers about accessibility concerns.
346
- */
347
- const flexDirection = cssStyleObject['flex-direction'] || cssStyleObject['flexDirection'];
348
- const validFlexDirectionValue = ['column', 'column-reverse'].includes(flexDirection);
349
- if (!validFlexDirectionValue) {
350
- return false;
351
- }
352
- return true;
353
- };