@atlaskit/eslint-plugin-design-system 8.37.0 → 8.37.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 (26) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/dist/cjs/ast-nodes/object-entry.js +13 -0
  3. package/dist/cjs/rules/use-primitives/transformers/compiled-styled/index.js +2 -1
  4. package/dist/cjs/rules/use-primitives/transformers/emotion-css/index.js +3 -3
  5. package/dist/cjs/rules/use-primitives/utils/validate-styles.js +113 -0
  6. package/dist/cjs/rules/utils/create-no-tagged-template-expression-rule/index.js +18 -9
  7. package/dist/cjs/rules/utils/is-supported-import.js +3 -1
  8. package/dist/es2019/ast-nodes/object-entry.js +13 -0
  9. package/dist/es2019/rules/use-primitives/transformers/compiled-styled/index.js +2 -1
  10. package/dist/es2019/rules/use-primitives/transformers/emotion-css/index.js +3 -3
  11. package/dist/es2019/rules/use-primitives/utils/validate-styles.js +106 -0
  12. package/dist/es2019/rules/utils/create-no-tagged-template-expression-rule/index.js +27 -8
  13. package/dist/es2019/rules/utils/is-supported-import.js +1 -1
  14. package/dist/esm/ast-nodes/object-entry.js +13 -0
  15. package/dist/esm/rules/use-primitives/transformers/compiled-styled/index.js +2 -1
  16. package/dist/esm/rules/use-primitives/transformers/emotion-css/index.js +3 -3
  17. package/dist/esm/rules/use-primitives/utils/validate-styles.js +104 -0
  18. package/dist/esm/rules/utils/create-no-tagged-template-expression-rule/index.js +19 -10
  19. package/dist/esm/rules/utils/is-supported-import.js +3 -1
  20. package/dist/types/ast-nodes/object-entry.d.ts +2 -1
  21. package/dist/types/rules/use-primitives/config/index.d.ts +1 -1
  22. package/dist/types/rules/use-primitives/utils/validate-styles.d.ts +3 -0
  23. package/dist/types-ts4.5/ast-nodes/object-entry.d.ts +2 -1
  24. package/dist/types-ts4.5/rules/use-primitives/config/index.d.ts +1 -1
  25. package/dist/types-ts4.5/rules/use-primitives/utils/validate-styles.d.ts +3 -0
  26. package/package.json +2 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,22 @@
1
1
  # @atlaskit/eslint-plugin-design-system
2
2
 
3
+ ## 8.37.2
4
+
5
+ ### Patch Changes
6
+
7
+ - [#80662](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/pull-requests/80662) [`4833299b00d4`](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/commits/4833299b00d4) - For `no-css-tagged-template-expression` and `no-styled-tagged-template-expression`:
8
+
9
+ - When importing from Emotion, stop applying autofixer when the styles contain `!important`.
10
+ - When importing from any library, stop applying autofixer when a selector contains a tagged template interpolation (previously only styled-components).
11
+
12
+ - [#81697](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/pull-requests/81697) [`cf3483e7e87d`](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/commits/cf3483e7e87d) - Adding a missing `license` entry to the `package.json` for this package. This package was already licensed under `Apache-2.0` (see `LICENSE` file), so this change is not changing it's license.
13
+
14
+ ## 8.37.1
15
+
16
+ ### Patch Changes
17
+
18
+ - [#81307](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/pull-requests/81307) [`6420f933c8ae`](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/commits/6420f933c8ae) - Fix issue where `use-primitives` was incorrectly transforming styles with string properties.
19
+
3
20
  ## 8.37.0
4
21
 
5
22
  ### Minor Changes
@@ -4,6 +4,7 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.ObjectEntry = void 0;
7
+ var _eslintCodemodUtils = require("eslint-codemod-utils");
7
8
  var ObjectEntry = exports.ObjectEntry = {
8
9
  deleteEntry: function deleteEntry(node, context, fixer) {
9
10
  var _lastToken;
@@ -23,5 +24,17 @@ var ObjectEntry = exports.ObjectEntry = {
23
24
  lastToken = sourceCode.getTokenBefore(lastToken);
24
25
  }
25
26
  return fixer.removeRange([prevToken.range[1], lastToken.range[1]]);
27
+ },
28
+ getPropertyName: function getPropertyName(node) {
29
+ // SpreadElements don't really have a property name
30
+ if (!(0, _eslintCodemodUtils.isNodeOfType)(node, 'Property')) {
31
+ return undefined;
32
+ }
33
+ if ((0, _eslintCodemodUtils.isNodeOfType)(node.key, 'Literal')) {
34
+ return node.key.raw;
35
+ }
36
+ if ((0, _eslintCodemodUtils.isNodeOfType)(node.key, 'Identifier')) {
37
+ return node.key.name;
38
+ }
26
39
  }
27
40
  };
@@ -10,6 +10,7 @@ var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers
10
10
  var _eslintCodemodUtils = require("eslint-codemod-utils");
11
11
  var ast = _interopRequireWildcard(require("../../../../ast-nodes"));
12
12
  var _utils = require("../../utils");
13
+ var _validateStyles = require("../../utils/validate-styles");
13
14
  var _convertJsxCallSite = require("./convert-jsx-call-site");
14
15
  var _convertStyledComponentCallToJsx = require("./convert-styled-component-call-to-jsx");
15
16
  var _findValidJsxUsageToTransform = require("./find-valid-jsx-usage-to-transform");
@@ -60,7 +61,7 @@ var CompiledStyled = exports.CompiledStyled = {
60
61
  };
61
62
  }
62
63
  var styledComponentVariableRef = (0, _findValidStyledComponentCall.findValidStyledComponentCall)(node);
63
- if (!styledComponentVariableRef || !(0, _eslintCodemodUtils.isNodeOfType)(styledComponentVariableRef.id, 'Identifier') || !(0, _utils.isValidCssPropertiesToTransform)(node, config)) {
64
+ if (!styledComponentVariableRef || !(0, _eslintCodemodUtils.isNodeOfType)(styledComponentVariableRef.id, 'Identifier') || !(config.patterns.includes('string-style-property-fix') ? (0, _validateStyles.validateStyles)(node, config) : (0, _utils.isValidCssPropertiesToTransform)(node, config))) {
64
65
  return {
65
66
  success: false
66
67
  };
@@ -10,6 +10,7 @@ var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers
10
10
  var _eslintCodemodUtils = require("eslint-codemod-utils");
11
11
  var ast = _interopRequireWildcard(require("../../../../ast-nodes"));
12
12
  var _utils = require("../../utils");
13
+ var _validateStyles = require("../../utils/validate-styles");
13
14
  var _cssToXcss = require("../css-to-xcss");
14
15
  var supported = _interopRequireWildcard(require("./supported"));
15
16
  var _upsertImportDeclaration = require("./upsert-import-declaration");
@@ -44,7 +45,6 @@ var EmotionCSS = exports.EmotionCSS = {
44
45
  });
45
46
  },
46
47
  _check: function _check(node, _ref2) {
47
- var _cssVariableValue$nod, _cssVariableValue$nod2;
48
48
  var context = _ref2.context,
49
49
  config = _ref2.config;
50
50
  if (!config.patterns.includes('compiled-css-function')) {
@@ -91,10 +91,10 @@ var EmotionCSS = exports.EmotionCSS = {
91
91
  var cssVariableDefinition = (0, _eslintCodemodUtils.getIdentifierInParentScope)(context.getScope(), cssAttrValue.value);
92
92
  var cssVariableValue = (0, _utils.getVariableDefinitionValue)(cssVariableDefinition);
93
93
  // Check if `cssVariableValue` is a function called `css()`
94
- if (ast.FunctionCall.getName(cssVariableValue === null || cssVariableValue === void 0 || (_cssVariableValue$nod = cssVariableValue.node) === null || _cssVariableValue$nod === void 0 ? void 0 : _cssVariableValue$nod.init) !== 'css') {
94
+ if (ast.FunctionCall.getName(cssVariableValue === null || cssVariableValue === void 0 ? void 0 : cssVariableValue.node.init) !== 'css') {
95
95
  return false;
96
96
  }
97
- if (!(0, _utils.isValidCssPropertiesToTransform)(cssVariableValue === null || cssVariableValue === void 0 || (_cssVariableValue$nod2 = cssVariableValue.node) === null || _cssVariableValue$nod2 === void 0 ? void 0 : _cssVariableValue$nod2.init, config)) {
97
+ if (!(config.patterns.includes('string-style-property-fix') ? (0, _validateStyles.validateStyles)(cssVariableValue === null || cssVariableValue === void 0 ? void 0 : cssVariableValue.node.init, config) : (0, _utils.isValidCssPropertiesToTransform)(cssVariableValue === null || cssVariableValue === void 0 ? void 0 : cssVariableValue.node.init, config))) {
98
98
  return false;
99
99
  }
100
100
  var importDeclaration = ast.Root.findImportsByModule(context.getSourceCode().ast.body, '@atlaskit/primitives');
@@ -0,0 +1,113 @@
1
+ "use strict";
2
+
3
+ var _typeof = require("@babel/runtime/helpers/typeof");
4
+ Object.defineProperty(exports, "__esModule", {
5
+ value: true
6
+ });
7
+ exports.validateStyles = void 0;
8
+ var _eslintCodemodUtils = require("eslint-codemod-utils");
9
+ var ast = _interopRequireWildcard(require("../../../ast-nodes"));
10
+ var _transformers = require("../transformers");
11
+ var _cssToXcss = require("../transformers/css-to-xcss");
12
+ function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(e) { return e ? t : r; })(e); }
13
+ function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != _typeof(e) && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
14
+ var validateStyles = exports.validateStyles = function validateStyles(node, config) {
15
+ if (!node) {
16
+ return false;
17
+ }
18
+ var cssObjectExpression = node.arguments[0];
19
+ // Bail on empty object calls
20
+ if (!(0, _eslintCodemodUtils.isNodeOfType)(cssObjectExpression, 'ObjectExpression')) {
21
+ return false;
22
+ }
23
+ if (!ast.Object.isFlat(cssObjectExpression)) {
24
+ return false;
25
+ }
26
+ var entries = ast.Object.getEntries(cssObjectExpression);
27
+ if (entries.length === 0) {
28
+ return false;
29
+ }
30
+ if (!config.patterns.includes('multiple-properties') && entries.length > 1) {
31
+ return false;
32
+ }
33
+ return entries.every(function (entry) {
34
+ // Bail on SpreadElements
35
+ if (!(0, _eslintCodemodUtils.isNodeOfType)(entry, 'Property')) {
36
+ return false;
37
+ }
38
+ var key = entry.key,
39
+ value = entry.value;
40
+
41
+ // Bail if the property is not something we understand, e.g. `{ [SOME_CONSTANT]: '8px' }`
42
+ if (!(0, _eslintCodemodUtils.isNodeOfType)(key, 'Identifier')) {
43
+ return false;
44
+ }
45
+ var property = ast.ObjectEntry.getPropertyName(entry);
46
+ if (!property) {
47
+ return false;
48
+ }
49
+
50
+ // Currently, we handle values like `'8px'` (a 'Literal') or `token('space.100')` (a 'CallExpression')
51
+ if ((0, _eslintCodemodUtils.isNodeOfType)(value, 'Literal')) {
52
+ if (!value.value) {
53
+ return false;
54
+ }
55
+ var valueString = value.value.toString();
56
+ if (!_transformers.supportedStylesMap[property] || !_transformers.supportedStylesMap[property][valueString]) {
57
+ return false;
58
+ }
59
+ } else if ((0, _eslintCodemodUtils.isNodeOfType)(value, 'CallExpression')) {
60
+ // If it's a function call, then make sure it's the `token` function (and we have `css-property-with-tokens` enabled)
61
+
62
+ // Short-circuit when token calls are found but pattern is not enabled in config
63
+ if (isTokenCall(value) && !config.patterns.includes('css-property-with-tokens')) {
64
+ return false;
65
+ }
66
+
67
+ // Bail if it's a function, but that function call is not `tokens('<token>')`
68
+ if (!isTokenCall(value)) {
69
+ return false;
70
+ }
71
+ } else {
72
+ return false;
73
+ }
74
+
75
+ // Bail if dimension property is found but pattern is not enabled in config
76
+ // Note: We don't need to exhaustively re-check the key/value pair here. That's already been done above.
77
+ // This is just to check wether 'dimension-properties' pattern is enabled and can be removed when the pattern is completely rolled out
78
+ var isDimensionProperty = !!_cssToXcss.supportedDimensionAttributesMap[property];
79
+ if (isDimensionProperty && !config.patterns.includes('dimension-properties')) {
80
+ return false;
81
+ }
82
+ return true;
83
+ });
84
+ };
85
+ var isTokenCall = function isTokenCall(node) {
86
+ // Is it a function call?
87
+ if (!(0, _eslintCodemodUtils.isNodeOfType)(node, 'CallExpression')) {
88
+ return false;
89
+ }
90
+
91
+ // Is the function called 'token'?
92
+ if (ast.FunctionCall.getName(node) !== 'token') {
93
+ return false;
94
+ }
95
+ var token = ast.FunctionCall.getArgumentAtPos(node, 0);
96
+ var fallback = ast.FunctionCall.getArgumentAtPos(node, 1);
97
+ if (!token || token.type !== 'Literal') {
98
+ return false;
99
+ }
100
+
101
+ // Is the token one that we understand
102
+ if (!Object.values(_transformers.spaceTokenMap).includes(token.value)) {
103
+ return false;
104
+ }
105
+
106
+ // Not all `token()` calls have a fall back. This is fine, but if there is a fallback, make sure it's the same as the fallback xcss will use
107
+ if (fallback && fallback.type === 'Literal') {
108
+ if (_transformers.spaceTokenMap[fallback.value] !== token.value) {
109
+ return false;
110
+ }
111
+ }
112
+ return true;
113
+ };
@@ -43,12 +43,11 @@ var createNoTaggedTemplateExpressionRule = exports.createNoTaggedTemplateExpress
43
43
  if (!isUsage(node.tag, references, importSources)) {
44
44
  return;
45
45
  }
46
- var isSC = (0, _isSupportedImport.isStyledComponents)(node.tag, references, importSources);
47
46
  context.report({
48
47
  messageId: messageId,
49
48
  node: node,
50
49
  fix: /*#__PURE__*/_regenerator.default.mark(function fix(fixer) {
51
- var quasi, source, args, oldCode, withoutQuasi, newCode;
50
+ var quasi, source, args, oldCode, withoutQuasi, newCode, usesEmotion;
52
51
  return _regenerator.default.wrap(function fix$(_context) {
53
52
  while (1) switch (_context.prev = _context.next) {
54
53
  case 0:
@@ -95,18 +94,28 @@ var createNoTaggedTemplateExpressionRule = exports.createNoTaggedTemplateExpress
95
94
  }
96
95
  return _context.abrupt("return");
97
96
  case 16:
98
- if (!(isSC && /\$\{.*:[\s]*\{/.test(newCode) || /\$\{.*\(.*:[\s]*\{/.test(newCode))) {
99
- _context.next = 18;
97
+ // For styles like `position: initial !important`,
98
+ // Emotion can give typechecking errors when using object syntax
99
+ // due to csstype being overly strict
100
+ usesEmotion = (0, _isSupportedImport.isEmotion)(node.tag, references, importSources);
101
+ if (!(usesEmotion && !!newCode.match(/!\s*important/gm))) {
102
+ _context.next = 19;
100
103
  break;
101
104
  }
102
105
  return _context.abrupt("return");
103
- case 18:
104
- _context.next = 20;
106
+ case 19:
107
+ if (!/\$\{.*:[\s]*\{/.test(newCode)) {
108
+ _context.next = 21;
109
+ break;
110
+ }
111
+ return _context.abrupt("return");
112
+ case 21:
113
+ _context.next = 23;
105
114
  return fixer.insertTextBefore(node, newCode);
106
- case 20:
107
- _context.next = 22;
115
+ case 23:
116
+ _context.next = 25;
108
117
  return fixer.remove(node);
109
- case 22:
118
+ case 25:
110
119
  case "end":
111
120
  return _context.stop();
112
121
  }
@@ -121,7 +121,9 @@ var isStyled = exports.isStyled = isSupportedImportWrapper('styled', ['styled-co
121
121
  var isImportedFrom = exports.isImportedFrom = function isImportedFrom(moduleName) {
122
122
  var exactMatch = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
123
123
  return function (nodeToCheck, referencesInScope, importSources) {
124
- if (!importSources.includes(moduleName)) {
124
+ if (!importSources.some(function (importSource) {
125
+ return importSource === moduleName || !exactMatch && importSource.startsWith(moduleName);
126
+ })) {
125
127
  // Don't go through the trouble of checking the import sources does not include this
126
128
  // We'll assume this is skipped elsewhere.
127
129
  return false;
@@ -1,3 +1,4 @@
1
+ import { isNodeOfType } from 'eslint-codemod-utils';
1
2
  const ObjectEntry = {
2
3
  deleteEntry(node, context, fixer) {
3
4
  var _lastToken;
@@ -17,6 +18,18 @@ const ObjectEntry = {
17
18
  lastToken = sourceCode.getTokenBefore(lastToken);
18
19
  }
19
20
  return fixer.removeRange([prevToken.range[1], lastToken.range[1]]);
21
+ },
22
+ getPropertyName(node) {
23
+ // SpreadElements don't really have a property name
24
+ if (!isNodeOfType(node, 'Property')) {
25
+ return undefined;
26
+ }
27
+ if (isNodeOfType(node.key, 'Literal')) {
28
+ return node.key.raw;
29
+ }
30
+ if (isNodeOfType(node.key, 'Identifier')) {
31
+ return node.key.name;
32
+ }
20
33
  }
21
34
  };
22
35
  export { ObjectEntry };
@@ -3,6 +3,7 @@
3
3
  import { isNodeOfType } from 'eslint-codemod-utils';
4
4
  import * as ast from '../../../../ast-nodes';
5
5
  import { isValidCssPropertiesToTransform } from '../../utils';
6
+ import { validateStyles } from '../../utils/validate-styles';
6
7
  import { convertJsxCallSite } from './convert-jsx-call-site';
7
8
  import { convertStyledComponentToXcss } from './convert-styled-component-call-to-jsx';
8
9
  import { findValidJsxUsageToTransform } from './find-valid-jsx-usage-to-transform';
@@ -52,7 +53,7 @@ export const CompiledStyled = {
52
53
  };
53
54
  }
54
55
  const styledComponentVariableRef = findValidStyledComponentCall(node);
55
- if (!styledComponentVariableRef || !isNodeOfType(styledComponentVariableRef.id, 'Identifier') || !isValidCssPropertiesToTransform(node, config)) {
56
+ if (!styledComponentVariableRef || !isNodeOfType(styledComponentVariableRef.id, 'Identifier') || !(config.patterns.includes('string-style-property-fix') ? validateStyles(node, config) : isValidCssPropertiesToTransform(node, config))) {
56
57
  return {
57
58
  success: false
58
59
  };
@@ -3,6 +3,7 @@
3
3
  import { getIdentifierInParentScope, isNodeOfType } from 'eslint-codemod-utils';
4
4
  import * as ast from '../../../../ast-nodes';
5
5
  import { getVariableDefinitionValue, getVariableUsagesCount, isValidCssPropertiesToTransform } from '../../utils';
6
+ import { validateStyles } from '../../utils/validate-styles';
6
7
  import { cssToXcssTransformer } from '../css-to-xcss';
7
8
  import * as supported from './supported';
8
9
  import { upsertImportDeclaration } from './upsert-import-declaration';
@@ -37,7 +38,6 @@ export const EmotionCSS = {
37
38
  context,
38
39
  config
39
40
  }) {
40
- var _cssVariableValue$nod, _cssVariableValue$nod2;
41
41
  if (!config.patterns.includes('compiled-css-function')) {
42
42
  return false;
43
43
  }
@@ -82,10 +82,10 @@ export const EmotionCSS = {
82
82
  const cssVariableDefinition = getIdentifierInParentScope(context.getScope(), cssAttrValue.value);
83
83
  const cssVariableValue = getVariableDefinitionValue(cssVariableDefinition);
84
84
  // Check if `cssVariableValue` is a function called `css()`
85
- if (ast.FunctionCall.getName(cssVariableValue === null || cssVariableValue === void 0 ? void 0 : (_cssVariableValue$nod = cssVariableValue.node) === null || _cssVariableValue$nod === void 0 ? void 0 : _cssVariableValue$nod.init) !== 'css') {
85
+ if (ast.FunctionCall.getName(cssVariableValue === null || cssVariableValue === void 0 ? void 0 : cssVariableValue.node.init) !== 'css') {
86
86
  return false;
87
87
  }
88
- if (!isValidCssPropertiesToTransform(cssVariableValue === null || cssVariableValue === void 0 ? void 0 : (_cssVariableValue$nod2 = cssVariableValue.node) === null || _cssVariableValue$nod2 === void 0 ? void 0 : _cssVariableValue$nod2.init, config)) {
88
+ if (!(config.patterns.includes('string-style-property-fix') ? validateStyles(cssVariableValue === null || cssVariableValue === void 0 ? void 0 : cssVariableValue.node.init, config) : isValidCssPropertiesToTransform(cssVariableValue === null || cssVariableValue === void 0 ? void 0 : cssVariableValue.node.init, config))) {
89
89
  return false;
90
90
  }
91
91
  const importDeclaration = ast.Root.findImportsByModule(context.getSourceCode().ast.body, '@atlaskit/primitives');
@@ -0,0 +1,106 @@
1
+ import { isNodeOfType } from 'eslint-codemod-utils';
2
+ import * as ast from '../../../ast-nodes';
3
+ import { spaceTokenMap, supportedStylesMap } from '../transformers';
4
+ import { supportedDimensionAttributesMap } from '../transformers/css-to-xcss';
5
+ export const validateStyles = (node, config) => {
6
+ if (!node) {
7
+ return false;
8
+ }
9
+ const cssObjectExpression = node.arguments[0];
10
+ // Bail on empty object calls
11
+ if (!isNodeOfType(cssObjectExpression, 'ObjectExpression')) {
12
+ return false;
13
+ }
14
+ if (!ast.Object.isFlat(cssObjectExpression)) {
15
+ return false;
16
+ }
17
+ const entries = ast.Object.getEntries(cssObjectExpression);
18
+ if (entries.length === 0) {
19
+ return false;
20
+ }
21
+ if (!config.patterns.includes('multiple-properties') && entries.length > 1) {
22
+ return false;
23
+ }
24
+ return entries.every(entry => {
25
+ // Bail on SpreadElements
26
+ if (!isNodeOfType(entry, 'Property')) {
27
+ return false;
28
+ }
29
+ const {
30
+ key,
31
+ value
32
+ } = entry;
33
+
34
+ // Bail if the property is not something we understand, e.g. `{ [SOME_CONSTANT]: '8px' }`
35
+ if (!isNodeOfType(key, 'Identifier')) {
36
+ return false;
37
+ }
38
+ const property = ast.ObjectEntry.getPropertyName(entry);
39
+ if (!property) {
40
+ return false;
41
+ }
42
+
43
+ // Currently, we handle values like `'8px'` (a 'Literal') or `token('space.100')` (a 'CallExpression')
44
+ if (isNodeOfType(value, 'Literal')) {
45
+ if (!value.value) {
46
+ return false;
47
+ }
48
+ const valueString = value.value.toString();
49
+ if (!supportedStylesMap[property] || !supportedStylesMap[property][valueString]) {
50
+ return false;
51
+ }
52
+ } else if (isNodeOfType(value, 'CallExpression')) {
53
+ // If it's a function call, then make sure it's the `token` function (and we have `css-property-with-tokens` enabled)
54
+
55
+ // Short-circuit when token calls are found but pattern is not enabled in config
56
+ if (isTokenCall(value) && !config.patterns.includes('css-property-with-tokens')) {
57
+ return false;
58
+ }
59
+
60
+ // Bail if it's a function, but that function call is not `tokens('<token>')`
61
+ if (!isTokenCall(value)) {
62
+ return false;
63
+ }
64
+ } else {
65
+ return false;
66
+ }
67
+
68
+ // Bail if dimension property is found but pattern is not enabled in config
69
+ // Note: We don't need to exhaustively re-check the key/value pair here. That's already been done above.
70
+ // This is just to check wether 'dimension-properties' pattern is enabled and can be removed when the pattern is completely rolled out
71
+ const isDimensionProperty = !!supportedDimensionAttributesMap[property];
72
+ if (isDimensionProperty && !config.patterns.includes('dimension-properties')) {
73
+ return false;
74
+ }
75
+ return true;
76
+ });
77
+ };
78
+ const isTokenCall = node => {
79
+ // Is it a function call?
80
+ if (!isNodeOfType(node, 'CallExpression')) {
81
+ return false;
82
+ }
83
+
84
+ // Is the function called 'token'?
85
+ if (ast.FunctionCall.getName(node) !== 'token') {
86
+ return false;
87
+ }
88
+ const token = ast.FunctionCall.getArgumentAtPos(node, 0);
89
+ const fallback = ast.FunctionCall.getArgumentAtPos(node, 1);
90
+ if (!token || token.type !== 'Literal') {
91
+ return false;
92
+ }
93
+
94
+ // Is the token one that we understand
95
+ if (!Object.values(spaceTokenMap).includes(token.value)) {
96
+ return false;
97
+ }
98
+
99
+ // Not all `token()` calls have a fall back. This is fine, but if there is a fallback, make sure it's the same as the fallback xcss will use
100
+ if (fallback && fallback.type === 'Literal') {
101
+ if (spaceTokenMap[fallback.value] !== token.value) {
102
+ return false;
103
+ }
104
+ }
105
+ return true;
106
+ };
@@ -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, isStyledComponents } from '../is-supported-import';
4
+ import { getImportSources, isEmotion } 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,7 +35,6 @@ 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);
39
38
  context.report({
40
39
  messageId,
41
40
  node,
@@ -77,12 +76,22 @@ export const createNoTaggedTemplateExpressionRule = (isUsage, messageId) => cont
77
76
  return;
78
77
  }
79
78
 
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) || /\$\{.*\(.*:[\s]*\{/.test(newCode)) {
79
+ // For styles like `position: initial !important`,
80
+ // Emotion can give typechecking errors when using object syntax
81
+ // due to csstype being overly strict
82
+ const usesEmotion = isEmotion(node.tag, references, importSources);
83
+ if (usesEmotion && !!newCode.match(/!\s*important/gm)) {
84
+ return;
85
+ }
86
+
87
+ // For styled-components, we might also want to similarly disallow or autofix `styled.div({ color: props => props.color })` as it's broken too (both type and functionality). This is tracked in https://product-fabric.atlassian.net/browse/USS-26.
88
+ if (/\$\{.*:[\s]*\{/.test(newCode)) {
83
89
  /**
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.
90
+ * If we find a variable in a selector, we skip it. There are two reasons:
91
+ *
92
+ * - `styled-components@3.x` does not support variables in a selector (see the first example).
93
+ *
94
+ * - We cannot guarantee that the contents of an function call is actually a selector, and not a CSS block (see the third example).
86
95
  *
87
96
  * @examples
88
97
  * ```tsx
@@ -90,11 +99,21 @@ export const createNoTaggedTemplateExpressionRule = (isUsage, messageId) => cont
90
99
  * & + ${Button} { color: red; }
91
100
  * `;
92
101
  * ```
93
- *```tsx
102
+ *
103
+ * ```tsx
94
104
  * const Component = styled.div`
95
105
  * ${mixin()} button { color: red; }
96
106
  * `;
97
107
  * ```
108
+ *
109
+ * ```tsx
110
+ * const styles = `&:active { color: blue; }`;
111
+ * const Component = styled.div`
112
+ * ${styles} &:hover {
113
+ * color: red;
114
+ * }
115
+ * `;
116
+ * ```
98
117
  */
99
118
  return;
100
119
  }
@@ -110,7 +110,7 @@ export const isKeyframes = isSupportedImportWrapper('keyframes');
110
110
  // `styled` is also the explicit default of `styled-components` and `@emotion/styled`, so we also match on default imports generally
111
111
  export const isStyled = isSupportedImportWrapper('styled', ['styled-components', '@emotion/styled']);
112
112
  export const isImportedFrom = (moduleName, exactMatch = true) => (nodeToCheck, referencesInScope, importSources) => {
113
- if (!importSources.includes(moduleName)) {
113
+ if (!importSources.some(importSource => importSource === moduleName || !exactMatch && importSource.startsWith(moduleName))) {
114
114
  // Don't go through the trouble of checking the import sources does not include this
115
115
  // We'll assume this is skipped elsewhere.
116
116
  return false;
@@ -1,3 +1,4 @@
1
+ import { isNodeOfType } from 'eslint-codemod-utils';
1
2
  var ObjectEntry = {
2
3
  deleteEntry: function deleteEntry(node, context, fixer) {
3
4
  var _lastToken;
@@ -17,6 +18,18 @@ var ObjectEntry = {
17
18
  lastToken = sourceCode.getTokenBefore(lastToken);
18
19
  }
19
20
  return fixer.removeRange([prevToken.range[1], lastToken.range[1]]);
21
+ },
22
+ getPropertyName: function getPropertyName(node) {
23
+ // SpreadElements don't really have a property name
24
+ if (!isNodeOfType(node, 'Property')) {
25
+ return undefined;
26
+ }
27
+ if (isNodeOfType(node.key, 'Literal')) {
28
+ return node.key.raw;
29
+ }
30
+ if (isNodeOfType(node.key, 'Identifier')) {
31
+ return node.key.name;
32
+ }
20
33
  }
21
34
  };
22
35
  export { ObjectEntry };
@@ -4,6 +4,7 @@ import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray";
4
4
  import { isNodeOfType } from 'eslint-codemod-utils';
5
5
  import * as ast from '../../../../ast-nodes';
6
6
  import { isValidCssPropertiesToTransform } from '../../utils';
7
+ import { validateStyles } from '../../utils/validate-styles';
7
8
  import { convertJsxCallSite } from './convert-jsx-call-site';
8
9
  import { convertStyledComponentToXcss } from './convert-styled-component-call-to-jsx';
9
10
  import { findValidJsxUsageToTransform } from './find-valid-jsx-usage-to-transform';
@@ -50,7 +51,7 @@ export var CompiledStyled = {
50
51
  };
51
52
  }
52
53
  var styledComponentVariableRef = findValidStyledComponentCall(node);
53
- if (!styledComponentVariableRef || !isNodeOfType(styledComponentVariableRef.id, 'Identifier') || !isValidCssPropertiesToTransform(node, config)) {
54
+ if (!styledComponentVariableRef || !isNodeOfType(styledComponentVariableRef.id, 'Identifier') || !(config.patterns.includes('string-style-property-fix') ? validateStyles(node, config) : isValidCssPropertiesToTransform(node, config))) {
54
55
  return {
55
56
  success: false
56
57
  };
@@ -4,6 +4,7 @@ import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray";
4
4
  import { getIdentifierInParentScope, isNodeOfType } from 'eslint-codemod-utils';
5
5
  import * as ast from '../../../../ast-nodes';
6
6
  import { getVariableDefinitionValue, getVariableUsagesCount, isValidCssPropertiesToTransform } from '../../utils';
7
+ import { validateStyles } from '../../utils/validate-styles';
7
8
  import { cssToXcssTransformer } from '../css-to-xcss';
8
9
  import * as supported from './supported';
9
10
  import { upsertImportDeclaration } from './upsert-import-declaration';
@@ -34,7 +35,6 @@ export var EmotionCSS = {
34
35
  });
35
36
  },
36
37
  _check: function _check(node, _ref2) {
37
- var _cssVariableValue$nod, _cssVariableValue$nod2;
38
38
  var context = _ref2.context,
39
39
  config = _ref2.config;
40
40
  if (!config.patterns.includes('compiled-css-function')) {
@@ -81,10 +81,10 @@ export var EmotionCSS = {
81
81
  var cssVariableDefinition = getIdentifierInParentScope(context.getScope(), cssAttrValue.value);
82
82
  var cssVariableValue = getVariableDefinitionValue(cssVariableDefinition);
83
83
  // Check if `cssVariableValue` is a function called `css()`
84
- if (ast.FunctionCall.getName(cssVariableValue === null || cssVariableValue === void 0 || (_cssVariableValue$nod = cssVariableValue.node) === null || _cssVariableValue$nod === void 0 ? void 0 : _cssVariableValue$nod.init) !== 'css') {
84
+ if (ast.FunctionCall.getName(cssVariableValue === null || cssVariableValue === void 0 ? void 0 : cssVariableValue.node.init) !== 'css') {
85
85
  return false;
86
86
  }
87
- if (!isValidCssPropertiesToTransform(cssVariableValue === null || cssVariableValue === void 0 || (_cssVariableValue$nod2 = cssVariableValue.node) === null || _cssVariableValue$nod2 === void 0 ? void 0 : _cssVariableValue$nod2.init, config)) {
87
+ if (!(config.patterns.includes('string-style-property-fix') ? validateStyles(cssVariableValue === null || cssVariableValue === void 0 ? void 0 : cssVariableValue.node.init, config) : isValidCssPropertiesToTransform(cssVariableValue === null || cssVariableValue === void 0 ? void 0 : cssVariableValue.node.init, config))) {
88
88
  return false;
89
89
  }
90
90
  var importDeclaration = ast.Root.findImportsByModule(context.getSourceCode().ast.body, '@atlaskit/primitives');
@@ -0,0 +1,104 @@
1
+ import { isNodeOfType } from 'eslint-codemod-utils';
2
+ import * as ast from '../../../ast-nodes';
3
+ import { spaceTokenMap, supportedStylesMap } from '../transformers';
4
+ import { supportedDimensionAttributesMap } from '../transformers/css-to-xcss';
5
+ export var validateStyles = function validateStyles(node, config) {
6
+ if (!node) {
7
+ return false;
8
+ }
9
+ var cssObjectExpression = node.arguments[0];
10
+ // Bail on empty object calls
11
+ if (!isNodeOfType(cssObjectExpression, 'ObjectExpression')) {
12
+ return false;
13
+ }
14
+ if (!ast.Object.isFlat(cssObjectExpression)) {
15
+ return false;
16
+ }
17
+ var entries = ast.Object.getEntries(cssObjectExpression);
18
+ if (entries.length === 0) {
19
+ return false;
20
+ }
21
+ if (!config.patterns.includes('multiple-properties') && entries.length > 1) {
22
+ return false;
23
+ }
24
+ return entries.every(function (entry) {
25
+ // Bail on SpreadElements
26
+ if (!isNodeOfType(entry, 'Property')) {
27
+ return false;
28
+ }
29
+ var key = entry.key,
30
+ value = entry.value;
31
+
32
+ // Bail if the property is not something we understand, e.g. `{ [SOME_CONSTANT]: '8px' }`
33
+ if (!isNodeOfType(key, 'Identifier')) {
34
+ return false;
35
+ }
36
+ var property = ast.ObjectEntry.getPropertyName(entry);
37
+ if (!property) {
38
+ return false;
39
+ }
40
+
41
+ // Currently, we handle values like `'8px'` (a 'Literal') or `token('space.100')` (a 'CallExpression')
42
+ if (isNodeOfType(value, 'Literal')) {
43
+ if (!value.value) {
44
+ return false;
45
+ }
46
+ var valueString = value.value.toString();
47
+ if (!supportedStylesMap[property] || !supportedStylesMap[property][valueString]) {
48
+ return false;
49
+ }
50
+ } else if (isNodeOfType(value, 'CallExpression')) {
51
+ // If it's a function call, then make sure it's the `token` function (and we have `css-property-with-tokens` enabled)
52
+
53
+ // Short-circuit when token calls are found but pattern is not enabled in config
54
+ if (isTokenCall(value) && !config.patterns.includes('css-property-with-tokens')) {
55
+ return false;
56
+ }
57
+
58
+ // Bail if it's a function, but that function call is not `tokens('<token>')`
59
+ if (!isTokenCall(value)) {
60
+ return false;
61
+ }
62
+ } else {
63
+ return false;
64
+ }
65
+
66
+ // Bail if dimension property is found but pattern is not enabled in config
67
+ // Note: We don't need to exhaustively re-check the key/value pair here. That's already been done above.
68
+ // This is just to check wether 'dimension-properties' pattern is enabled and can be removed when the pattern is completely rolled out
69
+ var isDimensionProperty = !!supportedDimensionAttributesMap[property];
70
+ if (isDimensionProperty && !config.patterns.includes('dimension-properties')) {
71
+ return false;
72
+ }
73
+ return true;
74
+ });
75
+ };
76
+ var isTokenCall = function isTokenCall(node) {
77
+ // Is it a function call?
78
+ if (!isNodeOfType(node, 'CallExpression')) {
79
+ return false;
80
+ }
81
+
82
+ // Is the function called 'token'?
83
+ if (ast.FunctionCall.getName(node) !== 'token') {
84
+ return false;
85
+ }
86
+ var token = ast.FunctionCall.getArgumentAtPos(node, 0);
87
+ var fallback = ast.FunctionCall.getArgumentAtPos(node, 1);
88
+ if (!token || token.type !== 'Literal') {
89
+ return false;
90
+ }
91
+
92
+ // Is the token one that we understand
93
+ if (!Object.values(spaceTokenMap).includes(token.value)) {
94
+ return false;
95
+ }
96
+
97
+ // Not all `token()` calls have a fall back. This is fine, but if there is a fallback, make sure it's the same as the fallback xcss will use
98
+ if (fallback && fallback.type === 'Literal') {
99
+ if (spaceTokenMap[fallback.value] !== token.value) {
100
+ return false;
101
+ }
102
+ }
103
+ return true;
104
+ };
@@ -2,7 +2,7 @@ import _regeneratorRuntime from "@babel/runtime/regenerator";
2
2
  // 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
3
3
  // eslint-disable-next-line import/no-extraneous-dependencies
4
4
 
5
- import { getImportSources, isStyledComponents } from '../is-supported-import';
5
+ import { getImportSources, isEmotion } from '../is-supported-import';
6
6
  import { generate } from './generate';
7
7
  import { getTaggedTemplateExpressionOffset } from './get-tagged-template-expression-offset';
8
8
  import { toArguments } from './to-arguments';
@@ -36,12 +36,11 @@ export var createNoTaggedTemplateExpressionRule = function createNoTaggedTemplat
36
36
  if (!isUsage(node.tag, references, importSources)) {
37
37
  return;
38
38
  }
39
- var isSC = isStyledComponents(node.tag, references, importSources);
40
39
  context.report({
41
40
  messageId: messageId,
42
41
  node: node,
43
42
  fix: /*#__PURE__*/_regeneratorRuntime.mark(function fix(fixer) {
44
- var quasi, source, args, oldCode, withoutQuasi, newCode;
43
+ var quasi, source, args, oldCode, withoutQuasi, newCode, usesEmotion;
45
44
  return _regeneratorRuntime.wrap(function fix$(_context) {
46
45
  while (1) switch (_context.prev = _context.next) {
47
46
  case 0:
@@ -88,18 +87,28 @@ export var createNoTaggedTemplateExpressionRule = function createNoTaggedTemplat
88
87
  }
89
88
  return _context.abrupt("return");
90
89
  case 16:
91
- if (!(isSC && /\$\{.*:[\s]*\{/.test(newCode) || /\$\{.*\(.*:[\s]*\{/.test(newCode))) {
92
- _context.next = 18;
90
+ // For styles like `position: initial !important`,
91
+ // Emotion can give typechecking errors when using object syntax
92
+ // due to csstype being overly strict
93
+ usesEmotion = isEmotion(node.tag, references, importSources);
94
+ if (!(usesEmotion && !!newCode.match(/!\s*important/gm))) {
95
+ _context.next = 19;
93
96
  break;
94
97
  }
95
98
  return _context.abrupt("return");
96
- case 18:
97
- _context.next = 20;
99
+ case 19:
100
+ if (!/\$\{.*:[\s]*\{/.test(newCode)) {
101
+ _context.next = 21;
102
+ break;
103
+ }
104
+ return _context.abrupt("return");
105
+ case 21:
106
+ _context.next = 23;
98
107
  return fixer.insertTextBefore(node, newCode);
99
- case 20:
100
- _context.next = 22;
108
+ case 23:
109
+ _context.next = 25;
101
110
  return fixer.remove(node);
102
- case 22:
111
+ case 25:
103
112
  case "end":
104
113
  return _context.stop();
105
114
  }
@@ -115,7 +115,9 @@ export var isStyled = isSupportedImportWrapper('styled', ['styled-components', '
115
115
  export var isImportedFrom = function isImportedFrom(moduleName) {
116
116
  var exactMatch = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
117
117
  return function (nodeToCheck, referencesInScope, importSources) {
118
- if (!importSources.includes(moduleName)) {
118
+ if (!importSources.some(function (importSource) {
119
+ return importSource === moduleName || !exactMatch && importSource.startsWith(moduleName);
120
+ })) {
119
121
  // Don't go through the trouble of checking the import sources does not include this
120
122
  // We'll assume this is skipped elsewhere.
121
123
  return false;
@@ -1,6 +1,7 @@
1
1
  import type { Rule } from 'eslint';
2
- import type { Property, SpreadElement } from 'eslint-codemod-utils';
2
+ import { Property, SpreadElement } from 'eslint-codemod-utils';
3
3
  declare const ObjectEntry: {
4
4
  deleteEntry(node: Property | SpreadElement, context: Rule.RuleContext, fixer: Rule.RuleFixer): Rule.Fix;
5
+ getPropertyName(node: Property | SpreadElement): string | undefined;
5
6
  };
6
7
  export { ObjectEntry };
@@ -1,4 +1,4 @@
1
- type Pattern = 'compiled-css-function' | 'compiled-styled-object' | 'css-template-literal' | 'css-property-with-tokens' | 'css-property-multiple-values' | 'multiple-properties' | 'dimension-properties' | 'jsx-order-fix';
1
+ type Pattern = 'compiled-css-function' | 'compiled-styled-object' | 'css-template-literal' | 'css-property-with-tokens' | 'css-property-multiple-values' | 'multiple-properties' | 'dimension-properties' | 'jsx-order-fix' | 'string-style-property-fix';
2
2
  export interface RuleConfig {
3
3
  patterns: Pattern[];
4
4
  }
@@ -0,0 +1,3 @@
1
+ import { CallExpression } from 'eslint-codemod-utils';
2
+ import { RuleConfig } from '../config';
3
+ export declare const validateStyles: (node: CallExpression, config: RuleConfig) => boolean;
@@ -1,6 +1,7 @@
1
1
  import type { Rule } from 'eslint';
2
- import type { Property, SpreadElement } from 'eslint-codemod-utils';
2
+ import { Property, SpreadElement } from 'eslint-codemod-utils';
3
3
  declare const ObjectEntry: {
4
4
  deleteEntry(node: Property | SpreadElement, context: Rule.RuleContext, fixer: Rule.RuleFixer): Rule.Fix;
5
+ getPropertyName(node: Property | SpreadElement): string | undefined;
5
6
  };
6
7
  export { ObjectEntry };
@@ -1,4 +1,4 @@
1
- type Pattern = 'compiled-css-function' | 'compiled-styled-object' | 'css-template-literal' | 'css-property-with-tokens' | 'css-property-multiple-values' | 'multiple-properties' | 'dimension-properties' | 'jsx-order-fix';
1
+ type Pattern = 'compiled-css-function' | 'compiled-styled-object' | 'css-template-literal' | 'css-property-with-tokens' | 'css-property-multiple-values' | 'multiple-properties' | 'dimension-properties' | 'jsx-order-fix' | 'string-style-property-fix';
2
2
  export interface RuleConfig {
3
3
  patterns: Pattern[];
4
4
  }
@@ -0,0 +1,3 @@
1
+ import { CallExpression } from 'eslint-codemod-utils';
2
+ import { RuleConfig } from '../config';
3
+ export declare const validateStyles: (node: CallExpression, config: RuleConfig) => boolean;
package/package.json CHANGED
@@ -1,8 +1,9 @@
1
1
  {
2
2
  "name": "@atlaskit/eslint-plugin-design-system",
3
3
  "description": "The essential plugin for use with the Atlassian Design System.",
4
- "version": "8.37.0",
4
+ "version": "8.37.2",
5
5
  "author": "Atlassian Pty Ltd",
6
+ "license": "Apache-2.0",
6
7
  "publishConfig": {
7
8
  "registry": "https://registry.npmjs.org/"
8
9
  },