@atlaskit/eslint-plugin-design-system 8.33.0 → 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 +6 -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/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/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/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
|
@@ -5,7 +5,6 @@ import { spacing as spacingScale } from '@atlaskit/tokens/tokens-raw';
|
|
|
5
5
|
import { findIdentifierInParentScope } from '../utils/find-in-parent';
|
|
6
6
|
import { isColorCssPropertyName, isCurrentSurfaceCustomPropertyName } from '../utils/is-color';
|
|
7
7
|
import { borderWidthValueToToken, isBorderRadius, isBorderSizeProperty, isShapeProperty, radiusValueToToken } from './shape';
|
|
8
|
-
import { isCodeFontFamily, isFontFamily, isFontSize, isFontSizeSmall, isTypographyProperty, typographyValueToToken } from './typography';
|
|
9
8
|
const 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'];
|
|
10
9
|
const spacingValueToToken = Object.fromEntries(spacingScale.map(token => [token.value, token.cleanName]));
|
|
11
10
|
export function insertTokensImport(fixer) {
|
|
@@ -31,8 +30,10 @@ export const splitShorthandValues = str => {
|
|
|
31
30
|
};
|
|
32
31
|
export const getValueFromShorthand = str => {
|
|
33
32
|
const valueString = String(str);
|
|
34
|
-
const fontFamily = /(sans-serif$)|(monospace$)/;
|
|
35
|
-
|
|
33
|
+
const fontFamily = /(Charlie)|(sans-serif$)|(monospace$)/;
|
|
34
|
+
const fontWeightString = /(regular$)|(medium$)|(semibold$)|(bold$)/;
|
|
35
|
+
const fontStyleString = /(inherit$)|(normal$)|(italic$)/;
|
|
36
|
+
if (fontFamily.test(valueString) || fontWeightString.test(valueString) || fontStyleString.test(valueString)) {
|
|
36
37
|
return [valueString];
|
|
37
38
|
}
|
|
38
39
|
// If we want to filter out NaN just add .filter(Boolean)
|
|
@@ -57,6 +58,8 @@ const getRawExpressionForToken = (node, context) => {
|
|
|
57
58
|
}).join(', ')})}`;
|
|
58
59
|
return call;
|
|
59
60
|
};
|
|
61
|
+
const isFontSize = node => isNodeOfType(node, 'CallExpression') && isNodeOfType(node.callee, 'Identifier') && (node.callee.name === 'fontSize' || node.callee.name === 'getFontSize');
|
|
62
|
+
const isFontSizeSmall = node => isNodeOfType(node, 'CallExpression') && isNodeOfType(node.callee, 'Identifier') && node.callee.name === 'fontSizeSmall';
|
|
60
63
|
const getValueFromCallExpression = (node, context) => {
|
|
61
64
|
if (!isNodeOfType(node, 'CallExpression')) {
|
|
62
65
|
return null;
|
|
@@ -73,12 +76,6 @@ const getValueFromCallExpression = (node, context) => {
|
|
|
73
76
|
if (isFontSizeSmall(node)) {
|
|
74
77
|
return 11;
|
|
75
78
|
}
|
|
76
|
-
if (isFontFamily(node)) {
|
|
77
|
-
return `-apple-system, BlinkMacSystemFont, \'Segoe UI\', \'Roboto\', \'Oxygen\', \'Ubuntu\', \'Fira Sans\', \'Droid Sans\', \'Helvetica Neue\', sans-serif`;
|
|
78
|
-
}
|
|
79
|
-
if (isCodeFontFamily(node)) {
|
|
80
|
-
return `\'SFMono-Medium\', \'SF Mono\', \'Segoe UI Mono\', \'Roboto Mono\', \'Ubuntu Mono\', Menlo, Consolas, Courier, monospace`;
|
|
81
|
-
}
|
|
82
79
|
if (isToken(node)) {
|
|
83
80
|
return getRawExpressionForToken(node, context);
|
|
84
81
|
}
|
|
@@ -281,7 +278,7 @@ export const convertHyphenatedNameToCamelCase = prop => {
|
|
|
281
278
|
|
|
282
279
|
/**
|
|
283
280
|
* @param node
|
|
284
|
-
* @returns The furthest parent node that is on the same line as the input node
|
|
281
|
+
* @returns The furthest parent node that is on the same line as the input node.
|
|
285
282
|
*/
|
|
286
283
|
export const findParentNodeForLine = node => {
|
|
287
284
|
var _node$loc, _node$parent$loc;
|
|
@@ -296,14 +293,13 @@ export const findParentNodeForLine = node => {
|
|
|
296
293
|
};
|
|
297
294
|
|
|
298
295
|
/**
|
|
299
|
-
* Returns an array of domains that are relevant to the provided property based on the rule options
|
|
296
|
+
* Returns an array of domains that are relevant to the provided property based on the rule options.
|
|
300
297
|
* @param propertyName camelCase CSS property
|
|
301
|
-
* @param targetOptions Array containing the types of properties that should be included in the rule
|
|
298
|
+
* @param targetOptions Array containing the types of properties that should be included in the rule.
|
|
302
299
|
* @example
|
|
303
300
|
* ```
|
|
304
301
|
* propertyName: padding, targetOptions: ['spacing'] -> returns ['spacing']
|
|
305
|
-
* propertyName:
|
|
306
|
-
* propertyName: backgroundColor, targetOptions: ['spacing', 'typography'] -> returns []
|
|
302
|
+
* propertyName: backgroundColor, targetOptions: ['spacing'] -> returns []
|
|
307
303
|
* propertyName: backgroundColor, targetOptions: ['color', 'spacing'] -> returns ['color']
|
|
308
304
|
* ```
|
|
309
305
|
*/
|
|
@@ -318,15 +314,12 @@ export function getDomainsForProperty(propertyName, targetOptions) {
|
|
|
318
314
|
if (isShapeProperty(propertyName) && targetOptions.includes('shape')) {
|
|
319
315
|
domains.push('shape');
|
|
320
316
|
}
|
|
321
|
-
if (isTypographyProperty(propertyName) && targetOptions.includes('typography')) {
|
|
322
|
-
domains.push('typography');
|
|
323
|
-
}
|
|
324
317
|
return domains;
|
|
325
318
|
}
|
|
326
319
|
|
|
327
320
|
/**
|
|
328
321
|
* Function that removes JS comments from a string of code,
|
|
329
|
-
* sometimes makers will have single or multiline comments in their tagged template literals styles, this can mess with our parsing logic
|
|
322
|
+
* sometimes makers will have single or multiline comments in their tagged template literals styles, this can mess with our parsing logic.
|
|
330
323
|
*/
|
|
331
324
|
export function cleanComments(str) {
|
|
332
325
|
return str.replace(/\/\*([\s\S]*?)\*\//g, '').replace(/\/\/(.*)/g, '');
|
|
@@ -334,11 +327,11 @@ export function cleanComments(str) {
|
|
|
334
327
|
|
|
335
328
|
/**
|
|
336
329
|
* Returns an array of tuples representing a processed css within `TaggedTemplateExpression` node.
|
|
337
|
-
*
|
|
330
|
+
* Each element of the array is a tuple `[string, string]`,
|
|
338
331
|
* where the first element is the processed css line with computed values
|
|
339
|
-
* and the second element of the tuple is the original css line from source
|
|
340
|
-
* @param node TaggedTemplateExpression node
|
|
341
|
-
* @param context Rule.RuleContext
|
|
332
|
+
* and the second element of the tuple is the original css line from source.
|
|
333
|
+
* @param node TaggedTemplateExpression node.
|
|
334
|
+
* @param context Rule.RuleContext.
|
|
342
335
|
* @example
|
|
343
336
|
* ```
|
|
344
337
|
* `[['padding: 8', 'padding: ${gridSize()}'], ['margin: 6', 'margin: 6px' ]]`
|
|
@@ -403,7 +396,7 @@ export function getFontSizeValueInScope(cssProperties) {
|
|
|
403
396
|
|
|
404
397
|
/**
|
|
405
398
|
* Attempts to remove all non-essential words & characters from a style block.
|
|
406
|
-
* Including selectors and queries
|
|
399
|
+
* Including selectors and queries.
|
|
407
400
|
* @param styleString string of css properties
|
|
408
401
|
*/
|
|
409
402
|
export function splitCssProperties(styleString) {
|
|
@@ -418,8 +411,8 @@ export function splitCssProperties(styleString) {
|
|
|
418
411
|
}
|
|
419
412
|
|
|
420
413
|
/**
|
|
421
|
-
*
|
|
422
|
-
* @param originalVaue string representing a css property value e.g 1em, 12px
|
|
414
|
+
* Returns whether the current string is a token value.
|
|
415
|
+
* @param originalVaue string representing a css property value e.g 1em, 12px.
|
|
423
416
|
*/
|
|
424
417
|
export function isTokenValueString(originalValue) {
|
|
425
418
|
return originalValue.startsWith('${token(') && originalValue.endsWith('}');
|
|
@@ -433,19 +426,27 @@ export function includesTokenString(originalValue) {
|
|
|
433
426
|
*
|
|
434
427
|
* -> for pixels this '8px'
|
|
435
428
|
* -> for weights '400'
|
|
436
|
-
* -> for family 'Arial'
|
|
429
|
+
* -> for family 'Arial'.
|
|
437
430
|
*
|
|
438
431
|
* @internal
|
|
439
432
|
*/
|
|
440
433
|
export function normaliseValue(propertyName, value) {
|
|
441
|
-
const
|
|
434
|
+
const isFontStringProperty = /fontWeight|fontFamily|fontStyle/.test(propertyName);
|
|
435
|
+
const isLineHeight = /lineHeight/.test(propertyName);
|
|
442
436
|
const propertyValue = typeof value === 'string' ? value.trim() : value;
|
|
443
|
-
|
|
437
|
+
let lookupValue;
|
|
438
|
+
if (isFontStringProperty) {
|
|
439
|
+
lookupValue = `${propertyValue}`;
|
|
440
|
+
} else if (isLineHeight) {
|
|
441
|
+
lookupValue = value === 1 ? `${propertyValue}` : `${propertyValue}px`;
|
|
442
|
+
} else {
|
|
443
|
+
lookupValue = typeof propertyValue === 'string' ? propertyValue : `${propertyValue}px`;
|
|
444
|
+
}
|
|
444
445
|
return lookupValue;
|
|
445
446
|
}
|
|
446
447
|
export function findTokenNameByPropertyValue(propertyName, value) {
|
|
447
448
|
const lookupValue = normaliseValue(propertyName, value);
|
|
448
|
-
const tokenName = isShapeProperty(propertyName) ? isBorderSizeProperty(propertyName) ? borderWidthValueToToken[lookupValue] : radiusValueToToken[lookupValue] :
|
|
449
|
+
const tokenName = isShapeProperty(propertyName) ? isBorderSizeProperty(propertyName) ? borderWidthValueToToken[lookupValue] : radiusValueToToken[lookupValue] : spacingValueToToken[lookupValue];
|
|
449
450
|
if (!tokenName) {
|
|
450
451
|
return undefined;
|
|
451
452
|
}
|
|
@@ -454,9 +455,9 @@ export function findTokenNameByPropertyValue(propertyName, value) {
|
|
|
454
455
|
|
|
455
456
|
/**
|
|
456
457
|
* Returns a stringifiable node with the token expression corresponding to its matching token.
|
|
457
|
-
*
|
|
458
|
-
* @param propertyName string camelCased css property
|
|
459
|
-
* @param value
|
|
458
|
+
* If no token found for the pair the function returns undefined.
|
|
459
|
+
* @param propertyName string camelCased css property.
|
|
460
|
+
* @param value The computed value e.g '8px' -> '8'.
|
|
460
461
|
*/
|
|
461
462
|
export function getTokenReplacement(propertyName, value) {
|
|
462
463
|
const tokenName = findTokenNameByPropertyValue(propertyName, value);
|
|
@@ -466,17 +467,20 @@ export function getTokenReplacement(propertyName, value) {
|
|
|
466
467
|
const fallbackValue = normaliseValue(propertyName, value);
|
|
467
468
|
return getTokenNodeForValue(propertyName, fallbackValue);
|
|
468
469
|
}
|
|
469
|
-
export function
|
|
470
|
-
const
|
|
470
|
+
export function getPropertyNodeFromParent(property, parentNode) {
|
|
471
|
+
const propertyNode = parentNode.properties.find(node => {
|
|
471
472
|
if (!isNodeOfType(node, 'Property')) {
|
|
472
473
|
return;
|
|
473
474
|
}
|
|
474
475
|
if (!isNodeOfType(node.key, 'Identifier')) {
|
|
475
476
|
return;
|
|
476
477
|
}
|
|
477
|
-
return node.key.name ===
|
|
478
|
+
return node.key.name === property;
|
|
478
479
|
});
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
480
|
+
return propertyNode;
|
|
481
|
+
}
|
|
482
|
+
export function getValueForPropertyNode(propertyNode, context) {
|
|
483
|
+
const propertyValueRaw = isNodeOfType(propertyNode, 'Property') ? getValue(propertyNode.value, context) : null;
|
|
484
|
+
const propertyValue = Array.isArray(propertyValueRaw) ? propertyValueRaw[0] : propertyValueRaw;
|
|
485
|
+
return propertyValue;
|
|
482
486
|
}
|
|
@@ -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 const ruleSchema = {
|
|
2
|
+
type: 'array',
|
|
3
|
+
items: {
|
|
4
|
+
type: 'object',
|
|
5
|
+
properties: {
|
|
6
|
+
failSilently: {
|
|
7
|
+
type: 'boolean'
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
};
|
|
12
|
+
const defaultConfig = {
|
|
13
|
+
failSilently: false
|
|
14
|
+
};
|
|
15
|
+
export const 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,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
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
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
|
+
const create = context => {
|
|
6
|
+
const config = getConfig(context.options[0]);
|
|
7
|
+
return {
|
|
8
|
+
// const styles = css({ fontSize: '14px, ... }), styled.div({ fontSize: 14, ... })
|
|
9
|
+
ObjectExpression: node => errorBoundary(() => {
|
|
10
|
+
return StyleObject.lint(node, {
|
|
11
|
+
context
|
|
12
|
+
});
|
|
13
|
+
}, {
|
|
14
|
+
config
|
|
15
|
+
})
|
|
16
|
+
};
|
|
17
|
+
};
|
|
18
|
+
const rule = createLintRule({
|
|
19
|
+
meta: {
|
|
20
|
+
name: 'use-tokens-typography',
|
|
21
|
+
type: 'problem',
|
|
22
|
+
fixable: 'code',
|
|
23
|
+
hasSuggestions: true,
|
|
24
|
+
docs: {
|
|
25
|
+
description: 'Enforces usage of design tokens for typography properties rather than hard-coded values.',
|
|
26
|
+
recommended: false,
|
|
27
|
+
severity: 'warn'
|
|
28
|
+
},
|
|
29
|
+
messages: {
|
|
30
|
+
noRawTypographyValues: 'Typography primitives or tokens should be used instead of hard-coded values.\n\n@meta <<{{payload}}>>.'
|
|
31
|
+
},
|
|
32
|
+
schema: ruleSchema
|
|
33
|
+
},
|
|
34
|
+
create
|
|
35
|
+
});
|
|
36
|
+
export default rule;
|
|
@@ -0,0 +1,209 @@
|
|
|
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 const StyleObject = {
|
|
9
|
+
lint(node, {
|
|
10
|
+
context
|
|
11
|
+
}) {
|
|
12
|
+
// To force the correct node type
|
|
13
|
+
if (!isNodeOfType(node, 'ObjectExpression')) {
|
|
14
|
+
return {
|
|
15
|
+
success: false
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Check whether all criteria needed to make a transformation are met
|
|
20
|
+
const {
|
|
21
|
+
success,
|
|
22
|
+
refs
|
|
23
|
+
} = StyleObject._check(node, {
|
|
24
|
+
context
|
|
25
|
+
});
|
|
26
|
+
if (!success || !refs) {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
const {
|
|
30
|
+
fontSizeNode,
|
|
31
|
+
fontSizeRaw,
|
|
32
|
+
tokensImportNode
|
|
33
|
+
} = refs;
|
|
34
|
+
const fontSizeValue = normaliseValue('fontSize', fontSizeRaw);
|
|
35
|
+
|
|
36
|
+
// -- Font weight --
|
|
37
|
+
const fontWeightNode = ASTObject.getEntryByPropertyName(node, 'fontWeight');
|
|
38
|
+
const fontWeightRaw = fontWeightNode && getValueForPropertyNode(fontWeightNode, context);
|
|
39
|
+
|
|
40
|
+
// If no fontWeight value exists, default to 400 to avoid matching with a bolder token resulting in a visual change
|
|
41
|
+
let fontWeightValue = fontWeightRaw && normaliseValue('fontWeight', fontWeightRaw) || defaultFontWeight;
|
|
42
|
+
fontWeightValue = fontWeightValue.length === 3 ? fontWeightValue : fontWeightMap[fontWeightValue] || defaultFontWeight;
|
|
43
|
+
|
|
44
|
+
// -- Line height --
|
|
45
|
+
const lineHeightNode = ASTObject.getEntryByPropertyName(node, 'lineHeight');
|
|
46
|
+
const lineHeightRaw = lineHeightNode && getValueForPropertyNode(lineHeightNode, context);
|
|
47
|
+
let shouldAddFontWeight = false;
|
|
48
|
+
let lineHeightValue = lineHeightRaw && normaliseValue('lineHeight', lineHeightRaw) || undefined;
|
|
49
|
+
if (lineHeightValue === fontSizeValue) {
|
|
50
|
+
lineHeightValue = '1';
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// -- Match tokens --
|
|
54
|
+
let matchingTokens = findTypographyTokenForValues(fontSizeValue, lineHeightValue);
|
|
55
|
+
if (matchingTokens.length) {
|
|
56
|
+
// If we have multiple matching tokens, try matching fontWeight
|
|
57
|
+
let matchingTokensWithWeight = matchingTokens.filter(token => fontWeightValue ? token.values.fontWeight === fontWeightValue : token);
|
|
58
|
+
if (matchingTokensWithWeight.length) {
|
|
59
|
+
// Possibly narrowed down tokens
|
|
60
|
+
matchingTokens = matchingTokensWithWeight;
|
|
61
|
+
} else {
|
|
62
|
+
// Ended up with 0 matches by matching fontWeight
|
|
63
|
+
// return body token and add fontWeight manually
|
|
64
|
+
matchingTokens = matchingTokens.filter(token => token.tokenName.includes('.body'));
|
|
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
|
+
const fontFamilyNode = ASTObject.getEntryByPropertyName(node, 'fontFamily');
|
|
74
|
+
const fontFamilyRaw = fontFamilyNode && getValueForPropertyNode(fontFamilyNode, context);
|
|
75
|
+
const fontFamilyValue = fontFamilyRaw && normaliseValue('fontFamily', fontFamilyRaw) || undefined;
|
|
76
|
+
let 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
|
+
const fontStyleNode = ASTObject.getEntryByPropertyName(node, 'fontStyle');
|
|
92
|
+
const fontStyleRaw = fontStyleNode && getValueForPropertyNode(fontStyleNode, context);
|
|
93
|
+
const fontStyleValue = fontStyleRaw && normaliseValue('fontStyle', fontStyleRaw) || undefined;
|
|
94
|
+
let fontStyleToAdd;
|
|
95
|
+
if (fontStyleValue === 'italic') {
|
|
96
|
+
fontStyleToAdd = 'italic';
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// -- Letter spacing --
|
|
100
|
+
const 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
|
+
const matchingToken = matchingTokens[0];
|
|
106
|
+
|
|
107
|
+
// fontSize node is always first
|
|
108
|
+
const nodesToReplace = [fontSizeNode, fontWeightNode, lineHeightNode, fontFamilyNode, fontStyleNode, letterSpacingNode].filter(notUndefined);
|
|
109
|
+
const fontFamilyTokenName = fontFamilyToAdd ? `font.family.brand.${fontFamilyToAdd}` : '';
|
|
110
|
+
const fontWeightReplacementToken = shouldAddFontWeight ? findFontWeightTokenForValue(fontWeightValue) : undefined;
|
|
111
|
+
const fontWeightReplacement = fontWeightReplacementToken && getTokenProperty('fontWeight', fontWeightReplacementToken.tokenName, fontWeightValue);
|
|
112
|
+
const 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
|
+
const fontStyleReplacement = fontStyleToAdd && getLiteralProperty('fontStyle', fontStyleToAdd);
|
|
116
|
+
const fixerRefs = {
|
|
117
|
+
matchingToken,
|
|
118
|
+
nodesToReplace,
|
|
119
|
+
tokensImportNode,
|
|
120
|
+
fontWeightReplacement,
|
|
121
|
+
fontFamilyReplacement,
|
|
122
|
+
fontStyleReplacement
|
|
123
|
+
};
|
|
124
|
+
context.report({
|
|
125
|
+
node: fontSizeNode,
|
|
126
|
+
messageId: 'noRawTypographyValues',
|
|
127
|
+
data: {
|
|
128
|
+
payload: `fontSize:${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:${fontSizeRaw}`
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
return;
|
|
142
|
+
},
|
|
143
|
+
_check(node, {
|
|
144
|
+
context
|
|
145
|
+
}) {
|
|
146
|
+
if (!isDecendantOfStyleBlock(node) && !isDecendantOfType(node, 'JSXExpressionContainer')) {
|
|
147
|
+
return {
|
|
148
|
+
success: false
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// -- Font size --
|
|
153
|
+
const fontSizeNode = ASTObject.getEntryByPropertyName(node, 'fontSize');
|
|
154
|
+
if (!fontSizeNode || !isValidPropertyNode(fontSizeNode) || isDecendantOfGlobalToken(fontSizeNode.value)) {
|
|
155
|
+
return {
|
|
156
|
+
success: false
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
const fontSizeRaw = getValueForPropertyNode(fontSizeNode, context);
|
|
160
|
+
|
|
161
|
+
// Without a valid fontSize value we can't be certain what token should be used; exit
|
|
162
|
+
if (!fontSizeRaw) {
|
|
163
|
+
return {
|
|
164
|
+
success: false
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
const importDeclaration = Root.findImportsByModule(context.getSourceCode().ast.body, '@atlaskit/tokens');
|
|
168
|
+
|
|
169
|
+
// If there is more than one `@atlaskit/tokens` import, then it becomes difficult to determine which import to transform
|
|
170
|
+
if (importDeclaration.length > 1) {
|
|
171
|
+
return {
|
|
172
|
+
success: false
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
return {
|
|
176
|
+
success: true,
|
|
177
|
+
refs: {
|
|
178
|
+
fontSizeNode,
|
|
179
|
+
fontSizeRaw,
|
|
180
|
+
tokensImportNode: importDeclaration[0]
|
|
181
|
+
}
|
|
182
|
+
};
|
|
183
|
+
},
|
|
184
|
+
_fix(refs, context) {
|
|
185
|
+
return fixer => {
|
|
186
|
+
const {
|
|
187
|
+
matchingToken,
|
|
188
|
+
nodesToReplace,
|
|
189
|
+
tokensImportNode,
|
|
190
|
+
fontWeightReplacement,
|
|
191
|
+
fontFamilyReplacement,
|
|
192
|
+
fontStyleReplacement
|
|
193
|
+
} = refs;
|
|
194
|
+
const fontSizeNode = nodesToReplace[0];
|
|
195
|
+
return (!tokensImportNode ? [insertTokensImport(fixer)] : []).concat(nodesToReplace.map((node, index) => {
|
|
196
|
+
// Replace first node with token, delete remaining nodes. Guaranteed to be fontSize
|
|
197
|
+
if (index === 0) {
|
|
198
|
+
return fixer.replaceText(node, `${getTokenProperty('font', matchingToken.tokenName, matchingToken.tokenValue)}`);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// We don't replace fontWeight/fontFamily/fontStyle here in case it occurs before the font property.
|
|
202
|
+
// Instead delete the original property and add below
|
|
203
|
+
return ObjectEntry.deleteEntry(node, context, fixer);
|
|
204
|
+
}),
|
|
205
|
+
// Make sure font weight/family/style properties are added AFTER font property to ensure they override corectly
|
|
206
|
+
fontWeightReplacement ? [fixer.insertTextAfter(fontSizeNode, `,\n${fontWeightReplacement}`)] : [], fontFamilyReplacement ? [fixer.insertTextAfter(fontSizeNode, `,\n${fontFamilyReplacement}`)] : [], fontStyleReplacement ? [fixer.insertTextAfter(fontSizeNode, `,\n${fontStyleReplacement}`)] : []);
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
};
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { callExpression, identifier, isNodeOfType, literal, property } from 'eslint-codemod-utils';
|
|
2
|
+
import { typographyPalette } from '@atlaskit/tokens/palettes-raw';
|
|
3
|
+
import { typographyAdg3 as typographyTokens } from '@atlaskit/tokens/tokens-raw';
|
|
4
|
+
export const typographyProperties = ['fontSize', 'fontWeight', 'fontFamily', 'lineHeight'];
|
|
5
|
+
export const isTypographyProperty = propertyName => {
|
|
6
|
+
return typographyProperties.includes(propertyName);
|
|
7
|
+
};
|
|
8
|
+
export const isFontSize = node => isNodeOfType(node, 'CallExpression') && isNodeOfType(node.callee, 'Identifier') && (node.callee.name === 'fontSize' || node.callee.name === 'getFontSize');
|
|
9
|
+
export const isFontSizeSmall = node => isNodeOfType(node, 'CallExpression') && isNodeOfType(node.callee, 'Identifier') && node.callee.name === 'fontSizeSmall';
|
|
10
|
+
export const isFontFamily = node => isNodeOfType(node, 'CallExpression') && isNodeOfType(node.callee, 'Identifier') && (node.callee.name === 'fontFamily' || node.callee.name === 'getFontFamily');
|
|
11
|
+
export const isCodeFontFamily = node => isNodeOfType(node, 'CallExpression') && isNodeOfType(node.callee, 'Identifier') && (node.callee.name === 'codeFontFamily' || node.callee.name === 'getCodeFontFamily');
|
|
12
|
+
export const typographyValueToToken = typographyTokens
|
|
13
|
+
// we're filtering here to remove the `font` tokens.
|
|
14
|
+
.filter(t => t.attributes.group === 'typography').filter(t => t.cleanName.includes('font.heading') || t.cleanName.includes('font.body')).map(currentToken => {
|
|
15
|
+
var _typographyPalette$fi, _typographyPalette$fi2, _typographyPalette$fi3;
|
|
16
|
+
const individualValues = {
|
|
17
|
+
fontSize: (_typographyPalette$fi = typographyPalette.find(baseToken => baseToken.path.slice(-1)[0] ===
|
|
18
|
+
// @ts-expect-error token.original.value can be a string, due to the typographyTokens export including deprecated tokens
|
|
19
|
+
currentToken.original.value.fontSize)) === null || _typographyPalette$fi === void 0 ? void 0 : _typographyPalette$fi.value,
|
|
20
|
+
fontWeight: (_typographyPalette$fi2 = typographyPalette.find(baseToken => baseToken.path.slice(-1)[0] ===
|
|
21
|
+
// @ts-expect-error token.original.value can be a string, due to the typographyTokens export including deprecated tokens
|
|
22
|
+
currentToken.original.value.fontWeight)) === null || _typographyPalette$fi2 === void 0 ? void 0 : _typographyPalette$fi2.value,
|
|
23
|
+
lineHeight: (_typographyPalette$fi3 = typographyPalette.find(baseToken => baseToken.path.slice(-1)[0] ===
|
|
24
|
+
// @ts-expect-error token.original.value can be a string, due to the typographyTokens export including deprecated tokens
|
|
25
|
+
currentToken.original.value.lineHeight)) === null || _typographyPalette$fi3 === void 0 ? void 0 : _typographyPalette$fi3.value
|
|
26
|
+
};
|
|
27
|
+
return {
|
|
28
|
+
tokenName: currentToken.cleanName,
|
|
29
|
+
tokenValue: currentToken.value,
|
|
30
|
+
values: individualValues
|
|
31
|
+
};
|
|
32
|
+
});
|
|
33
|
+
export function findTypographyTokenForValues(fontSize, lineHeight) {
|
|
34
|
+
let matchingTokens = typographyValueToToken.filter(token => token.values.fontSize === fontSize)
|
|
35
|
+
// If lineHeight == 1, we don't match to a token
|
|
36
|
+
.filter(() => lineHeight === '1' ? false : true);
|
|
37
|
+
return matchingTokens;
|
|
38
|
+
}
|
|
39
|
+
export const fontWeightTokens = typographyTokens.filter(token => token.attributes.group === 'fontWeight').map(token => {
|
|
40
|
+
return {
|
|
41
|
+
tokenName: token.cleanName,
|
|
42
|
+
tokenValue: token.value,
|
|
43
|
+
values: {}
|
|
44
|
+
};
|
|
45
|
+
});
|
|
46
|
+
export function findFontWeightTokenForValue(fontWeight) {
|
|
47
|
+
return fontWeightTokens.find(token => token.tokenValue === fontWeight);
|
|
48
|
+
}
|
|
49
|
+
export const fontWeightMap = {
|
|
50
|
+
regular: '400',
|
|
51
|
+
medium: '500',
|
|
52
|
+
semibold: '600',
|
|
53
|
+
bold: '700'
|
|
54
|
+
};
|
|
55
|
+
export const defaultFontWeight = fontWeightMap.regular;
|
|
56
|
+
export const fontFamilyTokens = typographyTokens.filter(token => token.attributes.group === 'fontFamily');
|
|
57
|
+
export function findFontFamilyValueForToken(tokenName) {
|
|
58
|
+
var _fontFamilyTokens$fin;
|
|
59
|
+
// Note this will only ever be undefined if the tokens get renamed, and should never happen.
|
|
60
|
+
return ((_fontFamilyTokens$fin = fontFamilyTokens.find(token => token.cleanName === tokenName)) === null || _fontFamilyTokens$fin === void 0 ? void 0 : _fontFamilyTokens$fin.value) || '';
|
|
61
|
+
}
|
|
62
|
+
export function notUndefined(value) {
|
|
63
|
+
return value !== undefined;
|
|
64
|
+
}
|
|
65
|
+
export function isValidPropertyNode(node) {
|
|
66
|
+
if (!isNodeOfType(node.key, 'Identifier') && !isNodeOfType(node.key, 'Literal')) {
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
return true;
|
|
70
|
+
}
|
|
71
|
+
function getTokenNode(tokenName, tokenValue) {
|
|
72
|
+
return callExpression({
|
|
73
|
+
callee: identifier({
|
|
74
|
+
name: 'token'
|
|
75
|
+
}),
|
|
76
|
+
arguments: [literal({
|
|
77
|
+
value: `'${tokenName}'`
|
|
78
|
+
}), literal(tokenValue)],
|
|
79
|
+
optional: false
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
export function getTokenProperty(propertyName, tokenName, tokenFallback) {
|
|
83
|
+
return property({
|
|
84
|
+
key: identifier(propertyName),
|
|
85
|
+
value: getTokenNode(tokenName, tokenFallback)
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
export function getLiteralProperty(propertyName, propertyValue) {
|
|
89
|
+
return property({
|
|
90
|
+
key: identifier(propertyName),
|
|
91
|
+
value: literal(propertyValue)
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
export function convertPropertyNodeToStringableNode(node) {
|
|
95
|
+
return property({
|
|
96
|
+
key: node.key,
|
|
97
|
+
value: node.value
|
|
98
|
+
});
|
|
99
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
var ObjectEntry = {
|
|
2
|
+
deleteEntry: function deleteEntry(node, context, fixer) {
|
|
3
|
+
var _lastToken;
|
|
4
|
+
// context.getSourceCode() is deprecated in favour of context.sourceCode, however this returns undefined for some reason
|
|
5
|
+
var sourceCode = context.getSourceCode();
|
|
6
|
+
|
|
7
|
+
// fixer.remove() doesn't account for things like commas or newlines within an ObjectExpression and will result in invalid output.
|
|
8
|
+
// This approach specifically removes the node and trailing comma, and should work for single- and multi-line objects.
|
|
9
|
+
// From https://github.com/eslint/eslint/issues/9576#issuecomment-341737453
|
|
10
|
+
var prevToken = sourceCode.getTokenBefore(node);
|
|
11
|
+
while (((_prevToken = prevToken) === null || _prevToken === void 0 ? void 0 : _prevToken.value) !== ',' && ((_prevToken2 = prevToken) === null || _prevToken2 === void 0 ? void 0 : _prevToken2.value) !== '{') {
|
|
12
|
+
var _prevToken, _prevToken2;
|
|
13
|
+
prevToken = sourceCode.getTokenBefore(node);
|
|
14
|
+
}
|
|
15
|
+
var lastToken = sourceCode.getTokenAfter(node);
|
|
16
|
+
if (((_lastToken = lastToken) === null || _lastToken === void 0 ? void 0 : _lastToken.value) !== ',') {
|
|
17
|
+
lastToken = sourceCode.getTokenBefore(lastToken);
|
|
18
|
+
}
|
|
19
|
+
return fixer.removeRange([prevToken.range[1], lastToken.range[1]]);
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
export { ObjectEntry };
|
|
@@ -27,7 +27,7 @@ var ASTObjectExpression = {
|
|
|
27
27
|
});
|
|
28
28
|
},
|
|
29
29
|
/**
|
|
30
|
-
* Returns
|
|
30
|
+
* Returns the first Property node from an Object that matches the provided name.
|
|
31
31
|
*/
|
|
32
32
|
getEntryByPropertyName: function getEntryByPropertyName(node, name) {
|
|
33
33
|
return node.properties.find(function (property) {
|
|
@@ -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::d90c2cf5e100daf98915f9467f2e5663>>
|
|
4
4
|
* @codegenCommand yarn workspace @atlaskit/eslint-plugin-design-system codegen
|
|
5
5
|
*/
|
|
6
6
|
export default {
|
|
@@ -35,6 +35,7 @@ export default {
|
|
|
35
35
|
'@atlaskit/design-system/use-href-in-link-item': 'warn',
|
|
36
36
|
'@atlaskit/design-system/use-primitives': 'warn',
|
|
37
37
|
'@atlaskit/design-system/use-primitives-text': 'warn',
|
|
38
|
+
'@atlaskit/design-system/use-tokens-typography': 'warn',
|
|
38
39
|
'@atlaskit/design-system/use-visually-hidden': 'error'
|
|
39
40
|
}
|
|
40
41
|
};
|