@wise/wds-codemods 1.0.0-experimental-601f194 → 1.0.0-experimental-a536a63
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_helpers = require('./helpers-RWhTD5Is.js');
|
|
3
|
-
const require_transformer = require('./transformer-
|
|
3
|
+
const require_transformer = require('./transformer-wd_8jk1P.js');
|
|
4
4
|
let node_child_process = require("node:child_process");
|
|
5
5
|
let node_fs_promises = require("node:fs/promises");
|
|
6
6
|
node_fs_promises = require_helpers.__toESM(node_fs_promises);
|
|
@@ -185,7 +185,7 @@ const MIGRATION_RULES = `Migration rules:
|
|
|
185
185
|
`;
|
|
186
186
|
const SYSTEM_PROMPT = `Transform TypeScript/JSX code from legacy Wise Design System (WDS) components to the new ListItem component and ListItem subcomponents from '@transferwise/components'.
|
|
187
187
|
|
|
188
|
-
Read and transform each file
|
|
188
|
+
Read and transform each file individually, instead of reading them all at the start. Only make a single edit or write per file, once you've fully processed it.
|
|
189
189
|
|
|
190
190
|
Rules:
|
|
191
191
|
1. Ignore any files that do not contain deprecated WDS components, unless they are necessary for context.
|
|
@@ -198,7 +198,9 @@ Rules:
|
|
|
198
198
|
8. Note: New components may lack feature parity with legacy versions
|
|
199
199
|
9. Only modify code requiring changes per migration rules, and any impacted surrounding code for context.
|
|
200
200
|
10. Provide only the transformed code as output, without explanations or additional text
|
|
201
|
-
11. Do not summarise the initial user request in a response
|
|
201
|
+
11. Do not summarise the initial user request in a response.
|
|
202
|
+
12. Use glob or grep tool usage to find the files with deprecated components.
|
|
203
|
+
13. Final result response should just be whether the migration was successful overall, or if any errors were encountered
|
|
202
204
|
|
|
203
205
|
Make the necessary updates to the files and do not respond with any explanations or reasoning.
|
|
204
206
|
|
|
@@ -207,30 +209,26 @@ You'll receive:
|
|
|
207
209
|
- Deprecated component names at the end of this prompt
|
|
208
210
|
- Migration context and rules for each deprecated component
|
|
209
211
|
|
|
210
|
-
Response formats (json object string, not inside a code snippet):
|
|
211
|
-
{
|
|
212
|
-
type: "processing" | "done",
|
|
213
|
-
path: <directory-being-processed>
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
{
|
|
217
|
-
type: "updated",
|
|
218
|
-
path: <file-path>,
|
|
219
|
-
additions: <number-of-lines-added>,
|
|
220
|
-
deletions: <number-of-lines-removed>,
|
|
221
|
-
changes: <number-of-lines-changed>,
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
{
|
|
225
|
-
type: "error",
|
|
226
|
-
path: <file-path>,
|
|
227
|
-
message: <error-message>
|
|
228
|
-
}
|
|
229
|
-
|
|
230
212
|
Deprecated components: ${DEPRECATED_COMPONENT_NAMES.join(", ")}.
|
|
231
213
|
|
|
232
214
|
${MIGRATION_RULES}`;
|
|
233
215
|
|
|
216
|
+
//#endregion
|
|
217
|
+
//#region src/transforms/list-item/helpers.ts
|
|
218
|
+
/** Split the path to get the relative path after the directory, and wrap with ANSI color codes */
|
|
219
|
+
function formatPathOutput(directory, path) {
|
|
220
|
+
return `\x1b[32m${path ? path.split(directory)[1] ?? path : directory}\x1b[0m`;
|
|
221
|
+
}
|
|
222
|
+
/** Generates a formatted string representing the total elapsed time since the given start time */
|
|
223
|
+
function generateElapsedTime(startTime) {
|
|
224
|
+
const endTime = Date.now();
|
|
225
|
+
const elapsedTime = Math.floor((endTime - startTime) / 1e3);
|
|
226
|
+
const hours = Math.floor(elapsedTime / 3600);
|
|
227
|
+
const minutes = Math.floor(elapsedTime % 3600 / 60);
|
|
228
|
+
const seconds = elapsedTime % 60;
|
|
229
|
+
return `${hours ? `${hours}h ` : ""}${minutes ? `${minutes}m ` : ""}${seconds ? `${seconds}s` : ""}`;
|
|
230
|
+
}
|
|
231
|
+
|
|
234
232
|
//#endregion
|
|
235
233
|
//#region src/transforms/list-item/claude.ts
|
|
236
234
|
const CLAUDE_SETTINGS_FILE = ".claude/settings.json";
|
|
@@ -290,26 +288,26 @@ async function queryClaude(path, options, isDebug = false) {
|
|
|
290
288
|
options,
|
|
291
289
|
prompt: path
|
|
292
290
|
});
|
|
291
|
+
const modifiedFiles = [];
|
|
293
292
|
for await (const message of result) switch (message.type) {
|
|
294
|
-
case "system": break;
|
|
295
293
|
case "assistant":
|
|
296
294
|
for (const msg of message.message.content) switch (msg.type) {
|
|
297
295
|
case "tool_use":
|
|
298
|
-
if (
|
|
299
|
-
|
|
300
|
-
|
|
296
|
+
if (msg.name === "Glob" || msg.name === "Grep") console.log(`${CONSOLE_ICONS.focus} Processing directory: ${formatPathOutput(path)}...`);
|
|
297
|
+
else if (msg.name === "Read") console.log(`${CONSOLE_ICONS.info} Reading ${formatPathOutput(path, msg.input.file_path)}`);
|
|
298
|
+
else if ((msg.name === "Write" || msg.name === "Edit") && !modifiedFiles.includes(msg.input.file_path)) {
|
|
299
|
+
modifiedFiles.push(msg.input.file_path);
|
|
300
|
+
console.log(`${CONSOLE_ICONS.info} Migrated ${formatPathOutput(path, msg.input.file_path)}`);
|
|
301
301
|
}
|
|
302
302
|
break;
|
|
303
|
-
|
|
304
|
-
default: console.log(msg);
|
|
303
|
+
default:
|
|
305
304
|
}
|
|
306
305
|
break;
|
|
307
|
-
case "user": break;
|
|
308
306
|
case "result":
|
|
309
|
-
if (message.subtype === "success") console.log(`${CONSOLE_ICONS.success} ${
|
|
307
|
+
if (message.subtype === "success") console.log(`${CONSOLE_ICONS.success} Migrated all applicable files in ${formatPathOutput(path)}`);
|
|
310
308
|
else console.log(`${CONSOLE_ICONS.error} Claude encountered an error: ${message.errors.join("\n").trim()}`);
|
|
311
309
|
break;
|
|
312
|
-
default:
|
|
310
|
+
default:
|
|
313
311
|
}
|
|
314
312
|
}
|
|
315
313
|
|
|
@@ -320,12 +318,7 @@ const transformer = async (targetPaths, codemodPath, isDebug = false) => {
|
|
|
320
318
|
const queryOptions = await initiateClaudeSessionOptions(isDebug);
|
|
321
319
|
console.log(`${CONSOLE_ICONS.info} Analysing targetted paths - this may take a while...`);
|
|
322
320
|
for (const directory of targetPaths) if (require_helpers.assessPrerequisites(directory, codemodPath)) await queryClaude(directory, queryOptions, isDebug);
|
|
323
|
-
|
|
324
|
-
const elapsedTime = Math.floor((endTime - startTime) / 1e3);
|
|
325
|
-
const hours = Math.floor(elapsedTime / 3600);
|
|
326
|
-
const minutes = Math.floor(elapsedTime % 3600 / 60);
|
|
327
|
-
const seconds = elapsedTime % 60;
|
|
328
|
-
console.log(`${CONSOLE_ICONS.success} Finished migrating - elapsed time: \x1b[1m${hours ? `${hours}h ` : ""}${minutes ? `${minutes}m ` : ""}${seconds ? `${seconds}s` : ""}\x1b[0m`);
|
|
321
|
+
console.log(`${CONSOLE_ICONS.success} Finished migrating - elapsed time: \x1b[1m${generateElapsedTime(startTime)}\x1b[0m`);
|
|
329
322
|
};
|
|
330
323
|
var transformer_default = transformer;
|
|
331
324
|
|
|
@@ -342,4 +335,4 @@ Object.defineProperty(exports, 'transformer_default', {
|
|
|
342
335
|
return transformer_default;
|
|
343
336
|
}
|
|
344
337
|
});
|
|
345
|
-
//# sourceMappingURL=transformer-
|
|
338
|
+
//# sourceMappingURL=transformer-wd_8jk1P.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transformer-wd_8jk1P.js","names":["modifiedFiles: string[]","assessPrerequisites"],"sources":["../src/constants.ts","../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 CONSOLE_ICONS = {\n info: '\\x1b[34mℹ\\x1b[0m', // Blue info icon\n focus: '\\x1b[34m➙\\x1b[0m', // Blue arrow icon\n success: '\\x1b[32m✔\\x1b[0m', // Green checkmark\n warning: '\\x1b[33m⚠\\x1b[0m', // Yellow warning icon\n error: '\\x1b[31m✖\\x1b[0m', // Red cross icon\n};\n","const DEPRECATED_COMPONENT_NAMES = [\n 'ActionOption',\n 'NavigationOption',\n 'NavigationOptionsList',\n 'Summary',\n 'SwitchOption',\n 'CheckboxOption',\n 'RadioOption',\n];\n\nconst MIGRATION_RULES = `Migration rules:\n# Legacy Component → ListItem Migration Guide\n\n## Universal Rules\n\n1. Wrap all \\`ListItem\\` in \\`<List>\\`\n2. \\`title\\` → \\`title\\` (direct)\n3. \\`content\\` or \\`description\\` → \\`subtitle\\`\n4. \\`disabled\\` stays on \\`ListItem\\` (not controls)\n5. Keep HTML attributes (\\`id\\`, \\`name\\`, \\`aria-label\\`), remove: \\`as\\`, \\`complex\\`, \\`showMediaAtAllSizes\\`, \\`showMediaCircle\\`, \\`isContainerAligned\\`\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<List><ListItem title=\"Title\" subtitle=\"Text\" control={<ListItem.Button priority=\"secondary-neutral\" onClick={fn}>Click</ListItem.Button>} /></List>\n\\`\\`\\`\n\n---\n\n## CheckboxOption → ListItem.Checkbox\n\n- \\`onChange\\`: \\`(checked: boolean)\\` → \\`(event: ChangeEvent)\\` use \\`event.target.checked\\`\n- \\`id\\`, \\`name\\` move to Checkbox\n\n\\`\\`\\`tsx\n<CheckboxOption id=\"x\" name=\"y\" title=\"Title\" content=\"Text\" checked={v} onChange={(c) => set(c)} />\n→\n<List><ListItem title=\"Title\" subtitle=\"Text\" control={<ListItem.Checkbox id=\"x\" name=\"y\" checked={v} onChange={(e) => set(e.target.checked)} />} /></List>\n\\`\\`\\`\n\n---\n\n## RadioOption → ListItem.Radio\n\n- \\`id\\`, \\`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<List><ListItem title=\"Title\" subtitle=\"Text\" control={<ListItem.Radio id=\"x\" name=\"y\" value=\"v\" checked={v==='v'} onChange={set} />} /></List>\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<List><ListItem title=\"Title\" subtitle=\"Text\" control={<ListItem.Switch checked={v} aria-label=\"Toggle\" onClick={() => set(!v)} />} /></List>\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<List><ListItem title=\"Title\" subtitle=\"Text\" control={<ListItem.Navigation onClick={fn} />} /></List>\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<List><ListItem title=\"Title\" media={<ListItem.AvatarView><Icon /></ListItem.AvatarView>} /></List>\n\\`\\`\\`\n\n---\n\n## Summary → ListItem\n\n**Basic:**\n\n- \\`icon\\` → wrap in \\`ListItem.AvatarView\\` with \\`size={32}\\` as \\`media\\`\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\n\n\\`\\`\\`tsx\n// Basic\n<Summary title=\"T\" description=\"D\" icon={<Icon />} />\n→\n<List><ListItem title=\"T\" subtitle=\"D\" media={<ListItem.AvatarView size={32}><Icon /></ListItem.AvatarView>} /></List>\n\n// Status\n<Summary title=\"T\" description=\"D\" icon={<Icon />} status={Status.DONE} />\n→\n<List><ListItem title=\"T\" subtitle=\"D\" media={<ListItem.AvatarView size={32} badge={{status:'positive'}}><Icon /></ListItem.AvatarView>} /></List>\n\n// Action\n<Summary title=\"T\" description=\"D\" icon={<Icon />} action={{text:'Go', href:'/go'}} />\n→\n<List><ListItem title=\"T\" subtitle=\"D\" media={<ListItem.AvatarView size={32}><Icon /></ListItem.AvatarView>} additionalInfo={<ListItem.AdditionalInfo action={{label:'Go', href:'/go'}} />} /></List>\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<List><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>} /></List>\n\n// Popover\n<Summary title=\"T\" description=\"D\" icon={<Icon />} info={{title:'Help', content:'Text', presentation:'POPOVER', 'aria-label':'Info'}} />\n→\n<List><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>} /></List>\n\\`\\`\\`\n\n---\n\n## DefinitionList → Multiple ListItem\n\n- Array → individual \\`ListItem\\`s\n- \\`value\\` → \\`subtitle\\`\n- \\`key\\` → React \\`key\\` prop\n- Action type: \"Edit\"/\"Update\"/\"View\" → \\`ListItem.Button priority=\"secondary-neutral\"\\`, \"Change\"/\"Password\" → \\`ListItem.Navigation\\`, \"Copy\" → \\`ListItem.IconButton\\`\n\n\\`\\`\\`tsx\n<DefinitionList definitions={[\n {title:'T1', value:'V1', key:'k1'},\n {title:'T2', value:'V2', key:'k2', action:{label:'Edit', onClick:fn}}\n]} />\n→\n<List>\n <ListItem key=\"k1\" title=\"T1\" subtitle=\"V1\" />\n <ListItem key=\"k2\" title=\"T2\" subtitle=\"V2\" control={<ListItem.Button priority=\"secondary-neutral\" onClick={fn}>Edit</ListItem.Button>} />\n</List>\n\\`\\`\\`\n`;\n\nexport const SYSTEM_PROMPT = `Transform TypeScript/JSX code from legacy Wise Design System (WDS) components to the new ListItem component and ListItem subcomponents from '@transferwise/components'.\n\nRead and transform each file individually, instead of reading them all at the start. Only make a single edit or write per file, once you've fully processed it.\n\nRules:\n1. Ignore any files that do not contain deprecated WDS components, unless they are necessary for context.\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. Provide only the transformed code as output, without explanations or additional text\n11. Do not summarise the initial user request in a response.\n12. Use glob or grep tool usage to find the files with deprecated components.\n13. Final result response should just be whether the migration was successful overall, or if any errors were encountered\n\nMake the necessary updates to the files and do not respond with any explanations or reasoning. \n\nYou'll receive:\n- File paths/directories to search 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): string {\n return `\\x1b[32m${path ? (path.split(directory)[1] ?? path) : directory}\\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","import { type Options, query } from '@anthropic-ai/claude-agent-sdk';\nimport { execSync } from 'child_process';\nimport { readFileSync } from 'fs';\nimport { resolve } from 'path';\n\nimport { CONSOLE_ICONS } from '../../constants';\nimport { SYSTEM_PROMPT } from './constants';\nimport { formatPathOutput } from './helpers';\nimport type { ClaudeResponseToolUse, ClaudeSettings } from './types';\n\nconst CLAUDE_SETTINGS_FILE = '.claude/settings.json';\n\nexport function getQueryOptions(sessionId?: string, isDebug?: boolean): 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) {\n throw new Error(\n 'Failed to retrieve Anthropic API key. Please check your Claude Code x LLM Gateway configuration - https://transferwise.atlassian.net/wiki/x/_YUe3Q',\n );\n }\n\n const envVars = {\n ANTHROPIC_AUTH_TOKEN: apiKey,\n ANTHROPIC_BASE_URL: settings?.env?.ANTHROPIC_BASE_URL,\n ANTHROPIC_CUSTOM_HEADERS: settings?.env?.ANTHROPIC_CUSTOM_HEADERS,\n ANTHROPIC_DEFAULT_SONNET_MODEL: settings.env?.ANTHROPIC_DEFAULT_SONNET_MODEL,\n ANTHROPIC_DEFAULT_HAIKU_MODEL: settings.env?.ANTHROPIC_DEFAULT_HAIKU_MODEL,\n ANTHROPIC_DEFAULT_OPUS_MODEL: settings.env?.ANTHROPIC_DEFAULT_OPUS_MODEL,\n API_TIMEOUT_MS: settings.env?.API_TIMEOUT_MS,\n PATH: process.env.PATH, // Specifying PATH as Claude Agent SDK seems to struggle consuming the actual environment PATH\n };\n\n // if (isDebug) {\n // console.debug(`${CONSOLE_ICONS.info} Resolved Claude environment variables:`, JSON.stringify(envVars));\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 settingSources: ['local', 'project', 'user'],\n };\n}\n\n// Initiate a new Claude session/conversation and return reusable session ID\nexport async function initiateClaudeSessionOptions(isDebug = false): Promise<Options> {\n console.log(\n `${CONSOLE_ICONS.info} Starting Claude instance - your browser may open for Okta authentication if required.`,\n );\n\n const options = getQueryOptions(undefined, isDebug);\n const result = query({\n options,\n prompt: `You'll be given directories 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 console.log(`${CONSOLE_ICONS.success} Successfully initialised Claude instance`);\n options.resume = message.session_id;\n }\n break;\n default:\n if (message.type === 'result' && message.subtype !== 'success') {\n console.log(\n `${CONSOLE_ICONS.error} Claude encountered an error: ${message.errors.join('\\n')}`,\n );\n }\n }\n }\n\n return options;\n}\n\nexport async function queryClaude(path: string, options: Options, isDebug = false) {\n const result = query({\n options,\n prompt: path,\n });\n const modifiedFiles: string[] = [];\n\n for await (const message of result) {\n switch (message.type) {\n case 'assistant':\n for (const msg of message.message.content) {\n switch (msg.type) {\n // Handles logging of tool uses to determine key stages of the migration ()\n case 'tool_use':\n if (msg.name === 'Glob' || msg.name === 'Grep') {\n console.log(\n `${CONSOLE_ICONS.focus} Processing directory: ${formatPathOutput(path)}...`,\n );\n // NOTE: Possibly want to only log reading files when in debug mode, to reduce noise\n } else if (msg.name === 'Read') {\n console.log(\n `${CONSOLE_ICONS.info} Reading ${formatPathOutput(path, (msg as ClaudeResponseToolUse).input.file_path)}`,\n );\n } else if (\n (msg.name === 'Write' || msg.name === 'Edit') &&\n !modifiedFiles.includes((msg as ClaudeResponseToolUse).input.file_path) // Safeguard against duplicate logs, where Claude may write multiple times to the same file\n ) {\n modifiedFiles.push((msg as ClaudeResponseToolUse).input.file_path);\n console.log(\n `${CONSOLE_ICONS.info} Migrated ${formatPathOutput(path, (msg as ClaudeResponseToolUse).input.file_path)}`,\n );\n }\n break;\n default:\n }\n }\n break;\n case 'result':\n if (message.subtype === 'success') {\n // TODO: Handle case where migration failed for some files?\n console.log(\n `${CONSOLE_ICONS.success} Migrated all applicable files in ${formatPathOutput(path)}`,\n );\n } else {\n console.log(\n `${CONSOLE_ICONS.error} Claude encountered an error: ${message.errors.join('\\n').trim()}`,\n );\n }\n break;\n default:\n }\n }\n}\n","import { CONSOLE_ICONS } from '../../constants';\nimport { assessPrerequisites } from '../../controller/helpers';\nimport { initiateClaudeSessionOptions, queryClaude } from './claude';\nimport { generateElapsedTime } from './helpers';\n\nconst transformer = async (targetPaths: string[], codemodPath: string, isDebug = false) => {\n const startTime = Date.now();\n\n // TODO: We need to check whether the user is connected to the VPN\n\n const queryOptions = await initiateClaudeSessionOptions(isDebug);\n\n console.log(`${CONSOLE_ICONS.info} Analysing targetted paths - this may take a while...`);\n\n for (const directory of targetPaths) {\n const isCompliant = assessPrerequisites(directory, codemodPath);\n\n if (isCompliant) {\n // TODO: Potential improvement could be getting all of the file paths first -\n // Make sure claude can still handle related files (imported components/wrapping parents)\n // Get all files within directory, and call queryClaude for each file\n await queryClaude(directory, queryOptions, isDebug);\n }\n\n // await queryClaude(directory, queryOptions, isDebug);\n }\n\n console.log(\n `${CONSOLE_ICONS.success} Finished migrating - elapsed time: \\x1b[1m${generateElapsedTime(startTime)}\\x1b[0m`,\n );\n};\n\nexport default transformer;\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wise/wds-codemods",
|
|
3
|
-
"version": "1.0.0-experimental-
|
|
3
|
+
"version": "1.0.0-experimental-a536a63",
|
|
4
4
|
"license": "UNLICENSED",
|
|
5
5
|
"author": "Wise Payments Ltd.",
|
|
6
6
|
"repository": {
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"test:watch": "jest --watch"
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@anthropic-ai/claude-agent-sdk": "^0.1.
|
|
37
|
+
"@anthropic-ai/claude-agent-sdk": "^0.1.37",
|
|
38
38
|
"@inquirer/prompts": "^7.8.6",
|
|
39
39
|
"jscodeshift": "^17.3"
|
|
40
40
|
},
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"transformer-B9Mt_gBf.js","names":["assessPrerequisites"],"sources":["../src/constants.ts","../src/transforms/list-item/constants.ts","../src/transforms/list-item/claude.ts","../src/transforms/list-item/transformer.ts"],"sourcesContent":["export const CONSOLE_ICONS = {\n info: '\\x1b[34mℹ\\x1b[0m', // Blue info icon\n focus: '\\x1b[34m➙\\x1b[0m', // Blue arrow icon\n success: '\\x1b[32m✔\\x1b[0m', // Green checkmark\n warning: '\\x1b[33m⚠\\x1b[0m', // Yellow warning icon\n error: '\\x1b[31m✖\\x1b[0m', // Red cross icon\n};\n","const DEPRECATED_COMPONENT_NAMES = [\n 'ActionOption',\n 'NavigationOption',\n 'NavigationOptionsList',\n 'Summary',\n 'SwitchOption',\n 'CheckboxOption',\n 'RadioOption',\n];\n\nconst MIGRATION_RULES = `Migration rules:\n# Legacy Component → ListItem Migration Guide\n\n## Universal Rules\n\n1. Wrap all \\`ListItem\\` in \\`<List>\\`\n2. \\`title\\` → \\`title\\` (direct)\n3. \\`content\\` or \\`description\\` → \\`subtitle\\`\n4. \\`disabled\\` stays on \\`ListItem\\` (not controls)\n5. Keep HTML attributes (\\`id\\`, \\`name\\`, \\`aria-label\\`), remove: \\`as\\`, \\`complex\\`, \\`showMediaAtAllSizes\\`, \\`showMediaCircle\\`, \\`isContainerAligned\\`\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<List><ListItem title=\"Title\" subtitle=\"Text\" control={<ListItem.Button priority=\"secondary-neutral\" onClick={fn}>Click</ListItem.Button>} /></List>\n\\`\\`\\`\n\n---\n\n## CheckboxOption → ListItem.Checkbox\n\n- \\`onChange\\`: \\`(checked: boolean)\\` → \\`(event: ChangeEvent)\\` use \\`event.target.checked\\`\n- \\`id\\`, \\`name\\` move to Checkbox\n\n\\`\\`\\`tsx\n<CheckboxOption id=\"x\" name=\"y\" title=\"Title\" content=\"Text\" checked={v} onChange={(c) => set(c)} />\n→\n<List><ListItem title=\"Title\" subtitle=\"Text\" control={<ListItem.Checkbox id=\"x\" name=\"y\" checked={v} onChange={(e) => set(e.target.checked)} />} /></List>\n\\`\\`\\`\n\n---\n\n## RadioOption → ListItem.Radio\n\n- \\`id\\`, \\`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<List><ListItem title=\"Title\" subtitle=\"Text\" control={<ListItem.Radio id=\"x\" name=\"y\" value=\"v\" checked={v==='v'} onChange={set} />} /></List>\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<List><ListItem title=\"Title\" subtitle=\"Text\" control={<ListItem.Switch checked={v} aria-label=\"Toggle\" onClick={() => set(!v)} />} /></List>\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<List><ListItem title=\"Title\" subtitle=\"Text\" control={<ListItem.Navigation onClick={fn} />} /></List>\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<List><ListItem title=\"Title\" media={<ListItem.AvatarView><Icon /></ListItem.AvatarView>} /></List>\n\\`\\`\\`\n\n---\n\n## Summary → ListItem\n\n**Basic:**\n\n- \\`icon\\` → wrap in \\`ListItem.AvatarView\\` with \\`size={32}\\` as \\`media\\`\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\n\n\\`\\`\\`tsx\n// Basic\n<Summary title=\"T\" description=\"D\" icon={<Icon />} />\n→\n<List><ListItem title=\"T\" subtitle=\"D\" media={<ListItem.AvatarView size={32}><Icon /></ListItem.AvatarView>} /></List>\n\n// Status\n<Summary title=\"T\" description=\"D\" icon={<Icon />} status={Status.DONE} />\n→\n<List><ListItem title=\"T\" subtitle=\"D\" media={<ListItem.AvatarView size={32} badge={{status:'positive'}}><Icon /></ListItem.AvatarView>} /></List>\n\n// Action\n<Summary title=\"T\" description=\"D\" icon={<Icon />} action={{text:'Go', href:'/go'}} />\n→\n<List><ListItem title=\"T\" subtitle=\"D\" media={<ListItem.AvatarView size={32}><Icon /></ListItem.AvatarView>} additionalInfo={<ListItem.AdditionalInfo action={{label:'Go', href:'/go'}} />} /></List>\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<List><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>} /></List>\n\n// Popover\n<Summary title=\"T\" description=\"D\" icon={<Icon />} info={{title:'Help', content:'Text', presentation:'POPOVER', 'aria-label':'Info'}} />\n→\n<List><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>} /></List>\n\\`\\`\\`\n\n---\n\n## DefinitionList → Multiple ListItem\n\n- Array → individual \\`ListItem\\`s\n- \\`value\\` → \\`subtitle\\`\n- \\`key\\` → React \\`key\\` prop\n- Action type: \"Edit\"/\"Update\"/\"View\" → \\`ListItem.Button priority=\"secondary-neutral\"\\`, \"Change\"/\"Password\" → \\`ListItem.Navigation\\`, \"Copy\" → \\`ListItem.IconButton\\`\n\n\\`\\`\\`tsx\n<DefinitionList definitions={[\n {title:'T1', value:'V1', key:'k1'},\n {title:'T2', value:'V2', key:'k2', action:{label:'Edit', onClick:fn}}\n]} />\n→\n<List>\n <ListItem key=\"k1\" title=\"T1\" subtitle=\"V1\" />\n <ListItem key=\"k2\" title=\"T2\" subtitle=\"V2\" control={<ListItem.Button priority=\"secondary-neutral\" onClick={fn}>Edit</ListItem.Button>} />\n</List>\n\\`\\`\\`\n`;\n\nexport const SYSTEM_PROMPT = `Transform TypeScript/JSX code from legacy Wise Design System (WDS) components to the new ListItem component and ListItem subcomponents from '@transferwise/components'.\n\nRead and transform each file one at a time, instead of reading them all at the start.\n\nRules:\n1. Ignore any files that do not contain deprecated WDS components, unless they are necessary for context.\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. Provide only the transformed code as output, without explanations or additional text\n11. Do not summarise the initial user request in a response, only use the response formats in JSON for all communication.\n\nMake the necessary updates to the files and do not respond with any explanations or reasoning. \n\nYou'll receive:\n- File paths/directories to search in individual queries\n- Deprecated component names at the end of this prompt\n- Migration context and rules for each deprecated component\n\nResponse formats (json object string, not inside a code snippet):\n{\n type: \"processing\" | \"done\",\n path: <directory-being-processed>\n}\n\n{\n type: \"updated\",\n path: <file-path>,\n additions: <number-of-lines-added>,\n deletions: <number-of-lines-removed>,\n changes: <number-of-lines-changed>,\n}\n\n{\n type: \"error\",\n path: <file-path>,\n message: <error-message>\n}\n\nDeprecated components: ${DEPRECATED_COMPONENT_NAMES.join(', ')}.\n\n${MIGRATION_RULES}`;\n","import { type Options, query } from '@anthropic-ai/claude-agent-sdk';\nimport { execSync } from 'child_process';\nimport { readFileSync } from 'fs';\nimport { resolve } from 'path';\n\nimport { CONSOLE_ICONS } from '../../constants';\nimport { SYSTEM_PROMPT } from './constants';\nimport type { ClaudeResponseMessage } from './types';\n\ninterface ClaudeSettings {\n apiKeyHelper?: string;\n env?: {\n ANTHROPIC_BASE_URL?: string;\n ANTHROPIC_CUSTOM_HEADERS?: string;\n ANTHROPIC_DEFAULT_SONNET_MODEL?: string;\n ANTHROPIC_DEFAULT_HAIKU_MODEL?: string;\n ANTHROPIC_DEFAULT_OPUS_MODEL?: string;\n API_TIMEOUT_MS?: string;\n [key: string]: unknown;\n };\n [key: string]: unknown;\n}\n\nconst CLAUDE_SETTINGS_FILE = '.claude/settings.json';\n\nexport function getQueryOptions(sessionId?: string, isDebug?: boolean): 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) {\n throw new Error(\n 'Failed to retrieve Anthropic API key. Please check your Claude Code x LLM Gateway configuration - https://transferwise.atlassian.net/wiki/x/_YUe3Q',\n );\n }\n\n const envVars = {\n ANTHROPIC_AUTH_TOKEN: apiKey,\n ANTHROPIC_BASE_URL: settings?.env?.ANTHROPIC_BASE_URL,\n ANTHROPIC_CUSTOM_HEADERS: settings?.env?.ANTHROPIC_CUSTOM_HEADERS,\n ANTHROPIC_DEFAULT_SONNET_MODEL: settings.env?.ANTHROPIC_DEFAULT_SONNET_MODEL,\n ANTHROPIC_DEFAULT_HAIKU_MODEL: settings.env?.ANTHROPIC_DEFAULT_HAIKU_MODEL,\n ANTHROPIC_DEFAULT_OPUS_MODEL: settings.env?.ANTHROPIC_DEFAULT_OPUS_MODEL,\n API_TIMEOUT_MS: settings.env?.API_TIMEOUT_MS,\n PATH: process.env.PATH, // Specifying PATH as Claude Agent SDK seems to struggle consuming the actual environment PATH\n };\n\n // if (isDebug) {\n // console.debug(`${CONSOLE_ICONS.info} Resolved Claude environment variables:`, JSON.stringify(envVars));\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 settingSources: ['local', 'project', 'user'],\n };\n}\n\n// Initiate a new Claude session/conversation and return reusable session ID\nexport async function initiateClaudeSessionOptions(isDebug = false): Promise<Options> {\n console.log(\n `${CONSOLE_ICONS.info} Starting Claude instance - your browser may open for Okta authentication if required.`,\n );\n\n const options = getQueryOptions(undefined, isDebug);\n const result = query({\n options,\n prompt: `You'll be given directories 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 console.log(`${CONSOLE_ICONS.success} Successfully initialised Claude instance`);\n options.resume = message.session_id;\n }\n break;\n default:\n if (message.type === 'result' && message.subtype !== 'success') {\n console.log(\n `${CONSOLE_ICONS.error} Claude encountered an error: ${message.errors.join('\\n')}`,\n );\n }\n }\n }\n\n return options;\n}\n\nexport async function queryClaude(path: string, options: Options, isDebug = false) {\n const result = query({\n options,\n prompt: path,\n });\n\n // TODO: Ensure we're handling all potential types of messages here.\n for await (const message of result) {\n switch (message.type) {\n case 'system':\n break;\n case 'assistant':\n for (const msg of message.message.content) {\n switch (msg.type) {\n // TODO: Handle tool usage for determining file being read/changed\n case 'tool_use':\n if (isDebug) {\n if (msg.name === 'Read') {\n // TODO:\n console.log(`${CONSOLE_ICONS.info} Processing: ${msg.input.file_path}`);\n } else if (msg.name === 'Write') {\n console.log(`${CONSOLE_ICONS.info} Updated: ${msg.input.file_path}`);\n }\n }\n break;\n case 'text':\n // TODO: Doesn't work - needs fixing\n // const parsedMessage = JSON.parse(msg.text) as ClaudeResponseMessage;\n // // if (msg.text.includes('Processing')) {\n // // const [prefix, directory] = msg.text.split(' ');\n // // console.log(`${CONSOLE_ICONS.info} ${prefix} \\x1b[32m${directory}\\x1b[0m...`);\n // // }\n // switch (parsedMessage.type) {\n // case 'processing':\n // console.log(\n // `${CONSOLE_ICONS.info} Processing \\x1b[32m${parsedMessage.path}\\x1b[0m...`,\n // );\n // break;\n // case 'done':\n // // NOTE: Don't think this is ever used tbf\n // console.log(\n // `${CONSOLE_ICONS.success} Finished processing: \\x1b[32m${parsedMessage.path}\\x1b[0m`,\n // );\n // break;\n // case 'updated':\n // console.log(\n // `${CONSOLE_ICONS.success} \\x1b[32m${parsedMessage.path}\\x1b[0m - ${parsedMessage.additions} additions, ${parsedMessage.deletions} deletions, ${parsedMessage.changes} changes`,\n // );\n // break;\n // case 'error':\n // console.log(\n // `${CONSOLE_ICONS.error} Something went wrong - \\x1b[32m${parsedMessage.path}\\x1b[0m - ${parsedMessage.message}`,\n // );\n // break;\n // default:\n // console.log(JSON.stringify(parsedMessage));\n break;\n default:\n console.log(msg);\n }\n }\n\n // TODO: Handle things that need manual review/added to report.\n\n break;\n case 'user':\n // TODO: Can use these for dry run/logging changes to console.\n // TODO: Can also identify tool usage to log which files are being changed.\n // console.log(`User: ${JSON.stringify(message)}`);\n break;\n case 'result':\n if (message.subtype === 'success') {\n console.log(\n `${CONSOLE_ICONS.success} ${message.result.trim().split('\\n').join(`\\n${CONSOLE_ICONS.success} `)}`,\n );\n } else {\n console.log(\n `${CONSOLE_ICONS.error} Claude encountered an error: ${message.errors.join('\\n').trim()}`,\n );\n }\n\n break;\n default:\n // console.log(JSON.stringify(message));\n break;\n }\n }\n}\n","import { query } from '@anthropic-ai/claude-agent-sdk';\n\nimport { CONSOLE_ICONS } from '../../constants';\nimport { assessPrerequisites } from '../../controller/helpers';\nimport { getQueryOptions, initiateClaudeSessionOptions, queryClaude } from './claude';\n\nconst transformer = async (targetPaths: string[], codemodPath: string, isDebug = false) => {\n const startTime = Date.now();\n\n // TODO: We need to confirm you're connected to the VPN\n\n const queryOptions = await initiateClaudeSessionOptions(isDebug);\n\n console.log(`${CONSOLE_ICONS.info} Analysing targetted paths - this may take a while...`);\n\n // TODO: Potential improvement could be getting all of the file paths first -\n for (const directory of targetPaths) {\n const isCompliant = assessPrerequisites(directory, codemodPath);\n\n if (isCompliant) {\n // Get all files within directory, and call queryClaude for each file\n await queryClaude(directory, queryOptions, isDebug);\n }\n\n // await queryClaude(directory, queryOptions, isDebug);\n }\n\n // TODO: Move to utility function\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 console.log(\n `${CONSOLE_ICONS.success} Finished migrating - elapsed time: \\x1b[1m${hours ? `${hours}h ` : ''}${minutes ? `${minutes}m ` : ''}${seconds ? `${seconds}s` : ''}\\x1b[0m`,\n );\n};\n\nexport default transformer;\n"],"mappings":"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"}
|