@atlaskit/eslint-plugin-design-system 8.7.0 → 8.8.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 (26) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +1 -1
  3. package/constellation/index/usage.mdx +17 -2
  4. package/dist/cjs/rules/consistent-css-prop-usage/index.js +107 -95
  5. package/dist/cjs/rules/consistent-css-prop-usage/types.js +5 -0
  6. package/dist/cjs/rules/ensure-design-token-usage/index.js +24 -2
  7. package/dist/cjs/rules/ensure-design-token-usage/rule-meta.js +1 -0
  8. package/dist/cjs/rules/ensure-design-token-usage/spacing.js +19 -4
  9. package/dist/cjs/rules/ensure-design-token-usage/utils.js +9 -4
  10. package/dist/es2019/rules/consistent-css-prop-usage/index.js +62 -51
  11. package/dist/es2019/rules/consistent-css-prop-usage/types.js +1 -0
  12. package/dist/es2019/rules/ensure-design-token-usage/index.js +25 -3
  13. package/dist/es2019/rules/ensure-design-token-usage/rule-meta.js +1 -0
  14. package/dist/es2019/rules/ensure-design-token-usage/spacing.js +19 -4
  15. package/dist/es2019/rules/ensure-design-token-usage/utils.js +7 -3
  16. package/dist/esm/rules/consistent-css-prop-usage/index.js +107 -95
  17. package/dist/esm/rules/consistent-css-prop-usage/types.js +1 -0
  18. package/dist/esm/rules/ensure-design-token-usage/index.js +25 -3
  19. package/dist/esm/rules/ensure-design-token-usage/rule-meta.js +1 -0
  20. package/dist/esm/rules/ensure-design-token-usage/spacing.js +19 -4
  21. package/dist/esm/rules/ensure-design-token-usage/utils.js +7 -3
  22. package/dist/types/rules/consistent-css-prop-usage/types.d.ts +4 -0
  23. package/dist/types/rules/ensure-design-token-usage/utils.d.ts +15 -0
  24. package/dist/types-ts4.5/rules/consistent-css-prop-usage/types.d.ts +4 -0
  25. package/dist/types-ts4.5/rules/ensure-design-token-usage/utils.d.ts +15 -0
  26. package/package.json +2 -2
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # @atlaskit/eslint-plugin-design-system
2
2
 
3
+ ## 8.8.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [`6c149f3e71d`](https://bitbucket.org/atlassian/atlassian-frontend/commits/6c149f3e71d) - The `ensure-design-token-usage` rules now report on use of the CSS `calc` function when used with padding, margin, and gap properties.
8
+
9
+ ## 8.7.1
10
+
11
+ ### Patch Changes
12
+
13
+ - [`d6845989896`](https://bitbucket.org/atlassian/atlassian-frontend/commits/d6845989896) - Configuration added to consistent-css-prop-usage where it is now possible to specify what function names the rule should lint against, and what position is recommended for styles (top or bottom).
14
+
3
15
  ## 8.7.0
4
16
 
5
17
  ### Minor Changes
package/README.md CHANGED
@@ -49,7 +49,7 @@ module.exports = {
49
49
 
50
50
  | Rule | Description | Recommended | Fixable | Suggestions |
51
51
  | ------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- | ------- | ----------- |
52
- | <a href="./src/rules/consistent-css-prop-usage/README.md">consistent-css-prop-usage</a> | Ensures consistency with CSS and xcss prop usages | Yes | Yes | |
52
+ | <a href="./src/rules/consistent-css-prop-usage/README.md">consistent-css-prop-usage</a> | Ensures consistency with `css` and `xcss` prop usages | Yes | Yes | |
53
53
  | <a href="./src/rules/ensure-design-token-usage/README.md">ensure-design-token-usage</a> | Enforces usage of design tokens rather than hard-coded values. | Yes | Yes | Yes |
54
54
  | <a href="./src/rules/ensure-design-token-usage-preview/README.md">ensure-design-token-usage/preview</a> | Enforces usage of pre-release design tokens rather than hard-coded values. | | Yes | Yes |
55
55
  | <a href="./src/rules/icon-label/README.md">icon-label</a> | Enforces accessible usage of icon labels when composed with Atlassian Design System components. | Yes | Yes | |
@@ -13,7 +13,7 @@ This plugin contains rules that should be used when working with the [Atlassian
13
13
 
14
14
  | Rule | Description | Recommended | Fixable | Suggestions |
15
15
  | ---------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- | ------- | ----------- |
16
- | <a href="#consistent-css-prop-usage">consistent-css-prop-usage</a> | Ensures consistency with CSS and xcss prop usages | Yes | Yes | |
16
+ | <a href="#consistent-css-prop-usage">consistent-css-prop-usage</a> | Ensures consistency with `css` and `xcss` prop usages | Yes | Yes | |
17
17
  | <a href="#ensure-design-token-usage">ensure-design-token-usage</a> | Enforces usage of design tokens rather than hard-coded values. | Yes | Yes | Yes |
18
18
  | <a href="#ensure-design-token-usage-preview">ensure-design-token-usage/preview</a> | Enforces usage of pre-release design tokens rather than hard-coded values. | | Yes | Yes |
19
19
  | <a href="#icon-label">icon-label</a> | Enforces accessible usage of icon labels when composed with Atlassian Design System components. | Yes | Yes | |
@@ -51,7 +51,9 @@ This rule checks for the following cases:
51
51
  - When styles are coming from outside of the module i.e. using imports.
52
52
  - When styles are spread inside another styles and not using array composition.
53
53
 
54
- This rule has no options.
54
+ All the above can also work for custom `css` functions, such as `xcss` (https://atlassian.design/components/primitives/xcss/).
55
+
56
+ This rule has options - see below.
55
57
 
56
58
  <h3>Examples</h3>
57
59
 
@@ -128,6 +130,19 @@ function Button({ children }) {
128
130
  }
129
131
  ```
130
132
 
133
+ <h3>Options</h3>
134
+
135
+ This rule comes with options to support different repository configurations.
136
+
137
+ #### cssFunctions
138
+
139
+ An array of function names the linting rule should target. Defaults to `['css', 'xcss']`.
140
+
141
+ #### stylesPlacement
142
+
143
+ The rule prevents inline styles from being created. This option defines what the error message should say: "(...) styles at the top (...)" or "(...) styles at the bottom (...)".
144
+ Defaults to `top`.
145
+
131
146
  ## ensure-design-token-usage
132
147
 
133
148
  Using design tokens results in a harmonious experience for end users whilst providing theming and consistency.
@@ -5,12 +5,14 @@ Object.defineProperty(exports, "__esModule", {
5
5
  value: true
6
6
  });
7
7
  exports.default = void 0;
8
+ var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
8
9
  var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
9
10
  var _eslintCodemodUtils = require("eslint-codemod-utils");
11
+ var _assign = _interopRequireDefault(require("lodash/assign"));
10
12
  var _createRule = require("../utils/create-rule");
11
13
  var declarationSuffix = 'Styles';
12
- function isCssCallExpression(node) {
13
- return !!((0, _eslintCodemodUtils.isNodeOfType)(node, 'CallExpression') && node.callee && node.callee.type === 'Identifier' && (node.callee.name === 'css' || node.callee.name === 'xcss') && node.arguments.length && node.arguments[0].type === 'ObjectExpression');
14
+ function isCssCallExpression(node, cssFunctions) {
15
+ return !!((0, _eslintCodemodUtils.isNodeOfType)(node, 'CallExpression') && node.callee && node.callee.type === 'Identifier' && cssFunctions.includes(node.callee.name) && node.arguments.length && node.arguments[0].type === 'ObjectExpression');
14
16
  }
15
17
  function findSpreadProperties(node) {
16
18
  // @ts-ignore
@@ -20,7 +22,7 @@ function findSpreadProperties(node) {
20
22
  property.type === 'ExperimentalSpreadProperty';
21
23
  });
22
24
  }
23
- function analyzeIdentifier(context, sourceIdentifier) {
25
+ function analyzeIdentifier(context, sourceIdentifier, configuration) {
24
26
  var _getIdentifierInParen;
25
27
  var scope = context.getScope();
26
28
  var _ref = ((_getIdentifierInParen = (0, _eslintCodemodUtils.getIdentifierInParentScope)(scope, sourceIdentifier.name)) === null || _getIdentifierInParen === void 0 ? void 0 : _getIdentifierInParen.identifiers) || [],
@@ -42,11 +44,11 @@ function analyzeIdentifier(context, sourceIdentifier) {
42
44
  // When variable is declared inside the component
43
45
  context.report({
44
46
  node: sourceIdentifier,
45
- messageId: 'cssOnTopOfModule'
47
+ messageId: configuration.stylesPlacement === 'bottom' ? 'cssAtBottomOfModule' : 'cssOnTopOfModule'
46
48
  });
47
49
  return;
48
50
  }
49
- if (identifier.parent && identifier.parent.init && !isCssCallExpression(identifier.parent.init)) {
51
+ if (identifier.parent && identifier.parent.init && !isCssCallExpression(identifier.parent.init, configuration.cssFunctions)) {
50
52
  // When variable value is not of type css({})
51
53
  context.report({
52
54
  node: identifier,
@@ -65,117 +67,127 @@ function analyzeIdentifier(context, sourceIdentifier) {
65
67
  });
66
68
  }
67
69
  }
68
- function traverseExpression(context, expression) {
69
- switch (expression.type) {
70
- case 'Identifier':
71
- // {styles}
72
- // We've found an identifier - time to analyze it!
73
- analyzeIdentifier(context, expression);
74
- break;
75
- case 'ArrayExpression':
76
- // {[styles, moreStyles]}
77
- // We've found an array expression - let's traverse again over each element individually.
78
- expression.elements.forEach(function (element) {
79
- return traverseExpression(context, element);
80
- });
81
- break;
82
- case 'LogicalExpression':
83
- // {isEnabled && styles}
84
- // We've found a logical expression - we're only interested in the right expression so
85
- // let's traverse that and see what it is!
86
- traverseExpression(context, expression.right);
87
- break;
88
- case 'ConditionalExpression':
89
- // {isEnabled ? styles : null}
90
- // We've found a conditional expression - we're only interested in the consequent and
91
- // alternate (styles : null)
92
- traverseExpression(context, expression.consequent);
93
- traverseExpression(context, expression.alternate);
94
- break;
95
- case 'CallExpression':
96
- case 'ObjectExpression':
97
- case 'TaggedTemplateExpression':
98
- case 'TemplateLiteral':
99
- // We've found elements that shouldn't be here! Report an error.
100
- context.report({
101
- node: expression,
102
- messageId: 'cssOnTopOfModule'
103
- });
104
- break;
105
- default:
106
- // Do nothing!
107
- break;
70
+ var traverseExpressionWithConfig = function traverseExpressionWithConfig(context, expression, configuration) {
71
+ function traverseExpression(expression) {
72
+ switch (expression.type) {
73
+ case 'Identifier':
74
+ // {styles}
75
+ // We've found an identifier - time to analyze it!
76
+ analyzeIdentifier(context, expression, configuration);
77
+ break;
78
+ case 'ArrayExpression':
79
+ // {[styles, moreStyles]}
80
+ // We've found an array expression - let's traverse again over each element individually.
81
+ expression.elements.forEach(function (element) {
82
+ return traverseExpression(element);
83
+ });
84
+ break;
85
+ case 'LogicalExpression':
86
+ // {isEnabled && styles}
87
+ // We've found a logical expression - we're only interested in the right expression so
88
+ // let's traverse that and see what it is!
89
+ traverseExpression(expression.right);
90
+ break;
91
+ case 'ConditionalExpression':
92
+ // {isEnabled ? styles : null}
93
+ // We've found a conditional expression - we're only interested in the consequent and
94
+ // alternate (styles : null)
95
+ traverseExpression(expression.consequent);
96
+ traverseExpression(expression.alternate);
97
+ break;
98
+ case 'CallExpression':
99
+ case 'ObjectExpression':
100
+ case 'TaggedTemplateExpression':
101
+ case 'TemplateLiteral':
102
+ // We've found elements that shouldn't be here! Report an error.
103
+ context.report({
104
+ node: expression,
105
+ messageId: configuration.stylesPlacement === 'bottom' ? 'cssAtBottomOfModule' : 'cssOnTopOfModule'
106
+ });
107
+ break;
108
+ default:
109
+ // Do nothing!
110
+ break;
111
+ }
108
112
  }
109
- }
113
+ traverseExpression(expression);
114
+ };
115
+ var defaultConfig = {
116
+ cssFunctions: ['css', 'xcss'],
117
+ stylesPlacement: 'top'
118
+ };
110
119
  var rule = (0, _createRule.createLintRule)({
111
120
  meta: {
112
121
  name: 'consistent-css-prop-usage',
113
122
  docs: {
114
- description: 'Ensures consistency with CSS and xcss prop usages',
123
+ description: 'Ensures consistency with `css` and `xcss` prop usages',
115
124
  url: 'https://developer.atlassian.com/cloud/framework/atlassian-frontend/development/styling',
116
125
  recommended: true,
117
126
  severity: 'error'
118
127
  },
119
128
  fixable: 'code',
120
129
  messages: {
121
- cssOnTopOfModule: "Create styles at the top of the module scope using the css function.",
130
+ cssOnTopOfModule: "Create styles at the top of the module scope using the respective css function.",
131
+ cssAtBottomOfModule: "Create styles at the bottom of the module scope using the respective css function.",
122
132
  cssObjectTypeOnly: "Create styles using objects passed to the css function.",
123
133
  cssInModule: "Imported styles should not be used, instead define in the module, import a component, or use a design token.",
124
- cssArrayStylesOnly: "Compose styles with an array on the CSS prop instead of using object spread.",
134
+ cssArrayStylesOnly: "Compose styles with an array on the css prop instead of using object spread.",
125
135
  shouldEndInStyles: 'Declared styles should end in "styles".'
126
136
  }
127
137
  },
128
138
  create: function create(context) {
129
- return {
130
- 'CallExpression[callee.name=css], CallExpression[callee.name=xcss]': function CallExpressionCalleeNameCssCallExpressionCalleeNameXcss(node) {
131
- if (node.parent.type !== 'VariableDeclarator') {
132
- // We aren't interested in these that don't have a parent.
133
- return;
139
+ var _ref3;
140
+ var mergedConfig = (0, _assign.default)({}, defaultConfig, context.options[0]);
141
+ var callSelector = mergedConfig.cssFunctions.map(function (fn) {
142
+ return "CallExpression[callee.name=".concat(fn, "]");
143
+ }).join(',');
144
+ return _ref3 = {}, (0, _defineProperty2.default)(_ref3, callSelector, function (node) {
145
+ if (node.parent.type !== 'VariableDeclarator') {
146
+ // We aren't interested in these that don't have a parent.
147
+ return;
148
+ }
149
+ var identifier = node.parent.id;
150
+ if (identifier.type === 'Identifier' && identifier.name.endsWith(declarationSuffix)) {
151
+ // Already prefixed! Nothing to do.
152
+ return;
153
+ }
154
+ context.report({
155
+ node: identifier,
156
+ messageId: 'shouldEndInStyles',
157
+ fix: function fix(fixer) {
158
+ var _context$getScope$var;
159
+ var identifierName = identifier.type === 'Identifier' ? identifier.name : '';
160
+ var references = ((_context$getScope$var = context.getScope().variables.find(function (varb) {
161
+ return varb.name === identifierName;
162
+ })) === null || _context$getScope$var === void 0 ? void 0 : _context$getScope$var.references) || [];
163
+ var newIdentifierName = "".concat(identifierName
164
+ // Remove "Style" if it is already defined.
165
+ .replace(/Style$/, '')).concat(declarationSuffix);
166
+ return references.filter(function (ref) {
167
+ return ref.identifier.name === identifierName;
168
+ }).map(function (ref) {
169
+ if (ref.identifier.parent && ref.identifier.parent.shorthand) {
170
+ return fixer.replaceText(ref.identifier, "".concat(identifierName, ": ").concat(newIdentifierName));
171
+ }
172
+ return fixer.replaceText(ref.identifier, newIdentifierName);
173
+ });
134
174
  }
135
- var identifier = node.parent.id;
136
- if (identifier.type === 'Identifier' && identifier.name.endsWith(declarationSuffix)) {
137
- // Already prefixed! Nothing to do.
175
+ });
176
+ }), (0, _defineProperty2.default)(_ref3, "JSXAttribute", function JSXAttribute(node) {
177
+ var name = node.name,
178
+ value = node.value;
179
+ if (name.type === 'JSXIdentifier' && mergedConfig.cssFunctions.includes(name.name)) {
180
+ // When not a jsx expression. For eg. css=""
181
+ if ((value === null || value === void 0 ? void 0 : value.type) !== 'JSXExpressionContainer') {
182
+ context.report({
183
+ node: value,
184
+ messageId: mergedConfig.stylesPlacement === 'bottom' ? 'cssAtBottomOfModule' : 'cssOnTopOfModule'
185
+ });
138
186
  return;
139
187
  }
140
- context.report({
141
- node: identifier,
142
- messageId: 'shouldEndInStyles',
143
- fix: function fix(fixer) {
144
- var _context$getScope$var;
145
- var identifierName = identifier.type === 'Identifier' ? identifier.name : '';
146
- var references = ((_context$getScope$var = context.getScope().variables.find(function (varb) {
147
- return varb.name === identifierName;
148
- })) === null || _context$getScope$var === void 0 ? void 0 : _context$getScope$var.references) || [];
149
- var newIdentifierName = "".concat(identifierName
150
- // Remove "Style" if it is already defined.
151
- .replace(/Style$/, '')).concat(declarationSuffix);
152
- return references.filter(function (ref) {
153
- return ref.identifier.name === identifierName;
154
- }).map(function (ref) {
155
- if (ref.identifier.parent && ref.identifier.parent.shorthand) {
156
- return fixer.replaceText(ref.identifier, "".concat(identifierName, ": ").concat(newIdentifierName));
157
- }
158
- return fixer.replaceText(ref.identifier, newIdentifierName);
159
- });
160
- }
161
- });
162
- },
163
- JSXAttribute: function JSXAttribute(node) {
164
- var name = node.name,
165
- value = node.value;
166
- if (name.type === 'JSXIdentifier' && (name.name === 'css' || name.name === 'xcss')) {
167
- // When not a jsx expression. For eg. css=""
168
- if ((value === null || value === void 0 ? void 0 : value.type) !== 'JSXExpressionContainer') {
169
- context.report({
170
- node: value,
171
- messageId: 'cssOnTopOfModule'
172
- });
173
- return;
174
- }
175
- traverseExpression(context, value.expression);
176
- }
188
+ traverseExpressionWithConfig(context, value.expression, mergedConfig);
177
189
  }
178
- };
190
+ }), _ref3;
179
191
  }
180
192
  });
181
193
  var _default = rule;
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
@@ -79,8 +79,20 @@ var createWithConfig = function createWithConfig(initialConfig) {
79
79
  if (domains.length === 0 || (0, _isNode.isDecendantOfGlobalToken)(node.value)) {
80
80
  return;
81
81
  }
82
- if ((0, _eslintCodemodUtils.isNodeOfType)(node.value, 'TemplateLiteral') && node.value.expressions.some(_isNode.isDecendantOfGlobalToken)) {
83
- return;
82
+ if ((0, _eslintCodemodUtils.isNodeOfType)(node.value, 'TemplateLiteral')) {
83
+ var value = (0, _utils.getValueFromTemplateLiteralRaw)(node.value, context);
84
+ if (Array.isArray(value) && value.some(_utils.isCalc)) {
85
+ return context.report({
86
+ node: node,
87
+ messageId: 'noCalcUsage',
88
+ data: {
89
+ payload: "".concat(propertyName)
90
+ }
91
+ });
92
+ }
93
+ if (node.value.expressions.some(_isNode.isDecendantOfGlobalToken)) {
94
+ return;
95
+ }
84
96
  }
85
97
  if (domains.includes('color')) {
86
98
  return (0, _color.lintObjectForColor)(node, context, config);
@@ -172,6 +184,16 @@ var createWithConfig = function createWithConfig(initialConfig) {
172
184
  if (!originalValue) {
173
185
  return originalValue;
174
186
  }
187
+ if ((0, _utils.isCalc)(originalValue)) {
188
+ context.report({
189
+ node: node,
190
+ messageId: 'noCalcUsage',
191
+ data: {
192
+ payload: "".concat(propertyName)
193
+ }
194
+ });
195
+ return originalValue;
196
+ }
175
197
  if ((0, _utils.isTokenValueString)(originalValue)) {
176
198
  // if the value is already valid, nothing to report or replace
177
199
  return originalValue;
@@ -42,6 +42,7 @@ var ruleMeta = {
42
42
  noRawRadiusValues: 'The use of shape tokens is preferred over the direct application of border properties.\n\n@meta <<{{payload}}>>',
43
43
  noRawSpacingValues: 'The use of spacing primitives or tokens is preferred over the direct application of spacing properties.\n\n@meta <<{{payload}}>>',
44
44
  autofixesPossible: 'Automated corrections available for spacing values. Apply autofix to replace values with appropriate tokens',
45
+ noCalcUsage: 'The use of space tokens is preferred over using the CSS calc function. If using a value that is not aligned to the spacing scale, consider aligning to the scale and using tokens instead.',
45
46
  hardCodedColor: "Colors can be sourced from the global theme using the token function.",
46
47
  legacyElevation: "Elevations can be sourced from the global theme using the token function made of both a background and shadow. Use \"card\" for card elevations, and \"overlay\" for anything else that should appear elevated."
47
48
  }
@@ -22,13 +22,19 @@ var lintObjectForSpacing = function lintObjectForSpacing(node, context, ruleConf
22
22
  });
23
23
  return;
24
24
  }
25
+ var propertyName = node.key.name;
26
+ var isFontFamily = /fontFamily/.test(propertyName);
25
27
 
26
- // Don't report on CSS calc function
28
+ // Report on CSS calc function for strings
27
29
  if ((0, _eslintCodemodUtils.isNodeOfType)(node.value, 'Literal') && (0, _utils.isCalc)(node.value.value)) {
28
- return;
30
+ return context.report({
31
+ node: node,
32
+ messageId: 'noCalcUsage',
33
+ data: {
34
+ payload: "".concat(propertyName)
35
+ }
36
+ });
29
37
  }
30
- var propertyName = node.key.name;
31
- var isFontFamily = /fontFamily/.test(propertyName);
32
38
  var value = (0, _utils.getValue)(node.value, context);
33
39
 
34
40
  // Value is a token string (e.g. set via a variable)
@@ -87,6 +93,15 @@ var lintObjectForSpacing = function lintObjectForSpacing(node, context, ruleConf
87
93
  * { padding: '8px 0px' }
88
94
  */
89
95
  valuesForProperty.forEach(function (val) {
96
+ if ((0, _utils.isCalc)(val)) {
97
+ return context.report({
98
+ node: node,
99
+ messageId: 'noCalcUsage',
100
+ data: {
101
+ payload: "".concat(propertyName, ":").concat(val)
102
+ }
103
+ });
104
+ }
90
105
  var pixelValue = (0, _utils.emToPixels)(val, fontSize);
91
106
 
92
107
  // Do not report or suggest a token to replace 0 or auto
@@ -13,7 +13,7 @@ exports.getFontSizeValueInScope = getFontSizeValueInScope;
13
13
  exports.getRawExpression = void 0;
14
14
  exports.getTokenNodeForValue = getTokenNodeForValue;
15
15
  exports.getTokenReplacement = getTokenReplacement;
16
- exports.getValueFromShorthand = exports.getValue = void 0;
16
+ exports.getValueFromTemplateLiteralRaw = exports.getValueFromShorthand = exports.getValue = void 0;
17
17
  exports.includesTokenString = includesTokenString;
18
18
  exports.insertTokensImport = insertTokensImport;
19
19
  exports.isSpacingProperty = exports.isCalc = exports.isAuto = void 0;
@@ -207,7 +207,7 @@ var getValueFromUnaryExpression = function getValueFromUnaryExpression(node, con
207
207
  * // results in [2, 4, 0]
208
208
  * ```
209
209
  */
210
- var getValueFromTemplateLiteral = function getValueFromTemplateLiteral(node, context) {
210
+ var getValueFromTemplateLiteralRaw = function getValueFromTemplateLiteralRaw(node, context) {
211
211
  if (!(0, _eslintCodemodUtils.isNodeOfType)(node, "TemplateLiteral")) {
212
212
  return null;
213
213
  }
@@ -218,7 +218,12 @@ var getValueFromTemplateLiteral = function getValueFromTemplateLiteral(node, con
218
218
  if (fontFamily.test(combinedString)) {
219
219
  return combinedString;
220
220
  }
221
- return combinedString.split(' ').map(removePixelSuffix);
221
+ return combinedString.split(' ');
222
+ };
223
+ exports.getValueFromTemplateLiteralRaw = getValueFromTemplateLiteralRaw;
224
+ var getValueFromTemplateLiteral = function getValueFromTemplateLiteral(node, context) {
225
+ var value = getValueFromTemplateLiteralRaw(node, context);
226
+ return Array.isArray(value) ? value.map(removePixelSuffix) : value;
222
227
  };
223
228
  var getValueFromBinaryExpression = function getValueFromBinaryExpression(node, context) {
224
229
  if (!(0, _eslintCodemodUtils.isNodeOfType)(node, 'BinaryExpression')) {
@@ -252,7 +257,7 @@ var emToPixels = function emToPixels(value, fontSize) {
252
257
  exports.emToPixels = emToPixels;
253
258
  var percentageOrEmOrAuto = /(%$)|(\d+em$)|(auto$)/;
254
259
  var removePixelSuffix = function removePixelSuffix(value) {
255
- if (typeof value === 'string' && percentageOrEmOrAuto.test(value)) {
260
+ if (typeof value === 'string' && (percentageOrEmOrAuto.test(value) || isCalc(value))) {
256
261
  return value;
257
262
  }
258
263
  return Number(typeof value === 'string' ? value.replace('px', '') : value);