Skip to content

Commit 0cf6435

Browse files
committed
feat(tokens): provide autofix for global non-color tokens
1 parent f950da7 commit 0cf6435

File tree

10 files changed

+704
-49
lines changed

10 files changed

+704
-49
lines changed
Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1-
export * from './configs';
2-
export * from './ruleCuration';
3-
export * from './ruleCustomization'
1+
export * from "./configs";
2+
export * from "./ruleCuration";
3+
export * from "./ruleCustomization";
4+
export * from "./tokenLists";

packages/eslint-plugin-pf-codemods/src/ruleCustomization.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,6 @@ export const warningRules = [
4949
"tabs-update-markup",
5050
"tabs-warn-children-type-changed",
5151
"Th-Td-warn-update-markup",
52-
"tokens-warn",
5352
"toolbarLabelGroupContent-updated-markup",
5453
"tooltip-warn-triggerRef-may-be-required",
5554
"treeView-warn-selectable-styling-modifier-removed",

packages/eslint-plugin-pf-codemods/src/rules/v6/tokensWarn/tokens-warn.ts

Lines changed: 150 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,82 +1,204 @@
11
import { Rule } from "eslint";
22
import {
3+
IdentifierWithParent,
34
getDefaultDeclarationString,
45
getDefaultImportsFromPackage,
56
getFromPackage,
67
} from "../../helpers";
7-
import { ImportDeclaration, ImportSpecifier, Literal } from "estree-jsx";
8-
import { oldTokens } from "./tokenLists/oldTokens";
9-
import { oldCssVarNamesV5 } from "./tokenLists/oldCssVarNamesV5";
8+
import {
9+
Identifier,
10+
ImportDeclaration,
11+
ImportSpecifier,
12+
Literal,
13+
} from "estree-jsx";
14+
import {
15+
oldTokens,
16+
oldCssVarNamesV5,
17+
globalNonColorTokensMap,
18+
oldGlobalNonColorTokens,
19+
oldGlobalNonColorCssVarNames,
20+
globalNonColorCssVarNamesMap,
21+
} from "../../../tokenLists";
1022

1123
module.exports = {
12-
meta: {},
24+
meta: { fixable: "code" },
1325
create: function (context: Rule.RuleContext) {
1426
const tokensPackage = "@patternfly/react-tokens";
1527

1628
const { imports: tokenSpecifiers } = getFromPackage(context, tokensPackage);
1729

18-
const defaultTokensWithDeclaration = getDefaultImportsFromPackage(
30+
const defaultTokenImports = getDefaultImportsFromPackage(
1931
context,
2032
tokensPackage
2133
)
2234
.map((specifier) => ({
35+
specifier,
2336
path: getDefaultDeclarationString(specifier),
2437
declaration: specifier.parent,
2538
}))
2639
.filter(({ path }) => path !== undefined)
27-
.map(({ path, declaration }) => ({
40+
.map(({ specifier, path, declaration }) => ({
41+
specifier,
2842
token: (path as string).split("/").pop() as string,
2943
declaration,
3044
}));
3145

32-
const getMessage = (tokenName: string) =>
46+
const getWarnMessage = (tokenName: string) =>
3347
`${tokenName} is an old CSS token. About half of our tokens have been replaced with newer ones. To find a suitable replacement token, check our new documentation https://staging-v6.patternfly.org/tokens/all-patternfly-tokens.`;
3448

49+
const getFixMessage = (oldToken: string, newToken: string) =>
50+
`${oldToken} is an old CSS token and has been replaced with ${newToken}. If you want to use a different token, check our new documentation https://staging-v6.patternfly.org/tokens/all-patternfly-tokens.`;
51+
52+
const shouldReplaceToken = (token: string) =>
53+
oldGlobalNonColorTokens.includes(token) &&
54+
globalNonColorTokensMap[token as keyof typeof globalNonColorTokensMap] !==
55+
"SKIP";
56+
57+
const replaceToken = (
58+
node: ImportDeclaration | ImportSpecifier | Identifier,
59+
oldToken: string
60+
) => {
61+
const newToken =
62+
globalNonColorTokensMap[
63+
oldToken as keyof typeof globalNonColorTokensMap
64+
];
65+
66+
context.report({
67+
node,
68+
message: getFixMessage(oldToken, newToken),
69+
fix(fixer) {
70+
if (node.type === "ImportDeclaration") {
71+
const newDeclaration = node.source.value
72+
?.toString()
73+
.replace(oldToken, newToken) as string;
74+
75+
return [
76+
fixer.replaceText(node.specifiers[0], newToken),
77+
fixer.replaceText(node.source, `"${newDeclaration}"`),
78+
];
79+
}
80+
81+
if (node.type === "ImportSpecifier") {
82+
return fixer.replaceText(node.imported, newToken);
83+
}
84+
85+
return fixer.replaceText(node, newToken);
86+
},
87+
});
88+
};
89+
90+
const replaceTokenOrWarn = (
91+
node: ImportSpecifier | ImportDeclaration,
92+
token: string
93+
) => {
94+
if (shouldReplaceToken(token)) {
95+
replaceToken(node, token);
96+
} else if (oldTokens.includes(token)) {
97+
context.report({
98+
node,
99+
message: getWarnMessage(token),
100+
});
101+
}
102+
};
103+
35104
return {
36105
ImportSpecifier(node: ImportSpecifier) {
37106
if (tokenSpecifiers.includes(node)) {
38-
const tokenName = node.imported.name;
39-
if (oldTokens.includes(tokenName)) {
40-
context.report({
41-
node,
42-
message: getMessage(tokenName),
43-
});
44-
}
107+
const token = node.imported.name;
108+
replaceTokenOrWarn(node, token);
45109
}
46110
},
47111
ImportDeclaration(node: ImportDeclaration) {
48-
const tokenWithDeclaration = defaultTokensWithDeclaration.find(
112+
const tokenWithDeclaration = defaultTokenImports.find(
49113
({ declaration }) => node.source.value === declaration?.source.value
50114
);
51115

116+
if (!tokenWithDeclaration) {
117+
return;
118+
}
119+
120+
replaceTokenOrWarn(node, tokenWithDeclaration.token);
121+
},
122+
Identifier(node: Identifier) {
123+
const parentType = (node as IdentifierWithParent).parent?.type;
124+
// handle ImportSpecifier and ImportDeclaration separately
52125
if (
53-
tokenWithDeclaration &&
54-
oldTokens.includes(tokenWithDeclaration.token)
126+
parentType === "ImportSpecifier" ||
127+
parentType === "ImportDefaultSpecifier"
55128
) {
56-
context.report({
57-
node,
58-
message: getMessage(tokenWithDeclaration.token),
59-
});
129+
return;
130+
}
131+
132+
const tokenInfo = defaultTokenImports.find(
133+
({ specifier }) => node.name === specifier.local.name
134+
);
135+
136+
if (tokenInfo && shouldReplaceToken(tokenInfo.token)) {
137+
replaceToken(node, tokenInfo.token);
138+
}
139+
140+
const unaliasedTokenSpecifier = tokenSpecifiers.find(
141+
(specifier) =>
142+
specifier.local.name === specifier.imported.name &&
143+
node.name === specifier.local.name
144+
);
145+
146+
if (unaliasedTokenSpecifier && shouldReplaceToken(node.name)) {
147+
replaceToken(node, node.name);
60148
}
61149
},
62150
Literal(node: Literal) {
63-
if (
64-
typeof node.value === "string" &&
65-
[...oldCssVarNames, ...oldCssVars].includes(node.value)
66-
) {
151+
if (typeof node.value !== "string") {
152+
return;
153+
}
154+
155+
let varName = node.value;
156+
const varRegex = /var\(([^)]+)\)/;
157+
const match = node.value.match(varRegex);
158+
159+
if (match) {
160+
varName = match[1];
161+
}
162+
163+
const shouldReplaceVar =
164+
oldGlobalNonColorCssVarNames.includes(varName) &&
165+
globalNonColorCssVarNamesMap[
166+
varName as keyof typeof globalNonColorCssVarNamesMap
167+
] !== "SKIP";
168+
169+
if (shouldReplaceVar) {
170+
const newVarName =
171+
globalNonColorCssVarNamesMap[
172+
varName as keyof typeof globalNonColorCssVarNamesMap
173+
];
174+
175+
if (newVarName !== "SKIP") {
176+
context.report({
177+
node,
178+
message: getFixMessage(varName, newVarName),
179+
fix(fixer) {
180+
return fixer.replaceText(
181+
node,
182+
node.value?.toString().startsWith("var")
183+
? `"var(${newVarName})"`
184+
: `"${newVarName}"`
185+
);
186+
},
187+
});
188+
}
189+
} else if (oldCssVarNames.includes(varName)) {
67190
context.report({
68191
node,
69-
message: getMessage(node.value),
192+
message: getWarnMessage(node.value),
70193
});
71194
}
72195
},
73196
};
74197
},
75198
};
76199

77-
// consumers may run class-name-updater before codemods, so we have to check also old tokens with v6 prefix
200+
// consumers may have run the old class-name-updater before codemods, so we should check also old tokens with v6 prefix
78201
const oldCssVarNamesV6 = oldCssVarNamesV5.map((cssVarName) =>
79202
cssVarName.replace("v5", "v6")
80203
);
81204
const oldCssVarNames = [...oldCssVarNamesV5, ...oldCssVarNamesV6];
82-
const oldCssVars = oldCssVarNames.map((cssVarName) => `var(${cssVarName})`);
Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,29 @@
1+
// replacements (fixable with --fix)
2+
import global_BorderWidth_lg from "@patternfly/react-tokens/dist/esm/global_BorderWidth_lg";
3+
import { global_FontWeight_normal } from "@patternfly/react-tokens";
4+
5+
global_BorderWidth_lg;
6+
global_FontWeight_normal;
7+
8+
document.documentElement.style.setProperty("--pf-v5-global--ZIndex--lg", "3");
9+
<div
10+
style={{
11+
borderWidth: "var(--pf-v5-global--BorderWidth--lg)",
12+
boxShadow: "var(--pf-v5-global--BoxShadow--sm)",
13+
marginTop: "var(--pf-v5-global--spacer--3xl)",
14+
}}
15+
></div>;
16+
17+
// warnings (not fixable)
118
import global_warning_color_100 from "@patternfly/react-tokens/dist/esm/global_warning_color_100";
219
import { c_alert__FontSize } from "@patternfly/react-tokens";
320

421
global_warning_color_100;
522
c_alert__FontSize;
623

7-
<>
8-
<div
9-
style={{
10-
"--pf-v5-global--success-color--200": "#abc",
11-
}}
12-
></div>
13-
<div style={{ borderWidth: "var(--pf-v5-global--BorderWidth--lg)" }}></div>
14-
</>;
24+
<div
25+
style={{
26+
color: "var(--pf-v5-global--success-color--200)",
27+
width: "var(--pf-v5-global--arrow--width)",
28+
}}
29+
></div>;
Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,29 @@
1+
// replacements (fixable with --fix)
2+
import global_border_width_extra_strong from "@patternfly/react-tokens/dist/esm/global_border_width_extra_strong";
3+
import { global_font_weight_body_default } from "@patternfly/react-tokens";
4+
5+
global_border_width_extra_strong;
6+
global_font_weight_body_default;
7+
8+
document.documentElement.style.setProperty("--pf-t--global--z-index--lg", "3");
9+
<div
10+
style={{
11+
borderWidth: "var(--pf-t--global--border--width--extra-strong)",
12+
boxShadow: "var(--pf-t--global--box-shadow--sm)",
13+
marginTop: "var(--pf-t--global--spacer--3xl)",
14+
}}
15+
></div>;
16+
17+
// warnings (not fixable)
118
import global_warning_color_100 from "@patternfly/react-tokens/dist/esm/global_warning_color_100";
219
import { c_alert__FontSize } from "@patternfly/react-tokens";
320

421
global_warning_color_100;
522
c_alert__FontSize;
623

7-
<>
8-
<div
9-
style={{
10-
"--pf-v5-global--success-color--200": "#abc",
11-
}}
12-
></div>
13-
<div style={{ borderWidth: "var(--pf-v5-global--BorderWidth--lg)" }}></div>
14-
</>;
24+
<div
25+
style={{
26+
color: "var(--pf-v5-global--success-color--200)",
27+
width: "var(--pf-v5-global--arrow--width)",
28+
}}
29+
></div>;
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
export * from "./oldCssVarNamesV5";
2+
export * from "./oldGlobalCssVarNames";
3+
export * from "./oldGlobalTokens";
4+
export * from "./oldTokens";
5+
export * from "./sameGlobalCssVarNames";
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// names of css variables of the oldTokens list
1+
// names of css variables for each token of the oldTokens list
22
export const oldCssVarNamesV5 = [
33
"--pf-v5-c-about-modal-box__brand--PaddingBottom",
44
"--pf-v5-c-about-modal-box__brand--PaddingLeft",

0 commit comments

Comments
 (0)