@atlaskit/eslint-plugin-design-system 8.33.0 → 8.36.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 +28 -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-primitives-text/usage.mdx +13 -3
- 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-primitives/transformers/compiled-styled/find-valid-jsx-usage-to-transform.js +24 -1
- package/dist/cjs/rules/use-primitives/transformers/compiled-styled/index.js +2 -1
- package/dist/cjs/rules/use-primitives-text/config/index.js +2 -1
- package/dist/cjs/rules/use-primitives-text/transformers/common.js +10 -1
- package/dist/cjs/rules/use-primitives-text/transformers/emphasis-elements.js +6 -3
- package/dist/cjs/rules/use-primitives-text/transformers/paragraph-elements.js +19 -4
- package/dist/cjs/rules/use-primitives-text/transformers/span-elements.js +6 -4
- package/dist/cjs/rules/use-primitives-text/transformers/strong-elements.js +6 -3
- 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-primitives/transformers/compiled-styled/find-valid-jsx-usage-to-transform.js +17 -3
- package/dist/es2019/rules/use-primitives/transformers/compiled-styled/index.js +2 -1
- package/dist/es2019/rules/use-primitives-text/config/index.js +2 -1
- package/dist/es2019/rules/use-primitives-text/transformers/common.js +9 -2
- package/dist/es2019/rules/use-primitives-text/transformers/emphasis-elements.js +7 -4
- package/dist/es2019/rules/use-primitives-text/transformers/paragraph-elements.js +20 -5
- package/dist/es2019/rules/use-primitives-text/transformers/span-elements.js +7 -5
- package/dist/es2019/rules/use-primitives-text/transformers/strong-elements.js +7 -4
- 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-primitives/transformers/compiled-styled/find-valid-jsx-usage-to-transform.js +23 -2
- package/dist/esm/rules/use-primitives/transformers/compiled-styled/index.js +2 -1
- package/dist/esm/rules/use-primitives-text/config/index.js +2 -1
- package/dist/esm/rules/use-primitives-text/transformers/common.js +9 -2
- package/dist/esm/rules/use-primitives-text/transformers/emphasis-elements.js +7 -4
- package/dist/esm/rules/use-primitives-text/transformers/paragraph-elements.js +20 -5
- package/dist/esm/rules/use-primitives-text/transformers/span-elements.js +7 -5
- package/dist/esm/rules/use-primitives-text/transformers/strong-elements.js +7 -4
- 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-primitives/config/index.d.ts +1 -1
- package/dist/types/rules/use-primitives/transformers/compiled-styled/find-valid-jsx-usage-to-transform.d.ts +3 -1
- package/dist/types/rules/use-primitives/transformers/compiled-styled/index.d.ts +1 -1
- package/dist/types/rules/use-primitives/transformers/emotion-css/index.d.ts +1 -2
- package/dist/types/rules/use-primitives-text/config/index.d.ts +1 -0
- package/dist/types/rules/use-primitives-text/transformers/common.d.ts +6 -0
- package/dist/types/rules/use-primitives-text/transformers/emphasis-elements.d.ts +2 -10
- package/dist/types/rules/use-primitives-text/transformers/paragraph-elements.d.ts +4 -12
- package/dist/types/rules/use-primitives-text/transformers/span-elements.d.ts +2 -10
- package/dist/types/rules/use-primitives-text/transformers/strong-elements.d.ts +2 -10
- 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 +194 -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-primitives/config/index.d.ts +1 -1
- package/dist/types-ts4.5/rules/use-primitives/transformers/compiled-styled/find-valid-jsx-usage-to-transform.d.ts +3 -1
- package/dist/types-ts4.5/rules/use-primitives/transformers/compiled-styled/index.d.ts +1 -1
- package/dist/types-ts4.5/rules/use-primitives/transformers/emotion-css/index.d.ts +1 -2
- package/dist/types-ts4.5/rules/use-primitives-text/config/index.d.ts +1 -0
- package/dist/types-ts4.5/rules/use-primitives-text/transformers/common.d.ts +6 -0
- package/dist/types-ts4.5/rules/use-primitives-text/transformers/emphasis-elements.d.ts +2 -10
- package/dist/types-ts4.5/rules/use-primitives-text/transformers/paragraph-elements.d.ts +4 -12
- package/dist/types-ts4.5/rules/use-primitives-text/transformers/span-elements.d.ts +2 -10
- package/dist/types-ts4.5/rules/use-primitives-text/transformers/strong-elements.d.ts +2 -10
- 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 +194 -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
|
@@ -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
|
};
|
|
@@ -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
|
}
|