@wise/wds-codemods 1.0.0-experimental-2614d6a → 1.0.0-experimental-e08b4bf
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +4 -3
- package/dist/index.js.map +1 -1
- package/dist/{transformer-BHViIQiv.js → transformer-DoAMzZmy.js} +43 -65
- package/dist/transformer-DoAMzZmy.js.map +1 -0
- package/dist/transforms/list-item/transformer.js +1 -1
- package/package.json +1 -1
- package/dist/transformer-BHViIQiv.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
const require_constants = require('./constants-CcE2TmzN.js');
|
|
3
|
-
const require_transformer = require('./transformer-
|
|
3
|
+
const require_transformer = require('./transformer-DoAMzZmy.js');
|
|
4
4
|
const require_helpers = require('./helpers-IFtIGywc.js');
|
|
5
5
|
let node_child_process = require("node:child_process");
|
|
6
6
|
let node_fs_promises = require("node:fs/promises");
|
|
@@ -43,6 +43,8 @@ async function runCodemod(transformsDir) {
|
|
|
43
43
|
const candidate = args[0];
|
|
44
44
|
isDebug = args.includes("--debug");
|
|
45
45
|
try {
|
|
46
|
+
const root = require_helpers.findProjectRoot();
|
|
47
|
+
const packagesPromise = require_helpers.findPackages();
|
|
46
48
|
const resolvedTransformsDir = transformsDir ?? node_path.default.resolve(currentDirPath, "../dist/transforms");
|
|
47
49
|
if (isDebug) console.debug(`${require_constants.CONSOLE_ICONS.info} Resolved transforms directory: ${resolvedTransformsDir}`);
|
|
48
50
|
const { transformFiles: resolvedTransformNames } = await require_helpers.loadTransformModules_default(resolvedTransformsDir);
|
|
@@ -66,8 +68,7 @@ async function runCodemod(transformsDir) {
|
|
|
66
68
|
if (transformFile === "list-item") require_helpers.validateClaudeConfig();
|
|
67
69
|
const spinnies$1 = new spinnies.default();
|
|
68
70
|
spinnies$1.add("loading-codemod", { text: `Loading codemod (${transformFile})...` });
|
|
69
|
-
const
|
|
70
|
-
const packages = await require_helpers.findPackages();
|
|
71
|
+
const packages = await packagesPromise;
|
|
71
72
|
const reportPath = node_path.default.resolve(root, "codemod-report.txt");
|
|
72
73
|
spinnies$1.succeed("loading-codemod", { text: `Successfully loaded codemod: \x1b[2m${transformFile}\x1b[0m` });
|
|
73
74
|
await resetReportFile(reportPath);
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["path","fs","CONSOLE_ICONS","logToInquirer","loadTransformModules","transformFile: string","spinnies","Spinnies","findProjectRoot","findPackages","runTransformPrompts","getOptions","assessPrerequisitesBatch","transformer","assessPrerequisites","error: unknown"],"sources":["../src/controller/index.ts","../src/index.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport { execSync } from 'node:child_process';\nimport fs from 'node:fs/promises';\nimport path from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\nimport { select as list } from '@inquirer/prompts';\nimport Spinnies from 'spinnies';\n\nimport { CONSOLE_ICONS } from '../constants';\nimport transformer from '../transforms/list-item/transformer';\nimport {\n assessPrerequisites,\n assessPrerequisitesBatch,\n findPackages,\n findProjectRoot,\n getOptions,\n loadTransformModules,\n logToInquirer,\n runTransformPrompts,\n validateClaudeConfig,\n} from './helpers';\n\nlet isDebug = false;\nconst currentFilePath = fileURLToPath(import.meta.url);\nconst currentDirPath = path.dirname(currentFilePath);\n\nconst resetReportFile = async (reportPath: string) => {\n try {\n await fs.access(reportPath);\n await fs.rm(reportPath);\n console.debug(\n `${CONSOLE_ICONS.info} Removed existing report file${isDebug ? `: ${reportPath}` : '.'}`,\n );\n } catch {\n console.debug(\n `${CONSOLE_ICONS.info} No existing report file to remove${isDebug ? `: ${reportPath}` : '.'}`,\n );\n }\n};\n\nconst summariseReportFile = async (reportPath: string) => {\n try {\n const reportContent = await fs.readFile(reportPath, 'utf8');\n const lines = reportContent.split('\\n').filter(Boolean);\n if (lines.length) {\n console.debug(\n `\\n${CONSOLE_ICONS.warning} ${lines.length} manual review${lines.length > 1 ? 's are' : ' is'} required. See ${reportPath} for details.`,\n );\n } else {\n console.debug(\n `${CONSOLE_ICONS.info} Report file exists but is empty${isDebug ? `: ${reportPath}` : '.'}`,\n );\n }\n } catch {\n console.debug(`${CONSOLE_ICONS.info} No report file generated - no manual reviews needed`);\n }\n};\n\nconst log = (label: string, value?: string): void => {\n if (typeof logToInquirer === 'function') {\n logToInquirer(label, value || '');\n } else {\n console.info(label, value || '');\n }\n};\n\nasync function runCodemod(transformsDir?: string) {\n const args = process.argv.slice(2);\n const candidate = args[0];\n isDebug = args.includes('--debug');\n\n try {\n const resolvedTransformsDir =\n transformsDir ?? path.resolve(currentDirPath, '../dist/transforms');\n\n if (isDebug) {\n console.debug(\n `${CONSOLE_ICONS.info} Resolved transforms directory: ${resolvedTransformsDir}`,\n );\n }\n\n const { transformFiles: resolvedTransformNames } =\n await loadTransformModules(resolvedTransformsDir);\n if (resolvedTransformNames.length === 0) {\n throw new Error(\n `${CONSOLE_ICONS.error} No transform scripts found${isDebug ? ` in: ${resolvedTransformsDir}` : '.'}`,\n );\n }\n\n let transformFile: string;\n\n if (candidate && resolvedTransformNames.includes(candidate)) {\n log('Select codemod to run:', candidate);\n transformFile = candidate;\n } else {\n transformFile = await list({\n message: 'Select codemod to run:',\n choices: resolvedTransformNames.map((name: string) => ({ name, value: name })),\n });\n log('Selected codemod:', transformFile);\n }\n\n const codemodPath = path.resolve(resolvedTransformsDir, transformFile, 'transformer.js');\n if (isDebug) {\n console.debug(`${CONSOLE_ICONS.info} Resolved codemod path: ${codemodPath}`);\n }\n\n if (transformFile === 'list-item') {\n validateClaudeConfig();\n }\n\n const spinnies = new Spinnies();\n spinnies.add('loading-codemod', { text: `Loading codemod (${transformFile})...` });\n const root = findProjectRoot();\n const packages = await findPackages();\n const reportPath = path.resolve(root, 'codemod-report.txt');\n spinnies.succeed('loading-codemod', {\n text: `Successfully loaded codemod: \\x1b[2m${transformFile}\\x1b[0m`,\n });\n\n await resetReportFile(reportPath);\n\n const promptAnswers = await runTransformPrompts(codemodPath);\n\n const options = await getOptions({\n packages,\n root,\n transformFiles: resolvedTransformNames,\n preselectedTransformFile: transformFile,\n });\n\n // Handle ListItem transform differently as it uses Claude, not jscodeshift\n if (transformFile === 'list-item') {\n spinnies.add('prerequisite-check', { text: 'Checking prerequisites...' });\n // Fail-fast: check prerequisites for all target paths up-front\n const { allPassed, packageRootToTargets, failedPackageRoots } = assessPrerequisitesBatch(\n options.targetPaths,\n codemodPath,\n spinnies,\n );\n if (!allPassed) {\n spinnies.add('prerequisite-partial-failure', {\n text: 'One or more packages failed prerequisite checks - these targets will be skipped:',\n status: 'fail',\n });\n\n for (const failedRoot of failedPackageRoots) {\n const targets = packageRootToTargets.get(failedRoot) ?? [];\n // Filter out targets that belong to failed package roots\n options.targetPaths = options.targetPaths.filter(\n (targetPath) => !targets.includes(targetPath),\n );\n spinnies.add(`prerequisite-fail-${failedRoot}`, {\n text: `- \\x1b[2m\\x1b[31m${failedRoot}\\x1b[0m${targets.length > 0 && targets[0] !== failedRoot ? ` (targets: ${targets.join(', ')})` : ''}\\x1b[0m`,\n indent: 2,\n status: 'non-spinnable',\n });\n }\n }\n\n if (!options.targetPaths.length) {\n spinnies.add('no-valid-targets', {\n text: `${CONSOLE_ICONS.error} No valid target paths remaining after prerequisite checks - exiting.`,\n status: 'fail',\n });\n spinnies.stopAll('fail');\n return;\n }\n\n await transformer(options.targetPaths, options, isDebug);\n } else {\n // Button codemod doesn't use spinnies\n spinnies.stopAll('succeed');\n\n await Promise.all(\n options.targetPaths.map(async (targetPath) => {\n console.info(\n `${CONSOLE_ICONS.focus} \\x1b[1mProcessing:\\x1b[0m \\x1b[32m${targetPath}\\x1b[0m`,\n );\n\n // Check prerequisites for this target before running\n const ok = assessPrerequisites(targetPath, codemodPath);\n if (!ok) {\n return;\n }\n\n const answerArgs = Object.entries(promptAnswers).map(\n ([promptName, answerValue]) => `--${promptName}=${String(answerValue)}`,\n );\n\n const argsList = [\n '-t',\n codemodPath,\n targetPath,\n options.isDry ? '--dry' : '',\n options.isPrint ? '--print' : '',\n options.ignorePatterns\n ? options.ignorePatterns\n .split(',')\n .map((pattern) => `--ignore-pattern=${pattern.trim()}`)\n .join(' ')\n : '',\n options.useGitIgnore ? '--gitignore' : '',\n ...answerArgs,\n ].filter(Boolean);\n const command = `npx jscodeshift ${argsList.join(' ')}`;\n\n if (isDebug) {\n console.debug(`${CONSOLE_ICONS.info} Running: ${command}`);\n }\n\n return execSync(command, { stdio: 'inherit' });\n }),\n );\n }\n\n // Skip report summarisation for list item\n if (transformFile !== 'list-item') {\n await summariseReportFile(reportPath);\n }\n } catch (error: unknown) {\n if (error instanceof Error) {\n console.error(`${CONSOLE_ICONS.error} Error running ${candidate} codemod:`, error.message);\n } else {\n console.error(`${CONSOLE_ICONS.error} Error running ${candidate} codemod:`, error);\n }\n if (process.env.NODE_ENV !== 'test') {\n process.exit(1);\n }\n }\n}\n\nexport { runCodemod };\n","#!/usr/bin/env node\nimport { runCodemod } from './controller';\n\nvoid runCodemod();\n"],"mappings":";;;;;;;;;;;;;;;AAwBA,IAAI,UAAU;AACd,MAAM,4FAAgD;AACtD,MAAM,iBAAiBA,kBAAK,QAAQ,gBAAgB;AAEpD,MAAM,kBAAkB,OAAO,eAAuB;AACpD,KAAI;AACF,QAAMC,yBAAG,OAAO,WAAW;AAC3B,QAAMA,yBAAG,GAAG,WAAW;AACvB,UAAQ,MACN,GAAGC,gCAAc,KAAK,+BAA+B,UAAU,KAAK,eAAe,MACpF;SACK;AACN,UAAQ,MACN,GAAGA,gCAAc,KAAK,oCAAoC,UAAU,KAAK,eAAe,MACzF;;;AAIL,MAAM,sBAAsB,OAAO,eAAuB;AACxD,KAAI;EAEF,MAAM,SADgB,MAAMD,yBAAG,SAAS,YAAY,OAAO,EAC/B,MAAM,KAAK,CAAC,OAAO,QAAQ;AACvD,MAAI,MAAM,OACR,SAAQ,MACN,KAAKC,gCAAc,QAAQ,IAAI,MAAM,OAAO,gBAAgB,MAAM,SAAS,IAAI,UAAU,MAAM,iBAAiB,WAAW,eAC5H;MAED,SAAQ,MACN,GAAGA,gCAAc,KAAK,kCAAkC,UAAU,KAAK,eAAe,MACvF;SAEG;AACN,UAAQ,MAAM,GAAGA,gCAAc,KAAK,sDAAsD;;;AAI9F,MAAM,OAAO,OAAe,UAAyB;AACnD,KAAI,OAAOC,kCAAkB,WAC3B,+BAAc,OAAO,SAAS,GAAG;KAEjC,SAAQ,KAAK,OAAO,SAAS,GAAG;;AAIpC,eAAe,WAAW,eAAwB;CAChD,MAAM,OAAO,QAAQ,KAAK,MAAM,EAAE;CAClC,MAAM,YAAY,KAAK;AACvB,WAAU,KAAK,SAAS,UAAU;AAElC,KAAI;EACF,MAAM,wBACJ,iBAAiBH,kBAAK,QAAQ,gBAAgB,qBAAqB;AAErE,MAAI,QACF,SAAQ,MACN,GAAGE,gCAAc,KAAK,kCAAkC,wBACzD;EAGH,MAAM,EAAE,gBAAgB,2BACtB,MAAME,6CAAqB,sBAAsB;AACnD,MAAI,uBAAuB,WAAW,EACpC,OAAM,IAAI,MACR,GAAGF,gCAAc,MAAM,6BAA6B,UAAU,QAAQ,0BAA0B,MACjG;EAGH,IAAIG;AAEJ,MAAI,aAAa,uBAAuB,SAAS,UAAU,EAAE;AAC3D,OAAI,0BAA0B,UAAU;AACxC,mBAAgB;SACX;AACL,mBAAgB,qCAAW;IACzB,SAAS;IACT,SAAS,uBAAuB,KAAK,UAAkB;KAAE;KAAM,OAAO;KAAM,EAAE;IAC/E,CAAC;AACF,OAAI,qBAAqB,cAAc;;EAGzC,MAAM,cAAcL,kBAAK,QAAQ,uBAAuB,eAAe,iBAAiB;AACxF,MAAI,QACF,SAAQ,MAAM,GAAGE,gCAAc,KAAK,0BAA0B,cAAc;AAG9E,MAAI,kBAAkB,YACpB,uCAAsB;EAGxB,MAAMI,aAAW,IAAIC,kBAAU;AAC/B,aAAS,IAAI,mBAAmB,EAAE,MAAM,oBAAoB,cAAc,OAAO,CAAC;EAClF,MAAM,OAAOC,iCAAiB;EAC9B,MAAM,WAAW,MAAMC,8BAAc;EACrC,MAAM,aAAaT,kBAAK,QAAQ,MAAM,qBAAqB;AAC3D,aAAS,QAAQ,mBAAmB,EAClC,MAAM,uCAAuC,cAAc,UAC5D,CAAC;AAEF,QAAM,gBAAgB,WAAW;EAEjC,MAAM,gBAAgB,MAAMU,oCAAoB,YAAY;EAE5D,MAAM,UAAU,MAAMC,mCAAW;GAC/B;GACA;GACA,gBAAgB;GAChB,0BAA0B;GAC3B,CAAC;AAGF,MAAI,kBAAkB,aAAa;AACjC,cAAS,IAAI,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;GAEzE,MAAM,EAAE,WAAW,sBAAsB,uBAAuBC,yCAC9D,QAAQ,aACR,aACAN,WACD;AACD,OAAI,CAAC,WAAW;AACd,eAAS,IAAI,gCAAgC;KAC3C,MAAM;KACN,QAAQ;KACT,CAAC;AAEF,SAAK,MAAM,cAAc,oBAAoB;KAC3C,MAAM,UAAU,qBAAqB,IAAI,WAAW,IAAI,EAAE;AAE1D,aAAQ,cAAc,QAAQ,YAAY,QACvC,eAAe,CAAC,QAAQ,SAAS,WAAW,CAC9C;AACD,gBAAS,IAAI,qBAAqB,cAAc;MAC9C,MAAM,oBAAoB,WAAW,SAAS,QAAQ,SAAS,KAAK,QAAQ,OAAO,aAAa,cAAc,QAAQ,KAAK,KAAK,CAAC,KAAK,GAAG;MACzI,QAAQ;MACR,QAAQ;MACT,CAAC;;;AAIN,OAAI,CAAC,QAAQ,YAAY,QAAQ;AAC/B,eAAS,IAAI,oBAAoB;KAC/B,MAAM,GAAGJ,gCAAc,MAAM;KAC7B,QAAQ;KACT,CAAC;AACF,eAAS,QAAQ,OAAO;AACxB;;AAGF,SAAMW,wCAAY,QAAQ,aAAa,SAAS,QAAQ;SACnD;AAEL,cAAS,QAAQ,UAAU;AAE3B,SAAM,QAAQ,IACZ,QAAQ,YAAY,IAAI,OAAO,eAAe;AAC5C,YAAQ,KACN,GAAGX,gCAAc,MAAM,qCAAqC,WAAW,SACxE;AAID,QAAI,CADOY,oCAAoB,YAAY,YAAY,CAErD;IAGF,MAAM,aAAa,OAAO,QAAQ,cAAc,CAAC,KAC9C,CAAC,YAAY,iBAAiB,KAAK,WAAW,GAAG,OAAO,YAAY,GACtE;IAiBD,MAAM,UAAU,mBAfC;KACf;KACA;KACA;KACA,QAAQ,QAAQ,UAAU;KAC1B,QAAQ,UAAU,YAAY;KAC9B,QAAQ,iBACJ,QAAQ,eACL,MAAM,IAAI,CACV,KAAK,YAAY,oBAAoB,QAAQ,MAAM,GAAG,CACtD,KAAK,IAAI,GACZ;KACJ,QAAQ,eAAe,gBAAgB;KACvC,GAAG;KACJ,CAAC,OAAO,QAAQ,CAC2B,KAAK,IAAI;AAErD,QAAI,QACF,SAAQ,MAAM,GAAGZ,gCAAc,KAAK,YAAY,UAAU;AAG5D,4CAAgB,SAAS,EAAE,OAAO,WAAW,CAAC;KAC9C,CACH;;AAIH,MAAI,kBAAkB,YACpB,OAAM,oBAAoB,WAAW;UAEhCa,OAAgB;AACvB,MAAI,iBAAiB,MACnB,SAAQ,MAAM,GAAGb,gCAAc,MAAM,iBAAiB,UAAU,YAAY,MAAM,QAAQ;MAE1F,SAAQ,MAAM,GAAGA,gCAAc,MAAM,iBAAiB,UAAU,YAAY,MAAM;AAEpF,MAAI,QAAQ,IAAI,aAAa,OAC3B,SAAQ,KAAK,EAAE;;;;;;AClOhB,YAAY"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["path","fs","CONSOLE_ICONS","logToInquirer","findProjectRoot","findPackages","loadTransformModules","transformFile: string","spinnies","Spinnies","runTransformPrompts","getOptions","assessPrerequisitesBatch","transformer","assessPrerequisites","error: unknown"],"sources":["../src/controller/index.ts","../src/index.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport { execSync } from 'node:child_process';\nimport fs from 'node:fs/promises';\nimport path from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\nimport { select as list } from '@inquirer/prompts';\nimport Spinnies from 'spinnies';\n\nimport { CONSOLE_ICONS } from '../constants';\nimport transformer from '../transforms/list-item/transformer';\nimport {\n assessPrerequisites,\n assessPrerequisitesBatch,\n findPackages,\n findProjectRoot,\n getOptions,\n loadTransformModules,\n logToInquirer,\n runTransformPrompts,\n validateClaudeConfig,\n} from './helpers';\n\nlet isDebug = false;\nconst currentFilePath = fileURLToPath(import.meta.url);\nconst currentDirPath = path.dirname(currentFilePath);\n\nconst resetReportFile = async (reportPath: string) => {\n try {\n await fs.access(reportPath);\n await fs.rm(reportPath);\n console.debug(\n `${CONSOLE_ICONS.info} Removed existing report file${isDebug ? `: ${reportPath}` : '.'}`,\n );\n } catch {\n console.debug(\n `${CONSOLE_ICONS.info} No existing report file to remove${isDebug ? `: ${reportPath}` : '.'}`,\n );\n }\n};\n\nconst summariseReportFile = async (reportPath: string) => {\n try {\n const reportContent = await fs.readFile(reportPath, 'utf8');\n const lines = reportContent.split('\\n').filter(Boolean);\n if (lines.length) {\n console.debug(\n `\\n${CONSOLE_ICONS.warning} ${lines.length} manual review${lines.length > 1 ? 's are' : ' is'} required. See ${reportPath} for details.`,\n );\n } else {\n console.debug(\n `${CONSOLE_ICONS.info} Report file exists but is empty${isDebug ? `: ${reportPath}` : '.'}`,\n );\n }\n } catch {\n console.debug(`${CONSOLE_ICONS.info} No report file generated - no manual reviews needed`);\n }\n};\n\nconst log = (label: string, value?: string): void => {\n if (typeof logToInquirer === 'function') {\n logToInquirer(label, value || '');\n } else {\n console.info(label, value || '');\n }\n};\n\nasync function runCodemod(transformsDir?: string) {\n const args = process.argv.slice(2);\n const candidate = args[0];\n isDebug = args.includes('--debug');\n\n try {\n const root = findProjectRoot();\n const packagesPromise = findPackages();\n\n const resolvedTransformsDir =\n transformsDir ?? path.resolve(currentDirPath, '../dist/transforms');\n\n if (isDebug) {\n console.debug(\n `${CONSOLE_ICONS.info} Resolved transforms directory: ${resolvedTransformsDir}`,\n );\n }\n\n const { transformFiles: resolvedTransformNames } =\n await loadTransformModules(resolvedTransformsDir);\n if (resolvedTransformNames.length === 0) {\n throw new Error(\n `${CONSOLE_ICONS.error} No transform scripts found${isDebug ? ` in: ${resolvedTransformsDir}` : '.'}`,\n );\n }\n\n let transformFile: string;\n\n if (candidate && resolvedTransformNames.includes(candidate)) {\n log('Select codemod to run:', candidate);\n transformFile = candidate;\n } else {\n transformFile = await list({\n message: 'Select codemod to run:',\n choices: resolvedTransformNames.map((name: string) => ({ name, value: name })),\n });\n log('Selected codemod:', transformFile);\n }\n\n const codemodPath = path.resolve(resolvedTransformsDir, transformFile, 'transformer.js');\n if (isDebug) {\n console.debug(`${CONSOLE_ICONS.info} Resolved codemod path: ${codemodPath}`);\n }\n\n if (transformFile === 'list-item') {\n validateClaudeConfig();\n }\n\n const spinnies = new Spinnies();\n spinnies.add('loading-codemod', { text: `Loading codemod (${transformFile})...` });\n const packages = await packagesPromise;\n const reportPath = path.resolve(root, 'codemod-report.txt');\n spinnies.succeed('loading-codemod', {\n text: `Successfully loaded codemod: \\x1b[2m${transformFile}\\x1b[0m`,\n });\n\n await resetReportFile(reportPath);\n\n const promptAnswers = await runTransformPrompts(codemodPath);\n\n const options = await getOptions({\n packages,\n root,\n transformFiles: resolvedTransformNames,\n preselectedTransformFile: transformFile,\n });\n\n // Handle ListItem transform differently as it uses Claude, not jscodeshift\n if (transformFile === 'list-item') {\n spinnies.add('prerequisite-check', { text: 'Checking prerequisites...' });\n // Fail-fast: check prerequisites for all target paths up-front\n const { allPassed, packageRootToTargets, failedPackageRoots } = assessPrerequisitesBatch(\n options.targetPaths,\n codemodPath,\n spinnies,\n );\n if (!allPassed) {\n spinnies.add('prerequisite-partial-failure', {\n text: 'One or more packages failed prerequisite checks - these targets will be skipped:',\n status: 'fail',\n });\n\n for (const failedRoot of failedPackageRoots) {\n const targets = packageRootToTargets.get(failedRoot) ?? [];\n // Filter out targets that belong to failed package roots\n options.targetPaths = options.targetPaths.filter(\n (targetPath) => !targets.includes(targetPath),\n );\n spinnies.add(`prerequisite-fail-${failedRoot}`, {\n text: `- \\x1b[2m\\x1b[31m${failedRoot}\\x1b[0m${targets.length > 0 && targets[0] !== failedRoot ? ` (targets: ${targets.join(', ')})` : ''}\\x1b[0m`,\n indent: 2,\n status: 'non-spinnable',\n });\n }\n }\n\n if (!options.targetPaths.length) {\n spinnies.add('no-valid-targets', {\n text: `${CONSOLE_ICONS.error} No valid target paths remaining after prerequisite checks - exiting.`,\n status: 'fail',\n });\n spinnies.stopAll('fail');\n return;\n }\n\n await transformer(options.targetPaths, options, isDebug);\n } else {\n // Button codemod doesn't use spinnies\n spinnies.stopAll('succeed');\n\n await Promise.all(\n options.targetPaths.map(async (targetPath) => {\n console.info(\n `${CONSOLE_ICONS.focus} \\x1b[1mProcessing:\\x1b[0m \\x1b[32m${targetPath}\\x1b[0m`,\n );\n\n // Check prerequisites for this target before running\n const ok = assessPrerequisites(targetPath, codemodPath);\n if (!ok) {\n return;\n }\n\n const answerArgs = Object.entries(promptAnswers).map(\n ([promptName, answerValue]) => `--${promptName}=${String(answerValue)}`,\n );\n\n const argsList = [\n '-t',\n codemodPath,\n targetPath,\n options.isDry ? '--dry' : '',\n options.isPrint ? '--print' : '',\n options.ignorePatterns\n ? options.ignorePatterns\n .split(',')\n .map((pattern) => `--ignore-pattern=${pattern.trim()}`)\n .join(' ')\n : '',\n options.useGitIgnore ? '--gitignore' : '',\n ...answerArgs,\n ].filter(Boolean);\n const command = `npx jscodeshift ${argsList.join(' ')}`;\n\n if (isDebug) {\n console.debug(`${CONSOLE_ICONS.info} Running: ${command}`);\n }\n\n return execSync(command, { stdio: 'inherit' });\n }),\n );\n }\n\n // Skip report summarisation for list item\n if (transformFile !== 'list-item') {\n await summariseReportFile(reportPath);\n }\n } catch (error: unknown) {\n if (error instanceof Error) {\n console.error(`${CONSOLE_ICONS.error} Error running ${candidate} codemod:`, error.message);\n } else {\n console.error(`${CONSOLE_ICONS.error} Error running ${candidate} codemod:`, error);\n }\n if (process.env.NODE_ENV !== 'test') {\n process.exit(1);\n }\n }\n}\n\nexport { runCodemod };\n","#!/usr/bin/env node\nimport { runCodemod } from './controller';\n\nvoid runCodemod();\n"],"mappings":"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"}
|
|
@@ -26,7 +26,6 @@ function logStaticMessage(spinnies$1, message) {
|
|
|
26
26
|
|
|
27
27
|
//#endregion
|
|
28
28
|
//#region src/transforms/list-item/constants.ts
|
|
29
|
-
const CONCURRENCY_LIMIT = 10;
|
|
30
29
|
const DEPRECATED_COMPONENT_NAMES = [
|
|
31
30
|
"ActionOption",
|
|
32
31
|
"NavigationOption",
|
|
@@ -36,7 +35,6 @@ const DEPRECATED_COMPONENT_NAMES = [
|
|
|
36
35
|
"CheckboxOption",
|
|
37
36
|
"RadioOption"
|
|
38
37
|
];
|
|
39
|
-
const GREP_PATTERN = new RegExp(`import\\s*\\{[\\s\\S]*?(${DEPRECATED_COMPONENT_NAMES.join("|")})[\\s\\S]*?\\}\\s*from\\s*['"]@transferwise/components['"]`, "u");
|
|
40
38
|
const MIGRATION_RULES = `Migration rules:
|
|
41
39
|
# Legacy Component → ListItem Migration Guide
|
|
42
40
|
|
|
@@ -305,12 +303,10 @@ async function checkVPN(spinnies$1, baseUrl) {
|
|
|
305
303
|
spinnies$1.succeed("vpnCheck", { text: "Connected to VPN" });
|
|
306
304
|
break;
|
|
307
305
|
}
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
});
|
|
313
|
-
}
|
|
306
|
+
spinnies$1.update("vpnCheck", { text: "Please connect to VPN... retrying in 3s" });
|
|
307
|
+
await new Promise((response) => {
|
|
308
|
+
setTimeout(response, 3e3);
|
|
309
|
+
});
|
|
314
310
|
}
|
|
315
311
|
}
|
|
316
312
|
return true;
|
|
@@ -370,7 +366,7 @@ async function initiateClaudeSessionOptions(spinnies$1) {
|
|
|
370
366
|
}
|
|
371
367
|
return options;
|
|
372
368
|
}
|
|
373
|
-
async function queryClaude(directory, filePath, options, spinnies$1, isDebug = false) {
|
|
369
|
+
async function queryClaude(directory, filePath, options, codemodOptions, spinnies$1, isDebug = false) {
|
|
374
370
|
const startTime = Date.now();
|
|
375
371
|
const debugSpinnies = new spinnies.default();
|
|
376
372
|
const result = (0, __anthropic_ai_claude_agent_sdk.query)({
|
|
@@ -381,8 +377,10 @@ async function queryClaude(directory, filePath, options, spinnies$1, isDebug = f
|
|
|
381
377
|
case "assistant":
|
|
382
378
|
for (const msg of message.message.content) switch (msg.type) {
|
|
383
379
|
case "tool_use":
|
|
384
|
-
if (msg.name === "Read")
|
|
385
|
-
|
|
380
|
+
if (msg.name === "Read") {
|
|
381
|
+
spinnies$1.remove("placeholder");
|
|
382
|
+
spinnies$1.update(`file-${filePath}`, { text: `${formatPathOutput(directory, filePath, true)} - Reading...` });
|
|
383
|
+
} else if (msg.name === "Edit") spinnies$1.update(`file-${filePath}`, { text: `${formatPathOutput(directory, filePath, true)} - Migrating...` });
|
|
386
384
|
break;
|
|
387
385
|
case "text":
|
|
388
386
|
if (isDebug) logStaticMessage(debugSpinnies, `${require_constants.CONSOLE_ICONS.claude} ${formatClaudeResponseContent(msg.text)}`);
|
|
@@ -391,10 +389,10 @@ async function queryClaude(directory, filePath, options, spinnies$1, isDebug = f
|
|
|
391
389
|
}
|
|
392
390
|
break;
|
|
393
391
|
case "result":
|
|
394
|
-
if (message.subtype === "success") spinnies$1.succeed(`file-${filePath}`, { text:
|
|
392
|
+
if (message.subtype === "success") spinnies$1.succeed(`file-${filePath}`, { text: `${formatPathOutput(directory, filePath, true)} - Migrated in: ${generateElapsedTime(startTime)}` });
|
|
395
393
|
else {
|
|
396
394
|
logStaticMessage(debugSpinnies, `${require_constants.CONSOLE_ICONS.error} Claude encountered an error:`);
|
|
397
|
-
|
|
395
|
+
console.log(message);
|
|
398
396
|
spinnies$1.stopAll("fail");
|
|
399
397
|
debugSpinnies.stopAll("fail");
|
|
400
398
|
}
|
|
@@ -411,67 +409,47 @@ const transformer = async (targetPaths, codemodOptions, isDebug = false) => {
|
|
|
411
409
|
const queryOptions = await initiateClaudeSessionOptions(spinnies$1);
|
|
412
410
|
spinnies$1.remove("placeholder");
|
|
413
411
|
spinnies$1.add("analysing", { text: "Analysing targetted paths - this may take a while..." });
|
|
414
|
-
const
|
|
415
|
-
const processFile = async (directory, filePath) => {
|
|
416
|
-
spinnies$1.remove("placeholder");
|
|
417
|
-
spinnies$1.add(`file-${filePath}`, {
|
|
418
|
-
indent: 4,
|
|
419
|
-
text: `\x1b[2m${formatPathOutput(directory, filePath)} - Parsing...\x1b[0m`
|
|
420
|
-
});
|
|
421
|
-
const originalFileContent = (0, node_fs.readFileSync)(filePath, "utf-8");
|
|
422
|
-
spinnies$1.update(`file-${filePath}`, {
|
|
423
|
-
indent: 4,
|
|
424
|
-
text: `\x1b[2m${formatPathOutput(directory, filePath)} - Processing...\x1b[0m`
|
|
425
|
-
});
|
|
426
|
-
await queryClaude(directory, filePath, queryOptions, spinnies$1, isDebug);
|
|
427
|
-
if (codemodOptions.isPrint) {
|
|
428
|
-
const newFileContent = (0, node_fs.readFileSync)(filePath, "utf-8");
|
|
429
|
-
logStaticMessage(spinnies$1, `Diff for ${formatPathOutput(directory, filePath, true)}:`);
|
|
430
|
-
logStaticMessage(spinnies$1, generateDiff(originalFileContent, newFileContent));
|
|
431
|
-
}
|
|
432
|
-
};
|
|
433
|
-
const processDirectory = async (directory) => {
|
|
412
|
+
for (const directory of targetPaths) {
|
|
434
413
|
spinnies$1.add(`directory-${directory}`, {
|
|
435
414
|
indent: 2,
|
|
436
|
-
text: `${formatPathOutput(directory)} - Processing
|
|
415
|
+
text: `${formatPathOutput(directory)} - Processing directory`
|
|
437
416
|
});
|
|
438
417
|
const allTsxFiles = (0, node_child_process.execSync)(`find "${directory}" -name "*.tsx" -type f`, { encoding: "utf-8" }).trim().split("\n").filter(Boolean);
|
|
439
|
-
const
|
|
418
|
+
const grepPattern = /* @__PURE__ */ new RegExp(`import\\s*\\{[\\s\\S]*?(${DEPRECATED_COMPONENT_NAMES.join("|")})[\\s\\S]*?\\}\\s*from\\s*['"]@transferwise/components['"]`);
|
|
440
419
|
const matchingFilePaths = allTsxFiles.filter((filePath) => {
|
|
441
|
-
|
|
442
|
-
|
|
420
|
+
try {
|
|
421
|
+
const content = (0, node_fs.readFileSync)(filePath, "utf-8");
|
|
422
|
+
return grepPattern.test(content);
|
|
423
|
+
} catch {
|
|
424
|
+
return false;
|
|
425
|
+
}
|
|
443
426
|
});
|
|
444
427
|
spinnies$1.update(`directory-${directory}`, { text: `${formatPathOutput(directory)} - Found \x1b[32m${matchingFilePaths.length}\x1b[0m \x1b[2m*.tsx\x1b[0m file(s) needing migration` });
|
|
445
|
-
const
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
completedFilesInDirectory.count += 1;
|
|
450
|
-
spinnies$1.update(`directory-${directory}`, { text: `${formatPathOutput(directory)} - Migrated \x1b[32m${completedFilesInDirectory.count}\x1b[0m/\x1b[32m${matchingFilePaths.length}\x1b[0m \x1b[2m*.tsx\x1b[0m files` });
|
|
428
|
+
for (const filePath of matchingFilePaths) {
|
|
429
|
+
spinnies$1.add(`file-${filePath}`, {
|
|
430
|
+
indent: 4,
|
|
431
|
+
text: `${formatPathOutput(directory, filePath, true)} - Parsing...`
|
|
451
432
|
});
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
spinnies$1.add("placeholder", {
|
|
457
|
-
text: "\x1B[2mThere are still additional files to be queued, waiting for other files to finish first...\x1B[0m",
|
|
458
|
-
indent: 4
|
|
459
|
-
});
|
|
460
|
-
await Promise.race(globalExecutingFiles);
|
|
461
|
-
} else break;
|
|
462
|
-
}
|
|
463
|
-
Promise.all(filesQueued).then(() => {
|
|
464
|
-
if (matchingFilePaths.length === 0) spinnies$1.succeed(`directory-${directory}`, { text: `${formatPathOutput(directory)}\x1b[2m - No files need migration\x1b[0m` });
|
|
465
|
-
else spinnies$1.succeed(`directory-${directory}`, {
|
|
466
|
-
indent: 2,
|
|
467
|
-
text: `${formatPathOutput(directory)} - Migrated \x1b[32m${matchingFilePaths.length}\x1b[0m file(s) successfully!`
|
|
433
|
+
const originalFileContent = (0, node_fs.readFileSync)(filePath, "utf-8");
|
|
434
|
+
spinnies$1.update(`file-${filePath}`, {
|
|
435
|
+
indent: 4,
|
|
436
|
+
text: `${formatPathOutput(directory, filePath, true)} - Processing...`
|
|
468
437
|
});
|
|
438
|
+
await queryClaude(directory, filePath, queryOptions, codemodOptions, spinnies$1, isDebug);
|
|
439
|
+
const newFileContent = (0, node_fs.readFileSync)(filePath, "utf-8");
|
|
440
|
+
if (codemodOptions.isPrint) {
|
|
441
|
+
logStaticMessage(spinnies$1, `Diff for ${formatPathOutput(directory, filePath, true)}:`);
|
|
442
|
+
logStaticMessage(spinnies$1, generateDiff(originalFileContent, newFileContent));
|
|
443
|
+
}
|
|
444
|
+
spinnies$1.remove("placeholder");
|
|
445
|
+
}
|
|
446
|
+
if (matchingFilePaths.length === 0) spinnies$1.succeed(`directory-${directory}`, { text: `${formatPathOutput(directory)} - No files need migration` });
|
|
447
|
+
else spinnies$1.succeed(`directory-${directory}`, {
|
|
448
|
+
indent: 2,
|
|
449
|
+
text: `${formatPathOutput(directory)} - Migrated \x1b[32m${matchingFilePaths.length}\x1b[0m file(s) successfully`
|
|
469
450
|
});
|
|
470
|
-
|
|
471
|
-
};
|
|
472
|
-
for (const directory of targetPaths) await processDirectory(directory);
|
|
473
|
-
await Promise.all(globalExecutingFiles);
|
|
474
|
-
spinnies$1.update("analysing", { text: "Successfully analysed all targetted paths" });
|
|
451
|
+
}
|
|
452
|
+
spinnies$1.succeed("analysing", { text: "Successfully analysed all targetted paths" });
|
|
475
453
|
spinnies$1.stopAll("succeed");
|
|
476
454
|
spinnies$1.add("done", {
|
|
477
455
|
text: `Finished migrating - elapsed time: \x1b[1m${generateElapsedTime(startTime)}\x1b[0m`,
|
|
@@ -487,4 +465,4 @@ Object.defineProperty(exports, 'transformer_default', {
|
|
|
487
465
|
return transformer_default;
|
|
488
466
|
}
|
|
489
467
|
});
|
|
490
|
-
//# sourceMappingURL=transformer-
|
|
468
|
+
//# sourceMappingURL=transformer-DoAMzZmy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transformer-DoAMzZmy.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 'NavigationOptionsList',\n 'Summary',\n 'SwitchOption',\n 'CheckboxOption',\n 'RadioOption',\n];\n\nconst MIGRATION_RULES = `Migration rules:\n# Legacy Component → ListItem Migration Guide\n\n## Universal Rules\n\n1. \\`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\n## DefinitionList → Multiple ListItem\n\n- Array → individual \\`ListItem\\`s\n- \\`value\\` → \\`subtitle\\`\n- \\`key\\` → React \\`key\\` prop\n- Action type: \"Edit\"/\"Update\"/\"View\" → \\`ListItem.Button priority=\"secondary-neutral\"\\`, \"Change\"/\"Password\" → \\`ListItem.Navigation\\`, \"Copy\" → \\`ListItem.IconButton\\`\n\n\\`\\`\\`tsx\n<DefinitionList definitions={[\n {title:'T1', value:'V1', key:'k1'},\n {title:'T2', value:'V2', key:'k2', action:{label:'Edit', onClick:fn}}\n]} />\n→\n\n <ListItem key=\"k1\" title=\"T1\" subtitle=\"V1\" />\n <ListItem key=\"k2\" title=\"T2\" subtitle=\"V2\" control={<ListItem.Button priority=\"secondary-neutral\" onClick={fn}>Edit</ListItem.Button>} />\n\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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"}
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"transformer-BHViIQiv.js","names":["https","spinnies","Spinnies","CONSOLE_ICONS","spinnies","Spinnies","globalExecutingFiles: Promise<void>[]","filesQueued: Promise<void>[]"],"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 CONCURRENCY_LIMIT = 10; // Set to 10 to avoid process memory issues\nconst DEPRECATED_COMPONENT_NAMES = [\n 'ActionOption',\n 'NavigationOption',\n 'NavigationOptionsList',\n 'Summary',\n 'SwitchOption',\n 'CheckboxOption',\n 'RadioOption',\n];\nexport const GREP_PATTERN = new RegExp(\n `import\\\\s*\\\\{[\\\\s\\\\S]*?(${DEPRECATED_COMPONENT_NAMES.join('|')})[\\\\s\\\\S]*?\\\\}\\\\s*from\\\\s*['\"]@transferwise/components['\"]`,\n 'u',\n);\n\nconst MIGRATION_RULES = `Migration rules:\n# Legacy Component → ListItem Migration Guide\n\n## Universal Rules\n\n1. \\`title\\` → \\`title\\` (direct)\n2. \\`content\\` or \\`description\\` → \\`subtitle\\`\n3. \\`disabled\\` stays on \\`ListItem\\` (not controls)\n4. Keep HTML attributes (\\`id\\`, \\`name\\`, \\`aria-label\\`), remove: \\`as\\`, \\`complex\\`, \\`showMediaAtAllSizes\\`, \\`showMediaCircle\\`, \\`isContainerAligned\\`\n5. In strings, don't convert \\`\\`to\\`'\\`or\\`\"\\`. Preserve what is there.\n\n---\n\n## ActionOption → ListItem.Button\n\n- \\`action\\` → Button children\n- \\`onClick\\` → Button \\`onClick\\`\n- Priority: default/\\`\"primary\"\\` → \\`\"primary\"\\`, \\`\"secondary\"\\` → \\`\"secondary-neutral\"\\`, \\`\"secondary-send\"\\` → \\`\"secondary\"\\`, \\`\"tertiary\"\\` → \\`\"tertiary\"\\`\n\n\\`\\`\\`tsx\n<ActionOption title=\"Title\" content=\"Text\" action=\"Click\" priority=\"secondary\" onClick={fn} />\n→\n<ListItem title=\"Title\" subtitle=\"Text\" control={<ListItem.Button priority=\"secondary-neutral\" onClick={fn}>Click</ListItem.Button>} />\n\\`\\`\\`\n\n---\n\n## CheckboxOption → ListItem.Checkbox\n\n- \\`onChange\\`: \\`(checked: boolean)\\` → \\`(event: ChangeEvent)\\` use \\`event.target.checked\\`\n- \\`name\\` move to Checkbox\n- Don't move \\`id\\` to Checkbox\n\n\\`\\`\\`tsx\n<CheckboxOption id=\"x\" name=\"y\" title=\"Title\" content=\"Text\" checked={v} onChange={(c) => set(c)} />\n→\n<ListItem title=\"Title\" subtitle=\"Text\" control={<ListItem.Checkbox name=\"y\" checked={v} onChange={(e) => set(e.target.checked)} />} />\n\\`\\`\\`\n\n---\n\n## RadioOption → ListItem.Radio\n\n- \\`name\\`, \\`value\\`, \\`checked\\`, \\`onChange\\` move to Radio\n- Don't move \\`id\\` to Radio\n\n\\`\\`\\`tsx\n<RadioOption id=\"x\" name=\"y\" value=\"v\" title=\"Title\" content=\"Text\" checked={v==='v'} onChange={set} />\n→\n<ListItem title=\"Title\" subtitle=\"Text\" control={<ListItem.Radio name=\"y\" value=\"v\" checked={v==='v'} onChange={set} />} />\n\\`\\`\\`\n\n---\n\n## SwitchOption → ListItem.Switch\n\n- \\`onChange\\` → \\`onClick\\`, toggle manually\n- \\`aria-label\\` moves to Switch\n\n\\`\\`\\`tsx\n<SwitchOption title=\"Title\" content=\"Text\" checked={v} aria-label=\"Toggle\" onChange={set} />\n→\n<ListItem title=\"Title\" subtitle=\"Text\" control={<ListItem.Switch checked={v} aria-label=\"Toggle\" onClick={() => set(!v)} />} />\n\\`\\`\\`\n\n---\n\n## NavigationOption → ListItem.Navigation\n\n- \\`onClick\\` or \\`href\\` move to Navigation\n\n\\`\\`\\`tsx\n<NavigationOption title=\"Title\" content=\"Text\" onClick={fn} />\n→\n<ListItem title=\"Title\" subtitle=\"Text\" control={<ListItem.Navigation onClick={fn} />} />\n\\`\\`\\`\n\n---\n\n## Option → ListItem\n\n- Wrap \\`media\\` in \\`ListItem.AvatarView\\`\n\n\\`\\`\\`tsx\n<Option media={<Icon />} title=\"Title\" />\n→\n<ListItem title=\"Title\" media={<ListItem.AvatarView><Icon /></ListItem.AvatarView>} />\n\\`\\`\\`\n\n---\n\n## Summary → ListItem\n\n**Basic:**\n\n- \\`icon\\` → wrap in \\`ListItem.AvatarView\\` with \\`size={32}\\` as \\`media\\`\n- Remove \\`size\\` from child \\`<Icon />\\`\n\n**Status:**\n\n- \\`Status.DONE\\` → \\`badge={{ status: 'positive' }}\\`\n- \\`Status.PENDING\\` → \\`badge={{ status: 'pending' }}\\`\n- \\`Status.NOT_DONE\\` → no badge\n\n**Action:**\n\n- \\`action.text\\` → \\`action.label\\` in \\`ListItem.AdditionalInfo\\` as \\`additionalInfo\\`\n\n**Info (requires state):**\n\n- \\`MODAL\\` → \\`ListItem.IconButton partiallyInteractive\\` + \\`<Modal>\\` in \\`control\\`\n- \\`POPOVER\\` → \\`<Popover>\\` wrapping \\`ListItem.IconButton partiallyInteractive\\` in \\`control\\`\n- Use \\`QuestionMarkCircle\\` icon (import from \\`@transferwise/icons\\`)\n\n\\`\\`\\`tsx\n// Basic\n<Summary title=\"T\" description=\"D\" icon={<Icon />} />\n→\n<ListItem title=\"T\" subtitle=\"D\" media={<ListItem.AvatarView size={32}><Icon /></ListItem.AvatarView>} />\n\n// Status\n<Summary title=\"T\" description=\"D\" icon={<Icon />} status={Status.DONE} />\n→\n<ListItem title=\"T\" subtitle=\"D\" media={<ListItem.AvatarView size={32} badge={{status:'positive'}}><Icon /></ListItem.AvatarView>} />\n\n// Action\n<Summary title=\"T\" description=\"D\" icon={<Icon />} action={{text:'Go', href:'/go'}} />\n→\n<ListItem title=\"T\" subtitle=\"D\" media={<ListItem.AvatarView size={32}><Icon /></ListItem.AvatarView>} additionalInfo={<ListItem.AdditionalInfo action={{label:'Go', href:'/go'}} />} />\n\n// Modal (add: const [open, setOpen] = useState(false))\n<Summary title=\"T\" description=\"D\" icon={<Icon />} info={{title:'Help', content:'Text', presentation:'MODAL', 'aria-label':'Info'}} />\n→\n<ListItem title=\"T\" subtitle=\"D\" media={<ListItem.AvatarView size={32}><Icon /></ListItem.AvatarView>} control={<ListItem.IconButton partiallyInteractive aria-label=\"Info\" onClick={()=>setOpen(!open)}><QuestionMarkCircle /><Modal open={open} title=\"Help\" body=\"Text\" onClose={()=>setOpen(false)} /></ListItem.IconButton>} />\n\n// Popover\n<Summary title=\"T\" description=\"D\" icon={<Icon />} info={{title:'Help', content:'Text', presentation:'POPOVER', 'aria-label':'Info'}} />\n→\n<ListItem title=\"T\" subtitle=\"D\" media={<ListItem.AvatarView size={32}><Icon /></ListItem.AvatarView>} control={<Popover title=\"Help\" content=\"Text\" onClose={()=>setOpen(false)}><ListItem.IconButton partiallyInteractive aria-label=\"Info\"><QuestionMarkCircle /></ListItem.IconButton></Popover>} />\n\\`\\`\\`\n\n---\n\n## DefinitionList → Multiple ListItem\n\n- Array → individual \\`ListItem\\`s\n- \\`value\\` → \\`subtitle\\`\n- \\`key\\` → React \\`key\\` prop\n- Action type: \"Edit\"/\"Update\"/\"View\" → \\`ListItem.Button priority=\"secondary-neutral\"\\`, \"Change\"/\"Password\" → \\`ListItem.Navigation\\`, \"Copy\" → \\`ListItem.IconButton\\`\n\n\\`\\`\\`tsx\n<DefinitionList definitions={[\n {title:'T1', value:'V1', key:'k1'},\n {title:'T2', value:'V2', key:'k2', action:{label:'Edit', onClick:fn}}\n]} />\n→\n\n <ListItem key=\"k1\" title=\"T1\" subtitle=\"V1\" />\n <ListItem key=\"k2\" title=\"T2\" subtitle=\"V2\" control={<ListItem.Button priority=\"secondary-neutral\" onClick={fn}>Edit</ListItem.Button>} />\n\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 { 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 // Countdown from 3s, continuously resetting\n for (let countdown = 5; countdown > 0; countdown -= 1) {\n spinnies.update('vpnCheck', {\n text: `Please connect to VPN... retrying in \\x1b[32m${countdown}\\x1b[0ms`,\n });\n await new Promise<void>((response) => {\n setTimeout(response, 1000);\n });\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 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.update(`file-${filePath}`, {\n text: `\\x1b[2m${formatPathOutput(directory, filePath)} - Reading...\\x1b[0m`,\n });\n } else if (msg.name === 'Edit') {\n spinnies.update(`file-${filePath}`, {\n text: `\\x1b[2m${formatPathOutput(directory, filePath)} - Migrating...\\x1b[0m`,\n });\n }\n break;\n case 'text':\n // Log Claude's text responses in debug mode\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: `\\x1b[0m${formatPathOutput(directory, filePath, true)}\\x1b[0m\\x1b[2m - Migrated in ${generateElapsedTime(startTime)}\\x1b[0m`,\n });\n } else {\n // Silence non-error errors (false positives)\n logStaticMessage(debugSpinnies, `${CONSOLE_ICONS.error} Claude encountered an error:`);\n logStaticMessage(debugSpinnies, JSON.stringify(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 { CONCURRENCY_LIMIT, GREP_PATTERN } 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 // Shared array to track all executing files across all directories\n const globalExecutingFiles: Promise<void>[] = [];\n\n // Helper to process a single file with shared concurrency limit\n const processFile = async (directory: string, filePath: string) => {\n spinnies.remove('placeholder');\n spinnies.add(`file-${filePath}`, {\n indent: 4,\n text: `\\x1b[2m${formatPathOutput(directory, filePath)} - Parsing...\\x1b[0m`,\n });\n\n // Store original file content for diffing later\n const originalFileContent = readFileSync(filePath, 'utf-8');\n spinnies.update(`file-${filePath}`, {\n indent: 4,\n text: `\\x1b[2m${formatPathOutput(directory, filePath)} - Processing...\\x1b[0m`,\n });\n\n await queryClaude(directory, filePath, queryOptions, spinnies, isDebug);\n\n // Create diff from originalFileContent to newFileContent\n if (codemodOptions.isPrint) {\n // Grab new file content after modification so we can create diff\n const newFileContent = readFileSync(filePath, 'utf-8');\n\n logStaticMessage(spinnies, `Diff for ${formatPathOutput(directory, filePath, true)}:`);\n logStaticMessage(spinnies, generateDiff(originalFileContent, newFileContent));\n }\n };\n\n // Process directory function that returns when all files are queued\n const processDirectory = async (directory: string): Promise<{ filesQueued: Promise<void>[] }> => {\n spinnies.add(`directory-${directory}`, {\n indent: 2,\n text: `${formatPathOutput(directory)} - Processing...`,\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 // Array to track promises for files queued in this directory\n const filesQueued: Promise<void>[] = [];\n\n // Filter files that match the pattern by reading and testing each file\n const matchingFilePaths = allTsxFiles.filter((filePath) => {\n const content = readFileSync(filePath, 'utf-8');\n return GREP_PATTERN.test(content);\n });\n\n 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 // Using object to avoid closure issues in loop, as they're passed by reference\n const completedFilesInDirectory = { count: 0 };\n\n // Process files with a shared concurrency limit between directories\n for (let i = 0; i < matchingFilePaths.length; i += 1) {\n const promise = processFile(directory, matchingFilePaths[i]).then(() => {\n // Remove from global executing files when done\n void globalExecutingFiles.splice(globalExecutingFiles.indexOf(promise), 1);\n completedFilesInDirectory.count += 1;\n spinnies.update(`directory-${directory}`, {\n text: `${formatPathOutput(directory)} - Migrated \\x1b[32m${completedFilesInDirectory.count}\\x1b[0m/\\x1b[32m${matchingFilePaths.length}\\x1b[0m \\x1b[2m*.tsx\\x1b[0m files`,\n });\n });\n\n globalExecutingFiles.push(promise);\n filesQueued.push(promise);\n\n const moreFilesToProcess = i < matchingFilePaths.length - 1;\n\n // If we're at concurrency limit...\n if (globalExecutingFiles.length >= CONCURRENCY_LIMIT) {\n // With more files to process, wait for any to finish\n if (moreFilesToProcess) {\n spinnies.add('placeholder', {\n // Wrap in italics\n text: '\\x1b[2mThere are still additional files to be queued, waiting for other files to finish first...\\x1b[0m',\n indent: 4,\n });\n await Promise.race(globalExecutingFiles);\n } else {\n // With no more files to add, break and start next directory\n break;\n }\n }\n }\n\n // Promise handler for this directory\n void Promise.all(filesQueued).then(() => {\n if (matchingFilePaths.length === 0) {\n spinnies.succeed(`directory-${directory}`, {\n text: `${formatPathOutput(directory)}\\x1b[2m - No files need migration\\x1b[0m`,\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 return { filesQueued };\n };\n\n // Process directories, starting the next one when the current has fewer than 10 files left\n for (const directory of targetPaths) {\n await processDirectory(directory);\n }\n\n // Wait for all files across all directories to complete migration\n await Promise.all(globalExecutingFiles);\n\n spinnies.update('analysing', {\n text: 'Successfully analysed all targetted paths',\n });\n spinnies.stopAll('succeed'); // Just in case any spinnies are left running\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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"}
|