@atlaskit/eslint-plugin-design-system 4.15.4 → 4.15.6
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 +12 -0
- package/dist/cjs/rules/ensure-design-token-usage-spacing/index.js +105 -166
- package/dist/cjs/rules/ensure-design-token-usage-spacing/utils.js +192 -6
- package/dist/cjs/version.json +1 -1
- package/dist/es2019/rules/ensure-design-token-usage-spacing/index.js +87 -144
- package/dist/es2019/rules/ensure-design-token-usage-spacing/utils.js +153 -4
- package/dist/es2019/version.json +1 -1
- package/dist/esm/rules/ensure-design-token-usage-spacing/index.js +102 -163
- package/dist/esm/rules/ensure-design-token-usage-spacing/utils.js +175 -6
- package/dist/esm/version.json +1 -1
- package/dist/types/rules/ensure-design-token-usage-spacing/utils.d.ts +123 -1
- package/package.json +4 -5
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# @atlaskit/eslint-plugin-design-system
|
|
2
2
|
|
|
3
|
+
## 4.15.6
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [`6a43a780a85`](https://bitbucket.org/atlassian/atlassian-frontend/commits/6a43a780a85) - Enhance token replacement capabilities of ensure-design-tokens-usage-spacing rule in tagged template literal strings
|
|
8
|
+
|
|
9
|
+
## 4.15.5
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- [`dda18b361da`](https://bitbucket.org/atlassian/atlassian-frontend/commits/dda18b361da) - Bump version of `eslint-codemod-utils`.
|
|
14
|
+
|
|
3
15
|
## 4.15.4
|
|
4
16
|
|
|
5
17
|
### Patch Changes
|
|
@@ -8,91 +8,10 @@ exports.default = void 0;
|
|
|
8
8
|
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
|
|
9
9
|
var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
|
|
10
10
|
var _eslintCodemodUtils = require("eslint-codemod-utils");
|
|
11
|
-
var _tokensRaw = require("@atlaskit/tokens/tokens-raw");
|
|
12
11
|
var _isNode = require("../utils/is-node");
|
|
13
12
|
var _utils = require("./utils");
|
|
14
13
|
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
|
|
15
14
|
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { (0, _defineProperty2.default)(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
|
|
16
|
-
/**
|
|
17
|
-
* Currently we have a wide range of experimental spacing tokens that we are testing.
|
|
18
|
-
* We only want transforms to apply to the stable scale values, not the rest.
|
|
19
|
-
* This could be removed in the future.
|
|
20
|
-
*/
|
|
21
|
-
var onlyScaleTokens = _tokensRaw.spacing.filter(function (token) {
|
|
22
|
-
return token.name.startsWith('space.');
|
|
23
|
-
});
|
|
24
|
-
var spacingValueToToken = Object.fromEntries(onlyScaleTokens.map(function (token) {
|
|
25
|
-
return [token.attributes['pixelValue'], token.name];
|
|
26
|
-
}));
|
|
27
|
-
var typographyValueToToken = Object.fromEntries(_tokensRaw.typography.map(function (currentToken) {
|
|
28
|
-
// Group tokens by property name (e.g. fontSize, fontFamily, lineHeight)
|
|
29
|
-
// This allows us to look up values specific to a property
|
|
30
|
-
// (so as not to mix tokens with overlapping values e.g. font size and line height both have tokens for 16px)
|
|
31
|
-
var tokenGroup = currentToken.attributes.group;
|
|
32
|
-
return [tokenGroup, Object.fromEntries(_tokensRaw.typography.map(function (token) {
|
|
33
|
-
return token.attributes.group === tokenGroup ? [token.value.replaceAll("\"", "'"), token.name] : [];
|
|
34
|
-
}).filter(function (token) {
|
|
35
|
-
return token.length;
|
|
36
|
-
}))];
|
|
37
|
-
}));
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* Returns a token node for a given value including fallbacks.
|
|
41
|
-
* @param propertyName camelCase CSS property
|
|
42
|
-
* @param value string representing pixel value, or font family, or number representing font weight
|
|
43
|
-
* @example
|
|
44
|
-
* ```
|
|
45
|
-
* propertyName: padding, value: '8px' => token('space.100', '8px')
|
|
46
|
-
* propertyName: fontWeight, value: 400 => token('font.weight.regular', '400')
|
|
47
|
-
* ```
|
|
48
|
-
*/
|
|
49
|
-
function getTokenNodeForValue(propertyName, value) {
|
|
50
|
-
var token = (0, _utils.isTypographyProperty)(propertyName) ? typographyValueToToken[propertyName][value] : spacingValueToToken[value];
|
|
51
|
-
var fallbackValue = propertyName === 'fontFamily' ? "\"".concat(value, "\"") : "'".concat(value, "'");
|
|
52
|
-
return (0, _eslintCodemodUtils.callExpression)({
|
|
53
|
-
callee: (0, _eslintCodemodUtils.identifier)({
|
|
54
|
-
name: 'token'
|
|
55
|
-
}),
|
|
56
|
-
arguments: [(0, _eslintCodemodUtils.literal)({
|
|
57
|
-
value: "'".concat(token !== null && token !== void 0 ? token : '', "'")
|
|
58
|
-
}), (0, _eslintCodemodUtils.literal)(fallbackValue)],
|
|
59
|
-
optional: false
|
|
60
|
-
});
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* Returns a boolean that signals wether the current property is revelant under the current configuration
|
|
65
|
-
* @param propertyName camelCase CSS property
|
|
66
|
-
* @param targetOptions Array containing the types of properties that should be included in the rule
|
|
67
|
-
* @example
|
|
68
|
-
* ```
|
|
69
|
-
* propertyName: padding, targetOptions: ['spacing']
|
|
70
|
-
* propertyName: fontWeight, targetOptions: ['spacing', 'typography']
|
|
71
|
-
* ```
|
|
72
|
-
*/
|
|
73
|
-
function shouldAnalyzeProperty(propertyName, targetOptions) {
|
|
74
|
-
if ((0, _utils.isSpacingProperty)(propertyName) && targetOptions.includes('spacing')) {
|
|
75
|
-
return true;
|
|
76
|
-
}
|
|
77
|
-
if ((0, _utils.isTypographyProperty)(propertyName) && targetOptions.includes('typography')) {
|
|
78
|
-
return true;
|
|
79
|
-
}
|
|
80
|
-
return false;
|
|
81
|
-
}
|
|
82
|
-
/**
|
|
83
|
-
* Attempts to remove all non-essential words & characters from a style block.
|
|
84
|
-
* Including selectors and queries
|
|
85
|
-
* Adapted from ensure-design-token-usage
|
|
86
|
-
* @param styleString string of css properties
|
|
87
|
-
*/
|
|
88
|
-
function splitCssProperties(styleString) {
|
|
89
|
-
return styleString.split('\n').filter(function (line) {
|
|
90
|
-
return !line.trim().startsWith('@');
|
|
91
|
-
}).join('\n').replace(/\n/g, '').split(/;|(?<!\$){|(?<!\${.+?)}/) // don't split on template literal expressions i.e. `${...}`
|
|
92
|
-
.map(function (el) {
|
|
93
|
-
return el.trim() || '';
|
|
94
|
-
}).filter(Boolean);
|
|
95
|
-
}
|
|
96
15
|
var rule = {
|
|
97
16
|
meta: {
|
|
98
17
|
type: 'problem',
|
|
@@ -102,7 +21,8 @@ var rule = {
|
|
|
102
21
|
recommended: true
|
|
103
22
|
},
|
|
104
23
|
messages: {
|
|
105
|
-
noRawSpacingValues: 'The use of spacing primitives or tokens is preferred over the direct application of spacing properties.\n\n@meta <<{{payload}}>>'
|
|
24
|
+
noRawSpacingValues: 'The use of spacing primitives or tokens is preferred over the direct application of spacing properties.\n\n@meta <<{{payload}}>>',
|
|
25
|
+
autofixesPossible: 'Automated corrections available for spacing values. Apply autofix to replace values with appropriate tokens'
|
|
106
26
|
}
|
|
107
27
|
},
|
|
108
28
|
create: function create(context) {
|
|
@@ -146,7 +66,7 @@ var rule = {
|
|
|
146
66
|
if (!(0, _eslintCodemodUtils.isNodeOfType)(node.key, 'Identifier')) {
|
|
147
67
|
return;
|
|
148
68
|
}
|
|
149
|
-
if (!shouldAnalyzeProperty(node.key.name, targetCategories)) {
|
|
69
|
+
if (!(0, _utils.shouldAnalyzeProperty)(node.key.name, targetCategories)) {
|
|
150
70
|
return;
|
|
151
71
|
}
|
|
152
72
|
if ((0, _isNode.isDecendantOfGlobalToken)(node.value)) {
|
|
@@ -195,16 +115,16 @@ var rule = {
|
|
|
195
115
|
},
|
|
196
116
|
fix: function fix(fixer) {
|
|
197
117
|
var _node$loc;
|
|
198
|
-
if (!shouldAnalyzeProperty(propertyName, targetCategories)) {
|
|
118
|
+
if (!(0, _utils.shouldAnalyzeProperty)(propertyName, targetCategories)) {
|
|
199
119
|
return null;
|
|
200
120
|
}
|
|
201
121
|
var pixelValueString = "".concat(pixelValue, "px");
|
|
202
122
|
var lookupValue = /fontWeight|fontFamily/.test(propertyName) ? pixelValue : pixelValueString;
|
|
203
|
-
var tokenName = (0, _utils.isTypographyProperty)(propertyName) ? typographyValueToToken[propertyName][lookupValue] : spacingValueToToken[lookupValue];
|
|
123
|
+
var tokenName = (0, _utils.isTypographyProperty)(propertyName) ? _utils.typographyValueToToken[propertyName][lookupValue] : _utils.spacingValueToToken[lookupValue];
|
|
204
124
|
if (!tokenName) {
|
|
205
125
|
return null;
|
|
206
126
|
}
|
|
207
|
-
var replacementValue = getTokenNodeForValue(propertyName, lookupValue);
|
|
127
|
+
var replacementValue = (0, _utils.getTokenNodeForValue)(propertyName, lookupValue);
|
|
208
128
|
return [fixer.insertTextBefore(node, "// TODO Delete this comment after verifying spacing token -> previous value `".concat((0, _eslintCodemodUtils.node)(node.value), "`\n").concat(' '.padStart(((_node$loc = node.loc) === null || _node$loc === void 0 ? void 0 : _node$loc.start.column) || 0))), fixer.replaceText(node, (0, _eslintCodemodUtils.property)(_objectSpread(_objectSpread({}, node), {}, {
|
|
209
129
|
value: replacementValue
|
|
210
130
|
})).toString())];
|
|
@@ -237,7 +157,7 @@ var rule = {
|
|
|
237
157
|
return fixer.replaceText(node.value, "`".concat(values.map(function (value) {
|
|
238
158
|
var pixelValue = (0, _utils.emToPixels)(value, fontSize);
|
|
239
159
|
var pixelValueString = "".concat(pixelValue, "px");
|
|
240
|
-
return "${".concat(getTokenNodeForValue(propertyName, pixelValueString), "}");
|
|
160
|
+
return "${".concat((0, _utils.getTokenNodeForValue)(propertyName, pixelValueString), "}");
|
|
241
161
|
}).join(' '), "`"));
|
|
242
162
|
} : undefined
|
|
243
163
|
});
|
|
@@ -253,96 +173,115 @@ var rule = {
|
|
|
253
173
|
return;
|
|
254
174
|
}
|
|
255
175
|
var parentNode = (0, _utils.findParentNodeForLine)(node);
|
|
256
|
-
var
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
var
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
value = _style$split4[1];
|
|
279
|
-
var propertyName = (0, _utils.convertHyphenatedNameToCamelCase)(rawProperty);
|
|
280
|
-
if (!shouldAnalyzeProperty(propertyName, targetCategories)) {
|
|
281
|
-
return;
|
|
176
|
+
var processedCssLines = (0, _utils.processCssNode)(node, context);
|
|
177
|
+
var globalFontSize = (0, _utils.getFontSizeValueInScope)(processedCssLines);
|
|
178
|
+
var textForSource = context.getSourceCode().getText(node.quasi);
|
|
179
|
+
var allReplacedValues = [];
|
|
180
|
+
var completeSource = processedCssLines.reduce(function (currentSource, _ref) {
|
|
181
|
+
var _ref2 = (0, _slicedToArray2.default)(_ref, 2),
|
|
182
|
+
resolvedCssLine = _ref2[0],
|
|
183
|
+
originalCssLine = _ref2[1];
|
|
184
|
+
var _resolvedCssLine$spli = resolvedCssLine.split(':'),
|
|
185
|
+
_resolvedCssLine$spli2 = (0, _slicedToArray2.default)(_resolvedCssLine$spli, 2),
|
|
186
|
+
originalProperty = _resolvedCssLine$spli2[0],
|
|
187
|
+
resolvedCssValues = _resolvedCssLine$spli2[1];
|
|
188
|
+
var _originalCssLine$spli = originalCssLine.split(':'),
|
|
189
|
+
_originalCssLine$spli2 = (0, _slicedToArray2.default)(_originalCssLine$spli, 2),
|
|
190
|
+
_ = _originalCssLine$spli2[0],
|
|
191
|
+
originalCssValues = _originalCssLine$spli2[1];
|
|
192
|
+
var propertyName = (0, _utils.convertHyphenatedNameToCamelCase)(originalProperty);
|
|
193
|
+
var isFontFamily = /fontFamily/.test(propertyName);
|
|
194
|
+
var replacedValuesPerProperty = [originalProperty];
|
|
195
|
+
if (!(0, _utils.shouldAnalyzeProperty)(propertyName, targetCategories) || !resolvedCssValues || !(0, _utils.isValidSpacingValue)(resolvedCssValues, globalFontSize)) {
|
|
196
|
+
// in all of these cases no changes should be made to the current property
|
|
197
|
+
return currentSource;
|
|
282
198
|
}
|
|
283
199
|
|
|
284
|
-
//
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
200
|
+
// gets the values from the associated property, numeric values or NaN
|
|
201
|
+
var processedNumericValues = (0, _utils.getValueFromShorthand)(resolvedCssValues);
|
|
202
|
+
var processedValues = (0, _utils.splitShorthandValues)(resolvedCssValues);
|
|
203
|
+
// only splits shorthand values but it does not transform NaNs so tokens are preserved
|
|
204
|
+
var originalValues = (0, _utils.splitShorthandValues)(originalCssValues);
|
|
205
|
+
|
|
206
|
+
// reconstructing the string
|
|
207
|
+
// should replace what it can and preserve the raw value for everything else
|
|
208
|
+
|
|
209
|
+
var replacementValue = processedNumericValues
|
|
210
|
+
// put together resolved value and original value on a tuple
|
|
211
|
+
.map(function (value, index) {
|
|
212
|
+
return [
|
|
213
|
+
// if emToPX conversion fails we'll default to original value
|
|
214
|
+
(0, _utils.emToPixels)(value, globalFontSize) || value, processedValues[index], originalValues[index]];
|
|
215
|
+
}).map(function (_ref3) {
|
|
216
|
+
var _ref4 = (0, _slicedToArray2.default)(_ref3, 3),
|
|
217
|
+
numericOrNanValue = _ref4[0],
|
|
218
|
+
pxValue = _ref4[1],
|
|
219
|
+
originalValue = _ref4[2];
|
|
220
|
+
if ((0, _utils.isTokenValueString)(originalValue)) {
|
|
221
|
+
// if the value is already valid, nothing to report or replace
|
|
222
|
+
return originalValue;
|
|
223
|
+
}
|
|
224
|
+
if (isNaN(numericOrNanValue) && !isFontFamily) {
|
|
225
|
+
// this can be either a weird expression or a fontsize declaration
|
|
226
|
+
|
|
227
|
+
// we can't replace a NaN but we can alert what the offending value is
|
|
228
|
+
context.report({
|
|
229
|
+
node: node,
|
|
230
|
+
messageId: 'noRawSpacingValues',
|
|
231
|
+
data: {
|
|
232
|
+
payload: "".concat(propertyName, ":").concat(originalValue)
|
|
233
|
+
}
|
|
234
|
+
});
|
|
235
|
+
return originalValue;
|
|
298
236
|
}
|
|
299
|
-
|
|
300
|
-
|
|
237
|
+
|
|
238
|
+
// value is numeric or fontFamily, and needs replacing we'll report first
|
|
301
239
|
context.report({
|
|
302
240
|
node: node,
|
|
303
241
|
messageId: 'noRawSpacingValues',
|
|
304
242
|
data: {
|
|
305
|
-
payload: "".concat(propertyName, ":").concat(
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
var allResolvableValues = values.every(function (value) {
|
|
309
|
-
return !Number.isNaN((0, _utils.emToPixels)(value, fontSize));
|
|
310
|
-
});
|
|
311
|
-
if (!allResolvableValues) {
|
|
312
|
-
return null;
|
|
313
|
-
}
|
|
314
|
-
var replacementValue = values.map(function (value) {
|
|
315
|
-
var propertyValue = typeof value === 'string' ? value.trim() : value;
|
|
316
|
-
var pixelValue = isFontFamily ? propertyValue : (0, _utils.emToPixels)(propertyValue, fontSize);
|
|
317
|
-
var pixelValueString = "".concat(pixelValue, "px");
|
|
318
|
-
var lookupValue = /fontWeight|fontFamily/.test(propertyName) ? pixelValue : pixelValueString;
|
|
319
|
-
var tokenName = (0, _utils.isTypographyProperty)(propertyName) ? typographyValueToToken[propertyName][lookupValue] : spacingValueToToken[lookupValue];
|
|
320
|
-
if (!tokenName) {
|
|
321
|
-
return pixelValueString;
|
|
322
|
-
}
|
|
323
|
-
var replacementTokenValue = getTokenNodeForValue(propertyName, lookupValue);
|
|
243
|
+
payload: "".concat(propertyName, ":").concat(numericOrNanValue)
|
|
244
|
+
}
|
|
245
|
+
});
|
|
324
246
|
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
247
|
+
// from here on we know value is numeric or a font family, so it might or might not have a token equivalent
|
|
248
|
+
var replacementToken = (0, _utils.getTokenReplacement)(propertyName, numericOrNanValue);
|
|
249
|
+
if (!replacementToken) {
|
|
250
|
+
return originalValue;
|
|
251
|
+
}
|
|
252
|
+
replacedValuesPerProperty.push(isFontFamily ? numericOrNanValue.trim() : pxValue);
|
|
253
|
+
return replacementToken;
|
|
254
|
+
}).join(' ');
|
|
255
|
+
if (replacedValuesPerProperty.length > 1) {
|
|
256
|
+
// first value is the property name, so it will always have at least 1
|
|
257
|
+
allReplacedValues.push(replacedValuesPerProperty);
|
|
258
|
+
}
|
|
329
259
|
|
|
330
|
-
|
|
331
|
-
|
|
260
|
+
// replace property:val with new property:val
|
|
261
|
+
var replacedCssLine = currentSource.replace(originalCssLine, // padding: ${gridSize()}px;
|
|
262
|
+
"".concat(originalProperty, ": ").concat(replacementValue));
|
|
263
|
+
if (!replacedCssLine) {
|
|
264
|
+
return currentSource;
|
|
265
|
+
}
|
|
266
|
+
return replacedCssLine;
|
|
267
|
+
}, textForSource);
|
|
268
|
+
if (completeSource !== textForSource) {
|
|
269
|
+
// means we found some replacement values, well give the option to fix them
|
|
332
270
|
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
271
|
+
var replacementComments = "".concat(allReplacedValues.map(function (replacedProperties) {
|
|
272
|
+
var _replacedProperties = (0, _slicedToArray2.default)(replacedProperties, 1),
|
|
273
|
+
propertyName = _replacedProperties[0];
|
|
274
|
+
var replacedValues = replacedProperties.slice(1).join(' ');
|
|
275
|
+
return "// TODO Delete this comment after verifying spacing token -> previous value `".concat(propertyName, ": ").concat(replacedValues, "`");
|
|
276
|
+
}).join('\n'), "\n");
|
|
277
|
+
context.report({
|
|
278
|
+
node: node,
|
|
279
|
+
messageId: 'autofixesPossible',
|
|
280
|
+
fix: function fix(fixer) {
|
|
281
|
+
return [fixer.insertTextBefore(parentNode, replacementComments), fixer.replaceText(node.quasi, completeSource)];
|
|
282
|
+
}
|
|
344
283
|
});
|
|
345
|
-
}
|
|
284
|
+
}
|
|
346
285
|
}
|
|
347
286
|
};
|
|
348
287
|
}
|
|
@@ -6,11 +6,50 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
6
6
|
});
|
|
7
7
|
exports.emToPixels = exports.convertHyphenatedNameToCamelCase = void 0;
|
|
8
8
|
exports.findIdentifierInParentScope = findIdentifierInParentScope;
|
|
9
|
-
exports.
|
|
9
|
+
exports.findParentNodeForLine = void 0;
|
|
10
|
+
exports.getFontSizeValueInScope = getFontSizeValueInScope;
|
|
11
|
+
exports.getRawExpression = void 0;
|
|
12
|
+
exports.getTokenNodeForValue = getTokenNodeForValue;
|
|
13
|
+
exports.getTokenReplacement = getTokenReplacement;
|
|
14
|
+
exports.isSpacingProperty = exports.getValueFromShorthand = exports.getValue = void 0;
|
|
15
|
+
exports.isTokenValueString = isTokenValueString;
|
|
16
|
+
exports.onlyScaleTokens = exports.isValidSpacingValue = exports.isTypographyProperty = void 0;
|
|
17
|
+
exports.processCssNode = processCssNode;
|
|
18
|
+
exports.removePixelSuffix = void 0;
|
|
19
|
+
exports.shouldAnalyzeProperty = shouldAnalyzeProperty;
|
|
20
|
+
exports.spacingValueToToken = void 0;
|
|
21
|
+
exports.splitCssProperties = splitCssProperties;
|
|
22
|
+
exports.typographyValueToToken = exports.splitShorthandValues = void 0;
|
|
10
23
|
var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
|
|
11
24
|
var _eslintCodemodUtils = require("eslint-codemod-utils");
|
|
25
|
+
var _tokensRaw = require("@atlaskit/tokens/tokens-raw");
|
|
12
26
|
var typographyProperties = ['fontSize', 'fontWeight', 'fontFamily', 'lineHeight'];
|
|
13
27
|
var properties = ['padding', 'paddingBlock', 'paddingInline', 'paddingLeft', 'paddingTop', 'paddingRight', 'paddingBottom', 'paddingInline', 'paddingInlineStart', 'paddingInlineEnd', 'paddingBlock', 'paddingBlockStart', 'paddingBlockEnd', 'marginLeft', 'marginTop', 'marginRight', 'marginBottom', 'marginInline', 'marginInlineStart', 'marginInlineEnd', 'marginBlock', 'marginBlockStart', 'marginBlockEnd', 'margin', 'gap', 'rowGap', 'gridRowGap', 'columnGap', 'gridColumnGap'];
|
|
28
|
+
/**
|
|
29
|
+
* Currently we have a wide range of experimental spacing tokens that we are testing.
|
|
30
|
+
* We only want transforms to apply to the stable scale values, not the rest.
|
|
31
|
+
* This could be removed in the future.
|
|
32
|
+
*/
|
|
33
|
+
var onlyScaleTokens = _tokensRaw.spacing.filter(function (token) {
|
|
34
|
+
return token.name.startsWith('space.');
|
|
35
|
+
});
|
|
36
|
+
exports.onlyScaleTokens = onlyScaleTokens;
|
|
37
|
+
var spacingValueToToken = Object.fromEntries(onlyScaleTokens.map(function (token) {
|
|
38
|
+
return [token.attributes['pixelValue'], token.name];
|
|
39
|
+
}));
|
|
40
|
+
exports.spacingValueToToken = spacingValueToToken;
|
|
41
|
+
var typographyValueToToken = Object.fromEntries(_tokensRaw.typography.map(function (currentToken) {
|
|
42
|
+
// Group tokens by property name (e.g. fontSize, fontFamily, lineHeight)
|
|
43
|
+
// This allows us to look up values specific to a property
|
|
44
|
+
// (so as not to mix tokens with overlapping values e.g. font size and line height both have tokens for 16px)
|
|
45
|
+
var tokenGroup = currentToken.attributes.group;
|
|
46
|
+
return [tokenGroup, Object.fromEntries(_tokensRaw.typography.map(function (token) {
|
|
47
|
+
return token.attributes.group === tokenGroup ? [token.value.replaceAll("\"", "'"), token.name] : [];
|
|
48
|
+
}).filter(function (token) {
|
|
49
|
+
return token.length;
|
|
50
|
+
}))];
|
|
51
|
+
}));
|
|
52
|
+
exports.typographyValueToToken = typographyValueToToken;
|
|
14
53
|
function findIdentifierInParentScope(_ref) {
|
|
15
54
|
var scope = _ref.scope,
|
|
16
55
|
identifierName = _ref.identifierName;
|
|
@@ -34,6 +73,11 @@ var isTypographyProperty = function isTypographyProperty(propertyName) {
|
|
|
34
73
|
return typographyProperties.includes(propertyName);
|
|
35
74
|
};
|
|
36
75
|
exports.isTypographyProperty = isTypographyProperty;
|
|
76
|
+
var splitShorthandValues = function splitShorthandValues(str) {
|
|
77
|
+
// Regex accomplishes split str by whitespace but ignore spaces in between ${}
|
|
78
|
+
return str.split(/(\${[^}]*}\S*)|\s+/g).filter(Boolean);
|
|
79
|
+
};
|
|
80
|
+
exports.splitShorthandValues = splitShorthandValues;
|
|
37
81
|
var getValueFromShorthand = function getValueFromShorthand(str) {
|
|
38
82
|
var valueString = String(str);
|
|
39
83
|
var fontFamily = /(sans-serif$)|(monospace$)/;
|
|
@@ -41,9 +85,7 @@ var getValueFromShorthand = function getValueFromShorthand(str) {
|
|
|
41
85
|
return [valueString];
|
|
42
86
|
}
|
|
43
87
|
// If we want to filter out NaN just add .filter(Boolean)
|
|
44
|
-
return String(str).trim()
|
|
45
|
-
return val !== '';
|
|
46
|
-
}).map(removePixelSuffix);
|
|
88
|
+
return splitShorthandValues(String(str).trim()).map(removePixelSuffix);
|
|
47
89
|
};
|
|
48
90
|
exports.getValueFromShorthand = getValueFromShorthand;
|
|
49
91
|
var isGridSize = function isGridSize(node) {
|
|
@@ -129,7 +171,7 @@ var getRawExpression = function getRawExpression(node, context) {
|
|
|
129
171
|
if (!(
|
|
130
172
|
// if not one of our recognized types or doesn't have a range prop, early return
|
|
131
173
|
|
|
132
|
-
(0, _eslintCodemodUtils.isNodeOfType)(node, 'Literal') || (0, _eslintCodemodUtils.isNodeOfType)(node, 'Identifier') || (0, _eslintCodemodUtils.isNodeOfType)(node, 'BinaryExpression') || (0, _eslintCodemodUtils.isNodeOfType)(node, 'UnaryExpression') || (0, _eslintCodemodUtils.isNodeOfType)(node, 'TemplateLiteral') || (0, _eslintCodemodUtils.isNodeOfType)(node, 'CallExpression')) || !Array.isArray(node.range)) {
|
|
174
|
+
(0, _eslintCodemodUtils.isNodeOfType)(node, 'Literal') || (0, _eslintCodemodUtils.isNodeOfType)(node, 'Identifier') || (0, _eslintCodemodUtils.isNodeOfType)(node, 'BinaryExpression') || (0, _eslintCodemodUtils.isNodeOfType)(node, 'UnaryExpression') || (0, _eslintCodemodUtils.isNodeOfType)(node, 'TemplateLiteral') || (0, _eslintCodemodUtils.isNodeOfType)(node, 'CallExpression') || (0, _eslintCodemodUtils.isNodeOfType)(node, 'ArrowFunctionExpression')) || !Array.isArray(node.range)) {
|
|
133
175
|
return null;
|
|
134
176
|
}
|
|
135
177
|
var _node$range = (0, _slicedToArray2.default)(node.range, 2),
|
|
@@ -142,6 +184,9 @@ var getValueFromIdentifier = function getValueFromIdentifier(node, context) {
|
|
|
142
184
|
if (!(0, _eslintCodemodUtils.isNodeOfType)(node, 'Identifier')) {
|
|
143
185
|
return null;
|
|
144
186
|
}
|
|
187
|
+
if (node.name === 'gridSize') {
|
|
188
|
+
return 8;
|
|
189
|
+
}
|
|
145
190
|
var scope = context.getScope();
|
|
146
191
|
var variable = findIdentifierInParentScope({
|
|
147
192
|
scope: scope,
|
|
@@ -286,4 +331,145 @@ var findParentNodeForLine = function findParentNodeForLine(node) {
|
|
|
286
331
|
return findParentNodeForLine(node.parent);
|
|
287
332
|
}
|
|
288
333
|
};
|
|
289
|
-
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* Returns a boolean that signals wether the current property is revelant under the current configuration
|
|
337
|
+
* @param propertyName camelCase CSS property
|
|
338
|
+
* @param targetOptions Array containing the types of properties that should be included in the rule
|
|
339
|
+
* @example
|
|
340
|
+
* ```
|
|
341
|
+
* propertyName: padding, targetOptions: ['spacing']
|
|
342
|
+
* propertyName: fontWeight, targetOptions: ['spacing', 'typography']
|
|
343
|
+
* ```
|
|
344
|
+
*/
|
|
345
|
+
exports.findParentNodeForLine = findParentNodeForLine;
|
|
346
|
+
function shouldAnalyzeProperty(propertyName, targetOptions) {
|
|
347
|
+
if (isSpacingProperty(propertyName) && targetOptions.includes('spacing')) {
|
|
348
|
+
return true;
|
|
349
|
+
}
|
|
350
|
+
if (isTypographyProperty(propertyName) && targetOptions.includes('typography')) {
|
|
351
|
+
return true;
|
|
352
|
+
}
|
|
353
|
+
return false;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* Returns an array of tuples representing a processed css within `TaggedTemplateExpression` node.
|
|
358
|
+
* each element of the array is a tuple `[string, string]`,
|
|
359
|
+
* where the first element is the processed css line with computed values
|
|
360
|
+
* and the second element of the tuple is the original css line from source
|
|
361
|
+
* @param node TaggedTemplateExpression node
|
|
362
|
+
* @param context Rule.RuleContext
|
|
363
|
+
* @example
|
|
364
|
+
* ```
|
|
365
|
+
* `[['padding: 8', 'padding: ${gridSize()}'], ['margin: 6', 'margin: 6px' ]]`
|
|
366
|
+
* ```
|
|
367
|
+
*/
|
|
368
|
+
function processCssNode(node, context) {
|
|
369
|
+
var combinedString = node.quasi.quasis.map(function (q, i) {
|
|
370
|
+
return "".concat(q.value.raw).concat(node.quasi.expressions[i] ? getValue(node.quasi.expressions[i], context) : '');
|
|
371
|
+
}).join('');
|
|
372
|
+
var rawString = node.quasi.quasis.map(function (q, i) {
|
|
373
|
+
return "".concat(q.value.raw).concat(node.quasi.expressions[i] ? "${".concat(getRawExpression(node.quasi.expressions[i], context), "}") : '');
|
|
374
|
+
}).join('');
|
|
375
|
+
var cssProperties = splitCssProperties(combinedString);
|
|
376
|
+
var unalteredCssProperties = splitCssProperties(rawString);
|
|
377
|
+
return cssProperties.map(function (cssProperty, index) {
|
|
378
|
+
return [cssProperty, unalteredCssProperties[index]];
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* Returns a token node for a given value including fallbacks.
|
|
384
|
+
* @param propertyName camelCase CSS property
|
|
385
|
+
* @param value string representing pixel value, or font family, or number representing font weight
|
|
386
|
+
* @example
|
|
387
|
+
* ```
|
|
388
|
+
* propertyName: padding, value: '8px' => token('space.100', '8px')
|
|
389
|
+
* propertyName: fontWeight, value: 400 => token('font.weight.regular', '400')
|
|
390
|
+
* ```
|
|
391
|
+
*/
|
|
392
|
+
function getTokenNodeForValue(propertyName, value) {
|
|
393
|
+
var token = isTypographyProperty(propertyName) ? typographyValueToToken[propertyName][value] : spacingValueToToken[value];
|
|
394
|
+
var fallbackValue = propertyName === 'fontFamily' ? {
|
|
395
|
+
value: "".concat(value),
|
|
396
|
+
raw: "\"".concat(value, "\"")
|
|
397
|
+
} : "".concat(value);
|
|
398
|
+
return (0, _eslintCodemodUtils.callExpression)({
|
|
399
|
+
callee: (0, _eslintCodemodUtils.identifier)({
|
|
400
|
+
name: 'token'
|
|
401
|
+
}),
|
|
402
|
+
arguments: [(0, _eslintCodemodUtils.literal)({
|
|
403
|
+
value: "'".concat(token !== null && token !== void 0 ? token : '', "'")
|
|
404
|
+
}), (0, _eslintCodemodUtils.literal)(fallbackValue)],
|
|
405
|
+
optional: false
|
|
406
|
+
});
|
|
407
|
+
}
|
|
408
|
+
function getFontSizeValueInScope(cssProperties) {
|
|
409
|
+
var fontSizeNode = cssProperties.find(function (_ref2) {
|
|
410
|
+
var _ref3 = (0, _slicedToArray2.default)(_ref2, 1),
|
|
411
|
+
style = _ref3[0];
|
|
412
|
+
var _style$split = style.split(':'),
|
|
413
|
+
_style$split2 = (0, _slicedToArray2.default)(_style$split, 2),
|
|
414
|
+
rawProperty = _style$split2[0],
|
|
415
|
+
value = _style$split2[1];
|
|
416
|
+
return /font-size/.test(rawProperty) ? value : null;
|
|
417
|
+
});
|
|
418
|
+
if (!fontSizeNode) {
|
|
419
|
+
return undefined;
|
|
420
|
+
}
|
|
421
|
+
var _fontSizeNode$0$split = fontSizeNode[0].split(':'),
|
|
422
|
+
_fontSizeNode$0$split2 = (0, _slicedToArray2.default)(_fontSizeNode$0$split, 2),
|
|
423
|
+
_ = _fontSizeNode$0$split2[0],
|
|
424
|
+
fontSizeValue = _fontSizeNode$0$split2[1];
|
|
425
|
+
if (!fontSizeValue) {
|
|
426
|
+
return undefined;
|
|
427
|
+
}
|
|
428
|
+
return getValueFromShorthand(fontSizeValue)[0];
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
/**
|
|
432
|
+
* Attempts to remove all non-essential words & characters from a style block.
|
|
433
|
+
* Including selectors and queries
|
|
434
|
+
* Adapted from ensure-design-token-usage
|
|
435
|
+
* @param styleString string of css properties
|
|
436
|
+
*/
|
|
437
|
+
function splitCssProperties(styleString) {
|
|
438
|
+
return styleString.split('\n').filter(function (line) {
|
|
439
|
+
return !line.trim().startsWith('@');
|
|
440
|
+
}).join('\n').replace(/\n/g, '').split(/;|(?<!\$){|(?<!\${.+?)}/) // don't split on template literal expressions i.e. `${...}`
|
|
441
|
+
.map(function (el) {
|
|
442
|
+
return el.trim() || '';
|
|
443
|
+
}).filter(Boolean);
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
/**
|
|
447
|
+
* returns wether the current string is a token value
|
|
448
|
+
* @param originalVaue string representing a css property value e.g 1em, 12px
|
|
449
|
+
*/
|
|
450
|
+
function isTokenValueString(originalValue) {
|
|
451
|
+
return originalValue.startsWith('${token(') && originalValue.endsWith('}');
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
/**
|
|
455
|
+
* Returns a string with token expression corresponding to input parameters
|
|
456
|
+
* if no token found for the pair the function returns undefined
|
|
457
|
+
* @param propertyName string camelCased css property
|
|
458
|
+
* @param value the computed value e.g '8px' -> '8'
|
|
459
|
+
*/
|
|
460
|
+
function getTokenReplacement(propertyName, value) {
|
|
461
|
+
var isFontWeightOrFamily = /fontWeight|fontFamily/.test(propertyName);
|
|
462
|
+
var propertyValue = typeof value === 'string' ? value.trim() : value;
|
|
463
|
+
var pixelValue = propertyValue;
|
|
464
|
+
var pixelValueString = "".concat(propertyValue, "px");
|
|
465
|
+
var lookupValue = isFontWeightOrFamily ? pixelValue : pixelValueString;
|
|
466
|
+
var tokenName = isTypographyProperty(propertyName) ? typographyValueToToken[propertyName][lookupValue] : spacingValueToToken[lookupValue];
|
|
467
|
+
if (!tokenName) {
|
|
468
|
+
return undefined;
|
|
469
|
+
}
|
|
470
|
+
var replacementTokenValue = getTokenNodeForValue(propertyName, lookupValue);
|
|
471
|
+
|
|
472
|
+
// ${token('...', '...')}
|
|
473
|
+
var replacementSubValue = '${' + replacementTokenValue.toString() + '}';
|
|
474
|
+
return replacementSubValue;
|
|
475
|
+
}
|
package/dist/cjs/version.json
CHANGED