@wise/wds-codemods 1.0.0-experimental-115746b → 1.0.0-experimental-ca872a6
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/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
const require_constants = require('./constants-CcE2TmzN.js');
|
|
3
|
-
const require_transformer = require('./transformer-
|
|
3
|
+
const require_transformer = require('./transformer-DakNFxg-.js');
|
|
4
4
|
const require_helpers = require('./helpers-Du-iz0Iu.js');
|
|
5
5
|
let node_child_process = require("node:child_process");
|
|
6
6
|
let node_fs_promises = require("node:fs/promises");
|
|
@@ -26,13 +26,21 @@ const GREP_PATTERN = new RegExp(`import\\s*\\{[\\s\\S]*?(${DEPRECATED_COMPONENT_
|
|
|
26
26
|
const MIGRATION_RULES = `Migration rules:
|
|
27
27
|
# Legacy Component → ListItem Migration Guide
|
|
28
28
|
|
|
29
|
-
##
|
|
29
|
+
## Import Management
|
|
30
|
+
1. Remove imports: \`{ ActionOption, NavigationOption, NavigationOptionList, Summary, SwitchOption, CheckboxOption, RadioOption }\` from \`'@transferwise/components'\`
|
|
31
|
+
2. Add import: \`{ ListItem }\` from \`'@transferwise/components'\`
|
|
32
|
+
3. For Summary with info: Add \`{ Modal }\` and/or \`{ Popover }\` from \`'@transferwise/components'\`
|
|
33
|
+
4. For Summary with info: Add \`{ QuestionMarkCircle }\` from \`'@transferwise/icons'\`
|
|
34
|
+
5. For Summary with info modal: Add \`{ useState }\` from \`'react'\`
|
|
30
35
|
|
|
36
|
+
## Universal Rules
|
|
31
37
|
1. \`title\` → \`title\` (direct)
|
|
32
38
|
2. \`content\` or \`description\` → \`subtitle\`
|
|
33
39
|
3. \`disabled\` stays on \`ListItem\` (not controls)
|
|
34
|
-
4.
|
|
35
|
-
5.
|
|
40
|
+
4. \`id\` stays on \`ListItem\` (not controls)
|
|
41
|
+
5. Remove deprecated props: \`as\`, \`complex\`, \`showMediaAtAllSizes\`, \`showMediaCircle\`, \`isContainerAligned\`
|
|
42
|
+
6. Preserve string quotes exactly as they are (don't convert \`\` to \`'\` or \`"\`)
|
|
43
|
+
7. When wrapping icons in \`ListItem.AvatarView\`: move \`size\` from icon to AvatarView (except Summary always uses 32)
|
|
36
44
|
|
|
37
45
|
---
|
|
38
46
|
|
|
@@ -40,7 +48,11 @@ const MIGRATION_RULES = `Migration rules:
|
|
|
40
48
|
|
|
41
49
|
- \`action\` → Button children
|
|
42
50
|
- \`onClick\` → Button \`onClick\`
|
|
43
|
-
- Priority:
|
|
51
|
+
- Priority mapping:
|
|
52
|
+
- default or \`"primary"\` → \`"primary"\`
|
|
53
|
+
- \`"secondary"\` → \`"secondary-neutral"\`
|
|
54
|
+
- \`"secondary-send"\` → \`"secondary"\`
|
|
55
|
+
- \`"tertiary"\` → \`"tertiary"\`
|
|
44
56
|
|
|
45
57
|
\`\`\`tsx
|
|
46
58
|
<ActionOption title="Title" content="Text" action="Click" priority="secondary" onClick={fn} />
|
|
@@ -52,14 +64,14 @@ const MIGRATION_RULES = `Migration rules:
|
|
|
52
64
|
|
|
53
65
|
## CheckboxOption → ListItem.Checkbox
|
|
54
66
|
|
|
55
|
-
- \`
|
|
56
|
-
- \`
|
|
57
|
-
-
|
|
67
|
+
- \`name\` is moved to \`ListItem.Checkbox\`
|
|
68
|
+
- \`onChange\`: \`(checked: boolean) => void\` → \`(event: ChangeEvent<HTMLInputElement>) => void\`
|
|
69
|
+
- Update handler: \`(checked) => setX(checked)\` → \`(event) => setX(event.target.checked)\`
|
|
58
70
|
|
|
59
71
|
\`\`\`tsx
|
|
60
72
|
<CheckboxOption id="x" name="y" title="Title" content="Text" checked={v} onChange={(c) => set(c)} />
|
|
61
73
|
→
|
|
62
|
-
<ListItem title="Title" subtitle="Text" control={<ListItem.Checkbox name="y" checked={v} onChange={(e) => set(e.target.checked)} />} />
|
|
74
|
+
<ListItem id="x" title="Title" subtitle="Text" control={<ListItem.Checkbox name="y" checked={v} onChange={(e) => set(e.target.checked)} />} />
|
|
63
75
|
\`\`\`
|
|
64
76
|
|
|
65
77
|
---
|
|
@@ -67,25 +79,24 @@ const MIGRATION_RULES = `Migration rules:
|
|
|
67
79
|
## RadioOption → ListItem.Radio
|
|
68
80
|
|
|
69
81
|
- \`name\`, \`value\`, \`checked\`, \`onChange\` move to Radio
|
|
70
|
-
- Don't move \`id\` to Radio
|
|
71
82
|
|
|
72
83
|
\`\`\`tsx
|
|
73
84
|
<RadioOption id="x" name="y" value="v" title="Title" content="Text" checked={v==='v'} onChange={set} />
|
|
74
85
|
→
|
|
75
|
-
<ListItem title="Title" subtitle="Text" control={<ListItem.Radio name="y" value="v" checked={v==='v'} onChange={set} />} />
|
|
86
|
+
<ListItem id="x" title="Title" subtitle="Text" control={<ListItem.Radio name="y" value="v" checked={v==='v'} onChange={set} />} />
|
|
76
87
|
\`\`\`
|
|
77
88
|
|
|
78
89
|
---
|
|
79
90
|
|
|
80
91
|
## SwitchOption → ListItem.Switch
|
|
81
92
|
|
|
82
|
-
- \`
|
|
83
|
-
- \`
|
|
93
|
+
- \`aria-label\` is moved to \`ListItem.Switch\`
|
|
94
|
+
- \`onChange\` → \`onClick\` with manual toggle: \`onChange={set}\` → \`onClick={() => set(!v)}\`
|
|
84
95
|
|
|
85
96
|
\`\`\`tsx
|
|
86
|
-
<SwitchOption title="Title" content="Text" checked={v} aria-label="Toggle" onChange={set} />
|
|
97
|
+
<SwitchOption id="x" title="Title" content="Text" checked={v} aria-label="Toggle" onChange={set} />
|
|
87
98
|
→
|
|
88
|
-
<ListItem title="Title" subtitle="Text" control={<ListItem.Switch checked={v} aria-label="Toggle" onClick={() => set(!v)} />} />
|
|
99
|
+
<ListItem id="x" title="Title" subtitle="Text" control={<ListItem.Switch checked={v} aria-label="Toggle" onClick={() => set(!v)} />} />
|
|
89
100
|
\`\`\`
|
|
90
101
|
|
|
91
102
|
---
|
|
@@ -102,44 +113,69 @@ const MIGRATION_RULES = `Migration rules:
|
|
|
102
113
|
|
|
103
114
|
---
|
|
104
115
|
|
|
116
|
+
## NavigationOptionList → (remove wrapper)
|
|
117
|
+
|
|
118
|
+
- Convert \`NavigationOptionList\` wrapper to \`<List></List>\`
|
|
119
|
+
- Convert each child \`NavigationOption\` to \`ListItem\` with \`ListItem.Navigation\`
|
|
120
|
+
|
|
121
|
+
\`\`\`tsx
|
|
122
|
+
<NavigationOptionList>
|
|
123
|
+
<NavigationOption title="T1" content="C1" onClick={fn1} />
|
|
124
|
+
<NavigationOption title="T2" content="C2" href="/link" />
|
|
125
|
+
</NavigationOptionList>
|
|
126
|
+
→
|
|
127
|
+
<>
|
|
128
|
+
<ListItem title="T1" subtitle="C1" control={<ListItem.Navigation onClick={fn1} />} />
|
|
129
|
+
<ListItem title="T2" subtitle="C2" control={<ListItem.Navigation href="/link" />} />
|
|
130
|
+
</>
|
|
131
|
+
\`\`\`
|
|
132
|
+
|
|
133
|
+
---
|
|
134
|
+
|
|
105
135
|
## Option → ListItem
|
|
106
136
|
|
|
107
137
|
- Wrap \`media\` in \`ListItem.AvatarView\`
|
|
138
|
+
- Move \`size\` prop from icon to AvatarView (keeping same value)
|
|
108
139
|
|
|
109
140
|
\`\`\`tsx
|
|
110
|
-
<Option media={<Icon />} title="Title" />
|
|
141
|
+
<Option media={<Icon size={48} />} title="Title" />
|
|
111
142
|
→
|
|
112
|
-
<ListItem title="Title" media={<ListItem.AvatarView><Icon /></ListItem.AvatarView>} />
|
|
143
|
+
<ListItem title="Title" media={<ListItem.AvatarView size={48}><Icon /></ListItem.AvatarView>} />
|
|
113
144
|
\`\`\`
|
|
114
145
|
|
|
115
146
|
---
|
|
116
147
|
|
|
117
148
|
## Summary → ListItem
|
|
118
149
|
|
|
119
|
-
|
|
150
|
+
### Basic
|
|
151
|
+
- \`icon\` prop → wrap in \`ListItem.AvatarView\` with \`size={32}\` as \`media\`
|
|
152
|
+
- ALWAYS use \`size={32}\` on AvatarView (regardless of original icon size)
|
|
153
|
+
- ALWAYS remove \`size\` prop from child icon
|
|
154
|
+
- Summary-specific: always uses 32, while other components move size value from icon to AvatarView
|
|
120
155
|
|
|
121
|
-
|
|
122
|
-
-
|
|
123
|
-
|
|
124
|
-
**Status:**
|
|
125
|
-
|
|
126
|
-
- \`Status.DONE\` → \`badge={{ status: 'positive' }}\`
|
|
127
|
-
- \`Status.PENDING\` → \`badge={{ status: 'pending' }}\`
|
|
156
|
+
### Status mapping
|
|
157
|
+
- \`Status.DONE\` → \`badge={{ status: 'positive' }}\` on AvatarView
|
|
158
|
+
- \`Status.PENDING\` → \`badge={{ status: 'pending' }}\` on AvatarView
|
|
128
159
|
- \`Status.NOT_DONE\` → no badge
|
|
129
160
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
-
|
|
161
|
+
### Action
|
|
162
|
+
- \`action.text\` → \`action.label\` in \`ListItem.AdditionalInfo\`
|
|
163
|
+
- Pass entire action object including \`href\`, \`onClick\`, \`target\`
|
|
164
|
+
- Remove \`aria-label\` from action (not supported)
|
|
133
165
|
|
|
134
|
-
|
|
166
|
+
### Info with Modal (add state management)
|
|
167
|
+
- Add: \`const [open, setOpen] = useState(false)\`
|
|
168
|
+
- Create \`ListItem.IconButton\` with \`partiallyInteractive\` and \`onClick\`
|
|
169
|
+
- Render \`Modal\` separately with state
|
|
170
|
+
- Transfer \`aria-label\` to IconButton
|
|
135
171
|
|
|
136
|
-
|
|
137
|
-
- \`
|
|
138
|
-
-
|
|
172
|
+
### Info with Popover
|
|
173
|
+
- \`Popover\` wraps \`ListItem.IconButton\` with \`partiallyInteractive\`
|
|
174
|
+
- Transfer \`aria-label\` to IconButton
|
|
139
175
|
|
|
140
176
|
\`\`\`tsx
|
|
141
|
-
// Basic
|
|
142
|
-
<Summary title="T" description="D" icon={<Icon />} />
|
|
177
|
+
// Basic (icon with or without size prop always becomes size={32} on AvatarView)
|
|
178
|
+
<Summary title="T" description="D" icon={<Icon size={24} />} />
|
|
143
179
|
→
|
|
144
180
|
<ListItem title="T" subtitle="D" media={<ListItem.AvatarView size={32}><Icon /></ListItem.AvatarView>} />
|
|
145
181
|
|
|
@@ -149,19 +185,32 @@ const MIGRATION_RULES = `Migration rules:
|
|
|
149
185
|
<ListItem title="T" subtitle="D" media={<ListItem.AvatarView size={32} badge={{status:'positive'}}><Icon /></ListItem.AvatarView>} />
|
|
150
186
|
|
|
151
187
|
// Action
|
|
152
|
-
<Summary title="T" description="D" icon={<Icon />} action={{text:'Go', href:'/go'}} />
|
|
188
|
+
<Summary title="T" description="D" icon={<Icon />} action={{text:'Go', href:'/go', target:'_blank'}} />
|
|
153
189
|
→
|
|
154
|
-
<ListItem title="T" subtitle="D" media={<ListItem.AvatarView size={32}><Icon /></ListItem.AvatarView>} additionalInfo={<ListItem.AdditionalInfo action={{label:'Go', href:'/go'}} />} />
|
|
190
|
+
<ListItem title="T" subtitle="D" media={<ListItem.AvatarView size={32}><Icon /></ListItem.AvatarView>} additionalInfo={<ListItem.AdditionalInfo action={{label:'Go', href:'/go', target:'_blank'}} />} />
|
|
155
191
|
|
|
156
|
-
// Modal (
|
|
192
|
+
// Modal (requires state: const [open, setOpen] = useState(false))
|
|
157
193
|
<Summary title="T" description="D" icon={<Icon />} info={{title:'Help', content:'Text', presentation:'MODAL', 'aria-label':'Info'}} />
|
|
158
194
|
→
|
|
159
|
-
|
|
195
|
+
<>
|
|
196
|
+
<ListItem title="T" subtitle="D" media={<ListItem.AvatarView size={32}><Icon /></ListItem.AvatarView>} control={
|
|
197
|
+
<ListItem.IconButton partiallyInteractive aria-label="Info" onClick={()=>setOpen(!open)}>
|
|
198
|
+
<QuestionMarkCircle />
|
|
199
|
+
</ListItem.IconButton>
|
|
200
|
+
} />
|
|
201
|
+
<Modal open={open} title="Help" body="Text" onClose={()=>setOpen(false)} />
|
|
202
|
+
</>
|
|
160
203
|
|
|
161
204
|
// Popover
|
|
162
205
|
<Summary title="T" description="D" icon={<Icon />} info={{title:'Help', content:'Text', presentation:'POPOVER', 'aria-label':'Info'}} />
|
|
163
206
|
→
|
|
164
|
-
<ListItem title="T" subtitle="D" media={<ListItem.AvatarView size={32}><Icon /></ListItem.AvatarView>} control={
|
|
207
|
+
<ListItem title="T" subtitle="D" media={<ListItem.AvatarView size={32}><Icon /></ListItem.AvatarView>} control={
|
|
208
|
+
<Popover title="Help" content="Text" onClose={()=>setOpen(false)}>
|
|
209
|
+
<ListItem.IconButton partiallyInteractive aria-label="Info">
|
|
210
|
+
<QuestionMarkCircle />
|
|
211
|
+
</ListItem.IconButton>
|
|
212
|
+
</Popover>
|
|
213
|
+
} />
|
|
165
214
|
\`\`\`
|
|
166
215
|
|
|
167
216
|
`;
|
|
@@ -170,20 +219,20 @@ const SYSTEM_PROMPT = `You are a code migration assistant that helps migrate Typ
|
|
|
170
219
|
Rules:
|
|
171
220
|
1. Only ever modify files via the Edit tool - do not use the Write tool
|
|
172
221
|
2. When identifying what code to migrate within a file, explain how you identified it first.
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
222
|
+
3. Migrate components per provided migration rules
|
|
223
|
+
4. Maintain TypeScript type safety and update types to match new API
|
|
224
|
+
5. Map props: handle renamed, deprecated, new required, and changed types
|
|
225
|
+
6. Update imports to new WDS components and types
|
|
226
|
+
7. Preserve code style, formatting, and calculated logic
|
|
227
|
+
8. Handle conditional rendering, spread props, and complex expressions
|
|
228
|
+
9. Note: New components may lack feature parity with legacy versions
|
|
229
|
+
10. Only modify code requiring changes per migration rules, and any impacted surrounding code for context.
|
|
230
|
+
11. Final result response should just be whether the migration was successful overall, or if any errors were encountered
|
|
231
|
+
- Do not summarise or explain the changes made
|
|
232
|
+
12. Explain your reasoning and justification before making changes, as you edit each file.
|
|
233
|
+
- Keep it concise and succinct, as only bullet points
|
|
234
|
+
13. After modifying the file, do not summarise the changes made.
|
|
235
|
+
14. If you do not have permission to edit a file, still attempt to edit it and then move onto the next file.
|
|
187
236
|
|
|
188
237
|
You'll receive:
|
|
189
238
|
- File paths to migrate in individual queries
|
|
@@ -379,4 +428,4 @@ Object.defineProperty(exports, 'transformer_default', {
|
|
|
379
428
|
return transformer_default;
|
|
380
429
|
}
|
|
381
430
|
});
|
|
382
|
-
//# sourceMappingURL=transformer-
|
|
431
|
+
//# sourceMappingURL=transformer-DakNFxg-.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transformer-DakNFxg-.js","names":["https","CONSOLE_ICONS","Manager","Listr"],"sources":["../src/transforms/list-item/constants.ts","../src/transforms/list-item/helpers.ts","../src/transforms/list-item/claude.ts","../src/transforms/list-item/transformer.ts"],"sourcesContent":["export const CLAUDE_SETTINGS_FILE = '.claude/settings.json';\nexport const VPN_COUNTDOWN_TIMEOUT = 5;\nexport const DIRECTORY_CONCURRENCY_LIMIT = 3;\nexport const FILE_CONCURRENCY_LIMIT = 10;\nconst DEPRECATED_COMPONENT_NAMES = [\n 'ActionOption',\n 'NavigationOption',\n 'NavigationOptionList',\n 'Summary',\n 'SwitchOption',\n 'CheckboxOption',\n 'RadioOption',\n];\nexport const GREP_PATTERN = new RegExp(\n `import\\\\s*\\\\{[\\\\s\\\\S]*?(${DEPRECATED_COMPONENT_NAMES.join('|')})[\\\\s\\\\S]*?\\\\}\\\\s*from\\\\s*['\"]@transferwise/components['\"]`,\n 'u',\n);\n\nconst MIGRATION_RULES = `Migration rules:\n# Legacy Component → ListItem Migration Guide\n\n## Import Management\n1. Remove imports: \\`{ ActionOption, NavigationOption, NavigationOptionList, Summary, SwitchOption, CheckboxOption, RadioOption }\\` from \\`'@transferwise/components'\\`\n2. Add import: \\`{ ListItem }\\` from \\`'@transferwise/components'\\`\n3. For Summary with info: Add \\`{ Modal }\\` and/or \\`{ Popover }\\` from \\`'@transferwise/components'\\`\n4. For Summary with info: Add \\`{ QuestionMarkCircle }\\` from \\`'@transferwise/icons'\\`\n5. For Summary with info modal: Add \\`{ useState }\\` from \\`'react'\\`\n\n## Universal Rules\n1. \\`title\\` → \\`title\\` (direct)\n2. \\`content\\` or \\`description\\` → \\`subtitle\\`\n3. \\`disabled\\` stays on \\`ListItem\\` (not controls)\n4. \\`id\\` stays on \\`ListItem\\` (not controls)\n5. Remove deprecated props: \\`as\\`, \\`complex\\`, \\`showMediaAtAllSizes\\`, \\`showMediaCircle\\`, \\`isContainerAligned\\`\n6. Preserve string quotes exactly as they are (don't convert \\`\\` to \\`'\\` or \\`\"\\`)\n7. When wrapping icons in \\`ListItem.AvatarView\\`: move \\`size\\` from icon to AvatarView (except Summary always uses 32)\n\n---\n\n## ActionOption → ListItem.Button\n\n- \\`action\\` → Button children\n- \\`onClick\\` → Button \\`onClick\\`\n- Priority mapping:\n - default or \\`\"primary\"\\` → \\`\"primary\"\\`\n - \\`\"secondary\"\\` → \\`\"secondary-neutral\"\\`\n - \\`\"secondary-send\"\\` → \\`\"secondary\"\\`\n - \\`\"tertiary\"\\` → \\`\"tertiary\"\\`\n\n\\`\\`\\`tsx\n<ActionOption title=\"Title\" content=\"Text\" action=\"Click\" priority=\"secondary\" onClick={fn} />\n→\n<ListItem title=\"Title\" subtitle=\"Text\" control={<ListItem.Button priority=\"secondary-neutral\" onClick={fn}>Click</ListItem.Button>} />\n\\`\\`\\`\n\n---\n\n## CheckboxOption → ListItem.Checkbox\n\n- \\`name\\` is moved to \\`ListItem.Checkbox\\`\n- \\`onChange\\`: \\`(checked: boolean) => void\\` → \\`(event: ChangeEvent<HTMLInputElement>) => void\\`\n - Update handler: \\`(checked) => setX(checked)\\` → \\`(event) => setX(event.target.checked)\\`\n\n\\`\\`\\`tsx\n<CheckboxOption id=\"x\" name=\"y\" title=\"Title\" content=\"Text\" checked={v} onChange={(c) => set(c)} />\n→\n<ListItem id=\"x\" title=\"Title\" subtitle=\"Text\" control={<ListItem.Checkbox name=\"y\" checked={v} onChange={(e) => set(e.target.checked)} />} />\n\\`\\`\\`\n\n---\n\n## RadioOption → ListItem.Radio\n\n- \\`name\\`, \\`value\\`, \\`checked\\`, \\`onChange\\` move to Radio\n\n\\`\\`\\`tsx\n<RadioOption id=\"x\" name=\"y\" value=\"v\" title=\"Title\" content=\"Text\" checked={v==='v'} onChange={set} />\n→\n<ListItem id=\"x\" title=\"Title\" subtitle=\"Text\" control={<ListItem.Radio name=\"y\" value=\"v\" checked={v==='v'} onChange={set} />} />\n\\`\\`\\`\n\n---\n\n## SwitchOption → ListItem.Switch\n\n- \\`aria-label\\` is moved to \\`ListItem.Switch\\`\n- \\`onChange\\` → \\`onClick\\` with manual toggle: \\`onChange={set}\\` → \\`onClick={() => set(!v)}\\`\n\n\\`\\`\\`tsx\n<SwitchOption id=\"x\" title=\"Title\" content=\"Text\" checked={v} aria-label=\"Toggle\" onChange={set} />\n→\n<ListItem id=\"x\" title=\"Title\" subtitle=\"Text\" control={<ListItem.Switch checked={v} aria-label=\"Toggle\" onClick={() => set(!v)} />} />\n\\`\\`\\`\n\n---\n\n## NavigationOption → ListItem.Navigation\n\n- \\`onClick\\` or \\`href\\` move to Navigation\n\n\\`\\`\\`tsx\n<NavigationOption title=\"Title\" content=\"Text\" onClick={fn} />\n→\n<ListItem title=\"Title\" subtitle=\"Text\" control={<ListItem.Navigation onClick={fn} />} />\n\\`\\`\\`\n\n---\n\n## NavigationOptionList → (remove wrapper)\n\n- Convert \\`NavigationOptionList\\` wrapper to \\`<List></List>\\`\n- Convert each child \\`NavigationOption\\` to \\`ListItem\\` with \\`ListItem.Navigation\\`\n\n\\`\\`\\`tsx\n<NavigationOptionList>\n <NavigationOption title=\"T1\" content=\"C1\" onClick={fn1} />\n <NavigationOption title=\"T2\" content=\"C2\" href=\"/link\" />\n</NavigationOptionList>\n→\n<>\n <ListItem title=\"T1\" subtitle=\"C1\" control={<ListItem.Navigation onClick={fn1} />} />\n <ListItem title=\"T2\" subtitle=\"C2\" control={<ListItem.Navigation href=\"/link\" />} />\n</>\n\\`\\`\\`\n\n---\n\n## Option → ListItem\n\n- Wrap \\`media\\` in \\`ListItem.AvatarView\\`\n- Move \\`size\\` prop from icon to AvatarView (keeping same value)\n\n\\`\\`\\`tsx\n<Option media={<Icon size={48} />} title=\"Title\" />\n→\n<ListItem title=\"Title\" media={<ListItem.AvatarView size={48}><Icon /></ListItem.AvatarView>} />\n\\`\\`\\`\n\n---\n\n## Summary → ListItem\n\n### Basic\n- \\`icon\\` prop → wrap in \\`ListItem.AvatarView\\` with \\`size={32}\\` as \\`media\\`\n- ALWAYS use \\`size={32}\\` on AvatarView (regardless of original icon size)\n- ALWAYS remove \\`size\\` prop from child icon\n- Summary-specific: always uses 32, while other components move size value from icon to AvatarView\n\n### Status mapping\n- \\`Status.DONE\\` → \\`badge={{ status: 'positive' }}\\` on AvatarView\n- \\`Status.PENDING\\` → \\`badge={{ status: 'pending' }}\\` on AvatarView\n- \\`Status.NOT_DONE\\` → no badge\n\n### Action\n- \\`action.text\\` → \\`action.label\\` in \\`ListItem.AdditionalInfo\\`\n- Pass entire action object including \\`href\\`, \\`onClick\\`, \\`target\\`\n- Remove \\`aria-label\\` from action (not supported)\n\n### Info with Modal (add state management)\n- Add: \\`const [open, setOpen] = useState(false)\\`\n- Create \\`ListItem.IconButton\\` with \\`partiallyInteractive\\` and \\`onClick\\`\n- Render \\`Modal\\` separately with state\n- Transfer \\`aria-label\\` to IconButton\n\n### Info with Popover\n- \\`Popover\\` wraps \\`ListItem.IconButton\\` with \\`partiallyInteractive\\`\n- Transfer \\`aria-label\\` to IconButton\n\n\\`\\`\\`tsx\n// Basic (icon with or without size prop always becomes size={32} on AvatarView)\n<Summary title=\"T\" description=\"D\" icon={<Icon size={24} />} />\n→\n<ListItem title=\"T\" subtitle=\"D\" media={<ListItem.AvatarView size={32}><Icon /></ListItem.AvatarView>} />\n\n// Status\n<Summary title=\"T\" description=\"D\" icon={<Icon />} status={Status.DONE} />\n→\n<ListItem title=\"T\" subtitle=\"D\" media={<ListItem.AvatarView size={32} badge={{status:'positive'}}><Icon /></ListItem.AvatarView>} />\n\n// Action\n<Summary title=\"T\" description=\"D\" icon={<Icon />} action={{text:'Go', href:'/go', target:'_blank'}} />\n→\n<ListItem title=\"T\" subtitle=\"D\" media={<ListItem.AvatarView size={32}><Icon /></ListItem.AvatarView>} additionalInfo={<ListItem.AdditionalInfo action={{label:'Go', href:'/go', target:'_blank'}} />} />\n\n// Modal (requires state: const [open, setOpen] = useState(false))\n<Summary title=\"T\" description=\"D\" icon={<Icon />} info={{title:'Help', content:'Text', presentation:'MODAL', 'aria-label':'Info'}} />\n→\n<>\n <ListItem title=\"T\" subtitle=\"D\" media={<ListItem.AvatarView size={32}><Icon /></ListItem.AvatarView>} control={\n <ListItem.IconButton partiallyInteractive aria-label=\"Info\" onClick={()=>setOpen(!open)}>\n <QuestionMarkCircle />\n </ListItem.IconButton>\n } />\n <Modal open={open} title=\"Help\" body=\"Text\" onClose={()=>setOpen(false)} />\n</>\n\n// Popover\n<Summary title=\"T\" description=\"D\" icon={<Icon />} info={{title:'Help', content:'Text', presentation:'POPOVER', 'aria-label':'Info'}} />\n→\n<ListItem title=\"T\" subtitle=\"D\" media={<ListItem.AvatarView size={32}><Icon /></ListItem.AvatarView>} control={\n <Popover title=\"Help\" content=\"Text\" onClose={()=>setOpen(false)}>\n <ListItem.IconButton partiallyInteractive aria-label=\"Info\">\n <QuestionMarkCircle />\n </ListItem.IconButton>\n </Popover>\n} />\n\\`\\`\\`\n\n`;\n\nexport const SYSTEM_PROMPT = `You are a code migration assistant that helps migrate TypeScript/JSX code from deprecated Wise Design System (WDS) components to the new ListItem component and ListItem subcomponents from '@transferwise/components'.\n\nRules:\n1. Only ever modify files via the Edit tool - do not use the Write tool\n2. When identifying what code to migrate within a file, explain how you identified it first.\n3. Migrate components per provided migration rules\n4. Maintain TypeScript type safety and update types to match new API\n5. Map props: handle renamed, deprecated, new required, and changed types\n6. Update imports to new WDS components and types\n7. Preserve code style, formatting, and calculated logic\n8. Handle conditional rendering, spread props, and complex expressions\n9. Note: New components may lack feature parity with legacy versions\n10. Only modify code requiring changes per migration rules, and any impacted surrounding code for context.\n11. Final result response should just be whether the migration was successful overall, or if any errors were encountered\n - Do not summarise or explain the changes made\n12. Explain your reasoning and justification before making changes, as you edit each file.\n - Keep it concise and succinct, as only bullet points\n13. After modifying the file, do not summarise the changes made.\n14. If you do not have permission to edit a file, still attempt to edit it and then move onto the next file.\n\nYou'll receive:\n- File paths to migrate in individual queries\n- Deprecated component names at the end of this prompt\n- Migration context and rules for each deprecated component\n\nDeprecated components: ${DEPRECATED_COMPONENT_NAMES.join(', ')}.\n\n${MIGRATION_RULES}`;\n","/** Split the path to get the relative path after the directory, and wrap with ANSI color codes */\nexport function formatPathOutput(directory: string, path?: string, asDim?: boolean): string {\n const relativePath = path ? (path.split(directory.replace('.', ''))[1] ?? path) : directory;\n return asDim ? `\\x1b[2m${relativePath}\\x1b[0m` : `\\x1b[32m${relativePath}\\x1b[0m`;\n}\n\n/** Generates a formatted string representing the total elapsed time since the given start time */\nexport function generateElapsedTime(startTime: number): string {\n const endTime = Date.now();\n const elapsedTime = Math.floor((endTime - startTime) / 1000);\n const hours = Math.floor(elapsedTime / 3600);\n const minutes = Math.floor((elapsedTime % 3600) / 60);\n const seconds = elapsedTime % 60;\n\n return `${hours ? `${hours}h ` : ''}${minutes ? `${minutes}m ` : ''}${seconds ? `${seconds}s` : ''}`;\n}\n","/* eslint-disable no-param-reassign */\nimport https from 'node:https';\n\nimport { type Options, query } from '@anthropic-ai/claude-agent-sdk';\nimport type { Manager } from '@listr2/manager';\nimport { execSync } from 'child_process';\nimport { readFileSync } from 'fs';\nimport type { DefaultRenderer, ListrTaskWrapper, SimpleRenderer } from 'listr2';\nimport { resolve } from 'path';\n\nimport { CONSOLE_ICONS } from '../../constants';\nimport {\n CLAUDE_SETTINGS_FILE,\n DIRECTORY_CONCURRENCY_LIMIT,\n SYSTEM_PROMPT,\n VPN_COUNTDOWN_TIMEOUT,\n} from './constants';\nimport { formatPathOutput, generateElapsedTime } from './helpers';\nimport type { ClaudeSettings } from './types';\n\nasync function checkVPN(\n baseUrl: string | undefined,\n task: ListrTaskWrapper<never, typeof DefaultRenderer, typeof SimpleRenderer>,\n): Promise<void> {\n if (!baseUrl) return;\n\n const checkOnce = async (): Promise<boolean> =>\n new Promise<boolean>((resolveCheck) => {\n const url = new URL('/health', baseUrl);\n const req = https.get(url, { timeout: 2000, rejectUnauthorized: false }, (res) => {\n const ok = !!(res.statusCode && res.statusCode >= 200 && res.statusCode < 400);\n res.resume();\n resolveCheck(ok);\n });\n req.on('timeout', () => {\n req.destroy(new Error('timeout'));\n });\n req.on('error', () => resolveCheck(false));\n });\n\n while (true) {\n const ok = await checkOnce();\n if (ok) {\n task.title = 'Connected to VPN';\n break;\n }\n\n // Countdown from 5s\n for (let countdown = VPN_COUNTDOWN_TIMEOUT; countdown > 0; countdown -= 1) {\n task.output = `Please connect to VPN... retrying in ${countdown}s`;\n await new Promise<void>((response) => {\n setTimeout(response, 1000);\n });\n }\n }\n}\n\nexport function getQueryOptions(sessionId?: string): Options {\n // Read settings from ~/.claude/settings.json to get headers and apiKeyHelper\n const claudeSettingsPath = resolve(process.env.HOME || '', CLAUDE_SETTINGS_FILE);\n const settings = JSON.parse(readFileSync(claudeSettingsPath, 'utf-8')) as ClaudeSettings;\n\n // Get API key by executing the apiKeyHelper script, for authenticating with Okta via LLM Gateway\n let apiKey;\n try {\n apiKey = execSync(`bash ${settings.apiKeyHelper}`, {\n encoding: 'utf-8',\n }).trim();\n } catch {}\n\n if (!apiKey || !settings.env?.ANTHROPIC_BASE_URL) {\n throw new Error(\n 'Failed to retrieve Anthropic API key or Base URL. Please check your Claude Code x LLM Gateway configuration - https://transferwise.atlassian.net/wiki/x/_YUe3Q',\n );\n }\n\n const { ANTHROPIC_CUSTOM_HEADERS, ...restEnvVars } = settings?.env ?? {};\n\n const envVars = {\n ANTHROPIC_AUTH_TOKEN: apiKey,\n ANTHROPIC_CUSTOM_HEADERS,\n ...restEnvVars,\n PATH: process.env.PATH, // Specifying PATH, as Claude Agent SDK seems to struggle consuming the actual environment PATH\n };\n\n return {\n resume: sessionId,\n env: envVars,\n permissionMode: 'acceptEdits',\n systemPrompt: {\n type: 'preset',\n preset: 'claude_code',\n append: SYSTEM_PROMPT,\n },\n allowedTools: ['Grep', 'Read'],\n settingSources: ['local', 'project', 'user'],\n };\n}\n\n/** Initiate a new Claude session/conversation and return reusable options */\nexport async function initiateClaudeSessionOptions(manager: Manager): Promise<Options> {\n const options = getQueryOptions(undefined);\n\n manager.add([\n {\n title: 'Checking VPN connection',\n task: async (\n _,\n task: ListrTaskWrapper<never, typeof DefaultRenderer, typeof SimpleRenderer>,\n ) => checkVPN(options.env?.ANTHROPIC_BASE_URL, task),\n },\n ]);\n\n await manager.runAll();\n\n manager.add([\n {\n title: 'Starting Claude instance',\n task: async (_, task) => {\n task.output = 'Your browser may open for Okta authentication if required';\n\n const result = query({\n options,\n prompt: `You'll be given file paths in additional individual queries to search in for files using deprecated Wise Design System (WDS) components. Migrate the code per the provided migration rules.`,\n });\n\n for await (const message of result) {\n switch (message.type) {\n case 'system':\n if (message.subtype === 'init' && !options.resume) {\n task.title = 'Successfully initialised Claude instance\\n';\n // Set the session ID to resume the conversation in future queries\n options.resume = message.session_id;\n }\n break;\n default:\n if (message.type === 'result' && message.subtype !== 'success') {\n throw new Error(\n `Claude encountered an error when initialising: ${message.errors.join('\\n')}`,\n );\n }\n }\n }\n\n task.task.complete();\n },\n },\n ]);\n\n await manager.runAll().finally(() => {\n // Set manager to run tasks concurrently, once initialisation steps are done\n manager.options = {\n concurrent: DIRECTORY_CONCURRENCY_LIMIT,\n };\n });\n\n return options;\n}\n\n// Queries Claude with the given path and handles logging of tool uses and results\nexport async function queryClaude(\n directory: string,\n filePath: string,\n options: Options,\n task: ListrTaskWrapper<never, typeof DefaultRenderer, typeof SimpleRenderer>,\n isDebug = false,\n) {\n const startTime = Date.now();\n const result = query({\n options,\n prompt: filePath,\n });\n\n for await (const message of result) {\n switch (message.type) {\n case 'result':\n if (message.subtype === 'success' && isDebug) {\n task.title = `\\x1b[2m${formatPathOutput(directory, filePath)}\\x1b[0m]`;\n task.output = `\\x1b[2mMigrated in ${generateElapsedTime(startTime)}\\x1b[0m`;\n } else if (message.is_error) {\n task.title = `\\x1b[2m${formatPathOutput(directory, filePath)}\\x1b[0m]`;\n task.output = `${CONSOLE_ICONS.error} Claude encountered an error: ${JSON.stringify(message)}`;\n }\n break;\n default:\n }\n }\n}\n","/* eslint-disable no-param-reassign */\n/* eslint-disable no-underscore-dangle */\nimport { Manager } from '@listr2/manager';\nimport { execSync } from 'child_process';\nimport { readFileSync } from 'fs';\nimport {\n type DefaultRenderer,\n Listr,\n type ListrTask,\n type ListrTaskWrapper,\n type SimpleRenderer,\n} from 'listr2';\n\nimport { initiateClaudeSessionOptions, queryClaude } from './claude';\nimport { FILE_CONCURRENCY_LIMIT, GREP_PATTERN } from './constants';\nimport { formatPathOutput, generateElapsedTime } from './helpers';\n\nconst transformer = async (targetPaths: string[], isDebug = false) => {\n process.setMaxListeners(20); // Resolves potential memory issues with how Claude handles it's own event listeners\n const startTime = Date.now();\n // Create manager for handling multiple listr instances\n const manager = new Manager({\n concurrent: true,\n });\n const queryOptions = await initiateClaudeSessionOptions(manager);\n\n // Process each directory\n for (const directory of targetPaths) {\n // Find all .tsx files in the directory\n const allTsxFiles = execSync(`find \"${directory}\" -name \"*.tsx\" -type f`, {\n encoding: 'utf-8',\n })\n .trim()\n .split('\\n')\n .filter(Boolean);\n\n // Filter files that match the pattern by reading and testing each file\n const matchingFilePaths = allTsxFiles.filter((filePath) => {\n const content = readFileSync(filePath, 'utf-8');\n return GREP_PATTERN.test(content);\n });\n\n // No files to process in this directory, so we add a task that's immediately skipped\n if (matchingFilePaths.length === 0) {\n manager.add([\n {\n title: `\\x1b[2m${formatPathOutput(directory)} - No files need migration\\x1b[0m`,\n task: (ctx: ListrTask): void => {\n ctx.skip = true;\n },\n },\n ]);\n } else {\n manager.add([\n {\n title: `${formatPathOutput(directory)} - Found \\x1b[32m${matchingFilePaths.length}\\x1b[0m file(s) needing migration`,\n task: async (_ctx, parentTask) => {\n parentTask.title = `${formatPathOutput(directory)} - Migrating \\x1b[32m${matchingFilePaths.length}\\x1b[0m file(s)...`;\n const completedFilesInDirectory = { count: 0 };\n return parentTask\n .newListr(\n matchingFilePaths.map((filePath) => ({\n title: '', // No title so it runs in the background without any console output\n task: async (\n _fileCtx,\n fileTask: ListrTaskWrapper<\n never,\n typeof DefaultRenderer,\n typeof SimpleRenderer\n >,\n ) => {\n await queryClaude(directory, filePath, queryOptions, fileTask, isDebug).finally(\n () => {\n // Update parent task title with progress for each completed file migration\n completedFilesInDirectory.count += 1;\n const isDim = completedFilesInDirectory.count === matchingFilePaths.length;\n parentTask.title = `${isDim ? '\\x1b[2m' : ''}${formatPathOutput(directory)} - Migrated \\x1b[32m${completedFilesInDirectory.count}\\x1b[0m/\\x1b[32m${matchingFilePaths.length}\\x1b[0m files${isDim ? '\\x1b[0m' : ''}`;\n },\n );\n },\n })),\n { concurrent: FILE_CONCURRENCY_LIMIT },\n )\n .run();\n },\n },\n ]);\n }\n }\n\n // Run all directory tasks, with final follow up/summary task\n await manager.runAll().finally(async () => {\n await new Listr([\n {\n title: `Finished migrating - elapsed time: \\x1b[32m${generateElapsedTime(startTime)}\\x1b[0m `,\n task: async () => {\n // Task completes immediately\n },\n },\n ]).run();\n });\n};\n\nexport default transformer;\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"transformer-BBzz1o9D.js","names":["https","CONSOLE_ICONS","Manager","Listr"],"sources":["../src/transforms/list-item/constants.ts","../src/transforms/list-item/helpers.ts","../src/transforms/list-item/claude.ts","../src/transforms/list-item/transformer.ts"],"sourcesContent":["export const CLAUDE_SETTINGS_FILE = '.claude/settings.json';\nexport const VPN_COUNTDOWN_TIMEOUT = 5;\nexport const DIRECTORY_CONCURRENCY_LIMIT = 3;\nexport const FILE_CONCURRENCY_LIMIT = 10;\nconst DEPRECATED_COMPONENT_NAMES = [\n 'ActionOption',\n 'NavigationOption',\n 'NavigationOptionList',\n 'Summary',\n 'SwitchOption',\n 'CheckboxOption',\n 'RadioOption',\n];\nexport const GREP_PATTERN = new RegExp(\n `import\\\\s*\\\\{[\\\\s\\\\S]*?(${DEPRECATED_COMPONENT_NAMES.join('|')})[\\\\s\\\\S]*?\\\\}\\\\s*from\\\\s*['\"]@transferwise/components['\"]`,\n 'u',\n);\n\nconst MIGRATION_RULES = `Migration rules:\n# Legacy Component → ListItem Migration Guide\n\n## Universal Rules\n\n1. \\`title\\` → \\`title\\` (direct)\n2. \\`content\\` or \\`description\\` → \\`subtitle\\`\n3. \\`disabled\\` stays on \\`ListItem\\` (not controls)\n4. Keep HTML attributes (\\`id\\`, \\`name\\`, \\`aria-label\\`), remove: \\`as\\`, \\`complex\\`, \\`showMediaAtAllSizes\\`, \\`showMediaCircle\\`, \\`isContainerAligned\\`\n5. In strings, don't convert \\`\\`to\\`'\\`or\\`\"\\`. Preserve what is there.\n\n---\n\n## ActionOption → ListItem.Button\n\n- \\`action\\` → Button children\n- \\`onClick\\` → Button \\`onClick\\`\n- Priority: default/\\`\"primary\"\\` → \\`\"primary\"\\`, \\`\"secondary\"\\` → \\`\"secondary-neutral\"\\`, \\`\"secondary-send\"\\` → \\`\"secondary\"\\`, \\`\"tertiary\"\\` → \\`\"tertiary\"\\`\n\n\\`\\`\\`tsx\n<ActionOption title=\"Title\" content=\"Text\" action=\"Click\" priority=\"secondary\" onClick={fn} />\n→\n<ListItem title=\"Title\" subtitle=\"Text\" control={<ListItem.Button priority=\"secondary-neutral\" onClick={fn}>Click</ListItem.Button>} />\n\\`\\`\\`\n\n---\n\n## CheckboxOption → ListItem.Checkbox\n\n- \\`onChange\\`: \\`(checked: boolean)\\` → \\`(event: ChangeEvent)\\` use \\`event.target.checked\\`\n- \\`name\\` move to Checkbox\n- Don't move \\`id\\` to Checkbox\n\n\\`\\`\\`tsx\n<CheckboxOption id=\"x\" name=\"y\" title=\"Title\" content=\"Text\" checked={v} onChange={(c) => set(c)} />\n→\n<ListItem title=\"Title\" subtitle=\"Text\" control={<ListItem.Checkbox name=\"y\" checked={v} onChange={(e) => set(e.target.checked)} />} />\n\\`\\`\\`\n\n---\n\n## RadioOption → ListItem.Radio\n\n- \\`name\\`, \\`value\\`, \\`checked\\`, \\`onChange\\` move to Radio\n- Don't move \\`id\\` to Radio\n\n\\`\\`\\`tsx\n<RadioOption id=\"x\" name=\"y\" value=\"v\" title=\"Title\" content=\"Text\" checked={v==='v'} onChange={set} />\n→\n<ListItem title=\"Title\" subtitle=\"Text\" control={<ListItem.Radio name=\"y\" value=\"v\" checked={v==='v'} onChange={set} />} />\n\\`\\`\\`\n\n---\n\n## SwitchOption → ListItem.Switch\n\n- \\`onChange\\` → \\`onClick\\`, toggle manually\n- \\`aria-label\\` moves to Switch\n\n\\`\\`\\`tsx\n<SwitchOption title=\"Title\" content=\"Text\" checked={v} aria-label=\"Toggle\" onChange={set} />\n→\n<ListItem title=\"Title\" subtitle=\"Text\" control={<ListItem.Switch checked={v} aria-label=\"Toggle\" onClick={() => set(!v)} />} />\n\\`\\`\\`\n\n---\n\n## NavigationOption → ListItem.Navigation\n\n- \\`onClick\\` or \\`href\\` move to Navigation\n\n\\`\\`\\`tsx\n<NavigationOption title=\"Title\" content=\"Text\" onClick={fn} />\n→\n<ListItem title=\"Title\" subtitle=\"Text\" control={<ListItem.Navigation onClick={fn} />} />\n\\`\\`\\`\n\n---\n\n## Option → ListItem\n\n- Wrap \\`media\\` in \\`ListItem.AvatarView\\`\n\n\\`\\`\\`tsx\n<Option media={<Icon />} title=\"Title\" />\n→\n<ListItem title=\"Title\" media={<ListItem.AvatarView><Icon /></ListItem.AvatarView>} />\n\\`\\`\\`\n\n---\n\n## Summary → ListItem\n\n**Basic:**\n\n- \\`icon\\` → wrap in \\`ListItem.AvatarView\\` with \\`size={32}\\` as \\`media\\`\n- Remove \\`size\\` from child \\`<Icon />\\`\n\n**Status:**\n\n- \\`Status.DONE\\` → \\`badge={{ status: 'positive' }}\\`\n- \\`Status.PENDING\\` → \\`badge={{ status: 'pending' }}\\`\n- \\`Status.NOT_DONE\\` → no badge\n\n**Action:**\n\n- \\`action.text\\` → \\`action.label\\` in \\`ListItem.AdditionalInfo\\` as \\`additionalInfo\\`\n\n**Info (requires state):**\n\n- \\`MODAL\\` → \\`ListItem.IconButton partiallyInteractive\\` + \\`<Modal>\\` in \\`control\\`\n- \\`POPOVER\\` → \\`<Popover>\\` wrapping \\`ListItem.IconButton partiallyInteractive\\` in \\`control\\`\n- Use \\`QuestionMarkCircle\\` icon (import from \\`@transferwise/icons\\`)\n\n\\`\\`\\`tsx\n// Basic\n<Summary title=\"T\" description=\"D\" icon={<Icon />} />\n→\n<ListItem title=\"T\" subtitle=\"D\" media={<ListItem.AvatarView size={32}><Icon /></ListItem.AvatarView>} />\n\n// Status\n<Summary title=\"T\" description=\"D\" icon={<Icon />} status={Status.DONE} />\n→\n<ListItem title=\"T\" subtitle=\"D\" media={<ListItem.AvatarView size={32} badge={{status:'positive'}}><Icon /></ListItem.AvatarView>} />\n\n// Action\n<Summary title=\"T\" description=\"D\" icon={<Icon />} action={{text:'Go', href:'/go'}} />\n→\n<ListItem title=\"T\" subtitle=\"D\" media={<ListItem.AvatarView size={32}><Icon /></ListItem.AvatarView>} additionalInfo={<ListItem.AdditionalInfo action={{label:'Go', href:'/go'}} />} />\n\n// Modal (add: const [open, setOpen] = useState(false))\n<Summary title=\"T\" description=\"D\" icon={<Icon />} info={{title:'Help', content:'Text', presentation:'MODAL', 'aria-label':'Info'}} />\n→\n<ListItem title=\"T\" subtitle=\"D\" media={<ListItem.AvatarView size={32}><Icon /></ListItem.AvatarView>} control={<ListItem.IconButton partiallyInteractive aria-label=\"Info\" onClick={()=>setOpen(!open)}><QuestionMarkCircle /><Modal open={open} title=\"Help\" body=\"Text\" onClose={()=>setOpen(false)} /></ListItem.IconButton>} />\n\n// Popover\n<Summary title=\"T\" description=\"D\" icon={<Icon />} info={{title:'Help', content:'Text', presentation:'POPOVER', 'aria-label':'Info'}} />\n→\n<ListItem title=\"T\" subtitle=\"D\" media={<ListItem.AvatarView size={32}><Icon /></ListItem.AvatarView>} control={<Popover title=\"Help\" content=\"Text\" onClose={()=>setOpen(false)}><ListItem.IconButton partiallyInteractive aria-label=\"Info\"><QuestionMarkCircle /></ListItem.IconButton></Popover>} />\n\\`\\`\\`\n\n`;\n\nexport const SYSTEM_PROMPT = `You are a code migration assistant that helps migrate TypeScript/JSX code from deprecated Wise Design System (WDS) components to the new ListItem component and ListItem subcomponents from '@transferwise/components'.\n\nRules:\n1. Only ever modify files via the Edit tool - do not use the Write tool\n2. When identifying what code to migrate within a file, explain how you identified it first.\n2. Migrate components per provided migration rules\n3. Maintain TypeScript type safety and update types to match new API\n4. Map props: handle renamed, deprecated, new required, and changed types\n5. Update imports to new WDS components and types\n6. Preserve code style, formatting, and calculated logic\n7. Handle conditional rendering, spread props, and complex expressions\n8. Note: New components may lack feature parity with legacy versions\n9. Only modify code requiring changes per migration rules, and any impacted surrounding code for context.\n10. Final result response should just be whether the migration was successful overall, or if any errors were encountered\n - Do not summarise or explain the changes made\n11. Explain your reasoning and justification before making changes, as you edit each file.\n - Keep it concise and succinct, as only bullet points\n12. After modifying the file, do not summarise the changes made.\n13. If you do not have permission to edit a file, still attempt to edit it and then move onto the next file.\n\nYou'll receive:\n- File paths to migrate in individual queries\n- Deprecated component names at the end of this prompt\n- Migration context and rules for each deprecated component\n\nDeprecated components: ${DEPRECATED_COMPONENT_NAMES.join(', ')}.\n\n${MIGRATION_RULES}`;\n","/** Split the path to get the relative path after the directory, and wrap with ANSI color codes */\nexport function formatPathOutput(directory: string, path?: string, asDim?: boolean): string {\n const relativePath = path ? (path.split(directory.replace('.', ''))[1] ?? path) : directory;\n return asDim ? `\\x1b[2m${relativePath}\\x1b[0m` : `\\x1b[32m${relativePath}\\x1b[0m`;\n}\n\n/** Generates a formatted string representing the total elapsed time since the given start time */\nexport function generateElapsedTime(startTime: number): string {\n const endTime = Date.now();\n const elapsedTime = Math.floor((endTime - startTime) / 1000);\n const hours = Math.floor(elapsedTime / 3600);\n const minutes = Math.floor((elapsedTime % 3600) / 60);\n const seconds = elapsedTime % 60;\n\n return `${hours ? `${hours}h ` : ''}${minutes ? `${minutes}m ` : ''}${seconds ? `${seconds}s` : ''}`;\n}\n","/* eslint-disable no-param-reassign */\nimport https from 'node:https';\n\nimport { type Options, query } from '@anthropic-ai/claude-agent-sdk';\nimport type { Manager } from '@listr2/manager';\nimport { execSync } from 'child_process';\nimport { readFileSync } from 'fs';\nimport type { DefaultRenderer, ListrTaskWrapper, SimpleRenderer } from 'listr2';\nimport { resolve } from 'path';\n\nimport { CONSOLE_ICONS } from '../../constants';\nimport {\n CLAUDE_SETTINGS_FILE,\n DIRECTORY_CONCURRENCY_LIMIT,\n SYSTEM_PROMPT,\n VPN_COUNTDOWN_TIMEOUT,\n} from './constants';\nimport { formatPathOutput, generateElapsedTime } from './helpers';\nimport type { ClaudeSettings } from './types';\n\nasync function checkVPN(\n baseUrl: string | undefined,\n task: ListrTaskWrapper<never, typeof DefaultRenderer, typeof SimpleRenderer>,\n): Promise<void> {\n if (!baseUrl) return;\n\n const checkOnce = async (): Promise<boolean> =>\n new Promise<boolean>((resolveCheck) => {\n const url = new URL('/health', baseUrl);\n const req = https.get(url, { timeout: 2000, rejectUnauthorized: false }, (res) => {\n const ok = !!(res.statusCode && res.statusCode >= 200 && res.statusCode < 400);\n res.resume();\n resolveCheck(ok);\n });\n req.on('timeout', () => {\n req.destroy(new Error('timeout'));\n });\n req.on('error', () => resolveCheck(false));\n });\n\n while (true) {\n const ok = await checkOnce();\n if (ok) {\n task.title = 'Connected to VPN';\n break;\n }\n\n // Countdown from 5s\n for (let countdown = VPN_COUNTDOWN_TIMEOUT; countdown > 0; countdown -= 1) {\n task.output = `Please connect to VPN... retrying in ${countdown}s`;\n await new Promise<void>((response) => {\n setTimeout(response, 1000);\n });\n }\n }\n}\n\nexport function getQueryOptions(sessionId?: string): Options {\n // Read settings from ~/.claude/settings.json to get headers and apiKeyHelper\n const claudeSettingsPath = resolve(process.env.HOME || '', CLAUDE_SETTINGS_FILE);\n const settings = JSON.parse(readFileSync(claudeSettingsPath, 'utf-8')) as ClaudeSettings;\n\n // Get API key by executing the apiKeyHelper script, for authenticating with Okta via LLM Gateway\n let apiKey;\n try {\n apiKey = execSync(`bash ${settings.apiKeyHelper}`, {\n encoding: 'utf-8',\n }).trim();\n } catch {}\n\n if (!apiKey || !settings.env?.ANTHROPIC_BASE_URL) {\n throw new Error(\n 'Failed to retrieve Anthropic API key or Base URL. Please check your Claude Code x LLM Gateway configuration - https://transferwise.atlassian.net/wiki/x/_YUe3Q',\n );\n }\n\n const { ANTHROPIC_CUSTOM_HEADERS, ...restEnvVars } = settings?.env ?? {};\n\n const envVars = {\n ANTHROPIC_AUTH_TOKEN: apiKey,\n ANTHROPIC_CUSTOM_HEADERS,\n ...restEnvVars,\n PATH: process.env.PATH, // Specifying PATH, as Claude Agent SDK seems to struggle consuming the actual environment PATH\n };\n\n return {\n resume: sessionId,\n env: envVars,\n permissionMode: 'acceptEdits',\n systemPrompt: {\n type: 'preset',\n preset: 'claude_code',\n append: SYSTEM_PROMPT,\n },\n allowedTools: ['Grep', 'Read'],\n settingSources: ['local', 'project', 'user'],\n };\n}\n\n/** Initiate a new Claude session/conversation and return reusable options */\nexport async function initiateClaudeSessionOptions(manager: Manager): Promise<Options> {\n const options = getQueryOptions(undefined);\n\n manager.add([\n {\n title: 'Checking VPN connection',\n task: async (\n _,\n task: ListrTaskWrapper<never, typeof DefaultRenderer, typeof SimpleRenderer>,\n ) => checkVPN(options.env?.ANTHROPIC_BASE_URL, task),\n },\n ]);\n\n await manager.runAll();\n\n manager.add([\n {\n title: 'Starting Claude instance',\n task: async (_, task) => {\n task.output = 'Your browser may open for Okta authentication if required';\n\n const result = query({\n options,\n prompt: `You'll be given file paths in additional individual queries to search in for files using deprecated Wise Design System (WDS) components. Migrate the code per the provided migration rules.`,\n });\n\n for await (const message of result) {\n switch (message.type) {\n case 'system':\n if (message.subtype === 'init' && !options.resume) {\n task.title = 'Successfully initialised Claude instance\\n';\n // Set the session ID to resume the conversation in future queries\n options.resume = message.session_id;\n }\n break;\n default:\n if (message.type === 'result' && message.subtype !== 'success') {\n throw new Error(\n `Claude encountered an error when initialising: ${message.errors.join('\\n')}`,\n );\n }\n }\n }\n\n task.task.complete();\n },\n },\n ]);\n\n await manager.runAll().finally(() => {\n // Set manager to run tasks concurrently, once initialisation steps are done\n manager.options = {\n concurrent: DIRECTORY_CONCURRENCY_LIMIT,\n };\n });\n\n return options;\n}\n\n// Queries Claude with the given path and handles logging of tool uses and results\nexport async function queryClaude(\n directory: string,\n filePath: string,\n options: Options,\n task: ListrTaskWrapper<never, typeof DefaultRenderer, typeof SimpleRenderer>,\n isDebug = false,\n) {\n const startTime = Date.now();\n const result = query({\n options,\n prompt: filePath,\n });\n\n for await (const message of result) {\n switch (message.type) {\n case 'result':\n if (message.subtype === 'success' && isDebug) {\n task.title = `\\x1b[2m${formatPathOutput(directory, filePath)}\\x1b[0m]`;\n task.output = `\\x1b[2mMigrated in ${generateElapsedTime(startTime)}\\x1b[0m`;\n } else if (message.is_error) {\n task.title = `\\x1b[2m${formatPathOutput(directory, filePath)}\\x1b[0m]`;\n task.output = `${CONSOLE_ICONS.error} Claude encountered an error: ${JSON.stringify(message)}`;\n }\n break;\n default:\n }\n }\n}\n","/* eslint-disable no-param-reassign */\n/* eslint-disable no-underscore-dangle */\nimport { Manager } from '@listr2/manager';\nimport { execSync } from 'child_process';\nimport { readFileSync } from 'fs';\nimport {\n type DefaultRenderer,\n Listr,\n type ListrTask,\n type ListrTaskWrapper,\n type SimpleRenderer,\n} from 'listr2';\n\nimport { initiateClaudeSessionOptions, queryClaude } from './claude';\nimport { FILE_CONCURRENCY_LIMIT, GREP_PATTERN } from './constants';\nimport { formatPathOutput, generateElapsedTime } from './helpers';\n\nconst transformer = async (targetPaths: string[], isDebug = false) => {\n process.setMaxListeners(20); // Resolves potential memory issues with how Claude handles it's own event listeners\n const startTime = Date.now();\n // Create manager for handling multiple listr instances\n const manager = new Manager({\n concurrent: true,\n });\n const queryOptions = await initiateClaudeSessionOptions(manager);\n\n // Process each directory\n for (const directory of targetPaths) {\n // Find all .tsx files in the directory\n const allTsxFiles = execSync(`find \"${directory}\" -name \"*.tsx\" -type f`, {\n encoding: 'utf-8',\n })\n .trim()\n .split('\\n')\n .filter(Boolean);\n\n // Filter files that match the pattern by reading and testing each file\n const matchingFilePaths = allTsxFiles.filter((filePath) => {\n const content = readFileSync(filePath, 'utf-8');\n return GREP_PATTERN.test(content);\n });\n\n // No files to process in this directory, so we add a task that's immediately skipped\n if (matchingFilePaths.length === 0) {\n manager.add([\n {\n title: `\\x1b[2m${formatPathOutput(directory)} - No files need migration\\x1b[0m`,\n task: (ctx: ListrTask): void => {\n ctx.skip = true;\n },\n },\n ]);\n } else {\n manager.add([\n {\n title: `${formatPathOutput(directory)} - Found \\x1b[32m${matchingFilePaths.length}\\x1b[0m file(s) needing migration`,\n task: async (_ctx, parentTask) => {\n parentTask.title = `${formatPathOutput(directory)} - Migrating \\x1b[32m${matchingFilePaths.length}\\x1b[0m file(s)...`;\n const completedFilesInDirectory = { count: 0 };\n return parentTask\n .newListr(\n matchingFilePaths.map((filePath) => ({\n title: '', // No title so it runs in the background without any console output\n task: async (\n _fileCtx,\n fileTask: ListrTaskWrapper<\n never,\n typeof DefaultRenderer,\n typeof SimpleRenderer\n >,\n ) => {\n await queryClaude(directory, filePath, queryOptions, fileTask, isDebug).finally(\n () => {\n // Update parent task title with progress for each completed file migration\n completedFilesInDirectory.count += 1;\n const isDim = completedFilesInDirectory.count === matchingFilePaths.length;\n parentTask.title = `${isDim ? '\\x1b[2m' : ''}${formatPathOutput(directory)} - Migrated \\x1b[32m${completedFilesInDirectory.count}\\x1b[0m/\\x1b[32m${matchingFilePaths.length}\\x1b[0m files${isDim ? '\\x1b[0m' : ''}`;\n },\n );\n },\n })),\n { concurrent: FILE_CONCURRENCY_LIMIT },\n )\n .run();\n },\n },\n ]);\n }\n }\n\n // Run all directory tasks, with final follow up/summary task\n await manager.runAll().finally(async () => {\n await new Listr([\n {\n title: `Finished migrating - elapsed time: \\x1b[32m${generateElapsedTime(startTime)}\\x1b[0m `,\n task: async () => {\n // Task completes immediately\n },\n },\n ]).run();\n });\n};\n\nexport default transformer;\n"],"mappings":";;;;;;;;;;;AAAA,MAAa,uBAAuB;AACpC,MAAa,wBAAwB;AACrC,MAAa,8BAA8B;AAC3C,MAAa,yBAAyB;AACtC,MAAM,6BAA6B;CACjC;CACA;CACA;CACA;CACA;CACA;CACA;CACD;AACD,MAAa,eAAe,IAAI,OAC9B,2BAA2B,2BAA2B,KAAK,IAAI,CAAC,6DAChE,IACD;AAED,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+IxB,MAAa,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;yBAyBJ,2BAA2B,KAAK,KAAK,CAAC;;EAE7D;;;;;AC3LF,SAAgB,iBAAiB,WAAmB,MAAe,OAAyB;CAC1F,MAAM,eAAe,OAAQ,KAAK,MAAM,UAAU,QAAQ,KAAK,GAAG,CAAC,CAAC,MAAM,OAAQ;AAClF,QAAO,QAAQ,UAAU,aAAa,WAAW,WAAW,aAAa;;;AAI3E,SAAgB,oBAAoB,WAA2B;CAC7D,MAAM,UAAU,KAAK,KAAK;CAC1B,MAAM,cAAc,KAAK,OAAO,UAAU,aAAa,IAAK;CAC5D,MAAM,QAAQ,KAAK,MAAM,cAAc,KAAK;CAC5C,MAAM,UAAU,KAAK,MAAO,cAAc,OAAQ,GAAG;CACrD,MAAM,UAAU,cAAc;AAE9B,QAAO,GAAG,QAAQ,GAAG,MAAM,MAAM,KAAK,UAAU,GAAG,QAAQ,MAAM,KAAK,UAAU,GAAG,QAAQ,KAAK;;;;;ACMlG,eAAe,SACb,SACA,MACe;AACf,KAAI,CAAC,QAAS;CAEd,MAAM,YAAY,YAChB,IAAI,SAAkB,iBAAiB;EACrC,MAAM,MAAM,IAAI,IAAI,WAAW,QAAQ;EACvC,MAAM,MAAMA,mBAAM,IAAI,KAAK;GAAE,SAAS;GAAM,oBAAoB;GAAO,GAAG,QAAQ;GAChF,MAAM,KAAK,CAAC,EAAE,IAAI,cAAc,IAAI,cAAc,OAAO,IAAI,aAAa;AAC1E,OAAI,QAAQ;AACZ,gBAAa,GAAG;IAChB;AACF,MAAI,GAAG,iBAAiB;AACtB,OAAI,wBAAQ,IAAI,MAAM,UAAU,CAAC;IACjC;AACF,MAAI,GAAG,eAAe,aAAa,MAAM,CAAC;GAC1C;AAEJ,QAAO,MAAM;AAEX,MADW,MAAM,WAAW,EACpB;AACN,QAAK,QAAQ;AACb;;AAIF,OAAK,IAAI,YAAY,uBAAuB,YAAY,GAAG,aAAa,GAAG;AACzE,QAAK,SAAS,wCAAwC,UAAU;AAChE,SAAM,IAAI,SAAe,aAAa;AACpC,eAAW,UAAU,IAAK;KAC1B;;;;AAKR,SAAgB,gBAAgB,WAA6B;CAE3D,MAAM,4CAA6B,QAAQ,IAAI,QAAQ,IAAI,qBAAqB;CAChF,MAAM,WAAW,KAAK,gCAAmB,oBAAoB,QAAQ,CAAC;CAGtE,IAAI;AACJ,KAAI;AACF,4CAAkB,QAAQ,SAAS,gBAAgB,EACjD,UAAU,SACX,CAAC,CAAC,MAAM;SACH;AAER,KAAI,CAAC,UAAU,CAAC,SAAS,KAAK,mBAC5B,OAAM,IAAI,MACR,iKACD;CAGH,MAAM,EAAE,0BAA0B,GAAG,gBAAgB,UAAU,OAAO,EAAE;AASxE,QAAO;EACL,QAAQ;EACR,KATc;GACd,sBAAsB;GACtB;GACA,GAAG;GACH,MAAM,QAAQ,IAAI;GACnB;EAKC,gBAAgB;EAChB,cAAc;GACZ,MAAM;GACN,QAAQ;GACR,QAAQ;GACT;EACD,cAAc,CAAC,QAAQ,OAAO;EAC9B,gBAAgB;GAAC;GAAS;GAAW;GAAO;EAC7C;;;AAIH,eAAsB,6BAA6B,SAAoC;CACrF,MAAM,UAAU,gBAAgB,OAAU;AAE1C,SAAQ,IAAI,CACV;EACE,OAAO;EACP,MAAM,OACJ,GACA,SACG,SAAS,QAAQ,KAAK,oBAAoB,KAAK;EACrD,CACF,CAAC;AAEF,OAAM,QAAQ,QAAQ;AAEtB,SAAQ,IAAI,CACV;EACE,OAAO;EACP,MAAM,OAAO,GAAG,SAAS;AACvB,QAAK,SAAS;GAEd,MAAM,mDAAe;IACnB;IACA,QAAQ;IACT,CAAC;AAEF,cAAW,MAAM,WAAW,OAC1B,SAAQ,QAAQ,MAAhB;IACE,KAAK;AACH,SAAI,QAAQ,YAAY,UAAU,CAAC,QAAQ,QAAQ;AACjD,WAAK,QAAQ;AAEb,cAAQ,SAAS,QAAQ;;AAE3B;IACF,QACE,KAAI,QAAQ,SAAS,YAAY,QAAQ,YAAY,UACnD,OAAM,IAAI,MACR,kDAAkD,QAAQ,OAAO,KAAK,KAAK,GAC5E;;AAKT,QAAK,KAAK,UAAU;;EAEvB,CACF,CAAC;AAEF,OAAM,QAAQ,QAAQ,CAAC,cAAc;AAEnC,UAAQ,UAAU,EAChB,YAAY,6BACb;GACD;AAEF,QAAO;;AAIT,eAAsB,YACpB,WACA,UACA,SACA,MACA,UAAU,OACV;CACA,MAAM,YAAY,KAAK,KAAK;CAC5B,MAAM,mDAAe;EACnB;EACA,QAAQ;EACT,CAAC;AAEF,YAAW,MAAM,WAAW,OAC1B,SAAQ,QAAQ,MAAhB;EACE,KAAK;AACH,OAAI,QAAQ,YAAY,aAAa,SAAS;AAC5C,SAAK,QAAQ,UAAU,iBAAiB,WAAW,SAAS,CAAC;AAC7D,SAAK,SAAS,sBAAsB,oBAAoB,UAAU,CAAC;cAC1D,QAAQ,UAAU;AAC3B,SAAK,QAAQ,UAAU,iBAAiB,WAAW,SAAS,CAAC;AAC7D,SAAK,SAAS,GAAGC,gCAAc,MAAM,gCAAgC,KAAK,UAAU,QAAQ;;AAE9F;EACF;;;;;;ACvKN,MAAM,cAAc,OAAO,aAAuB,UAAU,UAAU;AACpE,SAAQ,gBAAgB,GAAG;CAC3B,MAAM,YAAY,KAAK,KAAK;CAE5B,MAAM,UAAU,IAAIC,wBAAQ,EAC1B,YAAY,MACb,CAAC;CACF,MAAM,eAAe,MAAM,6BAA6B,QAAQ;AAGhE,MAAK,MAAM,aAAa,aAAa;EAUnC,MAAM,qDARuB,SAAS,UAAU,0BAA0B,EACxE,UAAU,SACX,CAAC,CACC,MAAM,CACN,MAAM,KAAK,CACX,OAAO,QAAQ,CAGoB,QAAQ,aAAa;GACzD,MAAM,oCAAuB,UAAU,QAAQ;AAC/C,UAAO,aAAa,KAAK,QAAQ;IACjC;AAGF,MAAI,kBAAkB,WAAW,EAC/B,SAAQ,IAAI,CACV;GACE,OAAO,UAAU,iBAAiB,UAAU,CAAC;GAC7C,OAAO,QAAyB;AAC9B,QAAI,OAAO;;GAEd,CACF,CAAC;MAEF,SAAQ,IAAI,CACV;GACE,OAAO,GAAG,iBAAiB,UAAU,CAAC,mBAAmB,kBAAkB,OAAO;GAClF,MAAM,OAAO,MAAM,eAAe;AAChC,eAAW,QAAQ,GAAG,iBAAiB,UAAU,CAAC,uBAAuB,kBAAkB,OAAO;IAClG,MAAM,4BAA4B,EAAE,OAAO,GAAG;AAC9C,WAAO,WACJ,SACC,kBAAkB,KAAK,cAAc;KACnC,OAAO;KACP,MAAM,OACJ,UACA,aAKG;AACH,YAAM,YAAY,WAAW,UAAU,cAAc,UAAU,QAAQ,CAAC,cAChE;AAEJ,iCAA0B,SAAS;OACnC,MAAM,QAAQ,0BAA0B,UAAU,kBAAkB;AACpE,kBAAW,QAAQ,GAAG,QAAQ,YAAY,KAAK,iBAAiB,UAAU,CAAC,sBAAsB,0BAA0B,MAAM,kBAAkB,kBAAkB,OAAO,eAAe,QAAQ,YAAY;QAElN;;KAEJ,EAAE,EACH,EAAE,YAAY,wBAAwB,CACvC,CACA,KAAK;;GAEX,CACF,CAAC;;AAKN,OAAM,QAAQ,QAAQ,CAAC,QAAQ,YAAY;AACzC,QAAM,IAAIC,aAAM,CACd;GACE,OAAO,8CAA8C,oBAAoB,UAAU,CAAC;GACpF,MAAM,YAAY;GAGnB,CACF,CAAC,CAAC,KAAK;GACR;;AAGJ,0BAAe"}
|