@wise/wds-codemods 1.2.1-experimental-583aaa3 → 1.2.1-experimental-aa64212
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 +32 -27
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -26,16 +26,11 @@ const toMixpanelProperties = (properties) => ({
|
|
|
26
26
|
Transform: properties.transform,
|
|
27
27
|
Stage: properties.stage,
|
|
28
28
|
Engine: properties.engine,
|
|
29
|
-
"Is Monorepo": properties.isMonorepo,
|
|
30
29
|
Repository: properties.repository,
|
|
31
30
|
Timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
32
31
|
Error: properties.error,
|
|
33
|
-
Duration: properties.durationMs
|
|
32
|
+
Duration: properties.durationMs / 1e3
|
|
34
33
|
});
|
|
35
|
-
/**
|
|
36
|
-
* Event names follow Wise Mixpanel naming conventions:
|
|
37
|
-
* `[Subject] - [Action]` with past-tense verbs, capitalized words, delimited by " - ".
|
|
38
|
-
*/
|
|
39
34
|
const TRACKING_EVENT = "WDS - Codemod";
|
|
40
35
|
/**
|
|
41
36
|
* Fire-and-forget event tracking. Never throws — failures are silently ignored
|
|
@@ -50,19 +45,20 @@ const track = (event, properties, debug = false) => {
|
|
|
50
45
|
mp.track(event, { ...mapped });
|
|
51
46
|
} catch {}
|
|
52
47
|
};
|
|
53
|
-
/**
|
|
54
|
-
* Returns a flush promise you can await before process.exit.
|
|
55
|
-
* Mixpanel's Node client batches requests, so we give it a small window.
|
|
56
|
-
*/
|
|
57
|
-
const flushTracking = async () => new Promise((resolve) => {
|
|
58
|
-
setTimeout(resolve, 300);
|
|
59
|
-
});
|
|
60
48
|
//#endregion
|
|
61
49
|
//#region src/controller/helpers/claudePrereqs.ts
|
|
62
|
-
function handleClaudePrereqs({ options, codemodPath, spinners }) {
|
|
50
|
+
function handleClaudePrereqs({ options, codemodPath, spinners, transformFile, isDebug, repository, startTime }) {
|
|
63
51
|
spinners.add("prerequisite-check", { text: "Checking prerequisites..." });
|
|
64
52
|
const { allPassed, packageRootToTargets, failedPackageRoots } = assessPrerequisitesBatch(options.targetPaths, codemodPath, spinners);
|
|
65
53
|
if (!allPassed) {
|
|
54
|
+
track(TRACKING_EVENT, {
|
|
55
|
+
transform: transformFile,
|
|
56
|
+
stage: "Failed",
|
|
57
|
+
engine: "claude",
|
|
58
|
+
repository,
|
|
59
|
+
error: "Prerequisites check failed for some packages",
|
|
60
|
+
durationMs: Date.now() - startTime
|
|
61
|
+
}, isDebug);
|
|
66
62
|
spinners.add("prerequisite-partial-failure", {
|
|
67
63
|
text: "One or more packages failed prerequisite checks - these targets will be skipped:",
|
|
68
64
|
status: "fail"
|
|
@@ -78,6 +74,14 @@ function handleClaudePrereqs({ options, codemodPath, spinners }) {
|
|
|
78
74
|
}
|
|
79
75
|
}
|
|
80
76
|
if (!options.targetPaths.length) {
|
|
77
|
+
track(TRACKING_EVENT, {
|
|
78
|
+
transform: transformFile,
|
|
79
|
+
stage: "Failed",
|
|
80
|
+
engine: "claude",
|
|
81
|
+
repository,
|
|
82
|
+
error: "No valid target paths remaining after prerequisite checks",
|
|
83
|
+
durationMs: Date.now() - startTime
|
|
84
|
+
}, isDebug);
|
|
81
85
|
spinners.add("no-valid-targets", {
|
|
82
86
|
text: `${CONSOLE_ICONS.error} No valid target paths remaining after prerequisite checks - exiting.`,
|
|
83
87
|
status: "fail"
|
|
@@ -118,8 +122,11 @@ async function runCodemod(transformsDir) {
|
|
|
118
122
|
const candidate = args[0];
|
|
119
123
|
isDebug = args.includes("--debug");
|
|
120
124
|
const startTime = Date.now();
|
|
125
|
+
let root = "";
|
|
126
|
+
let repository = "unknown";
|
|
121
127
|
try {
|
|
122
|
-
|
|
128
|
+
root = findProjectRoot();
|
|
129
|
+
repository = path.basename(root);
|
|
123
130
|
const packagesPromise = findPackages();
|
|
124
131
|
const resolvedTransformsDir = transformsDir ?? path.resolve(currentDirPath, "../dist/transforms");
|
|
125
132
|
if (isDebug) console.debug(`${CONSOLE_ICONS.info} Resolved transforms directory: ${resolvedTransformsDir}`);
|
|
@@ -160,18 +167,17 @@ async function runCodemod(transformsDir) {
|
|
|
160
167
|
const trackingProps = {
|
|
161
168
|
transform: transformFile,
|
|
162
169
|
engine: codemodConfig?.type,
|
|
163
|
-
|
|
164
|
-
repository: path.basename(root)
|
|
170
|
+
repository
|
|
165
171
|
};
|
|
166
|
-
track(TRACKING_EVENT, {
|
|
167
|
-
...trackingProps,
|
|
168
|
-
stage: "Started"
|
|
169
|
-
});
|
|
170
172
|
if (codemodConfig?.type === "claude") {
|
|
171
173
|
handleClaudePrereqs({
|
|
172
174
|
options,
|
|
173
175
|
codemodPath,
|
|
174
|
-
spinners
|
|
176
|
+
spinners,
|
|
177
|
+
transformFile,
|
|
178
|
+
isDebug,
|
|
179
|
+
repository,
|
|
180
|
+
startTime
|
|
175
181
|
});
|
|
176
182
|
const transformer = (await import(codemodPath)).default;
|
|
177
183
|
await transformer(options.targetPaths, isDebug);
|
|
@@ -199,22 +205,21 @@ async function runCodemod(transformsDir) {
|
|
|
199
205
|
if (codemodConfig?.type === "jscodeshift") await summariseReportFile(reportPath);
|
|
200
206
|
track(TRACKING_EVENT, {
|
|
201
207
|
...trackingProps,
|
|
202
|
-
stage: "
|
|
208
|
+
stage: "Success",
|
|
203
209
|
durationMs: Date.now() - startTime
|
|
204
|
-
});
|
|
210
|
+
}, isDebug);
|
|
205
211
|
} catch (error) {
|
|
206
212
|
if (error instanceof Error) console.error(`${CONSOLE_ICONS.error} Error running ${candidate} codemod:`, error.message);
|
|
207
213
|
else console.error(`${CONSOLE_ICONS.error} Error running ${candidate} codemod:`, String(error));
|
|
208
214
|
track(TRACKING_EVENT, {
|
|
209
215
|
transform: candidate ?? "unknown",
|
|
210
216
|
stage: "Failed",
|
|
217
|
+
repository,
|
|
211
218
|
error: error instanceof Error ? error.message : String(error),
|
|
212
219
|
durationMs: Date.now() - startTime
|
|
213
|
-
});
|
|
214
|
-
await flushTracking();
|
|
220
|
+
}, isDebug);
|
|
215
221
|
if (process.env.NODE_ENV !== "test") process.exit(1);
|
|
216
222
|
}
|
|
217
|
-
await flushTracking();
|
|
218
223
|
}
|
|
219
224
|
//#endregion
|
|
220
225
|
//#region src/index.ts
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["list"],"sources":["../src/helpers/tracking/index.ts","../src/controller/helpers/claudePrereqs.ts","../src/controller/index.ts","../src/index.ts"],"sourcesContent":["import Mixpanel from 'mixpanel';\n\nimport { CONSOLE_ICONS } from '../../constants/common';\n\nconst MIXPANEL_TOKEN = '8ba4a7a5182f05e0a79ded57d5d2f051';\n\nlet client: Mixpanel.Mixpanel | null = null;\n\nconst getClient = (): Mixpanel.Mixpanel | null => {\n if (!MIXPANEL_TOKEN) return null;\n client ??= Mixpanel.init(MIXPANEL_TOKEN, {\n geolocate: false,\n });\n return client;\n};\n\n/**\n * Internal interface for passing tracking data from call sites.\n * Property names are mapped to Wise Mixpanel naming conventions before sending.\n *\n * @see https://transferwise.atlassian.net/wiki — Handbook for Mixpanel tracking\n */\nexport interface TrackingProperties {\n transform: string;\n stage: 'Started' | 'Finished' | 'Failed';\n engine?: 'jscodeshift' | 'claude';\n isMonorepo?: boolean;\n repository?: string;\n error?: string;\n durationMs?: number;\n}\n\n/**\n * Maps internal camelCase properties to Wise Mixpanel naming conventions:\n * - Spaces between words, each word capitalized\n * - Booleans prefixed with \"Is\"\n * - Counts use \"Number Of\"\n * - Duration in seconds (not ms)\n */\nconst toMixpanelProperties = (\n properties: TrackingProperties,\n): Record<string, string | number | boolean | undefined> => ({\n Transform: properties.transform,\n Stage: properties.stage,\n Engine: properties.engine,\n 'Is Monorepo': properties.isMonorepo,\n Repository: properties.repository,\n Timestamp: new Date().toISOString(),\n Error: properties.error,\n Duration: properties.durationMs != null ? properties.durationMs / 1000 : undefined,\n});\n\n/**\n * Event names follow Wise Mixpanel naming conventions:\n * `[Subject] - [Action]` with past-tense verbs, capitalized words, delimited by \" - \".\n */\nexport const TRACKING_EVENT = 'WDS - Codemod' as const;\n\n/**\n * Fire-and-forget event tracking. Never throws — failures are silently ignored\n * so tracking never interferes with the CLI experience.\n */\nexport const track = (event: string, properties: TrackingProperties, debug = false): void => {\n try {\n const mapped = toMixpanelProperties(properties);\n\n if (debug) {\n console.debug(`${CONSOLE_ICONS.info} [Mixpanel] ${event}`, JSON.stringify(mapped, null, 2));\n }\n\n const mp = getClient();\n if (!mp) return;\n\n mp.track(event, {\n ...mapped,\n });\n } catch {\n // Intentionally swallowed — tracking must never break the CLI\n }\n};\n\n/**\n * Returns a flush promise you can await before process.exit.\n * Mixpanel's Node client batches requests, so we give it a small window.\n */\nexport const flushTracking = async (): Promise<void> =>\n new Promise((resolve) => {\n // Mixpanel Node SDK sends HTTP requests async. Give it a brief grace period.\n setTimeout(resolve, 300);\n });\n","import type Spinnies from 'spinnies';\n\nimport { CONSOLE_ICONS } from '../../constants/common';\nimport type { CodemodOptions } from '../types';\nimport { assessPrerequisitesBatch } from './dependencyChecks';\n\ninterface ClaudePrereqHandlerOptions {\n options: CodemodOptions;\n spinners: Spinnies;\n codemodPath: string;\n}\n\nexport function handleClaudePrereqs({\n options,\n codemodPath,\n spinners,\n}: ClaudePrereqHandlerOptions) {\n spinners.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 spinners,\n );\n if (!allPassed) {\n spinners.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 // eslint-disable-next-line no-param-reassign\n options.targetPaths = options.targetPaths.filter(\n (targetPath) => !targets.includes(targetPath),\n );\n spinners.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 spinners.add('no-valid-targets', {\n text: `${CONSOLE_ICONS.error} No valid target paths remaining after prerequisite checks - exiting.`,\n status: 'fail',\n });\n spinners.stopAll('fail');\n spinners.checkIfActiveSpinners();\n }\n}\n","#!/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/common';\nimport { flushTracking, track, TRACKING_EVENT, type TrackingProperties } from '../helpers/tracking';\nimport {\n assessPrerequisites,\n findPackages,\n findProjectRoot,\n getCodemodConfig,\n getOptions,\n loadTransformModules,\n logToInquirer,\n runTransformPrompts,\n validateClaudeConfig,\n} from './helpers';\nimport { handleClaudePrereqs } from './helpers/claudePrereqs';\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 const startTime = Date.now();\n\n try {\n const root = findProjectRoot();\n const packagesPromise = findPackages();\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 const codemodConfig = getCodemodConfig(codemodPath);\n if (isDebug) {\n console.debug(`${CONSOLE_ICONS.info} Resolved codemod path: ${codemodPath}`);\n }\n\n if (codemodConfig?.type === 'claude') {\n validateClaudeConfig();\n }\n\n const spinners = new Spinnies();\n spinners.add('loading-codemod', { text: `Loading codemod (${transformFile})...` });\n const packages = await packagesPromise;\n const reportPath = path.resolve(root, 'codemod-report.txt');\n spinners.succeed('loading-codemod', {\n text: `Successfully loaded codemod: \\x1b[2m${transformFile}\\x1b[0m`,\n });\n\n if (codemodConfig?.type === 'jscodeshift') {\n await resetReportFile(reportPath);\n }\n\n const promptAnswers = await runTransformPrompts(codemodPath);\n const options = await getOptions({\n packages,\n root,\n transformFiles: resolvedTransformNames,\n preselectedTransformFile: transformFile,\n transformerType: codemodConfig?.type,\n });\n\n const trackingProps: Omit<TrackingProperties, 'stage'> = {\n transform: transformFile,\n engine: codemodConfig?.type,\n isMonorepo: args.includes('--monorepo'),\n repository: path.basename(root),\n };\n\n track(TRACKING_EVENT, { ...trackingProps, stage: 'Started' });\n\n // Handle Claude transforms differently, as they work on multiple targets at once\n if (codemodConfig?.type === 'claude') {\n handleClaudePrereqs({ options, codemodPath, spinners });\n\n // Dynamically import the transformer module\n const transformerModule = (await import(codemodPath)) as {\n default: (targetPaths: string[], isDebug?: boolean) => Promise<void>;\n };\n const transformer = transformerModule.default;\n await transformer(options.targetPaths, isDebug);\n } else {\n // Button codemod doesn't use spinnies\n spinners.stopAll('succeed');\n spinners.checkIfActiveSpinners();\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 if (codemodConfig?.type === 'jscodeshift') {\n await summariseReportFile(reportPath);\n }\n\n track(TRACKING_EVENT, {\n ...trackingProps,\n stage: 'Finished',\n durationMs: Date.now() - startTime,\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:`, String(error));\n }\n track(TRACKING_EVENT, {\n transform: candidate ?? 'unknown',\n stage: 'Failed',\n error: error instanceof Error ? error.message : String(error),\n durationMs: Date.now() - startTime,\n });\n\n await flushTracking();\n\n if (process.env.NODE_ENV !== 'test') {\n process.exit(1);\n }\n }\n\n await flushTracking();\n}\n\nexport { runCodemod };\n","#!/usr/bin/env node\nimport { runCodemod } from './controller';\n\nvoid runCodemod();\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["list"],"sources":["../src/helpers/tracking/index.ts","../src/controller/helpers/claudePrereqs.ts","../src/controller/index.ts","../src/index.ts"],"sourcesContent":["import Mixpanel from 'mixpanel';\n\nimport { CONSOLE_ICONS } from '../../constants/common';\n\nconst MIXPANEL_TOKEN = '8ba4a7a5182f05e0a79ded57d5d2f051';\n\nlet client: Mixpanel.Mixpanel | null = null;\n\nconst getClient = (): Mixpanel.Mixpanel | null => {\n if (!MIXPANEL_TOKEN) return null;\n client ??= Mixpanel.init(MIXPANEL_TOKEN, {\n geolocate: false,\n });\n return client;\n};\n\n/**\n * Internal interface for passing tracking data from call sites.\n * Property names are mapped to Wise Mixpanel naming conventions before sending.\n *\n * @see https://transferwise.atlassian.net/wiki — Handbook for Mixpanel tracking\n */\nexport interface TrackingProperties {\n transform: string;\n stage: 'Success' | 'Failed';\n engine?: 'jscodeshift' | 'claude';\n repository: string;\n error?: string;\n durationMs: number;\n}\n\n/**\n * Maps internal camelCase properties to Wise Mixpanel naming conventions:\n * - Spaces between words, each word capitalized\n * - Booleans prefixed with \"Is\"\n * - Counts use \"Number Of\"\n * - Duration in seconds (not ms)\n */\nconst toMixpanelProperties = (\n properties: TrackingProperties,\n): Record<string, string | number | boolean | undefined> => ({\n Transform: properties.transform,\n Stage: properties.stage,\n Engine: properties.engine,\n Repository: properties.repository,\n Timestamp: new Date().toISOString(),\n Error: properties.error,\n Duration: properties.durationMs / 1000,\n});\n\nexport const TRACKING_EVENT = 'WDS - Codemod' as const;\n\n/**\n * Fire-and-forget event tracking. Never throws — failures are silently ignored\n * so tracking never interferes with the CLI experience.\n */\nexport const track = (event: string, properties: TrackingProperties, debug = false): void => {\n try {\n const mapped = toMixpanelProperties(properties);\n\n if (debug) {\n console.debug(`${CONSOLE_ICONS.info} [Mixpanel] ${event}`, JSON.stringify(mapped, null, 2));\n }\n\n const mp = getClient();\n if (!mp) return;\n\n mp.track(event, {\n ...mapped,\n });\n } catch {\n // Intentionally swallowed — tracking must never break the CLI\n }\n};\n","import type Spinnies from 'spinnies';\n\nimport { CONSOLE_ICONS } from '../../constants/common';\nimport { track, TRACKING_EVENT } from '../../helpers/tracking';\nimport type { CodemodOptions } from '../types';\nimport { assessPrerequisitesBatch } from './dependencyChecks';\n\ninterface ClaudePrereqHandlerOptions {\n options: CodemodOptions;\n spinners: Spinnies;\n codemodPath: string;\n transformFile: string;\n isDebug: boolean;\n repository: string;\n startTime: number;\n}\n\nexport function handleClaudePrereqs({\n options,\n codemodPath,\n spinners,\n transformFile,\n isDebug,\n repository,\n startTime,\n}: ClaudePrereqHandlerOptions) {\n spinners.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 spinners,\n );\n if (!allPassed) {\n track(\n TRACKING_EVENT,\n {\n transform: transformFile,\n stage: 'Failed',\n engine: 'claude',\n repository,\n error: 'Prerequisites check failed for some packages',\n durationMs: Date.now() - startTime,\n },\n isDebug,\n );\n\n spinners.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 // eslint-disable-next-line no-param-reassign\n options.targetPaths = options.targetPaths.filter(\n (targetPath) => !targets.includes(targetPath),\n );\n spinners.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 track(\n TRACKING_EVENT,\n {\n transform: transformFile,\n stage: 'Failed',\n engine: 'claude',\n repository,\n error: 'No valid target paths remaining after prerequisite checks',\n durationMs: Date.now() - startTime,\n },\n isDebug,\n );\n\n spinners.add('no-valid-targets', {\n text: `${CONSOLE_ICONS.error} No valid target paths remaining after prerequisite checks - exiting.`,\n status: 'fail',\n });\n spinners.stopAll('fail');\n spinners.checkIfActiveSpinners();\n }\n}\n","#!/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/common';\nimport { track, TRACKING_EVENT, type TrackingProperties } from '../helpers/tracking';\nimport {\n assessPrerequisites,\n findPackages,\n findProjectRoot,\n getCodemodConfig,\n getOptions,\n loadTransformModules,\n logToInquirer,\n runTransformPrompts,\n validateClaudeConfig,\n} from './helpers';\nimport { handleClaudePrereqs } from './helpers/claudePrereqs';\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 const startTime = Date.now();\n let root = '';\n let repository = 'unknown';\n\n try {\n root = findProjectRoot();\n repository = path.basename(root);\n const packagesPromise = findPackages();\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 const codemodConfig = getCodemodConfig(codemodPath);\n if (isDebug) {\n console.debug(`${CONSOLE_ICONS.info} Resolved codemod path: ${codemodPath}`);\n }\n\n if (codemodConfig?.type === 'claude') {\n validateClaudeConfig();\n }\n\n const spinners = new Spinnies();\n spinners.add('loading-codemod', { text: `Loading codemod (${transformFile})...` });\n const packages = await packagesPromise;\n const reportPath = path.resolve(root, 'codemod-report.txt');\n spinners.succeed('loading-codemod', {\n text: `Successfully loaded codemod: \\x1b[2m${transformFile}\\x1b[0m`,\n });\n\n if (codemodConfig?.type === 'jscodeshift') {\n await resetReportFile(reportPath);\n }\n\n const promptAnswers = await runTransformPrompts(codemodPath);\n const options = await getOptions({\n packages,\n root,\n transformFiles: resolvedTransformNames,\n preselectedTransformFile: transformFile,\n transformerType: codemodConfig?.type,\n });\n\n const trackingProps: Omit<TrackingProperties, 'stage' | 'durationMs'> = {\n transform: transformFile,\n engine: codemodConfig?.type,\n repository,\n };\n\n // Handle Claude transforms differently, as they work on multiple targets at once\n if (codemodConfig?.type === 'claude') {\n handleClaudePrereqs({\n options,\n codemodPath,\n spinners,\n transformFile,\n isDebug,\n repository,\n startTime,\n });\n\n // Dynamically import the transformer module\n const transformerModule = (await import(codemodPath)) as {\n default: (targetPaths: string[], isDebug?: boolean) => Promise<void>;\n };\n const transformer = transformerModule.default;\n await transformer(options.targetPaths, isDebug);\n } else {\n // Button codemod doesn't use spinnies\n spinners.stopAll('succeed');\n spinners.checkIfActiveSpinners();\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 if (codemodConfig?.type === 'jscodeshift') {\n await summariseReportFile(reportPath);\n }\n\n track(\n TRACKING_EVENT,\n {\n ...trackingProps,\n stage: 'Success',\n durationMs: Date.now() - startTime,\n },\n isDebug,\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:`, String(error));\n }\n track(\n TRACKING_EVENT,\n {\n transform: candidate ?? 'unknown',\n stage: 'Failed',\n repository,\n error: error instanceof Error ? error.message : String(error),\n durationMs: Date.now() - startTime,\n },\n isDebug,\n );\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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"}
|