@juspay/svelte-ui-components 3.2.0 → 3.2.2
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/README.md +55 -2
- package/dist/ThemeSwitcher/ThemeSwitcher.svelte +4 -0
- package/dist-wc/index.js +2047 -841
- package/package.json +20 -5
- package/scripts/codemod/README.md +76 -0
- package/scripts/codemod/cli.ts +206 -0
- package/scripts/codemod/map.ts +118 -0
- package/scripts/codemod/transform.ts +549 -0
- package/scripts/codemod/tsconfig.json +15 -0
- package/scripts/codemod/wc-children.ts +75 -0
- package/scripts/postinstall.mjs +56 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@juspay/svelte-ui-components",
|
|
3
|
-
"version": "3.2.
|
|
3
|
+
"version": "3.2.2",
|
|
4
4
|
"description": "A themeable Svelte 5 UI component library with CSS custom property driven styling",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"svelte",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"package": "svelte-kit sync && svelte-package && publint",
|
|
28
28
|
"prepublishOnly": "npm run build:wc && npm run package",
|
|
29
29
|
"test": "npm run test:integration && npm run test:unit",
|
|
30
|
-
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json && npm run check:codemod && npm run check:migrate",
|
|
30
|
+
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json && npm run check:codemod && npm run check:migrate && npm run check:wc-parity",
|
|
31
31
|
"check:codemod": "tsc -p scripts/codemod/tsconfig.json",
|
|
32
32
|
"codemod": "node scripts/codemod/cli.ts",
|
|
33
33
|
"check:wc": "svelte-check --tsconfig ./tsconfig.wc.json --compiler-warnings \"options_missing_custom_element:ignore\"",
|
|
@@ -40,7 +40,9 @@
|
|
|
40
40
|
"test:visual": "scripts/visual-test.sh",
|
|
41
41
|
"test:visual:update": "scripts/visual-test.sh --update-snapshots",
|
|
42
42
|
"migrate": "node scripts/migrate/cli.ts",
|
|
43
|
-
"check:migrate": "tsc -p scripts/migrate/tsconfig.json"
|
|
43
|
+
"check:migrate": "tsc -p scripts/migrate/tsconfig.json",
|
|
44
|
+
"check:wc-parity": "tsc -p scripts/wc-parity/tsconfig.json",
|
|
45
|
+
"postinstall": "node scripts/postinstall.mjs"
|
|
44
46
|
},
|
|
45
47
|
"exports": {
|
|
46
48
|
".": {
|
|
@@ -56,13 +58,17 @@
|
|
|
56
58
|
"dist",
|
|
57
59
|
"!dist/**/*.test.*",
|
|
58
60
|
"!dist/**/*.spec.*",
|
|
59
|
-
"dist-wc"
|
|
61
|
+
"dist-wc",
|
|
62
|
+
"scripts/codemod",
|
|
63
|
+
"!scripts/codemod/*.test.ts",
|
|
64
|
+
"scripts/postinstall.mjs"
|
|
60
65
|
],
|
|
61
66
|
"peerDependencies": {
|
|
62
67
|
"lottie-web": ">=5.0.0",
|
|
63
68
|
"marked": "^18.0.0",
|
|
64
69
|
"svelte": "^5.41.2",
|
|
65
|
-
"type-decoder": "^2.1.0"
|
|
70
|
+
"type-decoder": "^2.1.0",
|
|
71
|
+
"typescript": ">=5.0.0"
|
|
66
72
|
},
|
|
67
73
|
"peerDependenciesMeta": {
|
|
68
74
|
"lottie-web": {
|
|
@@ -70,6 +76,9 @@
|
|
|
70
76
|
},
|
|
71
77
|
"marked": {
|
|
72
78
|
"optional": true
|
|
79
|
+
},
|
|
80
|
+
"typescript": {
|
|
81
|
+
"optional": true
|
|
73
82
|
}
|
|
74
83
|
},
|
|
75
84
|
"devDependencies": {
|
|
@@ -183,5 +192,11 @@
|
|
|
183
192
|
"replaceText": {
|
|
184
193
|
"(ABC-\\d+)": "[`$1`](https://juspay.atlassian.net/browse/$1)"
|
|
185
194
|
}
|
|
195
|
+
},
|
|
196
|
+
"bin": {
|
|
197
|
+
"sui-codemod": "./scripts/codemod/cli.ts"
|
|
198
|
+
},
|
|
199
|
+
"engines": {
|
|
200
|
+
"node": ">=22.18"
|
|
186
201
|
}
|
|
187
202
|
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# polymorph → SUI consumer migration codemod
|
|
2
|
+
|
|
3
|
+
Also reports the one breaking change in the 4.0.0 custom-element surface:
|
|
4
|
+
`children` is no longer a declared property on `sui-chat-bubble`,
|
|
5
|
+
`sui-draggable` or `sui-resizable`, so assigning it silently loses the content.
|
|
6
|
+
That check runs on every forward migration and in dry runs, reports rather than
|
|
7
|
+
rewrites (moving content into markup is a decision, not a rename), and only
|
|
8
|
+
fires in files that also mention one of the three elements. See
|
|
9
|
+
`wc-children.ts`.
|
|
10
|
+
|
|
11
|
+
This package is published, so consumers can run it without a checkout:
|
|
12
|
+
|
|
13
|
+
```sh
|
|
14
|
+
npx sui-codemod --dry-run ./src
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Mechanically migrates a consumer codebase from `polymorph-ui-components` to
|
|
18
|
+
`@juspay/svelte-ui-components`:
|
|
19
|
+
|
|
20
|
+
- rewrites import/export/dynamic-import specifiers (subpaths like `/wc` are
|
|
21
|
+
preserved), in `.svelte`, `.ts` and `.js` files;
|
|
22
|
+
- renames the 28 synthetic event props (see `map.ts`) on library components in
|
|
23
|
+
`.svelte` markup. All but one differ from the fork only in case; Stepper's
|
|
24
|
+
`onhandlestepclick` becomes `onstepclick`, stepping over the deprecated
|
|
25
|
+
`onhandleStepClick` alias that the casing match would otherwise have picked.
|
|
26
|
+
Renaming is component-aware: `onsort` is renamed on a
|
|
27
|
+
`<Table>` imported from the library (through aliases,
|
|
28
|
+
`import { Table as DataTable }`, namespace imports and
|
|
29
|
+
`<svelte:component this={Table}>`), and left alone on your own components
|
|
30
|
+
and on native elements.
|
|
31
|
+
|
|
32
|
+
## Usage
|
|
33
|
+
|
|
34
|
+
Run from a checkout of this repo (dependencies resolve here), pointing at the
|
|
35
|
+
consumer project. Requires Node >= 22.18 (runs TypeScript directly).
|
|
36
|
+
|
|
37
|
+
```sh
|
|
38
|
+
# preview
|
|
39
|
+
npm run codemod -- --dry-run ../consumer-app/src
|
|
40
|
+
|
|
41
|
+
# apply
|
|
42
|
+
npm run codemod -- ../consumer-app/src
|
|
43
|
+
|
|
44
|
+
# migrate in the other direction (SUI -> polymorph)
|
|
45
|
+
npm run codemod -- --reverse ../consumer-app/src
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Directories are walked recursively; `node_modules`, `.git`, `.svelte-kit`,
|
|
49
|
+
`dist`, `dist-wc`, `build`, `coverage`, `playwright-report` and `test-results`
|
|
50
|
+
are skipped.
|
|
51
|
+
|
|
52
|
+
## What it will not do (by design)
|
|
53
|
+
|
|
54
|
+
Anything it cannot prove safe is reported as a `WARN` with `file:line:column`
|
|
55
|
+
instead of being rewritten — silent wrong rewrites are worse than reported
|
|
56
|
+
skips:
|
|
57
|
+
|
|
58
|
+
- **Spread attributes.** `<Table {...props} />` may carry a renamed prop
|
|
59
|
+
inside `props`; the object is defined elsewhere, so it is warned about, not
|
|
60
|
+
rewritten.
|
|
61
|
+
- **Unresolvable components.** `const Picked = Table;` then
|
|
62
|
+
`<Picked onsort={...} />` — the tag does not resolve to an import, so a
|
|
63
|
+
warning is emitted when it carries a renameable prop name.
|
|
64
|
+
- **Default imports** from the library cannot be resolved to a component.
|
|
65
|
+
- **Semantic renames** — same event, different word — are not casing pairs
|
|
66
|
+
and need a human: Gallery `onclose`→`onDismiss`, `onchange`→`onIndexChange`;
|
|
67
|
+
MediaUpload `onchange`→`onFilesChange`, `onerror`→`onRejected`.
|
|
68
|
+
- Props typed against library types in `<script>` (e.g. an object literal fed
|
|
69
|
+
to a spread) are not rewritten — the spread warning covers the usage site.
|
|
70
|
+
|
|
71
|
+
Shorthand attributes are expanded when renamed (`{onsort}` →
|
|
72
|
+
`onSort={onsort}`), so a `--reverse` round trip restores the original names
|
|
73
|
+
but not the shorthand form.
|
|
74
|
+
|
|
75
|
+
`map.ts` documents how the pair list was derived and verified against both
|
|
76
|
+
libraries' sources.
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
import { existsSync, readdirSync, readFileSync, statSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { join, resolve } from 'node:path';
|
|
3
|
+
import { pathToFileURL } from 'node:url';
|
|
4
|
+
import { parseArgs } from 'node:util';
|
|
5
|
+
import type { Direction } from './map.ts';
|
|
6
|
+
import { transformModuleSpecifiers, transformSvelte } from './transform.ts';
|
|
7
|
+
import type { TransformResult } from './transform.ts';
|
|
8
|
+
import { findChildrenAssignments } from './wc-children.ts';
|
|
9
|
+
|
|
10
|
+
export type CliSummary = {
|
|
11
|
+
readonly exitCode: number;
|
|
12
|
+
readonly filesScanned: number;
|
|
13
|
+
readonly filesChanged: number;
|
|
14
|
+
readonly propsRenamed: number;
|
|
15
|
+
readonly importsRewritten: number;
|
|
16
|
+
readonly warnings: number;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const USAGE = [
|
|
20
|
+
'Usage: node scripts/codemod/cli.ts [--reverse] [--dry-run] <path...>',
|
|
21
|
+
'',
|
|
22
|
+
'Migrates consumer sources from polymorph-ui-components to',
|
|
23
|
+
'@juspay/svelte-ui-components: rewrites import specifiers and renames the',
|
|
24
|
+
'casing-only synthetic event props on library components (see map.ts).',
|
|
25
|
+
'',
|
|
26
|
+
' --reverse migrate @juspay/svelte-ui-components -> polymorph-ui-components',
|
|
27
|
+
' --dry-run print every change as a diff without writing any file',
|
|
28
|
+
' --help show this help',
|
|
29
|
+
'',
|
|
30
|
+
'Paths may be files or directories; directories are walked recursively,',
|
|
31
|
+
'skipping node_modules, .git, .svelte-kit, dist, dist-wc, build, coverage,',
|
|
32
|
+
'playwright-report and test-results.'
|
|
33
|
+
].join('\n');
|
|
34
|
+
|
|
35
|
+
const SKIPPED_DIRECTORIES = new Set([
|
|
36
|
+
'node_modules',
|
|
37
|
+
'.git',
|
|
38
|
+
'.svelte-kit',
|
|
39
|
+
'dist',
|
|
40
|
+
'dist-wc',
|
|
41
|
+
'build',
|
|
42
|
+
'coverage',
|
|
43
|
+
'playwright-report',
|
|
44
|
+
'test-results'
|
|
45
|
+
]);
|
|
46
|
+
|
|
47
|
+
const SCRIPT_FILE = /\.(ts|js|mts|mjs|cts|cjs)$/;
|
|
48
|
+
|
|
49
|
+
function collectFiles(path: string, out: Set<string>): void {
|
|
50
|
+
const stats = statSync(path);
|
|
51
|
+
if (stats.isFile()) {
|
|
52
|
+
if (path.endsWith('.svelte') || SCRIPT_FILE.test(path)) {
|
|
53
|
+
out.add(path);
|
|
54
|
+
}
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
if (!stats.isDirectory()) {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
const entries = [...readdirSync(path, { withFileTypes: true })].sort((a, b) =>
|
|
61
|
+
a.name.localeCompare(b.name)
|
|
62
|
+
);
|
|
63
|
+
for (const entry of entries) {
|
|
64
|
+
if (entry.isDirectory()) {
|
|
65
|
+
if (!SKIPPED_DIRECTORIES.has(entry.name)) {
|
|
66
|
+
collectFiles(join(path, entry.name), out);
|
|
67
|
+
}
|
|
68
|
+
} else if (entry.isFile()) {
|
|
69
|
+
collectFiles(join(path, entry.name), out);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Every edit the transform makes stays on a single line, so old and new text
|
|
76
|
+
* always have the same line count and a pairwise comparison is an exact diff.
|
|
77
|
+
*/
|
|
78
|
+
function printLineDiff(
|
|
79
|
+
file: string,
|
|
80
|
+
before: string,
|
|
81
|
+
after: string,
|
|
82
|
+
log: (line: string) => void
|
|
83
|
+
): void {
|
|
84
|
+
const oldLines = before.split('\n');
|
|
85
|
+
const newLines = after.split('\n');
|
|
86
|
+
log(`--- ${file}`);
|
|
87
|
+
if (oldLines.length !== newLines.length) {
|
|
88
|
+
log(' (line counts differ; showing full replacement)');
|
|
89
|
+
for (const line of oldLines) {
|
|
90
|
+
log(`- ${line}`);
|
|
91
|
+
}
|
|
92
|
+
for (const line of newLines) {
|
|
93
|
+
log(`+ ${line}`);
|
|
94
|
+
}
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
oldLines.forEach((oldLine, index) => {
|
|
98
|
+
const newLine = newLines.at(index) ?? '';
|
|
99
|
+
if (oldLine !== newLine) {
|
|
100
|
+
log(`@@ ${file}:${index + 1}`);
|
|
101
|
+
log(`- ${oldLine}`);
|
|
102
|
+
log(`+ ${newLine}`);
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export function runCodemod(argv: ReadonlyArray<string>, log: (line: string) => void): CliSummary {
|
|
108
|
+
const failure = {
|
|
109
|
+
exitCode: 2,
|
|
110
|
+
filesScanned: 0,
|
|
111
|
+
filesChanged: 0,
|
|
112
|
+
propsRenamed: 0,
|
|
113
|
+
importsRewritten: 0,
|
|
114
|
+
warnings: 0
|
|
115
|
+
};
|
|
116
|
+
let parsed;
|
|
117
|
+
try {
|
|
118
|
+
parsed = parseArgs({
|
|
119
|
+
args: [...argv],
|
|
120
|
+
options: {
|
|
121
|
+
reverse: { type: 'boolean' },
|
|
122
|
+
'dry-run': { type: 'boolean' },
|
|
123
|
+
help: { type: 'boolean' }
|
|
124
|
+
},
|
|
125
|
+
allowPositionals: true,
|
|
126
|
+
strict: true
|
|
127
|
+
});
|
|
128
|
+
} catch (error) {
|
|
129
|
+
log(error instanceof Error ? error.message : String(error));
|
|
130
|
+
log(USAGE);
|
|
131
|
+
return failure;
|
|
132
|
+
}
|
|
133
|
+
if (parsed.values.help === true) {
|
|
134
|
+
log(USAGE);
|
|
135
|
+
return { ...failure, exitCode: 0 };
|
|
136
|
+
}
|
|
137
|
+
if (parsed.positionals.length === 0) {
|
|
138
|
+
log(USAGE);
|
|
139
|
+
return failure;
|
|
140
|
+
}
|
|
141
|
+
for (const path of parsed.positionals) {
|
|
142
|
+
if (!existsSync(path)) {
|
|
143
|
+
log(`path does not exist: ${path}`);
|
|
144
|
+
return failure;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
const direction: Direction = parsed.values.reverse === true ? 'to-poly' : 'to-sui';
|
|
148
|
+
const dryRun = parsed.values['dry-run'] === true;
|
|
149
|
+
|
|
150
|
+
const files = new Set<string>();
|
|
151
|
+
for (const path of parsed.positionals) {
|
|
152
|
+
collectFiles(resolve(path), files);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
let filesChanged = 0;
|
|
156
|
+
let propsRenamed = 0;
|
|
157
|
+
let importsRewritten = 0;
|
|
158
|
+
let warnings = 0;
|
|
159
|
+
for (const file of files) {
|
|
160
|
+
const source = readFileSync(file, 'utf8');
|
|
161
|
+
const result: TransformResult = file.endsWith('.svelte')
|
|
162
|
+
? transformSvelte(source, file, direction)
|
|
163
|
+
: transformModuleSpecifiers(source, file, direction);
|
|
164
|
+
// Reported in both directions and in dry runs: it is a 4.0.0 breaking change
|
|
165
|
+
// to report, not a rename to apply, so it never contributes to filesChanged.
|
|
166
|
+
const childrenWarnings = direction === 'to-sui' ? findChildrenAssignments(source, file) : [];
|
|
167
|
+
for (const warning of [...result.warnings, ...childrenWarnings]) {
|
|
168
|
+
log(`${warning.file}:${warning.line}:${warning.column} WARN ${warning.message}`);
|
|
169
|
+
}
|
|
170
|
+
warnings += result.warnings.length + childrenWarnings.length;
|
|
171
|
+
if (!result.changed) {
|
|
172
|
+
continue;
|
|
173
|
+
}
|
|
174
|
+
filesChanged += 1;
|
|
175
|
+
propsRenamed += result.propsRenamed;
|
|
176
|
+
importsRewritten += result.importsRewritten;
|
|
177
|
+
if (dryRun) {
|
|
178
|
+
printLineDiff(file, source, result.code, log);
|
|
179
|
+
} else {
|
|
180
|
+
writeFileSync(file, result.code);
|
|
181
|
+
log(
|
|
182
|
+
`${file}: ${result.propsRenamed} prop(s) renamed, ${result.importsRewritten} import(s) rewritten`
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
log(
|
|
187
|
+
`${dryRun ? '[dry run] ' : ''}scanned ${files.size} file(s), changed ${filesChanged}, ` +
|
|
188
|
+
`renamed ${propsRenamed} prop(s), rewrote ${importsRewritten} import(s), ${warnings} warning(s)`
|
|
189
|
+
);
|
|
190
|
+
return {
|
|
191
|
+
exitCode: 0,
|
|
192
|
+
filesScanned: files.size,
|
|
193
|
+
filesChanged,
|
|
194
|
+
propsRenamed,
|
|
195
|
+
importsRewritten,
|
|
196
|
+
warnings
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const entryPoint = process.argv.at(1) ?? '';
|
|
201
|
+
if (entryPoint !== '' && import.meta.url === pathToFileURL(entryPoint).href) {
|
|
202
|
+
const summary = runCodemod(process.argv.slice(2), (line) => {
|
|
203
|
+
console.log(line);
|
|
204
|
+
});
|
|
205
|
+
process.exitCode = summary.exitCode;
|
|
206
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Casing-only prop pairs between this library (SUI) and its fork
|
|
3
|
+
* `polymorph-ui-components` (poly). Poly renamed synthesized event props to
|
|
4
|
+
* all-lowercase; these are the props whose names differ ONLY in case between
|
|
5
|
+
* the two libraries.
|
|
6
|
+
*
|
|
7
|
+
* Derived mechanically (not copied from docs): the `$props()` destructure of
|
|
8
|
+
* every component exported by both `src/lib/index.ts` (SUI @ release) and
|
|
9
|
+
* `dist/index.js` of polymorph-ui-components@0.7.0 was parsed with
|
|
10
|
+
* `svelte/compiler`, and a pair was accepted only when
|
|
11
|
+
* `lower(suiName) === lower(polyName)`, the names differ, and neither side
|
|
12
|
+
* also declares the other side's spelling (which would make a rewrite
|
|
13
|
+
* ambiguous or unnecessary). Cross-checked against each component's
|
|
14
|
+
* `properties.ts` / `properties.d.ts`.
|
|
15
|
+
*
|
|
16
|
+
* That derivation is deprecation-blind, and one pair had to be corrected by
|
|
17
|
+
* hand afterwards: where a component keeps a deprecated alias whose lowercase
|
|
18
|
+
* spelling matches the fork's, the alias wins the case-insensitive match and
|
|
19
|
+
* outranks the canonical prop. Stepper is the only such pair. `map.test.ts`
|
|
20
|
+
* now asserts no target is `@deprecated`, so a future alias cannot repeat it.
|
|
21
|
+
*
|
|
22
|
+
* Deliberately excluded (same event, different word — a semantic rename, not
|
|
23
|
+
* a casing pair; these need a human):
|
|
24
|
+
* Gallery: poly `onclose` vs SUI `onDismiss`, poly `onchange` vs SUI `onIndexChange`
|
|
25
|
+
* MediaUpload: poly `onchange` vs SUI `onFilesChange`, poly `onerror` vs SUI `onRejected`
|
|
26
|
+
* Props spelled identically on both sides (e.g. `onkeydown` on ListItem or
|
|
27
|
+
* Modal) are intentionally absent: they need no rewrite. `onkeydown` on Input
|
|
28
|
+
* IS listed because SUI's Input only accepts `onKeyDown`.
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
export type PropPair = {
|
|
32
|
+
readonly component: string;
|
|
33
|
+
readonly sui: string;
|
|
34
|
+
readonly poly: string;
|
|
35
|
+
/**
|
|
36
|
+
* Superseded SUI spellings that still work. Never a rewrite target, but
|
|
37
|
+
* recognized as a source when rewriting back to the fork.
|
|
38
|
+
*/
|
|
39
|
+
readonly suiDeprecatedAliases?: readonly string[];
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export const SUI_PACKAGE = '@juspay/svelte-ui-components';
|
|
43
|
+
export const POLY_PACKAGE = 'polymorph-ui-components';
|
|
44
|
+
|
|
45
|
+
export const PROP_PAIRS: ReadonlyArray<PropPair> = [
|
|
46
|
+
{ component: 'Gallery', sui: 'onDeleteClick', poly: 'ondeleteclick' },
|
|
47
|
+
{ component: 'Gallery', sui: 'onEditClick', poly: 'oneditclick' },
|
|
48
|
+
{ component: 'Gallery', sui: 'onImageClick', poly: 'onimageclick' },
|
|
49
|
+
{ component: 'Gallery', sui: 'onOpen', poly: 'onopen' },
|
|
50
|
+
{ component: 'Input', sui: 'onBlur', poly: 'onblur' },
|
|
51
|
+
{ component: 'Input', sui: 'onClick', poly: 'onclick' },
|
|
52
|
+
{ component: 'Input', sui: 'onFocus', poly: 'onfocus' },
|
|
53
|
+
{ component: 'Input', sui: 'onFocusout', poly: 'onfocusout' },
|
|
54
|
+
{ component: 'Input', sui: 'onInput', poly: 'oninput' },
|
|
55
|
+
{ component: 'Input', sui: 'onKeyDown', poly: 'onkeydown' },
|
|
56
|
+
{ component: 'Input', sui: 'onPaste', poly: 'onpaste' },
|
|
57
|
+
{ component: 'Input', sui: 'onStateChange', poly: 'onstatechange' },
|
|
58
|
+
{ component: 'ListItem', sui: 'oncenterTextClick', poly: 'oncentertextclick' },
|
|
59
|
+
{ component: 'ListItem', sui: 'onitemClick', poly: 'onitemclick' },
|
|
60
|
+
{ component: 'ListItem', sui: 'onleftImageClick', poly: 'onleftimageclick' },
|
|
61
|
+
{ component: 'ListItem', sui: 'onrightImageClick', poly: 'onrightimageclick' },
|
|
62
|
+
{ component: 'ListItem', sui: 'ontopSectionClick', poly: 'ontopsectionclick' },
|
|
63
|
+
{ component: 'MediaUpload', sui: 'onRemove', poly: 'onremove' },
|
|
64
|
+
{ component: 'Modal', sui: 'onheaderLeftImageClick', poly: 'onheaderleftimageclick' },
|
|
65
|
+
{ component: 'Modal', sui: 'onheaderRightImageClick', poly: 'onheaderrightimageclick' },
|
|
66
|
+
{ component: 'Modal', sui: 'onoverlayClick', poly: 'onoverlayclick' },
|
|
67
|
+
{ component: 'Modal', sui: 'onprimaryButtonClick', poly: 'onprimarybuttonclick' },
|
|
68
|
+
{ component: 'Modal', sui: 'onsecondaryButtonClick', poly: 'onsecondarybuttonclick' },
|
|
69
|
+
// The lowercase match here is `onhandleStepClick`, which SUI deprecates in
|
|
70
|
+
// favour of `onstepclick`. Rewriting to the alias would be case-correct and
|
|
71
|
+
// still wrong: it is the one spelling of this event scheduled for removal.
|
|
72
|
+
{
|
|
73
|
+
component: 'Stepper',
|
|
74
|
+
sui: 'onstepclick',
|
|
75
|
+
poly: 'onhandlestepclick',
|
|
76
|
+
suiDeprecatedAliases: ['onhandleStepClick']
|
|
77
|
+
},
|
|
78
|
+
{ component: 'Table', sui: 'onRowClick', poly: 'onrowclick' },
|
|
79
|
+
{ component: 'Table', sui: 'onSort', poly: 'onsort' },
|
|
80
|
+
{ component: 'Toast', sui: 'onToastHide', poly: 'ontoasthide' },
|
|
81
|
+
{ component: 'Toolbar', sui: 'onbackClick', poly: 'onbackclick' }
|
|
82
|
+
];
|
|
83
|
+
|
|
84
|
+
export type Direction = 'to-sui' | 'to-poly';
|
|
85
|
+
|
|
86
|
+
export type DirectionConfig = {
|
|
87
|
+
readonly fromPackage: string;
|
|
88
|
+
readonly toPackage: string;
|
|
89
|
+
/** component name -> (source prop name -> target prop name) */
|
|
90
|
+
readonly renames: ReadonlyMap<string, ReadonlyMap<string, string>>;
|
|
91
|
+
/** every source prop name across all components, for unresolved-tag warnings */
|
|
92
|
+
readonly allFromProps: ReadonlySet<string>;
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
export function directionConfig(direction: Direction): DirectionConfig {
|
|
96
|
+
const renames = new Map<string, Map<string, string>>();
|
|
97
|
+
const allFromProps = new Set<string>();
|
|
98
|
+
for (const pair of PROP_PAIRS) {
|
|
99
|
+
const table = renames.get(pair.component) ?? new Map<string, string>();
|
|
100
|
+
// Deprecated aliases are sources, never targets: a consumer moving to SUI
|
|
101
|
+
// lands on the canonical prop, while one moving back to the fork is
|
|
102
|
+
// recognized whichever spelling they are currently on.
|
|
103
|
+
const sources =
|
|
104
|
+
direction === 'to-sui' ? [pair.poly] : [pair.sui, ...(pair.suiDeprecatedAliases ?? [])];
|
|
105
|
+
const to = direction === 'to-sui' ? pair.sui : pair.poly;
|
|
106
|
+
for (const from of sources) {
|
|
107
|
+
table.set(from, to);
|
|
108
|
+
allFromProps.add(from);
|
|
109
|
+
}
|
|
110
|
+
renames.set(pair.component, table);
|
|
111
|
+
}
|
|
112
|
+
return {
|
|
113
|
+
fromPackage: direction === 'to-sui' ? POLY_PACKAGE : SUI_PACKAGE,
|
|
114
|
+
toPackage: direction === 'to-sui' ? SUI_PACKAGE : POLY_PACKAGE,
|
|
115
|
+
renames,
|
|
116
|
+
allFromProps
|
|
117
|
+
};
|
|
118
|
+
}
|