@atlaskit/eslint-plugin-design-system 8.25.2 → 8.27.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 (80) hide show
  1. package/CHANGELOG.md +42 -0
  2. package/README.md +4 -0
  3. package/constellation/index/usage.mdx +402 -6
  4. package/dist/cjs/presets/all.codegen.js +5 -1
  5. package/dist/cjs/presets/recommended.codegen.js +5 -1
  6. package/dist/cjs/rules/consistent-css-prop-usage/index.js +254 -32
  7. package/dist/cjs/rules/index.codegen.js +9 -1
  8. package/dist/cjs/rules/no-empty-styled-expression/index.js +75 -0
  9. package/dist/cjs/rules/no-exported-css/index.js +37 -0
  10. package/dist/cjs/rules/no-exported-keyframes/index.js +37 -0
  11. package/dist/cjs/rules/no-invalid-css-map/index.js +102 -0
  12. package/dist/cjs/rules/no-invalid-css-map/utils.js +193 -0
  13. package/dist/cjs/rules/utils/create-no-exported-rule/check-if-supported-export.js +158 -0
  14. package/dist/cjs/rules/utils/create-no-exported-rule/is-styled-component.js +80 -0
  15. package/dist/cjs/rules/utils/create-no-exported-rule/main.js +66 -0
  16. package/dist/cjs/rules/utils/get-first-supported-import.js +28 -0
  17. package/dist/cjs/rules/utils/is-supported-import.js +53 -16
  18. package/dist/es2019/presets/all.codegen.js +5 -1
  19. package/dist/es2019/presets/recommended.codegen.js +5 -1
  20. package/dist/es2019/rules/consistent-css-prop-usage/index.js +251 -33
  21. package/dist/es2019/rules/index.codegen.js +9 -1
  22. package/dist/es2019/rules/no-empty-styled-expression/index.js +65 -0
  23. package/dist/es2019/rules/no-exported-css/index.js +31 -0
  24. package/dist/es2019/rules/no-exported-keyframes/index.js +31 -0
  25. package/dist/es2019/rules/no-invalid-css-map/index.js +95 -0
  26. package/dist/es2019/rules/no-invalid-css-map/utils.js +134 -0
  27. package/dist/es2019/rules/utils/create-no-exported-rule/check-if-supported-export.js +142 -0
  28. package/dist/es2019/rules/utils/create-no-exported-rule/is-styled-component.js +70 -0
  29. package/dist/es2019/rules/utils/create-no-exported-rule/main.js +59 -0
  30. package/dist/es2019/rules/utils/get-first-supported-import.js +22 -0
  31. package/dist/es2019/rules/utils/is-supported-import.js +50 -15
  32. package/dist/esm/presets/all.codegen.js +5 -1
  33. package/dist/esm/presets/recommended.codegen.js +5 -1
  34. package/dist/esm/rules/consistent-css-prop-usage/index.js +255 -33
  35. package/dist/esm/rules/index.codegen.js +9 -1
  36. package/dist/esm/rules/no-empty-styled-expression/index.js +68 -0
  37. package/dist/esm/rules/no-exported-css/index.js +31 -0
  38. package/dist/esm/rules/no-exported-keyframes/index.js +31 -0
  39. package/dist/esm/rules/no-invalid-css-map/index.js +96 -0
  40. package/dist/esm/rules/no-invalid-css-map/utils.js +186 -0
  41. package/dist/esm/rules/utils/create-no-exported-rule/check-if-supported-export.js +151 -0
  42. package/dist/esm/rules/utils/create-no-exported-rule/is-styled-component.js +74 -0
  43. package/dist/esm/rules/utils/create-no-exported-rule/main.js +60 -0
  44. package/dist/esm/rules/utils/get-first-supported-import.js +22 -0
  45. package/dist/esm/rules/utils/is-supported-import.js +51 -15
  46. package/dist/types/index.codegen.d.ts +8 -0
  47. package/dist/types/presets/all.codegen.d.ts +5 -1
  48. package/dist/types/presets/recommended.codegen.d.ts +5 -1
  49. package/dist/types/rules/consistent-css-prop-usage/types.d.ts +7 -2
  50. package/dist/types/rules/index.codegen.d.ts +4 -0
  51. package/dist/types/rules/no-empty-styled-expression/index.d.ts +3 -0
  52. package/dist/types/rules/no-exported-css/index.d.ts +3 -0
  53. package/dist/types/rules/no-exported-keyframes/index.d.ts +3 -0
  54. package/dist/types/rules/no-invalid-css-map/index.d.ts +3 -0
  55. package/dist/types/rules/no-invalid-css-map/utils.d.ts +14 -0
  56. package/dist/types/rules/use-primitives/utils/update-jsx-attribute-by-name.d.ts +1 -1
  57. package/dist/types/rules/utils/create-no-exported-rule/check-if-supported-export.d.ts +15 -0
  58. package/dist/types/rules/utils/create-no-exported-rule/is-styled-component.d.ts +14 -0
  59. package/dist/types/rules/utils/create-no-exported-rule/main.d.ts +19 -0
  60. package/dist/types/rules/utils/create-rule.d.ts +1 -1
  61. package/dist/types/rules/utils/get-first-supported-import.d.ts +17 -0
  62. package/dist/types/rules/utils/is-supported-import.d.ts +26 -8
  63. package/dist/types-ts4.5/index.codegen.d.ts +8 -0
  64. package/dist/types-ts4.5/presets/all.codegen.d.ts +5 -1
  65. package/dist/types-ts4.5/presets/recommended.codegen.d.ts +5 -1
  66. package/dist/types-ts4.5/rules/consistent-css-prop-usage/types.d.ts +7 -2
  67. package/dist/types-ts4.5/rules/index.codegen.d.ts +4 -0
  68. package/dist/types-ts4.5/rules/no-empty-styled-expression/index.d.ts +3 -0
  69. package/dist/types-ts4.5/rules/no-exported-css/index.d.ts +3 -0
  70. package/dist/types-ts4.5/rules/no-exported-keyframes/index.d.ts +3 -0
  71. package/dist/types-ts4.5/rules/no-invalid-css-map/index.d.ts +3 -0
  72. package/dist/types-ts4.5/rules/no-invalid-css-map/utils.d.ts +14 -0
  73. package/dist/types-ts4.5/rules/use-primitives/utils/update-jsx-attribute-by-name.d.ts +1 -1
  74. package/dist/types-ts4.5/rules/utils/create-no-exported-rule/check-if-supported-export.d.ts +15 -0
  75. package/dist/types-ts4.5/rules/utils/create-no-exported-rule/is-styled-component.d.ts +14 -0
  76. package/dist/types-ts4.5/rules/utils/create-no-exported-rule/main.d.ts +19 -0
  77. package/dist/types-ts4.5/rules/utils/create-rule.d.ts +1 -1
  78. package/dist/types-ts4.5/rules/utils/get-first-supported-import.d.ts +17 -0
  79. package/dist/types-ts4.5/rules/utils/is-supported-import.d.ts +26 -8
  80. package/package.json +3 -1
@@ -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::5026ba2cb55b3c1bcacbfe7fb7728a6c>>
3
+ * @codegen <<SignedSource::914085544778f4543f43e3e30d0982e0>>
4
4
  * @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
5
5
  */
6
6
  export default {
@@ -16,6 +16,10 @@ export default {
16
16
  '@atlaskit/design-system/no-deprecated-apis': 'error',
17
17
  '@atlaskit/design-system/no-deprecated-design-token-usage': 'warn',
18
18
  '@atlaskit/design-system/no-deprecated-imports': 'error',
19
+ '@atlaskit/design-system/no-empty-styled-expression': 'warn',
20
+ '@atlaskit/design-system/no-exported-css': 'warn',
21
+ '@atlaskit/design-system/no-exported-keyframes': 'warn',
22
+ '@atlaskit/design-system/no-invalid-css-map': 'error',
19
23
  '@atlaskit/design-system/no-margin': 'warn',
20
24
  '@atlaskit/design-system/no-nested-styles': 'error',
21
25
  '@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::839224bfab98c1ddf6018dec5320968e>>
3
+ * @codegen <<SignedSource::577269c832952ce359cde6a50f26f4e0>>
4
4
  * @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
5
5
  */
6
6
  export default {
@@ -14,6 +14,10 @@ export default {
14
14
  '@atlaskit/design-system/no-deprecated-apis': 'error',
15
15
  '@atlaskit/design-system/no-deprecated-design-token-usage': 'warn',
16
16
  '@atlaskit/design-system/no-deprecated-imports': 'error',
17
+ '@atlaskit/design-system/no-empty-styled-expression': 'warn',
18
+ '@atlaskit/design-system/no-exported-css': 'warn',
19
+ '@atlaskit/design-system/no-exported-keyframes': 'warn',
20
+ '@atlaskit/design-system/no-invalid-css-map': 'error',
17
21
  '@atlaskit/design-system/no-nested-styles': 'error',
18
22
  '@atlaskit/design-system/no-unsafe-design-token-usage': 'error',
19
23
  '@atlaskit/design-system/no-unsafe-style-overrides': 'warn',
@@ -3,20 +3,27 @@ import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
3
3
  import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray";
4
4
  // eslint-disable-next-line import/no-extraneous-dependencies
5
5
 
6
- import { getIdentifierInParentScope, isNodeOfType } from 'eslint-codemod-utils';
6
+ import { getIdentifierInParentScope, insertAtStartOfFile, insertImportDeclaration, isNodeOfType } from 'eslint-codemod-utils';
7
+ import estraverse from 'estraverse';
7
8
  import assign from 'lodash/assign';
9
+ import { Import } from '../../ast-nodes';
8
10
  import { createLintRule } from '../utils/create-rule';
9
- // File-level tracking of styles hoisted from the cssOnTopOfModule/cssAtBottomOfModule fixers
11
+ import { getFirstSupportedImport } from '../utils/get-first-supported-import';
12
+ import { getModuleOfIdentifier } from '../utils/get-import-node-by-source';
13
+ import { CSS_IN_JS_IMPORTS } from '../utils/is-supported-import';
14
+ // File-level tracking of styles hoisted from the cssAtTopOfModule/cssAtBottomOfModule fixers
10
15
  var hoistedCss = [];
16
+ var isDOMElementName = function isDOMElementName(elementName) {
17
+ return elementName.charAt(0) !== elementName.charAt(0).toUpperCase() && elementName.charAt(0) === elementName.charAt(0).toLowerCase();
18
+ };
11
19
  function isCssCallExpression(node, cssFunctions) {
12
20
  cssFunctions = [].concat(_toConsumableArray(cssFunctions), ['cssMap']);
13
21
  return !!(isNodeOfType(node, 'CallExpression') && node.callee && node.callee.type === 'Identifier' && cssFunctions.includes(node.callee.name) && node.arguments.length && node.arguments[0].type === 'ObjectExpression');
14
22
  }
15
23
  function findSpreadProperties(node) {
16
- // @ts-ignore
17
24
  return node.properties.filter(function (property) {
18
25
  return property.type === 'SpreadElement' ||
19
- // @ts-ignore
26
+ // @ts-expect-error
20
27
  property.type === 'ExperimentalSpreadProperty';
21
28
  });
22
29
  }
@@ -27,9 +34,8 @@ var getProgramNode = function getProgramNode(expression) {
27
34
  return expression.parent;
28
35
  };
29
36
 
30
- // TODO: This can be optimised by implementing a fixer at the very end (Program:exit) and handling all validations at once
31
37
  /**
32
- * Generates the declarator string when fixing the cssOnTopOfModule/cssAtBottomOfModule cases.
38
+ * Generates the declarator string when fixing the cssAtTopOfModule/cssAtBottomOfModule cases.
33
39
  * When `styles` already exists, `styles_1, styles_2, ..., styles_X` are incrementally created for each unhoisted style.
34
40
  * The generated `styles` varibale declaration names must be manually modified to be more informative at the discretion of owning teams.
35
41
  */
@@ -61,7 +67,7 @@ var getDeclaratorString = function getDeclaratorString(context) {
61
67
  hoistedCss = [].concat(_toConsumableArray(hoistedCss), ["".concat(declaratorName).concat(count)]);
62
68
  return "".concat(declaratorName).concat(count);
63
69
  };
64
- function analyzeIdentifier(context, sourceIdentifier, configuration) {
70
+ function analyzeIdentifier(context, sourceIdentifier, configuration, cssAttributeName) {
65
71
  var _getIdentifierInParen, _getIdentifierInParen2;
66
72
  var scope = context.getScope();
67
73
  var _ref = (_getIdentifierInParen = (_getIdentifierInParen2 = getIdentifierInParentScope(scope, sourceIdentifier.name)) === null || _getIdentifierInParen2 === void 0 ? void 0 : _getIdentifierInParen2.identifiers) !== null && _getIdentifierInParen !== void 0 ? _getIdentifierInParen : [],
@@ -83,8 +89,11 @@ function analyzeIdentifier(context, sourceIdentifier, configuration) {
83
89
  // When variable is declared inside the component
84
90
  context.report({
85
91
  node: sourceIdentifier,
86
- messageId: configuration.stylesPlacement === 'bottom' ? 'cssAtBottomOfModule' : 'cssOnTopOfModule',
92
+ messageId: configuration.stylesPlacement === 'bottom' ? 'cssAtBottomOfModule' : 'cssAtTopOfModule',
87
93
  fix: function fix(fixer) {
94
+ if (configuration.fixNamesOnly) {
95
+ return [];
96
+ }
88
97
  return fixCssNotInModuleScope(fixer, context, configuration, identifier);
89
98
  }
90
99
  });
@@ -92,10 +101,30 @@ function analyzeIdentifier(context, sourceIdentifier, configuration) {
92
101
  }
93
102
  if (identifier.parent && identifier.parent.init && !isCssCallExpression(identifier.parent.init, configuration.cssFunctions)) {
94
103
  // When variable value is not of type css({})
95
- context.report({
96
- node: identifier,
97
- messageId: 'cssObjectTypeOnly'
98
- });
104
+ var value = identifier.parent.init;
105
+ if (!value) {
106
+ return;
107
+ }
108
+ var valueExpression =
109
+ // @ts-expect-error remove once eslint types are switched to @typescript-eslint
110
+ value.type === 'TSAsExpression' ? value.expression : value;
111
+ if (['ObjectExpression', 'TemplateLiteral'].includes(valueExpression.type)) {
112
+ context.report({
113
+ node: identifier,
114
+ messageId: 'cssObjectTypeOnly',
115
+ fix: function fix(fixer) {
116
+ if (configuration.fixNamesOnly) {
117
+ return [];
118
+ }
119
+ return addCssFunctionCall(fixer, context, identifier.parent, configuration, cssAttributeName);
120
+ }
121
+ });
122
+ } else {
123
+ context.report({
124
+ node: identifier,
125
+ messageId: 'cssObjectTypeOnly'
126
+ });
127
+ }
99
128
  return;
100
129
  }
101
130
  var spreadProperties = isNodeOfType(identifier.parent.init, 'CallExpression') && findSpreadProperties(identifier.parent.init.arguments[0]);
@@ -111,12 +140,133 @@ function analyzeIdentifier(context, sourceIdentifier, configuration) {
111
140
  }
112
141
 
113
142
  /**
114
- * Fixer for the cssOnTopOfModule/cssAtBottomOfModule violation cases.
143
+ * Returns a fixer that adds `import { css } from 'import-source'` or
144
+ * `import { xcss } from 'import-source'` to the start of the file, depending
145
+ * on the value of cssAttributeName and importSource.
146
+ */
147
+ var addImportSource = function addImportSource(context, fixer, configuration, cssAttributeName) {
148
+ var importSource = cssAttributeName === 'xcss' ? configuration.xcssImportSource : configuration.cssImportSource;
149
+
150
+ // Add the `import { css } from 'my-css-in-js-library';` statement
151
+ var packageImport = getFirstSupportedImport(context, [importSource]);
152
+ if (packageImport) {
153
+ var addCssImport = Import.insertNamedSpecifiers(packageImport, [cssAttributeName], fixer);
154
+ if (addCssImport) {
155
+ return addCssImport;
156
+ }
157
+ } else {
158
+ return insertAtStartOfFile(fixer, "".concat(insertImportDeclaration(importSource, [cssAttributeName]), ";\n"));
159
+ }
160
+ };
161
+
162
+ /**
163
+ * Returns a list of fixes that:
164
+ * - add the `css` or `xcss` function call around the current node.
165
+ * - add an import statement for the package from which `css` is imported
166
+ */
167
+ var addCssFunctionCall = function addCssFunctionCall(fixer, context, node, configuration, cssAttributeName) {
168
+ var fixes = [];
169
+ var sourceCode = context.getSourceCode();
170
+ if (node.type !== 'VariableDeclarator' || !node.init || !cssAttributeName) {
171
+ return [];
172
+ }
173
+ var compiledImportFix = addImportSource(context, fixer, configuration, cssAttributeName);
174
+ if (compiledImportFix) {
175
+ fixes.push(compiledImportFix);
176
+ }
177
+ var init = node.init;
178
+ var initString = sourceCode.getText(init);
179
+ if (node.init.type === 'TemplateLiteral') {
180
+ fixes.push(fixer.replaceText(init, "".concat(cssAttributeName).concat(initString)));
181
+ } else {
182
+ fixes.push(fixer.replaceText(init, "".concat(cssAttributeName, "(").concat(initString, ")")));
183
+ }
184
+ return fixes;
185
+ };
186
+
187
+ /**
188
+ * Check if the expression has or potentially has a local variable
189
+ * (as opposed to an imported one), erring on the side ot "yes"
190
+ * when an expression is too complicated to analyse.
191
+ *
192
+ * This is useful because expressions containing local variables
193
+ * cannot be easily hoisted, whereas this is not a problem with imported
194
+ * variables.
195
+ *
196
+ * @param context Context of the rule.
197
+ * @param node Any node that is potentially hoistable.
198
+ * @returns Whether the node potentially has a local variable (and thus is not safe to hoist).
199
+ */
200
+ var potentiallyHasLocalVariable = function potentiallyHasLocalVariable(context, node) {
201
+ var hasPotentiallyLocalVariable = false;
202
+ var isImportedVariable = function isImportedVariable(identifier) {
203
+ return !!getModuleOfIdentifier(context.getSourceCode(), identifier);
204
+ };
205
+ estraverse.traverse(node, {
206
+ enter: function enter(node, _parent) {
207
+ if (isNodeOfType(node, 'SpreadElement') ||
208
+ // @ts-expect-error remove once we can be sure that no parser interprets
209
+ // the spread operator as ExperimentalSpreadProperty anymore
210
+ isNodeOfType(node, 'ExperimentalSpreadProperty')) {
211
+ // Spread elements could contain anything... so we don't bother.
212
+ //
213
+ // e.g. <div css={css({ ...(!height && { visibility: 'hidden' })} />
214
+ hasPotentiallyLocalVariable = true;
215
+ this.break();
216
+ }
217
+ if (!isNodeOfType(node, 'Property')) {
218
+ return;
219
+ }
220
+ switch (node.value.type) {
221
+ case 'Literal':
222
+ break;
223
+ case 'Identifier':
224
+ // e.g. css({ margin: myVariable })
225
+ if (!isImportedVariable(node.value.name)) {
226
+ hasPotentiallyLocalVariable = true;
227
+ }
228
+ this.break();
229
+ break;
230
+ case 'MemberExpression':
231
+ // e.g. css({ margin: props.color })
232
+ // css({ margin: props.media.color })
233
+ if (node.value.object.type === 'Identifier' && isImportedVariable(node.value.object.name)) {
234
+ // We found an imported variable, don't do anything.
235
+ } else {
236
+ // e.g. css({ margin: [some complicated expression].media.color })
237
+ // This can potentially get too complex, so we assume there's a local
238
+ // variable in there somewhere.
239
+ hasPotentiallyLocalVariable = true;
240
+ }
241
+ this.break();
242
+ break;
243
+ case 'TemplateLiteral':
244
+ if (!!node.value.expressions.length) {
245
+ // Too many edge cases here, don't bother...
246
+ // e.g. css({ animation: `${expandStyles(right, rightExpanded, isExpanded)} 0.2s ease-in-out` });
247
+ hasPotentiallyLocalVariable = true;
248
+ this.break();
249
+ }
250
+ break;
251
+ default:
252
+ // Catch-all for values such as "A && B", "A ? B : C"
253
+ hasPotentiallyLocalVariable = true;
254
+ this.break();
255
+ break;
256
+ }
257
+ }
258
+ });
259
+ return hasPotentiallyLocalVariable;
260
+ };
261
+
262
+ /**
263
+ * Fixer for the cssAtTopOfModule/cssAtBottomOfModule violation cases.
115
264
  * This deals with Identifiers and Expressions passed from the traverseExpressionWithConfig() function.
265
+ *
116
266
  * @param fixer The ESLint RuleFixer object
117
- * @param context The context of the node
267
+ * @param context The context of the rule
118
268
  * @param configuration The configuration of the rule, determining whether the fix is implmeneted at the top or bottom of the module
119
- * @param node Either an IdentifierWithParent node. Expression, or SpreadElement that we handle
269
+ * @param node Any potentially hoistable node, or an identifier.
120
270
  * @param cssAttributeName An optional parameter only added when we fix an ObjectExpression
121
271
  */
122
272
  var fixCssNotInModuleScope = function fixCssNotInModuleScope(fixer, context, configuration, node, cssAttributeName) {
@@ -135,35 +285,42 @@ var fixCssNotInModuleScope = function fixCssNotInModuleScope(fixer, context, con
135
285
  });
136
286
  }
137
287
  var moduleString;
138
- var implementFixer = [];
288
+ var fixes = [];
139
289
  if (node.type === 'Identifier') {
140
290
  var identifier = node;
141
291
  var declarator = identifier.parent.parent;
142
292
  moduleString = sourceCode.getText(declarator);
143
- implementFixer.push(fixer.remove(declarator));
293
+ fixes.push(fixer.remove(declarator));
144
294
  } else {
295
+ if (potentiallyHasLocalVariable(context, node)) {
296
+ return [];
297
+ }
145
298
  var _declarator = getDeclaratorString(context);
146
299
  var text = sourceCode.getText(node);
147
300
 
148
301
  // If this has been passed, then we know it's an ObjectExpression
149
302
  if (cssAttributeName) {
150
303
  moduleString = "const ".concat(_declarator, " = ").concat(cssAttributeName, "(").concat(text, ");");
304
+ var compiledImportFix = addImportSource(context, fixer, configuration, cssAttributeName);
305
+ if (compiledImportFix) {
306
+ fixes.push(compiledImportFix);
307
+ }
151
308
  } else {
152
- moduleString = moduleString = "const ".concat(_declarator, " = ").concat(text, ";");
309
+ moduleString = "const ".concat(_declarator, " = ").concat(text, ";");
153
310
  }
154
- implementFixer.push(fixer.replaceText(node, _declarator));
311
+ fixes.push(fixer.replaceText(node, _declarator));
155
312
  }
156
- return [].concat(implementFixer, [
157
- // Insert the node either before or after
313
+ return [].concat(fixes, [
314
+ // Insert the node either before or after, depending on the rule configuration
158
315
  configuration.stylesPlacement === 'bottom' ? fixer.insertTextAfter(fixerNodePlacement, '\n' + moduleString) : fixer.insertTextBefore(fixerNodePlacement, moduleString + '\n')]);
159
316
  };
160
317
 
161
318
  /**
162
319
  * Handle different cases based on what's been passed in the css-related JSXAttribute
163
- * @param context the context of the node
320
+ * @param context the context of the rule
164
321
  * @param expression the expression of the JSXAttribute value
165
322
  * @param configuration what css-related functions to account for (eg. css, xcss, cssMap), and whether to detect bottom vs top expressions
166
- * @param cssAttributeName used to encapsulate ObjectExpressions when cssOnTopOfModule/cssAtBottomOfModule violations are triggered
323
+ * @param cssAttributeName used to encapsulate ObjectExpressions when cssAtTopOfModule/cssAtBottomOfModule violations are triggered
167
324
  */
168
325
  var traverseExpressionWithConfig = function traverseExpressionWithConfig(context, expression, configuration, cssAttributeName) {
169
326
  function traverseExpression(expression) {
@@ -171,7 +328,7 @@ var traverseExpressionWithConfig = function traverseExpressionWithConfig(context
171
328
  case 'Identifier':
172
329
  // {styles}
173
330
  // We've found an identifier - time to analyze it!
174
- analyzeIdentifier(context, expression, configuration);
331
+ analyzeIdentifier(context, expression, configuration, cssAttributeName);
175
332
  break;
176
333
  case 'ArrayExpression':
177
334
  // {[styles, moreStyles]}
@@ -200,8 +357,12 @@ var traverseExpressionWithConfig = function traverseExpressionWithConfig(context
200
357
  // We've found elements that shouldn't be here! Report an error.
201
358
  context.report({
202
359
  node: expression,
203
- messageId: configuration.stylesPlacement === 'bottom' ? 'cssAtBottomOfModule' : 'cssOnTopOfModule',
360
+ messageId: configuration.stylesPlacement === 'bottom' ? 'cssAtBottomOfModule' : 'cssAtTopOfModule',
204
361
  fix: function fix(fixer) {
362
+ if (configuration.fixNamesOnly) {
363
+ return [];
364
+ }
365
+
205
366
  // Don't fix CallExpressions unless they're from cssFunctions or cssMap
206
367
  if (expression.type === 'CallExpression' && !isCssCallExpression(expression, configuration.cssFunctions)) {
207
368
  return [];
@@ -213,6 +374,17 @@ var traverseExpressionWithConfig = function traverseExpressionWithConfig(context
213
374
  }
214
375
  });
215
376
  break;
377
+
378
+ // @ts-expect-error - our ESLint-related types assume vanilla JS, when in fact
379
+ // it is running @typescript-eslint
380
+ //
381
+ // Switching to the more accurate @typescript-eslint types would break
382
+ // eslint-codemod-utils and all ESLint rules in packages/design-system,
383
+ // so we just leave this as-is.
384
+ case 'TSAsExpression':
385
+ // @ts-expect-error
386
+ traverseExpression(expression.expression);
387
+ break;
216
388
  default:
217
389
  // Do nothing!
218
390
  break;
@@ -222,10 +394,15 @@ var traverseExpressionWithConfig = function traverseExpressionWithConfig(context
222
394
  };
223
395
  var defaultConfig = {
224
396
  cssFunctions: ['css', 'xcss'],
225
- stylesPlacement: 'top'
397
+ stylesPlacement: 'top',
398
+ cssImportSource: CSS_IN_JS_IMPORTS.compiled,
399
+ xcssImportSource: CSS_IN_JS_IMPORTS.atlaskitPrimitives,
400
+ excludeReactComponents: false,
401
+ fixNamesOnly: false
226
402
  };
227
403
  var rule = createLintRule({
228
404
  meta: {
405
+ type: 'problem',
229
406
  name: 'consistent-css-prop-usage',
230
407
  docs: {
231
408
  description: 'Ensures consistency with `css` and `xcss` prop usages',
@@ -235,13 +412,42 @@ var rule = createLintRule({
235
412
  },
236
413
  fixable: 'code',
237
414
  messages: {
238
- cssOnTopOfModule: "Create styles at the top of the module scope using the respective css function.",
415
+ cssAtTopOfModule: "Create styles at the top of the module scope using the respective css function.",
239
416
  cssAtBottomOfModule: "Create styles at the bottom of the module scope using the respective css function.",
240
- cssObjectTypeOnly: "Create styles using objects passed to the css function.",
241
- cssInModule: "Imported styles should not be used, instead define in the module, import a component, or use a design token.",
417
+ cssObjectTypeOnly: "Create styles using objects passed to a css function call, e.g. `css({ textAlign: 'center'; })`.",
418
+ cssInModule: "Imported styles should not be used; instead define in the module, import a component, or use a design token.",
242
419
  cssArrayStylesOnly: "Compose styles with an array on the css prop instead of using object spread.",
420
+ noMemberExpressions: "Styles should be a regular variable (e.g. 'buttonStyles'), not a member of an object (e.g. 'myObject.styles').",
243
421
  shouldEndInStyles: 'Declared styles should end in "styles".'
244
- }
422
+ },
423
+ schema: [{
424
+ type: 'object',
425
+ properties: {
426
+ cssFunctions: {
427
+ type: 'array',
428
+ items: [{
429
+ type: 'string'
430
+ }]
431
+ },
432
+ stylesPlacement: {
433
+ type: 'string',
434
+ enum: ['top', 'bottom']
435
+ },
436
+ cssImportSource: {
437
+ type: 'string'
438
+ },
439
+ xcssImportSource: {
440
+ type: 'string'
441
+ },
442
+ excludeReactComponents: {
443
+ type: 'boolean'
444
+ },
445
+ fixNamesOnly: {
446
+ type: 'boolean'
447
+ }
448
+ },
449
+ additionalProperties: false
450
+ }]
245
451
  },
246
452
  create: function create(context) {
247
453
  var _ref3;
@@ -283,9 +489,20 @@ var rule = createLintRule({
283
489
  });
284
490
  }
285
491
  });
286
- }), _defineProperty(_ref3, "JSXAttribute", function JSXAttribute(node) {
492
+ }), _defineProperty(_ref3, "JSXAttribute", function JSXAttribute(nodeOriginal) {
493
+ var node = nodeOriginal;
287
494
  var name = node.name,
288
495
  value = node.value;
496
+ if (mergedConfig.excludeReactComponents && node.parent.type === 'JSXOpeningElement') {
497
+ // e.g. <item.before />
498
+ if (node.parent.name.type === 'JSXMemberExpression') {
499
+ return;
500
+ }
501
+ // e.g. <div />, <MenuItem />
502
+ if (node.parent.name.type === 'JSXIdentifier' && !isDOMElementName(node.parent.name.name)) {
503
+ return;
504
+ }
505
+ }
289
506
 
290
507
  // Always reset to empty array
291
508
  hoistedCss = [];
@@ -293,11 +510,16 @@ var rule = createLintRule({
293
510
  // When not a jsx expression. For eg. css=""
294
511
  if ((value === null || value === void 0 ? void 0 : value.type) !== 'JSXExpressionContainer') {
295
512
  context.report({
296
- node: value,
297
- messageId: mergedConfig.stylesPlacement === 'bottom' ? 'cssAtBottomOfModule' : 'cssOnTopOfModule'
513
+ node: node,
514
+ messageId: mergedConfig.stylesPlacement === 'bottom' ? 'cssAtBottomOfModule' : 'cssAtTopOfModule'
298
515
  });
299
516
  return;
300
517
  }
518
+ if (value.expression.type === 'JSXEmptyExpression') {
519
+ // e.g. the comment in
520
+ // <div css={/* Hello there */} />
521
+ return;
522
+ }
301
523
  traverseExpressionWithConfig(context, value.expression, mergedConfig, name.name);
302
524
  }
303
525
  }), _ref3;
@@ -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::a67afc33adf50db651edb1cf12e16ef3>>
3
+ * @codegen <<SignedSource::0a2d88c9772eb438048415f13550f592>>
4
4
  * @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
5
5
  */
6
6
  import consistentCssPropUsage from './consistent-css-prop-usage';
@@ -13,6 +13,10 @@ import noCssTaggedTemplateExpression from './no-css-tagged-template-expression';
13
13
  import noDeprecatedApis from './no-deprecated-apis';
14
14
  import noDeprecatedDesignTokenUsage from './no-deprecated-design-token-usage';
15
15
  import noDeprecatedImports from './no-deprecated-imports';
16
+ import noEmptyStyledExpression from './no-empty-styled-expression';
17
+ import noExportedCss from './no-exported-css';
18
+ import noExportedKeyframes from './no-exported-keyframes';
19
+ import noInvalidCssMap from './no-invalid-css-map';
16
20
  import noMargin from './no-margin';
17
21
  import noNestedStyles from './no-nested-styles';
18
22
  import noPhysicalProperties from './no-physical-properties';
@@ -37,6 +41,10 @@ export default {
37
41
  'no-deprecated-apis': noDeprecatedApis,
38
42
  'no-deprecated-design-token-usage': noDeprecatedDesignTokenUsage,
39
43
  'no-deprecated-imports': noDeprecatedImports,
44
+ 'no-empty-styled-expression': noEmptyStyledExpression,
45
+ 'no-exported-css': noExportedCss,
46
+ 'no-exported-keyframes': noExportedKeyframes,
47
+ 'no-invalid-css-map': noInvalidCssMap,
40
48
  'no-margin': noMargin,
41
49
  'no-nested-styles': noNestedStyles,
42
50
  'no-physical-properties': noPhysicalProperties,
@@ -0,0 +1,68 @@
1
+ import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
2
+ import { createLintRule } from '../utils/create-rule';
3
+ import { getImportSources, isStyled } from '../utils/is-supported-import';
4
+ var isEmptyStyledExpression = function isEmptyStyledExpression(node) {
5
+ var _node$arguments = _slicedToArray(node.arguments, 1),
6
+ firstArg = _node$arguments[0];
7
+ if (node.arguments.length === 0) {
8
+ return true;
9
+ } else if (node.arguments.length === 1 && (firstArg === null || firstArg === void 0 ? void 0 : firstArg.type) === 'ArrayExpression') {
10
+ return firstArg.elements.length === 0;
11
+ } else if (node.arguments.length === 1 && (firstArg === null || firstArg === void 0 ? void 0 : firstArg.type) === 'ObjectExpression') {
12
+ return firstArg.properties.length === 0;
13
+ }
14
+ return false;
15
+ };
16
+ var createNoEmptyStyledExpressionRule = function createNoEmptyStyledExpressionRule(isEmptyStyledExpression, messageId) {
17
+ return function (context) {
18
+ var importSources = getImportSources(context);
19
+ return {
20
+ 'CallExpression[callee.type="MemberExpression"]': function CallExpressionCalleeTypeMemberExpression(node) {
21
+ var _context$getScope = context.getScope(),
22
+ references = _context$getScope.references;
23
+
24
+ // If we have styled.div(...), make sure `callee` only refers to the
25
+ // `styled` part instead of the whole `styled.div` expression.
26
+ var callee = node.callee.type === 'MemberExpression' ? node.callee.object : node.callee;
27
+ if (!isStyled(callee, references, importSources)) {
28
+ return;
29
+ }
30
+ if (!isEmptyStyledExpression(node)) {
31
+ return;
32
+ }
33
+ context.report({
34
+ messageId: messageId,
35
+ node: node
36
+ });
37
+ }
38
+ };
39
+ };
40
+ };
41
+ var noEmptyStyledExpressionRule = createLintRule({
42
+ meta: {
43
+ name: 'no-empty-styled-expression',
44
+ docs: {
45
+ description: 'Forbids any styled expression to be used when passing empty arguments to styled.div() (or other JSX elements).',
46
+ recommended: true,
47
+ severity: 'warn'
48
+ },
49
+ messages: {
50
+ unexpected: 'Found an empty expression, or empty object argument passed to `styled` function call. This unnecessarily causes a major performance penalty - please use a plain JSX element or a React fragment instead (e.g. `<div>Hello</div>` or `<>Hello</>`).'
51
+ },
52
+ type: 'problem',
53
+ schema: [{
54
+ type: 'object',
55
+ properties: {
56
+ importSources: {
57
+ type: 'array',
58
+ items: [{
59
+ type: 'string'
60
+ }]
61
+ }
62
+ },
63
+ additionalProperties: false
64
+ }]
65
+ },
66
+ create: createNoEmptyStyledExpressionRule(isEmptyStyledExpression, 'unexpected')
67
+ });
68
+ export default noEmptyStyledExpressionRule;
@@ -0,0 +1,31 @@
1
+ import { createNoExportedRule } from '../utils/create-no-exported-rule/main';
2
+ import { createLintRule } from '../utils/create-rule';
3
+ import { isCss } from '../utils/is-supported-import';
4
+ var noExportedCssRule = createLintRule({
5
+ meta: {
6
+ name: 'no-exported-css',
7
+ type: 'problem',
8
+ docs: {
9
+ description: 'Forbid exporting `css` function calls. Exporting `css` function calls can result in unexpected behaviour at runtime, and is not statically analysable.',
10
+ recommended: true,
11
+ severity: 'warn'
12
+ },
13
+ messages: {
14
+ unexpected: "`css` can't be exported - this will cause unexpected behaviour at runtime. Instead, please move your `css(...)` code to the same file where these styles are being used."
15
+ },
16
+ schema: [{
17
+ type: 'object',
18
+ properties: {
19
+ importSources: {
20
+ type: 'array',
21
+ items: [{
22
+ type: 'string'
23
+ }]
24
+ }
25
+ },
26
+ additionalProperties: false
27
+ }]
28
+ },
29
+ create: createNoExportedRule(isCss, 'unexpected')
30
+ });
31
+ export default noExportedCssRule;
@@ -0,0 +1,31 @@
1
+ import { createNoExportedRule } from '../utils/create-no-exported-rule/main';
2
+ import { createLintRule } from '../utils/create-rule';
3
+ import { isKeyframes } from '../utils/is-supported-import';
4
+ var noExportedKeyframesRule = createLintRule({
5
+ meta: {
6
+ name: 'no-exported-keyframes',
7
+ type: 'problem',
8
+ docs: {
9
+ description: 'Forbid exporting `keyframes` function calls. Exporting `css` function calls can result in unexpected behaviour at runtime, and is not statically analysable.',
10
+ recommended: true,
11
+ severity: 'warn'
12
+ },
13
+ messages: {
14
+ unexpected: "`keyframes` can't be exported - this will cause unexpected behaviour at runtime. Instead, please move your `keyframes(...)` code to the same file where these styles are being used."
15
+ },
16
+ schema: [{
17
+ type: 'object',
18
+ properties: {
19
+ importSources: {
20
+ type: 'array',
21
+ items: [{
22
+ type: 'string'
23
+ }]
24
+ }
25
+ },
26
+ additionalProperties: false
27
+ }]
28
+ },
29
+ create: createNoExportedRule(isKeyframes, 'unexpected')
30
+ });
31
+ export default noExportedKeyframesRule;