@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 +6 -0
- package/dist/cjs/rules/ensure-design-token-usage/error-boundary.js +24 -0
- package/dist/cjs/rules/ensure-design-token-usage/index.js +208 -177
- package/dist/es2019/rules/ensure-design-token-usage/error-boundary.js +19 -0
- package/dist/es2019/rules/ensure-design-token-usage/index.js +33 -16
- package/dist/esm/rules/ensure-design-token-usage/error-boundary.js +18 -0
- package/dist/esm/rules/ensure-design-token-usage/index.js +208 -177
- package/dist/types/rules/ensure-design-token-usage/error-boundary.d.ts +11 -0
- package/dist/types/rules/ensure-design-token-usage/types.d.ts +1 -0
- package/dist/types-ts4.5/rules/ensure-design-token-usage/error-boundary.d.ts +11 -0
- package/dist/types-ts4.5/rules/ensure-design-token-usage/types.d.ts +1 -0
- package/package.json +1 -1
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
|
-
|
|
38
|
-
|
|
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,
|
|
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
|
-
|
|
56
|
-
|
|
57
|
-
|
|
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
|
-
|
|
72
|
-
|
|
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
|
-
|
|
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
|
-
|
|
85
|
-
|
|
86
|
-
|
|
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
|
|
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
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
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
|
-
|
|
117
|
-
|
|
118
|
-
|
|
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
|
-
|
|
149
|
-
|
|
150
|
-
|
|
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
|
-
|
|
161
|
-
|
|
162
|
-
|
|
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
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
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
|
-
|
|
173
|
-
|
|
188
|
+
// reconstructing the string
|
|
189
|
+
// should replace what it can and preserve the raw value for everything else
|
|
174
190
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
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: '
|
|
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
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
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
|
-
//
|
|
223
|
-
var
|
|
224
|
-
|
|
225
|
-
|
|
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
|
-
|
|
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
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
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
|
-
|
|
245
|
-
|
|
246
|
-
|
|
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,
|
|
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,
|
|
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,
|
|
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(
|
|
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
|
-
|
|
31
|
-
|
|
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
|
|
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
|
-
|
|
49
|
-
|
|
50
|
-
|
|
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
|
-
|
|
65
|
-
|
|
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
|
-
|
|
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
|
-
|
|
78
|
-
|
|
79
|
-
|
|
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
|
|
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
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
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
|
-
|
|
110
|
-
|
|
111
|
-
|
|
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
|
-
|
|
142
|
-
|
|
143
|
-
|
|
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
|
-
|
|
154
|
-
|
|
155
|
-
|
|
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
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
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
|
-
|
|
166
|
-
|
|
181
|
+
// reconstructing the string
|
|
182
|
+
// should replace what it can and preserve the raw value for everything else
|
|
167
183
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
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: '
|
|
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
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
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
|
-
//
|
|
216
|
-
var
|
|
217
|
-
|
|
218
|
-
|
|
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
|
-
|
|
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
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
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
|
-
|
|
238
|
-
|
|
239
|
-
|
|
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
|
|
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
|
|
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
|
|
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;
|
|
@@ -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;
|
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.
|
|
4
|
+
"version": "8.23.1",
|
|
5
5
|
"author": "Atlassian Pty Ltd",
|
|
6
6
|
"publishConfig": {
|
|
7
7
|
"registry": "https://registry.npmjs.org/"
|