@wise/wds-codemods 1.0.0-experimental-1fe0062 → 1.0.0-experimental-15c4e0a

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.
@@ -1,26 +1,40 @@
1
1
  const require_constants = require('./constants-CcE2TmzN.js');
2
2
  let node_child_process = require("node:child_process");
3
3
  let node_path = require("node:path");
4
- let _listr2_manager = require("@listr2/manager");
4
+ let spinnies = require("spinnies");
5
+ spinnies = require_constants.__toESM(spinnies);
5
6
  let node_fs = require("node:fs");
6
- let listr2 = require("listr2");
7
7
  let node_https = require("node:https");
8
8
  node_https = require_constants.__toESM(node_https);
9
- let _anthropic_ai_claude_agent_sdk = require("@anthropic-ai/claude-agent-sdk");
9
+ let __anthropic_ai_claude_agent_sdk = require("@anthropic-ai/claude-agent-sdk");
10
+ let diff = require("diff");
10
11
 
12
+ //#region src/helpers/spinnerLogs.ts
13
+ function logToConsole({ spinnies: spinnies$1, spinnerId, options }) {
14
+ spinnies$1.add(spinnerId, options);
15
+ }
16
+ function logStaticMessage(spinnies$1, message) {
17
+ logToConsole({
18
+ spinnies: spinnies$1,
19
+ spinnerId: `static-message-${Date.now()}-${message.slice(0, 10).replace(/\s+/gu, "-")}-${Math.floor(Math.random() * 1e4)}`,
20
+ options: {
21
+ text: message,
22
+ status: "non-spinnable"
23
+ }
24
+ });
25
+ }
26
+
27
+ //#endregion
11
28
  //#region src/transforms/list-item/constants.ts
12
- const DIRECTORY_CONCURRENCY_LIMIT = 3;
13
- const FILE_CONCURRENCY_LIMIT = 10;
14
29
  const DEPRECATED_COMPONENT_NAMES = [
15
30
  "ActionOption",
16
31
  "NavigationOption",
17
- "NavigationOptionsList",
32
+ "NavigationOptionList",
18
33
  "Summary",
19
34
  "SwitchOption",
20
35
  "CheckboxOption",
21
36
  "RadioOption"
22
37
  ];
23
- const GREP_PATTERN = new RegExp(`import\\s*\\{[\\s\\S]*?(${DEPRECATED_COMPONENT_NAMES.join("|")})[\\s\\S]*?\\}\\s*from\\s*['"]@transferwise/components['"]`, "u");
24
38
  const MIGRATION_RULES = `Migration rules:
25
39
  # Legacy Component → ListItem Migration Guide
26
40
 
@@ -162,26 +176,6 @@ const MIGRATION_RULES = `Migration rules:
162
176
  <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>} />
163
177
  \`\`\`
164
178
 
165
- ---
166
-
167
- ## DefinitionList → Multiple ListItem
168
-
169
- - Array → individual \`ListItem\`s
170
- - \`value\` → \`subtitle\`
171
- - \`key\` → React \`key\` prop
172
- - Action type: "Edit"/"Update"/"View" → \`ListItem.Button priority="secondary-neutral"\`, "Change"/"Password" → \`ListItem.Navigation\`, "Copy" → \`ListItem.IconButton\`
173
-
174
- \`\`\`tsx
175
- <DefinitionList definitions={[
176
- {title:'T1', value:'V1', key:'k1'},
177
- {title:'T2', value:'V2', key:'k2', action:{label:'Edit', onClick:fn}}
178
- ]} />
179
-
180
-
181
- <ListItem key="k1" title="T1" subtitle="V1" />
182
- <ListItem key="k2" title="T2" subtitle="V2" control={<ListItem.Button priority="secondary-neutral" onClick={fn}>Edit</ListItem.Button>} />
183
-
184
- \`\`\`
185
179
  `;
186
180
  const SYSTEM_PROMPT = `You are a code migration assistant that helps migrate TypeScript/JSX code from deprecated Wise Design System (WDS) components to the new ListItem component and ListItem subcomponents from '@transferwise/components'.
187
181
 
@@ -228,39 +222,74 @@ function generateElapsedTime(startTime) {
228
222
  const seconds = elapsedTime % 60;
229
223
  return `${hours ? `${hours}h ` : ""}${minutes ? `${minutes}m ` : ""}${seconds ? `${seconds}s` : ""}`;
230
224
  }
225
+ function formatClaudeResponseContent(content) {
226
+ return `\x1b[2m${content.replace(/\*\*(.+?)\*\*/gu, "\x1B[1m$1\x1B[0m\x1B[2m").replace(/`(.+?)`/gu, "\x1B[32m$1\x1B[0m\x1B[2m").split("\n").map((line, index) => index === 0 ? line : ` ${line}`).join("\n")}\x1b[0m`;
227
+ }
228
+ function generateDiff(original, modified) {
229
+ const lines = (0, diff.createPatch)("", original, modified, "", "").trim().split("\n").slice(4).filter((line) => !line.startsWith("\"));
230
+ let oldLineNumber = 0;
231
+ let newLineNumber = 0;
232
+ return lines.map((line) => {
233
+ const trimmedLine = line.trimEnd();
234
+ if (trimmedLine.startsWith("@@")) {
235
+ const match = /@@ -(\d+)(?:,\d+)? \+(\d+)(?:,\d+)? @@/u.exec(trimmedLine);
236
+ if (match) {
237
+ oldLineNumber = Number.parseInt(match[1], 10);
238
+ newLineNumber = Number.parseInt(match[2], 10);
239
+ }
240
+ return `\x1b[36m${trimmedLine}\x1b[0m`;
241
+ }
242
+ let linePrefix = "";
243
+ if (trimmedLine.startsWith("+")) {
244
+ linePrefix = `${newLineNumber.toString().padStart(4, " ")} `;
245
+ newLineNumber += 1;
246
+ return `\x1b[32m${linePrefix}${trimmedLine}\x1b[0m`;
247
+ }
248
+ if (trimmedLine.startsWith("-")) {
249
+ linePrefix = `${oldLineNumber.toString().padStart(4, " ")} `;
250
+ oldLineNumber += 1;
251
+ return `\x1b[31m${linePrefix}${trimmedLine}\x1b[0m`;
252
+ }
253
+ linePrefix = `${oldLineNumber.toString().padStart(4, " ")} `;
254
+ oldLineNumber += 1;
255
+ newLineNumber += 1;
256
+ return `${linePrefix}${trimmedLine}`;
257
+ }).join("\n");
258
+ }
231
259
 
232
260
  //#endregion
233
261
  //#region src/transforms/list-item/claude.ts
234
262
  const CLAUDE_SETTINGS_FILE = ".claude/settings.json";
235
- async function checkVPN(baseUrl, task) {
236
- if (!baseUrl) return;
237
- const checkOnce = async () => new Promise((resolveCheck) => {
238
- const url = new URL("/health", baseUrl);
239
- const req = node_https.default.get(url, {
240
- timeout: 2e3,
241
- rejectUnauthorized: false
242
- }, (res) => {
243
- const ok = !!(res.statusCode && res.statusCode >= 200 && res.statusCode < 400);
244
- res.resume();
245
- resolveCheck(ok);
246
- });
247
- req.on("timeout", () => {
248
- req.destroy(/* @__PURE__ */ new Error("timeout"));
263
+ async function checkVPN(spinnies$1, baseUrl) {
264
+ if (baseUrl) {
265
+ spinnies$1.add("vpnCheck", { text: "Checking VPN connection..." });
266
+ const checkOnce = async () => new Promise((resolveCheck) => {
267
+ const url = new URL("/health", baseUrl);
268
+ const req = node_https.default.get(url, {
269
+ timeout: 2e3,
270
+ rejectUnauthorized: false
271
+ }, (res) => {
272
+ const ok = !!(res.statusCode && res.statusCode >= 200 && res.statusCode < 400);
273
+ res.resume();
274
+ resolveCheck(ok);
275
+ });
276
+ req.on("timeout", () => {
277
+ req.destroy(/* @__PURE__ */ new Error("timeout"));
278
+ });
279
+ req.on("error", () => resolveCheck(false));
249
280
  });
250
- req.on("error", () => resolveCheck(false));
251
- });
252
- while (true) {
253
- if (await checkOnce()) {
254
- task.title = "Connected to VPN";
255
- break;
256
- }
257
- for (let countdown = 5; countdown > 0; countdown -= 1) {
258
- task.output = `Please connect to VPN... retrying in ${countdown}s`;
281
+ while (true) {
282
+ if (await checkOnce()) {
283
+ spinnies$1.succeed("vpnCheck", { text: "Connected to VPN" });
284
+ break;
285
+ }
286
+ spinnies$1.update("vpnCheck", { text: "Please connect to VPN... retrying in 3s" });
259
287
  await new Promise((response) => {
260
- setTimeout(response, 1e3);
288
+ setTimeout(response, 3e3);
261
289
  });
262
290
  }
263
291
  }
292
+ return true;
264
293
  }
265
294
  function getQueryOptions(sessionId) {
266
295
  const claudeSettingsPath = (0, node_path.resolve)(process.env.HOME || "", CLAUDE_SETTINGS_FILE);
@@ -269,7 +298,7 @@ function getQueryOptions(sessionId) {
269
298
  try {
270
299
  apiKey = (0, node_child_process.execSync)(`bash ${settings.apiKeyHelper}`, { encoding: "utf-8" }).trim();
271
300
  } catch {}
272
- 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");
301
+ if (!apiKey) throw new Error("Failed to retrieve Anthropic API key. Please check your Claude Code x LLM Gateway configuration - https://transferwise.atlassian.net/wiki/x/_YUe3Q");
273
302
  const { ANTHROPIC_CUSTOM_HEADERS, ...restEnvVars } = settings?.env ?? {};
274
303
  return {
275
304
  resume: sessionId,
@@ -294,52 +323,58 @@ function getQueryOptions(sessionId) {
294
323
  };
295
324
  }
296
325
  /** Initiate a new Claude session/conversation and return reusable options */
297
- async function initiateClaudeSessionOptions(manager) {
326
+ async function initiateClaudeSessionOptions(spinnies$1) {
298
327
  const options = getQueryOptions(void 0);
299
- manager.add([{
300
- title: "Checking VPN connection",
301
- task: async (_, task) => checkVPN(options.env?.ANTHROPIC_BASE_URL, task)
302
- }]);
303
- await manager.runAll();
304
- manager.add([{
305
- title: "Starting Claude instance",
306
- task: async (_, task) => {
307
- task.output = "Your browser may open for Okta authentication if required";
308
- const result = (0, _anthropic_ai_claude_agent_sdk.query)({
309
- options,
310
- prompt: `You'll be given file paths in additional individual queries to search in for files using deprecated Wise Design System (WDS) components. Migrate the code per the provided migration rules.`
311
- });
312
- for await (const message of result) switch (message.type) {
313
- case "system":
314
- if (message.subtype === "init" && !options.resume) {
315
- task.title = "Successfully initialised Claude instance\n";
316
- options.resume = message.session_id;
317
- }
318
- break;
319
- default: if (message.type === "result" && message.subtype !== "success") throw new Error(`Claude encountered an error when initialising: ${message.errors.join("\n")}`);
328
+ await checkVPN(spinnies$1, options.env?.ANTHROPIC_BASE_URL);
329
+ spinnies$1.add("claudeSession", { text: "Starting and verifying Claude instance - your browser may open for Okta authentication if required." });
330
+ const result = (0, __anthropic_ai_claude_agent_sdk.query)({
331
+ options,
332
+ prompt: `You'll be given file paths in additional individual queries to search in for files using deprecated Wise Design System (WDS) components. Migrate the code per the provided migration rules.`
333
+ });
334
+ for await (const message of result) switch (message.type) {
335
+ case "system":
336
+ if (message.subtype === "init" && !options.resume) {
337
+ spinnies$1.succeed("claudeSession", { text: "Successfully initialised Claude instance" });
338
+ spinnies$1.add("placeholder", { text: " " });
339
+ options.resume = message.session_id;
320
340
  }
321
- task.task.complete();
341
+ break;
342
+ default: if (message.type === "result" && message.subtype !== "success") {
343
+ spinnies$1.fail("claudeSession", { text: `Claude encountered an error when initialising: ${message.errors.join("\n")}` });
344
+ spinnies$1.stopAll("fail");
322
345
  }
323
- }]);
324
- await manager.runAll().finally(() => {
325
- manager.options = { concurrent: DIRECTORY_CONCURRENCY_LIMIT };
326
- });
346
+ }
327
347
  return options;
328
348
  }
329
- async function queryClaude(directory, filePath, options, task, isDebug = false) {
349
+ async function queryClaude(directory, filePath, options, codemodOptions, spinnies$1, isDebug = false) {
330
350
  const startTime = Date.now();
331
- const result = (0, _anthropic_ai_claude_agent_sdk.query)({
351
+ const debugSpinnies = new spinnies.default();
352
+ const result = (0, __anthropic_ai_claude_agent_sdk.query)({
332
353
  options,
333
354
  prompt: filePath
334
355
  });
335
356
  for await (const message of result) switch (message.type) {
357
+ case "assistant":
358
+ for (const msg of message.message.content) switch (msg.type) {
359
+ case "tool_use":
360
+ if (msg.name === "Read") {
361
+ spinnies$1.remove("placeholder");
362
+ spinnies$1.update(`file-${filePath}`, { text: `${formatPathOutput(directory, filePath, true)} - Reading...` });
363
+ } else if (msg.name === "Edit") spinnies$1.update(`file-${filePath}`, { text: `${formatPathOutput(directory, filePath, true)} - Migrating...` });
364
+ break;
365
+ case "text":
366
+ if (isDebug) logStaticMessage(debugSpinnies, `${require_constants.CONSOLE_ICONS.claude} ${formatClaudeResponseContent(msg.text)}`);
367
+ break;
368
+ default:
369
+ }
370
+ break;
336
371
  case "result":
337
- if (message.subtype === "success" && isDebug) {
338
- task.title = `\x1b[2m${formatPathOutput(directory, filePath)}\x1b[0m]`;
339
- task.output = `\x1b[2mMigrated in ${generateElapsedTime(startTime)}\x1b[0m`;
340
- } else if (message.is_error) {
341
- task.title = `\x1b[2m${formatPathOutput(directory, filePath)}\x1b[0m]`;
342
- task.output = `${require_constants.CONSOLE_ICONS.error} Claude encountered an error: ${JSON.stringify(message)}`;
372
+ if (message.subtype === "success") spinnies$1.succeed(`file-${filePath}`, { text: `${formatPathOutput(directory, filePath, true)} - Migrated in: ${generateElapsedTime(startTime)}` });
373
+ else {
374
+ logStaticMessage(debugSpinnies, `${require_constants.CONSOLE_ICONS.error} Claude encountered an error:`);
375
+ console.log(message);
376
+ spinnies$1.stopAll("fail");
377
+ debugSpinnies.stopAll("fail");
343
378
  }
344
379
  break;
345
380
  default:
@@ -348,45 +383,57 @@ async function queryClaude(directory, filePath, options, task, isDebug = false)
348
383
 
349
384
  //#endregion
350
385
  //#region src/transforms/list-item/transformer.ts
351
- const transformer = async (targetPaths, isDebug = false) => {
352
- process.setMaxListeners(20);
386
+ const transformer = async (targetPaths, codemodOptions, isDebug = false) => {
353
387
  const startTime = Date.now();
354
- const manager = new _listr2_manager.Manager({ concurrent: true });
355
- const queryOptions = await initiateClaudeSessionOptions(manager);
388
+ const spinnies$1 = new spinnies.default();
389
+ const queryOptions = await initiateClaudeSessionOptions(spinnies$1);
390
+ spinnies$1.remove("placeholder");
391
+ spinnies$1.add("analysing", { text: "Analysing targetted paths - this may take a while..." });
356
392
  for (const directory of targetPaths) {
357
- const matchingFilePaths = (0, node_child_process.execSync)(`find "${directory}" -name "*.tsx" -type f`, { encoding: "utf-8" }).trim().split("\n").filter(Boolean).filter((filePath) => {
358
- const content = (0, node_fs.readFileSync)(filePath, "utf-8");
359
- return GREP_PATTERN.test(content);
393
+ spinnies$1.add(`directory-${directory}`, {
394
+ indent: 2,
395
+ text: `${formatPathOutput(directory)} - Processing directory`
360
396
  });
361
- if (matchingFilePaths.length === 0) manager.add([{
362
- title: `\x1b[2m${formatPathOutput(directory)} - No files need migration\x1b[0m`,
363
- task: (ctx) => {
364
- ctx.skip = true;
397
+ const allTsxFiles = (0, node_child_process.execSync)(`find "${directory}" -name "*.tsx" -type f`, { encoding: "utf-8" }).trim().split("\n").filter(Boolean);
398
+ const grepPattern = /* @__PURE__ */ new RegExp(`import\\s*\\{[\\s\\S]*?(${DEPRECATED_COMPONENT_NAMES.join("|")})[\\s\\S]*?\\}\\s*from\\s*['"]@transferwise/components['"]`);
399
+ const matchingFilePaths = allTsxFiles.filter((filePath) => {
400
+ try {
401
+ const content = (0, node_fs.readFileSync)(filePath, "utf-8");
402
+ return grepPattern.test(content);
403
+ } catch {
404
+ return false;
365
405
  }
366
- }]);
367
- else manager.add([{
368
- title: `${formatPathOutput(directory)} - Found \x1b[32m${matchingFilePaths.length}\x1b[0m file(s) needing migration`,
369
- task: async (_ctx, parentTask) => {
370
- parentTask.title = `${formatPathOutput(directory)} - Migrating \x1b[32m${matchingFilePaths.length}\x1b[0m file(s)...`;
371
- const completedFilesInDirectory = { count: 0 };
372
- return parentTask.newListr(matchingFilePaths.map((filePath) => ({
373
- title: "",
374
- task: async (_fileCtx, fileTask) => {
375
- await queryClaude(directory, filePath, queryOptions, fileTask, isDebug).finally(() => {
376
- completedFilesInDirectory.count += 1;
377
- const isDim = completedFilesInDirectory.count === matchingFilePaths.length;
378
- parentTask.title = `${isDim ? "\x1B[2m" : ""}${formatPathOutput(directory)} - Migrated \x1b[32m${completedFilesInDirectory.count}\x1b[0m/\x1b[32m${matchingFilePaths.length}\x1b[0m files${isDim ? "\x1B[0m" : ""}`;
379
- });
380
- }
381
- })), { concurrent: FILE_CONCURRENCY_LIMIT }).run();
406
+ });
407
+ spinnies$1.update(`directory-${directory}`, { text: `${formatPathOutput(directory)} - Found \x1b[32m${matchingFilePaths.length}\x1b[0m \x1b[2m*.tsx\x1b[0m file(s) needing migration` });
408
+ for (const filePath of matchingFilePaths) {
409
+ spinnies$1.add(`file-${filePath}`, {
410
+ indent: 4,
411
+ text: `${formatPathOutput(directory, filePath, true)} - Parsing...`
412
+ });
413
+ const originalFileContent = (0, node_fs.readFileSync)(filePath, "utf-8");
414
+ spinnies$1.update(`file-${filePath}`, {
415
+ indent: 4,
416
+ text: `${formatPathOutput(directory, filePath, true)} - Processing...`
417
+ });
418
+ await queryClaude(directory, filePath, queryOptions, codemodOptions, spinnies$1, isDebug);
419
+ const newFileContent = (0, node_fs.readFileSync)(filePath, "utf-8");
420
+ if (codemodOptions.isPrint) {
421
+ logStaticMessage(spinnies$1, `Diff for ${formatPathOutput(directory, filePath, true)}:`);
422
+ logStaticMessage(spinnies$1, generateDiff(originalFileContent, newFileContent));
382
423
  }
383
- }]);
424
+ spinnies$1.remove("placeholder");
425
+ }
426
+ if (matchingFilePaths.length === 0) spinnies$1.succeed(`directory-${directory}`, { text: `${formatPathOutput(directory)} - No files need migration` });
427
+ else spinnies$1.succeed(`directory-${directory}`, {
428
+ indent: 2,
429
+ text: `${formatPathOutput(directory)} - Migrated \x1b[32m${matchingFilePaths.length}\x1b[0m file(s) successfully`
430
+ });
384
431
  }
385
- await manager.runAll().finally(async () => {
386
- await new listr2.Listr([{
387
- title: `Finished migrating - elapsed time: \x1b[32m${generateElapsedTime(startTime)}\x1b[0m `,
388
- task: async () => {}
389
- }]).run();
432
+ spinnies$1.succeed("analysing", { text: "Successfully analysed all targetted paths" });
433
+ spinnies$1.stopAll("succeed");
434
+ spinnies$1.add("done", {
435
+ text: `Finished migrating - elapsed time: \x1b[1m${generateElapsedTime(startTime)}\x1b[0m`,
436
+ status: "succeed"
390
437
  });
391
438
  };
392
439
  var transformer_default = transformer;
@@ -398,4 +445,4 @@ Object.defineProperty(exports, 'transformer_default', {
398
445
  return transformer_default;
399
446
  }
400
447
  });
401
- //# sourceMappingURL=transformer-DT78W0S6.js.map
448
+ //# sourceMappingURL=transformer-DLI9Ra38.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transformer-DLI9Ra38.js","names":["https","spinnies","Spinnies","CONSOLE_ICONS","spinnies","Spinnies"],"sources":["../src/helpers/spinnerLogs.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":["import type Spinnies from 'spinnies';\n\ninterface LogConsoleSpinnerSettings {\n spinnies: Spinnies;\n spinnerId: string;\n options: Partial<Spinnies.SpinnerOptions>;\n}\n\nexport function logToConsole({ spinnies, spinnerId, options }: LogConsoleSpinnerSettings): void {\n spinnies.add(spinnerId, options);\n}\n\nexport function logStaticMessage(spinnies: Spinnies, message: string): void {\n // Create a unique spinner ID for the static message using timestamp, first part of the message and a random number\n const uuid = `static-message-${Date.now()}-${message.slice(0, 10).replace(/\\s+/gu, '-')}-${Math.floor(Math.random() * 10000)}`;\n logToConsole({ spinnies, spinnerId: uuid, options: { text: message, status: 'non-spinnable' } });\n}\n","export const DEPRECATED_COMPONENT_NAMES = [\n 'ActionOption',\n 'NavigationOption',\n 'NavigationOptionList',\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. \\`title\\` → \\`title\\` (direct)\n2. \\`content\\` or \\`description\\` → \\`subtitle\\`\n3. \\`disabled\\` stays on \\`ListItem\\` (not controls)\n4. Keep HTML attributes (\\`id\\`, \\`name\\`, \\`aria-label\\`), remove: \\`as\\`, \\`complex\\`, \\`showMediaAtAllSizes\\`, \\`showMediaCircle\\`, \\`isContainerAligned\\`\n5. In strings, don't convert \\`\\`to\\`'\\`or\\`\"\\`. Preserve what is there.\n\n---\n\n## ActionOption → ListItem.Button\n\n- \\`action\\` → Button children\n- \\`onClick\\` → Button \\`onClick\\`\n- Priority: default/\\`\"primary\"\\` → \\`\"primary\"\\`, \\`\"secondary\"\\` → \\`\"secondary-neutral\"\\`, \\`\"secondary-send\"\\` → \\`\"secondary\"\\`, \\`\"tertiary\"\\` → \\`\"tertiary\"\\`\n\n\\`\\`\\`tsx\n<ActionOption title=\"Title\" content=\"Text\" action=\"Click\" priority=\"secondary\" onClick={fn} />\n→\n<ListItem title=\"Title\" subtitle=\"Text\" control={<ListItem.Button priority=\"secondary-neutral\" onClick={fn}>Click</ListItem.Button>} />\n\\`\\`\\`\n\n---\n\n## CheckboxOption → ListItem.Checkbox\n\n- \\`onChange\\`: \\`(checked: boolean)\\` → \\`(event: ChangeEvent)\\` use \\`event.target.checked\\`\n- \\`name\\` move to Checkbox\n- Don't move \\`id\\` to Checkbox\n\n\\`\\`\\`tsx\n<CheckboxOption id=\"x\" name=\"y\" title=\"Title\" content=\"Text\" checked={v} onChange={(c) => set(c)} />\n→\n<ListItem title=\"Title\" subtitle=\"Text\" control={<ListItem.Checkbox name=\"y\" checked={v} onChange={(e) => set(e.target.checked)} />} />\n\\`\\`\\`\n\n---\n\n## RadioOption → ListItem.Radio\n\n- \\`name\\`, \\`value\\`, \\`checked\\`, \\`onChange\\` move to Radio\n- Don't move \\`id\\` to Radio\n\n\\`\\`\\`tsx\n<RadioOption id=\"x\" name=\"y\" value=\"v\" title=\"Title\" content=\"Text\" checked={v==='v'} onChange={set} />\n→\n<ListItem title=\"Title\" subtitle=\"Text\" control={<ListItem.Radio name=\"y\" value=\"v\" checked={v==='v'} onChange={set} />} />\n\\`\\`\\`\n\n---\n\n## SwitchOption → ListItem.Switch\n\n- \\`onChange\\` → \\`onClick\\`, toggle manually\n- \\`aria-label\\` moves to Switch\n\n\\`\\`\\`tsx\n<SwitchOption title=\"Title\" content=\"Text\" checked={v} aria-label=\"Toggle\" onChange={set} />\n→\n<ListItem title=\"Title\" subtitle=\"Text\" control={<ListItem.Switch checked={v} aria-label=\"Toggle\" onClick={() => set(!v)} />} />\n\\`\\`\\`\n\n---\n\n## NavigationOption → ListItem.Navigation\n\n- \\`onClick\\` or \\`href\\` move to Navigation\n\n\\`\\`\\`tsx\n<NavigationOption title=\"Title\" content=\"Text\" onClick={fn} />\n→\n<ListItem title=\"Title\" subtitle=\"Text\" control={<ListItem.Navigation onClick={fn} />} />\n\\`\\`\\`\n\n---\n\n## Option → ListItem\n\n- Wrap \\`media\\` in \\`ListItem.AvatarView\\`\n\n\\`\\`\\`tsx\n<Option media={<Icon />} title=\"Title\" />\n→\n<ListItem title=\"Title\" media={<ListItem.AvatarView><Icon /></ListItem.AvatarView>} />\n\\`\\`\\`\n\n---\n\n## Summary → ListItem\n\n**Basic:**\n\n- \\`icon\\` → wrap in \\`ListItem.AvatarView\\` with \\`size={32}\\` as \\`media\\`\n- Remove \\`size\\` from child \\`<Icon />\\`\n\n**Status:**\n\n- \\`Status.DONE\\` → \\`badge={{ status: 'positive' }}\\`\n- \\`Status.PENDING\\` → \\`badge={{ status: 'pending' }}\\`\n- \\`Status.NOT_DONE\\` → no badge\n\n**Action:**\n\n- \\`action.text\\` → \\`action.label\\` in \\`ListItem.AdditionalInfo\\` as \\`additionalInfo\\`\n\n**Info (requires state):**\n\n- \\`MODAL\\` → \\`ListItem.IconButton partiallyInteractive\\` + \\`<Modal>\\` in \\`control\\`\n- \\`POPOVER\\` → \\`<Popover>\\` wrapping \\`ListItem.IconButton partiallyInteractive\\` in \\`control\\`\n- Use \\`QuestionMarkCircle\\` icon (import from \\`@transferwise/icons\\`)\n\n\\`\\`\\`tsx\n// Basic\n<Summary title=\"T\" description=\"D\" icon={<Icon />} />\n→\n<ListItem title=\"T\" subtitle=\"D\" media={<ListItem.AvatarView size={32}><Icon /></ListItem.AvatarView>} />\n\n// Status\n<Summary title=\"T\" description=\"D\" icon={<Icon />} status={Status.DONE} />\n→\n<ListItem title=\"T\" subtitle=\"D\" media={<ListItem.AvatarView size={32} badge={{status:'positive'}}><Icon /></ListItem.AvatarView>} />\n\n// Action\n<Summary title=\"T\" description=\"D\" icon={<Icon />} action={{text:'Go', href:'/go'}} />\n→\n<ListItem title=\"T\" subtitle=\"D\" media={<ListItem.AvatarView size={32}><Icon /></ListItem.AvatarView>} additionalInfo={<ListItem.AdditionalInfo action={{label:'Go', href:'/go'}} />} />\n\n// Modal (add: const [open, setOpen] = useState(false))\n<Summary title=\"T\" description=\"D\" icon={<Icon />} info={{title:'Help', content:'Text', presentation:'MODAL', 'aria-label':'Info'}} />\n→\n<ListItem title=\"T\" subtitle=\"D\" media={<ListItem.AvatarView size={32}><Icon /></ListItem.AvatarView>} control={<ListItem.IconButton partiallyInteractive aria-label=\"Info\" onClick={()=>setOpen(!open)}><QuestionMarkCircle /><Modal open={open} title=\"Help\" body=\"Text\" onClose={()=>setOpen(false)} /></ListItem.IconButton>} />\n\n// Popover\n<Summary title=\"T\" description=\"D\" icon={<Icon />} info={{title:'Help', content:'Text', presentation:'POPOVER', 'aria-label':'Info'}} />\n→\n<ListItem title=\"T\" subtitle=\"D\" media={<ListItem.AvatarView size={32}><Icon /></ListItem.AvatarView>} control={<Popover title=\"Help\" content=\"Text\" onClose={()=>setOpen(false)}><ListItem.IconButton partiallyInteractive aria-label=\"Info\"><QuestionMarkCircle /></ListItem.IconButton></Popover>} />\n\\`\\`\\`\n\n`;\n\nexport const SYSTEM_PROMPT = `You are a code migration assistant that helps migrate TypeScript/JSX code from deprecated Wise Design System (WDS) components to the new ListItem component and ListItem subcomponents from '@transferwise/components'.\n\nRules:\n1. Only ever modify files via the Edit tool - do not use the Write tool\n2. When identifying what code to migrate within a file, explain how you identified it first.\n2. Migrate components per provided migration rules\n3. Maintain TypeScript type safety and update types to match new API\n4. Map props: handle renamed, deprecated, new required, and changed types\n5. Update imports to new WDS components and types\n6. Preserve code style, formatting, and calculated logic\n7. Handle conditional rendering, spread props, and complex expressions\n8. Note: New components may lack feature parity with legacy versions\n9. Only modify code requiring changes per migration rules, and any impacted surrounding code for context.\n10. Final result response should just be whether the migration was successful overall, or if any errors were encountered\n - Do not summarise or explain the changes made\n11. Explain your reasoning and justification before making changes, as you edit each file.\n - Keep it concise and succinct, as only bullet points\n12. After modifying the file, do not summarise the changes made.\n13. If you do not have permission to edit a file, still attempt to edit it and then move onto the next file.\n\nYou'll receive:\n- File paths to migrate in individual queries\n- Deprecated component names at the end of this prompt\n- Migration context and rules for each deprecated component\n\nDeprecated components: ${DEPRECATED_COMPONENT_NAMES.join(', ')}.\n\n${MIGRATION_RULES}`;\n","import { createPatch } from 'diff';\n\n/** Split the path to get the relative path after the directory, and wrap with ANSI color codes */\nexport function formatPathOutput(directory: string, path?: string, asDim?: boolean): string {\n const relativePath = path ? (path.split(directory.replace('.', ''))[1] ?? path) : directory;\n return asDim ? `\\x1b[2m${relativePath}\\x1b[0m` : `\\x1b[32m${relativePath}\\x1b[0m`;\n}\n\n/** Generates a formatted string representing the total elapsed time since the given start time */\nexport function generateElapsedTime(startTime: number): string {\n const endTime = Date.now();\n const elapsedTime = Math.floor((endTime - startTime) / 1000);\n const hours = Math.floor(elapsedTime / 3600);\n const minutes = Math.floor((elapsedTime % 3600) / 60);\n const seconds = elapsedTime % 60;\n\n return `${hours ? `${hours}h ` : ''}${minutes ? `${minutes}m ` : ''}${seconds ? `${seconds}s` : ''}`;\n}\n\n// Formats Claude response with ANSI codes (bold for **, green for `), indenting all lines except the first. Also wraps entire content in dim color\nexport function formatClaudeResponseContent(content: string): string {\n const formatted = content\n .replace(/\\*\\*(.+?)\\*\\*/gu, '\\x1b[1m$1\\x1b[0m\\x1b[2m')\n .replace(/`(.+?)`/gu, '\\x1b[32m$1\\x1b[0m\\x1b[2m')\n .split('\\n')\n .map((line, index) => (index === 0 ? line : ` ${line}`))\n .join('\\n');\n\n return `\\x1b[2m${formatted}\\x1b[0m`;\n}\n\n// Generates a unified diff between the original and modified strings, with ANSI color codes for additions and deletions and line numbers\nexport function generateDiff(original: string, modified: string): string {\n const diffResult = createPatch('', original, modified, '', '').trim();\n\n // Parse and colorize the diff output\n const lines = diffResult\n .split('\\n')\n .slice(4) // Skip the header lines\n .filter((line) => !line.startsWith('\\\')); // Remove \"No newline\" messages\n\n // Track current line numbers from hunk headers\n let oldLineNumber = 0;\n let newLineNumber = 0;\n\n const colouredDiff = lines\n .map((line: string) => {\n const trimmedLine = line.trimEnd();\n\n // Parse hunk header to get starting line numbers - format: @@ -oldStart,oldCount +newStart,newCount @@\n if (trimmedLine.startsWith('@@')) {\n const match = /@@ -(\\d+)(?:,\\d+)? \\+(\\d+)(?:,\\d+)? @@/u.exec(trimmedLine);\n if (match) {\n oldLineNumber = Number.parseInt(match[1], 10);\n newLineNumber = Number.parseInt(match[2], 10);\n }\n return `\\x1b[36m${trimmedLine}\\x1b[0m`; // Cyan for hunk headers\n }\n\n let linePrefix = '';\n // Green styling for additions\n if (trimmedLine.startsWith('+')) {\n linePrefix = `${newLineNumber.toString().padStart(4, ' ')} `;\n newLineNumber += 1;\n return `\\x1b[32m${linePrefix}${trimmedLine}\\x1b[0m`;\n }\n\n // Red styling for deletions\n if (trimmedLine.startsWith('-')) {\n linePrefix = `${oldLineNumber.toString().padStart(4, ' ')} `;\n oldLineNumber += 1;\n return `\\x1b[31m${linePrefix}${trimmedLine}\\x1b[0m`;\n }\n\n // Handle unchanged lines\n linePrefix = `${oldLineNumber.toString().padStart(4, ' ')} `;\n oldLineNumber += 1;\n newLineNumber += 1;\n return `${linePrefix}${trimmedLine}`;\n })\n .join('\\n');\n\n return colouredDiff;\n}\n","import https from 'node:https';\n\nimport { type Options, query } from '@anthropic-ai/claude-agent-sdk';\nimport { execSync } from 'child_process';\nimport { readFileSync } from 'fs';\nimport { resolve } from 'path';\nimport Spinnies from 'spinnies';\n\nimport { CONSOLE_ICONS } from '../../constants';\nimport type { CodemodOptions } from '../../controller/types';\nimport { logStaticMessage } from '../../helpers/spinnerLogs';\nimport { SYSTEM_PROMPT } from './constants';\nimport { formatClaudeResponseContent, formatPathOutput, generateElapsedTime } from './helpers';\nimport type { ClaudeSettings } from './types';\n\nconst CLAUDE_SETTINGS_FILE = '.claude/settings.json';\n\nasync function checkVPN(spinnies: Spinnies, baseUrl?: string): Promise<boolean> {\n if (baseUrl) {\n spinnies.add('vpnCheck', { text: 'Checking VPN connection...' });\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 spinnies.succeed('vpnCheck', { text: 'Connected to VPN' });\n break;\n }\n\n // TODO: Could update this to count down from 3s and fail after a certain number of attempts\n spinnies.update('vpnCheck', { text: 'Please connect to VPN... retrying in 3s' });\n await new Promise<void>((response) => {\n setTimeout(response, 3000);\n });\n }\n }\n return true;\n}\n\nexport function getQueryOptions(sessionId?: string): Options {\n // Read settings from ~/.claude/settings.json to get headers and apiKeyHelper\n const claudeSettingsPath = resolve(process.env.HOME || '', CLAUDE_SETTINGS_FILE);\n const settings = JSON.parse(readFileSync(claudeSettingsPath, 'utf-8')) as ClaudeSettings;\n\n // Get API key by executing the apiKeyHelper script, for authenticating with Okta via LLM Gateway\n let apiKey;\n try {\n apiKey = execSync(`bash ${settings.apiKeyHelper}`, {\n encoding: 'utf-8',\n }).trim();\n } catch {}\n\n if (!apiKey) {\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 { ANTHROPIC_CUSTOM_HEADERS, ...restEnvVars } = settings?.env ?? {};\n\n const envVars = {\n ANTHROPIC_AUTH_TOKEN: apiKey,\n ANTHROPIC_CUSTOM_HEADERS,\n ...restEnvVars,\n PATH: process.env.PATH, // Specifying PATH, as Claude Agent SDK seems to struggle consuming the actual environment PATH\n };\n\n return {\n resume: sessionId,\n env: envVars,\n permissionMode: 'acceptEdits',\n systemPrompt: {\n type: 'preset',\n preset: 'claude_code',\n append: SYSTEM_PROMPT,\n },\n allowedTools: ['Grep', 'Read'],\n settingSources: ['local', 'project', 'user'],\n };\n}\n\n/** Initiate a new Claude session/conversation and return reusable options */\nexport async function initiateClaudeSessionOptions(spinnies: Spinnies): Promise<Options> {\n const options = getQueryOptions(undefined);\n await checkVPN(spinnies, options.env?.ANTHROPIC_BASE_URL);\n\n spinnies.add('claudeSession', {\n text: 'Starting and verifying Claude instance - your browser may open for Okta authentication if required.',\n });\n\n const result = query({\n options,\n prompt: `You'll be given file paths in additional individual queries to search in for files using deprecated Wise Design System (WDS) components. Migrate the code per the provided migration rules.`,\n });\n\n for await (const message of result) {\n switch (message.type) {\n case 'system':\n if (message.subtype === 'init' && !options.resume) {\n spinnies.succeed('claudeSession', { text: 'Successfully initialised Claude instance' });\n spinnies.add('placeholder', { text: ' ' });\n\n // Set the session ID to resume the conversation in future queries\n options.resume = message.session_id;\n }\n break;\n default:\n if (message.type === 'result' && message.subtype !== 'success') {\n spinnies.fail('claudeSession', {\n text: `Claude encountered an error when initialising: ${message.errors.join('\\n')}`,\n });\n spinnies.stopAll('fail');\n }\n }\n }\n\n return options;\n}\n\n// Queries Claude with the given path and handles logging of tool uses and results\nexport async function queryClaude(\n directory: string,\n filePath: string,\n options: Options,\n codemodOptions: CodemodOptions,\n spinnies: Spinnies,\n isDebug = false,\n) {\n const startTime = Date.now();\n const debugSpinnies = new Spinnies();\n const result = query({\n options,\n prompt: filePath,\n });\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 === 'Read') {\n spinnies.remove('placeholder');\n spinnies.update(`file-${filePath}`, {\n text: `${formatPathOutput(directory, filePath, true)} - Reading...`,\n });\n } else if (msg.name === 'Edit') {\n spinnies.update(`file-${filePath}`, {\n text: `${formatPathOutput(directory, filePath, true)} - Migrating...`,\n });\n }\n\n break;\n case 'text':\n if (isDebug) {\n logStaticMessage(\n debugSpinnies,\n `${CONSOLE_ICONS.claude} ${formatClaudeResponseContent(msg.text)}`,\n );\n }\n break;\n default:\n }\n }\n break;\n case 'result':\n if (message.subtype === 'success') {\n spinnies.succeed(`file-${filePath}`, {\n text: `${formatPathOutput(directory, filePath, true)} - Migrated in: ${generateElapsedTime(startTime)}`,\n });\n } else {\n // Silence non-error errors (false positives)\n logStaticMessage(debugSpinnies, `${CONSOLE_ICONS.error} Claude encountered an error:`);\n console.log(message);\n spinnies.stopAll('fail');\n debugSpinnies.stopAll('fail');\n }\n break;\n default:\n }\n }\n}\n","import { execSync } from 'child_process';\nimport { readFileSync } from 'fs';\nimport Spinnies from 'spinnies';\n\nimport type { CodemodOptions } from '../../controller/types';\nimport { logStaticMessage } from '../../helpers/spinnerLogs';\nimport { initiateClaudeSessionOptions, queryClaude } from './claude';\nimport { DEPRECATED_COMPONENT_NAMES } from './constants';\nimport { formatPathOutput, generateDiff, generateElapsedTime } from './helpers';\n\nconst transformer = async (\n targetPaths: string[],\n codemodOptions: CodemodOptions,\n isDebug = false,\n) => {\n const startTime = Date.now();\n const spinnies = new Spinnies();\n const queryOptions = await initiateClaudeSessionOptions(spinnies);\n\n spinnies.remove('placeholder');\n spinnies.add('analysing', {\n text: 'Analysing targetted paths - this may take a while...',\n });\n\n for (const directory of targetPaths) {\n spinnies.add(`directory-${directory}`, {\n indent: 2,\n text: `${formatPathOutput(directory)} - Processing directory`,\n });\n\n // First, 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 const grepPattern = new RegExp(\n `import\\\\s*\\\\{[\\\\s\\\\S]*?(${DEPRECATED_COMPONENT_NAMES.join('|')})[\\\\s\\\\S]*?\\\\}\\\\s*from\\\\s*['\"]@transferwise/components['\"]`,\n );\n\n // Filter files that match the pattern by reading and testing each file\n const matchingFilePaths = allTsxFiles.filter((filePath) => {\n try {\n const content = readFileSync(filePath, 'utf-8');\n return grepPattern.test(content);\n } catch {\n return false;\n }\n });\n\n spinnies.update(`directory-${directory}`, {\n text: `${formatPathOutput(directory)} - Found \\x1b[32m${matchingFilePaths.length}\\x1b[0m \\x1b[2m*.tsx\\x1b[0m file(s) needing migration`,\n });\n\n // Get file path and file content for each matching file, and query Claude for migration\n for (const filePath of matchingFilePaths) {\n spinnies.add(`file-${filePath}`, {\n indent: 4,\n text: `${formatPathOutput(directory, filePath, true)} - Parsing...`,\n });\n\n const originalFileContent = readFileSync(filePath, 'utf-8');\n\n spinnies.update(`file-${filePath}`, {\n indent: 4,\n text: `${formatPathOutput(directory, filePath, true)} - Processing...`,\n });\n\n await queryClaude(directory, filePath, queryOptions, codemodOptions, spinnies, isDebug);\n\n const newFileContent = readFileSync(filePath, 'utf-8');\n\n // Create diff from originalFileContent to newFileContent\n if (codemodOptions.isPrint) {\n logStaticMessage(spinnies, `Diff for ${formatPathOutput(directory, filePath, true)}:`);\n logStaticMessage(spinnies, generateDiff(originalFileContent, newFileContent));\n }\n\n spinnies.remove('placeholder');\n }\n\n if (matchingFilePaths.length === 0) {\n spinnies.succeed(`directory-${directory}`, {\n text: `${formatPathOutput(directory)} - No files need migration`,\n });\n } else {\n spinnies.succeed(`directory-${directory}`, {\n indent: 2,\n text: `${formatPathOutput(directory)} - Migrated \\x1b[32m${matchingFilePaths.length}\\x1b[0m file(s) successfully`,\n });\n }\n }\n\n spinnies.succeed('analysing', {\n text: 'Successfully analysed all targetted paths',\n });\n spinnies.stopAll('succeed');\n spinnies.add('done', {\n text: `Finished migrating - elapsed time: \\x1b[1m${generateElapsedTime(startTime)}\\x1b[0m`,\n status: 'succeed',\n });\n};\n\nexport default transformer;\n"],"mappings":"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"}
@@ -1,5 +1,6 @@
1
1
  Object.defineProperty(exports, '__esModule', { value: true });
2
- const require_helpers = require('../../helpers-A50d9jU_.js');
2
+ require('../../constants-CcE2TmzN.js');
3
+ const require_helpers = require('../../helpers-IFtIGywc.js');
3
4
 
4
5
  //#region src/helpers/addImport.ts
5
6
  /**