@wise/wds-codemods 0.0.1-experimental-2eb5228 โ 0.0.1-experimental-82259a8
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/.changeset/better-impalas-drop.md +1 -1
- package/.github/actions/test/action.yml +23 -0
- package/dist/index.js +10 -377
- package/dist/index.js.map +1 -1
- package/dist/transforms/button.d.ts +1 -5
- package/dist/transforms/button.js +96 -147
- package/dist/transforms/button.js.map +1 -1
- package/package.json +5 -7
- package/src/__tests__/runCodemod.test.ts +15 -57
- package/src/runCodemod.ts +1 -65
- package/src/transforms/button/__tests__/button.test.tsx +3 -25
- package/src/transforms/button/button.ts +126 -161
- package/src/transforms/helpers/createTestTransform.ts +2 -43
- package/src/transforms/helpers/index.ts +1 -1
- package/src/utils/__tests__/getOptions.test.ts +20 -69
- package/src/utils/getOptions.ts +2 -17
- package/src/transforms/helpers/__tests__/packageValidation.test.ts +0 -45
- package/src/transforms/helpers/packageValidation.ts +0 -53
- package/src/utils/__tests__/hasPackageVersion.test.ts +0 -190
- package/src/utils/hasPackageVersion.ts +0 -459
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
name: Test
|
|
2
|
+
description: 'Run all tests for the project'
|
|
3
|
+
|
|
4
|
+
inputs:
|
|
5
|
+
github-token:
|
|
6
|
+
required: true
|
|
7
|
+
description: 'secrets.GITHUB_TOKEN'
|
|
8
|
+
|
|
9
|
+
runs:
|
|
10
|
+
using: composite
|
|
11
|
+
steps:
|
|
12
|
+
- name: ๐ Mark repo directory as safe for git # https://github.blog/2022-04-12-git-security-vulnerability-announced/
|
|
13
|
+
run: git config --global --add safe.directory $GITHUB_WORKSPACE
|
|
14
|
+
shell: bash
|
|
15
|
+
|
|
16
|
+
- name: ๐ ๏ธ Bootstrap dependencies
|
|
17
|
+
uses: ./.github/actions/bootstrap
|
|
18
|
+
with:
|
|
19
|
+
github-token: ${{ inputs.github-token }}
|
|
20
|
+
|
|
21
|
+
- name: ๐งช Run tests
|
|
22
|
+
run: pnpm run test
|
|
23
|
+
shell: bash
|
package/dist/index.js
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
// src/runCodemod.ts
|
|
4
4
|
import fs2 from "node:fs/promises";
|
|
5
|
-
import { execSync
|
|
6
|
-
import
|
|
5
|
+
import { execSync } from "child_process";
|
|
6
|
+
import path3 from "path";
|
|
7
7
|
import { fileURLToPath } from "url";
|
|
8
8
|
|
|
9
9
|
// src/utils/getOptions.ts
|
|
@@ -21,7 +21,6 @@ async function getOptions(transformFiles) {
|
|
|
21
21
|
}
|
|
22
22
|
const gitignore2 = args.includes("--gitignore");
|
|
23
23
|
const noGitignore = args.includes("--no-gitignore");
|
|
24
|
-
const isMonorepo2 = args.includes("--monorepo");
|
|
25
24
|
if (!transformFile2 || !transformFiles.includes(transformFile2)) {
|
|
26
25
|
throw new Error("Invalid transform file specified.");
|
|
27
26
|
}
|
|
@@ -29,15 +28,7 @@ async function getOptions(transformFiles) {
|
|
|
29
28
|
throw new Error("Target path cannot be empty.");
|
|
30
29
|
}
|
|
31
30
|
const useGitignore = !!(gitignore2 || !gitignore2 && !noGitignore);
|
|
32
|
-
return {
|
|
33
|
-
transformFile: transformFile2,
|
|
34
|
-
targetPath: targetPath2,
|
|
35
|
-
dry: dry2,
|
|
36
|
-
print: print2,
|
|
37
|
-
ignorePattern: ignorePattern2,
|
|
38
|
-
gitignore: useGitignore,
|
|
39
|
-
isMonorepo: isMonorepo2
|
|
40
|
-
};
|
|
31
|
+
return { transformFile: transformFile2, targetPath: targetPath2, dry: dry2, print: print2, ignorePattern: ignorePattern2, gitignore: useGitignore };
|
|
41
32
|
}
|
|
42
33
|
const transformFile = await list({
|
|
43
34
|
message: "Select a codemod transform to run:",
|
|
@@ -47,10 +38,6 @@ async function getOptions(transformFiles) {
|
|
|
47
38
|
message: "Enter the target directory or file path to run codemod on:",
|
|
48
39
|
validate: (value) => value.trim() !== "" || "Target path cannot be empty"
|
|
49
40
|
});
|
|
50
|
-
const isMonorepo = await confirm({
|
|
51
|
-
message: "Are you targeting a monorepo packages/apps directory?",
|
|
52
|
-
default: true
|
|
53
|
-
});
|
|
54
41
|
const dry = await confirm({
|
|
55
42
|
message: "Run in dry mode (no changes written to files)?",
|
|
56
43
|
default: true
|
|
@@ -67,7 +54,7 @@ async function getOptions(transformFiles) {
|
|
|
67
54
|
message: "Respect .gitignore files?",
|
|
68
55
|
default: true
|
|
69
56
|
});
|
|
70
|
-
return { transformFile, targetPath, dry, print, ignorePattern, gitignore
|
|
57
|
+
return { transformFile, targetPath, dry, print, ignorePattern, gitignore };
|
|
71
58
|
}
|
|
72
59
|
var getOptions_default = getOptions;
|
|
73
60
|
|
|
@@ -93,328 +80,12 @@ var loadTransformModules_default = loadTransformModules;
|
|
|
93
80
|
import path2 from "path";
|
|
94
81
|
var REPORT_PATH = path2.resolve(process.cwd(), "codemod-report.txt");
|
|
95
82
|
|
|
96
|
-
// src/utils/hasPackageVersion.ts
|
|
97
|
-
import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
|
|
98
|
-
import path3 from "node:path";
|
|
99
|
-
import { execSync } from "child_process";
|
|
100
|
-
import semver from "semver";
|
|
101
|
-
import { parse as parseYaml } from "yaml";
|
|
102
|
-
var packageVersionCache = /* @__PURE__ */ new Map();
|
|
103
|
-
function hasPackageVersionFromPath(packageName, versionRequirement, startPath, isMonorepo = false) {
|
|
104
|
-
const cacheKey = `${packageName}@${versionRequirement}@${startPath}@${isMonorepo}`;
|
|
105
|
-
if (packageVersionCache.has(cacheKey)) {
|
|
106
|
-
return packageVersionCache.get(cacheKey);
|
|
107
|
-
}
|
|
108
|
-
let result;
|
|
109
|
-
if (isMonorepo) {
|
|
110
|
-
const projectRoot = findProjectRoot(startPath);
|
|
111
|
-
const allWorkspacePackages = projectRoot ? getWorkspacePackages(projectRoot) : [];
|
|
112
|
-
const resolvedStartPath = path3.resolve(startPath);
|
|
113
|
-
const targetWorkspacePackages = allWorkspacePackages.filter(
|
|
114
|
-
(packagePath) => path3.resolve(packagePath).startsWith(resolvedStartPath)
|
|
115
|
-
);
|
|
116
|
-
if (targetWorkspacePackages.length > 0) {
|
|
117
|
-
result = checkWorkspacePackages(
|
|
118
|
-
projectRoot,
|
|
119
|
-
packageName,
|
|
120
|
-
versionRequirement,
|
|
121
|
-
targetWorkspacePackages
|
|
122
|
-
);
|
|
123
|
-
} else {
|
|
124
|
-
result = checkMonorepoPackages(startPath, packageName, versionRequirement);
|
|
125
|
-
}
|
|
126
|
-
} else {
|
|
127
|
-
result = checkSinglePackage(startPath, packageName, versionRequirement);
|
|
128
|
-
}
|
|
129
|
-
packageVersionCache.set(cacheKey, result);
|
|
130
|
-
return result;
|
|
131
|
-
}
|
|
132
|
-
function getWorkspacePackages(projectRoot) {
|
|
133
|
-
return [
|
|
134
|
-
.../* @__PURE__ */ new Set([
|
|
135
|
-
...extractWorkspacesFromPackageJson(projectRoot),
|
|
136
|
-
...extractWorkspacesFromPnpmWorkspace(projectRoot)
|
|
137
|
-
])
|
|
138
|
-
];
|
|
139
|
-
}
|
|
140
|
-
function extractWorkspacesFromPackageJson(projectRoot) {
|
|
141
|
-
const packageJsonPath = path3.join(projectRoot, "package.json");
|
|
142
|
-
try {
|
|
143
|
-
const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf8"));
|
|
144
|
-
const patterns = Array.isArray(packageJson.workspaces) ? packageJson.workspaces : packageJson.workspaces?.packages ?? [];
|
|
145
|
-
return expandWorkspacePatterns(projectRoot, patterns);
|
|
146
|
-
} catch {
|
|
147
|
-
return [];
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
function extractWorkspacesFromPnpmWorkspace(projectRoot) {
|
|
151
|
-
const pnpmWorkspacePath = path3.join(projectRoot, "pnpm-workspace.yaml");
|
|
152
|
-
try {
|
|
153
|
-
const content = readFileSync(pnpmWorkspacePath, "utf8");
|
|
154
|
-
const { packages = [] } = parseYaml(content);
|
|
155
|
-
return expandWorkspacePatterns(projectRoot, packages);
|
|
156
|
-
} catch {
|
|
157
|
-
return [];
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
function expandWorkspacePatterns(projectRoot, patterns) {
|
|
161
|
-
return patterns.flatMap((pattern) => {
|
|
162
|
-
if (pattern.includes("**")) {
|
|
163
|
-
return findPackagesRecursively(projectRoot, pattern);
|
|
164
|
-
}
|
|
165
|
-
const parts = pattern.split("/");
|
|
166
|
-
const packages = [];
|
|
167
|
-
function searchPath(currentPath, remainingParts) {
|
|
168
|
-
if (remainingParts.length === 0) {
|
|
169
|
-
if (isPackageDirectory(currentPath)) packages.push(currentPath);
|
|
170
|
-
return;
|
|
171
|
-
}
|
|
172
|
-
const [part, ...rest] = remainingParts;
|
|
173
|
-
if (part === "*") {
|
|
174
|
-
try {
|
|
175
|
-
readdirSync(currentPath, { withFileTypes: true }).filter((entry) => entry.isDirectory()).forEach((entry) => searchPath(path3.join(currentPath, entry.name), rest));
|
|
176
|
-
} catch {
|
|
177
|
-
}
|
|
178
|
-
} else {
|
|
179
|
-
const nextPath = path3.join(currentPath, part);
|
|
180
|
-
if (existsSync(nextPath)) searchPath(nextPath, rest);
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
searchPath(projectRoot, parts);
|
|
184
|
-
return packages;
|
|
185
|
-
});
|
|
186
|
-
}
|
|
187
|
-
function findPackagesRecursively(projectRoot, pattern) {
|
|
188
|
-
const packages = [];
|
|
189
|
-
const prefix = pattern.split("**")[0].replace(/\/$/u, "");
|
|
190
|
-
const startPath = path3.join(projectRoot, prefix);
|
|
191
|
-
function walk(currentPath, depth = 0) {
|
|
192
|
-
if (depth > 8 || !existsSync(currentPath)) return;
|
|
193
|
-
try {
|
|
194
|
-
readdirSync(currentPath, { withFileTypes: true }).filter((entry) => entry.isDirectory()).forEach((entry) => {
|
|
195
|
-
const fullPath = path3.join(currentPath, entry.name);
|
|
196
|
-
if (isPackageDirectory(fullPath)) packages.push(fullPath);
|
|
197
|
-
walk(fullPath, depth + 1);
|
|
198
|
-
});
|
|
199
|
-
} catch {
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
walk(startPath);
|
|
203
|
-
return packages;
|
|
204
|
-
}
|
|
205
|
-
function isPackageDirectory(dirPath) {
|
|
206
|
-
try {
|
|
207
|
-
return statSync(dirPath).isDirectory() && existsSync(path3.join(dirPath, "package.json"));
|
|
208
|
-
} catch {
|
|
209
|
-
return false;
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
function checkWorkspacePackages(projectRoot, packageName, versionRequirement, workspacePaths) {
|
|
213
|
-
const foundPackages = [];
|
|
214
|
-
const notFoundPackages = [];
|
|
215
|
-
for (const workspacePath of workspacePaths) {
|
|
216
|
-
const packageDir = path3.basename(workspacePath);
|
|
217
|
-
const found = checkPackageInDirectory(workspacePath, packageName, versionRequirement, {
|
|
218
|
-
silent: true,
|
|
219
|
-
limitedScope: true
|
|
220
|
-
});
|
|
221
|
-
(found ? foundPackages : notFoundPackages).push(packageDir);
|
|
222
|
-
}
|
|
223
|
-
console.log(`\u{1F4C1} Found ${workspacePaths.length} packages to check`);
|
|
224
|
-
if (foundPackages.length > 0) {
|
|
225
|
-
console.log(
|
|
226
|
-
`\u2705 Found ${packageName} in ${foundPackages.length}/${workspacePaths.length} packages:`
|
|
227
|
-
);
|
|
228
|
-
console.log(` \u{1F4E6} ${foundPackages.join(", ")}`);
|
|
229
|
-
if (notFoundPackages.length > 0) {
|
|
230
|
-
console.log(`\u274C Not found in ${notFoundPackages.length} packages:`);
|
|
231
|
-
console.log(` \u{1F4E6} ${notFoundPackages.join(", ")}`);
|
|
232
|
-
}
|
|
233
|
-
} else {
|
|
234
|
-
console.log(`\u274C Package ${packageName} not found in any of ${workspacePaths.length} packages`);
|
|
235
|
-
if (notFoundPackages.length > 0) {
|
|
236
|
-
console.log(` \u{1F4E6} Checked: ${notFoundPackages.join(", ")}`);
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
return foundPackages.length > 0;
|
|
240
|
-
}
|
|
241
|
-
function checkMonorepoPackages(packagesPath, packageName, versionRequirement) {
|
|
242
|
-
console.log(`\u{1F4E6} Checking monorepo packages in: ${packagesPath}`);
|
|
243
|
-
try {
|
|
244
|
-
if (!existsSync(packagesPath)) {
|
|
245
|
-
return false;
|
|
246
|
-
}
|
|
247
|
-
const packageDirs = readdirSync(packagesPath).filter(
|
|
248
|
-
(entry) => statSync(path3.join(packagesPath, entry)).isDirectory()
|
|
249
|
-
);
|
|
250
|
-
console.log(`\u{1F4C1} Found ${packageDirs.length} packages to check`);
|
|
251
|
-
const foundPackages = [];
|
|
252
|
-
const notFoundPackages = [];
|
|
253
|
-
for (const entry of packageDirs) {
|
|
254
|
-
const packageDir = path3.join(packagesPath, entry);
|
|
255
|
-
const found = checkPackageInDirectory(packageDir, packageName, versionRequirement, {
|
|
256
|
-
silent: true,
|
|
257
|
-
limitedScope: true
|
|
258
|
-
});
|
|
259
|
-
(found ? foundPackages : notFoundPackages).push(entry);
|
|
260
|
-
}
|
|
261
|
-
if (foundPackages.length > 0) {
|
|
262
|
-
console.log(
|
|
263
|
-
`\u2705 Found ${packageName} in ${foundPackages.length}/${packageDirs.length} packages:`
|
|
264
|
-
);
|
|
265
|
-
console.log(` \u{1F4E6} ${foundPackages.join(", ")}`);
|
|
266
|
-
if (notFoundPackages.length > 0) {
|
|
267
|
-
console.log(`\u274C Not found in ${notFoundPackages.length} packages:`);
|
|
268
|
-
console.log(` \u{1F4E6} ${notFoundPackages.join(", ")}`);
|
|
269
|
-
}
|
|
270
|
-
} else {
|
|
271
|
-
console.log(`\u274C Package ${packageName} not found in any of ${packageDirs.length} packages`);
|
|
272
|
-
console.log(` \u{1F4E6} Checked: ${notFoundPackages.join(", ")}`);
|
|
273
|
-
}
|
|
274
|
-
return foundPackages.length > 0;
|
|
275
|
-
} catch (error) {
|
|
276
|
-
console.log(`\u26A0\uFE0F Error checking monorepo packages:`, error);
|
|
277
|
-
return false;
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
function checkSinglePackage(packagePath, packageName, versionRequirement) {
|
|
281
|
-
return checkPackageInDirectory(packagePath, packageName, versionRequirement, {
|
|
282
|
-
silent: true,
|
|
283
|
-
limitedScope: true
|
|
284
|
-
});
|
|
285
|
-
}
|
|
286
|
-
function checkPackageInDirectory(startPath, packageName, versionRequirement, options) {
|
|
287
|
-
const { checkType } = options;
|
|
288
|
-
const packageJsonResult = checkDirectDependencies(
|
|
289
|
-
startPath,
|
|
290
|
-
packageName,
|
|
291
|
-
versionRequirement,
|
|
292
|
-
options
|
|
293
|
-
);
|
|
294
|
-
if (checkType === "dependencies") return packageJsonResult;
|
|
295
|
-
if (packageJsonResult) return true;
|
|
296
|
-
if (checkType === "nodeModules")
|
|
297
|
-
return checkNodeModules(startPath, packageName, versionRequirement, options);
|
|
298
|
-
return checkNodeModules(startPath, packageName, versionRequirement, options);
|
|
299
|
-
}
|
|
300
|
-
function checkDirectDependencies(startPath, packageName, versionRequirement, { limitedScope }) {
|
|
301
|
-
const checker = (dir) => checkPackageJsonInDirectory(dir, packageName, versionRequirement);
|
|
302
|
-
return limitedScope ? checker(startPath) : walkDirectoryTree(startPath, checker);
|
|
303
|
-
}
|
|
304
|
-
function checkNodeModules(startPath, packageName, versionRequirement, { limitedScope }) {
|
|
305
|
-
const checker = (dir) => checkStandardNodeModules(dir, packageName, versionRequirement) || checkPnpmNodeModules(dir, packageName, versionRequirement);
|
|
306
|
-
return limitedScope ? checker(startPath) : walkDirectoryTree(startPath, checker);
|
|
307
|
-
}
|
|
308
|
-
function walkDirectoryTree(startPath, checkFunction) {
|
|
309
|
-
let currentDir = existsSync(startPath) && statSync(startPath).isFile() ? path3.dirname(startPath) : startPath;
|
|
310
|
-
currentDir = path3.resolve(currentDir);
|
|
311
|
-
const { root } = path3.parse(currentDir);
|
|
312
|
-
let dirCount = 0;
|
|
313
|
-
let previousDir = "";
|
|
314
|
-
while (currentDir !== root && currentDir !== previousDir && dirCount < 10) {
|
|
315
|
-
dirCount += 1;
|
|
316
|
-
if (checkFunction(currentDir)) {
|
|
317
|
-
return true;
|
|
318
|
-
}
|
|
319
|
-
previousDir = currentDir;
|
|
320
|
-
currentDir = path3.dirname(currentDir);
|
|
321
|
-
}
|
|
322
|
-
return false;
|
|
323
|
-
}
|
|
324
|
-
function checkPackageJsonInDirectory(dir, packageName, versionRequirement) {
|
|
325
|
-
const packageJsonPath = path3.join(dir, "package.json");
|
|
326
|
-
if (!existsSync(packageJsonPath)) return false;
|
|
327
|
-
try {
|
|
328
|
-
const packageJson = JSON.parse(
|
|
329
|
-
readFileSync(packageJsonPath, "utf8")
|
|
330
|
-
);
|
|
331
|
-
const allDeps = {
|
|
332
|
-
...packageJson.dependencies ?? {},
|
|
333
|
-
...packageJson.devDependencies ?? {},
|
|
334
|
-
...packageJson.peerDependencies ?? {},
|
|
335
|
-
...packageJson.optionalDependencies ?? {}
|
|
336
|
-
};
|
|
337
|
-
const installedVersion = allDeps[packageName];
|
|
338
|
-
return installedVersion ? isVersionSatisfied(installedVersion, versionRequirement) : false;
|
|
339
|
-
} catch {
|
|
340
|
-
return false;
|
|
341
|
-
}
|
|
342
|
-
}
|
|
343
|
-
function isVersionSatisfied(installedVersion, versionRequirement) {
|
|
344
|
-
if (semver.valid(installedVersion) && semver.satisfies(installedVersion, versionRequirement)) {
|
|
345
|
-
return true;
|
|
346
|
-
}
|
|
347
|
-
const cleanVersion = semver.coerce(installedVersion);
|
|
348
|
-
return cleanVersion ? semver.satisfies(cleanVersion.version, versionRequirement) : false;
|
|
349
|
-
}
|
|
350
|
-
function checkStandardNodeModules(baseDir, packageName, versionRequirement) {
|
|
351
|
-
const packagePath = path3.join(baseDir, "node_modules", packageName, "package.json");
|
|
352
|
-
if (!existsSync(packagePath)) return false;
|
|
353
|
-
try {
|
|
354
|
-
const packageJson = JSON.parse(readFileSync(packagePath, "utf8"));
|
|
355
|
-
return packageJson.version ? semver.satisfies(packageJson.version, versionRequirement) : false;
|
|
356
|
-
} catch {
|
|
357
|
-
return false;
|
|
358
|
-
}
|
|
359
|
-
}
|
|
360
|
-
function checkPnpmNodeModules(baseDir, packageName, versionRequirement) {
|
|
361
|
-
const pnpmDir = path3.join(baseDir, "node_modules", ".pnpm");
|
|
362
|
-
if (!existsSync(pnpmDir)) return false;
|
|
363
|
-
try {
|
|
364
|
-
const entries = readdirSync(pnpmDir);
|
|
365
|
-
for (const entry of entries) {
|
|
366
|
-
if (entry.startsWith(packageName.replace("/", "+")) || entry.includes(`${packageName}@`)) {
|
|
367
|
-
const packagePath = path3.join(pnpmDir, entry, "node_modules", packageName, "package.json");
|
|
368
|
-
if (existsSync(packagePath)) {
|
|
369
|
-
const packageJson = JSON.parse(
|
|
370
|
-
readFileSync(packagePath, "utf8")
|
|
371
|
-
);
|
|
372
|
-
if (packageJson.version && semver.satisfies(packageJson.version, versionRequirement)) {
|
|
373
|
-
return true;
|
|
374
|
-
}
|
|
375
|
-
}
|
|
376
|
-
}
|
|
377
|
-
}
|
|
378
|
-
} catch {
|
|
379
|
-
}
|
|
380
|
-
return false;
|
|
381
|
-
}
|
|
382
|
-
function findProjectRoot(startPath) {
|
|
383
|
-
try {
|
|
384
|
-
const gitRoot = execSync("git rev-parse --show-toplevel", {
|
|
385
|
-
cwd: startPath,
|
|
386
|
-
encoding: "utf8",
|
|
387
|
-
stdio: ["ignore", "pipe", "ignore"]
|
|
388
|
-
}).trim();
|
|
389
|
-
return gitRoot && existsSync(gitRoot) ? gitRoot : null;
|
|
390
|
-
} catch {
|
|
391
|
-
return null;
|
|
392
|
-
}
|
|
393
|
-
}
|
|
394
|
-
function findClosestGitignore(startPath) {
|
|
395
|
-
const projectRoot = findProjectRoot(startPath);
|
|
396
|
-
if (!projectRoot) {
|
|
397
|
-
return null;
|
|
398
|
-
}
|
|
399
|
-
const gitignorePath = path3.join(projectRoot, ".gitignore");
|
|
400
|
-
return existsSync(gitignorePath) ? gitignorePath : null;
|
|
401
|
-
}
|
|
402
|
-
|
|
403
83
|
// src/runCodemod.ts
|
|
404
84
|
var currentFilePath = fileURLToPath(import.meta.url);
|
|
405
|
-
var currentDirPath =
|
|
406
|
-
async function getPackageRequirements(transformPath) {
|
|
407
|
-
try {
|
|
408
|
-
const transformModule = await import(transformPath);
|
|
409
|
-
return transformModule.packageRequirements ?? [];
|
|
410
|
-
} catch (error) {
|
|
411
|
-
console.debug("Could not read package requirements from transform:", error);
|
|
412
|
-
return [];
|
|
413
|
-
}
|
|
414
|
-
}
|
|
85
|
+
var currentDirPath = path3.dirname(currentFilePath);
|
|
415
86
|
async function runCodemod(transformsDir) {
|
|
416
87
|
try {
|
|
417
|
-
const resolvedTransformsDir = transformsDir ??
|
|
88
|
+
const resolvedTransformsDir = transformsDir ?? path3.resolve(currentDirPath, "../dist/transforms");
|
|
418
89
|
console.debug(`Resolved transforms directory: ${resolvedTransformsDir}`);
|
|
419
90
|
const { transformFiles } = await loadTransformModules_default(resolvedTransformsDir);
|
|
420
91
|
if (transformFiles.length === 0) {
|
|
@@ -422,58 +93,20 @@ async function runCodemod(transformsDir) {
|
|
|
422
93
|
}
|
|
423
94
|
const resolvedTransformFiles = await Promise.all(transformFiles);
|
|
424
95
|
const options = await getOptions_default(resolvedTransformFiles);
|
|
425
|
-
const codemodPath =
|
|
96
|
+
const codemodPath = path3.resolve(resolvedTransformsDir, `${options.transformFile}.js`);
|
|
426
97
|
console.debug(`Resolved codemod path: ${codemodPath}`);
|
|
427
|
-
const packageRequirements = await getPackageRequirements(codemodPath);
|
|
428
|
-
const packageResults = {};
|
|
429
|
-
if (packageRequirements.length > 0) {
|
|
430
|
-
console.log("\u{1F4E6} Checking package requirements...");
|
|
431
|
-
for (const pkg of packageRequirements) {
|
|
432
|
-
console.log(`\u{1F50D} Checking for ${pkg.name}@${pkg.version} in ${options.targetPath}...`);
|
|
433
|
-
const found = hasPackageVersionFromPath(
|
|
434
|
-
pkg.name,
|
|
435
|
-
pkg.version,
|
|
436
|
-
options.targetPath,
|
|
437
|
-
options.isMonorepo
|
|
438
|
-
);
|
|
439
|
-
packageResults[`${pkg.name}@${pkg.version}`] = found;
|
|
440
|
-
if (found) {
|
|
441
|
-
console.log(`\u2705 Found ${pkg.name}@${pkg.version}`);
|
|
442
|
-
} else {
|
|
443
|
-
console.warn(
|
|
444
|
-
`\u274C Package ${pkg.name}@${pkg.version} not found in target. Transform may be skipped.`
|
|
445
|
-
);
|
|
446
|
-
}
|
|
447
|
-
}
|
|
448
|
-
}
|
|
449
|
-
let gitignoreArg = "";
|
|
450
|
-
if (options.gitignore) {
|
|
451
|
-
const closestGitignore = findClosestGitignore(options.targetPath);
|
|
452
|
-
if (closestGitignore) {
|
|
453
|
-
console.debug(`Found .gitignore at: ${closestGitignore}`);
|
|
454
|
-
gitignoreArg = `--ignore-config=${closestGitignore}`;
|
|
455
|
-
} else {
|
|
456
|
-
console.debug("No .gitignore found in directory tree, skipping gitignore option");
|
|
457
|
-
gitignoreArg = "";
|
|
458
|
-
}
|
|
459
|
-
}
|
|
460
98
|
const args = [
|
|
461
99
|
"-t",
|
|
462
100
|
codemodPath,
|
|
463
101
|
options.targetPath,
|
|
464
|
-
"--extensions=tsx,ts,jsx,js",
|
|
465
|
-
"--ignore-pattern=**/node_modules/**",
|
|
466
|
-
"--ignore-pattern=**/dist/**",
|
|
467
|
-
"--ignore-pattern=**/*.d.ts",
|
|
468
102
|
options.dry ? "--dry" : "",
|
|
469
103
|
options.print ? "--print" : "",
|
|
470
104
|
options.ignorePattern ? options.ignorePattern.split(",").map((pattern) => `--ignore-pattern=${pattern.trim()}`).join(" ") : "",
|
|
471
|
-
|
|
472
|
-
Object.keys(packageResults).length > 0 ? `--packageResults='${JSON.stringify(packageResults)}'` : ""
|
|
105
|
+
options.gitignore ? "--gitignore" : ""
|
|
473
106
|
].filter(Boolean);
|
|
474
107
|
const command = `npx jscodeshift ${args.join(" ")}`;
|
|
475
108
|
console.debug(`Running: ${command}`);
|
|
476
|
-
const reportPath =
|
|
109
|
+
const reportPath = path3.resolve(process.cwd(), "codemod-report.txt");
|
|
477
110
|
try {
|
|
478
111
|
await fs2.access(reportPath);
|
|
479
112
|
await fs2.rm(reportPath);
|
|
@@ -481,7 +114,7 @@ async function runCodemod(transformsDir) {
|
|
|
481
114
|
} catch {
|
|
482
115
|
console.debug(`No existing report file to remove: ${reportPath}`);
|
|
483
116
|
}
|
|
484
|
-
|
|
117
|
+
execSync(command, { stdio: "inherit" });
|
|
485
118
|
try {
|
|
486
119
|
const reportContent = await fs2.readFile(reportPath, "utf8");
|
|
487
120
|
const lines = reportContent.split("\n").filter(Boolean);
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/runCodemod.ts","../src/utils/getOptions.ts","../src/utils/loadTransformModules.ts","../src/utils/reportManualReview.ts","../src/utils/hasPackageVersion.ts","../src/index.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport fs from 'node:fs/promises';\n\nimport { execSync } from 'child_process';\nimport path from 'path';\nimport { fileURLToPath } from 'url';\n\nimport { getOptions, handleError, loadTransformModules } from './utils';\nimport { findClosestGitignore, hasPackageVersionFromPath } from './utils/hasPackageVersion';\n\nconst currentFilePath = fileURLToPath(import.meta.url);\nconst currentDirPath = path.dirname(currentFilePath);\n\n// Function to get package requirements from a transform\nasync function getPackageRequirements(\n transformPath: string,\n): Promise<{ name: string; version: string }[]> {\n try {\n const transformModule = (await import(transformPath)) as {\n packageRequirements?: { name: string; version: string }[];\n };\n return transformModule.packageRequirements ?? [];\n } catch (error) {\n console.debug('Could not read package requirements from transform:', error);\n return [];\n }\n}\n\nasync function runCodemod(transformsDir?: string) {\n try {\n const resolvedTransformsDir =\n transformsDir ?? path.resolve(currentDirPath, '../dist/transforms');\n console.debug(`Resolved transforms directory: ${resolvedTransformsDir}`);\n\n const { transformFiles } = await loadTransformModules(resolvedTransformsDir);\n\n if (transformFiles.length === 0) {\n throw new Error(`No transform scripts found in directory: ${resolvedTransformsDir}`);\n }\n\n const resolvedTransformFiles = await Promise.all(transformFiles);\n const options = await getOptions(resolvedTransformFiles);\n\n const codemodPath = path.resolve(resolvedTransformsDir, `${options.transformFile}.js`);\n console.debug(`Resolved codemod path: ${codemodPath}`);\n\n // Check package requirements from the transform\n const packageRequirements = await getPackageRequirements(codemodPath);\n const packageResults: Record<string, boolean> = {};\n\n if (packageRequirements.length > 0) {\n console.log('๐ฆ Checking package requirements...');\n\n for (const pkg of packageRequirements) {\n console.log(`๐ Checking for ${pkg.name}@${pkg.version} in ${options.targetPath}...`);\n\n // Pass the monorepo flag to the package checker\n const found = hasPackageVersionFromPath(\n pkg.name,\n pkg.version,\n options.targetPath,\n options.isMonorepo,\n );\n packageResults[`${pkg.name}@${pkg.version}`] = found;\n\n if (found) {\n console.log(`โ
Found ${pkg.name}@${pkg.version}`);\n } else {\n console.warn(\n `โ Package ${pkg.name}@${pkg.version} not found in target. Transform may be skipped.`,\n );\n }\n }\n }\n\n let gitignoreArg = '';\n if (options.gitignore) {\n const closestGitignore = findClosestGitignore(options.targetPath);\n if (closestGitignore) {\n console.debug(`Found .gitignore at: ${closestGitignore}`);\n gitignoreArg = `--ignore-config=${closestGitignore}`;\n } else {\n console.debug('No .gitignore found in directory tree, skipping gitignore option');\n gitignoreArg = '';\n }\n }\n\n const args = [\n '-t',\n codemodPath,\n options.targetPath,\n '--extensions=tsx,ts,jsx,js',\n '--ignore-pattern=**/node_modules/**',\n '--ignore-pattern=**/dist/**',\n '--ignore-pattern=**/*.d.ts',\n options.dry ? '--dry' : '',\n options.print ? '--print' : '',\n options.ignorePattern\n ? options.ignorePattern\n .split(',')\n .map((pattern) => `--ignore-pattern=${pattern.trim()}`)\n .join(' ')\n : '',\n gitignoreArg,\n Object.keys(packageResults).length > 0\n ? `--packageResults='${JSON.stringify(packageResults)}'`\n : '',\n ].filter(Boolean);\n\n const command = `npx jscodeshift ${args.join(' ')}`;\n\n console.debug(`Running: ${command}`);\n\n const reportPath = path.resolve(process.cwd(), 'codemod-report.txt');\n\n try {\n await fs.access(reportPath);\n await fs.rm(reportPath);\n console.debug(`Removed existing report file: ${reportPath}`);\n } catch {\n console.debug(`No existing report file to remove: ${reportPath}`);\n }\n\n execSync(command, { stdio: 'inherit' });\n\n try {\n const reportContent = await fs.readFile(reportPath, 'utf8');\n const lines = reportContent.split('\\n').filter(Boolean);\n if (lines.length) {\n console.log(\n `\\nโ ๏ธ ${lines.length} manual review${lines.length > 1 ? 's are' : ' is'} required. See ${reportPath} for details.`,\n );\n } else {\n console.debug(`Report file exists but is empty: ${reportPath}`);\n }\n } catch {\n console.debug(`No report file generated - no manual reviews needed`);\n }\n } catch (error: unknown) {\n if (error instanceof Error) {\n console.error('Error running codemod:', error.message);\n } else {\n console.error('Error running codemod:', error);\n }\n if (process.env.NODE_ENV !== 'test') {\n process.exit(1);\n }\n }\n}\n\nexport { runCodemod };\n","import { confirm, input, select as list } from '@inquirer/prompts';\nimport path from 'path';\n\nasync function getOptions(transformFiles: string[]) {\n const args = process.argv.slice(2);\n if (args.length > 0) {\n const [transformFile, targetPath] = args;\n const dry = args.includes('--dry') || args.includes('--dry-run');\n const print = args.includes('--print');\n const ignorePatternIndex = args.findIndex((arg) => arg === '--ignore-pattern');\n let ignorePattern: string | undefined;\n if (ignorePatternIndex !== -1 && args.length > ignorePatternIndex + 1) {\n ignorePattern = args[ignorePatternIndex + 1];\n }\n const gitignore = args.includes('--gitignore');\n const noGitignore = args.includes('--no-gitignore');\n const isMonorepo = args.includes('--monorepo');\n\n if (!transformFile || !transformFiles.includes(transformFile)) {\n throw new Error('Invalid transform file specified.');\n }\n if (!targetPath) {\n throw new Error('Target path cannot be empty.');\n }\n\n // If both --gitignore and --no-gitignore are specified, prioritize --gitignore\n const useGitignore = !!(gitignore || (!gitignore && !noGitignore));\n\n return {\n transformFile,\n targetPath,\n dry,\n print,\n ignorePattern,\n gitignore: useGitignore,\n isMonorepo,\n };\n }\n\n const transformFile = await list({\n message: 'Select a codemod transform to run:',\n choices: transformFiles.map((file) => ({ name: file, value: file })),\n });\n\n const targetPath = await input({\n message: 'Enter the target directory or file path to run codemod on:',\n validate: (value) => value.trim() !== '' || 'Target path cannot be empty',\n });\n\n const isMonorepo = await confirm({\n message: 'Are you targeting a monorepo packages/apps directory?',\n default: true,\n });\n\n const dry = await confirm({\n message: 'Run in dry mode (no changes written to files)?',\n default: true,\n });\n\n const print = await confirm({\n message: 'Print transformed source to console?',\n default: false,\n });\n\n const ignorePattern = await input({\n message: 'Enter ignore pattern(s) (comma separated) or leave empty:',\n validate: (value) => true,\n });\n\n const gitignore = await confirm({\n message: 'Respect .gitignore files?',\n default: true,\n });\n\n return { transformFile, targetPath, dry, print, ignorePattern, gitignore, isMonorepo };\n}\n\nexport default getOptions;\n","import { promises as fs } from 'fs';\nimport path from 'path';\n\ninterface TransformModule {\n default: {\n default: unknown;\n };\n}\n\nasync function loadTransformModules(transformsDir: string) {\n let transformModules: Record<string, unknown> = {};\n\n const files = await fs.readdir(transformsDir);\n const transformFiles = await Promise.all(\n files\n .filter((file) => file.endsWith('.js'))\n .map(async (file) => {\n const transformPath = path.join(transformsDir, file);\n const transformModule = (await import(transformPath)) as TransformModule;\n transformModules = { ...transformModules, [file]: transformModule.default.default };\n return file.replace('.js', '');\n }),\n );\n\n return { transformModules, transformFiles };\n}\n\nexport default loadTransformModules;\n","import fs from 'node:fs/promises';\n\nimport path from 'path';\n\nconst REPORT_PATH = path.resolve(process.cwd(), 'codemod-report.txt');\n\nconst reportManualReview = async (filePath: string, message: string): Promise<void> => {\n const lineMatch = /at line (\\d+)/u.exec(message);\n const lineNumber = lineMatch?.[1];\n\n const cleanMessage = message.replace(/ at line \\d+/u, '');\n const lineInfo = lineNumber ? `:${lineNumber}` : '';\n\n await fs.appendFile(REPORT_PATH, `[${filePath}${lineInfo}] ${cleanMessage}\\n`, 'utf8');\n};\n\nexport default reportManualReview;\n","import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs';\nimport path from 'node:path';\n\nimport { execSync } from 'child_process';\nimport semver from 'semver';\nimport { parse as parseYaml } from 'yaml';\n\ninterface PackageJson {\n name?: string;\n version?: string;\n dependencies?: Record<string, string>;\n devDependencies?: Record<string, string>;\n peerDependencies?: Record<string, string>;\n optionalDependencies?: Record<string, string>;\n workspaces?: string[] | { packages: string[] };\n}\n\ninterface CheckOptions {\n silent: boolean;\n limitedScope: boolean;\n checkType?: 'dependencies' | 'nodeModules';\n}\n\nexport const packageVersionCache = new Map<string, boolean>();\n\nexport default function hasPackageVersion(\n packageName: string,\n versionRequirement: string,\n): boolean {\n return hasPackageVersionFromPath(packageName, versionRequirement, process.cwd(), false);\n}\n\nexport function hasPackageVersionFromPath(\n packageName: string,\n versionRequirement: string,\n startPath: string,\n isMonorepo = false,\n): boolean {\n const cacheKey = `${packageName}@${versionRequirement}@${startPath}@${isMonorepo}`;\n\n if (packageVersionCache.has(cacheKey)) {\n return packageVersionCache.get(cacheKey)!;\n }\n\n let result: boolean;\n\n if (isMonorepo) {\n const projectRoot = findProjectRoot(startPath);\n const allWorkspacePackages = projectRoot ? getWorkspacePackages(projectRoot) : [];\n\n // Filter workspace packages to only include those within the target directory\n const resolvedStartPath = path.resolve(startPath);\n const targetWorkspacePackages = allWorkspacePackages.filter((packagePath) =>\n path.resolve(packagePath).startsWith(resolvedStartPath),\n );\n\n if (targetWorkspacePackages.length > 0) {\n result = checkWorkspacePackages(\n projectRoot!,\n packageName,\n versionRequirement,\n targetWorkspacePackages,\n );\n } else {\n // Fallback to checking the target directory directly for packages\n result = checkMonorepoPackages(startPath, packageName, versionRequirement);\n }\n } else {\n result = checkSinglePackage(startPath, packageName, versionRequirement);\n }\n\n packageVersionCache.set(cacheKey, result);\n return result;\n}\n\nfunction getWorkspacePackages(projectRoot: string): string[] {\n return [\n ...new Set([\n ...extractWorkspacesFromPackageJson(projectRoot),\n ...extractWorkspacesFromPnpmWorkspace(projectRoot),\n ]),\n ];\n}\n\nfunction extractWorkspacesFromPackageJson(projectRoot: string): string[] {\n const packageJsonPath = path.join(projectRoot, 'package.json');\n\n try {\n const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf8')) as PackageJson;\n const patterns = Array.isArray(packageJson.workspaces)\n ? packageJson.workspaces\n : (packageJson.workspaces?.packages ?? []);\n\n return expandWorkspacePatterns(projectRoot, patterns);\n } catch {\n return [];\n }\n}\n\nfunction extractWorkspacesFromPnpmWorkspace(projectRoot: string): string[] {\n const pnpmWorkspacePath = path.join(projectRoot, 'pnpm-workspace.yaml');\n\n try {\n const content = readFileSync(pnpmWorkspacePath, 'utf8');\n\n const { packages = [] } = parseYaml(content) as { packages?: string[] };\n return expandWorkspacePatterns(projectRoot, packages);\n } catch {\n return [];\n }\n}\n\nfunction expandWorkspacePatterns(projectRoot: string, patterns: string[]): string[] {\n return patterns.flatMap((pattern) => {\n if (pattern.includes('**')) {\n return findPackagesRecursively(projectRoot, pattern);\n }\n\n const parts = pattern.split('/');\n const packages: string[] = [];\n\n function searchPath(currentPath: string, remainingParts: string[]): void {\n if (remainingParts.length === 0) {\n if (isPackageDirectory(currentPath)) packages.push(currentPath);\n return;\n }\n\n const [part, ...rest] = remainingParts;\n\n if (part === '*') {\n try {\n readdirSync(currentPath, { withFileTypes: true })\n .filter((entry) => entry.isDirectory())\n .forEach((entry) => searchPath(path.join(currentPath, entry.name), rest));\n } catch {}\n } else {\n const nextPath = path.join(currentPath, part);\n if (existsSync(nextPath)) searchPath(nextPath, rest);\n }\n }\n\n searchPath(projectRoot, parts);\n return packages;\n });\n}\n\nfunction findPackagesRecursively(projectRoot: string, pattern: string): string[] {\n const packages: string[] = [];\n const prefix = pattern.split('**')[0].replace(/\\/$/u, '');\n const startPath = path.join(projectRoot, prefix);\n\n function walk(currentPath: string, depth = 0): void {\n if (depth > 8 || !existsSync(currentPath)) return;\n\n try {\n readdirSync(currentPath, { withFileTypes: true })\n .filter((entry) => entry.isDirectory())\n .forEach((entry) => {\n const fullPath = path.join(currentPath, entry.name);\n if (isPackageDirectory(fullPath)) packages.push(fullPath);\n walk(fullPath, depth + 1);\n });\n } catch {}\n }\n\n walk(startPath);\n return packages;\n}\n\nfunction isPackageDirectory(dirPath: string): boolean {\n try {\n return statSync(dirPath).isDirectory() && existsSync(path.join(dirPath, 'package.json'));\n } catch {\n return false;\n }\n}\n\nfunction checkWorkspacePackages(\n projectRoot: string,\n packageName: string,\n versionRequirement: string,\n workspacePaths: string[],\n): boolean {\n const foundPackages: string[] = [];\n const notFoundPackages: string[] = [];\n\n for (const workspacePath of workspacePaths) {\n const packageDir = path.basename(workspacePath);\n const found = checkPackageInDirectory(workspacePath, packageName, versionRequirement, {\n silent: true,\n limitedScope: true,\n });\n\n (found ? foundPackages : notFoundPackages).push(packageDir);\n }\n\n console.log(`๐ Found ${workspacePaths.length} packages to check`);\n\n if (foundPackages.length > 0) {\n console.log(\n `โ
Found ${packageName} in ${foundPackages.length}/${workspacePaths.length} packages:`,\n );\n console.log(` ๐ฆ ${foundPackages.join(', ')}`);\n\n if (notFoundPackages.length > 0) {\n console.log(`โ Not found in ${notFoundPackages.length} packages:`);\n console.log(` ๐ฆ ${notFoundPackages.join(', ')}`);\n }\n } else {\n console.log(`โ Package ${packageName} not found in any of ${workspacePaths.length} packages`);\n if (notFoundPackages.length > 0) {\n console.log(` ๐ฆ Checked: ${notFoundPackages.join(', ')}`);\n }\n }\n\n return foundPackages.length > 0;\n}\n\nfunction checkMonorepoPackages(\n packagesPath: string,\n packageName: string,\n versionRequirement: string,\n): boolean {\n console.log(`๐ฆ Checking monorepo packages in: ${packagesPath}`);\n try {\n if (!existsSync(packagesPath)) {\n return false;\n }\n\n const packageDirs = readdirSync(packagesPath).filter((entry) =>\n statSync(path.join(packagesPath, entry)).isDirectory(),\n );\n\n console.log(`๐ Found ${packageDirs.length} packages to check`);\n\n const foundPackages: string[] = [];\n const notFoundPackages: string[] = [];\n\n for (const entry of packageDirs) {\n const packageDir = path.join(packagesPath, entry);\n const found = checkPackageInDirectory(packageDir, packageName, versionRequirement, {\n silent: true,\n limitedScope: true,\n });\n\n (found ? foundPackages : notFoundPackages).push(entry);\n }\n\n if (foundPackages.length > 0) {\n console.log(\n `โ
Found ${packageName} in ${foundPackages.length}/${packageDirs.length} packages:`,\n );\n console.log(` ๐ฆ ${foundPackages.join(', ')}`);\n\n if (notFoundPackages.length > 0) {\n console.log(`โ Not found in ${notFoundPackages.length} packages:`);\n console.log(` ๐ฆ ${notFoundPackages.join(', ')}`);\n }\n } else {\n console.log(`โ Package ${packageName} not found in any of ${packageDirs.length} packages`);\n console.log(` ๐ฆ Checked: ${notFoundPackages.join(', ')}`);\n }\n\n return foundPackages.length > 0;\n } catch (error) {\n console.log(`โ ๏ธ Error checking monorepo packages:`, error);\n return false;\n }\n}\n\nfunction checkSinglePackage(\n packagePath: string,\n packageName: string,\n versionRequirement: string,\n): boolean {\n return checkPackageInDirectory(packagePath, packageName, versionRequirement, {\n silent: true,\n limitedScope: true,\n });\n}\n\nfunction checkPackageInDirectory(\n startPath: string,\n packageName: string,\n versionRequirement: string,\n options: CheckOptions,\n): boolean {\n const { checkType } = options;\n const packageJsonResult = checkDirectDependencies(\n startPath,\n packageName,\n versionRequirement,\n options,\n );\n\n if (checkType === 'dependencies') return packageJsonResult;\n if (packageJsonResult) return true;\n if (checkType === 'nodeModules')\n return checkNodeModules(startPath, packageName, versionRequirement, options);\n\n return checkNodeModules(startPath, packageName, versionRequirement, options);\n}\n\nfunction checkDirectDependencies(\n startPath: string,\n packageName: string,\n versionRequirement: string,\n { limitedScope }: CheckOptions,\n): boolean {\n const checker = (dir: string) =>\n checkPackageJsonInDirectory(dir, packageName, versionRequirement);\n return limitedScope ? checker(startPath) : walkDirectoryTree(startPath, checker);\n}\n\nfunction checkNodeModules(\n startPath: string,\n packageName: string,\n versionRequirement: string,\n { limitedScope }: CheckOptions,\n): boolean {\n const checker = (dir: string) =>\n checkStandardNodeModules(dir, packageName, versionRequirement) ||\n checkPnpmNodeModules(dir, packageName, versionRequirement);\n\n return limitedScope ? checker(startPath) : walkDirectoryTree(startPath, checker);\n}\n\nfunction walkDirectoryTree(startPath: string, checkFunction: (dir: string) => boolean): boolean {\n let currentDir =\n existsSync(startPath) && statSync(startPath).isFile() ? path.dirname(startPath) : startPath;\n\n currentDir = path.resolve(currentDir);\n\n const { root } = path.parse(currentDir);\n let dirCount = 0;\n let previousDir = '';\n\n while (currentDir !== root && currentDir !== previousDir && dirCount < 10) {\n dirCount += 1;\n\n if (checkFunction(currentDir)) {\n return true;\n }\n\n previousDir = currentDir;\n currentDir = path.dirname(currentDir);\n }\n\n return false;\n}\n\nfunction checkPackageJsonInDirectory(\n dir: string,\n packageName: string,\n versionRequirement: string,\n): boolean {\n const packageJsonPath = path.join(dir, 'package.json');\n\n if (!existsSync(packageJsonPath)) return false;\n\n try {\n const packageJson: PackageJson = JSON.parse(\n readFileSync(packageJsonPath, 'utf8'),\n ) as PackageJson;\n const allDeps = {\n ...(packageJson.dependencies ?? {}),\n ...(packageJson.devDependencies ?? {}),\n ...(packageJson.peerDependencies ?? {}),\n ...(packageJson.optionalDependencies ?? {}),\n };\n\n const installedVersion = allDeps[packageName];\n return installedVersion ? isVersionSatisfied(installedVersion, versionRequirement) : false;\n } catch {\n return false;\n }\n}\n\nfunction isVersionSatisfied(installedVersion: string, versionRequirement: string): boolean {\n if (semver.valid(installedVersion) && semver.satisfies(installedVersion, versionRequirement)) {\n return true;\n }\n\n const cleanVersion = semver.coerce(installedVersion);\n return cleanVersion ? semver.satisfies(cleanVersion.version, versionRequirement) : false;\n}\n\nfunction checkStandardNodeModules(\n baseDir: string,\n packageName: string,\n versionRequirement: string,\n): boolean {\n const packagePath = path.join(baseDir, 'node_modules', packageName, 'package.json');\n if (!existsSync(packagePath)) return false;\n\n try {\n const packageJson: PackageJson = JSON.parse(readFileSync(packagePath, 'utf8')) as PackageJson;\n return packageJson.version ? semver.satisfies(packageJson.version, versionRequirement) : false;\n } catch {\n return false;\n }\n}\n\nfunction checkPnpmNodeModules(\n baseDir: string,\n packageName: string,\n versionRequirement: string,\n): boolean {\n const pnpmDir = path.join(baseDir, 'node_modules', '.pnpm');\n if (!existsSync(pnpmDir)) return false;\n\n try {\n const entries = readdirSync(pnpmDir);\n\n for (const entry of entries) {\n if (entry.startsWith(packageName.replace('/', '+')) || entry.includes(`${packageName}@`)) {\n const packagePath = path.join(pnpmDir, entry, 'node_modules', packageName, 'package.json');\n\n if (existsSync(packagePath)) {\n const packageJson: PackageJson = JSON.parse(\n readFileSync(packagePath, 'utf8'),\n ) as PackageJson;\n if (packageJson.version && semver.satisfies(packageJson.version, versionRequirement)) {\n return true;\n }\n }\n }\n }\n } catch {\n // Silent\n }\n\n return false;\n}\n\nexport function findProjectRoot(startPath: string): string | null {\n try {\n const gitRoot = execSync('git rev-parse --show-toplevel', {\n cwd: startPath,\n encoding: 'utf8',\n stdio: ['ignore', 'pipe', 'ignore'],\n }).trim();\n\n return gitRoot && existsSync(gitRoot) ? gitRoot : null;\n } catch {\n return null;\n }\n}\n\nexport function findClosestGitignore(startPath: string): string | null {\n const projectRoot = findProjectRoot(startPath);\n\n if (!projectRoot) {\n return null;\n }\n\n const gitignorePath = path.join(projectRoot, '.gitignore');\n return existsSync(gitignorePath) ? gitignorePath : null;\n}\n","#!/usr/bin/env node\nimport { runCodemod } from './runCodemod';\n\nvoid runCodemod();\n"],"mappings":"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","names":["fs","execSync","path","transformFile","targetPath","dry","print","ignorePattern","gitignore","isMonorepo","path","path","path","path","fs","execSync"]}
|
|
1
|
+
{"version":3,"sources":["../src/runCodemod.ts","../src/utils/getOptions.ts","../src/utils/loadTransformModules.ts","../src/utils/reportManualReview.ts","../src/index.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport fs from 'node:fs/promises';\n\nimport { execSync } from 'child_process';\nimport path from 'path';\nimport { fileURLToPath } from 'url';\n\nimport { getOptions, handleError, loadTransformModules } from './utils';\n\nconst currentFilePath = fileURLToPath(import.meta.url);\nconst currentDirPath = path.dirname(currentFilePath);\n\nasync function runCodemod(transformsDir?: string) {\n try {\n const resolvedTransformsDir =\n transformsDir ?? path.resolve(currentDirPath, '../dist/transforms');\n console.debug(`Resolved transforms directory: ${resolvedTransformsDir}`);\n\n const { transformFiles } = await loadTransformModules(resolvedTransformsDir);\n\n if (transformFiles.length === 0) {\n throw new Error(`No transform scripts found in directory: ${resolvedTransformsDir}`);\n }\n\n const resolvedTransformFiles = await Promise.all(transformFiles);\n const options = await getOptions(resolvedTransformFiles);\n\n const codemodPath = path.resolve(resolvedTransformsDir, `${options.transformFile}.js`);\n console.debug(`Resolved codemod path: ${codemodPath}`);\n\n const args = [\n '-t',\n codemodPath,\n options.targetPath,\n options.dry ? '--dry' : '',\n options.print ? '--print' : '',\n options.ignorePattern\n ? options.ignorePattern\n .split(',')\n .map((pattern) => `--ignore-pattern=${pattern.trim()}`)\n .join(' ')\n : '',\n options.gitignore ? '--gitignore' : '',\n ].filter(Boolean);\n\n const command = `npx jscodeshift ${args.join(' ')}`;\n\n console.debug(`Running: ${command}`);\n\n const reportPath = path.resolve(process.cwd(), 'codemod-report.txt');\n\n try {\n await fs.access(reportPath);\n await fs.rm(reportPath);\n console.debug(`Removed existing report file: ${reportPath}`);\n } catch {\n console.debug(`No existing report file to remove: ${reportPath}`);\n }\n\n execSync(command, { stdio: 'inherit' });\n\n try {\n const reportContent = await fs.readFile(reportPath, 'utf8');\n const lines = reportContent.split('\\n').filter(Boolean);\n if (lines.length) {\n console.log(\n `\\nโ ๏ธ ${lines.length} manual review${lines.length > 1 ? 's are' : ' is'} required. See ${reportPath} for details.`,\n );\n } else {\n console.debug(`Report file exists but is empty: ${reportPath}`);\n }\n } catch {\n console.debug(`No report file generated - no manual reviews needed`);\n }\n } catch (error: unknown) {\n if (error instanceof Error) {\n console.error('Error running codemod:', error.message);\n } else {\n console.error('Error running codemod:', error);\n }\n if (process.env.NODE_ENV !== 'test') {\n process.exit(1);\n }\n }\n}\n\nexport { runCodemod };\n","import { confirm, input, select as list } from '@inquirer/prompts';\n\nasync function getOptions(transformFiles: string[]) {\n const args = process.argv.slice(2);\n if (args.length > 0) {\n const [transformFile, targetPath] = args;\n const dry = args.includes('--dry') || args.includes('--dry-run');\n const print = args.includes('--print');\n const ignorePatternIndex = args.findIndex((arg) => arg === '--ignore-pattern');\n let ignorePattern: string | undefined;\n if (ignorePatternIndex !== -1 && args.length > ignorePatternIndex + 1) {\n ignorePattern = args[ignorePatternIndex + 1];\n }\n const gitignore = args.includes('--gitignore');\n const noGitignore = args.includes('--no-gitignore');\n\n if (!transformFile || !transformFiles.includes(transformFile)) {\n throw new Error('Invalid transform file specified.');\n }\n if (!targetPath) {\n throw new Error('Target path cannot be empty.');\n }\n\n // If both --gitignore and --no-gitignore are specified, prioritize --gitignore\n const useGitignore = !!(gitignore || (!gitignore && !noGitignore));\n\n return { transformFile, targetPath, dry, print, ignorePattern, gitignore: useGitignore };\n }\n\n const transformFile = await list({\n message: 'Select a codemod transform to run:',\n choices: transformFiles.map((file) => ({ name: file, value: file })),\n });\n\n const targetPath = await input({\n message: 'Enter the target directory or file path to run codemod on:',\n validate: (value) => value.trim() !== '' || 'Target path cannot be empty',\n });\n\n const dry = await confirm({\n message: 'Run in dry mode (no changes written to files)?',\n default: true,\n });\n\n const print = await confirm({\n message: 'Print transformed source to console?',\n default: false,\n });\n\n const ignorePattern = await input({\n message: 'Enter ignore pattern(s) (comma separated) or leave empty:',\n validate: (value) => true,\n });\n\n const gitignore = await confirm({\n message: 'Respect .gitignore files?',\n default: true,\n });\n\n return { transformFile, targetPath, dry, print, ignorePattern, gitignore };\n}\n\nexport default getOptions;\n","import { promises as fs } from 'fs';\nimport path from 'path';\n\ninterface TransformModule {\n default: {\n default: unknown;\n };\n}\n\nasync function loadTransformModules(transformsDir: string) {\n let transformModules: Record<string, unknown> = {};\n\n const files = await fs.readdir(transformsDir);\n const transformFiles = await Promise.all(\n files\n .filter((file) => file.endsWith('.js'))\n .map(async (file) => {\n const transformPath = path.join(transformsDir, file);\n const transformModule = (await import(transformPath)) as TransformModule;\n transformModules = { ...transformModules, [file]: transformModule.default.default };\n return file.replace('.js', '');\n }),\n );\n\n return { transformModules, transformFiles };\n}\n\nexport default loadTransformModules;\n","import fs from 'node:fs/promises';\n\nimport path from 'path';\n\nconst REPORT_PATH = path.resolve(process.cwd(), 'codemod-report.txt');\n\nconst reportManualReview = async (filePath: string, message: string): Promise<void> => {\n const lineMatch = /at line (\\d+)/u.exec(message);\n const lineNumber = lineMatch?.[1];\n\n const cleanMessage = message.replace(/ at line \\d+/u, '');\n const lineInfo = lineNumber ? `:${lineNumber}` : '';\n\n await fs.appendFile(REPORT_PATH, `[${filePath}${lineInfo}] ${cleanMessage}\\n`, 'utf8');\n};\n\nexport default reportManualReview;\n","#!/usr/bin/env node\nimport { runCodemod } from './runCodemod';\n\nvoid runCodemod();\n"],"mappings":"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","names":["fs","path","transformFile","targetPath","dry","print","ignorePattern","gitignore","path","path","fs"]}
|
|
@@ -1,10 +1,6 @@
|
|
|
1
1
|
import { FileInfo, API, Options } from 'jscodeshift';
|
|
2
2
|
|
|
3
3
|
declare const parser = "tsx";
|
|
4
|
-
declare const packageRequirements: {
|
|
5
|
-
name: string;
|
|
6
|
-
version: string;
|
|
7
|
-
}[];
|
|
8
4
|
/**
|
|
9
5
|
* This transform function modifies the Button and ActionButton components from the @transferwise/components library.
|
|
10
6
|
* It updates the ActionButton component to use the Button component with specific attributes and mappings.
|
|
@@ -17,4 +13,4 @@ declare const packageRequirements: {
|
|
|
17
13
|
*/
|
|
18
14
|
declare const transformer: (file: FileInfo, api: API, options: Options) => string;
|
|
19
15
|
|
|
20
|
-
export { transformer as default,
|
|
16
|
+
export { transformer as default, parser };
|