@atlaskit/eslint-plugin-design-system 8.19.1 → 8.20.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 (35) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/constellation/index/usage.mdx +1 -1
  3. package/dist/cjs/rules/consistent-css-prop-usage/index.js +13 -4
  4. package/dist/cjs/rules/use-primitives/config/index.js +1 -1
  5. package/dist/cjs/rules/use-primitives/index.js +5 -6
  6. package/dist/cjs/rules/use-primitives/transformers/css-to-xcss.js +21 -7
  7. package/dist/cjs/rules/use-primitives/utils/convert-ast-object-expression-to-js-object.js +32 -19
  8. package/dist/cjs/rules/use-primitives/utils/find-valid-styled-component-call.js +1 -0
  9. package/dist/cjs/rules/use-primitives/utils/get-variable-usage-count.js +1 -0
  10. package/dist/cjs/rules/use-primitives/utils/is-valid-css-properties-to-transform.js +32 -8
  11. package/dist/es2019/rules/consistent-css-prop-usage/index.js +12 -4
  12. package/dist/es2019/rules/use-primitives/config/index.js +1 -1
  13. package/dist/es2019/rules/use-primitives/index.js +5 -6
  14. package/dist/es2019/rules/use-primitives/transformers/css-to-xcss.js +21 -7
  15. package/dist/es2019/rules/use-primitives/utils/convert-ast-object-expression-to-js-object.js +31 -19
  16. package/dist/es2019/rules/use-primitives/utils/find-valid-styled-component-call.js +1 -0
  17. package/dist/es2019/rules/use-primitives/utils/get-variable-usage-count.js +1 -0
  18. package/dist/es2019/rules/use-primitives/utils/is-valid-css-properties-to-transform.js +27 -8
  19. package/dist/esm/rules/consistent-css-prop-usage/index.js +13 -4
  20. package/dist/esm/rules/use-primitives/config/index.js +1 -1
  21. package/dist/esm/rules/use-primitives/index.js +5 -6
  22. package/dist/esm/rules/use-primitives/transformers/css-to-xcss.js +21 -7
  23. package/dist/esm/rules/use-primitives/utils/convert-ast-object-expression-to-js-object.js +31 -19
  24. package/dist/esm/rules/use-primitives/utils/find-valid-styled-component-call.js +1 -0
  25. package/dist/esm/rules/use-primitives/utils/get-variable-usage-count.js +1 -0
  26. package/dist/esm/rules/use-primitives/utils/is-valid-css-properties-to-transform.js +31 -8
  27. package/dist/types/rules/use-primitives/config/index.d.ts +3 -1
  28. package/dist/types/rules/use-primitives/utils/convert-ast-object-expression-to-js-object.d.ts +11 -2
  29. package/dist/types/rules/use-primitives/utils/get-variable-usage-count.d.ts +1 -0
  30. package/dist/types/rules/use-primitives/utils/is-valid-css-properties-to-transform.d.ts +2 -1
  31. package/dist/types-ts4.5/rules/use-primitives/config/index.d.ts +3 -1
  32. package/dist/types-ts4.5/rules/use-primitives/utils/convert-ast-object-expression-to-js-object.d.ts +11 -2
  33. package/dist/types-ts4.5/rules/use-primitives/utils/get-variable-usage-count.d.ts +1 -0
  34. package/dist/types-ts4.5/rules/use-primitives/utils/is-valid-css-properties-to-transform.d.ts +2 -1
  35. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # @atlaskit/eslint-plugin-design-system
2
2
 
3
+ ## 8.20.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#66409](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/pull-requests/66409) [`f6c48f4a67c1`](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/commits/f6c48f4a67c1) - Implemented functionality for the consistent-css-prop-usage rule to account for cssMap usages
8
+
9
+ ### Patch Changes
10
+
11
+ - [#66604](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/pull-requests/66604) [`3205b1daf57f`](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/commits/3205b1daf57f) - Refactor internal logic of `use-primitives` to better handle cases it will not suggest transformations for, based on the value of CSS properties.
12
+
13
+ ## 8.19.2
14
+
15
+ ### Patch Changes
16
+
17
+ - [#66118](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/pull-requests/66118) [`93988e6fd035`](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/commits/93988e6fd035) - `use-primitives` now handles tokenised padding/margin properties. This change is guarded by a config flag and not enabled by default.
18
+
3
19
  ## 8.19.1
4
20
 
5
21
  ### Patch Changes
@@ -142,7 +142,7 @@ This rule comes with options to support different repository configurations.
142
142
 
143
143
  #### cssFunctions
144
144
 
145
- An array of function names the linting rule should target. Defaults to `['css', 'xcss']`.
145
+ An array of function names the linting rule should target. Defaults to `['css', 'xcss']`. Functionality of cssMap will be linted regardless of the configuration of `cssFunctions` as it can be used with either attribute.
146
146
 
147
147
  #### stylesPlacement
148
148
 
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", {
6
6
  });
7
7
  exports.default = void 0;
8
8
  var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
9
+ var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
9
10
  var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
10
11
  var _eslintCodemodUtils = require("eslint-codemod-utils");
11
12
  var _assign = _interopRequireDefault(require("lodash/assign"));
@@ -25,9 +26,9 @@ function findSpreadProperties(node) {
25
26
  });
26
27
  }
27
28
  function analyzeIdentifier(context, sourceIdentifier, configuration) {
28
- var _getIdentifierInParen;
29
+ var _getIdentifierInParen, _getIdentifierInParen2;
29
30
  var scope = context.getScope();
30
- var _ref = ((_getIdentifierInParen = (0, _eslintCodemodUtils.getIdentifierInParentScope)(scope, sourceIdentifier.name)) === null || _getIdentifierInParen === void 0 ? void 0 : _getIdentifierInParen.identifiers) || [],
31
+ var _ref = (_getIdentifierInParen = (_getIdentifierInParen2 = (0, _eslintCodemodUtils.getIdentifierInParentScope)(scope, sourceIdentifier.name)) === null || _getIdentifierInParen2 === void 0 ? void 0 : _getIdentifierInParen2.identifiers) !== null && _getIdentifierInParen !== void 0 ? _getIdentifierInParen : [],
31
32
  _ref2 = (0, _slicedToArray2.default)(_ref, 1),
32
33
  identifier = _ref2[0];
33
34
  if (!identifier || !identifier.parent) {
@@ -50,7 +51,7 @@ function analyzeIdentifier(context, sourceIdentifier, configuration) {
50
51
  });
51
52
  return;
52
53
  }
53
- if (identifier.parent && identifier.parent.init && !isCssCallExpression(identifier.parent.init, configuration.cssFunctions)) {
54
+ if (identifier.parent && identifier.parent.init && !isCssCallExpression(identifier.parent.init, [].concat((0, _toConsumableArray2.default)(configuration.cssFunctions), ['cssMap']))) {
54
55
  // When variable value is not of type css({})
55
56
  context.report({
56
57
  node: identifier,
@@ -69,6 +70,13 @@ function analyzeIdentifier(context, sourceIdentifier, configuration) {
69
70
  });
70
71
  }
71
72
  }
73
+
74
+ /**
75
+ * Handle different cases based on what's been passed in the css-related JSXAttribute
76
+ * @param context the context of the node
77
+ * @param expression the expression of the JSXAttribute value
78
+ * @param configuration what css-related functions to account for (eg. css, xcss, cssMap), and whether to detect bottom vs top expressions
79
+ */
72
80
  var traverseExpressionWithConfig = function traverseExpressionWithConfig(context, expression, configuration) {
73
81
  function traverseExpression(expression) {
74
82
  switch (expression.type) {
@@ -140,7 +148,8 @@ var rule = (0, _createRule.createLintRule)({
140
148
  create: function create(context) {
141
149
  var _ref3;
142
150
  var mergedConfig = (0, _assign.default)({}, defaultConfig, context.options[0]);
143
- var callSelector = mergedConfig.cssFunctions.map(function (fn) {
151
+ var callSelectorFunctions = [].concat((0, _toConsumableArray2.default)(mergedConfig.cssFunctions), ['cssMap']);
152
+ var callSelector = callSelectorFunctions.map(function (fn) {
144
153
  return "CallExpression[callee.name=".concat(fn, "]");
145
154
  }).join(',');
146
155
  return _ref3 = {}, (0, _defineProperty2.default)(_ref3, callSelector, function (node) {
@@ -5,7 +5,7 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.getConfig = void 0;
7
7
  var defaults = {
8
- patterns: ['div-object-css']
8
+ patterns: ['compiled-css-function']
9
9
  };
10
10
  var getConfig = exports.getConfig = function getConfig(overrides) {
11
11
  // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign
@@ -37,7 +37,7 @@ var rule = (0, _createRule.createLintRule)({
37
37
  return;
38
38
  }
39
39
  var styledComponentVariableRef = (0, _utils.findValidStyledComponentCall)(node);
40
- if (!styledComponentVariableRef || !(0, _eslintCodemodUtils.isNodeOfType)(styledComponentVariableRef.id, 'Identifier') || !(0, _utils.isValidCssPropertiesToTransform)(node)) {
40
+ if (!styledComponentVariableRef || !(0, _eslintCodemodUtils.isNodeOfType)(styledComponentVariableRef.id, 'Identifier') || !(0, _utils.isValidCssPropertiesToTransform)(node, config)) {
41
41
  return;
42
42
  }
43
43
  var styledComponentJsxRef = (0, _utils.findValidJsxUsageToTransform)(styledComponentVariableRef.id.name, context.getScope());
@@ -60,7 +60,9 @@ var rule = (0, _createRule.createLintRule)({
60
60
  },
61
61
  // transforms <div css={...}> usages
62
62
  JSXOpeningElement: function JSXOpeningElement(node) {
63
- var config = (0, _config.getConfig)(context.options[0]);
63
+ if (!config.patterns.includes('compiled-css-function')) {
64
+ return false;
65
+ }
64
66
  if (!(0, _eslintCodemodUtils.isNodeOfType)(node, 'JSXOpeningElement')) {
65
67
  return;
66
68
  }
@@ -135,9 +137,6 @@ var shouldSuggestBox = function shouldSuggestBox(node, context, config
135
137
  if (!cssVariableValue || !(0, _utils.isFunctionNamed)(cssVariableValue, 'css')) {
136
138
  return false;
137
139
  }
138
- if (!config.patterns.includes('div-object-css')) {
139
- return false;
140
- }
141
- return (0, _utils.isValidCssPropertiesToTransform)(cssVariableValue.node.init);
140
+ return (0, _utils.isValidCssPropertiesToTransform)(cssVariableValue.node.init, config);
142
141
  };
143
142
  var _default = exports.default = rule;
@@ -39,7 +39,7 @@ var cssToXcssTransformer = exports.cssToXcssTransformer = function cssToXcssTran
39
39
  fixer.replaceText(cssVariableValue.node.init.callee, (0, _eslintCodemodUtils.identifier)('xcss').toString())].concat((0, _toConsumableArray2.default)(styledObjectToXcssTokens(cssObjectExpression, fixer)));
40
40
  };
41
41
 
42
- // Update css object values to xcss values. e.g. `'8px'` -> `'space.100'`
42
+ // Update css object values to xcss values
43
43
  // Note: `properties` in this context is a group of AST nodes that make up a key/value pair in an object.
44
44
  // e.g. `padding: '8px'`. For clarity, it's renamed to `entry` inside the `.map()`.
45
45
  var styledObjectToXcssTokens = exports.styledObjectToXcssTokens = function styledObjectToXcssTokens(styles, fixer) {
@@ -50,16 +50,30 @@ var styledObjectToXcssTokens = exports.styledObjectToXcssTokens = function style
50
50
  if (!(0, _eslintCodemodUtils.isNodeOfType)(entry.key, 'Identifier')) {
51
51
  return;
52
52
  }
53
- if (!(0, _eslintCodemodUtils.isNodeOfType)(entry.value, 'Literal')) {
54
- return;
53
+
54
+ // maps literal values like: 8px to 'space.100'
55
+ if ((0, _eslintCodemodUtils.isNodeOfType)(entry.value, 'Literal')) {
56
+ var value = entry.value.value;
57
+ if (typeof value !== 'string') {
58
+ return;
59
+ }
60
+ return fixer.replaceText(entry.value, (0, _eslintCodemodUtils.literal)("'".concat(supportedStylesMap[entry.key.name][value], "'")).toString());
55
61
  }
56
- var value = entry.value.value;
57
- if (typeof value !== 'string') {
58
- return;
62
+ // maps token calls like: token('space.100') to 'space.100'
63
+ if ((0, _eslintCodemodUtils.isNodeOfType)(entry.value, 'CallExpression')) {
64
+ var callExpression = entry.value;
65
+ // skip if not a call to `token`
66
+ if (!(0, _eslintCodemodUtils.isNodeOfType)(callExpression.callee, 'Identifier') || callExpression.callee.name !== 'token' || !(0, _eslintCodemodUtils.isNodeOfType)(callExpression.arguments[0], 'Literal')) {
67
+ return;
68
+ }
69
+ // the first argument of `token` is the token name and
70
+ // can be given directly to `xcss` as it has been validated already.
71
+ return fixer.replaceText(entry.value, (0, _eslintCodemodUtils.literal)("'".concat(callExpression.arguments[0].value, "'")).toString());
59
72
  }
60
- return fixer.replaceText(entry.value, (0, _eslintCodemodUtils.literal)("'".concat(supportedStylesMap[entry.key.name][value], "'")).toString());
61
73
  });
62
74
  };
75
+
76
+ // TODO: https://product-fabric.atlassian.net/browse/DSP-16054
63
77
  var spaceTokenMap = exports.spaceTokenMap = {
64
78
  '0px': 'space.0',
65
79
  '2px': 'space.025',
@@ -3,31 +3,23 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.convertASTObjectExpressionToJSObject = void 0;
6
+ exports.convertASTObjectExpressionToJSObject = exports.SPREAD_SYNTAX = void 0;
7
7
  var _eslintCodemodUtils = require("eslint-codemod-utils");
8
- // FIXME: This is only loosely typed
9
-
8
+ var SPREAD_SYNTAX = exports.SPREAD_SYNTAX = Symbol('SPREAD_SYNTAX');
10
9
  /**
11
10
  * Note: Not recursive. Only handles top level key/value pairs
12
11
  */
13
12
  var convertASTObjectExpressionToJSObject = exports.convertASTObjectExpressionToJSObject = function convertASTObjectExpressionToJSObject(styles) {
14
- var styleObj = {};
13
+ var styleObj = {
14
+ unsupported: []
15
+ };
15
16
 
16
- // if we see any spread props we stop and return false to indicate this is unsupported
17
+ // if we see any spread props we indicate that as unsupported
17
18
  if (!styles.properties.every(function (prop) {
18
19
  return (0, _eslintCodemodUtils.isNodeOfType)(prop, 'Property');
19
20
  })) {
20
- return false;
21
+ styleObj.unsupported.push(SPREAD_SYNTAX);
21
22
  }
22
-
23
- // TODO: We need to harden this logic.
24
- // It currently generates a false positive for:
25
- // styled.div({
26
- // marginTop: "0px",
27
- // marginBottom: token("space.100", "8px"),
28
- // })
29
- // as the value for `marginBottom` is not a string, so it is just skipped
30
- // from the resulting map and this causes the rule to trigger when it shouldn't
31
23
  styles.properties.forEach(function (prop) {
32
24
  if (!(0, _eslintCodemodUtils.isNodeOfType)(prop, 'Property')) {
33
25
  return;
@@ -35,13 +27,34 @@ var convertASTObjectExpressionToJSObject = exports.convertASTObjectExpressionToJ
35
27
  if (!(0, _eslintCodemodUtils.isNodeOfType)(prop.key, 'Identifier')) {
36
28
  return;
37
29
  }
38
- if (!(0, _eslintCodemodUtils.isNodeOfType)(prop.value, 'Literal')) {
30
+
31
+ // a literal string value, the base case
32
+ if ((0, _eslintCodemodUtils.isNodeOfType)(prop.value, 'Literal') && typeof prop.value.value === 'string') {
33
+ styleObj[prop.key.name] = prop.value.value;
39
34
  return;
40
35
  }
41
- if (typeof prop.value.value !== 'string') {
42
- return;
36
+
37
+ // try to handle a direct call to `token`
38
+ if ((0, _eslintCodemodUtils.isNodeOfType)(prop.value, 'CallExpression')) {
39
+ var callExpression = prop.value;
40
+ // strictly handle calls to `token`
41
+ if ((0, _eslintCodemodUtils.isNodeOfType)(callExpression.callee, 'Identifier') && callExpression.callee.name === 'token') {
42
+ // only two valid cases are supported
43
+ // one argument => token('space.100')
44
+ // two arguments => token('space.100', '8px')
45
+ if ((callExpression.arguments.length === 1 || callExpression.arguments.length === 2) && (0, _eslintCodemodUtils.isNodeOfType)(callExpression.arguments[0], 'Literal') && (typeof callExpression.arguments[1] === 'undefined' || (0, _eslintCodemodUtils.isNodeOfType)(callExpression.arguments[1], 'Literal'))) {
46
+ var _callExpression$argum;
47
+ styleObj[prop.key.name] = {
48
+ tokenName: String(callExpression.arguments[0].value),
49
+ fallbackValue: (_callExpression$argum = callExpression.arguments[1]) !== null && _callExpression$argum !== void 0 && _callExpression$argum.value ? String(callExpression.arguments[1].value) : undefined
50
+ };
51
+ return;
52
+ }
53
+ }
43
54
  }
44
- styleObj[prop.key.name] = prop.value.value;
55
+
56
+ // if we get here we have an unsupported value
57
+ styleObj.unsupported.push(prop.key.name);
45
58
  });
46
59
  return styleObj;
47
60
  };
@@ -34,6 +34,7 @@ var findValidStyledComponentCall = exports.findValidStyledComponentCall = functi
34
34
  *
35
35
  * In the future it could be enhanced to double check `styled` and `styled2`
36
36
  * are Compiled imports but as is should work for the majority of use cases
37
+ * https://product-fabric.atlassian.net/browse/DSP-16058
37
38
  */
38
39
  var isStyledCallExpression = function isStyledCallExpression(call) {
39
40
  if (!(0, _eslintCodemodUtils.isNodeOfType)(call, 'CallExpression')) {
@@ -5,6 +5,7 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.getVariableUsagesCount = void 0;
7
7
  /**
8
+ * TODO: Update this logic: https://product-fabric.atlassian.net/browse/DSP-16059
8
9
  * Using Regex here because otherwise we'd need to traverse the entire AST
9
10
  * We should harden this logic as we go.
10
11
  */
@@ -1,21 +1,33 @@
1
1
  "use strict";
2
2
 
3
+ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
3
4
  Object.defineProperty(exports, "__esModule", {
4
5
  value: true
5
6
  });
6
7
  exports.isValidCssPropertiesToTransform = void 0;
8
+ var _typeof2 = _interopRequireDefault(require("@babel/runtime/helpers/typeof"));
9
+ var _objectWithoutProperties2 = _interopRequireDefault(require("@babel/runtime/helpers/objectWithoutProperties"));
7
10
  var _eslintCodemodUtils = require("eslint-codemod-utils");
8
11
  var _cssToXcss = require("../transformers/css-to-xcss");
9
12
  var _convertAstObjectExpressionToJsObject = require("./convert-ast-object-expression-to-js-object");
10
- var isValidCssPropertiesToTransform = exports.isValidCssPropertiesToTransform = function isValidCssPropertiesToTransform(node) {
13
+ var _excluded = ["unsupported"];
14
+ var isValidCssPropertiesToTransform = exports.isValidCssPropertiesToTransform = function isValidCssPropertiesToTransform(node, config) {
11
15
  var cssObjectExpression = node.arguments[0];
12
16
  // Bail on empty object calls
13
17
  if (!cssObjectExpression || !(0, _eslintCodemodUtils.isNodeOfType)(cssObjectExpression, 'ObjectExpression')) {
14
18
  return false;
15
19
  }
16
- var cssObject = (0, _convertAstObjectExpressionToJsObject.convertASTObjectExpressionToJSObject)(cssObjectExpression);
17
- // Bail if there are less or more than 1 styles defined
18
- if (!cssObject || Object.keys(cssObject).length !== 1) {
20
+ var _convertASTObjectExpr = (0, _convertAstObjectExpressionToJsObject.convertASTObjectExpressionToJSObject)(cssObjectExpression),
21
+ unsupported = _convertASTObjectExpr.unsupported,
22
+ cssObject = (0, _objectWithoutProperties2.default)(_convertASTObjectExpr, _excluded);
23
+ // Bail if there are any unsupported styles
24
+ if (unsupported.length > 0 || Object.keys(cssObject).length !== 1) {
25
+ return false;
26
+ }
27
+ // Short-circuit when token calls are found but pattern is not enabled in config
28
+ if (!config.patterns.includes('css-property-with-tokens') && Object.values(cssObject).some(function (value) {
29
+ return (0, _typeof2.default)(value) === 'object' && value.tokenName;
30
+ })) {
19
31
  return false;
20
32
  }
21
33
 
@@ -25,10 +37,22 @@ var isValidCssPropertiesToTransform = exports.isValidCssPropertiesToTransform =
25
37
  // than the rule reporting on things that can't be mapped.
26
38
  var containsOnlyValidStyles = Object.keys(cssObject).every(function (styleProperty) {
27
39
  var styleValue = cssObject[styleProperty];
28
- return _cssToXcss.supportedStylesMap[styleProperty] &&
29
- // Is the key something we can map
30
- _cssToXcss.supportedStylesMap[styleProperty][styleValue] // Is the value something we can map
31
- ;
40
+
41
+ // token function call
42
+ if ((0, _typeof2.default)(styleValue) === 'object') {
43
+ // if there is no fallback value, we just map to the token name, if one is found
44
+ if (!styleValue.fallbackValue) {
45
+ return _cssToXcss.supportedStylesMap[styleProperty] && Object.values(_cssToXcss.supportedStylesMap[styleProperty]).includes(styleValue.tokenName);
46
+ }
47
+ // token with fallback
48
+ return _cssToXcss.supportedStylesMap[styleProperty] && _cssToXcss.supportedStylesMap[styleProperty][styleValue.fallbackValue] === styleValue.tokenName;
49
+ } else {
50
+ // direct value used
51
+ return _cssToXcss.supportedStylesMap[styleProperty] &&
52
+ // Is the key something we can map
53
+ _cssToXcss.supportedStylesMap[styleProperty][styleValue] // Is the value something we can map
54
+ ;
55
+ }
32
56
  });
33
57
 
34
58
  if (!containsOnlyValidStyles) {
@@ -14,9 +14,9 @@ function findSpreadProperties(node) {
14
14
  property.type === 'ExperimentalSpreadProperty');
15
15
  }
16
16
  function analyzeIdentifier(context, sourceIdentifier, configuration) {
17
- var _getIdentifierInParen;
17
+ var _getIdentifierInParen, _getIdentifierInParen2;
18
18
  const scope = context.getScope();
19
- const [identifier] = ((_getIdentifierInParen = getIdentifierInParentScope(scope, sourceIdentifier.name)) === null || _getIdentifierInParen === void 0 ? void 0 : _getIdentifierInParen.identifiers) || [];
19
+ const [identifier] = (_getIdentifierInParen = (_getIdentifierInParen2 = getIdentifierInParentScope(scope, sourceIdentifier.name)) === null || _getIdentifierInParen2 === void 0 ? void 0 : _getIdentifierInParen2.identifiers) !== null && _getIdentifierInParen !== void 0 ? _getIdentifierInParen : [];
20
20
  if (!identifier || !identifier.parent) {
21
21
  // Identifier isn't in the module, skip!
22
22
  return;
@@ -37,7 +37,7 @@ function analyzeIdentifier(context, sourceIdentifier, configuration) {
37
37
  });
38
38
  return;
39
39
  }
40
- if (identifier.parent && identifier.parent.init && !isCssCallExpression(identifier.parent.init, configuration.cssFunctions)) {
40
+ if (identifier.parent && identifier.parent.init && !isCssCallExpression(identifier.parent.init, [...configuration.cssFunctions, 'cssMap'])) {
41
41
  // When variable value is not of type css({})
42
42
  context.report({
43
43
  node: identifier,
@@ -56,6 +56,13 @@ function analyzeIdentifier(context, sourceIdentifier, configuration) {
56
56
  });
57
57
  }
58
58
  }
59
+
60
+ /**
61
+ * Handle different cases based on what's been passed in the css-related JSXAttribute
62
+ * @param context the context of the node
63
+ * @param expression the expression of the JSXAttribute value
64
+ * @param configuration what css-related functions to account for (eg. css, xcss, cssMap), and whether to detect bottom vs top expressions
65
+ */
59
66
  const traverseExpressionWithConfig = (context, expression, configuration) => {
60
67
  function traverseExpression(expression) {
61
68
  switch (expression.type) {
@@ -124,7 +131,8 @@ const rule = createLintRule({
124
131
  },
125
132
  create(context) {
126
133
  const mergedConfig = assign({}, defaultConfig, context.options[0]);
127
- const callSelector = mergedConfig.cssFunctions.map(fn => `CallExpression[callee.name=${fn}]`).join(',');
134
+ const callSelectorFunctions = [...mergedConfig.cssFunctions, 'cssMap'];
135
+ const callSelector = callSelectorFunctions.map(fn => `CallExpression[callee.name=${fn}]`).join(',');
128
136
  return {
129
137
  [callSelector]: node => {
130
138
  if (node.parent.type !== 'VariableDeclarator') {
@@ -1,5 +1,5 @@
1
1
  const defaults = {
2
- patterns: ['div-object-css']
2
+ patterns: ['compiled-css-function']
3
3
  };
4
4
  export const getConfig = overrides => {
5
5
  // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign
@@ -31,7 +31,7 @@ const rule = createLintRule({
31
31
  return;
32
32
  }
33
33
  const styledComponentVariableRef = findValidStyledComponentCall(node);
34
- if (!styledComponentVariableRef || !isNodeOfType(styledComponentVariableRef.id, 'Identifier') || !isValidCssPropertiesToTransform(node)) {
34
+ if (!styledComponentVariableRef || !isNodeOfType(styledComponentVariableRef.id, 'Identifier') || !isValidCssPropertiesToTransform(node, config)) {
35
35
  return;
36
36
  }
37
37
  const styledComponentJsxRef = findValidJsxUsageToTransform(styledComponentVariableRef.id.name, context.getScope());
@@ -54,7 +54,9 @@ const rule = createLintRule({
54
54
  },
55
55
  // transforms <div css={...}> usages
56
56
  JSXOpeningElement(node) {
57
- const config = getConfig(context.options[0]);
57
+ if (!config.patterns.includes('compiled-css-function')) {
58
+ return false;
59
+ }
58
60
  if (!isNodeOfType(node, 'JSXOpeningElement')) {
59
61
  return;
60
62
  }
@@ -129,9 +131,6 @@ const shouldSuggestBox = (node, context, config
129
131
  if (!cssVariableValue || !isFunctionNamed(cssVariableValue, 'css')) {
130
132
  return false;
131
133
  }
132
- if (!config.patterns.includes('div-object-css')) {
133
- return false;
134
- }
135
- return isValidCssPropertiesToTransform(cssVariableValue.node.init);
134
+ return isValidCssPropertiesToTransform(cssVariableValue.node.init, config);
136
135
  };
137
136
  export default rule;
@@ -31,7 +31,7 @@ export const cssToXcssTransformer = (node, context, fixer) => {
31
31
  fixer.replaceText(cssVariableValue.node.init.callee, identifier('xcss').toString()), ...styledObjectToXcssTokens(cssObjectExpression, fixer)];
32
32
  };
33
33
 
34
- // Update css object values to xcss values. e.g. `'8px'` -> `'space.100'`
34
+ // Update css object values to xcss values
35
35
  // Note: `properties` in this context is a group of AST nodes that make up a key/value pair in an object.
36
36
  // e.g. `padding: '8px'`. For clarity, it's renamed to `entry` inside the `.map()`.
37
37
  export const styledObjectToXcssTokens = (styles, fixer) => {
@@ -42,16 +42,30 @@ export const styledObjectToXcssTokens = (styles, fixer) => {
42
42
  if (!isNodeOfType(entry.key, 'Identifier')) {
43
43
  return;
44
44
  }
45
- if (!isNodeOfType(entry.value, 'Literal')) {
46
- return;
45
+
46
+ // maps literal values like: 8px to 'space.100'
47
+ if (isNodeOfType(entry.value, 'Literal')) {
48
+ const value = entry.value.value;
49
+ if (typeof value !== 'string') {
50
+ return;
51
+ }
52
+ return fixer.replaceText(entry.value, literal(`'${supportedStylesMap[entry.key.name][value]}'`).toString());
47
53
  }
48
- const value = entry.value.value;
49
- if (typeof value !== 'string') {
50
- return;
54
+ // maps token calls like: token('space.100') to 'space.100'
55
+ if (isNodeOfType(entry.value, 'CallExpression')) {
56
+ const callExpression = entry.value;
57
+ // skip if not a call to `token`
58
+ if (!isNodeOfType(callExpression.callee, 'Identifier') || callExpression.callee.name !== 'token' || !isNodeOfType(callExpression.arguments[0], 'Literal')) {
59
+ return;
60
+ }
61
+ // the first argument of `token` is the token name and
62
+ // can be given directly to `xcss` as it has been validated already.
63
+ return fixer.replaceText(entry.value, literal(`'${callExpression.arguments[0].value}'`).toString());
51
64
  }
52
- return fixer.replaceText(entry.value, literal(`'${supportedStylesMap[entry.key.name][value]}'`).toString());
53
65
  });
54
66
  };
67
+
68
+ // TODO: https://product-fabric.atlassian.net/browse/DSP-16054
55
69
  export const spaceTokenMap = {
56
70
  '0px': 'space.0',
57
71
  '2px': 'space.025',
@@ -1,26 +1,17 @@
1
1
  import { isNodeOfType } from 'eslint-codemod-utils';
2
-
3
- // FIXME: This is only loosely typed
4
-
2
+ export const SPREAD_SYNTAX = Symbol('SPREAD_SYNTAX');
5
3
  /**
6
4
  * Note: Not recursive. Only handles top level key/value pairs
7
5
  */
8
6
  export const convertASTObjectExpressionToJSObject = styles => {
9
- const styleObj = {};
7
+ const styleObj = {
8
+ unsupported: []
9
+ };
10
10
 
11
- // if we see any spread props we stop and return false to indicate this is unsupported
11
+ // if we see any spread props we indicate that as unsupported
12
12
  if (!styles.properties.every(prop => isNodeOfType(prop, 'Property'))) {
13
- return false;
13
+ styleObj.unsupported.push(SPREAD_SYNTAX);
14
14
  }
15
-
16
- // TODO: We need to harden this logic.
17
- // It currently generates a false positive for:
18
- // styled.div({
19
- // marginTop: "0px",
20
- // marginBottom: token("space.100", "8px"),
21
- // })
22
- // as the value for `marginBottom` is not a string, so it is just skipped
23
- // from the resulting map and this causes the rule to trigger when it shouldn't
24
15
  styles.properties.forEach(prop => {
25
16
  if (!isNodeOfType(prop, 'Property')) {
26
17
  return;
@@ -28,13 +19,34 @@ export const convertASTObjectExpressionToJSObject = styles => {
28
19
  if (!isNodeOfType(prop.key, 'Identifier')) {
29
20
  return;
30
21
  }
31
- if (!isNodeOfType(prop.value, 'Literal')) {
22
+
23
+ // a literal string value, the base case
24
+ if (isNodeOfType(prop.value, 'Literal') && typeof prop.value.value === 'string') {
25
+ styleObj[prop.key.name] = prop.value.value;
32
26
  return;
33
27
  }
34
- if (typeof prop.value.value !== 'string') {
35
- return;
28
+
29
+ // try to handle a direct call to `token`
30
+ if (isNodeOfType(prop.value, 'CallExpression')) {
31
+ const callExpression = prop.value;
32
+ // strictly handle calls to `token`
33
+ if (isNodeOfType(callExpression.callee, 'Identifier') && callExpression.callee.name === 'token') {
34
+ // only two valid cases are supported
35
+ // one argument => token('space.100')
36
+ // two arguments => token('space.100', '8px')
37
+ if ((callExpression.arguments.length === 1 || callExpression.arguments.length === 2) && isNodeOfType(callExpression.arguments[0], 'Literal') && (typeof callExpression.arguments[1] === 'undefined' || isNodeOfType(callExpression.arguments[1], 'Literal'))) {
38
+ var _callExpression$argum;
39
+ styleObj[prop.key.name] = {
40
+ tokenName: String(callExpression.arguments[0].value),
41
+ fallbackValue: (_callExpression$argum = callExpression.arguments[1]) !== null && _callExpression$argum !== void 0 && _callExpression$argum.value ? String(callExpression.arguments[1].value) : undefined
42
+ };
43
+ return;
44
+ }
45
+ }
36
46
  }
37
- styleObj[prop.key.name] = prop.value.value;
47
+
48
+ // if we get here we have an unsupported value
49
+ styleObj.unsupported.push(prop.key.name);
38
50
  });
39
51
  return styleObj;
40
52
  };
@@ -29,6 +29,7 @@ export const findValidStyledComponentCall = node => {
29
29
  *
30
30
  * In the future it could be enhanced to double check `styled` and `styled2`
31
31
  * are Compiled imports but as is should work for the majority of use cases
32
+ * https://product-fabric.atlassian.net/browse/DSP-16058
32
33
  */
33
34
  const isStyledCallExpression = call => {
34
35
  if (!isNodeOfType(call, 'CallExpression')) {
@@ -1,4 +1,5 @@
1
1
  /**
2
+ * TODO: Update this logic: https://product-fabric.atlassian.net/browse/DSP-16059
2
3
  * Using Regex here because otherwise we'd need to traverse the entire AST
3
4
  * We should harden this logic as we go.
4
5
  */
@@ -1,15 +1,22 @@
1
1
  import { isNodeOfType } from 'eslint-codemod-utils';
2
2
  import { supportedStylesMap } from '../transformers/css-to-xcss';
3
3
  import { convertASTObjectExpressionToJSObject } from './convert-ast-object-expression-to-js-object';
4
- export const isValidCssPropertiesToTransform = node => {
4
+ export const isValidCssPropertiesToTransform = (node, config) => {
5
5
  const cssObjectExpression = node.arguments[0];
6
6
  // Bail on empty object calls
7
7
  if (!cssObjectExpression || !isNodeOfType(cssObjectExpression, 'ObjectExpression')) {
8
8
  return false;
9
9
  }
10
- const cssObject = convertASTObjectExpressionToJSObject(cssObjectExpression);
11
- // Bail if there are less or more than 1 styles defined
12
- if (!cssObject || Object.keys(cssObject).length !== 1) {
10
+ const {
11
+ unsupported,
12
+ ...cssObject
13
+ } = convertASTObjectExpressionToJSObject(cssObjectExpression);
14
+ // Bail if there are any unsupported styles
15
+ if (unsupported.length > 0 || Object.keys(cssObject).length !== 1) {
16
+ return false;
17
+ }
18
+ // Short-circuit when token calls are found but pattern is not enabled in config
19
+ if (!config.patterns.includes('css-property-with-tokens') && Object.values(cssObject).some(value => typeof value === 'object' && value.tokenName)) {
13
20
  return false;
14
21
  }
15
22
 
@@ -19,10 +26,22 @@ export const isValidCssPropertiesToTransform = node => {
19
26
  // than the rule reporting on things that can't be mapped.
20
27
  const containsOnlyValidStyles = Object.keys(cssObject).every(styleProperty => {
21
28
  const styleValue = cssObject[styleProperty];
22
- return supportedStylesMap[styleProperty] &&
23
- // Is the key something we can map
24
- supportedStylesMap[styleProperty][styleValue] // Is the value something we can map
25
- ;
29
+
30
+ // token function call
31
+ if (typeof styleValue === 'object') {
32
+ // if there is no fallback value, we just map to the token name, if one is found
33
+ if (!styleValue.fallbackValue) {
34
+ return supportedStylesMap[styleProperty] && Object.values(supportedStylesMap[styleProperty]).includes(styleValue.tokenName);
35
+ }
36
+ // token with fallback
37
+ return supportedStylesMap[styleProperty] && supportedStylesMap[styleProperty][styleValue.fallbackValue] === styleValue.tokenName;
38
+ } else {
39
+ // direct value used
40
+ return supportedStylesMap[styleProperty] &&
41
+ // Is the key something we can map
42
+ supportedStylesMap[styleProperty][styleValue] // Is the value something we can map
43
+ ;
44
+ }
26
45
  });
27
46
 
28
47
  if (!containsOnlyValidStyles) {
@@ -1,4 +1,5 @@
1
1
  import _defineProperty from "@babel/runtime/helpers/defineProperty";
2
+ import _toConsumableArray from "@babel/runtime/helpers/toConsumableArray";
2
3
  import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
3
4
  // eslint-disable-next-line import/no-extraneous-dependencies
4
5
 
@@ -18,9 +19,9 @@ function findSpreadProperties(node) {
18
19
  });
19
20
  }
20
21
  function analyzeIdentifier(context, sourceIdentifier, configuration) {
21
- var _getIdentifierInParen;
22
+ var _getIdentifierInParen, _getIdentifierInParen2;
22
23
  var scope = context.getScope();
23
- var _ref = ((_getIdentifierInParen = getIdentifierInParentScope(scope, sourceIdentifier.name)) === null || _getIdentifierInParen === void 0 ? void 0 : _getIdentifierInParen.identifiers) || [],
24
+ var _ref = (_getIdentifierInParen = (_getIdentifierInParen2 = getIdentifierInParentScope(scope, sourceIdentifier.name)) === null || _getIdentifierInParen2 === void 0 ? void 0 : _getIdentifierInParen2.identifiers) !== null && _getIdentifierInParen !== void 0 ? _getIdentifierInParen : [],
24
25
  _ref2 = _slicedToArray(_ref, 1),
25
26
  identifier = _ref2[0];
26
27
  if (!identifier || !identifier.parent) {
@@ -43,7 +44,7 @@ function analyzeIdentifier(context, sourceIdentifier, configuration) {
43
44
  });
44
45
  return;
45
46
  }
46
- if (identifier.parent && identifier.parent.init && !isCssCallExpression(identifier.parent.init, configuration.cssFunctions)) {
47
+ if (identifier.parent && identifier.parent.init && !isCssCallExpression(identifier.parent.init, [].concat(_toConsumableArray(configuration.cssFunctions), ['cssMap']))) {
47
48
  // When variable value is not of type css({})
48
49
  context.report({
49
50
  node: identifier,
@@ -62,6 +63,13 @@ function analyzeIdentifier(context, sourceIdentifier, configuration) {
62
63
  });
63
64
  }
64
65
  }
66
+
67
+ /**
68
+ * Handle different cases based on what's been passed in the css-related JSXAttribute
69
+ * @param context the context of the node
70
+ * @param expression the expression of the JSXAttribute value
71
+ * @param configuration what css-related functions to account for (eg. css, xcss, cssMap), and whether to detect bottom vs top expressions
72
+ */
65
73
  var traverseExpressionWithConfig = function traverseExpressionWithConfig(context, expression, configuration) {
66
74
  function traverseExpression(expression) {
67
75
  switch (expression.type) {
@@ -133,7 +141,8 @@ var rule = createLintRule({
133
141
  create: function create(context) {
134
142
  var _ref3;
135
143
  var mergedConfig = assign({}, defaultConfig, context.options[0]);
136
- var callSelector = mergedConfig.cssFunctions.map(function (fn) {
144
+ var callSelectorFunctions = [].concat(_toConsumableArray(mergedConfig.cssFunctions), ['cssMap']);
145
+ var callSelector = callSelectorFunctions.map(function (fn) {
137
146
  return "CallExpression[callee.name=".concat(fn, "]");
138
147
  }).join(',');
139
148
  return _ref3 = {}, _defineProperty(_ref3, callSelector, function (node) {
@@ -1,5 +1,5 @@
1
1
  var defaults = {
2
- patterns: ['div-object-css']
2
+ patterns: ['compiled-css-function']
3
3
  };
4
4
  export var getConfig = function getConfig(overrides) {
5
5
  // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign
@@ -31,7 +31,7 @@ var rule = createLintRule({
31
31
  return;
32
32
  }
33
33
  var styledComponentVariableRef = findValidStyledComponentCall(node);
34
- if (!styledComponentVariableRef || !isNodeOfType(styledComponentVariableRef.id, 'Identifier') || !isValidCssPropertiesToTransform(node)) {
34
+ if (!styledComponentVariableRef || !isNodeOfType(styledComponentVariableRef.id, 'Identifier') || !isValidCssPropertiesToTransform(node, config)) {
35
35
  return;
36
36
  }
37
37
  var styledComponentJsxRef = findValidJsxUsageToTransform(styledComponentVariableRef.id.name, context.getScope());
@@ -54,7 +54,9 @@ var rule = createLintRule({
54
54
  },
55
55
  // transforms <div css={...}> usages
56
56
  JSXOpeningElement: function JSXOpeningElement(node) {
57
- var config = getConfig(context.options[0]);
57
+ if (!config.patterns.includes('compiled-css-function')) {
58
+ return false;
59
+ }
58
60
  if (!isNodeOfType(node, 'JSXOpeningElement')) {
59
61
  return;
60
62
  }
@@ -129,9 +131,6 @@ var shouldSuggestBox = function shouldSuggestBox(node, context, config
129
131
  if (!cssVariableValue || !isFunctionNamed(cssVariableValue, 'css')) {
130
132
  return false;
131
133
  }
132
- if (!config.patterns.includes('div-object-css')) {
133
- return false;
134
- }
135
- return isValidCssPropertiesToTransform(cssVariableValue.node.init);
134
+ return isValidCssPropertiesToTransform(cssVariableValue.node.init, config);
136
135
  };
137
136
  export default rule;
@@ -32,7 +32,7 @@ export var cssToXcssTransformer = function cssToXcssTransformer(node, context, f
32
32
  fixer.replaceText(cssVariableValue.node.init.callee, identifier('xcss').toString())].concat(_toConsumableArray(styledObjectToXcssTokens(cssObjectExpression, fixer)));
33
33
  };
34
34
 
35
- // Update css object values to xcss values. e.g. `'8px'` -> `'space.100'`
35
+ // Update css object values to xcss values
36
36
  // Note: `properties` in this context is a group of AST nodes that make up a key/value pair in an object.
37
37
  // e.g. `padding: '8px'`. For clarity, it's renamed to `entry` inside the `.map()`.
38
38
  export var styledObjectToXcssTokens = function styledObjectToXcssTokens(styles, fixer) {
@@ -43,16 +43,30 @@ export var styledObjectToXcssTokens = function styledObjectToXcssTokens(styles,
43
43
  if (!isNodeOfType(entry.key, 'Identifier')) {
44
44
  return;
45
45
  }
46
- if (!isNodeOfType(entry.value, 'Literal')) {
47
- return;
46
+
47
+ // maps literal values like: 8px to 'space.100'
48
+ if (isNodeOfType(entry.value, 'Literal')) {
49
+ var value = entry.value.value;
50
+ if (typeof value !== 'string') {
51
+ return;
52
+ }
53
+ return fixer.replaceText(entry.value, literal("'".concat(supportedStylesMap[entry.key.name][value], "'")).toString());
48
54
  }
49
- var value = entry.value.value;
50
- if (typeof value !== 'string') {
51
- return;
55
+ // maps token calls like: token('space.100') to 'space.100'
56
+ if (isNodeOfType(entry.value, 'CallExpression')) {
57
+ var callExpression = entry.value;
58
+ // skip if not a call to `token`
59
+ if (!isNodeOfType(callExpression.callee, 'Identifier') || callExpression.callee.name !== 'token' || !isNodeOfType(callExpression.arguments[0], 'Literal')) {
60
+ return;
61
+ }
62
+ // the first argument of `token` is the token name and
63
+ // can be given directly to `xcss` as it has been validated already.
64
+ return fixer.replaceText(entry.value, literal("'".concat(callExpression.arguments[0].value, "'")).toString());
52
65
  }
53
- return fixer.replaceText(entry.value, literal("'".concat(supportedStylesMap[entry.key.name][value], "'")).toString());
54
66
  });
55
67
  };
68
+
69
+ // TODO: https://product-fabric.atlassian.net/browse/DSP-16054
56
70
  export var spaceTokenMap = {
57
71
  '0px': 'space.0',
58
72
  '2px': 'space.025',
@@ -1,28 +1,19 @@
1
1
  import { isNodeOfType } from 'eslint-codemod-utils';
2
-
3
- // FIXME: This is only loosely typed
4
-
2
+ export var SPREAD_SYNTAX = Symbol('SPREAD_SYNTAX');
5
3
  /**
6
4
  * Note: Not recursive. Only handles top level key/value pairs
7
5
  */
8
6
  export var convertASTObjectExpressionToJSObject = function convertASTObjectExpressionToJSObject(styles) {
9
- var styleObj = {};
7
+ var styleObj = {
8
+ unsupported: []
9
+ };
10
10
 
11
- // if we see any spread props we stop and return false to indicate this is unsupported
11
+ // if we see any spread props we indicate that as unsupported
12
12
  if (!styles.properties.every(function (prop) {
13
13
  return isNodeOfType(prop, 'Property');
14
14
  })) {
15
- return false;
15
+ styleObj.unsupported.push(SPREAD_SYNTAX);
16
16
  }
17
-
18
- // TODO: We need to harden this logic.
19
- // It currently generates a false positive for:
20
- // styled.div({
21
- // marginTop: "0px",
22
- // marginBottom: token("space.100", "8px"),
23
- // })
24
- // as the value for `marginBottom` is not a string, so it is just skipped
25
- // from the resulting map and this causes the rule to trigger when it shouldn't
26
17
  styles.properties.forEach(function (prop) {
27
18
  if (!isNodeOfType(prop, 'Property')) {
28
19
  return;
@@ -30,13 +21,34 @@ export var convertASTObjectExpressionToJSObject = function convertASTObjectExpre
30
21
  if (!isNodeOfType(prop.key, 'Identifier')) {
31
22
  return;
32
23
  }
33
- if (!isNodeOfType(prop.value, 'Literal')) {
24
+
25
+ // a literal string value, the base case
26
+ if (isNodeOfType(prop.value, 'Literal') && typeof prop.value.value === 'string') {
27
+ styleObj[prop.key.name] = prop.value.value;
34
28
  return;
35
29
  }
36
- if (typeof prop.value.value !== 'string') {
37
- return;
30
+
31
+ // try to handle a direct call to `token`
32
+ if (isNodeOfType(prop.value, 'CallExpression')) {
33
+ var callExpression = prop.value;
34
+ // strictly handle calls to `token`
35
+ if (isNodeOfType(callExpression.callee, 'Identifier') && callExpression.callee.name === 'token') {
36
+ // only two valid cases are supported
37
+ // one argument => token('space.100')
38
+ // two arguments => token('space.100', '8px')
39
+ if ((callExpression.arguments.length === 1 || callExpression.arguments.length === 2) && isNodeOfType(callExpression.arguments[0], 'Literal') && (typeof callExpression.arguments[1] === 'undefined' || isNodeOfType(callExpression.arguments[1], 'Literal'))) {
40
+ var _callExpression$argum;
41
+ styleObj[prop.key.name] = {
42
+ tokenName: String(callExpression.arguments[0].value),
43
+ fallbackValue: (_callExpression$argum = callExpression.arguments[1]) !== null && _callExpression$argum !== void 0 && _callExpression$argum.value ? String(callExpression.arguments[1].value) : undefined
44
+ };
45
+ return;
46
+ }
47
+ }
38
48
  }
39
- styleObj[prop.key.name] = prop.value.value;
49
+
50
+ // if we get here we have an unsupported value
51
+ styleObj.unsupported.push(prop.key.name);
40
52
  });
41
53
  return styleObj;
42
54
  };
@@ -29,6 +29,7 @@ export var findValidStyledComponentCall = function findValidStyledComponentCall(
29
29
  *
30
30
  * In the future it could be enhanced to double check `styled` and `styled2`
31
31
  * are Compiled imports but as is should work for the majority of use cases
32
+ * https://product-fabric.atlassian.net/browse/DSP-16058
32
33
  */
33
34
  var isStyledCallExpression = function isStyledCallExpression(call) {
34
35
  if (!isNodeOfType(call, 'CallExpression')) {
@@ -1,4 +1,5 @@
1
1
  /**
2
+ * TODO: Update this logic: https://product-fabric.atlassian.net/browse/DSP-16059
2
3
  * Using Regex here because otherwise we'd need to traverse the entire AST
3
4
  * We should harden this logic as we go.
4
5
  */
@@ -1,15 +1,26 @@
1
+ import _typeof from "@babel/runtime/helpers/typeof";
2
+ import _objectWithoutProperties from "@babel/runtime/helpers/objectWithoutProperties";
3
+ var _excluded = ["unsupported"];
1
4
  import { isNodeOfType } from 'eslint-codemod-utils';
2
5
  import { supportedStylesMap } from '../transformers/css-to-xcss';
3
6
  import { convertASTObjectExpressionToJSObject } from './convert-ast-object-expression-to-js-object';
4
- export var isValidCssPropertiesToTransform = function isValidCssPropertiesToTransform(node) {
7
+ export var isValidCssPropertiesToTransform = function isValidCssPropertiesToTransform(node, config) {
5
8
  var cssObjectExpression = node.arguments[0];
6
9
  // Bail on empty object calls
7
10
  if (!cssObjectExpression || !isNodeOfType(cssObjectExpression, 'ObjectExpression')) {
8
11
  return false;
9
12
  }
10
- var cssObject = convertASTObjectExpressionToJSObject(cssObjectExpression);
11
- // Bail if there are less or more than 1 styles defined
12
- if (!cssObject || Object.keys(cssObject).length !== 1) {
13
+ var _convertASTObjectExpr = convertASTObjectExpressionToJSObject(cssObjectExpression),
14
+ unsupported = _convertASTObjectExpr.unsupported,
15
+ cssObject = _objectWithoutProperties(_convertASTObjectExpr, _excluded);
16
+ // Bail if there are any unsupported styles
17
+ if (unsupported.length > 0 || Object.keys(cssObject).length !== 1) {
18
+ return false;
19
+ }
20
+ // Short-circuit when token calls are found but pattern is not enabled in config
21
+ if (!config.patterns.includes('css-property-with-tokens') && Object.values(cssObject).some(function (value) {
22
+ return _typeof(value) === 'object' && value.tokenName;
23
+ })) {
13
24
  return false;
14
25
  }
15
26
 
@@ -19,10 +30,22 @@ export var isValidCssPropertiesToTransform = function isValidCssPropertiesToTran
19
30
  // than the rule reporting on things that can't be mapped.
20
31
  var containsOnlyValidStyles = Object.keys(cssObject).every(function (styleProperty) {
21
32
  var styleValue = cssObject[styleProperty];
22
- return supportedStylesMap[styleProperty] &&
23
- // Is the key something we can map
24
- supportedStylesMap[styleProperty][styleValue] // Is the value something we can map
25
- ;
33
+
34
+ // token function call
35
+ if (_typeof(styleValue) === 'object') {
36
+ // if there is no fallback value, we just map to the token name, if one is found
37
+ if (!styleValue.fallbackValue) {
38
+ return supportedStylesMap[styleProperty] && Object.values(supportedStylesMap[styleProperty]).includes(styleValue.tokenName);
39
+ }
40
+ // token with fallback
41
+ return supportedStylesMap[styleProperty] && supportedStylesMap[styleProperty][styleValue.fallbackValue] === styleValue.tokenName;
42
+ } else {
43
+ // direct value used
44
+ return supportedStylesMap[styleProperty] &&
45
+ // Is the key something we can map
46
+ supportedStylesMap[styleProperty][styleValue] // Is the value something we can map
47
+ ;
48
+ }
26
49
  });
27
50
 
28
51
  if (!containsOnlyValidStyles) {
@@ -1,4 +1,6 @@
1
+ type Pattern = 'compiled-css-function' | 'compiled-styled-object' | 'css-template-literal' | 'css-property-with-tokens' | 'css-property-multiple-values';
1
2
  export interface RuleConfig {
2
- patterns: ('div-object-css' | 'compiled-styled-object' | 'compiled-template-literal' | 'div-object-css-with-tokens' | 'div-object-css-with-multiple-vals')[];
3
+ patterns: Pattern[];
3
4
  }
4
5
  export declare const getConfig: (overrides: Partial<RuleConfig>) => Required<RuleConfig>;
6
+ export {};
@@ -1,9 +1,18 @@
1
+ import type { CSSProperties } from 'react';
1
2
  import type { Rule } from 'eslint';
2
3
  import { ObjectExpression } from 'eslint-codemod-utils';
4
+ export declare const SPREAD_SYNTAX: unique symbol;
5
+ type Token = {
6
+ tokenName: string;
7
+ fallbackValue: string | undefined;
8
+ };
3
9
  export type CSSPropStyleObject = {
4
- [key: string]: string | number;
10
+ [key in keyof CSSProperties]: string | number | Token;
11
+ } & {
12
+ unsupported: (keyof CSSProperties | typeof SPREAD_SYNTAX)[];
5
13
  };
6
14
  /**
7
15
  * Note: Not recursive. Only handles top level key/value pairs
8
16
  */
9
- export declare const convertASTObjectExpressionToJSObject: (styles: ObjectExpression & Partial<Rule.NodeParentExtension>) => CSSPropStyleObject | false;
17
+ export declare const convertASTObjectExpressionToJSObject: (styles: ObjectExpression & Partial<Rule.NodeParentExtension>) => CSSPropStyleObject;
18
+ export {};
@@ -1,5 +1,6 @@
1
1
  import type { Rule } from 'eslint';
2
2
  /**
3
+ * TODO: Update this logic: https://product-fabric.atlassian.net/browse/DSP-16059
3
4
  * Using Regex here because otherwise we'd need to traverse the entire AST
4
5
  * We should harden this logic as we go.
5
6
  */
@@ -1,3 +1,4 @@
1
1
  import type { Rule } from 'eslint';
2
2
  import { SimpleCallExpression } from 'eslint-codemod-utils';
3
- export declare const isValidCssPropertiesToTransform: (node: SimpleCallExpression & Rule.NodeParentExtension) => boolean;
3
+ import { RuleConfig } from '../config';
4
+ export declare const isValidCssPropertiesToTransform: (node: SimpleCallExpression & Rule.NodeParentExtension, config: RuleConfig) => boolean;
@@ -1,4 +1,6 @@
1
+ type Pattern = 'compiled-css-function' | 'compiled-styled-object' | 'css-template-literal' | 'css-property-with-tokens' | 'css-property-multiple-values';
1
2
  export interface RuleConfig {
2
- patterns: ('div-object-css' | 'compiled-styled-object' | 'compiled-template-literal' | 'div-object-css-with-tokens' | 'div-object-css-with-multiple-vals')[];
3
+ patterns: Pattern[];
3
4
  }
4
5
  export declare const getConfig: (overrides: Partial<RuleConfig>) => Required<RuleConfig>;
6
+ export {};
@@ -1,9 +1,18 @@
1
+ import type { CSSProperties } from 'react';
1
2
  import type { Rule } from 'eslint';
2
3
  import { ObjectExpression } from 'eslint-codemod-utils';
4
+ export declare const SPREAD_SYNTAX: unique symbol;
5
+ type Token = {
6
+ tokenName: string;
7
+ fallbackValue: string | undefined;
8
+ };
3
9
  export type CSSPropStyleObject = {
4
- [key: string]: string | number;
10
+ [key in keyof CSSProperties]: string | number | Token;
11
+ } & {
12
+ unsupported: (keyof CSSProperties | typeof SPREAD_SYNTAX)[];
5
13
  };
6
14
  /**
7
15
  * Note: Not recursive. Only handles top level key/value pairs
8
16
  */
9
- export declare const convertASTObjectExpressionToJSObject: (styles: ObjectExpression & Partial<Rule.NodeParentExtension>) => CSSPropStyleObject | false;
17
+ export declare const convertASTObjectExpressionToJSObject: (styles: ObjectExpression & Partial<Rule.NodeParentExtension>) => CSSPropStyleObject;
18
+ export {};
@@ -1,5 +1,6 @@
1
1
  import type { Rule } from 'eslint';
2
2
  /**
3
+ * TODO: Update this logic: https://product-fabric.atlassian.net/browse/DSP-16059
3
4
  * Using Regex here because otherwise we'd need to traverse the entire AST
4
5
  * We should harden this logic as we go.
5
6
  */
@@ -1,3 +1,4 @@
1
1
  import type { Rule } from 'eslint';
2
2
  import { SimpleCallExpression } from 'eslint-codemod-utils';
3
- export declare const isValidCssPropertiesToTransform: (node: SimpleCallExpression & Rule.NodeParentExtension) => boolean;
3
+ import { RuleConfig } from '../config';
4
+ export declare const isValidCssPropertiesToTransform: (node: SimpleCallExpression & Rule.NodeParentExtension, config: RuleConfig) => boolean;
package/package.json CHANGED
@@ -1,7 +1,7 @@
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.19.1",
4
+ "version": "8.20.0",
5
5
  "author": "Atlassian Pty Ltd",
6
6
  "publishConfig": {
7
7
  "registry": "https://registry.npmjs.org/"