@atlaskit/eslint-plugin-design-system 8.32.0 → 8.32.2

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 (22) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/constellation/no-styled-tagged-template-expression/usage.mdx +42 -0
  3. package/dist/cjs/rules/consistent-css-prop-usage/index.js +375 -334
  4. package/dist/cjs/rules/no-styled-tagged-template-expression/index.js +2 -2
  5. package/dist/cjs/rules/utils/create-no-tagged-template-expression-rule/index.js +10 -3
  6. package/dist/cjs/rules/utils/is-supported-import.js +64 -10
  7. package/dist/es2019/rules/consistent-css-prop-usage/index.js +283 -267
  8. package/dist/es2019/rules/no-styled-tagged-template-expression/index.js +1 -1
  9. package/dist/es2019/rules/utils/create-no-tagged-template-expression-rule/index.js +26 -1
  10. package/dist/es2019/rules/utils/is-supported-import.js +60 -10
  11. package/dist/esm/rules/consistent-css-prop-usage/index.js +375 -334
  12. package/dist/esm/rules/no-styled-tagged-template-expression/index.js +1 -1
  13. package/dist/esm/rules/utils/create-no-tagged-template-expression-rule/index.js +11 -4
  14. package/dist/esm/rules/utils/is-supported-import.js +63 -9
  15. package/dist/types/rules/utils/is-supported-import.d.ts +11 -0
  16. package/dist/types-ts4.5/rules/utils/is-supported-import.d.ts +11 -0
  17. package/package.json +1 -1
  18. package/dist/cjs/rules/no-styled-tagged-template-expression/is-styled.js +0 -53
  19. package/dist/es2019/rules/no-styled-tagged-template-expression/is-styled.js +0 -45
  20. package/dist/esm/rules/no-styled-tagged-template-expression/is-styled.js +0 -47
  21. package/dist/types/rules/no-styled-tagged-template-expression/is-styled.d.ts +0 -7
  22. package/dist/types-ts4.5/rules/no-styled-tagged-template-expression/is-styled.d.ts +0 -7
@@ -1,6 +1,6 @@
1
1
  import { createNoTaggedTemplateExpressionRule, noTaggedTemplateExpressionRuleSchema } from '../utils/create-no-tagged-template-expression-rule';
2
2
  import { createLintRule } from '../utils/create-rule';
3
- import { isStyled } from './is-styled';
3
+ import { isStyled } from '../utils/is-supported-import';
4
4
  const rule = createLintRule({
5
5
  meta: {
6
6
  name: 'no-styled-tagged-template-expression',
@@ -1,7 +1,7 @@
1
1
  // Original source from Compiled https://github.com/atlassian-labs/compiled/blob/master/packages/eslint-plugin/src/utils/create-no-tagged-template-expression-rule/index.ts
2
2
  // eslint-disable-next-line import/no-extraneous-dependencies
3
3
 
4
- import { getImportSources } from '../is-supported-import';
4
+ import { getImportSources, isStyledComponents } from '../is-supported-import';
5
5
  import { generate } from './generate';
6
6
  import { getTaggedTemplateExpressionOffset } from './get-tagged-template-expression-offset';
7
7
  import { toArguments } from './to-arguments';
@@ -35,6 +35,7 @@ export const createNoTaggedTemplateExpressionRule = (isUsage, messageId) => cont
35
35
  if (!isUsage(node.tag, references, importSources)) {
36
36
  return;
37
37
  }
38
+ const isSC = isStyledComponents(node.tag, references, importSources);
38
39
  context.report({
39
40
  messageId,
40
41
  node,
@@ -75,6 +76,30 @@ export const createNoTaggedTemplateExpressionRule = (isUsage, messageId) => cont
75
76
  if (oldCode === newCode) {
76
77
  return;
77
78
  }
79
+
80
+ // TODO: We might want to similarly disallow `styled.div({ color: props => props.color })` for SC as it's broken too (both type and functionality)
81
+ // Alternatively, autofix it to `styled.div(props => ({ color: props.color }))`?
82
+ if (isSC && /\$\{.*:[\s]*\{/.test(newCode)) {
83
+ /**
84
+ * If we find a variable in a selector when migrating `styled-components` code, we skip it.
85
+ * This is because `styled-components@3.x` does not support the syntax.
86
+ *
87
+ * @example
88
+ * ```tsx
89
+ * const Component = styled.div`
90
+ * & + ${Button} { color: red; }
91
+ * `;
92
+ * ```
93
+ * Becomes this code, which is not supported in `styled-components@3.x`:
94
+ * ```tsx
95
+ * const Component = styled.div({
96
+ * [`& + ${Button}`]: {
97
+ * color: 'red',
98
+ * });
99
+ * ```
100
+ */
101
+ return;
102
+ }
78
103
  yield fixer.insertTextBefore(node, newCode);
79
104
  yield fixer.remove(node);
80
105
  }
@@ -16,6 +16,21 @@ export const CSS_IN_JS_IMPORTS = {
16
16
  * By default all known import sources are checked against.
17
17
  */
18
18
  export const DEFAULT_IMPORT_SOURCES = Object.values(CSS_IN_JS_IMPORTS);
19
+ const getIdentifierNode = node => {
20
+ let identifierNode = node.type === 'Identifier' ? node : undefined;
21
+ if (!identifierNode) {
22
+ // Handles styled.div`` case
23
+ if (node.type === 'MemberExpression' && node.object.type === 'Identifier') {
24
+ identifierNode = node.object;
25
+ }
26
+
27
+ // Handles styled(Component)`` case
28
+ if (node.type === 'CallExpression' && node.callee.type === 'Identifier') {
29
+ identifierNode = node.callee;
30
+ }
31
+ }
32
+ return identifierNode;
33
+ };
19
34
 
20
35
  /**
21
36
  * Given the ESLint rule context, extract and parse the value of the importSources rule option.
@@ -35,7 +50,7 @@ export const getImportSources = context => {
35
50
  }
36
51
  return DEFAULT_IMPORT_SOURCES;
37
52
  };
38
- const isSupportedImportWrapper = functionName => {
53
+ const isSupportedImportWrapper = (functionName, defaultFromImportSources = []) => {
39
54
  const checkDefinitionHasImport = (def, importSources) => {
40
55
  if (def.type !== 'ImportBinding') {
41
56
  return false;
@@ -43,12 +58,19 @@ const isSupportedImportWrapper = functionName => {
43
58
  if (!def.parent || !importSources.includes(def.parent.source.value)) {
44
59
  return false;
45
60
  }
46
- return (
47
- // import { functionName } from 'import-source';
48
- def.node.type === 'ImportSpecifier' && def.node.imported.name === functionName ||
49
- // import functionName from 'import-source';
50
- def.node.type === 'ImportDefaultSpecifier' && def.node.local.name === functionName
51
- );
61
+
62
+ // Matches the imported name from a named import
63
+ // import { functionName, functioName as otherName } from 'import-source';
64
+ const isNamedImport = def.node.type === 'ImportSpecifier' && def.node.imported.name === functionName;
65
+
66
+ // Must explicitly match the local name from a default import
67
+ // import functionName from 'import-source';
68
+ const isDefaultImportMatchingLocal = def.node.type === 'ImportDefaultSpecifier' && def.node.local.name === functionName;
69
+
70
+ // Can match any local name from a default import
71
+ // import anything from 'import-source'
72
+ const isKnownDefaultImport = def.node.type === 'ImportDefaultSpecifier' && defaultFromImportSources.includes(def.parent.source.value);
73
+ return isNamedImport || isDefaultImportMatchingLocal || isKnownDefaultImport;
52
74
  };
53
75
 
54
76
  /**
@@ -68,9 +90,10 @@ const isSupportedImportWrapper = functionName => {
68
90
  * @returns Whether the above conditions are true.
69
91
  */
70
92
  const isSupportedImport = (nodeToCheck, referencesInScope, importSources) => {
71
- return nodeToCheck.type === 'Identifier' && referencesInScope.some(reference => {
93
+ const identifierNode = getIdentifierNode(nodeToCheck);
94
+ return (identifierNode === null || identifierNode === void 0 ? void 0 : identifierNode.type) === 'Identifier' && referencesInScope.some(reference => {
72
95
  var _reference$resolved;
73
- return reference.identifier === nodeToCheck && ((_reference$resolved = reference.resolved) === null || _reference$resolved === void 0 ? void 0 : _reference$resolved.defs.some(def => checkDefinitionHasImport(def, importSources)));
96
+ return reference.identifier === identifierNode && ((_reference$resolved = reference.resolved) === null || _reference$resolved === void 0 ? void 0 : _reference$resolved.defs.some(def => checkDefinitionHasImport(def, importSources)));
74
97
  });
75
98
  };
76
99
  return isSupportedImport;
@@ -83,4 +106,31 @@ export const isCss = isSupportedImportWrapper('css');
83
106
  export const isCxFunction = isSupportedImportWrapper('cx');
84
107
  export const isCssMap = isSupportedImportWrapper('cssMap');
85
108
  export const isKeyframes = isSupportedImportWrapper('keyframes');
86
- export const isStyled = isSupportedImportWrapper('styled');
109
+ // `styled` is also the explicit default of `styled-components` and `@emotion/styled`, so we also match on default imports generally
110
+ export const isStyled = isSupportedImportWrapper('styled', ['styled-components', '@emotion/styled']);
111
+ export const isImportedFrom = (moduleName, exactMatch = true) => (nodeToCheck, referencesInScope, importSources) => {
112
+ if (!importSources.includes(moduleName)) {
113
+ // Don't go through the trouble of checking the import sources does not include this
114
+ // We'll assume this is skipped elsewhere.
115
+ return false;
116
+ }
117
+ const identifierNode = getIdentifierNode(nodeToCheck);
118
+ return (identifierNode === null || identifierNode === void 0 ? void 0 : identifierNode.type) === 'Identifier' && referencesInScope.some(reference => {
119
+ var _reference$resolved2;
120
+ return reference.identifier === identifierNode && ((_reference$resolved2 = reference.resolved) === null || _reference$resolved2 === void 0 ? void 0 : _reference$resolved2.defs.some(def => {
121
+ var _def$parent, _String, _def$parent2;
122
+ return def.type === 'ImportBinding' && (((_def$parent = def.parent) === null || _def$parent === void 0 ? void 0 : _def$parent.source.value) === moduleName || !exactMatch && ((_String = String((_def$parent2 = def.parent) === null || _def$parent2 === void 0 ? void 0 : _def$parent2.source.value)) === null || _String === void 0 ? void 0 : _String.startsWith(moduleName)));
123
+ }));
124
+ });
125
+ };
126
+
127
+ /**
128
+ * Determine if this node is specifically from a `'styled-components'` import.
129
+ * This is because `styled-components@3.4` APIs are not consistent with Emotion and Compiled,
130
+ * we need to handle them differently in a few scenarios.
131
+ *
132
+ * This can be cleaned up when `'styled-components'` is no longer a valid ImportSource.
133
+ */
134
+ export const isStyledComponents = isImportedFrom('styled-components');
135
+ export const isCompiled = isImportedFrom('@compiled/', false);
136
+ export const isEmotion = isImportedFrom('@emotion/', false);