@pantheon-systems/pds-toolkit-react 2.0.0-alpha.59 → 2.0.0-alpha.60
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/codemods/v1-to-v2/css-token-renames.js +186 -0
- package/codemods/v1-to-v2/mappings.json +85 -7
- package/codemods/v1-to-v2/transforms/__testfixtures__/css-class-removals/basic.input.tsx +24 -0
- package/codemods/v1-to-v2/transforms/__testfixtures__/css-class-removals/basic.output.tsx +24 -0
- package/codemods/v1-to-v2/transforms/__testfixtures__/css-class-renames/basic.input.tsx +29 -0
- package/codemods/v1-to-v2/transforms/__testfixtures__/css-class-renames/basic.output.tsx +29 -0
- package/codemods/v1-to-v2/transforms/css-class-removals.js +97 -27
- package/codemods/v1-to-v2/transforms/css-class-renames.js +80 -16
- package/dist/components/SiteFooter/SiteFooter.d.ts +7 -3
- package/dist/components/buttons/MenuButton/MenuButton.d.ts +4 -1
- package/dist/components/icons/Icon/generated-icon-data.d.ts +1 -1
- package/dist/css/component-css/pds-button.css +1 -3
- package/dist/css/component-css/pds-checkbox.css +1 -1
- package/dist/css/component-css/pds-cta-link.css +1 -1
- package/dist/css/component-css/pds-expansion-panel-group.css +1 -1
- package/dist/css/component-css/pds-expansion-panel.css +1 -1
- package/dist/css/component-css/pds-index.css +11 -13
- package/dist/css/component-css/pds-menu-button.css +1 -1
- package/dist/css/component-css/pds-side-nav.css +1 -1
- package/dist/css/component-css/pds-tab-menu.css +2 -2
- package/dist/css/component-css/pds-utility-button.css +1 -1
- package/dist/css/component-css/pds-workspace-selector.css +1 -1
- package/dist/css/design-tokens/variables.dark.css +6 -13
- package/dist/css/design-tokens/variables.global.css +1 -0
- package/dist/css/design-tokens/variables.light.css +7 -14
- package/dist/css/pds-components.css +11 -13
- package/dist/css/pds-core.css +1 -1
- package/dist/index.css +1 -1
- package/dist/index.js +148 -141
- package/dist/index.js.map +1 -1
- package/package.json +7 -6
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* css-token-renames
|
|
4
|
+
*
|
|
5
|
+
* Find-and-replace script for PDS design token renames in CSS and SCSS files.
|
|
6
|
+
* Driven entirely by tokens.renames and tokens.patterns in the published
|
|
7
|
+
* @pantheon-systems/pds-design-tokens codemods mappings.
|
|
8
|
+
*
|
|
9
|
+
* Handles:
|
|
10
|
+
* - Exact token renames: --pds-old-name → --pds-new-name
|
|
11
|
+
* - Wildcard patterns: --pds-badge-*-secondary → --pds-badge-*-muted
|
|
12
|
+
* - CSS utility classes: pds-color-bg-default → pds-color-surface-default
|
|
13
|
+
* (in CSS selectors; JSX className usage is handled by css-class-renames)
|
|
14
|
+
*
|
|
15
|
+
* Usage (from a consuming app, after installing @pantheon-systems/pds-toolkit-react):
|
|
16
|
+
* node node_modules/@pantheon-systems/pds-toolkit-react/codemods/v1-to-v2/css-token-renames.js <path>
|
|
17
|
+
* node node_modules/@pantheon-systems/pds-toolkit-react/codemods/v1-to-v2/css-token-renames.js src/apps/workspace/
|
|
18
|
+
*
|
|
19
|
+
* Usage (from a local checkout of this repo):
|
|
20
|
+
* node codemods/v1-to-v2/css-token-renames.js <path>
|
|
21
|
+
*
|
|
22
|
+
* Options:
|
|
23
|
+
* --dry Preview changes without writing files
|
|
24
|
+
* --only=<category> Restrict to a token category (e.g. animation, color, spacing)
|
|
25
|
+
* --ext=css,scss File extensions to scan (default: css,scss)
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
import fs from 'fs';
|
|
29
|
+
import path from 'path';
|
|
30
|
+
import { createRequire } from 'module';
|
|
31
|
+
import { globSync } from 'glob';
|
|
32
|
+
|
|
33
|
+
// JSON imports via createRequire — works in both CJS and ESM contexts
|
|
34
|
+
const require = createRequire(import.meta.url);
|
|
35
|
+
const mappings = require('@pantheon-systems/pds-design-tokens/codemods/v1-to-v2/mappings.json');
|
|
36
|
+
|
|
37
|
+
// --- Argument parsing ---
|
|
38
|
+
|
|
39
|
+
const args = process.argv.slice(2);
|
|
40
|
+
const dry = args.includes('--dry');
|
|
41
|
+
const onlyArg = args.find((a) => a.startsWith('--only='));
|
|
42
|
+
const extArg = args.find((a) => a.startsWith('--ext='));
|
|
43
|
+
const targetPaths = args.filter((a) => !a.startsWith('--'));
|
|
44
|
+
|
|
45
|
+
const onlyCategory = onlyArg ? onlyArg.replace('--only=', '').trim() : null;
|
|
46
|
+
const extensions = extArg
|
|
47
|
+
? extArg
|
|
48
|
+
.replace('--ext=', '')
|
|
49
|
+
.split(',')
|
|
50
|
+
.map((e) => e.trim())
|
|
51
|
+
: ['css', 'scss'];
|
|
52
|
+
|
|
53
|
+
if (targetPaths.length === 0) {
|
|
54
|
+
console.error(
|
|
55
|
+
'Usage: node codemods/v1-to-v2/css-token-renames.js <path> [--dry] [--only=category] [--ext=css,scss]',
|
|
56
|
+
);
|
|
57
|
+
process.exit(1);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// --- Build rename and pattern lists ---
|
|
61
|
+
|
|
62
|
+
function buildRenames() {
|
|
63
|
+
const exact = []; // { from, to }
|
|
64
|
+
const patterns = []; // { regex, replacement, from }
|
|
65
|
+
|
|
66
|
+
for (const entry of mappings.tokens.renames) {
|
|
67
|
+
if (!entry.from || !entry.to || entry.from === entry.to) continue;
|
|
68
|
+
if (onlyCategory && entry.category !== onlyCategory) continue;
|
|
69
|
+
exact.push({ from: entry.from, to: entry.to });
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
for (const entry of mappings.tokens.patterns) {
|
|
73
|
+
if (!entry.from || !entry.to || entry.from === entry.to) continue;
|
|
74
|
+
if (onlyCategory && entry.category !== onlyCategory) continue;
|
|
75
|
+
|
|
76
|
+
// Convert wildcard pattern to regex.
|
|
77
|
+
// Escape special regex chars except *, then replace * with a capture group.
|
|
78
|
+
const escaped = entry.from
|
|
79
|
+
.replace(/[.+?^${}()|[\]\\]/g, '\\$&')
|
|
80
|
+
.replace(/\*/g, '([\\w-]+)');
|
|
81
|
+
const regex = new RegExp(escaped, 'g');
|
|
82
|
+
|
|
83
|
+
// Build replacement — each * in `to` becomes $1, $2, etc.
|
|
84
|
+
let captureIndex = 0;
|
|
85
|
+
const replacement = entry.to.replace(/\*/g, () => `$${++captureIndex}`);
|
|
86
|
+
|
|
87
|
+
patterns.push({ regex, replacement, from: entry.from });
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return { exact, patterns };
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const { exact: EXACT_RENAMES, patterns: PATTERNS } = buildRenames();
|
|
94
|
+
|
|
95
|
+
// --- File discovery ---
|
|
96
|
+
|
|
97
|
+
function findFiles(targetPath) {
|
|
98
|
+
const extPattern =
|
|
99
|
+
extensions.length === 1 ? extensions[0] : `{${extensions.join(',')}}`;
|
|
100
|
+
|
|
101
|
+
if (fs.existsSync(targetPath) && fs.statSync(targetPath).isFile()) {
|
|
102
|
+
return [targetPath];
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (fs.existsSync(targetPath) && fs.statSync(targetPath).isDirectory()) {
|
|
106
|
+
return globSync(`**/*.${extPattern}`, {
|
|
107
|
+
cwd: targetPath,
|
|
108
|
+
absolute: true,
|
|
109
|
+
ignore: ['**/node_modules/**', '**/dist/**', '**/build/**'],
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return globSync(targetPath, {
|
|
114
|
+
absolute: true,
|
|
115
|
+
ignore: ['**/node_modules/**', '**/dist/**', '**/build/**'],
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// --- Apply renames to file content ---
|
|
120
|
+
|
|
121
|
+
function applyRenames(content) {
|
|
122
|
+
let result = content;
|
|
123
|
+
let changed = false;
|
|
124
|
+
const appliedRenames = [];
|
|
125
|
+
|
|
126
|
+
// Exact renames
|
|
127
|
+
for (const { from, to } of EXACT_RENAMES) {
|
|
128
|
+
if (!result.includes(from)) continue;
|
|
129
|
+
const regex = new RegExp(from.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'g');
|
|
130
|
+
const next = result.replace(regex, to);
|
|
131
|
+
if (next !== result) {
|
|
132
|
+
appliedRenames.push(` ${from} → ${to}`);
|
|
133
|
+
result = next;
|
|
134
|
+
changed = true;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Wildcard patterns
|
|
139
|
+
for (const { regex, replacement, from } of PATTERNS) {
|
|
140
|
+
regex.lastIndex = 0;
|
|
141
|
+
const next = result.replace(regex, replacement);
|
|
142
|
+
if (next !== result) {
|
|
143
|
+
appliedRenames.push(` ${from} → ${replacement} (pattern)`);
|
|
144
|
+
result = next;
|
|
145
|
+
changed = true;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return { result, changed, appliedRenames };
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// --- Main ---
|
|
153
|
+
|
|
154
|
+
let totalFiles = 0;
|
|
155
|
+
let changedFiles = 0;
|
|
156
|
+
let totalRenames = 0;
|
|
157
|
+
|
|
158
|
+
const allFiles = targetPaths.flatMap(findFiles);
|
|
159
|
+
|
|
160
|
+
if (allFiles.length === 0) {
|
|
161
|
+
console.log('No CSS/SCSS files found matching the given path(s).');
|
|
162
|
+
process.exit(0);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
for (const filePath of allFiles) {
|
|
166
|
+
const content = fs.readFileSync(filePath, 'utf8');
|
|
167
|
+
const { result, changed, appliedRenames } = applyRenames(content);
|
|
168
|
+
|
|
169
|
+
totalFiles++;
|
|
170
|
+
if (!changed) continue;
|
|
171
|
+
|
|
172
|
+
changedFiles++;
|
|
173
|
+
totalRenames += appliedRenames.length;
|
|
174
|
+
|
|
175
|
+
console.log(
|
|
176
|
+
`${dry ? '[dry] ' : ''}${path.relative(process.cwd(), filePath)}`,
|
|
177
|
+
);
|
|
178
|
+
appliedRenames.forEach((r) => console.log(r));
|
|
179
|
+
|
|
180
|
+
if (!dry) fs.writeFileSync(filePath, result, 'utf8');
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
console.log(
|
|
184
|
+
`\n${dry ? '[dry run] ' : ''}${changedFiles}/${totalFiles} files changed, ${totalRenames} renames applied.`,
|
|
185
|
+
);
|
|
186
|
+
if (dry && changedFiles > 0) console.log('Run without --dry to apply changes.');
|
|
@@ -696,13 +696,6 @@
|
|
|
696
696
|
"replacement": "Use variant=\"link\"",
|
|
697
697
|
"reason": "The 'inline' button variant has been renamed to 'link' to describe its appearance (a text link) rather than its placement, and is no longer treated as legacy."
|
|
698
698
|
},
|
|
699
|
-
{
|
|
700
|
-
"component": "MenuButton",
|
|
701
|
-
"prop": "variant",
|
|
702
|
-
"oldValue": "navbar",
|
|
703
|
-
"replacement": "Use variant=\"subtle\"",
|
|
704
|
-
"reason": "The navbar variant has been removed in v2. Use subtle instead. The expanded-state transparency and icon-color behavior previously specific to the navbar variant now applies to all subtle MenuButton triggers automatically."
|
|
705
|
-
},
|
|
706
699
|
{
|
|
707
700
|
"component": "IconButton",
|
|
708
701
|
"prop": "variant",
|
|
@@ -1350,6 +1343,91 @@
|
|
|
1350
1343
|
"old": "pds-overline-text--lg",
|
|
1351
1344
|
"new": "pds-overline-l",
|
|
1352
1345
|
"reason": "Overline text size variants replaced with standalone size-based classes."
|
|
1346
|
+
},
|
|
1347
|
+
{
|
|
1348
|
+
"old": "pds-color-bg-default",
|
|
1349
|
+
"new": "pds-color-surface-default",
|
|
1350
|
+
"reason": "Background utility classes renamed to the surface naming convention. Mirrors the CSS custom property rename of the same name."
|
|
1351
|
+
},
|
|
1352
|
+
{
|
|
1353
|
+
"old": "pds-color-bg-default-secondary",
|
|
1354
|
+
"new": "pds-color-surface-default-secondary",
|
|
1355
|
+
"reason": "Background utility classes renamed to the surface naming convention. Mirrors the CSS custom property rename of the same name."
|
|
1356
|
+
},
|
|
1357
|
+
{
|
|
1358
|
+
"old": "pds-color-bg-reverse",
|
|
1359
|
+
"new": "pds-color-surface-reverse",
|
|
1360
|
+
"reason": "Background utility classes renamed to the surface naming convention. Mirrors the CSS custom property rename of the same name."
|
|
1361
|
+
},
|
|
1362
|
+
{
|
|
1363
|
+
"old": "pds-color-bg-transparent",
|
|
1364
|
+
"new": "pds-color-surface-transparent",
|
|
1365
|
+
"reason": "Background utility classes renamed to the surface naming convention."
|
|
1366
|
+
},
|
|
1367
|
+
{
|
|
1368
|
+
"old": "pds-color-bg-info",
|
|
1369
|
+
"new": "pds-color-surface-info",
|
|
1370
|
+
"reason": "Status background utility classes renamed to the surface naming convention."
|
|
1371
|
+
},
|
|
1372
|
+
{
|
|
1373
|
+
"old": "pds-color-bg-success",
|
|
1374
|
+
"new": "pds-color-surface-success",
|
|
1375
|
+
"reason": "Status background utility classes renamed to the surface naming convention."
|
|
1376
|
+
},
|
|
1377
|
+
{
|
|
1378
|
+
"old": "pds-color-bg-warning",
|
|
1379
|
+
"new": "pds-color-surface-warning",
|
|
1380
|
+
"reason": "Status background utility classes renamed to the surface naming convention."
|
|
1381
|
+
},
|
|
1382
|
+
{
|
|
1383
|
+
"old": "pds-color-bg-critical",
|
|
1384
|
+
"new": "pds-color-surface-critical",
|
|
1385
|
+
"reason": "Status background utility classes renamed to the surface naming convention."
|
|
1386
|
+
},
|
|
1387
|
+
{
|
|
1388
|
+
"old": "pds-color-bg-discovery",
|
|
1389
|
+
"new": "pds-color-surface-discovery",
|
|
1390
|
+
"reason": "Status background utility classes renamed to the surface naming convention."
|
|
1391
|
+
},
|
|
1392
|
+
{
|
|
1393
|
+
"old": "pds-color-fg-default",
|
|
1394
|
+
"new": "pds-color-foreground-default",
|
|
1395
|
+
"reason": "Foreground utility classes renamed to the foreground naming convention. Mirrors the CSS custom property rename of the same name."
|
|
1396
|
+
},
|
|
1397
|
+
{
|
|
1398
|
+
"old": "pds-color-fg-default-secondary",
|
|
1399
|
+
"new": "pds-color-foreground-default-secondary",
|
|
1400
|
+
"reason": "Foreground utility classes renamed to the foreground naming convention. Mirrors the CSS custom property rename of the same name."
|
|
1401
|
+
},
|
|
1402
|
+
{
|
|
1403
|
+
"old": "pds-color-fg-reverse",
|
|
1404
|
+
"new": "pds-color-foreground-reverse",
|
|
1405
|
+
"reason": "Foreground utility classes renamed to the foreground naming convention. Mirrors the CSS custom property rename of the same name."
|
|
1406
|
+
},
|
|
1407
|
+
{
|
|
1408
|
+
"old": "pds-color-fg-info",
|
|
1409
|
+
"new": "pds-color-foreground-info",
|
|
1410
|
+
"reason": "Status foreground utility classes renamed to the foreground naming convention."
|
|
1411
|
+
},
|
|
1412
|
+
{
|
|
1413
|
+
"old": "pds-color-fg-success",
|
|
1414
|
+
"new": "pds-color-foreground-success",
|
|
1415
|
+
"reason": "Status foreground utility classes renamed to the foreground naming convention."
|
|
1416
|
+
},
|
|
1417
|
+
{
|
|
1418
|
+
"old": "pds-color-fg-warning",
|
|
1419
|
+
"new": "pds-color-foreground-warning",
|
|
1420
|
+
"reason": "Status foreground utility classes renamed to the foreground naming convention."
|
|
1421
|
+
},
|
|
1422
|
+
{
|
|
1423
|
+
"old": "pds-color-fg-critical",
|
|
1424
|
+
"new": "pds-color-foreground-critical",
|
|
1425
|
+
"reason": "Status foreground utility classes renamed to the foreground naming convention."
|
|
1426
|
+
},
|
|
1427
|
+
{
|
|
1428
|
+
"old": "pds-color-fg-discovery",
|
|
1429
|
+
"new": "pds-color-foreground-discovery",
|
|
1430
|
+
"reason": "Status foreground utility classes renamed to the foreground naming convention."
|
|
1353
1431
|
}
|
|
1354
1432
|
],
|
|
1355
1433
|
"removals": [
|
|
@@ -29,3 +29,27 @@ const J = () => <div className='pds-button--subtle' />;
|
|
|
29
29
|
// Dynamic class — cannot transform, leave as-is
|
|
30
30
|
const cls = 'pds-button--navbar';
|
|
31
31
|
const K = () => <div className={cls} />;
|
|
32
|
+
|
|
33
|
+
// Bare ternary — direct rename in one branch, safe removal in the other
|
|
34
|
+
const L = ({ active }: { active: boolean }) => (
|
|
35
|
+
<div
|
|
36
|
+
className={active ? 'pds-button--navbar' : 'pds-typography--product'}
|
|
37
|
+
/>
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
// Bare logical expression — right-hand side is a flagged class
|
|
41
|
+
const M = ({ isOpen }: { isOpen: boolean }) => (
|
|
42
|
+
<div className={isOpen && 'pds-color-bg-brand'} />
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
// Ternary nested inside a template literal interpolation
|
|
46
|
+
const N = ({ isEmployee }: { isEmployee: boolean }) => (
|
|
47
|
+
<div
|
|
48
|
+
className={`chat-bubble ${
|
|
49
|
+
isEmployee ? 'pds-button--navbar' : 'pds-ts-9xl'
|
|
50
|
+
}`}
|
|
51
|
+
/>
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
// Non-className prop that still ends in "ClassName"
|
|
55
|
+
const O = () => <Icon iconClassName='pds-button--navbar' />;
|
|
@@ -29,3 +29,27 @@ const J = () => <div className='pds-button--subtle' />;
|
|
|
29
29
|
// Dynamic class — cannot transform, leave as-is
|
|
30
30
|
const cls = 'pds-button--navbar';
|
|
31
31
|
const K = () => <div className={cls} />;
|
|
32
|
+
|
|
33
|
+
// Bare ternary — direct rename in one branch, safe removal in the other
|
|
34
|
+
const L = ({ active }: { active: boolean }) => (
|
|
35
|
+
<div
|
|
36
|
+
className={active ? "pds-button--subtle" : ""}
|
|
37
|
+
/>
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
// Bare logical expression — right-hand side is a flagged class
|
|
41
|
+
const M = ({ isOpen }: { isOpen: boolean }) => (
|
|
42
|
+
<div className={isOpen && 'pds-color-bg-brand'} />
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
// Ternary nested inside a template literal interpolation
|
|
46
|
+
const N = ({ isEmployee }: { isEmployee: boolean }) => (
|
|
47
|
+
<div
|
|
48
|
+
className={`chat-bubble ${
|
|
49
|
+
isEmployee ? "pds-button--subtle" : "pds-ts-8xl"
|
|
50
|
+
}`}
|
|
51
|
+
/>
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
// Non-className prop that still ends in "ClassName"
|
|
55
|
+
const O = () => <Icon iconClassName="pds-button--subtle" />;
|
|
@@ -27,5 +27,34 @@ const G = () => <div className='pds-overline-m' />;
|
|
|
27
27
|
const cls = 'pds-overline-text';
|
|
28
28
|
const H = () => <div className={cls} />;
|
|
29
29
|
|
|
30
|
+
// Bare ternary — both branches are string literals
|
|
31
|
+
const J = ({ active }: { active: boolean }) => (
|
|
32
|
+
<div className={active ? 'pds-overline-text' : 'pds-overline-text--sm'} />
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
// Bare logical expression — right-hand side is a string literal
|
|
36
|
+
const K = ({ isOpen }: { isOpen: boolean }) => (
|
|
37
|
+
<div className={isOpen && 'pds-overline-text'} />
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
// Ternary nested inside a template literal interpolation
|
|
41
|
+
const L = ({ isEmployee }: { isEmployee: boolean }) => (
|
|
42
|
+
<div
|
|
43
|
+
className={`chat-bubble ${
|
|
44
|
+
isEmployee ? 'pds-overline-text--sm' : 'pds-overline-text--lg'
|
|
45
|
+
}`}
|
|
46
|
+
/>
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
// Ternary with one literal branch and one dynamic branch — only the literal
|
|
50
|
+
// branch can be renamed; the dynamic branch is left untouched
|
|
51
|
+
const M = ({ active, fallbackClass }: { active: boolean; fallbackClass: string }) => (
|
|
52
|
+
<div className={active ? 'pds-overline-text' : fallbackClass} />
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
// Non-className prop that still ends in "ClassName" — common for components
|
|
56
|
+
// with multiple styleable slots
|
|
57
|
+
const N = () => <Icon iconClassName='pds-overline-text' />;
|
|
58
|
+
|
|
30
59
|
// Non-className attribute — should be left alone
|
|
31
60
|
const I = () => <div id='pds-overline-text' />;
|
|
@@ -27,5 +27,34 @@ const G = () => <div className='pds-overline-m' />;
|
|
|
27
27
|
const cls = 'pds-overline-text';
|
|
28
28
|
const H = () => <div className={cls} />;
|
|
29
29
|
|
|
30
|
+
// Bare ternary — both branches are string literals
|
|
31
|
+
const J = ({ active }: { active: boolean }) => (
|
|
32
|
+
<div className={active ? "pds-overline-m" : "pds-overline-s"} />
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
// Bare logical expression — right-hand side is a string literal
|
|
36
|
+
const K = ({ isOpen }: { isOpen: boolean }) => (
|
|
37
|
+
<div className={isOpen && "pds-overline-m"} />
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
// Ternary nested inside a template literal interpolation
|
|
41
|
+
const L = ({ isEmployee }: { isEmployee: boolean }) => (
|
|
42
|
+
<div
|
|
43
|
+
className={`chat-bubble ${
|
|
44
|
+
isEmployee ? "pds-overline-s" : "pds-overline-l"
|
|
45
|
+
}`}
|
|
46
|
+
/>
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
// Ternary with one literal branch and one dynamic branch — only the literal
|
|
50
|
+
// branch can be renamed; the dynamic branch is left untouched
|
|
51
|
+
const M = ({ active, fallbackClass }: { active: boolean; fallbackClass: string }) => (
|
|
52
|
+
<div className={active ? "pds-overline-m" : fallbackClass} />
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
// Non-className prop that still ends in "ClassName" — common for components
|
|
56
|
+
// with multiple styleable slots
|
|
57
|
+
const N = () => <Icon iconClassName="pds-overline-m" />;
|
|
58
|
+
|
|
30
59
|
// Non-className attribute — should be left alone
|
|
31
60
|
const I = () => <div id='pds-overline-text' />;
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Codemod: css-class-removals
|
|
3
3
|
*
|
|
4
|
-
* Handles removed PDS CSS utility classes in JSX className attributes
|
|
4
|
+
* Handles removed PDS CSS utility classes in JSX className attributes — and
|
|
5
|
+
* any other prop ending in "ClassName" (e.g. iconClassName, labelClassName),
|
|
6
|
+
* a common pattern for components with multiple styleable slots.
|
|
5
7
|
* Driven entirely by cssClasses.removals in mappings.json.
|
|
6
8
|
*
|
|
7
9
|
* Three kinds of change:
|
|
@@ -21,6 +23,17 @@
|
|
|
21
23
|
* these manually. Removing them silently would break visible styles with
|
|
22
24
|
* no clear path forward.
|
|
23
25
|
*
|
|
26
|
+
* Handles ternaries / logical expressions, as a bare className expression or
|
|
27
|
+
* nested inside a template literal's ${...} slot, as long as every branch
|
|
28
|
+
* reached is itself a plain string literal:
|
|
29
|
+
* className={isActive ? 'pds-button--navbar' : 'other-class'}
|
|
30
|
+
* className={`base ${isEmployee ? 'pds-lead-text' : 'pds-ts-9xl'}`}
|
|
31
|
+
* className={isOpen && 'pds-button--navbar'}
|
|
32
|
+
*
|
|
33
|
+
* Does not handle truly dynamic class names (a variable, function call, or
|
|
34
|
+
* any branch of a ternary/logical expression that isn't itself a string
|
|
35
|
+
* literal).
|
|
36
|
+
*
|
|
24
37
|
* Usage:
|
|
25
38
|
* npx jscodeshift -t codemods/v1-to-v2/transforms/css-class-removals.js \
|
|
26
39
|
* --extensions=tsx,ts,jsx,js \
|
|
@@ -30,6 +43,7 @@
|
|
|
30
43
|
*/
|
|
31
44
|
|
|
32
45
|
const path = require('path');
|
|
46
|
+
|
|
33
47
|
const mappings = require(path.join(__dirname, '../mappings.json'));
|
|
34
48
|
|
|
35
49
|
// Extract the first pds-* class name from a replacement string, if present.
|
|
@@ -71,11 +85,11 @@ function buildLookups() {
|
|
|
71
85
|
return { renames, removals, flags };
|
|
72
86
|
}
|
|
73
87
|
|
|
74
|
-
const {
|
|
88
|
+
const { flags: FLAGS, removals: REMOVALS, renames: RENAMES } = buildLookups();
|
|
75
89
|
|
|
76
90
|
// Process a space-separated class string.
|
|
77
91
|
// Returns { result, changed, flagged[] }
|
|
78
|
-
function processClasses(classString
|
|
92
|
+
function processClasses(classString) {
|
|
79
93
|
let changed = false;
|
|
80
94
|
const flagged = [];
|
|
81
95
|
|
|
@@ -107,6 +121,52 @@ function processClasses(classString, filePath) {
|
|
|
107
121
|
return { result: normalised, changed: didChange, flagged };
|
|
108
122
|
}
|
|
109
123
|
|
|
124
|
+
// Recursively process any string-literal branch reachable from `node`
|
|
125
|
+
// without crossing into a non-literal value — i.e. it descends into
|
|
126
|
+
// ternaries (ConditionalExpression) and logical expressions (&&/||),
|
|
127
|
+
// processing each branch that is itself a string literal, but leaves
|
|
128
|
+
// anything else (a variable, a function call, a member expression, ...)
|
|
129
|
+
// untouched since its value isn't knowable statically. Returns the
|
|
130
|
+
// (possibly replaced) node, whether anything changed, and any flagged
|
|
131
|
+
// classes found along the way. Does not mutate in place, so callers must
|
|
132
|
+
// reassign the returned node back onto its parent.
|
|
133
|
+
function processStringLiteralBranches(node, j) {
|
|
134
|
+
if (!node) return { node, changed: false, flagged: [] };
|
|
135
|
+
|
|
136
|
+
if (node.type === 'StringLiteral' || node.type === 'Literal') {
|
|
137
|
+
const { changed, flagged, result } = processClasses(node.value);
|
|
138
|
+
return changed
|
|
139
|
+
? { node: j.stringLiteral(result), changed: true, flagged }
|
|
140
|
+
: { node, changed: false, flagged };
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (node.type === 'ConditionalExpression') {
|
|
144
|
+
const consequent = processStringLiteralBranches(node.consequent, j);
|
|
145
|
+
const alternate = processStringLiteralBranches(node.alternate, j);
|
|
146
|
+
if (consequent.changed) node.consequent = consequent.node;
|
|
147
|
+
if (alternate.changed) node.alternate = alternate.node;
|
|
148
|
+
return {
|
|
149
|
+
node,
|
|
150
|
+
changed: consequent.changed || alternate.changed,
|
|
151
|
+
flagged: [...consequent.flagged, ...alternate.flagged],
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if (node.type === 'LogicalExpression') {
|
|
156
|
+
const left = processStringLiteralBranches(node.left, j);
|
|
157
|
+
const right = processStringLiteralBranches(node.right, j);
|
|
158
|
+
if (left.changed) node.left = left.node;
|
|
159
|
+
if (right.changed) node.right = right.node;
|
|
160
|
+
return {
|
|
161
|
+
node,
|
|
162
|
+
changed: left.changed || right.changed,
|
|
163
|
+
flagged: [...left.flagged, ...right.flagged],
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
return { node, changed: false, flagged: [] };
|
|
168
|
+
}
|
|
169
|
+
|
|
110
170
|
module.exports = function transform(file, api) {
|
|
111
171
|
const j = api.jscodeshift;
|
|
112
172
|
const root = j(file.source);
|
|
@@ -115,18 +175,15 @@ module.exports = function transform(file, api) {
|
|
|
115
175
|
let changed = false;
|
|
116
176
|
|
|
117
177
|
root
|
|
118
|
-
.find(j.JSXAttribute
|
|
178
|
+
.find(j.JSXAttribute)
|
|
179
|
+
.filter((nodePath) => /className$/i.test(nodePath.node.name.name))
|
|
119
180
|
.forEach((nodePath) => {
|
|
120
181
|
const { value } = nodePath.node;
|
|
121
182
|
if (!value) return;
|
|
122
183
|
|
|
123
184
|
// className="pds-button--navbar foo"
|
|
124
185
|
if (value.type === 'StringLiteral') {
|
|
125
|
-
const {
|
|
126
|
-
result,
|
|
127
|
-
changed: c,
|
|
128
|
-
flagged,
|
|
129
|
-
} = processClasses(value.value, file.path);
|
|
186
|
+
const { changed: c, flagged, result } = processClasses(value.value);
|
|
130
187
|
if (c) {
|
|
131
188
|
nodePath.node.value = j.stringLiteral(result);
|
|
132
189
|
changed = true;
|
|
@@ -138,29 +195,14 @@ module.exports = function transform(file, api) {
|
|
|
138
195
|
if (value.type !== 'JSXExpressionContainer') return;
|
|
139
196
|
const { expression } = value;
|
|
140
197
|
|
|
141
|
-
// className={
|
|
142
|
-
if (expression.type === 'StringLiteral') {
|
|
143
|
-
const {
|
|
144
|
-
result,
|
|
145
|
-
changed: c,
|
|
146
|
-
flagged,
|
|
147
|
-
} = processClasses(expression.value, file.path);
|
|
148
|
-
if (c) {
|
|
149
|
-
value.expression = j.stringLiteral(result);
|
|
150
|
-
changed = true;
|
|
151
|
-
}
|
|
152
|
-
flagged.forEach((cls) => allFlagged.push(cls));
|
|
153
|
-
return;
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
// className={`pds-button--navbar ${x}`}
|
|
198
|
+
// className={`pds-button--navbar ${isActive ? 'a' : 'b'}`}
|
|
157
199
|
if (expression.type === 'TemplateLiteral') {
|
|
158
200
|
expression.quasis.forEach((quasi) => {
|
|
159
201
|
const {
|
|
160
|
-
result,
|
|
161
202
|
changed: c,
|
|
162
203
|
flagged,
|
|
163
|
-
|
|
204
|
+
result,
|
|
205
|
+
} = processClasses(quasi.value.raw);
|
|
164
206
|
if (c) {
|
|
165
207
|
quasi.value.raw = result;
|
|
166
208
|
quasi.value.cooked = result;
|
|
@@ -168,7 +210,35 @@ module.exports = function transform(file, api) {
|
|
|
168
210
|
}
|
|
169
211
|
flagged.forEach((cls) => allFlagged.push(cls));
|
|
170
212
|
});
|
|
213
|
+
|
|
214
|
+
expression.expressions.forEach((expr, index) => {
|
|
215
|
+
const {
|
|
216
|
+
changed: c,
|
|
217
|
+
flagged,
|
|
218
|
+
node: processed,
|
|
219
|
+
} = processStringLiteralBranches(expr, j);
|
|
220
|
+
if (c) {
|
|
221
|
+
expression.expressions[index] = processed;
|
|
222
|
+
changed = true;
|
|
223
|
+
}
|
|
224
|
+
flagged.forEach((cls) => allFlagged.push(cls));
|
|
225
|
+
});
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// className={'pds-button--navbar'}
|
|
230
|
+
// className={isActive ? 'pds-button--navbar' : 'other-class'}
|
|
231
|
+
// className={isOpen && 'pds-button--navbar'}
|
|
232
|
+
const {
|
|
233
|
+
changed: c,
|
|
234
|
+
flagged,
|
|
235
|
+
node: processed,
|
|
236
|
+
} = processStringLiteralBranches(expression, j);
|
|
237
|
+
if (c) {
|
|
238
|
+
value.expression = processed;
|
|
239
|
+
changed = true;
|
|
171
240
|
}
|
|
241
|
+
flagged.forEach((cls) => allFlagged.push(cls));
|
|
172
242
|
});
|
|
173
243
|
|
|
174
244
|
// Emit warnings for classes that need manual attention
|