@wise/wds-codemods 1.1.3-experimental-378bc79 → 1.1.3-experimental-4388372

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.
@@ -0,0 +1,255 @@
1
+ const require_common = require('./common-DdEQmI2h.js');
2
+ let node_child_process = require("node:child_process");
3
+ let node_path = require("node:path");
4
+ let node_fs = require("node:fs");
5
+ let _anthropic_ai_claude_agent_sdk = require("@anthropic-ai/claude-agent-sdk");
6
+ let node_https = require("node:https");
7
+ node_https = require_common.__toESM(node_https);
8
+ let _listr2_manager = require("@listr2/manager");
9
+ let listr2 = require("listr2");
10
+
11
+ //#region src/constants/claude.ts
12
+ const CLAUDE_SETTINGS_FILE = ".claude/settings.json";
13
+ const VPN_COUNTDOWN_TIMEOUT = 5;
14
+ const DIRECTORY_CONCURRENCY_LIMIT = 3;
15
+ const FILE_CONCURRENCY_LIMIT = 10;
16
+ const INITIAL_CLAUDE_PROMPT = `You are a code migration assistant that helps migrate TypeScript/JSX code of deprecated Wise Design System (WDS) components from '@transferwise/components', to their new replacement components as detailed in the relevant migration guide. The following is a list of rules that must always be followed throughout the migration process.
17
+
18
+ Rules:
19
+ 1. Only ever modify files via the Edit tool - do not use the Write tool
20
+ 2. When identifying what code to migrate within a file, explain how you identified it first.
21
+ 3. Migrate components per provided migration rules
22
+ 4. Maintain TypeScript type safety and update types to match new API
23
+ 5. Map props: handle renamed, deprecated, new required, and changed types
24
+ 6. Update imports to new WDS components and types
25
+ 7. Preserve code style, formatting, and calculated logic
26
+ 8. Handle conditional rendering, spread props, and complex expressions
27
+ 9. Note: New components may lack feature parity with legacy versions
28
+ 10. Only modify code requiring changes per migration rules, and any impacted surrounding code for context.
29
+ 11. Final result response should just be whether the migration was successful overall, or if any errors were encountered
30
+ - Do not summarise or explain the changes made
31
+ 12. Explain your reasoning and justification before making changes, as you edit each file.
32
+ - Keep it concise and succinct, as only bullet points
33
+ 13. After modifying the file, do not summarise the changes made.
34
+ 14. If you do not have permission to edit a file, still attempt to edit it and then move onto the next file.
35
+
36
+ Additionally you'll receive:
37
+ - File paths to migrate in individual queries
38
+ - Deprecated component names at the end of this prompt
39
+ - Migration context and guide for each deprecated component`;
40
+
41
+ //#endregion
42
+ //#region src/helpers/common/pathUtils.ts
43
+ /** Split the path string to get the relative path after the directory, and wrap with ANSI color codes */
44
+ function formatPathOutput(directory, path, asDim) {
45
+ const relativePath = path ? path.split(directory.replace(".", ""))[1] ?? path : directory;
46
+ return asDim ? `\x1b[2m${relativePath}\x1b[0m` : `\x1b[32m${relativePath}\x1b[0m`;
47
+ }
48
+
49
+ //#endregion
50
+ //#region src/helpers/common/timerUtils.ts
51
+ /** Generates a formatted string representing the total elapsed time since the given start time */
52
+ function generateElapsedTime(startTime) {
53
+ const endTime = Date.now();
54
+ const elapsedTime = Math.floor((endTime - startTime) / 1e3);
55
+ const hours = Math.floor(elapsedTime / 3600);
56
+ const minutes = Math.floor(elapsedTime % 3600 / 60);
57
+ const seconds = elapsedTime % 60;
58
+ return `${hours ? `${hours}h ` : ""}${minutes ? `${minutes}m ` : ""}${seconds ? `${seconds}s` : ""}`;
59
+ }
60
+
61
+ //#endregion
62
+ //#region src/helpers/common/vpnUtils.ts
63
+ /** Checks VPN connectivity by pinging the provided base URL's /health endpoint, with countdown retries */
64
+ async function checkVPN(baseUrl, task) {
65
+ if (!baseUrl) return;
66
+ const checkOnce = async () => new Promise((resolveCheck) => {
67
+ const url = new URL("/health", baseUrl);
68
+ const req = node_https.default.get(url, {
69
+ timeout: 2e3,
70
+ rejectUnauthorized: false
71
+ }, (res) => {
72
+ const ok = !!(res.statusCode && res.statusCode >= 200 && res.statusCode < 400);
73
+ res.resume();
74
+ resolveCheck(ok);
75
+ });
76
+ req.on("timeout", () => {
77
+ req.destroy(/* @__PURE__ */ new Error("timeout"));
78
+ });
79
+ req.on("error", () => resolveCheck(false));
80
+ });
81
+ while (true) {
82
+ if (await checkOnce()) {
83
+ task.title = "Connected to VPN";
84
+ break;
85
+ }
86
+ for (let countdown = VPN_COUNTDOWN_TIMEOUT; countdown > 0; countdown -= 1) {
87
+ task.output = `Please connect to VPN... retrying in ${countdown}s`;
88
+ await new Promise((response) => {
89
+ setTimeout(response, 1e3);
90
+ });
91
+ }
92
+ }
93
+ }
94
+
95
+ //#endregion
96
+ //#region src/helpers/claude/query.ts
97
+ /**
98
+ * Creates reusable query options for Claude session, including authentication and system prompt
99
+ */
100
+ function getQueryOptions({ additionalPromptContext }) {
101
+ const claudeSettingsPath = (0, node_path.resolve)(process.env.HOME || "", CLAUDE_SETTINGS_FILE);
102
+ const settings = JSON.parse((0, node_fs.readFileSync)(claudeSettingsPath, "utf-8"));
103
+ let apiKey;
104
+ try {
105
+ apiKey = (0, node_child_process.execSync)(`bash ${settings.apiKeyHelper}`, { encoding: "utf-8" }).trim();
106
+ } catch {}
107
+ if (!apiKey || !settings.env?.ANTHROPIC_BASE_URL) throw new Error("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");
108
+ const { ANTHROPIC_CUSTOM_HEADERS, ...restEnvVars } = settings?.env ?? {};
109
+ return {
110
+ env: {
111
+ ANTHROPIC_AUTH_TOKEN: apiKey,
112
+ ANTHROPIC_CUSTOM_HEADERS,
113
+ ...restEnvVars,
114
+ PATH: process.env.PATH
115
+ },
116
+ permissionMode: "acceptEdits",
117
+ systemPrompt: {
118
+ type: "preset",
119
+ preset: "claude_code",
120
+ append: `${INITIAL_CLAUDE_PROMPT}\n${additionalPromptContext}`
121
+ },
122
+ allowedTools: ["Grep", "Read"],
123
+ settingSources: [
124
+ "local",
125
+ "project",
126
+ "user"
127
+ ]
128
+ };
129
+ }
130
+ /**
131
+ * Initiate a new Claude session/conversation and return reusable options
132
+ */
133
+ async function initiateClaudeSessionOptions(manager, additionalPromptContext) {
134
+ let options = {};
135
+ manager.add([{
136
+ title: "Configuring Claude connection and instance...",
137
+ task: async (ctx, task) => {
138
+ options = getQueryOptions({ additionalPromptContext });
139
+ return task.newListr([{
140
+ title: "Checking VPN connection...",
141
+ task: async (subCtx, subtask) => checkVPN(options.env?.ANTHROPIC_BASE_URL, subtask)
142
+ }, {
143
+ title: "Initialising Claude session",
144
+ task: async (subCtx, subtask) => {
145
+ subtask.output = "Your browser may open for Okta authentication if required";
146
+ const result = (0, _anthropic_ai_claude_agent_sdk.query)({
147
+ options,
148
+ prompt: `This is an initialisation query to start a new Claude code migration session. No text response is needed.`
149
+ });
150
+ for await (const message of result) switch (message.type) {
151
+ case "system":
152
+ if (message.subtype === "init" && !options.resume) options.resume = message.session_id;
153
+ break;
154
+ default: if (message.type === "result" && message.subtype !== "success") throw new Error(`Claude encountered an error when initialising: ${message.errors.join("\n")}`);
155
+ }
156
+ task.title = "Successfully configured and initialised Claude\n";
157
+ }
158
+ }]);
159
+ }
160
+ }]);
161
+ await manager.runAll().finally(() => {
162
+ manager.options = { concurrent: DIRECTORY_CONCURRENCY_LIMIT };
163
+ });
164
+ return options;
165
+ }
166
+ /**
167
+ * Queries Claude with the given path and handles logging of success/error messages
168
+ */
169
+ async function queryClaude(directory, filePath, options, task, isDebug = false) {
170
+ const startTime = Date.now();
171
+ const result = (0, _anthropic_ai_claude_agent_sdk.query)({
172
+ options,
173
+ prompt: filePath
174
+ });
175
+ for await (const message of result) switch (message.type) {
176
+ case "result":
177
+ if (message.subtype === "success" && isDebug) {
178
+ task.title = `\x1b[2m${formatPathOutput(directory, filePath)}\x1b[0m]`;
179
+ task.output = `\x1b[2mMigrated in ${generateElapsedTime(startTime)}\x1b[0m`;
180
+ } else if (message.is_error) {
181
+ task.title = `\x1b[2m${formatPathOutput(directory, filePath)}\x1b[0m]`;
182
+ task.output = `${require_common.CONSOLE_ICONS.error} Claude encountered an error: ${JSON.stringify(message)}`;
183
+ }
184
+ break;
185
+ default:
186
+ }
187
+ }
188
+
189
+ //#endregion
190
+ //#region src/helpers/claude/transformer.ts
191
+ /**
192
+ * Performs code migration using Claude for the specified target directories, for the provided deprecated components and migration guides.
193
+ * @param targetDirectories - Array of directory paths to process
194
+ * @param componentGrepPattern - RegExp pattern to identify files needing migration (e.g. via deprecated component imports)
195
+ * @param isDebug - Whether to enable debug logging
196
+ * @param additionalPromptContext - Additional context to include in the initial Claude prompt before processing files
197
+ */
198
+ const claudeTransformer = async ({ targetDirectories, componentGrepPattern, additionalPromptContext, isDebug = false }) => {
199
+ process.setMaxListeners(20);
200
+ const startTime = Date.now();
201
+ const manager = new _listr2_manager.Manager({ concurrent: true });
202
+ const queryOptions = await initiateClaudeSessionOptions(manager, additionalPromptContext);
203
+ manager.add([{
204
+ title: "Processing target directories for migration...",
205
+ task: async (ctx, task) => {
206
+ return task.newListr(targetDirectories.map((directory) => {
207
+ const matchingFilePaths = (0, node_child_process.execSync)(`find "${directory}" -name "*.tsx" -type f`, { encoding: "utf-8" }).trim().split("\n").filter(Boolean).filter((filePath) => {
208
+ const content = (0, node_fs.readFileSync)(filePath, "utf-8");
209
+ return componentGrepPattern.test(content);
210
+ });
211
+ if (matchingFilePaths.length === 0) return {
212
+ title: `\x1b[2m${formatPathOutput(directory)} - No files need migration\x1b[0m`,
213
+ task: (subCtx) => {
214
+ subCtx.skip = true;
215
+ }
216
+ };
217
+ return {
218
+ title: `${formatPathOutput(directory)} - Found \x1b[32m${matchingFilePaths.length}\x1b[0m file(s) needing migration`,
219
+ task: async (subCtx, parentTask) => {
220
+ parentTask.title = `${formatPathOutput(directory)} - Migrating \x1b[32m${matchingFilePaths.length}\x1b[0m file(s)...`;
221
+ const completedFilesInDirectory = { count: 0 };
222
+ return parentTask.newListr(matchingFilePaths.map((filePath) => ({
223
+ title: "",
224
+ task: async (fileCtx, fileTask) => {
225
+ await queryClaude(directory, filePath, queryOptions, fileTask, isDebug).finally(() => {
226
+ completedFilesInDirectory.count += 1;
227
+ const isDim = completedFilesInDirectory.count === matchingFilePaths.length;
228
+ parentTask.title = `${isDim ? "\x1B[2m" : ""}${formatPathOutput(directory)} - Migrated \x1b[32m${completedFilesInDirectory.count}\x1b[0m/\x1b[32m${matchingFilePaths.length}\x1b[0m files${isDim ? "\x1B[0m" : ""}`;
229
+ });
230
+ }
231
+ })), { concurrent: FILE_CONCURRENCY_LIMIT }).run();
232
+ }
233
+ };
234
+ }), { rendererOptions: {
235
+ suffixSkips: false,
236
+ collapseSubtasks: false
237
+ } });
238
+ }
239
+ }]);
240
+ await manager.runAll().finally(async () => {
241
+ await new listr2.Listr([{
242
+ title: `Finished migrating - elapsed time: \x1b[32m${generateElapsedTime(startTime)}\x1b[0m `,
243
+ task: async () => {}
244
+ }]).run();
245
+ });
246
+ };
247
+
248
+ //#endregion
249
+ Object.defineProperty(exports, 'claudeTransformer', {
250
+ enumerable: true,
251
+ get: function () {
252
+ return claudeTransformer;
253
+ }
254
+ });
255
+ //# sourceMappingURL=claude-BHYfEMfb.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"claude-BHYfEMfb.js","names":["https","options: Options","CONSOLE_ICONS","Manager","Listr"],"sources":["../src/constants/claude.ts","../src/helpers/common/pathUtils.ts","../src/helpers/common/timerUtils.ts","../src/helpers/common/vpnUtils.ts","../src/helpers/claude/query.ts","../src/helpers/claude/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;\n\nexport const INITIAL_CLAUDE_PROMPT = `You are a code migration assistant that helps migrate TypeScript/JSX code of deprecated Wise Design System (WDS) components from '@transferwise/components', to their new replacement components as detailed in the relevant migration guide. The following is a list of rules that must always be followed throughout the migration process.\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\nAdditionally you'll receive:\n- File paths to migrate in individual queries\n- Deprecated component names at the end of this prompt\n- Migration context and guide for each deprecated component`;\n","/** Split the path string 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","/** 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 https from 'node:https';\n\nimport type { DefaultRenderer, ListrTaskWrapper, SimpleRenderer } from 'listr2';\n\nimport { VPN_COUNTDOWN_TIMEOUT } from '../../constants/claude';\n\n/** Checks VPN connectivity by pinging the provided base URL's /health endpoint, with countdown retries */\nexport async 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 // eslint-disable-next-line no-param-reassign\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 // eslint-disable-next-line no-param-reassign\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","import { execSync } from 'node:child_process';\nimport { readFileSync } from 'node:fs';\nimport { resolve } from 'node:path';\n\nimport { type Options, query } from '@anthropic-ai/claude-agent-sdk';\nimport type { Manager } from '@listr2/manager';\nimport type { DefaultRenderer, ListrTaskWrapper, SimpleRenderer } from 'listr2';\n\nimport {\n CLAUDE_SETTINGS_FILE,\n DIRECTORY_CONCURRENCY_LIMIT,\n INITIAL_CLAUDE_PROMPT,\n} from '../../constants/claude';\nimport { CONSOLE_ICONS } from '../../constants/common';\nimport { checkVPN, formatPathOutput, generateElapsedTime } from '../common';\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\ninterface QueryOptionProps {\n additionalPromptContext?: string;\n}\n\n/**\n * Creates reusable query options for Claude session, including authentication and system prompt\n */\nfunction getQueryOptions({ additionalPromptContext }: QueryOptionProps): 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 env: envVars,\n permissionMode: 'acceptEdits',\n systemPrompt: {\n type: 'preset',\n preset: 'claude_code',\n append: `${INITIAL_CLAUDE_PROMPT}\\n${additionalPromptContext}`,\n },\n allowedTools: ['Grep', 'Read'],\n settingSources: ['local', 'project', 'user'],\n };\n}\n\n/**\n * Initiate a new Claude session/conversation and return reusable options\n */\nexport async function initiateClaudeSessionOptions(\n manager: Manager,\n additionalPromptContext: string,\n): Promise<Options> {\n let options: Options = {};\n\n manager.add([\n {\n title: 'Configuring Claude connection and instance...',\n task: async (ctx, task) => {\n options = getQueryOptions({ additionalPromptContext });\n\n return task.newListr([\n {\n title: 'Checking VPN connection...',\n task: async (\n subCtx,\n subtask: ListrTaskWrapper<never, typeof DefaultRenderer, typeof SimpleRenderer>,\n ) => checkVPN(options.env?.ANTHROPIC_BASE_URL, subtask),\n },\n {\n title: 'Initialising Claude session',\n task: async (\n subCtx,\n subtask: ListrTaskWrapper<never, typeof DefaultRenderer, typeof SimpleRenderer>,\n ) => {\n // eslint-disable-next-line no-param-reassign\n subtask.output = 'Your browser may open for Okta authentication if required';\n\n const result = query({\n options,\n prompt: `This is an initialisation query to start a new Claude code migration session. No text response is needed.`,\n });\n\n for await (const message of result) {\n switch (message.type) {\n case 'system':\n if (message.subtype === 'init' && !options.resume) {\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 // eslint-disable-next-line no-param-reassign\n task.title = 'Successfully configured and initialised Claude\\n';\n },\n },\n ]);\n },\n },\n ]);\n\n // Set manager to run tasks concurrently, once initialisation steps are done\n await manager.runAll().finally(() => {\n // eslint-disable-next-line no-param-reassign\n manager.options = {\n concurrent: DIRECTORY_CONCURRENCY_LIMIT,\n };\n });\n\n return options;\n}\n\n/**\n * Queries Claude with the given path and handles logging of success/error messages\n */\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 // eslint-disable-next-line no-param-reassign\n task.title = `\\x1b[2m${formatPathOutput(directory, filePath)}\\x1b[0m]`;\n // eslint-disable-next-line no-param-reassign\n task.output = `\\x1b[2mMigrated in ${generateElapsedTime(startTime)}\\x1b[0m`;\n } else if (message.is_error) {\n // eslint-disable-next-line no-param-reassign\n task.title = `\\x1b[2m${formatPathOutput(directory, filePath)}\\x1b[0m]`;\n // eslint-disable-next-line no-param-reassign\n task.output = `${CONSOLE_ICONS.error} Claude encountered an error: ${JSON.stringify(message)}`;\n }\n break;\n default:\n }\n }\n}\n","import { execSync } from 'node:child_process';\nimport { readFileSync } from 'node:fs';\n\nimport { Manager } from '@listr2/manager';\nimport {\n type DefaultRenderer,\n Listr,\n type ListrTask,\n type ListrTaskWrapper,\n type SimpleRenderer,\n} from 'listr2';\n\nimport { FILE_CONCURRENCY_LIMIT } from '../../constants/claude';\nimport { formatPathOutput, generateElapsedTime } from '../common';\nimport { initiateClaudeSessionOptions, queryClaude } from './query';\n\ninterface ClaudeTransformerOptions {\n targetDirectories: string[];\n componentGrepPattern: RegExp;\n additionalPromptContext: string;\n isDebug?: boolean;\n}\n\n/**\n * Performs code migration using Claude for the specified target directories, for the provided deprecated components and migration guides.\n * @param targetDirectories - Array of directory paths to process\n * @param componentGrepPattern - RegExp pattern to identify files needing migration (e.g. via deprecated component imports)\n * @param isDebug - Whether to enable debug logging\n * @param additionalPromptContext - Additional context to include in the initial Claude prompt before processing files\n */\nexport const claudeTransformer = async ({\n targetDirectories,\n componentGrepPattern,\n additionalPromptContext,\n isDebug = false,\n}: ClaudeTransformerOptions) => {\n process.setMaxListeners(20); // Resolves potential memory issues with how Claude handles its 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, additionalPromptContext);\n\n manager.add([\n {\n title: 'Processing target directories for migration...',\n task: async (ctx, task) => {\n return task.newListr(\n targetDirectories.map((directory) => {\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 componentGrepPattern.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 return {\n title: `\\x1b[2m${formatPathOutput(directory)} - No files need migration\\x1b[0m`,\n task: (subCtx: ListrTask): void => {\n // eslint-disable-next-line no-param-reassign\n subCtx.skip = true;\n },\n };\n }\n\n return {\n title: `${formatPathOutput(directory)} - Found \\x1b[32m${matchingFilePaths.length}\\x1b[0m file(s) needing migration`,\n task: async (subCtx, parentTask) => {\n // eslint-disable-next-line no-param-reassign\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(\n directory,\n filePath,\n queryOptions,\n fileTask,\n isDebug,\n ).finally(() => {\n // Update parent task title with progress for each completed file migration\n completedFilesInDirectory.count += 1;\n const isDim =\n completedFilesInDirectory.count === matchingFilePaths.length;\n // eslint-disable-next-line no-param-reassign\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 { concurrent: FILE_CONCURRENCY_LIMIT },\n )\n .run();\n },\n };\n }),\n { rendererOptions: { suffixSkips: false, collapseSubtasks: false } },\n );\n },\n },\n ]);\n\n // Run all directory tasks concurrently, 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"],"mappings":"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"}
@@ -0,0 +1,327 @@
1
+ # Alert → InfoPrompt Migration Guide
2
+
3
+ ## Import Management
4
+
5
+ 1. Remove import: `Alert` (or `{ Alert }`) from `'@transferwise/components'`
6
+ 2. Remove import: `{ AlertArrowPosition }` from `'@transferwise/components'` (if used)
7
+ 3. Add import: `{ InfoPrompt }` from `'@transferwise/components'`
8
+ 4. If using custom `icon`: Add the icon import from `'@transferwise/icons'` (if not already present)
9
+ 5. Remove unused imports: `Sentiment`, `Status` enums if only used for Alert `type`
10
+
11
+ ## Universal Rules
12
+
13
+ 1. `type` → `sentiment` (with value mapping — see below)
14
+ 2. `message` → `description` (**required** in InfoPrompt; does **not** support `**bold**` markdown — strip markdown or render bold text before passing)
15
+ 3. `children` (deprecated) → `description` (convert ReactNode to plain string)
16
+ 4. `title` → `title` (direct)
17
+ 5. `className` → `className` (direct)
18
+ 6. `onDismiss` → `onDismiss` (signature changes: `MouseEventHandler<HTMLButtonElement>` → `() => void` — remove the event parameter)
19
+ 7. `icon` → `media` (wrap in `{ asset: <Icon /> }` object)
20
+ 8. `action.text` → `action.label`
21
+ 9. `action.as` → removed (InfoPrompt action is always a Link)
22
+ 10. `action['aria-label']` → removed (not supported)
23
+ 11. `statusIconLabel` → removed (use `media.asset` with accessible attributes like `title` or `aria-label` on the icon itself)
24
+ 12. Remove deprecated props: `arrow`, `dismissible`, `size`
25
+
26
+ ---
27
+
28
+ ## Type / Sentiment Mapping
29
+
30
+ | Alert `type` | InfoPrompt `sentiment` | Notes |
31
+ | ---------------- | ---------------------- | ------------------------------------------ |
32
+ | `'positive'` | `'success'` | |
33
+ | `'neutral'` | `'neutral'` | |
34
+ | `'warning'` | `'warning'` | |
35
+ | `'negative'` | `'negative'` | |
36
+ | `'pending'` | `'neutral'` | No pending sentiment; use `'neutral'` |
37
+ | `'success'` ❌ | `'success'` | Alert deprecated value |
38
+ | `'info'` ❌ | `'neutral'` | Alert deprecated value |
39
+ | `'error'` ❌ | `'negative'` | Alert deprecated value |
40
+
41
+ When using enum values:
42
+
43
+ | Alert enum | InfoPrompt string |
44
+ | ----------------------- | ------------------- |
45
+ | `Sentiment.POSITIVE` | `'success'` |
46
+ | `Sentiment.NEUTRAL` | `'neutral'` |
47
+ | `Sentiment.WARNING` | `'warning'` |
48
+ | `Sentiment.NEGATIVE` | `'negative'` |
49
+ | `Sentiment.PENDING` | `'neutral'` |
50
+ | `Status.PENDING` | `'neutral'` |
51
+
52
+ ---
53
+
54
+ ## Basic Migration
55
+
56
+ ```tsx
57
+ <Alert type="positive" message="Payment completed successfully." />
58
+
59
+ <InfoPrompt sentiment="success" description="Payment completed successfully." />
60
+ ```
61
+
62
+ ---
63
+
64
+ ## With Title
65
+
66
+ ```tsx
67
+ <Alert type="neutral" title="We're reviewing your information." message="This may take a few minutes." />
68
+
69
+ <InfoPrompt sentiment="neutral" title="We're reviewing your information." description="This may take a few minutes." />
70
+ ```
71
+
72
+ ---
73
+
74
+ ## With onDismiss
75
+
76
+ Alert's `onDismiss` receives a `MouseEvent`; InfoPrompt's `onDismiss` receives no arguments.
77
+
78
+ ```tsx
79
+ <Alert type="warning" message="Check your details." onDismiss={(e) => handleDismiss(e)} />
80
+
81
+ <InfoPrompt sentiment="warning" description="Check your details." onDismiss={() => handleDismiss()} />
82
+ ```
83
+
84
+ If the handler already ignores the event:
85
+
86
+ ```tsx
87
+ <Alert type="warning" message="Check your details." onDismiss={() => setVisible(false)} />
88
+
89
+ <InfoPrompt sentiment="warning" description="Check your details." onDismiss={() => setVisible(false)} />
90
+ ```
91
+
92
+ ---
93
+
94
+ ## With Link Action
95
+
96
+ Alert renders a `<Link>` by default (`as` omitted or `as="link"`).
97
+
98
+ - `action.text` → `action.label`
99
+ - `action['aria-label']` → removed (not supported; use a descriptive `label` instead)
100
+
101
+ ```tsx
102
+ <Alert
103
+ type="neutral"
104
+ message="New feature available."
105
+ action={{ text: 'Read more', href: '/features', target: '_blank' }}
106
+ />
107
+
108
+ <InfoPrompt
109
+ sentiment="neutral"
110
+ description="New feature available."
111
+ action={{ label: 'Read more', href: '/features', target: '_blank' }}
112
+ />
113
+ ```
114
+
115
+ ---
116
+
117
+ ## With Button Action
118
+
119
+ InfoPrompt does **not** support `as: 'button'`. The action is always rendered as a `<Link>`.
120
+
121
+ - If the Alert action used `as: 'button'` with an `href`, convert to a link action
122
+ - If the Alert action used `as: 'button'` without `href` (onClick only), convert to an action with `onClick`
123
+
124
+ ```tsx
125
+ <Alert
126
+ type="neutral"
127
+ message="Verify your identity."
128
+ action={{ text: 'Verify now', as: 'button', onClick: handleVerify }}
129
+ />
130
+
131
+ <InfoPrompt
132
+ sentiment="neutral"
133
+ description="Verify your identity."
134
+ action={{ label: 'Verify now', onClick: handleVerify }}
135
+ />
136
+ ```
137
+
138
+ ```tsx
139
+ <Alert
140
+ type="neutral"
141
+ message="Go to settings."
142
+ action={{ text: 'Open settings', as: 'button', href: '/settings', target: '_blank' }}
143
+ />
144
+
145
+ <InfoPrompt
146
+ sentiment="neutral"
147
+ description="Go to settings."
148
+ action={{ label: 'Open settings', href: '/settings', target: '_blank' }}
149
+ />
150
+ ```
151
+
152
+ ---
153
+
154
+ ## With Custom Icon → Media
155
+
156
+ Alert's `icon` accepts a bare ReactNode. InfoPrompt's `media` requires an object `{ asset: ReactNode }`.
157
+
158
+ The icon/asset should include its own accessibility attributes (e.g. `title`, `aria-label`).
159
+
160
+ ```tsx
161
+ <Alert type="neutral" message="Scheduled for later." icon={<ClockBorderless size={24} />} />
162
+
163
+ <InfoPrompt sentiment="neutral" description="Scheduled for later." media={{ asset: <ClockBorderless size={24} /> }} />
164
+ ```
165
+
166
+ ---
167
+
168
+ ## With statusIconLabel
169
+
170
+ `statusIconLabel` is removed. InfoPrompt's default status icons handle accessibility internally.
171
+ If you need a custom accessible name, provide it via `media.asset` attributes.
172
+
173
+ ```tsx
174
+ <Alert type="positive" message="Done!" statusIconLabel="Success indicator" />
175
+
176
+ <InfoPrompt sentiment="success" description="Done!" />
177
+ ```
178
+
179
+ ---
180
+
181
+ ## Bold Markdown in Message
182
+
183
+ Alert's `message` supports inline bold markdown (`**text**`). InfoPrompt's `description` is **plain text only**.
184
+
185
+ Strip the markdown delimiters or restructure your content:
186
+
187
+ ```tsx
188
+ <Alert type="positive" message="Payments sent **today** might not arrive in time." />
189
+
190
+ <InfoPrompt sentiment="success" description="Payments sent today might not arrive in time." />
191
+ ```
192
+
193
+ ---
194
+
195
+ ## Children (Deprecated in Alert)
196
+
197
+ Alert's `children` prop (deprecated) allowed arbitrary ReactNode content. InfoPrompt requires a string `description`.
198
+
199
+ Convert the child content to a plain text string:
200
+
201
+ ```tsx
202
+ <Alert type="neutral">Your transfer is <strong>in progress</strong>.</Alert>
203
+
204
+ <InfoPrompt sentiment="neutral" description="Your transfer is in progress." />
205
+ ```
206
+
207
+ ---
208
+
209
+ ## Deprecated `type` Values
210
+
211
+ If still using deprecated Alert type values, map them directly:
212
+
213
+ ```tsx
214
+ <Alert type="success" message="Done!" />
215
+
216
+ <InfoPrompt sentiment="success" description="Done!" />
217
+
218
+ <Alert type="info" message="FYI." />
219
+
220
+ <InfoPrompt sentiment="neutral" description="FYI." />
221
+
222
+ <Alert type="error" message="Something went wrong." />
223
+
224
+ <InfoPrompt sentiment="negative" description="Something went wrong." />
225
+ ```
226
+
227
+ ---
228
+
229
+ ## Arrow (Deprecated in Alert)
230
+
231
+ `arrow` is not supported in InfoPrompt. Use `InlinePrompt` instead for arrow/pointer behaviour.
232
+
233
+ ```tsx
234
+ <Alert type="neutral" message="Hint text." arrow="up-left" />
235
+
236
+ <InfoPrompt sentiment="neutral" description="Hint text." />
237
+ ```
238
+
239
+ If arrow pointing behaviour is required, migrate to `InlinePrompt` instead of `InfoPrompt`.
240
+
241
+ ---
242
+
243
+ ## Dismissible (Deprecated in Alert)
244
+
245
+ Replace the deprecated `dismissible` boolean with `onDismiss` handler:
246
+
247
+ ```tsx
248
+ <Alert type="neutral" message="Notice." dismissible />
249
+
250
+ <InfoPrompt sentiment="neutral" description="Notice." onDismiss={() => setVisible(false)} />
251
+ ```
252
+
253
+ ---
254
+
255
+ ## Full Example
256
+
257
+ ### Before
258
+
259
+ ```tsx
260
+ import { Alert, Sentiment } from '@transferwise/components';
261
+ import { ClockBorderless } from '@transferwise/icons';
262
+
263
+ function MyComponent() {
264
+ const [visible, setVisible] = useState(true);
265
+
266
+ if (!visible) return null;
267
+
268
+ return (
269
+ <Alert
270
+ type={Sentiment.POSITIVE}
271
+ title="Payment Successful"
272
+ message="Your payment of **$100** has been sent."
273
+ icon={<ClockBorderless size={24} />}
274
+ action={{ text: 'View details', href: '/transactions', target: '_blank', 'aria-label': 'View transaction details' }}
275
+ onDismiss={(e) => setVisible(false)}
276
+ />
277
+ );
278
+ }
279
+ ```
280
+
281
+ ### After
282
+
283
+ ```tsx
284
+ import { InfoPrompt } from '@transferwise/components';
285
+ import { ClockBorderless } from '@transferwise/icons';
286
+
287
+ function MyComponent() {
288
+ const [visible, setVisible] = useState(true);
289
+
290
+ if (!visible) return null;
291
+
292
+ return (
293
+ <InfoPrompt
294
+ sentiment="success"
295
+ title="Payment Successful"
296
+ description="Your payment of $100 has been sent."
297
+ media={{ asset: <ClockBorderless size={24} title="Clock" /> }}
298
+ action={{ label: 'View details', href: '/transactions', target: '_blank' }}
299
+ onDismiss={() => setVisible(false)}
300
+ />
301
+ );
302
+ }
303
+ ```
304
+
305
+ ---
306
+
307
+ ## New Props in InfoPrompt (Not in Alert)
308
+
309
+ | Prop | Type | Default | Description |
310
+ | ------------- | ----------- | ---------- | ------------------------------------------ |
311
+ | `aria-live` | `AriaLive` | `'polite'` | ARIA live region politeness level |
312
+ | `data-testid` | `string` | — | Test ID for testing tools |
313
+ | `sentiment` | — | — | Supports `'proposition'` (no Alert equivalent) |
314
+
315
+ ---
316
+
317
+ ## Props Removed (No InfoPrompt Equivalent)
318
+
319
+ | Alert Prop | Migration Action |
320
+ | ----------------- | ------------------------------------------------------------------ |
321
+ | `arrow` | Remove. Use `InlinePrompt` if pointer behaviour is needed. |
322
+ | `children` | Convert to `description` string. |
323
+ | `dismissible` | Replace with `onDismiss` handler. |
324
+ | `size` | Remove. Not supported. |
325
+ | `statusIconLabel` | Remove. Use `media.asset` with accessible icon attributes instead. |
326
+ | `action.as` | Remove. InfoPrompt action is always a Link. |
327
+ | `action['aria-label']` | Remove. Use a descriptive `action.label` instead. |
@@ -0,0 +1,6 @@
1
+ {
2
+ "type": "claude",
3
+ "prerequisites": {
4
+ "@transferwise/components": ">=46.126.0"
5
+ }
6
+ }
@@ -0,0 +1,27 @@
1
+ Object.defineProperty(exports, '__esModule', { value: true });
2
+ const require_common = require('../../common-DdEQmI2h.js');
3
+ const require_claude = require('../../claude-BHYfEMfb.js');
4
+ let node_path = require("node:path");
5
+ let node_url = require("node:url");
6
+ let node_fs = require("node:fs");
7
+
8
+ //#region src/transforms/info-prompt/transformer.ts
9
+ const DEPRECATED_COMPONENT_NAMES = ["Alert"];
10
+ const GREP_PATTERN = new RegExp(`import\\s*\\{[\\s\\S]*?(${DEPRECATED_COMPONENT_NAMES.join("|")})[\\s\\S]*?\\}\\s*from\\s*['"]@transferwise/components['"]`, "u");
11
+ const MIGRATION_RULES_PATH = (0, node_path.join)((0, node_path.dirname)((0, node_url.fileURLToPath)(require("url").pathToFileURL(__filename).href)), "MIGRATION_RULES.md");
12
+ const transformer = async (targetDirectories, isDebug = false) => {
13
+ const migrationRules = (0, node_fs.readFileSync)(MIGRATION_RULES_PATH, "utf-8");
14
+ return require_claude.claudeTransformer({
15
+ targetDirectories,
16
+ isDebug,
17
+ componentGrepPattern: GREP_PATTERN,
18
+ additionalPromptContext: `Deprecated components: ${DEPRECATED_COMPONENT_NAMES.join(", ")}\n${migrationRules}`
19
+ });
20
+ };
21
+ var transformer_default = transformer;
22
+
23
+ //#endregion
24
+ exports.DEPRECATED_COMPONENT_NAMES = DEPRECATED_COMPONENT_NAMES;
25
+ exports.GREP_PATTERN = GREP_PATTERN;
26
+ exports.default = transformer_default;
27
+ //# sourceMappingURL=transformer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transformer.js","names":["claudeTransformer"],"sources":["../../../src/transforms/info-prompt/transformer.ts"],"sourcesContent":["import { readFileSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\nimport { claudeTransformer } from '../../helpers/claude';\n\nexport const DEPRECATED_COMPONENT_NAMES = ['Alert'];\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\n\n\nconst MIGRATION_RULES_DIRECTORY = dirname(fileURLToPath(import.meta.url));\nconst MIGRATION_RULES_PATH = join(MIGRATION_RULES_DIRECTORY, 'MIGRATION_RULES.md');\n\nconst transformer = async (targetDirectories: string[], isDebug = false) => {\n const migrationRules = readFileSync(MIGRATION_RULES_PATH, 'utf-8');\n\n return claudeTransformer({\n targetDirectories,\n isDebug,\n componentGrepPattern: GREP_PATTERN,\n additionalPromptContext: `Deprecated components: ${DEPRECATED_COMPONENT_NAMES.join(', ')}\\n${migrationRules}`,\n });\n};\n\nexport default transformer;\n"],"mappings":";;;;;;;;AAMA,MAAa,6BAA6B,CAAC,QAAQ;AACnD,MAAa,eAAe,IAAI,OAC9B,2BAA2B,2BAA2B,KAAK,IAAI,CAAC,6DAChE,IACD;AAKD,MAAM,4IADkE,CAAC,EACZ,qBAAqB;AAElF,MAAM,cAAc,OAAO,mBAA6B,UAAU,UAAU;CAC1E,MAAM,2CAA8B,sBAAsB,QAAQ;AAElE,QAAOA,iCAAkB;EACvB;EACA;EACA,sBAAsB;EACtB,yBAAyB,0BAA0B,2BAA2B,KAAK,KAAK,CAAC,IAAI;EAC9F,CAAC;;AAGJ,0BAAe"}
@@ -1,252 +1,9 @@
1
1
  const require_common = require('../../common-DdEQmI2h.js');
2
- let node_child_process = require("node:child_process");
2
+ const require_claude = require('../../claude-BHYfEMfb.js');
3
3
  let node_path = require("node:path");
4
4
  let node_url = require("node:url");
5
5
  let node_fs = require("node:fs");
6
- let _anthropic_ai_claude_agent_sdk = require("@anthropic-ai/claude-agent-sdk");
7
- let node_https = require("node:https");
8
- node_https = require_common.__toESM(node_https);
9
- let _listr2_manager = require("@listr2/manager");
10
- let listr2 = require("listr2");
11
6
 
12
- //#region src/constants/claude.ts
13
- const CLAUDE_SETTINGS_FILE = ".claude/settings.json";
14
- const VPN_COUNTDOWN_TIMEOUT = 5;
15
- const DIRECTORY_CONCURRENCY_LIMIT = 3;
16
- const FILE_CONCURRENCY_LIMIT = 10;
17
- const INITIAL_CLAUDE_PROMPT = `You are a code migration assistant that helps migrate TypeScript/JSX code of deprecated Wise Design System (WDS) components from '@transferwise/components', to their new replacement components as detailed in the relevant migration guide. The following is a list of rules that must always be followed throughout the migration process.
18
-
19
- Rules:
20
- 1. Only ever modify files via the Edit tool - do not use the Write tool
21
- 2. When identifying what code to migrate within a file, explain how you identified it first.
22
- 3. Migrate components per provided migration rules
23
- 4. Maintain TypeScript type safety and update types to match new API
24
- 5. Map props: handle renamed, deprecated, new required, and changed types
25
- 6. Update imports to new WDS components and types
26
- 7. Preserve code style, formatting, and calculated logic
27
- 8. Handle conditional rendering, spread props, and complex expressions
28
- 9. Note: New components may lack feature parity with legacy versions
29
- 10. Only modify code requiring changes per migration rules, and any impacted surrounding code for context.
30
- 11. Final result response should just be whether the migration was successful overall, or if any errors were encountered
31
- - Do not summarise or explain the changes made
32
- 12. Explain your reasoning and justification before making changes, as you edit each file.
33
- - Keep it concise and succinct, as only bullet points
34
- 13. After modifying the file, do not summarise the changes made.
35
- 14. If you do not have permission to edit a file, still attempt to edit it and then move onto the next file.
36
-
37
- Additionally you'll receive:
38
- - File paths to migrate in individual queries
39
- - Deprecated component names at the end of this prompt
40
- - Migration context and guide for each deprecated component`;
41
-
42
- //#endregion
43
- //#region src/helpers/common/pathUtils.ts
44
- /** Split the path string to get the relative path after the directory, and wrap with ANSI color codes */
45
- function formatPathOutput(directory, path, asDim) {
46
- const relativePath = path ? path.split(directory.replace(".", ""))[1] ?? path : directory;
47
- return asDim ? `\x1b[2m${relativePath}\x1b[0m` : `\x1b[32m${relativePath}\x1b[0m`;
48
- }
49
-
50
- //#endregion
51
- //#region src/helpers/common/timerUtils.ts
52
- /** Generates a formatted string representing the total elapsed time since the given start time */
53
- function generateElapsedTime(startTime) {
54
- const endTime = Date.now();
55
- const elapsedTime = Math.floor((endTime - startTime) / 1e3);
56
- const hours = Math.floor(elapsedTime / 3600);
57
- const minutes = Math.floor(elapsedTime % 3600 / 60);
58
- const seconds = elapsedTime % 60;
59
- return `${hours ? `${hours}h ` : ""}${minutes ? `${minutes}m ` : ""}${seconds ? `${seconds}s` : ""}`;
60
- }
61
-
62
- //#endregion
63
- //#region src/helpers/common/vpnUtils.ts
64
- /** Checks VPN connectivity by pinging the provided base URL's /health endpoint, with countdown retries */
65
- async function checkVPN(baseUrl, task) {
66
- if (!baseUrl) return;
67
- const checkOnce = async () => new Promise((resolveCheck) => {
68
- const url = new URL("/health", baseUrl);
69
- const req = node_https.default.get(url, {
70
- timeout: 2e3,
71
- rejectUnauthorized: false
72
- }, (res) => {
73
- const ok = !!(res.statusCode && res.statusCode >= 200 && res.statusCode < 400);
74
- res.resume();
75
- resolveCheck(ok);
76
- });
77
- req.on("timeout", () => {
78
- req.destroy(/* @__PURE__ */ new Error("timeout"));
79
- });
80
- req.on("error", () => resolveCheck(false));
81
- });
82
- while (true) {
83
- if (await checkOnce()) {
84
- task.title = "Connected to VPN";
85
- break;
86
- }
87
- for (let countdown = VPN_COUNTDOWN_TIMEOUT; countdown > 0; countdown -= 1) {
88
- task.output = `Please connect to VPN... retrying in ${countdown}s`;
89
- await new Promise((response) => {
90
- setTimeout(response, 1e3);
91
- });
92
- }
93
- }
94
- }
95
-
96
- //#endregion
97
- //#region src/helpers/claude/query.ts
98
- /**
99
- * Creates reusable query options for Claude session, including authentication and system prompt
100
- */
101
- function getQueryOptions({ additionalPromptContext }) {
102
- const claudeSettingsPath = (0, node_path.resolve)(process.env.HOME || "", CLAUDE_SETTINGS_FILE);
103
- const settings = JSON.parse((0, node_fs.readFileSync)(claudeSettingsPath, "utf-8"));
104
- let apiKey;
105
- try {
106
- apiKey = (0, node_child_process.execSync)(`bash ${settings.apiKeyHelper}`, { encoding: "utf-8" }).trim();
107
- } catch {}
108
- if (!apiKey || !settings.env?.ANTHROPIC_BASE_URL) throw new Error("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");
109
- const { ANTHROPIC_CUSTOM_HEADERS, ...restEnvVars } = settings?.env ?? {};
110
- return {
111
- env: {
112
- ANTHROPIC_AUTH_TOKEN: apiKey,
113
- ANTHROPIC_CUSTOM_HEADERS,
114
- ...restEnvVars,
115
- PATH: process.env.PATH
116
- },
117
- permissionMode: "acceptEdits",
118
- systemPrompt: {
119
- type: "preset",
120
- preset: "claude_code",
121
- append: `${INITIAL_CLAUDE_PROMPT}\n${additionalPromptContext}`
122
- },
123
- allowedTools: ["Grep", "Read"],
124
- settingSources: [
125
- "local",
126
- "project",
127
- "user"
128
- ]
129
- };
130
- }
131
- /**
132
- * Initiate a new Claude session/conversation and return reusable options
133
- */
134
- async function initiateClaudeSessionOptions(manager, additionalPromptContext) {
135
- let options = {};
136
- manager.add([{
137
- title: "Configuring Claude connection and instance...",
138
- task: async (ctx, task) => {
139
- options = getQueryOptions({ additionalPromptContext });
140
- return task.newListr([{
141
- title: "Checking VPN connection...",
142
- task: async (subCtx, subtask) => checkVPN(options.env?.ANTHROPIC_BASE_URL, subtask)
143
- }, {
144
- title: "Initialising Claude session",
145
- task: async (subCtx, subtask) => {
146
- subtask.output = "Your browser may open for Okta authentication if required";
147
- const result = (0, _anthropic_ai_claude_agent_sdk.query)({
148
- options,
149
- prompt: `This is an initialisation query to start a new Claude code migration session. No text response is needed.`
150
- });
151
- for await (const message of result) switch (message.type) {
152
- case "system":
153
- if (message.subtype === "init" && !options.resume) options.resume = message.session_id;
154
- break;
155
- default: if (message.type === "result" && message.subtype !== "success") throw new Error(`Claude encountered an error when initialising: ${message.errors.join("\n")}`);
156
- }
157
- task.title = "Successfully configured and initialised Claude\n";
158
- }
159
- }]);
160
- }
161
- }]);
162
- await manager.runAll().finally(() => {
163
- manager.options = { concurrent: DIRECTORY_CONCURRENCY_LIMIT };
164
- });
165
- return options;
166
- }
167
- /**
168
- * Queries Claude with the given path and handles logging of success/error messages
169
- */
170
- async function queryClaude(directory, filePath, options, task, isDebug = false) {
171
- const startTime = Date.now();
172
- const result = (0, _anthropic_ai_claude_agent_sdk.query)({
173
- options,
174
- prompt: filePath
175
- });
176
- for await (const message of result) switch (message.type) {
177
- case "result":
178
- if (message.subtype === "success" && isDebug) {
179
- task.title = `\x1b[2m${formatPathOutput(directory, filePath)}\x1b[0m]`;
180
- task.output = `\x1b[2mMigrated in ${generateElapsedTime(startTime)}\x1b[0m`;
181
- } else if (message.is_error) {
182
- task.title = `\x1b[2m${formatPathOutput(directory, filePath)}\x1b[0m]`;
183
- task.output = `${require_common.CONSOLE_ICONS.error} Claude encountered an error: ${JSON.stringify(message)}`;
184
- }
185
- break;
186
- default:
187
- }
188
- }
189
-
190
- //#endregion
191
- //#region src/helpers/claude/transformer.ts
192
- /**
193
- * Performs code migration using Claude for the specified target directories, for the provided deprecated components and migration guides.
194
- * @param targetDirectories - Array of directory paths to process
195
- * @param componentGrepPattern - RegExp pattern to identify files needing migration (e.g. via deprecated component imports)
196
- * @param isDebug - Whether to enable debug logging
197
- * @param additionalPromptContext - Additional context to include in the initial Claude prompt before processing files
198
- */
199
- const claudeTransformer = async ({ targetDirectories, componentGrepPattern, additionalPromptContext, isDebug = false }) => {
200
- process.setMaxListeners(20);
201
- const startTime = Date.now();
202
- const manager = new _listr2_manager.Manager({ concurrent: true });
203
- const queryOptions = await initiateClaudeSessionOptions(manager, additionalPromptContext);
204
- manager.add([{
205
- title: "Processing target directories for migration...",
206
- task: async (ctx, task) => {
207
- return task.newListr(targetDirectories.map((directory) => {
208
- const matchingFilePaths = (0, node_child_process.execSync)(`find "${directory}" -name "*.tsx" -type f`, { encoding: "utf-8" }).trim().split("\n").filter(Boolean).filter((filePath) => {
209
- const content = (0, node_fs.readFileSync)(filePath, "utf-8");
210
- return componentGrepPattern.test(content);
211
- });
212
- if (matchingFilePaths.length === 0) return {
213
- title: `\x1b[2m${formatPathOutput(directory)} - No files need migration\x1b[0m`,
214
- task: (subCtx) => {
215
- subCtx.skip = true;
216
- }
217
- };
218
- return {
219
- title: `${formatPathOutput(directory)} - Found \x1b[32m${matchingFilePaths.length}\x1b[0m file(s) needing migration`,
220
- task: async (subCtx, parentTask) => {
221
- parentTask.title = `${formatPathOutput(directory)} - Migrating \x1b[32m${matchingFilePaths.length}\x1b[0m file(s)...`;
222
- const completedFilesInDirectory = { count: 0 };
223
- return parentTask.newListr(matchingFilePaths.map((filePath) => ({
224
- title: "",
225
- task: async (fileCtx, fileTask) => {
226
- await queryClaude(directory, filePath, queryOptions, fileTask, isDebug).finally(() => {
227
- completedFilesInDirectory.count += 1;
228
- const isDim = completedFilesInDirectory.count === matchingFilePaths.length;
229
- parentTask.title = `${isDim ? "\x1B[2m" : ""}${formatPathOutput(directory)} - Migrated \x1b[32m${completedFilesInDirectory.count}\x1b[0m/\x1b[32m${matchingFilePaths.length}\x1b[0m files${isDim ? "\x1B[0m" : ""}`;
230
- });
231
- }
232
- })), { concurrent: FILE_CONCURRENCY_LIMIT }).run();
233
- }
234
- };
235
- }), { rendererOptions: {
236
- suffixSkips: false,
237
- collapseSubtasks: false
238
- } });
239
- }
240
- }]);
241
- await manager.runAll().finally(async () => {
242
- await new listr2.Listr([{
243
- title: `Finished migrating - elapsed time: \x1b[32m${generateElapsedTime(startTime)}\x1b[0m `,
244
- task: async () => {}
245
- }]).run();
246
- });
247
- };
248
-
249
- //#endregion
250
7
  //#region src/transforms/list-item/constants.ts
251
8
  const DEPRECATED_COMPONENT_NAMES = [
252
9
  "ActionOption",
@@ -264,7 +21,7 @@ const GREP_PATTERN = new RegExp(`import\\s*\\{[\\s\\S]*?(${DEPRECATED_COMPONENT_
264
21
  const MIGRATION_RULES_PATH = (0, node_path.join)((0, node_path.dirname)((0, node_url.fileURLToPath)(require("url").pathToFileURL(__filename).href)), "MIGRATION_RULES.md");
265
22
  const transformer = async (targetDirectories, isDebug = false) => {
266
23
  const migrationRules = (0, node_fs.readFileSync)(MIGRATION_RULES_PATH, "utf-8");
267
- return claudeTransformer({
24
+ return require_claude.claudeTransformer({
268
25
  targetDirectories,
269
26
  isDebug,
270
27
  componentGrepPattern: GREP_PATTERN,
@@ -1 +1 @@
1
- {"version":3,"file":"transformer.js","names":["https","options: Options","CONSOLE_ICONS","Manager","Listr"],"sources":["../../../src/constants/claude.ts","../../../src/helpers/common/pathUtils.ts","../../../src/helpers/common/timerUtils.ts","../../../src/helpers/common/vpnUtils.ts","../../../src/helpers/claude/query.ts","../../../src/helpers/claude/transformer.ts","../../../src/transforms/list-item/constants.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;\n\nexport const INITIAL_CLAUDE_PROMPT = `You are a code migration assistant that helps migrate TypeScript/JSX code of deprecated Wise Design System (WDS) components from '@transferwise/components', to their new replacement components as detailed in the relevant migration guide. The following is a list of rules that must always be followed throughout the migration process.\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\nAdditionally you'll receive:\n- File paths to migrate in individual queries\n- Deprecated component names at the end of this prompt\n- Migration context and guide for each deprecated component`;\n","/** Split the path string 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","/** 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 https from 'node:https';\n\nimport type { DefaultRenderer, ListrTaskWrapper, SimpleRenderer } from 'listr2';\n\nimport { VPN_COUNTDOWN_TIMEOUT } from '../../constants/claude';\n\n/** Checks VPN connectivity by pinging the provided base URL's /health endpoint, with countdown retries */\nexport async 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 // eslint-disable-next-line no-param-reassign\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 // eslint-disable-next-line no-param-reassign\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","import { execSync } from 'node:child_process';\nimport { readFileSync } from 'node:fs';\nimport { resolve } from 'node:path';\n\nimport { type Options, query } from '@anthropic-ai/claude-agent-sdk';\nimport type { Manager } from '@listr2/manager';\nimport type { DefaultRenderer, ListrTaskWrapper, SimpleRenderer } from 'listr2';\n\nimport {\n CLAUDE_SETTINGS_FILE,\n DIRECTORY_CONCURRENCY_LIMIT,\n INITIAL_CLAUDE_PROMPT,\n} from '../../constants/claude';\nimport { CONSOLE_ICONS } from '../../constants/common';\nimport { checkVPN, formatPathOutput, generateElapsedTime } from '../common';\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\ninterface QueryOptionProps {\n additionalPromptContext?: string;\n}\n\n/**\n * Creates reusable query options for Claude session, including authentication and system prompt\n */\nfunction getQueryOptions({ additionalPromptContext }: QueryOptionProps): 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 env: envVars,\n permissionMode: 'acceptEdits',\n systemPrompt: {\n type: 'preset',\n preset: 'claude_code',\n append: `${INITIAL_CLAUDE_PROMPT}\\n${additionalPromptContext}`,\n },\n allowedTools: ['Grep', 'Read'],\n settingSources: ['local', 'project', 'user'],\n };\n}\n\n/**\n * Initiate a new Claude session/conversation and return reusable options\n */\nexport async function initiateClaudeSessionOptions(\n manager: Manager,\n additionalPromptContext: string,\n): Promise<Options> {\n let options: Options = {};\n\n manager.add([\n {\n title: 'Configuring Claude connection and instance...',\n task: async (ctx, task) => {\n options = getQueryOptions({ additionalPromptContext });\n\n return task.newListr([\n {\n title: 'Checking VPN connection...',\n task: async (\n subCtx,\n subtask: ListrTaskWrapper<never, typeof DefaultRenderer, typeof SimpleRenderer>,\n ) => checkVPN(options.env?.ANTHROPIC_BASE_URL, subtask),\n },\n {\n title: 'Initialising Claude session',\n task: async (\n subCtx,\n subtask: ListrTaskWrapper<never, typeof DefaultRenderer, typeof SimpleRenderer>,\n ) => {\n // eslint-disable-next-line no-param-reassign\n subtask.output = 'Your browser may open for Okta authentication if required';\n\n const result = query({\n options,\n prompt: `This is an initialisation query to start a new Claude code migration session. No text response is needed.`,\n });\n\n for await (const message of result) {\n switch (message.type) {\n case 'system':\n if (message.subtype === 'init' && !options.resume) {\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 // eslint-disable-next-line no-param-reassign\n task.title = 'Successfully configured and initialised Claude\\n';\n },\n },\n ]);\n },\n },\n ]);\n\n // Set manager to run tasks concurrently, once initialisation steps are done\n await manager.runAll().finally(() => {\n // eslint-disable-next-line no-param-reassign\n manager.options = {\n concurrent: DIRECTORY_CONCURRENCY_LIMIT,\n };\n });\n\n return options;\n}\n\n/**\n * Queries Claude with the given path and handles logging of success/error messages\n */\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 // eslint-disable-next-line no-param-reassign\n task.title = `\\x1b[2m${formatPathOutput(directory, filePath)}\\x1b[0m]`;\n // eslint-disable-next-line no-param-reassign\n task.output = `\\x1b[2mMigrated in ${generateElapsedTime(startTime)}\\x1b[0m`;\n } else if (message.is_error) {\n // eslint-disable-next-line no-param-reassign\n task.title = `\\x1b[2m${formatPathOutput(directory, filePath)}\\x1b[0m]`;\n // eslint-disable-next-line no-param-reassign\n task.output = `${CONSOLE_ICONS.error} Claude encountered an error: ${JSON.stringify(message)}`;\n }\n break;\n default:\n }\n }\n}\n","import { execSync } from 'node:child_process';\nimport { readFileSync } from 'node:fs';\n\nimport { Manager } from '@listr2/manager';\nimport {\n type DefaultRenderer,\n Listr,\n type ListrTask,\n type ListrTaskWrapper,\n type SimpleRenderer,\n} from 'listr2';\n\nimport { FILE_CONCURRENCY_LIMIT } from '../../constants/claude';\nimport { formatPathOutput, generateElapsedTime } from '../common';\nimport { initiateClaudeSessionOptions, queryClaude } from './query';\n\ninterface ClaudeTransformerOptions {\n targetDirectories: string[];\n componentGrepPattern: RegExp;\n additionalPromptContext: string;\n isDebug?: boolean;\n}\n\n/**\n * Performs code migration using Claude for the specified target directories, for the provided deprecated components and migration guides.\n * @param targetDirectories - Array of directory paths to process\n * @param componentGrepPattern - RegExp pattern to identify files needing migration (e.g. via deprecated component imports)\n * @param isDebug - Whether to enable debug logging\n * @param additionalPromptContext - Additional context to include in the initial Claude prompt before processing files\n */\nexport const claudeTransformer = async ({\n targetDirectories,\n componentGrepPattern,\n additionalPromptContext,\n isDebug = false,\n}: ClaudeTransformerOptions) => {\n process.setMaxListeners(20); // Resolves potential memory issues with how Claude handles its 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, additionalPromptContext);\n\n manager.add([\n {\n title: 'Processing target directories for migration...',\n task: async (ctx, task) => {\n return task.newListr(\n targetDirectories.map((directory) => {\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 componentGrepPattern.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 return {\n title: `\\x1b[2m${formatPathOutput(directory)} - No files need migration\\x1b[0m`,\n task: (subCtx: ListrTask): void => {\n // eslint-disable-next-line no-param-reassign\n subCtx.skip = true;\n },\n };\n }\n\n return {\n title: `${formatPathOutput(directory)} - Found \\x1b[32m${matchingFilePaths.length}\\x1b[0m file(s) needing migration`,\n task: async (subCtx, parentTask) => {\n // eslint-disable-next-line no-param-reassign\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(\n directory,\n filePath,\n queryOptions,\n fileTask,\n isDebug,\n ).finally(() => {\n // Update parent task title with progress for each completed file migration\n completedFilesInDirectory.count += 1;\n const isDim =\n completedFilesInDirectory.count === matchingFilePaths.length;\n // eslint-disable-next-line no-param-reassign\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 { concurrent: FILE_CONCURRENCY_LIMIT },\n )\n .run();\n },\n };\n }),\n { rendererOptions: { suffixSkips: false, collapseSubtasks: false } },\n );\n },\n },\n ]);\n\n // Run all directory tasks concurrently, 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","export const 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","import { readFileSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\nimport { claudeTransformer } from '../../helpers/claude';\nimport { DEPRECATED_COMPONENT_NAMES, GREP_PATTERN } from './constants';\n\nconst MIGRATION_RULES_DIRECTORY = dirname(fileURLToPath(import.meta.url));\nconst MIGRATION_RULES_PATH = join(MIGRATION_RULES_DIRECTORY, 'MIGRATION_RULES.md');\n\nconst transformer = async (targetDirectories: string[], isDebug = false) => {\n const migrationRules = readFileSync(MIGRATION_RULES_PATH, 'utf-8');\n\n return claudeTransformer({\n targetDirectories,\n isDebug,\n componentGrepPattern: GREP_PATTERN,\n additionalPromptContext: `Deprecated components: ${DEPRECATED_COMPONENT_NAMES.join(', ')}\\n${migrationRules}`,\n });\n};\n\nexport default transformer;\n"],"mappings":"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"}
1
+ {"version":3,"file":"transformer.js","names":["claudeTransformer"],"sources":["../../../src/transforms/list-item/constants.ts","../../../src/transforms/list-item/transformer.ts"],"sourcesContent":["export const 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","import { readFileSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\nimport { claudeTransformer } from '../../helpers/claude';\nimport { DEPRECATED_COMPONENT_NAMES, GREP_PATTERN } from './constants';\n\nconst MIGRATION_RULES_DIRECTORY = dirname(fileURLToPath(import.meta.url));\nconst MIGRATION_RULES_PATH = join(MIGRATION_RULES_DIRECTORY, 'MIGRATION_RULES.md');\n\nconst transformer = async (targetDirectories: string[], isDebug = false) => {\n const migrationRules = readFileSync(MIGRATION_RULES_PATH, 'utf-8');\n\n return claudeTransformer({\n targetDirectories,\n isDebug,\n componentGrepPattern: GREP_PATTERN,\n additionalPromptContext: `Deprecated components: ${DEPRECATED_COMPONENT_NAMES.join(', ')}\\n${migrationRules}`,\n });\n};\n\nexport default transformer;\n"],"mappings":";;;;;;;AAAA,MAAa,6BAA6B;CACxC;CACA;CACA;CACA;CACA;CACA;CACA;CACD;AACD,MAAa,eAAe,IAAI,OAC9B,2BAA2B,2BAA2B,KAAK,IAAI,CAAC,6DAChE,IACD;;;;ACJD,MAAM,4IADkE,CAAC,EACZ,qBAAqB;AAElF,MAAM,cAAc,OAAO,mBAA6B,UAAU,UAAU;CAC1E,MAAM,2CAA8B,sBAAsB,QAAQ;AAElE,QAAOA,iCAAkB;EACvB;EACA;EACA,sBAAsB;EACtB,yBAAyB,0BAA0B,2BAA2B,KAAK,KAAK,CAAC,IAAI;EAC9F,CAAC;;AAGJ,0BAAe"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wise/wds-codemods",
3
- "version": "1.1.3-experimental-378bc79",
3
+ "version": "1.1.3-experimental-4388372",
4
4
  "license": "UNLICENSED",
5
5
  "author": "Wise Payments Ltd.",
6
6
  "repository": {
@@ -39,7 +39,7 @@
39
39
  "@listr2/manager": "^3.0.5",
40
40
  "@types/spinnies": "^0.5.3",
41
41
  "jscodeshift": "^17.3",
42
- "listr2": "^10.2.1",
42
+ "listr2": "^9.0.5",
43
43
  "spinnies": "^0.5.1"
44
44
  },
45
45
  "devDependencies": {