@wise/wds-codemods 1.2.0 → 1.2.1-experimental-18899d8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,20 +1,88 @@
1
1
  #!/usr/bin/env node
2
- const require_common = require('./common-DdEQmI2h.js');
3
- const require_helpers = require('./helpers-Cj5geKJl.js');
4
- let node_child_process = require("node:child_process");
5
- let node_fs_promises = require("node:fs/promises");
6
- node_fs_promises = require_common.__toESM(node_fs_promises);
7
- let node_path = require("node:path");
8
- node_path = require_common.__toESM(node_path);
9
- let node_url = require("node:url");
10
- let _inquirer_prompts = require("@inquirer/prompts");
11
- let spinnies = require("spinnies");
12
- spinnies = require_common.__toESM(spinnies);
13
-
2
+ import { a as getOptions, c as findPackages, i as loadTransformModules, l as findProjectRoot, n as runTransformPrompts, o as logToInquirer, s as assessPrerequisitesBatch, t as validateClaudeConfig, u as getCodemodConfig } from "./helpers-BrTOp8HX.js";
3
+ import { t as CONSOLE_ICONS } from "./common-nTdKnfMB.js";
4
+ import { execSync } from "node:child_process";
5
+ import fs from "node:fs/promises";
6
+ import path from "node:path";
7
+ import { fileURLToPath } from "node:url";
8
+ import { select } from "@inquirer/prompts";
9
+ import Spinnies from "spinnies";
10
+ import { createHash } from "node:crypto";
11
+ import { hostname, userInfo } from "node:os";
12
+ import Mixpanel from "mixpanel";
13
+ //#region src/helpers/tracking/index.ts
14
+ const MIXPANEL_TOKEN = "8ba4a7a5182f05e0a79ded57d5d2f051";
15
+ let client = null;
16
+ const getClient = () => {
17
+ client ??= Mixpanel.init(MIXPANEL_TOKEN, { geolocate: false });
18
+ return client;
19
+ };
20
+ /**
21
+ * Anonymous but stable user identifier derived from OS username + hostname.
22
+ * No PII is sent — only a SHA-256 hash.
23
+ */
24
+ const getDistinctId = () => {
25
+ const raw = `${userInfo().username}@${hostname()}`;
26
+ return createHash("sha256").update(raw).digest("hex").slice(0, 16);
27
+ };
28
+ /**
29
+ * Maps internal camelCase properties to Wise Mixpanel naming conventions:
30
+ * - Spaces between words, each word capitalized
31
+ * - Booleans prefixed with "Is"
32
+ * - Counts use "Number Of"
33
+ * - Duration in seconds (not ms)
34
+ */
35
+ const toMixpanelProperties = (properties) => ({
36
+ Transform: properties.transform,
37
+ Engine: properties.engine,
38
+ "Is Monorepo": properties.isMonorepo,
39
+ "Is Git Ignore Enabled": properties.useGitIgnore,
40
+ "Is Ignore Patterns Used": properties.hasIgnorePatterns,
41
+ "Selection Method": properties.selectionMethod,
42
+ "Number Of Target Paths": properties.targetPathCount,
43
+ "Is Debug": properties.isDebug,
44
+ Repository: properties.repository,
45
+ Timestamp: (/* @__PURE__ */ new Date()).toISOString(),
46
+ Error: properties.error,
47
+ Duration: properties.durationMs != null ? properties.durationMs / 1e3 : void 0
48
+ });
49
+ /**
50
+ * Event names follow Wise Mixpanel naming conventions:
51
+ * `[Subject] - [Action]` with past-tense verbs, capitalized words, delimited by " - ".
52
+ */
53
+ const TRACKING_EVENTS = {
54
+ STARTED: "WDS - Codemod - Started",
55
+ FINISHED: "WDS - Codemod - Finished",
56
+ FAILED: "WDS - Codemod - Failed"
57
+ };
58
+ /**
59
+ * Fire-and-forget event tracking. Never throws — failures are silently ignored
60
+ * so tracking never interferes with the CLI experience.
61
+ */
62
+ const track = (event, properties) => {
63
+ try {
64
+ const mp = getClient();
65
+ const mapped = toMixpanelProperties(properties);
66
+ if (properties.isDebug) console.debug(`${CONSOLE_ICONS.info} [Mixpanel] ${event}`, JSON.stringify(mapped, null, 2));
67
+ if (!mp) return;
68
+ mp.track(event, {
69
+ distinct_id: getDistinctId(),
70
+ ...mapped
71
+ });
72
+ } catch {}
73
+ };
74
+ /**
75
+ * Returns a flush promise you can await before process.exit.
76
+ * Mixpanel's Node client batches requests, so we give it a small window.
77
+ */
78
+ const flushTracking = async () => new Promise((resolve) => {
79
+ setTimeout(resolve, 300);
80
+ });
81
+ //#endregion
14
82
  //#region src/controller/helpers/claudePrereqs.ts
15
83
  function handleClaudePrereqs({ options, codemodPath, spinners }) {
16
84
  spinners.add("prerequisite-check", { text: "Checking prerequisites..." });
17
- const { allPassed, packageRootToTargets, failedPackageRoots } = require_helpers.assessPrerequisitesBatch(options.targetPaths, codemodPath, spinners);
85
+ const { allPassed, packageRootToTargets, failedPackageRoots } = assessPrerequisitesBatch(options.targetPaths, codemodPath, spinners);
18
86
  if (!allPassed) {
19
87
  spinners.add("prerequisite-partial-failure", {
20
88
  text: "One or more packages failed prerequisite checks - these targets will be skipped:",
@@ -32,58 +100,58 @@ function handleClaudePrereqs({ options, codemodPath, spinners }) {
32
100
  }
33
101
  if (!options.targetPaths.length) {
34
102
  spinners.add("no-valid-targets", {
35
- text: `${require_common.CONSOLE_ICONS.error} No valid target paths remaining after prerequisite checks - exiting.`,
103
+ text: `${CONSOLE_ICONS.error} No valid target paths remaining after prerequisite checks - exiting.`,
36
104
  status: "fail"
37
105
  });
38
106
  spinners.stopAll("fail");
39
107
  spinners.checkIfActiveSpinners();
40
108
  }
41
109
  }
42
-
43
110
  //#endregion
44
111
  //#region src/controller/index.ts
45
112
  let isDebug = false;
46
- const currentFilePath = (0, node_url.fileURLToPath)(require("url").pathToFileURL(__filename).href);
47
- const currentDirPath = node_path.default.dirname(currentFilePath);
113
+ const currentFilePath = fileURLToPath(import.meta.url);
114
+ const currentDirPath = path.dirname(currentFilePath);
48
115
  const resetReportFile = async (reportPath) => {
49
116
  try {
50
- await node_fs_promises.default.access(reportPath);
51
- await node_fs_promises.default.rm(reportPath);
52
- console.debug(`${require_common.CONSOLE_ICONS.info} Removed existing report file${isDebug ? `: ${reportPath}` : "."}`);
117
+ await fs.access(reportPath);
118
+ await fs.rm(reportPath);
119
+ console.debug(`${CONSOLE_ICONS.info} Removed existing report file${isDebug ? `: ${reportPath}` : "."}`);
53
120
  } catch {
54
- console.debug(`${require_common.CONSOLE_ICONS.info} No existing report file to remove${isDebug ? `: ${reportPath}` : "."}`);
121
+ console.debug(`${CONSOLE_ICONS.info} No existing report file to remove${isDebug ? `: ${reportPath}` : "."}`);
55
122
  }
56
123
  };
57
124
  const summariseReportFile = async (reportPath) => {
58
125
  try {
59
- const lines = (await node_fs_promises.default.readFile(reportPath, "utf8")).split("\n").filter(Boolean);
60
- if (lines.length) console.debug(`\n${require_common.CONSOLE_ICONS.warning} ${lines.length} manual review${lines.length > 1 ? "s are" : " is"} required. See ${reportPath} for details.`);
61
- else console.debug(`${require_common.CONSOLE_ICONS.info} Report file exists but is empty${isDebug ? `: ${reportPath}` : "."}`);
126
+ const lines = (await fs.readFile(reportPath, "utf8")).split("\n").filter(Boolean);
127
+ if (lines.length) console.debug(`\n${CONSOLE_ICONS.warning} ${lines.length} manual review${lines.length > 1 ? "s are" : " is"} required. See ${reportPath} for details.`);
128
+ else console.debug(`${CONSOLE_ICONS.info} Report file exists but is empty${isDebug ? `: ${reportPath}` : "."}`);
62
129
  } catch {
63
- console.debug(`${require_common.CONSOLE_ICONS.info} No report file generated - no manual reviews needed`);
130
+ console.debug(`${CONSOLE_ICONS.info} No report file generated - no manual reviews needed`);
64
131
  }
65
132
  };
66
133
  const log = (label, value) => {
67
- if (typeof require_helpers.logToInquirer === "function") require_helpers.logToInquirer(label, value || "");
134
+ if (typeof logToInquirer === "function") logToInquirer(label, value || "");
68
135
  else console.info(label, value || "");
69
136
  };
70
137
  async function runCodemod(transformsDir) {
71
138
  const args = process.argv.slice(2);
72
139
  const candidate = args[0];
73
140
  isDebug = args.includes("--debug");
141
+ const startTime = Date.now();
74
142
  try {
75
- const root = require_helpers.findProjectRoot();
76
- const packagesPromise = require_helpers.findPackages();
77
- const resolvedTransformsDir = transformsDir ?? node_path.default.resolve(currentDirPath, "../dist/transforms");
78
- if (isDebug) console.debug(`${require_common.CONSOLE_ICONS.info} Resolved transforms directory: ${resolvedTransformsDir}`);
79
- const { transformFiles: resolvedTransformNames } = await require_helpers.loadTransformModules_default(resolvedTransformsDir);
80
- if (resolvedTransformNames.length === 0) throw new Error(`${require_common.CONSOLE_ICONS.error} No transform scripts found${isDebug ? ` in: ${resolvedTransformsDir}` : "."}`);
143
+ const root = findProjectRoot();
144
+ const packagesPromise = findPackages();
145
+ const resolvedTransformsDir = transformsDir ?? path.resolve(currentDirPath, "../dist/transforms");
146
+ if (isDebug) console.debug(`${CONSOLE_ICONS.info} Resolved transforms directory: ${resolvedTransformsDir}`);
147
+ const { transformFiles: resolvedTransformNames } = await loadTransformModules(resolvedTransformsDir);
148
+ if (resolvedTransformNames.length === 0) throw new Error(`${CONSOLE_ICONS.error} No transform scripts found${isDebug ? ` in: ${resolvedTransformsDir}` : "."}`);
81
149
  let transformFile;
82
150
  if (candidate && resolvedTransformNames.includes(candidate)) {
83
151
  log("Select codemod to run:", candidate);
84
152
  transformFile = candidate;
85
153
  } else {
86
- transformFile = await (0, _inquirer_prompts.select)({
154
+ transformFile = await select({
87
155
  message: "Select codemod to run:",
88
156
  choices: resolvedTransformNames.map((name) => ({
89
157
  name,
@@ -92,24 +160,37 @@ async function runCodemod(transformsDir) {
92
160
  });
93
161
  log("Selected codemod:", transformFile);
94
162
  }
95
- const codemodPath = node_path.default.resolve(resolvedTransformsDir, transformFile, "transformer.js");
96
- const codemodConfig = require_helpers.getCodemodConfig(codemodPath);
97
- if (isDebug) console.debug(`${require_common.CONSOLE_ICONS.info} Resolved codemod path: ${codemodPath}`);
98
- if (codemodConfig?.type === "claude") require_helpers.validateClaudeConfig();
99
- const spinners = new spinnies.default();
163
+ const codemodPath = path.resolve(resolvedTransformsDir, transformFile, "transformer.js");
164
+ const codemodConfig = getCodemodConfig(codemodPath);
165
+ if (isDebug) console.debug(`${CONSOLE_ICONS.info} Resolved codemod path: ${codemodPath}`);
166
+ if (codemodConfig?.type === "claude") validateClaudeConfig();
167
+ const spinners = new Spinnies();
100
168
  spinners.add("loading-codemod", { text: `Loading codemod (${transformFile})...` });
101
169
  const packages = await packagesPromise;
102
- const reportPath = node_path.default.resolve(root, "codemod-report.txt");
170
+ const reportPath = path.resolve(root, "codemod-report.txt");
103
171
  spinners.succeed("loading-codemod", { text: `Successfully loaded codemod: \x1b[2m${transformFile}\x1b[0m` });
104
172
  if (codemodConfig?.type === "jscodeshift") await resetReportFile(reportPath);
105
- const promptAnswers = await require_helpers.runTransformPrompts(codemodPath);
106
- const options = await require_helpers.getOptions_default({
173
+ const promptAnswers = await runTransformPrompts(codemodPath);
174
+ const options = await getOptions({
107
175
  packages,
108
176
  root,
109
177
  transformFiles: resolvedTransformNames,
110
178
  preselectedTransformFile: transformFile,
111
179
  transformerType: codemodConfig?.type
112
180
  });
181
+ const selectionMethod = candidate && resolvedTransformNames.includes(candidate) ? "cli" : "interactive";
182
+ const trackingProps = {
183
+ transform: transformFile,
184
+ engine: codemodConfig?.type,
185
+ isMonorepo: args.includes("--monorepo"),
186
+ useGitIgnore: options.useGitIgnore,
187
+ hasIgnorePatterns: Boolean(options.ignorePatterns),
188
+ selectionMethod,
189
+ targetPathCount: options.targetPaths.length,
190
+ isDebug,
191
+ repository: path.basename(root)
192
+ };
193
+ track(TRACKING_EVENTS.STARTED, trackingProps);
113
194
  if (codemodConfig?.type === "claude") {
114
195
  handleClaudePrereqs({
115
196
  options,
@@ -122,8 +203,7 @@ async function runCodemod(transformsDir) {
122
203
  spinners.stopAll("succeed");
123
204
  spinners.checkIfActiveSpinners();
124
205
  await Promise.all(options.targetPaths.map(async (targetPath) => {
125
- console.info(`${require_common.CONSOLE_ICONS.focus} \x1b[1mProcessing:\x1b[0m \x1b[32m${targetPath}\x1b[0m`);
126
- if (!require_helpers.assessPrerequisites(targetPath, codemodPath)) return;
206
+ console.info(`${CONSOLE_ICONS.focus} \x1b[1mProcessing:\x1b[0m \x1b[32m${targetPath}\x1b[0m`);
127
207
  const answerArgs = Object.entries(promptAnswers).map(([promptName, answerValue]) => `--${promptName}=${String(answerValue)}`);
128
208
  const command = `npx jscodeshift ${[
129
209
  "-t",
@@ -135,21 +215,32 @@ async function runCodemod(transformsDir) {
135
215
  options.useGitIgnore ? "--gitignore" : "",
136
216
  ...answerArgs
137
217
  ].filter(Boolean).join(" ")}`;
138
- if (isDebug) console.debug(`${require_common.CONSOLE_ICONS.info} Running: ${command}`);
139
- return (0, node_child_process.execSync)(command, { stdio: "inherit" });
218
+ if (isDebug) console.debug(`${CONSOLE_ICONS.info} Running: ${command}`);
219
+ return execSync(command, { stdio: "inherit" });
140
220
  }));
141
221
  }
142
222
  if (codemodConfig?.type === "jscodeshift") await summariseReportFile(reportPath);
223
+ track(TRACKING_EVENTS.FINISHED, {
224
+ ...trackingProps,
225
+ durationMs: Date.now() - startTime
226
+ });
143
227
  } catch (error) {
144
- if (error instanceof Error) console.error(`${require_common.CONSOLE_ICONS.error} Error running ${candidate} codemod:`, error.message);
145
- else console.error(`${require_common.CONSOLE_ICONS.error} Error running ${candidate} codemod:`, String(error));
228
+ if (error instanceof Error) console.error(`${CONSOLE_ICONS.error} Error running ${candidate} codemod:`, error.message);
229
+ else console.error(`${CONSOLE_ICONS.error} Error running ${candidate} codemod:`, String(error));
230
+ track(TRACKING_EVENTS.FAILED, {
231
+ transform: candidate ?? "unknown",
232
+ error: error instanceof Error ? error.message : String(error),
233
+ durationMs: Date.now() - startTime
234
+ });
235
+ await flushTracking();
146
236
  if (process.env.NODE_ENV !== "test") process.exit(1);
147
237
  }
238
+ await flushTracking();
148
239
  }
149
-
150
240
  //#endregion
151
241
  //#region src/index.ts
152
242
  runCodemod();
153
-
154
243
  //#endregion
244
+ export {};
245
+
155
246
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["assessPrerequisitesBatch","CONSOLE_ICONS","path","fs","CONSOLE_ICONS","logToInquirer","findProjectRoot","findPackages","loadTransformModules","transformFile: string","getCodemodConfig","Spinnies","runTransformPrompts","getOptions","assessPrerequisites","error: unknown"],"sources":["../src/controller/helpers/claudePrereqs.ts","../src/controller/index.ts","../src/index.ts"],"sourcesContent":["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 {\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\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 // 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 } 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 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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"}
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 { createHash } from 'node:crypto';\nimport { hostname, userInfo } from 'node:os';\n\nimport 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 * Anonymous but stable user identifier derived from OS username + hostname.\n * No PII is sent — only a SHA-256 hash.\n */\nconst getDistinctId = (): string => {\n const raw = `${userInfo().username}@${hostname()}`;\n return createHash('sha256').update(raw).digest('hex').slice(0, 16);\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 engine?: 'jscodeshift' | 'claude';\n isMonorepo?: boolean;\n useGitIgnore?: boolean;\n hasIgnorePatterns?: boolean;\n selectionMethod?: 'cli' | 'interactive';\n targetPathCount?: number;\n isDebug?: 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 Engine: properties.engine,\n 'Is Monorepo': properties.isMonorepo,\n 'Is Git Ignore Enabled': properties.useGitIgnore,\n 'Is Ignore Patterns Used': properties.hasIgnorePatterns,\n 'Selection Method': properties.selectionMethod,\n 'Number Of Target Paths': properties.targetPathCount,\n 'Is Debug': properties.isDebug,\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_EVENTS = {\n STARTED: 'WDS - Codemod - Started',\n FINISHED: 'WDS - Codemod - Finished',\n FAILED: 'WDS - Codemod - Failed',\n} 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): void => {\n try {\n const mp = getClient();\n const mapped = toMixpanelProperties(properties);\n\n if (properties.isDebug) {\n console.debug(`${CONSOLE_ICONS.info} [Mixpanel] ${event}`, JSON.stringify(mapped, null, 2));\n }\n\n if (!mp) return;\n\n mp.track(event, {\n distinct_id: getDistinctId(),\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 {\n flushTracking,\n track,\n TRACKING_EVENTS,\n type TrackingProperties,\n} 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 selectionMethod: TrackingProperties['selectionMethod'] =\n candidate && resolvedTransformNames.includes(candidate) ? 'cli' : 'interactive';\n\n const trackingProps: TrackingProperties = {\n transform: transformFile,\n engine: codemodConfig?.type,\n isMonorepo: args.includes('--monorepo'),\n useGitIgnore: options.useGitIgnore,\n hasIgnorePatterns: Boolean(options.ignorePatterns),\n selectionMethod,\n targetPathCount: options.targetPaths.length,\n isDebug,\n repository: path.basename(root),\n };\n\n track(TRACKING_EVENTS.STARTED, trackingProps);\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 // TODO: re-enable after testing tracking\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_EVENTS.FINISHED, {\n ...trackingProps,\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_EVENTS.FAILED, {\n transform: candidate ?? 'unknown',\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,6 +1,4 @@
1
- Object.defineProperty(exports, '__esModule', { value: true });
2
- const require_helpers = require('../../helpers-Cj5geKJl.js');
3
-
1
+ import { r as reportManualReview } from "../../helpers-BrTOp8HX.js";
4
2
  //#region src/helpers/jscodeshift/addImport.ts
5
3
  /**
6
4
  * Adds a named import if it doesn't already exist.
@@ -22,8 +20,6 @@ function addImport(root, sourceValue, importName, j) {
22
20
  }
23
21
  }
24
22
  }
25
- var addImport_default = addImport;
26
-
27
23
  //#endregion
28
24
  //#region src/helpers/jscodeshift/hasImport.ts
29
25
  /**
@@ -83,8 +79,6 @@ function hasImport(root, sourceValue, importName, j) {
83
79
  conflictingImports
84
80
  };
85
81
  }
86
- var hasImport_default = hasImport;
87
-
88
82
  //#endregion
89
83
  //#region src/helpers/jscodeshift/iconUtils.ts
90
84
  /**
@@ -104,7 +98,7 @@ const processIconChildren = (j, children, iconImports, openingElement) => {
104
98
  });
105
99
  if (iconChildIndex === -1) return;
106
100
  const iconChild = unwrapJsxElement(children[iconChildIndex]);
107
- if (!iconChild || iconChild.openingElement.name.type !== "JSXIdentifier") return;
101
+ if (iconChild?.openingElement.name.type !== "JSXIdentifier") return;
108
102
  iconChild.openingElement.name.name;
109
103
  const iconPropName = iconChildIndex <= totalChildren - 1 - iconChildIndex ? "addonStart" : "addonEnd";
110
104
  const iconObject = j.objectExpression([j.property("init", j.identifier("type"), j.literal("icon")), j.property("init", j.identifier("value"), iconChild)]);
@@ -117,15 +111,13 @@ const processIconChildren = (j, children, iconImports, openingElement) => {
117
111
  if (iconChildIndex - 1 >= 0 && isWhitespaceJsxText(children[iconChildIndex - 1])) children.splice(iconChildIndex - 1, 1);
118
112
  else if (isWhitespaceJsxText(children[iconChildIndex])) children.splice(iconChildIndex, 1);
119
113
  };
120
- var iconUtils_default = processIconChildren;
121
-
122
114
  //#endregion
123
115
  //#region src/helpers/jscodeshift/jsxElementUtils.ts
124
116
  /**
125
117
  * Rename a JSX element name if it is a JSXIdentifier.
126
118
  */
127
119
  const setNameIfJSXIdentifier = (elementName, newName) => {
128
- if (elementName && elementName.type === "JSXIdentifier") return {
120
+ if (elementName?.type === "JSXIdentifier") return {
129
121
  ...elementName,
130
122
  name: newName
131
123
  };
@@ -173,7 +165,6 @@ const removeAttributeByName = (j, openingElement, attributeName) => {
173
165
  return !(attr.type === "JSXAttribute" && attr.name.type === "JSXIdentifier" && attr.name.name === attributeName);
174
166
  });
175
167
  };
176
-
177
168
  //#endregion
178
169
  //#region src/helpers/jscodeshift/jsxReportingUtils.ts
179
170
  /**
@@ -346,7 +337,6 @@ const createReporter = (j, issues) => {
346
337
  issues
347
338
  });
348
339
  };
349
-
350
340
  //#endregion
351
341
  //#region src/transforms/button/transformer.ts
352
342
  const parser = "tsx";
@@ -483,9 +473,9 @@ const transformer = (file, api, options) => {
483
473
  return priority;
484
474
  };
485
475
  const reporter = createReporter(j, manualReviewIssues);
486
- const { exists: hasButtonImport, aliases: buttonAliases, resolvedName: buttonName, conflictingImports: conflictingButtonImport } = hasImport_default(root, "@transferwise/components", "Button", j);
476
+ const { exists: hasButtonImport, aliases: buttonAliases, resolvedName: buttonName, conflictingImports: conflictingButtonImport } = hasImport(root, "@transferwise/components", "Button", j);
487
477
  if (conflictingButtonImport.length) conflictingButtonImport.forEach((node) => reporter.reportConflictingImports(node));
488
- const { exists: hasActionButtonImport, remove: removeActionButtonImport, aliases: actionButtonAliases } = hasImport_default(root, "@transferwise/components", "ActionButton", j);
478
+ const { exists: hasActionButtonImport, remove: removeActionButtonImport, aliases: actionButtonAliases } = hasImport(root, "@transferwise/components", "ActionButton", j);
489
479
  if (!hasButtonImport && !hasActionButtonImport) return file.source;
490
480
  const iconImports = /* @__PURE__ */ new Set();
491
481
  root.find(j.ImportDeclaration, { source: { value: "@transferwise/icons" } }).forEach((path) => {
@@ -497,12 +487,12 @@ const transformer = (file, api, options) => {
497
487
  });
498
488
  });
499
489
  if (hasActionButtonImport) {
500
- if (!hasButtonImport) addImport_default(root, "@transferwise/components", "Button", j);
490
+ if (!hasButtonImport) addImport(root, "@transferwise/components", "Button", j);
501
491
  findJSXElementsByName(root, j)("ActionButton", actionButtonAliases).forEach((path) => {
502
492
  const { openingElement, closingElement } = path.node;
503
493
  openingElement.name = setNameIfJSXIdentifier(openingElement.name, buttonName);
504
494
  if (closingElement) closingElement.name = setNameIfJSXIdentifier(closingElement.name, buttonName);
505
- iconUtils_default(j, path.node.children, iconImports, openingElement);
495
+ processIconChildren(j, path.node.children, iconImports, openingElement);
506
496
  if ((openingElement.attributes ?? []).some((attr) => attr.type === "JSXSpreadAttribute")) reporter.reportSpreadProps(path);
507
497
  const legacyPropNames = [
508
498
  "priority",
@@ -511,7 +501,7 @@ const transformer = (file, api, options) => {
511
501
  ];
512
502
  const legacyProps = {};
513
503
  openingElement.attributes?.forEach((attr) => {
514
- if (attr.type === "JSXAttribute" && attr.name && attr.name.type === "JSXIdentifier") {
504
+ if (attr.type === "JSXAttribute" && attr.name?.type === "JSXIdentifier") {
515
505
  const { name } = attr.name;
516
506
  if (legacyPropNames.includes(name)) {
517
507
  if (attr.value) {
@@ -551,7 +541,7 @@ const transformer = (file, api, options) => {
551
541
  attribute: j.jsxAttribute(j.jsxIdentifier("v2")),
552
542
  name: "v2"
553
543
  }]);
554
- iconUtils_default(j, path.node.children, iconImports, openingElement);
544
+ processIconChildren(j, path.node.children, iconImports, openingElement);
555
545
  const legacyProps = {};
556
546
  const legacyPropNames = [
557
547
  "priority",
@@ -561,7 +551,7 @@ const transformer = (file, api, options) => {
561
551
  "sentiment"
562
552
  ];
563
553
  openingElement.attributes?.forEach((attr) => {
564
- if (attr.type === "JSXAttribute" && attr.name && attr.name.type === "JSXIdentifier") {
554
+ if (attr.type === "JSXAttribute" && attr.name?.type === "JSXIdentifier") {
565
555
  const { name } = attr.name;
566
556
  if (legacyPropNames.includes(name)) if (attr.value) {
567
557
  if (attr.value.type === "StringLiteral") legacyProps[name] = attr.value.value;
@@ -695,7 +685,7 @@ const transformer = (file, api, options) => {
695
685
  } else if (rawType !== void 0 || rawHtmlType !== void 0) reporter.reportAmbiguousExpression(path, typeof rawType === "string" ? "type" : "htmlType");
696
686
  }
697
687
  if ("sentiment" in legacyProps) {
698
- if (!openingElement.attributes?.some((attr) => attr.type === "JSXAttribute" && attr.name && attr.name.name === "sentiment")) {
688
+ if (!openingElement.attributes?.some((attr) => attr.type === "JSXAttribute" && attr.name?.name === "sentiment")) {
699
689
  const rawValue = legacyProps.sentiment;
700
690
  if (rawValue === "negative") {
701
691
  removeAttributeByName(j, openingElement, "sentiment");
@@ -729,13 +719,11 @@ const transformer = (file, api, options) => {
729
719
  if ((openingElement.attributes ?? []).some((attr) => attr.type === "JSXSpreadAttribute")) reporter.reportSpreadProps(path);
730
720
  });
731
721
  if (manualReviewIssues.length > 0) manualReviewIssues.forEach(async (issue) => {
732
- await require_helpers.reportManualReview_default(file.path, issue);
722
+ await reportManualReview(file.path, issue);
733
723
  });
734
724
  return root.toSource();
735
725
  };
736
- var transformer_default = transformer;
737
-
738
726
  //#endregion
739
- exports.default = transformer_default;
740
- exports.parser = parser;
727
+ export { transformer as default, parser };
728
+
741
729
  //# sourceMappingURL=transformer.js.map