@atlaskit/eslint-plugin-design-system 8.19.1 → 8.19.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 (31) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/dist/cjs/rules/use-primitives/config/index.js +1 -1
  3. package/dist/cjs/rules/use-primitives/index.js +5 -6
  4. package/dist/cjs/rules/use-primitives/transformers/css-to-xcss.js +21 -7
  5. package/dist/cjs/rules/use-primitives/utils/convert-ast-object-expression-to-js-object.js +30 -9
  6. package/dist/cjs/rules/use-primitives/utils/find-valid-styled-component-call.js +1 -0
  7. package/dist/cjs/rules/use-primitives/utils/get-variable-usage-count.js +1 -0
  8. package/dist/cjs/rules/use-primitives/utils/is-valid-css-properties-to-transform.js +25 -5
  9. package/dist/es2019/rules/use-primitives/config/index.js +1 -1
  10. package/dist/es2019/rules/use-primitives/index.js +5 -6
  11. package/dist/es2019/rules/use-primitives/transformers/css-to-xcss.js +21 -7
  12. package/dist/es2019/rules/use-primitives/utils/convert-ast-object-expression-to-js-object.js +30 -9
  13. package/dist/es2019/rules/use-primitives/utils/find-valid-styled-component-call.js +1 -0
  14. package/dist/es2019/rules/use-primitives/utils/get-variable-usage-count.js +1 -0
  15. package/dist/es2019/rules/use-primitives/utils/is-valid-css-properties-to-transform.js +21 -5
  16. package/dist/esm/rules/use-primitives/config/index.js +1 -1
  17. package/dist/esm/rules/use-primitives/index.js +5 -6
  18. package/dist/esm/rules/use-primitives/transformers/css-to-xcss.js +21 -7
  19. package/dist/esm/rules/use-primitives/utils/convert-ast-object-expression-to-js-object.js +30 -9
  20. package/dist/esm/rules/use-primitives/utils/find-valid-styled-component-call.js +1 -0
  21. package/dist/esm/rules/use-primitives/utils/get-variable-usage-count.js +1 -0
  22. package/dist/esm/rules/use-primitives/utils/is-valid-css-properties-to-transform.js +24 -5
  23. package/dist/types/rules/use-primitives/config/index.d.ts +3 -1
  24. package/dist/types/rules/use-primitives/utils/convert-ast-object-expression-to-js-object.d.ts +6 -1
  25. package/dist/types/rules/use-primitives/utils/get-variable-usage-count.d.ts +1 -0
  26. package/dist/types/rules/use-primitives/utils/is-valid-css-properties-to-transform.d.ts +2 -1
  27. package/dist/types-ts4.5/rules/use-primitives/config/index.d.ts +3 -1
  28. package/dist/types-ts4.5/rules/use-primitives/utils/convert-ast-object-expression-to-js-object.d.ts +6 -1
  29. package/dist/types-ts4.5/rules/use-primitives/utils/get-variable-usage-count.d.ts +1 -0
  30. package/dist/types-ts4.5/rules/use-primitives/utils/is-valid-css-properties-to-transform.d.ts +2 -1
  31. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # @atlaskit/eslint-plugin-design-system
2
2
 
3
+ ## 8.19.2
4
+
5
+ ### Patch Changes
6
+
7
+ - [#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.
8
+
3
9
  ## 8.19.1
4
10
 
5
11
  ### Patch Changes
@@ -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',
@@ -20,28 +20,49 @@ var convertASTObjectExpressionToJSObject = exports.convertASTObjectExpressionToJ
20
20
  return false;
21
21
  }
22
22
 
23
- // TODO: We need to harden this logic.
23
+ // TODO: https://product-fabric.atlassian.net/browse/DSP-16055
24
+ // We need to harden this logic asap.
24
25
  // It currently generates a false positive for:
25
26
  // styled.div({
26
27
  // marginTop: "0px",
27
- // marginBottom: token("space.100", "8px"),
28
+ // marginBottom: blah(...),
28
29
  // })
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
30
+ // as the value for `marginBottom` is not a string nor a `token` call, it is just skipped
31
+ // from the resulting map and this is inaccurate.
31
32
  styles.properties.forEach(function (prop) {
33
+ // cases we want to skip (see note above)
32
34
  if (!(0, _eslintCodemodUtils.isNodeOfType)(prop, 'Property')) {
33
35
  return;
34
36
  }
35
37
  if (!(0, _eslintCodemodUtils.isNodeOfType)(prop.key, 'Identifier')) {
36
38
  return;
37
39
  }
38
- if (!(0, _eslintCodemodUtils.isNodeOfType)(prop.value, 'Literal')) {
39
- return;
40
+
41
+ // a literal string value, the base case
42
+ if ((0, _eslintCodemodUtils.isNodeOfType)(prop.value, 'Literal') && typeof prop.value.value === 'string') {
43
+ styleObj[prop.key.name] = prop.value.value;
40
44
  }
41
- if (typeof prop.value.value !== 'string') {
42
- return;
45
+
46
+ // try to handle a direct call to `token`
47
+ if ((0, _eslintCodemodUtils.isNodeOfType)(prop.value, 'CallExpression')) {
48
+ var _callExpression$argum;
49
+ var callExpression = prop.value;
50
+ // skip if not a call to `token`
51
+ if (!(0, _eslintCodemodUtils.isNodeOfType)(callExpression.callee, 'Identifier') || callExpression.callee.name !== 'token') {
52
+ return;
53
+ }
54
+
55
+ // only two valid cases are supported
56
+ // one argument => token('space.100')
57
+ // two arguments => token('space.100', '8px')
58
+ if (callExpression.arguments.length !== 1 && callExpression.arguments.length !== 2 || !(0, _eslintCodemodUtils.isNodeOfType)(callExpression.arguments[0], 'Literal') || callExpression.arguments[1] && !(0, _eslintCodemodUtils.isNodeOfType)(callExpression.arguments[1], 'Literal')) {
59
+ return;
60
+ }
61
+ styleObj[prop.key.name] = {
62
+ tokenName: String(callExpression.arguments[0].value),
63
+ fallbackValue: (_callExpression$argum = callExpression.arguments[1]) !== null && _callExpression$argum !== void 0 && _callExpression$argum.value ? String(callExpression.arguments[1].value) : undefined
64
+ };
43
65
  }
44
- styleObj[prop.key.name] = prop.value.value;
45
66
  });
46
67
  return styleObj;
47
68
  };
@@ -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,13 +1,15 @@
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"));
7
9
  var _eslintCodemodUtils = require("eslint-codemod-utils");
8
10
  var _cssToXcss = require("../transformers/css-to-xcss");
9
11
  var _convertAstObjectExpressionToJsObject = require("./convert-ast-object-expression-to-js-object");
10
- var isValidCssPropertiesToTransform = exports.isValidCssPropertiesToTransform = function isValidCssPropertiesToTransform(node) {
12
+ var isValidCssPropertiesToTransform = exports.isValidCssPropertiesToTransform = function isValidCssPropertiesToTransform(node, config) {
11
13
  var cssObjectExpression = node.arguments[0];
12
14
  // Bail on empty object calls
13
15
  if (!cssObjectExpression || !(0, _eslintCodemodUtils.isNodeOfType)(cssObjectExpression, 'ObjectExpression')) {
@@ -18,6 +20,12 @@ var isValidCssPropertiesToTransform = exports.isValidCssPropertiesToTransform =
18
20
  if (!cssObject || Object.keys(cssObject).length !== 1) {
19
21
  return false;
20
22
  }
23
+ // Short-circuit when token calls are found but pattern is not enabled in config
24
+ if (!config.patterns.includes('css-property-with-tokens') && Object.values(cssObject).some(function (value) {
25
+ return (0, _typeof2.default)(value) === 'object' && value.tokenName;
26
+ })) {
27
+ return false;
28
+ }
21
29
 
22
30
  // NOTE: Our approach with this lint rule is to strictly whitelist css properties we can map.
23
31
  // It means we have to provide mappings for everything (e.g. `display: block`).
@@ -25,10 +33,22 @@ var isValidCssPropertiesToTransform = exports.isValidCssPropertiesToTransform =
25
33
  // than the rule reporting on things that can't be mapped.
26
34
  var containsOnlyValidStyles = Object.keys(cssObject).every(function (styleProperty) {
27
35
  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
- ;
36
+
37
+ // token function call
38
+ if ((0, _typeof2.default)(styleValue) === 'object') {
39
+ // if there is no fallback value, we just map to the token name, if one is found
40
+ if (!styleValue.fallbackValue) {
41
+ return _cssToXcss.supportedStylesMap[styleProperty] && Object.values(_cssToXcss.supportedStylesMap[styleProperty]).includes(styleValue.tokenName);
42
+ }
43
+ // token with fallback
44
+ return _cssToXcss.supportedStylesMap[styleProperty] && _cssToXcss.supportedStylesMap[styleProperty][styleValue.fallbackValue] === styleValue.tokenName;
45
+ } else {
46
+ // direct value used
47
+ return _cssToXcss.supportedStylesMap[styleProperty] &&
48
+ // Is the key something we can map
49
+ _cssToXcss.supportedStylesMap[styleProperty][styleValue] // Is the value something we can map
50
+ ;
51
+ }
32
52
  });
33
53
 
34
54
  if (!containsOnlyValidStyles) {
@@ -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',
@@ -13,28 +13,49 @@ export const convertASTObjectExpressionToJSObject = styles => {
13
13
  return false;
14
14
  }
15
15
 
16
- // TODO: We need to harden this logic.
16
+ // TODO: https://product-fabric.atlassian.net/browse/DSP-16055
17
+ // We need to harden this logic asap.
17
18
  // It currently generates a false positive for:
18
19
  // styled.div({
19
20
  // marginTop: "0px",
20
- // marginBottom: token("space.100", "8px"),
21
+ // marginBottom: blah(...),
21
22
  // })
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
23
+ // as the value for `marginBottom` is not a string nor a `token` call, it is just skipped
24
+ // from the resulting map and this is inaccurate.
24
25
  styles.properties.forEach(prop => {
26
+ // cases we want to skip (see note above)
25
27
  if (!isNodeOfType(prop, 'Property')) {
26
28
  return;
27
29
  }
28
30
  if (!isNodeOfType(prop.key, 'Identifier')) {
29
31
  return;
30
32
  }
31
- if (!isNodeOfType(prop.value, 'Literal')) {
32
- return;
33
+
34
+ // a literal string value, the base case
35
+ if (isNodeOfType(prop.value, 'Literal') && typeof prop.value.value === 'string') {
36
+ styleObj[prop.key.name] = prop.value.value;
33
37
  }
34
- if (typeof prop.value.value !== 'string') {
35
- return;
38
+
39
+ // try to handle a direct call to `token`
40
+ if (isNodeOfType(prop.value, 'CallExpression')) {
41
+ var _callExpression$argum;
42
+ const callExpression = prop.value;
43
+ // skip if not a call to `token`
44
+ if (!isNodeOfType(callExpression.callee, 'Identifier') || callExpression.callee.name !== 'token') {
45
+ return;
46
+ }
47
+
48
+ // only two valid cases are supported
49
+ // one argument => token('space.100')
50
+ // two arguments => token('space.100', '8px')
51
+ if (callExpression.arguments.length !== 1 && callExpression.arguments.length !== 2 || !isNodeOfType(callExpression.arguments[0], 'Literal') || callExpression.arguments[1] && !isNodeOfType(callExpression.arguments[1], 'Literal')) {
52
+ return;
53
+ }
54
+ styleObj[prop.key.name] = {
55
+ tokenName: String(callExpression.arguments[0].value),
56
+ fallbackValue: (_callExpression$argum = callExpression.arguments[1]) !== null && _callExpression$argum !== void 0 && _callExpression$argum.value ? String(callExpression.arguments[1].value) : undefined
57
+ };
36
58
  }
37
- styleObj[prop.key.name] = prop.value.value;
38
59
  });
39
60
  return styleObj;
40
61
  };
@@ -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,7 +1,7 @@
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')) {
@@ -12,6 +12,10 @@ export const isValidCssPropertiesToTransform = node => {
12
12
  if (!cssObject || Object.keys(cssObject).length !== 1) {
13
13
  return false;
14
14
  }
15
+ // Short-circuit when token calls are found but pattern is not enabled in config
16
+ if (!config.patterns.includes('css-property-with-tokens') && Object.values(cssObject).some(value => typeof value === 'object' && value.tokenName)) {
17
+ return false;
18
+ }
15
19
 
16
20
  // NOTE: Our approach with this lint rule is to strictly whitelist css properties we can map.
17
21
  // It means we have to provide mappings for everything (e.g. `display: block`).
@@ -19,10 +23,22 @@ export const isValidCssPropertiesToTransform = node => {
19
23
  // than the rule reporting on things that can't be mapped.
20
24
  const containsOnlyValidStyles = Object.keys(cssObject).every(styleProperty => {
21
25
  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
- ;
26
+
27
+ // token function call
28
+ if (typeof styleValue === 'object') {
29
+ // if there is no fallback value, we just map to the token name, if one is found
30
+ if (!styleValue.fallbackValue) {
31
+ return supportedStylesMap[styleProperty] && Object.values(supportedStylesMap[styleProperty]).includes(styleValue.tokenName);
32
+ }
33
+ // token with fallback
34
+ return supportedStylesMap[styleProperty] && supportedStylesMap[styleProperty][styleValue.fallbackValue] === styleValue.tokenName;
35
+ } else {
36
+ // direct value used
37
+ return supportedStylesMap[styleProperty] &&
38
+ // Is the key something we can map
39
+ supportedStylesMap[styleProperty][styleValue] // Is the value something we can map
40
+ ;
41
+ }
26
42
  });
27
43
 
28
44
  if (!containsOnlyValidStyles) {
@@ -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',
@@ -15,28 +15,49 @@ export var convertASTObjectExpressionToJSObject = function convertASTObjectExpre
15
15
  return false;
16
16
  }
17
17
 
18
- // TODO: We need to harden this logic.
18
+ // TODO: https://product-fabric.atlassian.net/browse/DSP-16055
19
+ // We need to harden this logic asap.
19
20
  // It currently generates a false positive for:
20
21
  // styled.div({
21
22
  // marginTop: "0px",
22
- // marginBottom: token("space.100", "8px"),
23
+ // marginBottom: blah(...),
23
24
  // })
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
25
+ // as the value for `marginBottom` is not a string nor a `token` call, it is just skipped
26
+ // from the resulting map and this is inaccurate.
26
27
  styles.properties.forEach(function (prop) {
28
+ // cases we want to skip (see note above)
27
29
  if (!isNodeOfType(prop, 'Property')) {
28
30
  return;
29
31
  }
30
32
  if (!isNodeOfType(prop.key, 'Identifier')) {
31
33
  return;
32
34
  }
33
- if (!isNodeOfType(prop.value, 'Literal')) {
34
- return;
35
+
36
+ // a literal string value, the base case
37
+ if (isNodeOfType(prop.value, 'Literal') && typeof prop.value.value === 'string') {
38
+ styleObj[prop.key.name] = prop.value.value;
35
39
  }
36
- if (typeof prop.value.value !== 'string') {
37
- return;
40
+
41
+ // try to handle a direct call to `token`
42
+ if (isNodeOfType(prop.value, 'CallExpression')) {
43
+ var _callExpression$argum;
44
+ var callExpression = prop.value;
45
+ // skip if not a call to `token`
46
+ if (!isNodeOfType(callExpression.callee, 'Identifier') || callExpression.callee.name !== 'token') {
47
+ return;
48
+ }
49
+
50
+ // only two valid cases are supported
51
+ // one argument => token('space.100')
52
+ // two arguments => token('space.100', '8px')
53
+ if (callExpression.arguments.length !== 1 && callExpression.arguments.length !== 2 || !isNodeOfType(callExpression.arguments[0], 'Literal') || callExpression.arguments[1] && !isNodeOfType(callExpression.arguments[1], 'Literal')) {
54
+ return;
55
+ }
56
+ styleObj[prop.key.name] = {
57
+ tokenName: String(callExpression.arguments[0].value),
58
+ fallbackValue: (_callExpression$argum = callExpression.arguments[1]) !== null && _callExpression$argum !== void 0 && _callExpression$argum.value ? String(callExpression.arguments[1].value) : undefined
59
+ };
38
60
  }
39
- styleObj[prop.key.name] = prop.value.value;
40
61
  });
41
62
  return styleObj;
42
63
  };
@@ -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,7 +1,8 @@
1
+ import _typeof from "@babel/runtime/helpers/typeof";
1
2
  import { isNodeOfType } from 'eslint-codemod-utils';
2
3
  import { supportedStylesMap } from '../transformers/css-to-xcss';
3
4
  import { convertASTObjectExpressionToJSObject } from './convert-ast-object-expression-to-js-object';
4
- export var isValidCssPropertiesToTransform = function isValidCssPropertiesToTransform(node) {
5
+ export var isValidCssPropertiesToTransform = function isValidCssPropertiesToTransform(node, config) {
5
6
  var cssObjectExpression = node.arguments[0];
6
7
  // Bail on empty object calls
7
8
  if (!cssObjectExpression || !isNodeOfType(cssObjectExpression, 'ObjectExpression')) {
@@ -12,6 +13,12 @@ export var isValidCssPropertiesToTransform = function isValidCssPropertiesToTran
12
13
  if (!cssObject || Object.keys(cssObject).length !== 1) {
13
14
  return false;
14
15
  }
16
+ // Short-circuit when token calls are found but pattern is not enabled in config
17
+ if (!config.patterns.includes('css-property-with-tokens') && Object.values(cssObject).some(function (value) {
18
+ return _typeof(value) === 'object' && value.tokenName;
19
+ })) {
20
+ return false;
21
+ }
15
22
 
16
23
  // NOTE: Our approach with this lint rule is to strictly whitelist css properties we can map.
17
24
  // It means we have to provide mappings for everything (e.g. `display: block`).
@@ -19,10 +26,22 @@ export var isValidCssPropertiesToTransform = function isValidCssPropertiesToTran
19
26
  // than the rule reporting on things that can't be mapped.
20
27
  var containsOnlyValidStyles = Object.keys(cssObject).every(function (styleProperty) {
21
28
  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
- ;
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,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,14 @@
1
1
  import type { Rule } from 'eslint';
2
2
  import { ObjectExpression } from 'eslint-codemod-utils';
3
+ type Token = {
4
+ tokenName: string;
5
+ fallbackValue: string | undefined;
6
+ };
3
7
  export type CSSPropStyleObject = {
4
- [key: string]: string | number;
8
+ [key: string]: string | number | Token;
5
9
  };
6
10
  /**
7
11
  * Note: Not recursive. Only handles top level key/value pairs
8
12
  */
9
13
  export declare const convertASTObjectExpressionToJSObject: (styles: ObjectExpression & Partial<Rule.NodeParentExtension>) => CSSPropStyleObject | false;
14
+ 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,14 @@
1
1
  import type { Rule } from 'eslint';
2
2
  import { ObjectExpression } from 'eslint-codemod-utils';
3
+ type Token = {
4
+ tokenName: string;
5
+ fallbackValue: string | undefined;
6
+ };
3
7
  export type CSSPropStyleObject = {
4
- [key: string]: string | number;
8
+ [key: string]: string | number | Token;
5
9
  };
6
10
  /**
7
11
  * Note: Not recursive. Only handles top level key/value pairs
8
12
  */
9
13
  export declare const convertASTObjectExpressionToJSObject: (styles: ObjectExpression & Partial<Rule.NodeParentExtension>) => CSSPropStyleObject | false;
14
+ 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.19.2",
5
5
  "author": "Atlassian Pty Ltd",
6
6
  "publishConfig": {
7
7
  "registry": "https://registry.npmjs.org/"