@atlaskit/eslint-plugin-design-system 8.23.0 → 8.23.1

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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # @atlaskit/eslint-plugin-design-system
2
2
 
3
+ ## 8.23.1
4
+
5
+ ### Patch Changes
6
+
7
+ - [#68093](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/pull-requests/68093) [`4c5371a76547`](https://stash.atlassian.com/projects/CONFCLOUD/repos/confluence-frontend/commits/4c5371a76547) - Wrap ensure-design-token-usage with an error boudary to stop it breaking issue-automat CI.
8
+
3
9
  ## 8.23.0
4
10
 
5
11
  ### Minor Changes
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.errorBoundary = void 0;
7
+ /**
8
+ * ESLint rules should NEVER throw exceptions, because that breaks the VSCode ESLint server
9
+ * (and probably the IntelliJ one too), which causes linting to fail in a file.
10
+ *
11
+ * It also breaks CI, which was the reason this error boundary was added. It's a final
12
+ * catch all.
13
+ */
14
+ var errorBoundary = exports.errorBoundary = function errorBoundary(func, _ref) {
15
+ var config = _ref.config;
16
+ try {
17
+ func();
18
+ } catch (err) {
19
+ if (!config.failSilently) {
20
+ // eslint-disable-next-line no-console
21
+ console.warn(err);
22
+ }
23
+ }
24
+ };
@@ -11,6 +11,7 @@ var _createRule = require("../utils/create-rule");
11
11
  var _isColor = require("../utils/is-color");
12
12
  var _isNode = require("../utils/is-node");
13
13
  var _color = require("./color");
14
+ var _errorBoundary = require("./error-boundary");
14
15
  var _ruleMeta = _interopRequireDefault(require("./rule-meta"));
15
16
  var _spacing = require("./spacing");
16
17
  var _utils = require("./utils");
@@ -19,7 +20,8 @@ var _utils = require("./utils");
19
20
  var defaultConfig = {
20
21
  domains: ['color', 'spacing'],
21
22
  applyImport: true,
22
- shouldEnforceFallbacks: false
23
+ shouldEnforceFallbacks: false,
24
+ failSilently: false
23
25
  };
24
26
  var createWithConfig = exports.createWithConfig = function createWithConfig(initialConfig) {
25
27
  return function (context) {
@@ -29,18 +31,27 @@ var createWithConfig = exports.createWithConfig = function createWithConfig(init
29
31
  domains: (userConfig === null || userConfig === void 0 ? void 0 : userConfig.domains) || initialConfig.domains,
30
32
  applyImport: (userConfig === null || userConfig === void 0 ? void 0 : userConfig.applyImport) !== undefined ? userConfig.applyImport : initialConfig.applyImport,
31
33
  shouldEnforceFallbacks: (userConfig === null || userConfig === void 0 ? void 0 : userConfig.shouldEnforceFallbacks) !== undefined ? userConfig.shouldEnforceFallbacks : initialConfig.shouldEnforceFallbacks,
32
- exceptions: (userConfig === null || userConfig === void 0 ? void 0 : userConfig.exceptions) || []
34
+ exceptions: (userConfig === null || userConfig === void 0 ? void 0 : userConfig.exceptions) || [],
35
+ failSilently: (userConfig === null || userConfig === void 0 ? void 0 : userConfig.failSilently) || defaultConfig.failSilently
33
36
  };
34
37
  var tokenNode = null;
35
38
  return {
36
39
  ImportDeclaration: function ImportDeclaration(node) {
37
- if (node.source.value === '@atlaskit/tokens' && config.applyImport) {
38
- tokenNode = node;
39
- }
40
+ return (0, _errorBoundary.errorBoundary)(function () {
41
+ if (node.source.value === '@atlaskit/tokens' && config.applyImport) {
42
+ tokenNode = node;
43
+ }
44
+ }, {
45
+ config: config
46
+ });
40
47
  },
41
48
  // For expressions within template literals (e.g. `color: ${red}`) - color only
42
49
  'TemplateLiteral > Identifier': function TemplateLiteralIdentifier(node) {
43
- return (0, _color.lintTemplateIdentifierForColor)(node, context, config);
50
+ return (0, _errorBoundary.errorBoundary)(function () {
51
+ return (0, _color.lintTemplateIdentifierForColor)(node, context, config);
52
+ }, {
53
+ config: config
54
+ });
44
55
  },
45
56
  // const styles = css({ color: 'red', margin: '4px' }), styled.div({ color: 'red', margin: '4px' })
46
57
  ObjectExpression: function (_ObjectExpression) {
@@ -52,220 +63,240 @@ var createWithConfig = exports.createWithConfig = function createWithConfig(init
52
63
  };
53
64
  return ObjectExpression;
54
65
  }(function (parentNode) {
55
- // To force the correct node type
56
- if (!(0, _eslintCodemodUtils.isNodeOfType)(parentNode, 'ObjectExpression')) {
57
- return;
58
- }
59
-
60
- // Return for nested objects - these get handled automatically so without returning we'd be doubling up
61
- if (parentNode.parent.type === 'Property') {
62
- return;
63
- }
64
- if (!(0, _isNode.isDecendantOfStyleBlock)(parentNode) && !(0, _isNode.isDecendantOfType)(parentNode, 'JSXExpressionContainer')) {
65
- return;
66
- }
67
- function findObjectStyles(node) {
68
- if (!(0, _eslintCodemodUtils.isNodeOfType)(node, 'Property')) {
66
+ return (0, _errorBoundary.errorBoundary)(function () {
67
+ // To force the correct node type
68
+ if (!(0, _eslintCodemodUtils.isNodeOfType)(parentNode, 'ObjectExpression')) {
69
69
  return;
70
70
  }
71
- if ((0, _eslintCodemodUtils.isNodeOfType)(node.value, 'ObjectExpression')) {
72
- return node.value.properties.forEach(findObjectStyles);
73
- }
74
- if (!(0, _eslintCodemodUtils.isNodeOfType)(node.key, 'Identifier') && !(0, _eslintCodemodUtils.isNodeOfType)(node.key, 'Literal')) {
71
+
72
+ // Return for nested objects - these get handled automatically so without returning we'd be doubling up
73
+ if (parentNode.parent.type === 'Property') {
75
74
  return;
76
75
  }
77
- var propertyName = (0, _eslintCodemodUtils.isNodeOfType)(node.key, 'Identifier') ? node.key.name : String(node.key.value);
78
-
79
- // Returns which domains to lint against based on rule's config and current property
80
- var domains = (0, _utils.getDomainsForProperty)(propertyName, config.domains);
81
- if (domains.length === 0 || (0, _isNode.isDecendantOfGlobalToken)(node.value)) {
76
+ if (!(0, _isNode.isDecendantOfStyleBlock)(parentNode) && !(0, _isNode.isDecendantOfType)(parentNode, 'JSXExpressionContainer')) {
82
77
  return;
83
78
  }
84
- if ((0, _eslintCodemodUtils.isNodeOfType)(node.value, 'TemplateLiteral')) {
85
- var value = (0, _utils.getValueFromTemplateLiteralRaw)(node.value, context);
86
- if (Array.isArray(value) && value.some(_utils.isCalc)) {
87
- return context.report({
88
- node: node,
89
- messageId: 'noCalcUsage',
90
- data: {
91
- payload: "".concat(propertyName)
92
- }
93
- });
79
+ function findObjectStyles(node) {
80
+ if (!(0, _eslintCodemodUtils.isNodeOfType)(node, 'Property')) {
81
+ return;
94
82
  }
95
- if (node.value.expressions.some(_isNode.isDecendantOfGlobalToken)) {
83
+ if ((0, _eslintCodemodUtils.isNodeOfType)(node.value, 'ObjectExpression')) {
84
+ return node.value.properties.forEach(findObjectStyles);
85
+ }
86
+ if (!(0, _eslintCodemodUtils.isNodeOfType)(node.key, 'Identifier') && !(0, _eslintCodemodUtils.isNodeOfType)(node.key, 'Literal')) {
96
87
  return;
97
88
  }
89
+ var propertyName = (0, _eslintCodemodUtils.isNodeOfType)(node.key, 'Identifier') ? node.key.name : String(node.key.value);
90
+
91
+ // Returns which domains to lint against based on rule's config and current property
92
+ var domains = (0, _utils.getDomainsForProperty)(propertyName, config.domains);
93
+ if (domains.length === 0 || (0, _isNode.isDecendantOfGlobalToken)(node.value)) {
94
+ return;
95
+ }
96
+ if ((0, _eslintCodemodUtils.isNodeOfType)(node.value, 'TemplateLiteral')) {
97
+ var value = (0, _utils.getValueFromTemplateLiteralRaw)(node.value, context);
98
+ if (Array.isArray(value) && value.some(_utils.isCalc)) {
99
+ return context.report({
100
+ node: node,
101
+ messageId: 'noCalcUsage',
102
+ data: {
103
+ payload: "".concat(propertyName)
104
+ }
105
+ });
106
+ }
107
+ if (node.value.expressions.some(_isNode.isDecendantOfGlobalToken)) {
108
+ return;
109
+ }
110
+ }
111
+ if (domains.includes('color')) {
112
+ return (0, _color.lintObjectForColor)(node, context, config);
113
+ }
114
+ if (domains.includes('spacing') || domains.includes('shape') || domains.includes('typography')) {
115
+ /**
116
+ * We do this in case the fontSize for a style object is declared alongside the `em` or `lineHeight` declaration
117
+ */
118
+ var fontSize = (0, _utils.getFontSizeFromNode)(parentNode, context);
119
+ return (0, _spacing.lintObjectForSpacing)(node, context, config, fontSize, tokenNode);
120
+ }
98
121
  }
99
- if (domains.includes('color')) {
100
- return (0, _color.lintObjectForColor)(node, context, config);
101
- }
102
- if (domains.includes('spacing') || domains.includes('shape') || domains.includes('typography')) {
103
- /**
104
- * We do this in case the fontSize for a style object is declared alongside the `em` or `lineHeight` declaration
105
- */
106
- var fontSize = (0, _utils.getFontSizeFromNode)(parentNode, context);
107
- return (0, _spacing.lintObjectForSpacing)(node, context, config, fontSize, tokenNode);
108
- }
109
- }
110
- parentNode.properties.forEach(findObjectStyles);
122
+ parentNode.properties.forEach(findObjectStyles);
123
+ }, {
124
+ config: config
125
+ });
111
126
  }),
112
127
  // CSSTemplateLiteral and StyledTemplateLiteral
113
128
  // const cssTemplateLiteral = css`color: red; padding: 12px`;
114
129
  // const styledTemplateLiteral = styled.p`color: red; padding: 8px`;
115
130
  'TaggedTemplateExpression[tag.name="css"],TaggedTemplateExpression[tag.object.name="styled"],TaggedTemplateExpression[tag.callee.name="styled"]': function TaggedTemplateExpressionTagNameCssTaggedTemplateExpressionTagObjectNameStyledTaggedTemplateExpressionTagCalleeNameStyled(node) {
116
- // To force the correct node type
117
- if (!(0, _eslintCodemodUtils.isNodeOfType)(node, 'TaggedTemplateExpression')) {
118
- return;
119
- }
120
- var processedCssLines = (0, _utils.processCssNode)(node, context);
121
- if (!processedCssLines) {
122
- // if we can't get a processed css we bail
123
- return;
124
- }
125
- var globalFontSize = (0, _utils.getFontSizeValueInScope)(processedCssLines);
126
- var textForSource = context.getSourceCode().getText(node.quasi);
127
- var allReplacedValues = [];
128
- var completeSource = processedCssLines.reduce(function (currentSource, _ref) {
129
- var _ref2 = (0, _slicedToArray2.default)(_ref, 2),
130
- resolvedCssLine = _ref2[0],
131
- originalCssLine = _ref2[1];
132
- var _resolvedCssLine$spli = resolvedCssLine.split(':'),
133
- _resolvedCssLine$spli2 = (0, _slicedToArray2.default)(_resolvedCssLine$spli, 2),
134
- originalProperty = _resolvedCssLine$spli2[0],
135
- resolvedCssValues = _resolvedCssLine$spli2[1];
136
- var _originalCssLine$spli = originalCssLine.split(':'),
137
- _originalCssLine$spli2 = (0, _slicedToArray2.default)(_originalCssLine$spli, 2),
138
- _ = _originalCssLine$spli2[0],
139
- originalCssValues = _originalCssLine$spli2[1];
140
- var propertyName = (0, _utils.convertHyphenatedNameToCamelCase)(originalProperty);
141
- var isFontFamily = /fontFamily/.test(propertyName);
142
- var replacedValuesPerProperty = [originalProperty];
143
- var domains = (0, _utils.getDomainsForProperty)(propertyName, config.domains);
144
- if (domains.length === 0 || !resolvedCssValues) {
145
- // in both of these cases no changes should be made to the current property
146
- return currentSource;
131
+ return (0, _errorBoundary.errorBoundary)(function () {
132
+ // To force the correct node type
133
+ if (!(0, _eslintCodemodUtils.isNodeOfType)(node, 'TaggedTemplateExpression')) {
134
+ return;
147
135
  }
148
- if (domains.includes('color')) {
149
- if ((0, _utils.includesTokenString)(resolvedCssValues.trim())) {
150
- return currentSource;
151
- }
152
- if ((0, _isColor.includesHardCodedColor)(resolvedCssValues)) {
153
- context.report({
154
- messageId: 'hardCodedColor',
155
- node: node
156
- });
157
- return currentSource;
158
- }
136
+ var processedCssLines = (0, _utils.processCssNode)(node, context);
137
+ if (!processedCssLines) {
138
+ // if we can't get a processed css we bail
139
+ return;
159
140
  }
160
- if (domains.includes('spacing') || domains.includes('typography') || domains.includes('shape')) {
161
- if (!(0, _utils.isValidSpacingValue)(resolvedCssValues, globalFontSize)) {
162
- // no changes should be made to the current property
141
+ var globalFontSize = (0, _utils.getFontSizeValueInScope)(processedCssLines);
142
+ var textForSource = context.getSourceCode().getText(node.quasi);
143
+ var allReplacedValues = [];
144
+ var completeSource = processedCssLines.reduce(function (currentSource, _ref) {
145
+ var _ref2 = (0, _slicedToArray2.default)(_ref, 2),
146
+ resolvedCssLine = _ref2[0],
147
+ originalCssLine = _ref2[1];
148
+ var _resolvedCssLine$spli = resolvedCssLine.split(':'),
149
+ _resolvedCssLine$spli2 = (0, _slicedToArray2.default)(_resolvedCssLine$spli, 2),
150
+ originalProperty = _resolvedCssLine$spli2[0],
151
+ resolvedCssValues = _resolvedCssLine$spli2[1];
152
+ var _originalCssLine$spli = originalCssLine.split(':'),
153
+ _originalCssLine$spli2 = (0, _slicedToArray2.default)(_originalCssLine$spli, 2),
154
+ _ = _originalCssLine$spli2[0],
155
+ originalCssValues = _originalCssLine$spli2[1];
156
+ var propertyName = (0, _utils.convertHyphenatedNameToCamelCase)(originalProperty);
157
+ var isFontFamily = /fontFamily/.test(propertyName);
158
+ var replacedValuesPerProperty = [originalProperty];
159
+ var domains = (0, _utils.getDomainsForProperty)(propertyName, config.domains);
160
+ if (domains.length === 0 || !resolvedCssValues) {
161
+ // in both of these cases no changes should be made to the current property
163
162
  return currentSource;
164
163
  }
164
+ if (domains.includes('color')) {
165
+ if ((0, _utils.includesTokenString)(resolvedCssValues.trim())) {
166
+ return currentSource;
167
+ }
168
+ if ((0, _isColor.includesHardCodedColor)(resolvedCssValues)) {
169
+ context.report({
170
+ messageId: 'hardCodedColor',
171
+ node: node
172
+ });
173
+ return currentSource;
174
+ }
175
+ }
176
+ if (domains.includes('spacing') || domains.includes('typography') || domains.includes('shape')) {
177
+ if (!(0, _utils.isValidSpacingValue)(resolvedCssValues, globalFontSize)) {
178
+ // no changes should be made to the current property
179
+ return currentSource;
180
+ }
165
181
 
166
- // gets the values from the associated property, numeric values or NaN
167
- var processedNumericValues = (0, _utils.getValueFromShorthand)(resolvedCssValues);
168
- var processedValues = (0, _utils.splitShorthandValues)(resolvedCssValues);
169
- // only splits shorthand values but it does not transform NaNs so tokens are preserved
170
- var originalValues = (0, _utils.splitShorthandValues)(originalCssValues);
182
+ // gets the values from the associated property, numeric values or NaN
183
+ var processedNumericValues = (0, _utils.getValueFromShorthand)(resolvedCssValues);
184
+ var processedValues = (0, _utils.splitShorthandValues)(resolvedCssValues);
185
+ // only splits shorthand values but it does not transform NaNs so tokens are preserved
186
+ var originalValues = (0, _utils.splitShorthandValues)(originalCssValues);
171
187
 
172
- // reconstructing the string
173
- // should replace what it can and preserve the raw value for everything else
188
+ // reconstructing the string
189
+ // should replace what it can and preserve the raw value for everything else
174
190
 
175
- var replacementValue = processedNumericValues
176
- // put together resolved value and original value on a tuple
177
- .map(function (value, index) {
178
- return [
179
- // if emToPX conversion fails we'll default to original value
180
- (0, _utils.emToPixels)(value, globalFontSize) || value, processedValues[index], originalValues[index]];
181
- }).map(function (_ref3) {
182
- var _ref4 = (0, _slicedToArray2.default)(_ref3, 3),
183
- numericOrNanValue = _ref4[0],
184
- pxValue = _ref4[1],
185
- originalValue = _ref4[2];
186
- if (!originalValue) {
187
- return originalValue;
188
- }
189
- if ((0, _utils.isCalc)(originalValue)) {
191
+ var replacementValue = processedNumericValues
192
+ // put together resolved value and original value on a tuple
193
+ .map(function (value, index) {
194
+ return [
195
+ // if emToPX conversion fails we'll default to original value
196
+ (0, _utils.emToPixels)(value, globalFontSize) || value, processedValues[index], originalValues[index]];
197
+ }).map(function (_ref3) {
198
+ var _ref4 = (0, _slicedToArray2.default)(_ref3, 3),
199
+ numericOrNanValue = _ref4[0],
200
+ pxValue = _ref4[1],
201
+ originalValue = _ref4[2];
202
+ if (!originalValue) {
203
+ return originalValue;
204
+ }
205
+ if ((0, _utils.isCalc)(originalValue)) {
206
+ context.report({
207
+ node: node,
208
+ messageId: 'noCalcUsage',
209
+ data: {
210
+ payload: "".concat(propertyName)
211
+ }
212
+ });
213
+ return originalValue;
214
+ }
215
+ if ((0, _utils.isTokenValueString)(originalValue)) {
216
+ // if the value is already valid, nothing to report or replace
217
+ return originalValue;
218
+ }
219
+
220
+ // do not replace 0 or auto with tokens
221
+ if ((0, _utils.isZero)(pxValue) || (0, _utils.isAuto)(pxValue)) {
222
+ return originalValue;
223
+ }
224
+ if (isNaN(numericOrNanValue) && !isFontFamily) {
225
+ // do not report if we have nothing to replace with
226
+ return originalValue;
227
+ }
228
+
229
+ // value is numeric or fontFamily, and needs replacing we'll report first
190
230
  context.report({
191
231
  node: node,
192
- messageId: 'noCalcUsage',
232
+ messageId: 'noRawSpacingValues',
193
233
  data: {
194
- payload: "".concat(propertyName)
234
+ payload: "".concat(propertyName, ":").concat(numericOrNanValue)
195
235
  }
196
236
  });
197
- return originalValue;
198
- }
199
- if ((0, _utils.isTokenValueString)(originalValue)) {
200
- // if the value is already valid, nothing to report or replace
201
- return originalValue;
202
- }
203
-
204
- // do not replace 0 or auto with tokens
205
- if ((0, _utils.isZero)(pxValue) || (0, _utils.isAuto)(pxValue)) {
206
- return originalValue;
207
- }
208
- if (isNaN(numericOrNanValue) && !isFontFamily) {
209
- // do not report if we have nothing to replace with
210
- return originalValue;
211
- }
212
237
 
213
- // value is numeric or fontFamily, and needs replacing we'll report first
214
- context.report({
215
- node: node,
216
- messageId: 'noRawSpacingValues',
217
- data: {
218
- payload: "".concat(propertyName, ":").concat(numericOrNanValue)
238
+ // from here on we know value is numeric or a font family, so it might or might not have a token equivalent
239
+ var replacementNode = (0, _utils.getTokenReplacement)(propertyName, numericOrNanValue);
240
+ if (!replacementNode) {
241
+ return originalValue;
219
242
  }
220
- });
243
+ var replacementToken = '${' + replacementNode.toString() + '}';
244
+ replacedValuesPerProperty.push(isFontFamily ? numericOrNanValue.trim() : pxValue);
245
+ return replacementToken;
246
+ }).join(' ');
247
+ if (replacedValuesPerProperty.length > 1) {
248
+ // first value is the property name, so it will always have at least 1
249
+ allReplacedValues.push(replacedValuesPerProperty);
250
+ }
221
251
 
222
- // from here on we know value is numeric or a font family, so it might or might not have a token equivalent
223
- var replacementNode = (0, _utils.getTokenReplacement)(propertyName, numericOrNanValue);
224
- if (!replacementNode) {
225
- return originalValue;
252
+ // replace property:val with new property:val
253
+ var replacedCssLine = currentSource.replace(originalCssLine, // padding: ${gridSize()}px;
254
+ "".concat(originalProperty, ": ").concat(replacementValue));
255
+ if (!replacedCssLine) {
256
+ return currentSource;
226
257
  }
227
- var replacementToken = '${' + replacementNode.toString() + '}';
228
- replacedValuesPerProperty.push(isFontFamily ? numericOrNanValue.trim() : pxValue);
229
- return replacementToken;
230
- }).join(' ');
231
- if (replacedValuesPerProperty.length > 1) {
232
- // first value is the property name, so it will always have at least 1
233
- allReplacedValues.push(replacedValuesPerProperty);
258
+ return replacedCssLine;
234
259
  }
260
+ return currentSource;
261
+ }, textForSource);
262
+ if (completeSource !== textForSource) {
263
+ // means we found some replacement values, we'll give the option to fix them
235
264
 
236
- // replace property:val with new property:val
237
- var replacedCssLine = currentSource.replace(originalCssLine, // padding: ${gridSize()}px;
238
- "".concat(originalProperty, ": ").concat(replacementValue));
239
- if (!replacedCssLine) {
240
- return currentSource;
241
- }
242
- return replacedCssLine;
265
+ context.report({
266
+ node: node,
267
+ messageId: 'autofixesPossible',
268
+ fix: function fix(fixer) {
269
+ return (!tokenNode && config.applyImport ? [(0, _utils.insertTokensImport)(fixer)] : []).concat([fixer.replaceText(node.quasi, completeSource)]);
270
+ }
271
+ });
243
272
  }
244
- return currentSource;
245
- }, textForSource);
246
- if (completeSource !== textForSource) {
247
- // means we found some replacement values, we'll give the option to fix them
248
-
249
- context.report({
250
- node: node,
251
- messageId: 'autofixesPossible',
252
- fix: function fix(fixer) {
253
- return (!tokenNode && config.applyImport ? [(0, _utils.insertTokensImport)(fixer)] : []).concat([fixer.replaceText(node.quasi, completeSource)]);
254
- }
255
- });
256
- }
273
+ }, {
274
+ config: config
275
+ });
257
276
  },
258
277
  // For inline JSX styles - literals (e.g. <Test color="red"/>) - color only
259
278
  'JSXAttribute > Literal': function JSXAttributeLiteral(node) {
260
- return (0, _color.lintJSXLiteralForColor)(node, context, config);
279
+ return (0, _errorBoundary.errorBoundary)(function () {
280
+ return (0, _color.lintJSXLiteralForColor)(node, context, config);
281
+ }, {
282
+ config: config
283
+ });
261
284
  },
262
285
  // For inline JSX styles - members (e.g. <Test color={color.red}/>) - color only
263
286
  'JSXExpressionContainer > MemberExpression': function JSXExpressionContainerMemberExpression(node) {
264
- return (0, _color.lintJSXMemberForColor)(node, context, config);
287
+ return (0, _errorBoundary.errorBoundary)(function () {
288
+ return (0, _color.lintJSXMemberForColor)(node, context, config);
289
+ }, {
290
+ config: config
291
+ });
265
292
  },
266
293
  // For inline JSX styles - identifiers (e.g. <Test color={red}/>) - color only
267
294
  'JSXExpressionContainer > Identifier': function JSXExpressionContainerIdentifier(node) {
268
- return (0, _color.lintJSXIdentifierForColor)(node, context, config);
295
+ return (0, _errorBoundary.errorBoundary)(function () {
296
+ return (0, _color.lintJSXIdentifierForColor)(node, context, config);
297
+ }, {
298
+ config: config
299
+ });
269
300
  }
270
301
  };
271
302
  };
@@ -0,0 +1,19 @@
1
+ /**
2
+ * ESLint rules should NEVER throw exceptions, because that breaks the VSCode ESLint server
3
+ * (and probably the IntelliJ one too), which causes linting to fail in a file.
4
+ *
5
+ * It also breaks CI, which was the reason this error boundary was added. It's a final
6
+ * catch all.
7
+ */
8
+ export const errorBoundary = (func, {
9
+ config
10
+ }) => {
11
+ try {
12
+ func();
13
+ } catch (err) {
14
+ if (!config.failSilently) {
15
+ // eslint-disable-next-line no-console
16
+ console.warn(err);
17
+ }
18
+ }
19
+ };
@@ -5,13 +5,15 @@ import { createLintRule } from '../utils/create-rule';
5
5
  import { includesHardCodedColor } from '../utils/is-color';
6
6
  import { isDecendantOfGlobalToken, isDecendantOfStyleBlock, isDecendantOfType } from '../utils/is-node';
7
7
  import { lintJSXIdentifierForColor, lintJSXLiteralForColor, lintJSXMemberForColor, lintObjectForColor, lintTemplateIdentifierForColor } from './color';
8
+ import { errorBoundary } from './error-boundary';
8
9
  import ruleMeta from './rule-meta';
9
10
  import { lintObjectForSpacing } from './spacing';
10
11
  import { convertHyphenatedNameToCamelCase, emToPixels, getDomainsForProperty, getFontSizeFromNode, getFontSizeValueInScope, getTokenReplacement, getValueFromShorthand, getValueFromTemplateLiteralRaw, includesTokenString, insertTokensImport, isAuto, isCalc, isTokenValueString, isValidSpacingValue, isZero, processCssNode, splitShorthandValues } from './utils';
11
12
  const defaultConfig = {
12
13
  domains: ['color', 'spacing'],
13
14
  applyImport: true,
14
- shouldEnforceFallbacks: false
15
+ shouldEnforceFallbacks: false,
16
+ failSilently: false
15
17
  };
16
18
  const createWithConfig = initialConfig => context => {
17
19
  const userConfig = context.options[0];
@@ -20,21 +22,26 @@ const createWithConfig = initialConfig => context => {
20
22
  domains: (userConfig === null || userConfig === void 0 ? void 0 : userConfig.domains) || initialConfig.domains,
21
23
  applyImport: (userConfig === null || userConfig === void 0 ? void 0 : userConfig.applyImport) !== undefined ? userConfig.applyImport : initialConfig.applyImport,
22
24
  shouldEnforceFallbacks: (userConfig === null || userConfig === void 0 ? void 0 : userConfig.shouldEnforceFallbacks) !== undefined ? userConfig.shouldEnforceFallbacks : initialConfig.shouldEnforceFallbacks,
23
- exceptions: (userConfig === null || userConfig === void 0 ? void 0 : userConfig.exceptions) || []
25
+ exceptions: (userConfig === null || userConfig === void 0 ? void 0 : userConfig.exceptions) || [],
26
+ failSilently: (userConfig === null || userConfig === void 0 ? void 0 : userConfig.failSilently) || defaultConfig.failSilently
24
27
  };
25
28
  let tokenNode = null;
26
29
  return {
27
- ImportDeclaration(node) {
30
+ ImportDeclaration: node => errorBoundary(() => {
28
31
  if (node.source.value === '@atlaskit/tokens' && config.applyImport) {
29
32
  tokenNode = node;
30
33
  }
31
- },
34
+ }, {
35
+ config
36
+ }),
32
37
  // For expressions within template literals (e.g. `color: ${red}`) - color only
33
- 'TemplateLiteral > Identifier': node => {
38
+ 'TemplateLiteral > Identifier': node => errorBoundary(() => {
34
39
  return lintTemplateIdentifierForColor(node, context, config);
35
- },
40
+ }, {
41
+ config
42
+ }),
36
43
  // const styles = css({ color: 'red', margin: '4px' }), styled.div({ color: 'red', margin: '4px' })
37
- ObjectExpression: parentNode => {
44
+ ObjectExpression: parentNode => errorBoundary(() => {
38
45
  // To force the correct node type
39
46
  if (!isNodeOfType(parentNode, 'ObjectExpression')) {
40
47
  return;
@@ -91,11 +98,13 @@ const createWithConfig = initialConfig => context => {
91
98
  }
92
99
  }
93
100
  parentNode.properties.forEach(findObjectStyles);
94
- },
101
+ }, {
102
+ config
103
+ }),
95
104
  // CSSTemplateLiteral and StyledTemplateLiteral
96
105
  // const cssTemplateLiteral = css`color: red; padding: 12px`;
97
106
  // const styledTemplateLiteral = styled.p`color: red; padding: 8px`;
98
- 'TaggedTemplateExpression[tag.name="css"],TaggedTemplateExpression[tag.object.name="styled"],TaggedTemplateExpression[tag.callee.name="styled"]': node => {
107
+ 'TaggedTemplateExpression[tag.name="css"],TaggedTemplateExpression[tag.object.name="styled"],TaggedTemplateExpression[tag.callee.name="styled"]': node => errorBoundary(() => {
99
108
  // To force the correct node type
100
109
  if (!isNodeOfType(node, 'TaggedTemplateExpression')) {
101
110
  return;
@@ -223,19 +232,27 @@ const createWithConfig = initialConfig => context => {
223
232
  }
224
233
  });
225
234
  }
226
- },
235
+ }, {
236
+ config
237
+ }),
227
238
  // For inline JSX styles - literals (e.g. <Test color="red"/>) - color only
228
- 'JSXAttribute > Literal': node => {
239
+ 'JSXAttribute > Literal': node => errorBoundary(() => {
229
240
  return lintJSXLiteralForColor(node, context, config);
230
- },
241
+ }, {
242
+ config
243
+ }),
231
244
  // For inline JSX styles - members (e.g. <Test color={color.red}/>) - color only
232
- 'JSXExpressionContainer > MemberExpression': node => {
245
+ 'JSXExpressionContainer > MemberExpression': node => errorBoundary(() => {
233
246
  return lintJSXMemberForColor(node, context, config);
234
- },
247
+ }, {
248
+ config
249
+ }),
235
250
  // For inline JSX styles - identifiers (e.g. <Test color={red}/>) - color only
236
- 'JSXExpressionContainer > Identifier': node => {
251
+ 'JSXExpressionContainer > Identifier': node => errorBoundary(() => {
237
252
  return lintJSXIdentifierForColor(node, context, config);
238
- }
253
+ }, {
254
+ config
255
+ })
239
256
  };
240
257
  };
241
258
  const rule = createLintRule({
@@ -0,0 +1,18 @@
1
+ /**
2
+ * ESLint rules should NEVER throw exceptions, because that breaks the VSCode ESLint server
3
+ * (and probably the IntelliJ one too), which causes linting to fail in a file.
4
+ *
5
+ * It also breaks CI, which was the reason this error boundary was added. It's a final
6
+ * catch all.
7
+ */
8
+ export var errorBoundary = function errorBoundary(func, _ref) {
9
+ var config = _ref.config;
10
+ try {
11
+ func();
12
+ } catch (err) {
13
+ if (!config.failSilently) {
14
+ // eslint-disable-next-line no-console
15
+ console.warn(err);
16
+ }
17
+ }
18
+ };
@@ -6,13 +6,15 @@ import { createLintRule } from '../utils/create-rule';
6
6
  import { includesHardCodedColor } from '../utils/is-color';
7
7
  import { isDecendantOfGlobalToken, isDecendantOfStyleBlock, isDecendantOfType } from '../utils/is-node';
8
8
  import { lintJSXIdentifierForColor, lintJSXLiteralForColor, lintJSXMemberForColor, lintObjectForColor, lintTemplateIdentifierForColor } from './color';
9
+ import { errorBoundary } from './error-boundary';
9
10
  import ruleMeta from './rule-meta';
10
11
  import { lintObjectForSpacing } from './spacing';
11
12
  import { convertHyphenatedNameToCamelCase, emToPixels, getDomainsForProperty, getFontSizeFromNode, getFontSizeValueInScope, getTokenReplacement, getValueFromShorthand, getValueFromTemplateLiteralRaw, includesTokenString, insertTokensImport, isAuto, isCalc, isTokenValueString, isValidSpacingValue, isZero, processCssNode, splitShorthandValues } from './utils';
12
13
  var defaultConfig = {
13
14
  domains: ['color', 'spacing'],
14
15
  applyImport: true,
15
- shouldEnforceFallbacks: false
16
+ shouldEnforceFallbacks: false,
17
+ failSilently: false
16
18
  };
17
19
  var createWithConfig = function createWithConfig(initialConfig) {
18
20
  return function (context) {
@@ -22,18 +24,27 @@ var createWithConfig = function createWithConfig(initialConfig) {
22
24
  domains: (userConfig === null || userConfig === void 0 ? void 0 : userConfig.domains) || initialConfig.domains,
23
25
  applyImport: (userConfig === null || userConfig === void 0 ? void 0 : userConfig.applyImport) !== undefined ? userConfig.applyImport : initialConfig.applyImport,
24
26
  shouldEnforceFallbacks: (userConfig === null || userConfig === void 0 ? void 0 : userConfig.shouldEnforceFallbacks) !== undefined ? userConfig.shouldEnforceFallbacks : initialConfig.shouldEnforceFallbacks,
25
- exceptions: (userConfig === null || userConfig === void 0 ? void 0 : userConfig.exceptions) || []
27
+ exceptions: (userConfig === null || userConfig === void 0 ? void 0 : userConfig.exceptions) || [],
28
+ failSilently: (userConfig === null || userConfig === void 0 ? void 0 : userConfig.failSilently) || defaultConfig.failSilently
26
29
  };
27
30
  var tokenNode = null;
28
31
  return {
29
32
  ImportDeclaration: function ImportDeclaration(node) {
30
- if (node.source.value === '@atlaskit/tokens' && config.applyImport) {
31
- tokenNode = node;
32
- }
33
+ return errorBoundary(function () {
34
+ if (node.source.value === '@atlaskit/tokens' && config.applyImport) {
35
+ tokenNode = node;
36
+ }
37
+ }, {
38
+ config: config
39
+ });
33
40
  },
34
41
  // For expressions within template literals (e.g. `color: ${red}`) - color only
35
42
  'TemplateLiteral > Identifier': function TemplateLiteralIdentifier(node) {
36
- return lintTemplateIdentifierForColor(node, context, config);
43
+ return errorBoundary(function () {
44
+ return lintTemplateIdentifierForColor(node, context, config);
45
+ }, {
46
+ config: config
47
+ });
37
48
  },
38
49
  // const styles = css({ color: 'red', margin: '4px' }), styled.div({ color: 'red', margin: '4px' })
39
50
  ObjectExpression: function (_ObjectExpression) {
@@ -45,220 +56,240 @@ var createWithConfig = function createWithConfig(initialConfig) {
45
56
  };
46
57
  return ObjectExpression;
47
58
  }(function (parentNode) {
48
- // To force the correct node type
49
- if (!isNodeOfType(parentNode, 'ObjectExpression')) {
50
- return;
51
- }
52
-
53
- // Return for nested objects - these get handled automatically so without returning we'd be doubling up
54
- if (parentNode.parent.type === 'Property') {
55
- return;
56
- }
57
- if (!isDecendantOfStyleBlock(parentNode) && !isDecendantOfType(parentNode, 'JSXExpressionContainer')) {
58
- return;
59
- }
60
- function findObjectStyles(node) {
61
- if (!isNodeOfType(node, 'Property')) {
59
+ return errorBoundary(function () {
60
+ // To force the correct node type
61
+ if (!isNodeOfType(parentNode, 'ObjectExpression')) {
62
62
  return;
63
63
  }
64
- if (isNodeOfType(node.value, 'ObjectExpression')) {
65
- return node.value.properties.forEach(findObjectStyles);
66
- }
67
- if (!isNodeOfType(node.key, 'Identifier') && !isNodeOfType(node.key, 'Literal')) {
64
+
65
+ // Return for nested objects - these get handled automatically so without returning we'd be doubling up
66
+ if (parentNode.parent.type === 'Property') {
68
67
  return;
69
68
  }
70
- var propertyName = isNodeOfType(node.key, 'Identifier') ? node.key.name : String(node.key.value);
71
-
72
- // Returns which domains to lint against based on rule's config and current property
73
- var domains = getDomainsForProperty(propertyName, config.domains);
74
- if (domains.length === 0 || isDecendantOfGlobalToken(node.value)) {
69
+ if (!isDecendantOfStyleBlock(parentNode) && !isDecendantOfType(parentNode, 'JSXExpressionContainer')) {
75
70
  return;
76
71
  }
77
- if (isNodeOfType(node.value, 'TemplateLiteral')) {
78
- var value = getValueFromTemplateLiteralRaw(node.value, context);
79
- if (Array.isArray(value) && value.some(isCalc)) {
80
- return context.report({
81
- node: node,
82
- messageId: 'noCalcUsage',
83
- data: {
84
- payload: "".concat(propertyName)
85
- }
86
- });
72
+ function findObjectStyles(node) {
73
+ if (!isNodeOfType(node, 'Property')) {
74
+ return;
87
75
  }
88
- if (node.value.expressions.some(isDecendantOfGlobalToken)) {
76
+ if (isNodeOfType(node.value, 'ObjectExpression')) {
77
+ return node.value.properties.forEach(findObjectStyles);
78
+ }
79
+ if (!isNodeOfType(node.key, 'Identifier') && !isNodeOfType(node.key, 'Literal')) {
89
80
  return;
90
81
  }
82
+ var propertyName = isNodeOfType(node.key, 'Identifier') ? node.key.name : String(node.key.value);
83
+
84
+ // Returns which domains to lint against based on rule's config and current property
85
+ var domains = getDomainsForProperty(propertyName, config.domains);
86
+ if (domains.length === 0 || isDecendantOfGlobalToken(node.value)) {
87
+ return;
88
+ }
89
+ if (isNodeOfType(node.value, 'TemplateLiteral')) {
90
+ var value = getValueFromTemplateLiteralRaw(node.value, context);
91
+ if (Array.isArray(value) && value.some(isCalc)) {
92
+ return context.report({
93
+ node: node,
94
+ messageId: 'noCalcUsage',
95
+ data: {
96
+ payload: "".concat(propertyName)
97
+ }
98
+ });
99
+ }
100
+ if (node.value.expressions.some(isDecendantOfGlobalToken)) {
101
+ return;
102
+ }
103
+ }
104
+ if (domains.includes('color')) {
105
+ return lintObjectForColor(node, context, config);
106
+ }
107
+ if (domains.includes('spacing') || domains.includes('shape') || domains.includes('typography')) {
108
+ /**
109
+ * We do this in case the fontSize for a style object is declared alongside the `em` or `lineHeight` declaration
110
+ */
111
+ var fontSize = getFontSizeFromNode(parentNode, context);
112
+ return lintObjectForSpacing(node, context, config, fontSize, tokenNode);
113
+ }
91
114
  }
92
- if (domains.includes('color')) {
93
- return lintObjectForColor(node, context, config);
94
- }
95
- if (domains.includes('spacing') || domains.includes('shape') || domains.includes('typography')) {
96
- /**
97
- * We do this in case the fontSize for a style object is declared alongside the `em` or `lineHeight` declaration
98
- */
99
- var fontSize = getFontSizeFromNode(parentNode, context);
100
- return lintObjectForSpacing(node, context, config, fontSize, tokenNode);
101
- }
102
- }
103
- parentNode.properties.forEach(findObjectStyles);
115
+ parentNode.properties.forEach(findObjectStyles);
116
+ }, {
117
+ config: config
118
+ });
104
119
  }),
105
120
  // CSSTemplateLiteral and StyledTemplateLiteral
106
121
  // const cssTemplateLiteral = css`color: red; padding: 12px`;
107
122
  // const styledTemplateLiteral = styled.p`color: red; padding: 8px`;
108
123
  'TaggedTemplateExpression[tag.name="css"],TaggedTemplateExpression[tag.object.name="styled"],TaggedTemplateExpression[tag.callee.name="styled"]': function TaggedTemplateExpressionTagNameCssTaggedTemplateExpressionTagObjectNameStyledTaggedTemplateExpressionTagCalleeNameStyled(node) {
109
- // To force the correct node type
110
- if (!isNodeOfType(node, 'TaggedTemplateExpression')) {
111
- return;
112
- }
113
- var processedCssLines = processCssNode(node, context);
114
- if (!processedCssLines) {
115
- // if we can't get a processed css we bail
116
- return;
117
- }
118
- var globalFontSize = getFontSizeValueInScope(processedCssLines);
119
- var textForSource = context.getSourceCode().getText(node.quasi);
120
- var allReplacedValues = [];
121
- var completeSource = processedCssLines.reduce(function (currentSource, _ref) {
122
- var _ref2 = _slicedToArray(_ref, 2),
123
- resolvedCssLine = _ref2[0],
124
- originalCssLine = _ref2[1];
125
- var _resolvedCssLine$spli = resolvedCssLine.split(':'),
126
- _resolvedCssLine$spli2 = _slicedToArray(_resolvedCssLine$spli, 2),
127
- originalProperty = _resolvedCssLine$spli2[0],
128
- resolvedCssValues = _resolvedCssLine$spli2[1];
129
- var _originalCssLine$spli = originalCssLine.split(':'),
130
- _originalCssLine$spli2 = _slicedToArray(_originalCssLine$spli, 2),
131
- _ = _originalCssLine$spli2[0],
132
- originalCssValues = _originalCssLine$spli2[1];
133
- var propertyName = convertHyphenatedNameToCamelCase(originalProperty);
134
- var isFontFamily = /fontFamily/.test(propertyName);
135
- var replacedValuesPerProperty = [originalProperty];
136
- var domains = getDomainsForProperty(propertyName, config.domains);
137
- if (domains.length === 0 || !resolvedCssValues) {
138
- // in both of these cases no changes should be made to the current property
139
- return currentSource;
124
+ return errorBoundary(function () {
125
+ // To force the correct node type
126
+ if (!isNodeOfType(node, 'TaggedTemplateExpression')) {
127
+ return;
140
128
  }
141
- if (domains.includes('color')) {
142
- if (includesTokenString(resolvedCssValues.trim())) {
143
- return currentSource;
144
- }
145
- if (includesHardCodedColor(resolvedCssValues)) {
146
- context.report({
147
- messageId: 'hardCodedColor',
148
- node: node
149
- });
150
- return currentSource;
151
- }
129
+ var processedCssLines = processCssNode(node, context);
130
+ if (!processedCssLines) {
131
+ // if we can't get a processed css we bail
132
+ return;
152
133
  }
153
- if (domains.includes('spacing') || domains.includes('typography') || domains.includes('shape')) {
154
- if (!isValidSpacingValue(resolvedCssValues, globalFontSize)) {
155
- // no changes should be made to the current property
134
+ var globalFontSize = getFontSizeValueInScope(processedCssLines);
135
+ var textForSource = context.getSourceCode().getText(node.quasi);
136
+ var allReplacedValues = [];
137
+ var completeSource = processedCssLines.reduce(function (currentSource, _ref) {
138
+ var _ref2 = _slicedToArray(_ref, 2),
139
+ resolvedCssLine = _ref2[0],
140
+ originalCssLine = _ref2[1];
141
+ var _resolvedCssLine$spli = resolvedCssLine.split(':'),
142
+ _resolvedCssLine$spli2 = _slicedToArray(_resolvedCssLine$spli, 2),
143
+ originalProperty = _resolvedCssLine$spli2[0],
144
+ resolvedCssValues = _resolvedCssLine$spli2[1];
145
+ var _originalCssLine$spli = originalCssLine.split(':'),
146
+ _originalCssLine$spli2 = _slicedToArray(_originalCssLine$spli, 2),
147
+ _ = _originalCssLine$spli2[0],
148
+ originalCssValues = _originalCssLine$spli2[1];
149
+ var propertyName = convertHyphenatedNameToCamelCase(originalProperty);
150
+ var isFontFamily = /fontFamily/.test(propertyName);
151
+ var replacedValuesPerProperty = [originalProperty];
152
+ var domains = getDomainsForProperty(propertyName, config.domains);
153
+ if (domains.length === 0 || !resolvedCssValues) {
154
+ // in both of these cases no changes should be made to the current property
156
155
  return currentSource;
157
156
  }
157
+ if (domains.includes('color')) {
158
+ if (includesTokenString(resolvedCssValues.trim())) {
159
+ return currentSource;
160
+ }
161
+ if (includesHardCodedColor(resolvedCssValues)) {
162
+ context.report({
163
+ messageId: 'hardCodedColor',
164
+ node: node
165
+ });
166
+ return currentSource;
167
+ }
168
+ }
169
+ if (domains.includes('spacing') || domains.includes('typography') || domains.includes('shape')) {
170
+ if (!isValidSpacingValue(resolvedCssValues, globalFontSize)) {
171
+ // no changes should be made to the current property
172
+ return currentSource;
173
+ }
158
174
 
159
- // gets the values from the associated property, numeric values or NaN
160
- var processedNumericValues = getValueFromShorthand(resolvedCssValues);
161
- var processedValues = splitShorthandValues(resolvedCssValues);
162
- // only splits shorthand values but it does not transform NaNs so tokens are preserved
163
- var originalValues = splitShorthandValues(originalCssValues);
175
+ // gets the values from the associated property, numeric values or NaN
176
+ var processedNumericValues = getValueFromShorthand(resolvedCssValues);
177
+ var processedValues = splitShorthandValues(resolvedCssValues);
178
+ // only splits shorthand values but it does not transform NaNs so tokens are preserved
179
+ var originalValues = splitShorthandValues(originalCssValues);
164
180
 
165
- // reconstructing the string
166
- // should replace what it can and preserve the raw value for everything else
181
+ // reconstructing the string
182
+ // should replace what it can and preserve the raw value for everything else
167
183
 
168
- var replacementValue = processedNumericValues
169
- // put together resolved value and original value on a tuple
170
- .map(function (value, index) {
171
- return [
172
- // if emToPX conversion fails we'll default to original value
173
- emToPixels(value, globalFontSize) || value, processedValues[index], originalValues[index]];
174
- }).map(function (_ref3) {
175
- var _ref4 = _slicedToArray(_ref3, 3),
176
- numericOrNanValue = _ref4[0],
177
- pxValue = _ref4[1],
178
- originalValue = _ref4[2];
179
- if (!originalValue) {
180
- return originalValue;
181
- }
182
- if (isCalc(originalValue)) {
184
+ var replacementValue = processedNumericValues
185
+ // put together resolved value and original value on a tuple
186
+ .map(function (value, index) {
187
+ return [
188
+ // if emToPX conversion fails we'll default to original value
189
+ emToPixels(value, globalFontSize) || value, processedValues[index], originalValues[index]];
190
+ }).map(function (_ref3) {
191
+ var _ref4 = _slicedToArray(_ref3, 3),
192
+ numericOrNanValue = _ref4[0],
193
+ pxValue = _ref4[1],
194
+ originalValue = _ref4[2];
195
+ if (!originalValue) {
196
+ return originalValue;
197
+ }
198
+ if (isCalc(originalValue)) {
199
+ context.report({
200
+ node: node,
201
+ messageId: 'noCalcUsage',
202
+ data: {
203
+ payload: "".concat(propertyName)
204
+ }
205
+ });
206
+ return originalValue;
207
+ }
208
+ if (isTokenValueString(originalValue)) {
209
+ // if the value is already valid, nothing to report or replace
210
+ return originalValue;
211
+ }
212
+
213
+ // do not replace 0 or auto with tokens
214
+ if (isZero(pxValue) || isAuto(pxValue)) {
215
+ return originalValue;
216
+ }
217
+ if (isNaN(numericOrNanValue) && !isFontFamily) {
218
+ // do not report if we have nothing to replace with
219
+ return originalValue;
220
+ }
221
+
222
+ // value is numeric or fontFamily, and needs replacing we'll report first
183
223
  context.report({
184
224
  node: node,
185
- messageId: 'noCalcUsage',
225
+ messageId: 'noRawSpacingValues',
186
226
  data: {
187
- payload: "".concat(propertyName)
227
+ payload: "".concat(propertyName, ":").concat(numericOrNanValue)
188
228
  }
189
229
  });
190
- return originalValue;
191
- }
192
- if (isTokenValueString(originalValue)) {
193
- // if the value is already valid, nothing to report or replace
194
- return originalValue;
195
- }
196
-
197
- // do not replace 0 or auto with tokens
198
- if (isZero(pxValue) || isAuto(pxValue)) {
199
- return originalValue;
200
- }
201
- if (isNaN(numericOrNanValue) && !isFontFamily) {
202
- // do not report if we have nothing to replace with
203
- return originalValue;
204
- }
205
230
 
206
- // value is numeric or fontFamily, and needs replacing we'll report first
207
- context.report({
208
- node: node,
209
- messageId: 'noRawSpacingValues',
210
- data: {
211
- payload: "".concat(propertyName, ":").concat(numericOrNanValue)
231
+ // from here on we know value is numeric or a font family, so it might or might not have a token equivalent
232
+ var replacementNode = getTokenReplacement(propertyName, numericOrNanValue);
233
+ if (!replacementNode) {
234
+ return originalValue;
212
235
  }
213
- });
236
+ var replacementToken = '${' + replacementNode.toString() + '}';
237
+ replacedValuesPerProperty.push(isFontFamily ? numericOrNanValue.trim() : pxValue);
238
+ return replacementToken;
239
+ }).join(' ');
240
+ if (replacedValuesPerProperty.length > 1) {
241
+ // first value is the property name, so it will always have at least 1
242
+ allReplacedValues.push(replacedValuesPerProperty);
243
+ }
214
244
 
215
- // from here on we know value is numeric or a font family, so it might or might not have a token equivalent
216
- var replacementNode = getTokenReplacement(propertyName, numericOrNanValue);
217
- if (!replacementNode) {
218
- return originalValue;
245
+ // replace property:val with new property:val
246
+ var replacedCssLine = currentSource.replace(originalCssLine, // padding: ${gridSize()}px;
247
+ "".concat(originalProperty, ": ").concat(replacementValue));
248
+ if (!replacedCssLine) {
249
+ return currentSource;
219
250
  }
220
- var replacementToken = '${' + replacementNode.toString() + '}';
221
- replacedValuesPerProperty.push(isFontFamily ? numericOrNanValue.trim() : pxValue);
222
- return replacementToken;
223
- }).join(' ');
224
- if (replacedValuesPerProperty.length > 1) {
225
- // first value is the property name, so it will always have at least 1
226
- allReplacedValues.push(replacedValuesPerProperty);
251
+ return replacedCssLine;
227
252
  }
253
+ return currentSource;
254
+ }, textForSource);
255
+ if (completeSource !== textForSource) {
256
+ // means we found some replacement values, we'll give the option to fix them
228
257
 
229
- // replace property:val with new property:val
230
- var replacedCssLine = currentSource.replace(originalCssLine, // padding: ${gridSize()}px;
231
- "".concat(originalProperty, ": ").concat(replacementValue));
232
- if (!replacedCssLine) {
233
- return currentSource;
234
- }
235
- return replacedCssLine;
258
+ context.report({
259
+ node: node,
260
+ messageId: 'autofixesPossible',
261
+ fix: function fix(fixer) {
262
+ return (!tokenNode && config.applyImport ? [insertTokensImport(fixer)] : []).concat([fixer.replaceText(node.quasi, completeSource)]);
263
+ }
264
+ });
236
265
  }
237
- return currentSource;
238
- }, textForSource);
239
- if (completeSource !== textForSource) {
240
- // means we found some replacement values, we'll give the option to fix them
241
-
242
- context.report({
243
- node: node,
244
- messageId: 'autofixesPossible',
245
- fix: function fix(fixer) {
246
- return (!tokenNode && config.applyImport ? [insertTokensImport(fixer)] : []).concat([fixer.replaceText(node.quasi, completeSource)]);
247
- }
248
- });
249
- }
266
+ }, {
267
+ config: config
268
+ });
250
269
  },
251
270
  // For inline JSX styles - literals (e.g. <Test color="red"/>) - color only
252
271
  'JSXAttribute > Literal': function JSXAttributeLiteral(node) {
253
- return lintJSXLiteralForColor(node, context, config);
272
+ return errorBoundary(function () {
273
+ return lintJSXLiteralForColor(node, context, config);
274
+ }, {
275
+ config: config
276
+ });
254
277
  },
255
278
  // For inline JSX styles - members (e.g. <Test color={color.red}/>) - color only
256
279
  'JSXExpressionContainer > MemberExpression': function JSXExpressionContainerMemberExpression(node) {
257
- return lintJSXMemberForColor(node, context, config);
280
+ return errorBoundary(function () {
281
+ return lintJSXMemberForColor(node, context, config);
282
+ }, {
283
+ config: config
284
+ });
258
285
  },
259
286
  // For inline JSX styles - identifiers (e.g. <Test color={red}/>) - color only
260
287
  'JSXExpressionContainer > Identifier': function JSXExpressionContainerIdentifier(node) {
261
- return lintJSXIdentifierForColor(node, context, config);
288
+ return errorBoundary(function () {
289
+ return lintJSXIdentifierForColor(node, context, config);
290
+ }, {
291
+ config: config
292
+ });
262
293
  }
263
294
  };
264
295
  };
@@ -0,0 +1,11 @@
1
+ import { RuleConfig } from './types';
2
+ /**
3
+ * ESLint rules should NEVER throw exceptions, because that breaks the VSCode ESLint server
4
+ * (and probably the IntelliJ one too), which causes linting to fail in a file.
5
+ *
6
+ * It also breaks CI, which was the reason this error boundary was added. It's a final
7
+ * catch all.
8
+ */
9
+ export declare const errorBoundary: (func: () => void, { config }: {
10
+ config: RuleConfig;
11
+ }) => void;
@@ -7,4 +7,5 @@ export type RuleConfig = {
7
7
  * List of exceptions that can be configured for the rule to always ignore.
8
8
  */
9
9
  exceptions?: string[];
10
+ failSilently?: boolean;
10
11
  };
@@ -0,0 +1,11 @@
1
+ import { RuleConfig } from './types';
2
+ /**
3
+ * ESLint rules should NEVER throw exceptions, because that breaks the VSCode ESLint server
4
+ * (and probably the IntelliJ one too), which causes linting to fail in a file.
5
+ *
6
+ * It also breaks CI, which was the reason this error boundary was added. It's a final
7
+ * catch all.
8
+ */
9
+ export declare const errorBoundary: (func: () => void, { config }: {
10
+ config: RuleConfig;
11
+ }) => void;
@@ -7,4 +7,5 @@ export type RuleConfig = {
7
7
  * List of exceptions that can be configured for the rule to always ignore.
8
8
  */
9
9
  exceptions?: string[];
10
+ failSilently?: boolean;
10
11
  };
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.23.0",
4
+ "version": "8.23.1",
5
5
  "author": "Atlassian Pty Ltd",
6
6
  "publishConfig": {
7
7
  "registry": "https://registry.npmjs.org/"