@juspay/svelte-ui-components 3.5.0 → 4.0.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.
Files changed (152) hide show
  1. package/README.md +25 -16
  2. package/dist/Accordion/Accordion.svelte +1 -5
  3. package/dist/Accordion/properties.d.ts +0 -1
  4. package/dist/AreaChart/AreaChart.svelte +2 -8
  5. package/dist/AreaChart/properties.d.ts +0 -10
  6. package/dist/AttachmentChipRow/AttachmentChipRow.svelte +30 -30
  7. package/dist/AttachmentChipRow/properties.d.ts +6 -6
  8. package/dist/Banner/Banner.svelte +1 -5
  9. package/dist/Banner/properties.d.ts +0 -1
  10. package/dist/BarChart/BarChart.svelte +7 -13
  11. package/dist/BarChart/properties.d.ts +1 -9
  12. package/dist/Book/Book.svelte +1 -5
  13. package/dist/Book/properties.d.ts +0 -1
  14. package/dist/Calendar/Calendar.svelte +3 -11
  15. package/dist/Calendar/properties.d.ts +0 -11
  16. package/dist/Chat/Chat.svelte +9 -29
  17. package/dist/Chat/properties.d.ts +0 -9
  18. package/dist/Chat/types.d.ts +7 -0
  19. package/dist/ChatBubble/ChatBubble.svelte +3 -11
  20. package/dist/ChatBubble/properties.d.ts +0 -3
  21. package/dist/ChatComposer/ChatComposer.svelte +18 -44
  22. package/dist/ChatComposer/properties.d.ts +0 -12
  23. package/dist/ChatHeader/ChatHeader.svelte +1 -5
  24. package/dist/ChatHeader/properties.d.ts +0 -1
  25. package/dist/ChatMessage/ChatMessage.svelte +87 -11
  26. package/dist/ChatMessage/properties.d.ts +18 -3
  27. package/dist/ChatMessageList/ChatMessageList.svelte +5 -11
  28. package/dist/ChatMessageList/properties.d.ts +0 -6
  29. package/dist/ChatSuggestions/ChatSuggestions.svelte +1 -5
  30. package/dist/ChatSuggestions/properties.d.ts +0 -1
  31. package/dist/CheckListItem/CheckListItem.svelte +1 -5
  32. package/dist/CheckListItem/properties.d.ts +0 -1
  33. package/dist/Checkbox/Checkbox.svelte +1 -5
  34. package/dist/Checkbox/properties.d.ts +0 -1
  35. package/dist/ChipInput/ChipInput.svelte +10 -20
  36. package/dist/ChipInput/properties.d.ts +0 -4
  37. package/dist/Choicebox/Choicebox.svelte +1 -5
  38. package/dist/Choicebox/properties.d.ts +0 -1
  39. package/dist/ColorPicker/ColorPicker.svelte +4 -10
  40. package/dist/ColorPicker/properties.d.ts +0 -2
  41. package/dist/Combobox/Combobox.svelte +15 -33
  42. package/dist/Combobox/properties.d.ts +0 -8
  43. package/dist/CommandMenu/CommandMenu.svelte +2 -8
  44. package/dist/CommandMenu/properties.d.ts +0 -2
  45. package/dist/ContextMenu/ContextMenu.svelte +3 -11
  46. package/dist/ContextMenu/properties.d.ts +0 -3
  47. package/dist/DateRangePicker/DateRangePicker.svelte +6 -20
  48. package/dist/DateRangePicker/properties.d.ts +0 -19
  49. package/dist/Draggable/Draggable.svelte +7 -7
  50. package/dist/Draggable/properties.d.ts +3 -3
  51. package/dist/DualAxisBarChart/DualAxisBarChart.svelte +1 -5
  52. package/dist/DualAxisBarChart/properties.d.ts +0 -4
  53. package/dist/FileDropzoneTrigger/FileDropzoneTrigger.svelte +1 -5
  54. package/dist/FileDropzoneTrigger/properties.d.ts +0 -1
  55. package/dist/FileInput/FileInput.svelte +2 -8
  56. package/dist/FileInput/properties.d.ts +0 -2
  57. package/dist/FunnelChart/FunnelChart.svelte +2 -8
  58. package/dist/FunnelChart/properties.d.ts +0 -8
  59. package/dist/Gallery/Gallery.svelte +15 -15
  60. package/dist/Gallery/properties.d.ts +6 -6
  61. package/dist/HITL/HITL.svelte +10 -8
  62. package/dist/HITL/properties.d.ts +2 -2
  63. package/dist/IframeViewer/IframeViewer.svelte +3 -2
  64. package/dist/IframeViewer/properties.d.ts +1 -1
  65. package/dist/Img/Img.svelte +119 -39
  66. package/dist/Img/properties.d.ts +0 -1
  67. package/dist/Input/Input.svelte +22 -37
  68. package/dist/Input/properties.d.ts +3 -10
  69. package/dist/InputButton/InputButton.svelte +2 -2
  70. package/dist/LineChart/LineChart.svelte +4 -10
  71. package/dist/LineChart/properties.d.ts +1 -11
  72. package/dist/ListItem/ListItem.svelte +10 -22
  73. package/dist/ListItem/properties.d.ts +5 -10
  74. package/dist/LottiePlayer/LottiePlayer.svelte +2 -8
  75. package/dist/LottiePlayer/properties.d.ts +0 -2
  76. package/dist/MediaPlayer/MediaPlayer.svelte +1 -5
  77. package/dist/MediaPlayer/properties.d.ts +0 -1
  78. package/dist/MediaUpload/MediaUpload.svelte +6 -6
  79. package/dist/MediaUpload/properties.d.ts +3 -3
  80. package/dist/Menu/Menu.svelte +3 -11
  81. package/dist/Menu/properties.d.ts +0 -3
  82. package/dist/Modal/Modal.svelte +15 -31
  83. package/dist/Modal/properties.d.ts +5 -11
  84. package/dist/Pagination/Pagination.svelte +3 -7
  85. package/dist/Pagination/properties.d.ts +1 -2
  86. package/dist/PieChart/PieChart.svelte +4 -10
  87. package/dist/PieChart/properties.d.ts +1 -9
  88. package/dist/Pill/Pill.svelte +1 -5
  89. package/dist/Pill/properties.d.ts +0 -1
  90. package/dist/Radio/Radio.svelte +1 -5
  91. package/dist/Radio/properties.d.ts +0 -1
  92. package/dist/Resizable/Resizable.svelte +3 -11
  93. package/dist/Resizable/properties.d.ts +0 -3
  94. package/dist/SankeyChart/SankeyChart.svelte +4 -14
  95. package/dist/SankeyChart/properties.d.ts +0 -12
  96. package/dist/Scroller/Scroller.svelte +1 -5
  97. package/dist/Scroller/properties.d.ts +0 -1
  98. package/dist/Select/Select.svelte +3 -11
  99. package/dist/Select/properties.d.ts +0 -3
  100. package/dist/Sheet/Sheet.svelte +3 -11
  101. package/dist/Sheet/properties.d.ts +0 -3
  102. package/dist/Slider/Slider.svelte +2 -8
  103. package/dist/Slider/properties.d.ts +0 -2
  104. package/dist/Snippet/Snippet.svelte +1 -5
  105. package/dist/Snippet/properties.d.ts +0 -1
  106. package/dist/SplitButton/SplitButton.svelte +1 -5
  107. package/dist/SplitButton/properties.d.ts +0 -1
  108. package/dist/SplitInput/SplitInput.svelte +6 -14
  109. package/dist/SplitInput/properties.d.ts +0 -3
  110. package/dist/StatCard/StatCard.svelte +2 -2
  111. package/dist/StatCard/properties.d.ts +1 -1
  112. package/dist/Status/Status.svelte +2 -6
  113. package/dist/Status/properties.d.ts +1 -2
  114. package/dist/Stepper/Stepper.svelte +2 -12
  115. package/dist/Stepper/properties.d.ts +1 -14
  116. package/dist/Table/BuiltinCell.svelte +1 -1
  117. package/dist/Table/Table.svelte +16 -17
  118. package/dist/Table/properties.d.ts +3 -35
  119. package/dist/Tabs/Tabs.svelte +2 -8
  120. package/dist/Tabs/properties.d.ts +0 -2
  121. package/dist/TaskList/TaskList.svelte +1 -4
  122. package/dist/TaskList/properties.d.ts +0 -1
  123. package/dist/ThemeSwitcher/ThemeSwitcher.svelte +1 -5
  124. package/dist/ThemeSwitcher/properties.d.ts +0 -1
  125. package/dist/ThinkingIndicator/ThinkingIndicator.svelte +4 -10
  126. package/dist/ThinkingIndicator/properties.d.ts +1 -3
  127. package/dist/Toast/Toast.svelte +2 -2
  128. package/dist/Toast/properties.d.ts +1 -1
  129. package/dist/Toggle/Toggle.svelte +1 -5
  130. package/dist/Toggle/properties.d.ts +0 -1
  131. package/dist/ToolCallLog/ToolCallLog.svelte +1 -10
  132. package/dist/ToolCallLog/properties.d.ts +0 -1
  133. package/dist/Toolbar/Toolbar.svelte +2 -6
  134. package/dist/Toolbar/properties.d.ts +1 -2
  135. package/dist/TypewriterText/TypewriterText.svelte +3 -3
  136. package/dist/TypewriterText/properties.d.ts +1 -1
  137. package/dist-codemod/bin.js +32 -0
  138. package/dist-codemod/cli.js +175 -0
  139. package/dist-codemod/legacy-pairs.js +228 -0
  140. package/dist-codemod/transform.js +278 -0
  141. package/dist-codemod/wc-children.js +66 -0
  142. package/dist-wc/index.js +4458 -5127
  143. package/package.json +9 -7
  144. package/scripts/postinstall.mjs +14 -12
  145. package/dist/deprecation.d.ts +0 -42
  146. package/dist/deprecation.js +0 -57
  147. package/scripts/codemod/README.md +0 -76
  148. package/scripts/codemod/cli.ts +0 -206
  149. package/scripts/codemod/map.ts +0 -118
  150. package/scripts/codemod/transform.ts +0 -549
  151. package/scripts/codemod/tsconfig.json +0 -15
  152. package/scripts/codemod/wc-children.ts +0 -75
@@ -1,549 +0,0 @@
1
- import ts from 'typescript';
2
- import { parse } from 'svelte/compiler';
3
- import type { AST } from 'svelte/compiler';
4
- import { directionConfig } from './map.ts';
5
- import type { Direction, DirectionConfig } from './map.ts';
6
-
7
- export type TransformWarning = {
8
- readonly file: string;
9
- readonly line: number;
10
- readonly column: number;
11
- readonly message: string;
12
- };
13
-
14
- export type TransformResult = {
15
- readonly code: string;
16
- readonly changed: boolean;
17
- readonly propsRenamed: number;
18
- readonly importsRewritten: number;
19
- readonly warnings: ReadonlyArray<TransformWarning>;
20
- };
21
-
22
- type Edit = { readonly start: number; readonly end: number; readonly text: string };
23
-
24
- type Program = AST.Script['content'];
25
- type LibraryElement = AST.Component | AST.SvelteComponent;
26
-
27
- /**
28
- * estree nodes inside `<script>` are not typed with offsets, but the acorn
29
- * parser svelte uses attaches them at runtime; recover them by narrowing.
30
- */
31
- function rangeOf(node: object): { readonly start: number; readonly end: number } | null {
32
- if (
33
- 'start' in node &&
34
- typeof node.start === 'number' &&
35
- 'end' in node &&
36
- typeof node.end === 'number'
37
- ) {
38
- return { start: node.start, end: node.end };
39
- }
40
- return null;
41
- }
42
-
43
- function positionOf(source: string, offset: number): { line: number; column: number } {
44
- const before = source.slice(0, offset);
45
- const line = (before.match(/\n/g) ?? []).length + 1;
46
- return { line, column: offset - before.lastIndexOf('\n') };
47
- }
48
-
49
- function applyEdits(source: string, edits: ReadonlyArray<Edit>): string {
50
- const ordered = [...edits].sort((a, b) => b.start - a.start);
51
- return ordered.reduce(
52
- (code, edit) => code.slice(0, edit.start) + edit.text + code.slice(edit.end),
53
- source
54
- );
55
- }
56
-
57
- type Binding =
58
- | { readonly kind: 'component'; readonly exported: string }
59
- | { readonly kind: 'namespace' }
60
- | { readonly kind: 'other' };
61
-
62
- type Scan = {
63
- readonly edits: Edit[];
64
- readonly warnings: TransformWarning[];
65
- readonly bindings: Map<string, Binding>;
66
- importsRewritten: number;
67
- importsFromPackage: boolean;
68
- propsRenamed: number;
69
- };
70
-
71
- function packageSubpath(specifier: string, fromPackage: string): string | null {
72
- if (specifier === fromPackage) {
73
- return '';
74
- }
75
- if (specifier.startsWith(`${fromPackage}/`)) {
76
- return specifier.slice(fromPackage.length);
77
- }
78
- return null;
79
- }
80
-
81
- function rewriteSpecifierEdit(
82
- source: string,
83
- literal: object,
84
- value: string,
85
- config: DirectionConfig
86
- ): Edit | null {
87
- const subpath = packageSubpath(value, config.fromPackage);
88
- if (subpath === null) {
89
- return null;
90
- }
91
- const range = rangeOf(literal);
92
- if (range === null) {
93
- return null;
94
- }
95
- const quote = source.charAt(range.start);
96
- return {
97
- start: range.start,
98
- end: range.end,
99
- text: `${quote}${config.toPackage}${subpath}${quote}`
100
- };
101
- }
102
-
103
- /** Deep-walk an estree subtree for dynamic `import('...')` of the source package. */
104
- function collectDynamicImportEdits(
105
- source: string,
106
- node: unknown,
107
- config: DirectionConfig,
108
- scan: Scan
109
- ): void {
110
- if (Array.isArray(node)) {
111
- for (const item of node) {
112
- collectDynamicImportEdits(source, item, config, scan);
113
- }
114
- return;
115
- }
116
- if (typeof node !== 'object' || node === null) {
117
- return;
118
- }
119
- if ('type' in node && node.type === 'ImportExpression' && 'source' in node) {
120
- const literal = node.source;
121
- if (
122
- typeof literal === 'object' &&
123
- literal !== null &&
124
- 'type' in literal &&
125
- literal.type === 'Literal' &&
126
- 'value' in literal &&
127
- typeof literal.value === 'string'
128
- ) {
129
- const edit = rewriteSpecifierEdit(source, literal, literal.value, config);
130
- if (edit !== null) {
131
- scan.edits.push(edit);
132
- scan.importsRewritten += 1;
133
- scan.importsFromPackage = true;
134
- }
135
- }
136
- }
137
- for (const value of Object.values(node)) {
138
- collectDynamicImportEdits(source, value, config, scan);
139
- }
140
- }
141
-
142
- function scanScript(
143
- source: string,
144
- file: string,
145
- program: Program,
146
- config: DirectionConfig,
147
- scan: Scan
148
- ): void {
149
- for (const statement of program.body) {
150
- if (
151
- (statement.type === 'ImportDeclaration' ||
152
- statement.type === 'ExportNamedDeclaration' ||
153
- statement.type === 'ExportAllDeclaration') &&
154
- statement.source != null &&
155
- typeof statement.source.value === 'string'
156
- ) {
157
- const edit = rewriteSpecifierEdit(source, statement.source, statement.source.value, config);
158
- if (edit !== null) {
159
- scan.edits.push(edit);
160
- scan.importsRewritten += 1;
161
- }
162
- }
163
- if (statement.type !== 'ImportDeclaration') {
164
- collectDynamicImportEdits(source, statement, config, scan);
165
- continue;
166
- }
167
- const specifier = typeof statement.source.value === 'string' ? statement.source.value : '';
168
- const fromPackage = packageSubpath(specifier, config.fromPackage) !== null;
169
- if (fromPackage) {
170
- scan.importsFromPackage = true;
171
- }
172
- for (const spec of statement.specifiers) {
173
- if (spec.type === 'ImportSpecifier') {
174
- const exported =
175
- spec.imported.type === 'Identifier'
176
- ? spec.imported.name
177
- : typeof spec.imported.value === 'string'
178
- ? spec.imported.value
179
- : null;
180
- scan.bindings.set(
181
- spec.local.name,
182
- fromPackage && exported !== null ? { kind: 'component', exported } : { kind: 'other' }
183
- );
184
- } else if (spec.type === 'ImportNamespaceSpecifier') {
185
- scan.bindings.set(spec.local.name, fromPackage ? { kind: 'namespace' } : { kind: 'other' });
186
- } else {
187
- scan.bindings.set(spec.local.name, { kind: 'other' });
188
- if (fromPackage) {
189
- const at = positionOf(source, rangeOf(spec)?.start ?? 0);
190
- scan.warnings.push({
191
- file,
192
- line: at.line,
193
- column: at.column,
194
- message: `default import from '${specifier}' cannot be resolved to a component; its usages are not rewritten`
195
- });
196
- }
197
- }
198
- }
199
- }
200
- }
201
-
202
- type Resolution =
203
- | { readonly kind: 'library'; readonly component: string }
204
- | { readonly kind: 'other' }
205
- | { readonly kind: 'unresolved' };
206
-
207
- function resolveTag(tagName: string, bindings: ReadonlyMap<string, Binding>): Resolution {
208
- const segments = tagName.split('.');
209
- const head = segments.at(0);
210
- if (typeof head !== 'string' || head.length === 0) {
211
- return { kind: 'unresolved' };
212
- }
213
- const binding = bindings.get(head) ?? null;
214
- if (binding === null) {
215
- return { kind: 'unresolved' };
216
- }
217
- if (segments.length === 1) {
218
- return binding.kind === 'component'
219
- ? { kind: 'library', component: binding.exported }
220
- : { kind: 'other' };
221
- }
222
- const member = segments.at(1);
223
- if (segments.length === 2 && binding.kind === 'namespace' && typeof member === 'string') {
224
- return { kind: 'library', component: member };
225
- }
226
- return { kind: 'other' };
227
- }
228
-
229
- function resolveThisExpression(
230
- expression: AST.SvelteComponent['expression'],
231
- bindings: ReadonlyMap<string, Binding>
232
- ): Resolution {
233
- if (expression.type === 'Identifier') {
234
- return resolveTag(expression.name, bindings);
235
- }
236
- if (
237
- expression.type === 'MemberExpression' &&
238
- !expression.computed &&
239
- expression.object.type === 'Identifier' &&
240
- expression.property.type === 'Identifier'
241
- ) {
242
- return resolveTag(`${expression.object.name}.${expression.property.name}`, bindings);
243
- }
244
- return { kind: 'unresolved' };
245
- }
246
-
247
- function renameAttributes(
248
- source: string,
249
- file: string,
250
- tagLabel: string,
251
- component: string,
252
- element: LibraryElement,
253
- config: DirectionConfig,
254
- scan: Scan
255
- ): void {
256
- const table = config.renames.get(component) ?? null;
257
- if (table === null) {
258
- return;
259
- }
260
- const presentNames = new Set<string>();
261
- for (const attribute of element.attributes) {
262
- if (attribute.type === 'Attribute') {
263
- presentNames.add(attribute.name);
264
- }
265
- }
266
- for (const attribute of element.attributes) {
267
- if (attribute.type === 'SpreadAttribute') {
268
- const at = positionOf(source, attribute.start);
269
- const renameable = [...table.keys()].sort().join(', ');
270
- scan.warnings.push({
271
- file,
272
- line: at.line,
273
- column: at.column,
274
- message: `spread attribute on <${tagLabel}> (${component}) may carry renamed props (${renameable}) — not rewritten, review manually`
275
- });
276
- continue;
277
- }
278
- if (attribute.type !== 'Attribute') {
279
- continue;
280
- }
281
- const target = table.get(attribute.name) ?? null;
282
- if (target === null) {
283
- continue;
284
- }
285
- if (presentNames.has(target)) {
286
- const at = positionOf(source, attribute.start);
287
- scan.warnings.push({
288
- file,
289
- line: at.line,
290
- column: at.column,
291
- message: `target prop '${target}' already present on <${tagLabel}> (${component}); '${attribute.name}' left as-is — review manually`
292
- });
293
- continue;
294
- }
295
- if (source.charAt(attribute.start) === '{') {
296
- // Shorthand `{prop}`: expand to `newName={prop}`, keeping the local identifier.
297
- scan.edits.push({
298
- start: attribute.start,
299
- end: attribute.end,
300
- text: `${target}={${attribute.name}}`
301
- });
302
- scan.propsRenamed += 1;
303
- continue;
304
- }
305
- const nameEnd = attribute.start + attribute.name.length;
306
- if (source.slice(attribute.start, nameEnd) !== attribute.name) {
307
- const at = positionOf(source, attribute.start);
308
- scan.warnings.push({
309
- file,
310
- line: at.line,
311
- column: at.column,
312
- message: `could not locate attribute name '${attribute.name}' on <${tagLabel}> in source text — not rewritten`
313
- });
314
- continue;
315
- }
316
- scan.edits.push({ start: attribute.start, end: nameEnd, text: target });
317
- scan.propsRenamed += 1;
318
- }
319
- }
320
-
321
- function warnUnresolvedElement(
322
- source: string,
323
- file: string,
324
- tagLabel: string,
325
- element: LibraryElement,
326
- config: DirectionConfig,
327
- scan: Scan
328
- ): void {
329
- const suspicious = element.attributes
330
- .filter((attribute) => attribute.type === 'Attribute')
331
- .map((attribute) => attribute.name)
332
- .filter((name) => config.allFromProps.has(name));
333
- if (suspicious.length === 0) {
334
- return;
335
- }
336
- const at = positionOf(source, element.start);
337
- scan.warnings.push({
338
- file,
339
- line: at.line,
340
- column: at.column,
341
- message: `<${tagLabel}> is not resolvable to an import but has renameable prop(s) ${suspicious.join(', ')} — not rewritten, review manually`
342
- });
343
- }
344
-
345
- function walkFragment(fragment: AST.Fragment, visit: (element: LibraryElement) => void): void {
346
- for (const node of fragment.nodes) {
347
- if (node.type === 'Component' || node.type === 'SvelteComponent') {
348
- visit(node);
349
- }
350
- // Structural recursion over every fragment-bearing property covers all
351
- // element and block kinds (including future ones) without enumerating them.
352
- if ('fragment' in node && typeof node.fragment === 'object') {
353
- walkFragment(node.fragment, visit);
354
- }
355
- if ('body' in node && typeof node.body === 'object') {
356
- walkFragment(node.body, visit);
357
- }
358
- if ('consequent' in node) {
359
- walkFragment(node.consequent, visit);
360
- }
361
- if ('alternate' in node && node.alternate !== null) {
362
- walkFragment(node.alternate, visit);
363
- }
364
- if ('fallback' in node) {
365
- const fallback = node.fallback ?? null;
366
- if (fallback !== null) {
367
- walkFragment(fallback, visit);
368
- }
369
- }
370
- if ('pending' in node && node.pending !== null) {
371
- walkFragment(node.pending, visit);
372
- }
373
- if ('then' in node && node.then !== null) {
374
- walkFragment(node.then, visit);
375
- }
376
- if ('catch' in node && node.catch !== null) {
377
- walkFragment(node.catch, visit);
378
- }
379
- }
380
- }
381
-
382
- export function transformSvelte(
383
- source: string,
384
- file: string,
385
- direction: Direction
386
- ): TransformResult {
387
- const config = directionConfig(direction);
388
- let root: AST.Root;
389
- try {
390
- root = parse(source, { modern: true, filename: file });
391
- } catch (error) {
392
- const message = error instanceof Error ? error.message : String(error);
393
- return {
394
- code: source,
395
- changed: false,
396
- propsRenamed: 0,
397
- importsRewritten: 0,
398
- warnings: [{ file, line: 1, column: 1, message: `could not parse file: ${message}` }]
399
- };
400
- }
401
- const scan: Scan = {
402
- edits: [],
403
- warnings: [],
404
- bindings: new Map(),
405
- importsRewritten: 0,
406
- importsFromPackage: false,
407
- propsRenamed: 0
408
- };
409
- const instance = root.instance ?? null;
410
- if (instance !== null) {
411
- scanScript(source, file, instance.content, config, scan);
412
- }
413
- const moduleScript = root.module ?? null;
414
- if (moduleScript !== null) {
415
- scanScript(source, file, moduleScript.content, config, scan);
416
- }
417
- walkFragment(root.fragment, (element) => {
418
- const resolution =
419
- element.type === 'Component'
420
- ? resolveTag(element.name, scan.bindings)
421
- : resolveThisExpression(element.expression, scan.bindings);
422
- const tagLabel = element.type === 'Component' ? element.name : 'svelte:component';
423
- if (resolution.kind === 'library') {
424
- renameAttributes(source, file, tagLabel, resolution.component, element, config, scan);
425
- } else if (resolution.kind === 'unresolved' && scan.importsFromPackage) {
426
- warnUnresolvedElement(source, file, tagLabel, element, config, scan);
427
- }
428
- });
429
- const code = applyEdits(source, scan.edits);
430
- return {
431
- code,
432
- changed: code !== source,
433
- propsRenamed: scan.propsRenamed,
434
- importsRewritten: scan.importsRewritten,
435
- warnings: scan.warnings
436
- };
437
- }
438
-
439
- function scriptKindFor(file: string): ts.ScriptKind {
440
- if (file.endsWith('.tsx')) {
441
- return ts.ScriptKind.TSX;
442
- }
443
- if (file.endsWith('.jsx')) {
444
- return ts.ScriptKind.JSX;
445
- }
446
- if (file.endsWith('.js') || file.endsWith('.mjs') || file.endsWith('.cjs')) {
447
- return ts.ScriptKind.JS;
448
- }
449
- return ts.ScriptKind.TS;
450
- }
451
-
452
- /** The module specifier a node carries, or null if it is not an import site. */
453
- function specifierOf(node: ts.Node): ts.StringLiteralLike | null {
454
- if (ts.isImportDeclaration(node) && ts.isStringLiteralLike(node.moduleSpecifier)) {
455
- return node.moduleSpecifier;
456
- }
457
- if (ts.isExportDeclaration(node)) {
458
- const specifier = node.moduleSpecifier;
459
- if (typeof specifier !== 'undefined' && ts.isStringLiteralLike(specifier)) {
460
- return specifier;
461
- }
462
- return null;
463
- }
464
- // `import('x')` and `require('x')`.
465
- if (ts.isCallExpression(node) && node.arguments.length > 0) {
466
- const callee = node.expression;
467
- const isImport = callee.kind === ts.SyntaxKind.ImportKeyword;
468
- const isRequire = ts.isIdentifier(callee) && callee.text === 'require';
469
- const first = node.arguments[0];
470
- if ((isImport || isRequire) && ts.isStringLiteralLike(first)) {
471
- return first;
472
- }
473
- return null;
474
- }
475
- // `import X = require('x')`. TS-only syntax whose specifier hangs off an
476
- // ExternalModuleReference rather than a call expression.
477
- if (ts.isImportEqualsDeclaration(node) && ts.isExternalModuleReference(node.moduleReference)) {
478
- const expression = node.moduleReference.expression;
479
- if (ts.isStringLiteralLike(expression)) {
480
- return expression;
481
- }
482
- return null;
483
- }
484
- // `import('x').Foo` in type position.
485
- if (ts.isImportTypeNode(node) && ts.isLiteralTypeNode(node.argument)) {
486
- const literal = node.argument.literal;
487
- if (ts.isStringLiteralLike(literal)) {
488
- return literal;
489
- }
490
- }
491
- return null;
492
- }
493
-
494
- /**
495
- * Import-specifier rewrite for plain .ts/.js files (barrels, utilities).
496
- *
497
- * Parsed rather than matched. This was a context-anchored regex on the
498
- * reasoning that a specifier only appears after `from`, `import`, `import(` or
499
- * `require(` -- true, but those words appear just as readily in a comment or
500
- * inside a quoted string, and the regex rewrote all of them. TypeScript is
501
- * already a devDependency and already type-checks this directory, so the exact
502
- * answer costs nothing here. Only the literal's interior is replaced, so the
503
- * original quote style survives.
504
- */
505
- export function transformModuleSpecifiers(
506
- source: string,
507
- file: string,
508
- direction: Direction
509
- ): TransformResult {
510
- const config = directionConfig(direction);
511
- const sourceFile = ts.createSourceFile(
512
- file,
513
- source,
514
- ts.ScriptTarget.ESNext,
515
- true,
516
- scriptKindFor(file)
517
- );
518
-
519
- const edits: { readonly start: number; readonly end: number; readonly text: string }[] = [];
520
- const visit = (node: ts.Node): void => {
521
- const specifier = specifierOf(node);
522
- if (specifier !== null) {
523
- const subpath = packageSubpath(specifier.text, config.fromPackage);
524
- if (subpath !== null) {
525
- edits.push({
526
- start: specifier.getStart(sourceFile) + 1,
527
- end: specifier.getEnd() - 1,
528
- text: `${config.toPackage}${subpath}`
529
- });
530
- }
531
- }
532
- ts.forEachChild(node, visit);
533
- };
534
- visit(sourceFile);
535
-
536
- // Applied back to front so earlier offsets stay valid.
537
- let code = source;
538
- for (const edit of [...edits].reverse()) {
539
- code = code.slice(0, edit.start) + edit.text + code.slice(edit.end);
540
- }
541
-
542
- return {
543
- code,
544
- changed: code !== source,
545
- propsRenamed: 0,
546
- importsRewritten: edits.length,
547
- warnings: []
548
- };
549
- }
@@ -1,15 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "strict": true,
4
- "noEmit": true,
5
- "module": "NodeNext",
6
- "moduleResolution": "NodeNext",
7
- "allowImportingTsExtensions": true,
8
- "target": "ES2023",
9
- "lib": ["ES2023"],
10
- "types": ["node"],
11
- "skipLibCheck": true,
12
- "forceConsistentCasingInFileNames": true
13
- },
14
- "include": ["./**/*.ts"]
15
- }
@@ -1,75 +0,0 @@
1
- import type { TransformWarning } from './transform.ts';
2
-
3
- /**
4
- * Detects the one breaking change in the 4.0.0 custom-element surface:
5
- * `children` is no longer a declared prop on any `sui-*` element.
6
- *
7
- * Three elements shipped it in 3.1.4 — `sui-chat-bubble`, `sui-draggable` and
8
- * `sui-resizable` — and none of them had a `<slot>`, so assigning the property
9
- * was the only way to give them content. Declaring it was also what left
10
- * `element.children` returning undefined instead of the native HTMLCollection,
11
- * so `el.children.length` threw. Both are fixed together: the declaration is
12
- * gone and those wrappers forward the default slot instead.
13
- *
14
- * This reports rather than rewrites. The fix moves content from a JavaScript
15
- * assignment into markup, which changes where the content is authored, not just
16
- * how it is spelled — a person has to decide what the light-DOM children are.
17
- * Auto-editing that would be guesswork dressed as a codemod.
18
- */
19
- export const CHILDREN_BREAKING_TAGS: ReadonlyArray<string> = [
20
- 'sui-chat-bubble',
21
- 'sui-draggable',
22
- 'sui-resizable'
23
- ];
24
-
25
- /**
26
- * Matches `<expr>.children =` but not `==`/`===`, and not `.childNodes`.
27
- * Deliberately loose on the left-hand side: a consumer reaches these elements
28
- * through any expression at all (`ref.current`, `this.$refs.panel`, a query
29
- * result), so anchoring on the property and confirming the tag appears in the
30
- * same file gives far better recall than trying to type the receiver.
31
- */
32
- const CHILDREN_ASSIGNMENT = /(^|[^.\w])([\w$\][().]*?)\.children\s*=(?!=)/gm;
33
-
34
- const lineAndColumn = (source: string, index: number): { line: number; column: number } => {
35
- const upTo = source.slice(0, index);
36
- const lines = upTo.split('\n');
37
- return { line: lines.length, column: (lines.at(-1)?.length ?? 0) + 1 };
38
- };
39
-
40
- /**
41
- * Returns one warning per `.children =` assignment in a file that also mentions
42
- * an affected tag. Requiring the tag in the same file is what keeps this from
43
- * flagging every DOM manipulation in a codebase: `element.children` is a common
44
- * expression, and only these three elements changed.
45
- */
46
- export const findChildrenAssignments = (
47
- source: string,
48
- file: string
49
- ): ReadonlyArray<TransformWarning> => {
50
- const mentioned = CHILDREN_BREAKING_TAGS.filter((tag) => source.includes(tag));
51
- if (mentioned.length === 0) {
52
- return [];
53
- }
54
-
55
- const warnings: TransformWarning[] = [];
56
- for (const match of source.matchAll(CHILDREN_ASSIGNMENT)) {
57
- if (typeof match.index !== 'number') {
58
- continue;
59
- }
60
- const { line, column } = lineAndColumn(source, match.index + match[1].length);
61
- warnings.push({
62
- file,
63
- line,
64
- column,
65
- message:
66
- `assignment to \`.children\` in a file using ${mentioned.join(', ')}. ` +
67
- 'That property is no longer declared on those elements in 4.0.0; assigning it ' +
68
- 'now sets an inert expando and the content silently disappears. Pass the ' +
69
- 'content as light-DOM children instead — `<sui-draggable><div>…</div></sui-draggable>` ' +
70
- '— which the wrappers now forward through their default slot. Reading ' +
71
- '`element.children` is unaffected, and in fact returns a real HTMLCollection again.'
72
- });
73
- }
74
- return warnings;
75
- };