@atlaskit/eslint-plugin-design-system 8.32.2 → 8.34.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/README.md +1 -0
- package/constellation/ensure-design-token-usage/usage.mdx +2 -2
- package/constellation/index/usage.mdx +1 -0
- package/constellation/use-tokens-typography/usage.mdx +42 -0
- package/dist/cjs/ast-nodes/index.js +7 -0
- package/dist/cjs/ast-nodes/object-entry.js +27 -0
- package/dist/cjs/ast-nodes/object.js +1 -1
- package/dist/cjs/presets/all.codegen.js +2 -1
- package/dist/cjs/rules/consistent-css-prop-usage/index.js +7 -0
- package/dist/cjs/rules/ensure-design-token-usage/index.js +5 -4
- package/dist/cjs/rules/ensure-design-token-usage/rule-meta.js +1 -1
- package/dist/cjs/rules/ensure-design-token-usage/spacing.js +5 -1
- package/dist/cjs/rules/ensure-design-token-usage/utils.js +52 -42
- package/dist/cjs/rules/index.codegen.js +3 -1
- package/dist/cjs/rules/use-tokens-typography/config/index.js +26 -0
- package/dist/cjs/rules/use-tokens-typography/error-boundary.js +24 -0
- package/dist/cjs/rules/use-tokens-typography/index.js +44 -0
- package/dist/cjs/rules/use-tokens-typography/transformers/style-object.js +212 -0
- package/dist/cjs/rules/use-tokens-typography/utils.js +146 -0
- package/dist/es2019/ast-nodes/index.js +1 -0
- package/dist/es2019/ast-nodes/object-entry.js +22 -0
- package/dist/es2019/ast-nodes/object.js +1 -1
- package/dist/es2019/presets/all.codegen.js +2 -1
- package/dist/es2019/rules/consistent-css-prop-usage/index.js +5 -0
- package/dist/es2019/rules/ensure-design-token-usage/index.js +6 -5
- package/dist/es2019/rules/ensure-design-token-usage/rule-meta.js +1 -1
- package/dist/es2019/rules/ensure-design-token-usage/spacing.js +5 -1
- package/dist/es2019/rules/ensure-design-token-usage/utils.js +42 -38
- package/dist/es2019/rules/index.codegen.js +3 -1
- package/dist/es2019/rules/use-tokens-typography/config/index.js +20 -0
- package/dist/es2019/rules/use-tokens-typography/error-boundary.js +19 -0
- package/dist/es2019/rules/use-tokens-typography/index.js +36 -0
- package/dist/es2019/rules/use-tokens-typography/transformers/style-object.js +209 -0
- package/dist/es2019/rules/use-tokens-typography/utils.js +99 -0
- package/dist/esm/ast-nodes/index.js +1 -0
- package/dist/esm/ast-nodes/object-entry.js +22 -0
- package/dist/esm/ast-nodes/object.js +1 -1
- package/dist/esm/presets/all.codegen.js +2 -1
- package/dist/esm/rules/consistent-css-prop-usage/index.js +7 -0
- package/dist/esm/rules/ensure-design-token-usage/index.js +6 -5
- package/dist/esm/rules/ensure-design-token-usage/rule-meta.js +1 -1
- package/dist/esm/rules/ensure-design-token-usage/spacing.js +5 -1
- package/dist/esm/rules/ensure-design-token-usage/utils.js +46 -38
- package/dist/esm/rules/index.codegen.js +3 -1
- package/dist/esm/rules/use-tokens-typography/config/index.js +20 -0
- package/dist/esm/rules/use-tokens-typography/error-boundary.js +18 -0
- package/dist/esm/rules/use-tokens-typography/index.js +38 -0
- package/dist/esm/rules/use-tokens-typography/transformers/style-object.js +206 -0
- package/dist/esm/rules/use-tokens-typography/utils.js +129 -0
- package/dist/types/ast-nodes/index.d.ts +1 -0
- package/dist/types/ast-nodes/object-entry.d.ts +6 -0
- package/dist/types/ast-nodes/object.d.ts +1 -1
- package/dist/types/index.codegen.d.ts +1 -0
- package/dist/types/presets/all.codegen.d.ts +2 -1
- package/dist/types/rules/ensure-design-token-usage/types.d.ts +1 -1
- package/dist/types/rules/ensure-design-token-usage/utils.d.ts +22 -22
- package/dist/types/rules/index.codegen.d.ts +1 -0
- package/dist/types/rules/use-tokens-typography/config/index.d.ts +6 -0
- package/dist/types/rules/use-tokens-typography/error-boundary.d.ts +11 -0
- package/dist/types/rules/use-tokens-typography/index.d.ts +3 -0
- package/dist/types/rules/use-tokens-typography/transformers/style-object.d.ts +31 -0
- package/dist/types/rules/use-tokens-typography/utils.d.ts +161 -0
- package/dist/types-ts4.5/ast-nodes/index.d.ts +1 -0
- package/dist/types-ts4.5/ast-nodes/object-entry.d.ts +6 -0
- package/dist/types-ts4.5/ast-nodes/object.d.ts +1 -1
- package/dist/types-ts4.5/index.codegen.d.ts +1 -0
- package/dist/types-ts4.5/presets/all.codegen.d.ts +2 -1
- package/dist/types-ts4.5/rules/ensure-design-token-usage/types.d.ts +1 -1
- package/dist/types-ts4.5/rules/ensure-design-token-usage/utils.d.ts +22 -22
- package/dist/types-ts4.5/rules/index.codegen.d.ts +1 -0
- package/dist/types-ts4.5/rules/use-tokens-typography/config/index.d.ts +6 -0
- package/dist/types-ts4.5/rules/use-tokens-typography/error-boundary.d.ts +11 -0
- package/dist/types-ts4.5/rules/use-tokens-typography/index.d.ts +3 -0
- package/dist/types-ts4.5/rules/use-tokens-typography/transformers/style-object.d.ts +31 -0
- package/dist/types-ts4.5/rules/use-tokens-typography/utils.d.ts +161 -0
- package/package.json +1 -1
- package/dist/cjs/rules/ensure-design-token-usage/typography.js +0 -39
- package/dist/es2019/rules/ensure-design-token-usage/typography.js +0 -19
- package/dist/esm/rules/ensure-design-token-usage/typography.js +0 -33
- package/dist/types/rules/ensure-design-token-usage/typography.d.ts +0 -9
- package/dist/types-ts4.5/rules/ensure-design-token-usage/typography.d.ts +0 -9
|
@@ -391,6 +391,13 @@ var JSXExpressionLinter = /*#__PURE__*/function () {
|
|
|
391
391
|
case 'CallExpression':
|
|
392
392
|
case 'TaggedTemplateExpression':
|
|
393
393
|
case 'TemplateLiteral':
|
|
394
|
+
if (expression.type === 'CallExpression' && expression.callee.type === 'Identifier' && expression.callee.name === 'cx') {
|
|
395
|
+
expression.arguments.forEach(function (exp) {
|
|
396
|
+
return exp && _this3.traverseExpression(exp);
|
|
397
|
+
});
|
|
398
|
+
return;
|
|
399
|
+
}
|
|
400
|
+
|
|
394
401
|
// We've found elements that shouldn't be here! Report an error.
|
|
395
402
|
this.context.report({
|
|
396
403
|
node: expression,
|
|
@@ -9,7 +9,7 @@ import { lintJSXIdentifierForColor, lintJSXLiteralForColor, lintJSXMemberForColo
|
|
|
9
9
|
import { errorBoundary } from './error-boundary';
|
|
10
10
|
import ruleMeta from './rule-meta';
|
|
11
11
|
import { lintObjectForSpacing } from './spacing';
|
|
12
|
-
import { convertHyphenatedNameToCamelCase, emToPixels, getDomainsForProperty,
|
|
12
|
+
import { convertHyphenatedNameToCamelCase, emToPixels, getDomainsForProperty, getFontSizeValueInScope, getPropertyNodeFromParent, getTokenReplacement, getValueForPropertyNode, getValueFromShorthand, getValueFromTemplateLiteralRaw, includesTokenString, insertTokensImport, isAuto, isCalc, isTokenValueString, isValidSpacingValue, isZero, processCssNode, splitShorthandValues } from './utils';
|
|
13
13
|
var defaultConfig = {
|
|
14
14
|
domains: ['color', 'spacing'],
|
|
15
15
|
applyImport: true,
|
|
@@ -104,11 +104,12 @@ var createWithConfig = function createWithConfig(initialConfig) {
|
|
|
104
104
|
if (domains.includes('color')) {
|
|
105
105
|
return lintObjectForColor(node, context, config);
|
|
106
106
|
}
|
|
107
|
-
if (domains.includes('spacing') || domains.includes('shape')
|
|
107
|
+
if (domains.includes('spacing') || domains.includes('shape')) {
|
|
108
108
|
/**
|
|
109
|
-
* We do this in case the fontSize for a style object is declared alongside the `em` or `lineHeight` declaration
|
|
109
|
+
* We do this in case the fontSize for a style object is declared alongside the `em` or `lineHeight` declaration.
|
|
110
110
|
*/
|
|
111
|
-
var
|
|
111
|
+
var fontSizeNode = getPropertyNodeFromParent('fontSize', parentNode);
|
|
112
|
+
var fontSize = fontSizeNode && getValueForPropertyNode(fontSizeNode, context);
|
|
112
113
|
return lintObjectForSpacing(node, context, config, fontSize, tokenNode);
|
|
113
114
|
}
|
|
114
115
|
}
|
|
@@ -166,7 +167,7 @@ var createWithConfig = function createWithConfig(initialConfig) {
|
|
|
166
167
|
return currentSource;
|
|
167
168
|
}
|
|
168
169
|
}
|
|
169
|
-
if (domains.includes('spacing') || domains.includes('
|
|
170
|
+
if (domains.includes('spacing') || domains.includes('shape')) {
|
|
170
171
|
if (!isValidSpacingValue(resolvedCssValues, globalFontSize)) {
|
|
171
172
|
// no changes should be made to the current property
|
|
172
173
|
return currentSource;
|
|
@@ -69,7 +69,8 @@ export var lintObjectForSpacing = function lintObjectForSpacing(node, context, r
|
|
|
69
69
|
payload: "".concat(propertyName, ":").concat(pixelValue)
|
|
70
70
|
},
|
|
71
71
|
fix: function fix(fixer) {
|
|
72
|
-
|
|
72
|
+
// Casting due to possibility of pixelValue being string | number from emToPixels
|
|
73
|
+
var replacementNode = pixelValue && getTokenReplacement(propertyName, pixelValue);
|
|
73
74
|
if (!replacementNode) {
|
|
74
75
|
return null;
|
|
75
76
|
}
|
|
@@ -116,6 +117,8 @@ export var lintObjectForSpacing = function lintObjectForSpacing(node, context, r
|
|
|
116
117
|
if (!allResolvableValues) {
|
|
117
118
|
return null;
|
|
118
119
|
}
|
|
120
|
+
|
|
121
|
+
// Casting due to possibility of value being string | number
|
|
119
122
|
var valuesWithTokenReplacement = valuesForProperty.filter(function (value) {
|
|
120
123
|
return findTokenNameByPropertyValue(propertyName, value);
|
|
121
124
|
}).filter(function (value) {
|
|
@@ -149,6 +152,7 @@ export var lintObjectForSpacing = function lintObjectForSpacing(node, context, r
|
|
|
149
152
|
var pixelValueString = "".concat(pixelValue, "px");
|
|
150
153
|
// if there is a token we take it, otherwise we go with the original value
|
|
151
154
|
|
|
155
|
+
// Casting due to possibility of value being string | number
|
|
152
156
|
return findTokenNameByPropertyValue(propertyName, value) ? "${".concat(getTokenNodeForValue(propertyName, pixelValueString), "}") : originalValues[index];
|
|
153
157
|
}).join(' '), "`"))]);
|
|
154
158
|
}
|
|
@@ -6,7 +6,6 @@ import { spacing as spacingScale } from '@atlaskit/tokens/tokens-raw';
|
|
|
6
6
|
import { findIdentifierInParentScope } from '../utils/find-in-parent';
|
|
7
7
|
import { isColorCssPropertyName, isCurrentSurfaceCustomPropertyName } from '../utils/is-color';
|
|
8
8
|
import { borderWidthValueToToken, isBorderRadius, isBorderSizeProperty, isShapeProperty, radiusValueToToken } from './shape';
|
|
9
|
-
import { isCodeFontFamily, isFontFamily, isFontSize, isFontSizeSmall, isTypographyProperty, typographyValueToToken } from './typography';
|
|
10
9
|
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', 'top', 'left', 'right', 'bottom', 'inlineStart', 'inlineEnd', 'blockStart', 'blockEnd', 'outline-offset'];
|
|
11
10
|
var spacingValueToToken = Object.fromEntries(spacingScale.map(function (token) {
|
|
12
11
|
return [token.value, token.cleanName];
|
|
@@ -34,8 +33,10 @@ export var splitShorthandValues = function splitShorthandValues(str) {
|
|
|
34
33
|
};
|
|
35
34
|
export var getValueFromShorthand = function getValueFromShorthand(str) {
|
|
36
35
|
var valueString = String(str);
|
|
37
|
-
var fontFamily = /(sans-serif$)|(monospace$)/;
|
|
38
|
-
|
|
36
|
+
var fontFamily = /(Charlie)|(sans-serif$)|(monospace$)/;
|
|
37
|
+
var fontWeightString = /(regular$)|(medium$)|(semibold$)|(bold$)/;
|
|
38
|
+
var fontStyleString = /(inherit$)|(normal$)|(italic$)/;
|
|
39
|
+
if (fontFamily.test(valueString) || fontWeightString.test(valueString) || fontStyleString.test(valueString)) {
|
|
39
40
|
return [valueString];
|
|
40
41
|
}
|
|
41
42
|
// If we want to filter out NaN just add .filter(Boolean)
|
|
@@ -64,6 +65,12 @@ var getRawExpressionForToken = function getRawExpressionForToken(node, context)
|
|
|
64
65
|
}).join(', '), ")}");
|
|
65
66
|
return call;
|
|
66
67
|
};
|
|
68
|
+
var isFontSize = function isFontSize(node) {
|
|
69
|
+
return isNodeOfType(node, 'CallExpression') && isNodeOfType(node.callee, 'Identifier') && (node.callee.name === 'fontSize' || node.callee.name === 'getFontSize');
|
|
70
|
+
};
|
|
71
|
+
var isFontSizeSmall = function isFontSizeSmall(node) {
|
|
72
|
+
return isNodeOfType(node, 'CallExpression') && isNodeOfType(node.callee, 'Identifier') && node.callee.name === 'fontSizeSmall';
|
|
73
|
+
};
|
|
67
74
|
var getValueFromCallExpression = function getValueFromCallExpression(node, context) {
|
|
68
75
|
if (!isNodeOfType(node, 'CallExpression')) {
|
|
69
76
|
return null;
|
|
@@ -80,12 +87,6 @@ var getValueFromCallExpression = function getValueFromCallExpression(node, conte
|
|
|
80
87
|
if (isFontSizeSmall(node)) {
|
|
81
88
|
return 11;
|
|
82
89
|
}
|
|
83
|
-
if (isFontFamily(node)) {
|
|
84
|
-
return "-apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif";
|
|
85
|
-
}
|
|
86
|
-
if (isCodeFontFamily(node)) {
|
|
87
|
-
return "'SFMono-Medium', 'SF Mono', 'Segoe UI Mono', 'Roboto Mono', 'Ubuntu Mono', Menlo, Consolas, Courier, monospace";
|
|
88
|
-
}
|
|
89
90
|
if (isToken(node)) {
|
|
90
91
|
return getRawExpressionForToken(node, context);
|
|
91
92
|
}
|
|
@@ -290,7 +291,7 @@ export var convertHyphenatedNameToCamelCase = function convertHyphenatedNameToCa
|
|
|
290
291
|
|
|
291
292
|
/**
|
|
292
293
|
* @param node
|
|
293
|
-
* @returns The furthest parent node that is on the same line as the input node
|
|
294
|
+
* @returns The furthest parent node that is on the same line as the input node.
|
|
294
295
|
*/
|
|
295
296
|
export var findParentNodeForLine = function findParentNodeForLine(node) {
|
|
296
297
|
var _node$loc, _node$parent$loc;
|
|
@@ -305,14 +306,13 @@ export var findParentNodeForLine = function findParentNodeForLine(node) {
|
|
|
305
306
|
};
|
|
306
307
|
|
|
307
308
|
/**
|
|
308
|
-
* Returns an array of domains that are relevant to the provided property based on the rule options
|
|
309
|
+
* Returns an array of domains that are relevant to the provided property based on the rule options.
|
|
309
310
|
* @param propertyName camelCase CSS property
|
|
310
|
-
* @param targetOptions Array containing the types of properties that should be included in the rule
|
|
311
|
+
* @param targetOptions Array containing the types of properties that should be included in the rule.
|
|
311
312
|
* @example
|
|
312
313
|
* ```
|
|
313
314
|
* propertyName: padding, targetOptions: ['spacing'] -> returns ['spacing']
|
|
314
|
-
* propertyName:
|
|
315
|
-
* propertyName: backgroundColor, targetOptions: ['spacing', 'typography'] -> returns []
|
|
315
|
+
* propertyName: backgroundColor, targetOptions: ['spacing'] -> returns []
|
|
316
316
|
* propertyName: backgroundColor, targetOptions: ['color', 'spacing'] -> returns ['color']
|
|
317
317
|
* ```
|
|
318
318
|
*/
|
|
@@ -327,15 +327,12 @@ export function getDomainsForProperty(propertyName, targetOptions) {
|
|
|
327
327
|
if (isShapeProperty(propertyName) && targetOptions.includes('shape')) {
|
|
328
328
|
domains.push('shape');
|
|
329
329
|
}
|
|
330
|
-
if (isTypographyProperty(propertyName) && targetOptions.includes('typography')) {
|
|
331
|
-
domains.push('typography');
|
|
332
|
-
}
|
|
333
330
|
return domains;
|
|
334
331
|
}
|
|
335
332
|
|
|
336
333
|
/**
|
|
337
334
|
* Function that removes JS comments from a string of code,
|
|
338
|
-
* sometimes makers will have single or multiline comments in their tagged template literals styles, this can mess with our parsing logic
|
|
335
|
+
* sometimes makers will have single or multiline comments in their tagged template literals styles, this can mess with our parsing logic.
|
|
339
336
|
*/
|
|
340
337
|
export function cleanComments(str) {
|
|
341
338
|
return str.replace(/\/\*([\s\S]*?)\*\//g, '').replace(/\/\/(.*)/g, '');
|
|
@@ -343,11 +340,11 @@ export function cleanComments(str) {
|
|
|
343
340
|
|
|
344
341
|
/**
|
|
345
342
|
* Returns an array of tuples representing a processed css within `TaggedTemplateExpression` node.
|
|
346
|
-
*
|
|
343
|
+
* Each element of the array is a tuple `[string, string]`,
|
|
347
344
|
* where the first element is the processed css line with computed values
|
|
348
|
-
* and the second element of the tuple is the original css line from source
|
|
349
|
-
* @param node TaggedTemplateExpression node
|
|
350
|
-
* @param context Rule.RuleContext
|
|
345
|
+
* and the second element of the tuple is the original css line from source.
|
|
346
|
+
* @param node TaggedTemplateExpression node.
|
|
347
|
+
* @param context Rule.RuleContext.
|
|
351
348
|
* @example
|
|
352
349
|
* ```
|
|
353
350
|
* `[['padding: 8', 'padding: ${gridSize()}'], ['margin: 6', 'margin: 6px' ]]`
|
|
@@ -422,7 +419,7 @@ export function getFontSizeValueInScope(cssProperties) {
|
|
|
422
419
|
|
|
423
420
|
/**
|
|
424
421
|
* Attempts to remove all non-essential words & characters from a style block.
|
|
425
|
-
* Including selectors and queries
|
|
422
|
+
* Including selectors and queries.
|
|
426
423
|
* @param styleString string of css properties
|
|
427
424
|
*/
|
|
428
425
|
export function splitCssProperties(styleString) {
|
|
@@ -447,8 +444,8 @@ export function splitCssProperties(styleString) {
|
|
|
447
444
|
}
|
|
448
445
|
|
|
449
446
|
/**
|
|
450
|
-
*
|
|
451
|
-
* @param originalVaue string representing a css property value e.g 1em, 12px
|
|
447
|
+
* Returns whether the current string is a token value.
|
|
448
|
+
* @param originalVaue string representing a css property value e.g 1em, 12px.
|
|
452
449
|
*/
|
|
453
450
|
export function isTokenValueString(originalValue) {
|
|
454
451
|
return originalValue.startsWith('${token(') && originalValue.endsWith('}');
|
|
@@ -462,19 +459,27 @@ export function includesTokenString(originalValue) {
|
|
|
462
459
|
*
|
|
463
460
|
* -> for pixels this '8px'
|
|
464
461
|
* -> for weights '400'
|
|
465
|
-
* -> for family 'Arial'
|
|
462
|
+
* -> for family 'Arial'.
|
|
466
463
|
*
|
|
467
464
|
* @internal
|
|
468
465
|
*/
|
|
469
466
|
export function normaliseValue(propertyName, value) {
|
|
470
|
-
var
|
|
467
|
+
var isFontStringProperty = /fontWeight|fontFamily|fontStyle/.test(propertyName);
|
|
468
|
+
var isLineHeight = /lineHeight/.test(propertyName);
|
|
471
469
|
var propertyValue = typeof value === 'string' ? value.trim() : value;
|
|
472
|
-
var lookupValue
|
|
470
|
+
var lookupValue;
|
|
471
|
+
if (isFontStringProperty) {
|
|
472
|
+
lookupValue = "".concat(propertyValue);
|
|
473
|
+
} else if (isLineHeight) {
|
|
474
|
+
lookupValue = value === 1 ? "".concat(propertyValue) : "".concat(propertyValue, "px");
|
|
475
|
+
} else {
|
|
476
|
+
lookupValue = typeof propertyValue === 'string' ? propertyValue : "".concat(propertyValue, "px");
|
|
477
|
+
}
|
|
473
478
|
return lookupValue;
|
|
474
479
|
}
|
|
475
480
|
export function findTokenNameByPropertyValue(propertyName, value) {
|
|
476
481
|
var lookupValue = normaliseValue(propertyName, value);
|
|
477
|
-
var tokenName = isShapeProperty(propertyName) ? isBorderSizeProperty(propertyName) ? borderWidthValueToToken[lookupValue] : radiusValueToToken[lookupValue] :
|
|
482
|
+
var tokenName = isShapeProperty(propertyName) ? isBorderSizeProperty(propertyName) ? borderWidthValueToToken[lookupValue] : radiusValueToToken[lookupValue] : spacingValueToToken[lookupValue];
|
|
478
483
|
if (!tokenName) {
|
|
479
484
|
return undefined;
|
|
480
485
|
}
|
|
@@ -483,9 +488,9 @@ export function findTokenNameByPropertyValue(propertyName, value) {
|
|
|
483
488
|
|
|
484
489
|
/**
|
|
485
490
|
* Returns a stringifiable node with the token expression corresponding to its matching token.
|
|
486
|
-
*
|
|
487
|
-
* @param propertyName string camelCased css property
|
|
488
|
-
* @param value
|
|
491
|
+
* If no token found for the pair the function returns undefined.
|
|
492
|
+
* @param propertyName string camelCased css property.
|
|
493
|
+
* @param value The computed value e.g '8px' -> '8'.
|
|
489
494
|
*/
|
|
490
495
|
export function getTokenReplacement(propertyName, value) {
|
|
491
496
|
var tokenName = findTokenNameByPropertyValue(propertyName, value);
|
|
@@ -495,17 +500,20 @@ export function getTokenReplacement(propertyName, value) {
|
|
|
495
500
|
var fallbackValue = normaliseValue(propertyName, value);
|
|
496
501
|
return getTokenNodeForValue(propertyName, fallbackValue);
|
|
497
502
|
}
|
|
498
|
-
export function
|
|
499
|
-
var
|
|
503
|
+
export function getPropertyNodeFromParent(property, parentNode) {
|
|
504
|
+
var propertyNode = parentNode.properties.find(function (node) {
|
|
500
505
|
if (!isNodeOfType(node, 'Property')) {
|
|
501
506
|
return;
|
|
502
507
|
}
|
|
503
508
|
if (!isNodeOfType(node.key, 'Identifier')) {
|
|
504
509
|
return;
|
|
505
510
|
}
|
|
506
|
-
return node.key.name ===
|
|
511
|
+
return node.key.name === property;
|
|
507
512
|
});
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
513
|
+
return propertyNode;
|
|
514
|
+
}
|
|
515
|
+
export function getValueForPropertyNode(propertyNode, context) {
|
|
516
|
+
var propertyValueRaw = isNodeOfType(propertyNode, 'Property') ? getValue(propertyNode.value, context) : null;
|
|
517
|
+
var propertyValue = Array.isArray(propertyValueRaw) ? propertyValueRaw[0] : propertyValueRaw;
|
|
518
|
+
return propertyValue;
|
|
511
519
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* THIS FILE WAS CREATED VIA CODEGEN DO NOT MODIFY {@see http://go/af-codegen}
|
|
3
|
-
* @codegen <<SignedSource::
|
|
3
|
+
* @codegen <<SignedSource::c283cd7ede5e813a9119cd707d339273>>
|
|
4
4
|
* @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
|
|
5
5
|
*/
|
|
6
6
|
import consistentCssPropUsage from './consistent-css-prop-usage';
|
|
@@ -32,6 +32,7 @@ import useHeadingLevelInSpotlightCard from './use-heading-level-in-spotlight-car
|
|
|
32
32
|
import useHrefInLinkItem from './use-href-in-link-item';
|
|
33
33
|
import usePrimitives from './use-primitives';
|
|
34
34
|
import usePrimitivesText from './use-primitives-text';
|
|
35
|
+
import useTokensTypography from './use-tokens-typography';
|
|
35
36
|
import useVisuallyHidden from './use-visually-hidden';
|
|
36
37
|
export default {
|
|
37
38
|
'consistent-css-prop-usage': consistentCssPropUsage,
|
|
@@ -63,5 +64,6 @@ export default {
|
|
|
63
64
|
'use-href-in-link-item': useHrefInLinkItem,
|
|
64
65
|
'use-primitives': usePrimitives,
|
|
65
66
|
'use-primitives-text': usePrimitivesText,
|
|
67
|
+
'use-tokens-typography': useTokensTypography,
|
|
66
68
|
'use-visually-hidden': useVisuallyHidden
|
|
67
69
|
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export var ruleSchema = {
|
|
2
|
+
type: 'array',
|
|
3
|
+
items: {
|
|
4
|
+
type: 'object',
|
|
5
|
+
properties: {
|
|
6
|
+
failSilently: {
|
|
7
|
+
type: 'boolean'
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
var defaultConfig = {
|
|
13
|
+
failSilently: false
|
|
14
|
+
};
|
|
15
|
+
export var getConfig = function getConfig(overrides) {
|
|
16
|
+
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign
|
|
17
|
+
// start with an empty object, then merge in the defaults, then merge in overrides.
|
|
18
|
+
// The empty object is returned, as well as modified in place
|
|
19
|
+
return Object.assign({}, defaultConfig, overrides);
|
|
20
|
+
};
|
|
@@ -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
|
+
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { createLintRule } from '../utils/create-rule';
|
|
2
|
+
import { getConfig, ruleSchema } from './config';
|
|
3
|
+
import { errorBoundary } from './error-boundary';
|
|
4
|
+
import { StyleObject } from './transformers/style-object';
|
|
5
|
+
var create = function create(context) {
|
|
6
|
+
var config = getConfig(context.options[0]);
|
|
7
|
+
return {
|
|
8
|
+
// const styles = css({ fontSize: '14px, ... }), styled.div({ fontSize: 14, ... })
|
|
9
|
+
ObjectExpression: function ObjectExpression(node) {
|
|
10
|
+
return errorBoundary(function () {
|
|
11
|
+
return StyleObject.lint(node, {
|
|
12
|
+
context: context
|
|
13
|
+
});
|
|
14
|
+
}, {
|
|
15
|
+
config: config
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
};
|
|
20
|
+
var rule = createLintRule({
|
|
21
|
+
meta: {
|
|
22
|
+
name: 'use-tokens-typography',
|
|
23
|
+
type: 'problem',
|
|
24
|
+
fixable: 'code',
|
|
25
|
+
hasSuggestions: true,
|
|
26
|
+
docs: {
|
|
27
|
+
description: 'Enforces usage of design tokens for typography properties rather than hard-coded values.',
|
|
28
|
+
recommended: false,
|
|
29
|
+
severity: 'warn'
|
|
30
|
+
},
|
|
31
|
+
messages: {
|
|
32
|
+
noRawTypographyValues: 'Typography primitives or tokens should be used instead of hard-coded values.\n\n@meta <<{{payload}}>>.'
|
|
33
|
+
},
|
|
34
|
+
schema: ruleSchema
|
|
35
|
+
},
|
|
36
|
+
create: create
|
|
37
|
+
});
|
|
38
|
+
export default rule;
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
/* eslint-disable @repo/internal/react/require-jsdoc */
|
|
2
|
+
|
|
3
|
+
import { isNodeOfType } from 'eslint-codemod-utils';
|
|
4
|
+
import { Object as ASTObject, ObjectEntry, Root } from '../../../ast-nodes';
|
|
5
|
+
import { getValueForPropertyNode, insertTokensImport, normaliseValue } from '../../ensure-design-token-usage/utils';
|
|
6
|
+
import { isDecendantOfGlobalToken, isDecendantOfStyleBlock, isDecendantOfType } from '../../utils/is-node';
|
|
7
|
+
import { convertPropertyNodeToStringableNode, defaultFontWeight, findFontFamilyValueForToken, findFontWeightTokenForValue, findTypographyTokenForValues, fontWeightMap, getLiteralProperty, getTokenProperty, isValidPropertyNode, notUndefined } from '../utils';
|
|
8
|
+
export var StyleObject = {
|
|
9
|
+
lint: function lint(node, _ref) {
|
|
10
|
+
var context = _ref.context;
|
|
11
|
+
// To force the correct node type
|
|
12
|
+
if (!isNodeOfType(node, 'ObjectExpression')) {
|
|
13
|
+
return {
|
|
14
|
+
success: false
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Check whether all criteria needed to make a transformation are met
|
|
19
|
+
var _StyleObject$_check = StyleObject._check(node, {
|
|
20
|
+
context: context
|
|
21
|
+
}),
|
|
22
|
+
success = _StyleObject$_check.success,
|
|
23
|
+
refs = _StyleObject$_check.refs;
|
|
24
|
+
if (!success || !refs) {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
var fontSizeNode = refs.fontSizeNode,
|
|
28
|
+
fontSizeRaw = refs.fontSizeRaw,
|
|
29
|
+
tokensImportNode = refs.tokensImportNode;
|
|
30
|
+
var fontSizeValue = normaliseValue('fontSize', fontSizeRaw);
|
|
31
|
+
|
|
32
|
+
// -- Font weight --
|
|
33
|
+
var fontWeightNode = ASTObject.getEntryByPropertyName(node, 'fontWeight');
|
|
34
|
+
var fontWeightRaw = fontWeightNode && getValueForPropertyNode(fontWeightNode, context);
|
|
35
|
+
|
|
36
|
+
// If no fontWeight value exists, default to 400 to avoid matching with a bolder token resulting in a visual change
|
|
37
|
+
var fontWeightValue = fontWeightRaw && normaliseValue('fontWeight', fontWeightRaw) || defaultFontWeight;
|
|
38
|
+
fontWeightValue = fontWeightValue.length === 3 ? fontWeightValue : fontWeightMap[fontWeightValue] || defaultFontWeight;
|
|
39
|
+
|
|
40
|
+
// -- Line height --
|
|
41
|
+
var lineHeightNode = ASTObject.getEntryByPropertyName(node, 'lineHeight');
|
|
42
|
+
var lineHeightRaw = lineHeightNode && getValueForPropertyNode(lineHeightNode, context);
|
|
43
|
+
var shouldAddFontWeight = false;
|
|
44
|
+
var lineHeightValue = lineHeightRaw && normaliseValue('lineHeight', lineHeightRaw) || undefined;
|
|
45
|
+
if (lineHeightValue === fontSizeValue) {
|
|
46
|
+
lineHeightValue = '1';
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// -- Match tokens --
|
|
50
|
+
var matchingTokens = findTypographyTokenForValues(fontSizeValue, lineHeightValue);
|
|
51
|
+
if (matchingTokens.length) {
|
|
52
|
+
// If we have multiple matching tokens, try matching fontWeight
|
|
53
|
+
var matchingTokensWithWeight = matchingTokens.filter(function (token) {
|
|
54
|
+
return fontWeightValue ? token.values.fontWeight === fontWeightValue : token;
|
|
55
|
+
});
|
|
56
|
+
if (matchingTokensWithWeight.length) {
|
|
57
|
+
// Possibly narrowed down tokens
|
|
58
|
+
matchingTokens = matchingTokensWithWeight;
|
|
59
|
+
} else {
|
|
60
|
+
// Ended up with 0 matches by matching fontWeight
|
|
61
|
+
// return body token and add fontWeight manually
|
|
62
|
+
matchingTokens = matchingTokens.filter(function (token) {
|
|
63
|
+
return token.tokenName.includes('.body');
|
|
64
|
+
});
|
|
65
|
+
shouldAddFontWeight = true;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Get other font-* nodes that we can replace/remove.
|
|
70
|
+
// These aren't needed for token matching.
|
|
71
|
+
|
|
72
|
+
// -- Font family --
|
|
73
|
+
var fontFamilyNode = ASTObject.getEntryByPropertyName(node, 'fontFamily');
|
|
74
|
+
var fontFamilyRaw = fontFamilyNode && getValueForPropertyNode(fontFamilyNode, context);
|
|
75
|
+
var fontFamilyValue = fontFamilyRaw && normaliseValue('fontFamily', fontFamilyRaw) || undefined;
|
|
76
|
+
var fontFamilyToAdd;
|
|
77
|
+
// If font family uses the Charlie font we can't replace; exit
|
|
78
|
+
if (fontFamilyValue) {
|
|
79
|
+
if (fontFamilyValue.toLowerCase().includes('charlie display')) {
|
|
80
|
+
fontFamilyToAdd = 'heading';
|
|
81
|
+
} else if (fontFamilyValue.toLowerCase().includes('charlie text')) {
|
|
82
|
+
fontFamilyToAdd = 'body';
|
|
83
|
+
}
|
|
84
|
+
} else {
|
|
85
|
+
// Font family node exists but we can't resolve its value
|
|
86
|
+
// Will need to re-add it below the font property to ensure it still applies
|
|
87
|
+
fontFamilyToAdd = fontFamilyNode ? 'original' : undefined;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// -- Font style --
|
|
91
|
+
var fontStyleNode = ASTObject.getEntryByPropertyName(node, 'fontStyle');
|
|
92
|
+
var fontStyleRaw = fontStyleNode && getValueForPropertyNode(fontStyleNode, context);
|
|
93
|
+
var fontStyleValue = fontStyleRaw && normaliseValue('fontStyle', fontStyleRaw) || undefined;
|
|
94
|
+
var fontStyleToAdd;
|
|
95
|
+
if (fontStyleValue === 'italic') {
|
|
96
|
+
fontStyleToAdd = 'italic';
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// -- Letter spacing --
|
|
100
|
+
var letterSpacingNode = ASTObject.getEntryByPropertyName(node, 'letterSpacing');
|
|
101
|
+
|
|
102
|
+
// A single matching token
|
|
103
|
+
// TOOD: Maybe suggest options if > 1 matching token
|
|
104
|
+
if (matchingTokens.length === 1) {
|
|
105
|
+
var matchingToken = matchingTokens[0];
|
|
106
|
+
|
|
107
|
+
// fontSize node is always first
|
|
108
|
+
var nodesToReplace = [fontSizeNode, fontWeightNode, lineHeightNode, fontFamilyNode, fontStyleNode, letterSpacingNode].filter(notUndefined);
|
|
109
|
+
var fontFamilyTokenName = fontFamilyToAdd ? "font.family.brand.".concat(fontFamilyToAdd) : '';
|
|
110
|
+
var fontWeightReplacementToken = shouldAddFontWeight ? findFontWeightTokenForValue(fontWeightValue) : undefined;
|
|
111
|
+
var fontWeightReplacement = fontWeightReplacementToken && getTokenProperty('fontWeight', fontWeightReplacementToken.tokenName, fontWeightValue);
|
|
112
|
+
var fontFamilyReplacement = fontFamilyToAdd && (fontFamilyToAdd === 'original' ? convertPropertyNodeToStringableNode(
|
|
113
|
+
// This will always exist if fontFamilyToAdd === 'original', TS can't figure that out.
|
|
114
|
+
fontFamilyNode) : getTokenProperty('fontFamily', fontFamilyTokenName, findFontFamilyValueForToken(fontFamilyTokenName)));
|
|
115
|
+
var fontStyleReplacement = fontStyleToAdd && getLiteralProperty('fontStyle', fontStyleToAdd);
|
|
116
|
+
var fixerRefs = {
|
|
117
|
+
matchingToken: matchingToken,
|
|
118
|
+
nodesToReplace: nodesToReplace,
|
|
119
|
+
tokensImportNode: tokensImportNode,
|
|
120
|
+
fontWeightReplacement: fontWeightReplacement,
|
|
121
|
+
fontFamilyReplacement: fontFamilyReplacement,
|
|
122
|
+
fontStyleReplacement: fontStyleReplacement
|
|
123
|
+
};
|
|
124
|
+
context.report({
|
|
125
|
+
node: fontSizeNode,
|
|
126
|
+
messageId: 'noRawTypographyValues',
|
|
127
|
+
data: {
|
|
128
|
+
payload: "fontSize:".concat(fontSizeRaw)
|
|
129
|
+
},
|
|
130
|
+
fix: StyleObject._fix(fixerRefs, context)
|
|
131
|
+
});
|
|
132
|
+
} else if (!matchingTokens.length) {
|
|
133
|
+
context.report({
|
|
134
|
+
node: fontSizeNode,
|
|
135
|
+
messageId: 'noRawTypographyValues',
|
|
136
|
+
data: {
|
|
137
|
+
payload: "fontSize:".concat(fontSizeRaw)
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
return;
|
|
142
|
+
},
|
|
143
|
+
_check: function _check(node, _ref2) {
|
|
144
|
+
var context = _ref2.context;
|
|
145
|
+
if (!isDecendantOfStyleBlock(node) && !isDecendantOfType(node, 'JSXExpressionContainer')) {
|
|
146
|
+
return {
|
|
147
|
+
success: false
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// -- Font size --
|
|
152
|
+
var fontSizeNode = ASTObject.getEntryByPropertyName(node, 'fontSize');
|
|
153
|
+
if (!fontSizeNode || !isValidPropertyNode(fontSizeNode) || isDecendantOfGlobalToken(fontSizeNode.value)) {
|
|
154
|
+
return {
|
|
155
|
+
success: false
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
var fontSizeRaw = getValueForPropertyNode(fontSizeNode, context);
|
|
159
|
+
|
|
160
|
+
// Without a valid fontSize value we can't be certain what token should be used; exit
|
|
161
|
+
if (!fontSizeRaw) {
|
|
162
|
+
return {
|
|
163
|
+
success: false
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
var importDeclaration = Root.findImportsByModule(context.getSourceCode().ast.body, '@atlaskit/tokens');
|
|
167
|
+
|
|
168
|
+
// If there is more than one `@atlaskit/tokens` import, then it becomes difficult to determine which import to transform
|
|
169
|
+
if (importDeclaration.length > 1) {
|
|
170
|
+
return {
|
|
171
|
+
success: false
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
return {
|
|
175
|
+
success: true,
|
|
176
|
+
refs: {
|
|
177
|
+
fontSizeNode: fontSizeNode,
|
|
178
|
+
fontSizeRaw: fontSizeRaw,
|
|
179
|
+
tokensImportNode: importDeclaration[0]
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
},
|
|
183
|
+
_fix: function _fix(refs, context) {
|
|
184
|
+
return function (fixer) {
|
|
185
|
+
var matchingToken = refs.matchingToken,
|
|
186
|
+
nodesToReplace = refs.nodesToReplace,
|
|
187
|
+
tokensImportNode = refs.tokensImportNode,
|
|
188
|
+
fontWeightReplacement = refs.fontWeightReplacement,
|
|
189
|
+
fontFamilyReplacement = refs.fontFamilyReplacement,
|
|
190
|
+
fontStyleReplacement = refs.fontStyleReplacement;
|
|
191
|
+
var fontSizeNode = nodesToReplace[0];
|
|
192
|
+
return (!tokensImportNode ? [insertTokensImport(fixer)] : []).concat(nodesToReplace.map(function (node, index) {
|
|
193
|
+
// Replace first node with token, delete remaining nodes. Guaranteed to be fontSize
|
|
194
|
+
if (index === 0) {
|
|
195
|
+
return fixer.replaceText(node, "".concat(getTokenProperty('font', matchingToken.tokenName, matchingToken.tokenValue)));
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// We don't replace fontWeight/fontFamily/fontStyle here in case it occurs before the font property.
|
|
199
|
+
// Instead delete the original property and add below
|
|
200
|
+
return ObjectEntry.deleteEntry(node, context, fixer);
|
|
201
|
+
}),
|
|
202
|
+
// Make sure font weight/family/style properties are added AFTER font property to ensure they override corectly
|
|
203
|
+
fontWeightReplacement ? [fixer.insertTextAfter(fontSizeNode, ",\n".concat(fontWeightReplacement))] : [], fontFamilyReplacement ? [fixer.insertTextAfter(fontSizeNode, ",\n".concat(fontFamilyReplacement))] : [], fontStyleReplacement ? [fixer.insertTextAfter(fontSizeNode, ",\n".concat(fontStyleReplacement))] : []);
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
};
|