@wise/wds-codemods 0.0.1-experimental-6c2101b
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 +5 -0
- package/.changeset/config.json +13 -0
- package/.github/CODEOWNERS +1 -0
- package/.github/actions/bootstrap/action.yml +49 -0
- package/.github/actions/commitlint/action.yml +27 -0
- package/.github/actions/test/action.yml +23 -0
- package/.github/workflows/cd-cd.yml +127 -0
- package/.github/workflows/renovate.yml +16 -0
- package/.husky/commit-msg +1 -0
- package/.husky/pre-commit +1 -0
- package/.nvmrc +1 -0
- package/.prettierignore +1 -0
- package/.prettierrc.js +5 -0
- package/README.md +184 -0
- package/babel.config.js +28 -0
- package/codemod-report.md +81 -0
- package/commitlint.config.js +3 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +2448 -0
- package/dist/index.js.map +1 -0
- package/dist/transforms/button.d.ts +20 -0
- package/dist/transforms/button.js +640 -0
- package/dist/transforms/button.js.map +1 -0
- package/eslint.config.js +15 -0
- package/jest.config.js +9 -0
- package/mkdocs.yml +4 -0
- package/package.json +68 -0
- package/renovate.json +9 -0
- package/scripts/build.sh +10 -0
- package/src/__tests__/runCodemod.test.ts +109 -0
- package/src/index.ts +4 -0
- package/src/runCodemod.ts +149 -0
- package/src/transforms/button/__tests__/button.test.tsx +175 -0
- package/src/transforms/button/button.ts +453 -0
- package/src/transforms/helpers/__tests__/createTestTransform.test.ts +27 -0
- package/src/transforms/helpers/__tests__/hasImport.test.ts +52 -0
- package/src/transforms/helpers/__tests__/iconUtils.test.ts +207 -0
- package/src/transforms/helpers/__tests__/jsxElementUtils.test.ts +130 -0
- package/src/transforms/helpers/__tests__/jsxReportingUtils.test.ts +265 -0
- package/src/transforms/helpers/__tests__/packageValidation.test.ts +45 -0
- package/src/transforms/helpers/createTestTransform.ts +59 -0
- package/src/transforms/helpers/hasImport.ts +60 -0
- package/src/transforms/helpers/iconUtils.ts +87 -0
- package/src/transforms/helpers/index.ts +5 -0
- package/src/transforms/helpers/jsxElementUtils.ts +67 -0
- package/src/transforms/helpers/jsxReportingUtils.ts +224 -0
- package/src/transforms/helpers/packageValidation.ts +53 -0
- package/src/utils/__tests__/getOptions.test.ts +219 -0
- package/src/utils/__tests__/handleError.test.ts +18 -0
- package/src/utils/__tests__/hasPackageVersion.test.ts +191 -0
- package/src/utils/__tests__/loadTransformModules.test.ts +51 -0
- package/src/utils/__tests__/reportManualReview.test.ts +42 -0
- package/src/utils/getOptions.ts +78 -0
- package/src/utils/handleError.ts +6 -0
- package/src/utils/hasPackageVersion.ts +482 -0
- package/src/utils/index.ts +4 -0
- package/src/utils/loadTransformModules.ts +28 -0
- package/src/utils/reportManualReview.ts +17 -0
- package/test-button.tsx +230 -0
- package/test-file.js +2 -0
- package/tsconfig.json +14 -0
- package/tsup.config.js +13 -0
|
@@ -0,0 +1,482 @@
|
|
|
1
|
+
import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
|
|
4
|
+
import semver from 'semver';
|
|
5
|
+
|
|
6
|
+
interface PackageJson {
|
|
7
|
+
name?: string;
|
|
8
|
+
version?: string;
|
|
9
|
+
dependencies?: Record<string, string>;
|
|
10
|
+
devDependencies?: Record<string, string>;
|
|
11
|
+
peerDependencies?: Record<string, string>;
|
|
12
|
+
optionalDependencies?: Record<string, string>;
|
|
13
|
+
workspaces?: string[] | { packages: string[] };
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
interface CheckOptions {
|
|
17
|
+
silent: boolean;
|
|
18
|
+
limitedScope: boolean;
|
|
19
|
+
checkType?: 'dependencies' | 'nodeModules';
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export const packageVersionCache = new Map<string, boolean>();
|
|
23
|
+
|
|
24
|
+
export default function hasPackageVersion(
|
|
25
|
+
packageName: string,
|
|
26
|
+
versionRequirement: string,
|
|
27
|
+
): boolean {
|
|
28
|
+
return hasPackageVersionFromPath(packageName, versionRequirement, process.cwd(), false);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function hasPackageVersionFromPath(
|
|
32
|
+
packageName: string,
|
|
33
|
+
versionRequirement: string,
|
|
34
|
+
startPath: string,
|
|
35
|
+
isMonorepo = false,
|
|
36
|
+
): boolean {
|
|
37
|
+
const cacheKey = `${packageName}@${versionRequirement}@${startPath}@${isMonorepo}`;
|
|
38
|
+
|
|
39
|
+
if (packageVersionCache.has(cacheKey)) {
|
|
40
|
+
return packageVersionCache.get(cacheKey)!;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Find project root and check for workspace packages
|
|
44
|
+
const projectRoot = findProjectRoot(startPath);
|
|
45
|
+
const workspacePackages = projectRoot ? getWorkspacePackages(projectRoot) : [];
|
|
46
|
+
|
|
47
|
+
const result =
|
|
48
|
+
workspacePackages.length > 0
|
|
49
|
+
? checkWorkspacePackages(projectRoot!, packageName, versionRequirement, workspacePackages)
|
|
50
|
+
: isMonorepo
|
|
51
|
+
? checkMonorepoPackages(startPath, packageName, versionRequirement)
|
|
52
|
+
: checkSinglePackage(startPath, packageName, versionRequirement);
|
|
53
|
+
|
|
54
|
+
packageVersionCache.set(cacheKey, result);
|
|
55
|
+
return result;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function getWorkspacePackages(projectRoot: string): string[] {
|
|
59
|
+
const packages: string[] = [];
|
|
60
|
+
|
|
61
|
+
// Check package.json workspaces
|
|
62
|
+
const packageJsonPath = path.join(projectRoot, 'package.json');
|
|
63
|
+
if (existsSync(packageJsonPath)) {
|
|
64
|
+
try {
|
|
65
|
+
const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf8')) as PackageJson;
|
|
66
|
+
if (packageJson.workspaces) {
|
|
67
|
+
const workspaces = Array.isArray(packageJson.workspaces)
|
|
68
|
+
? packageJson.workspaces
|
|
69
|
+
: packageJson.workspaces.packages;
|
|
70
|
+
|
|
71
|
+
for (const pattern of workspaces) {
|
|
72
|
+
const searchPath = path.join(projectRoot, pattern.replace('/*', ''));
|
|
73
|
+
|
|
74
|
+
if (existsSync(searchPath) && statSync(searchPath).isDirectory()) {
|
|
75
|
+
if (pattern.endsWith('/*')) {
|
|
76
|
+
const entries = readdirSync(searchPath);
|
|
77
|
+
const matchedPackages = entries
|
|
78
|
+
.map((entry) => path.join(searchPath, entry))
|
|
79
|
+
.filter((p) => {
|
|
80
|
+
try {
|
|
81
|
+
return statSync(p).isDirectory() && existsSync(path.join(p, 'package.json'));
|
|
82
|
+
} catch {
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
packages.push(...matchedPackages);
|
|
87
|
+
} else if (existsSync(path.join(searchPath, 'package.json'))) {
|
|
88
|
+
packages.push(searchPath);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
} catch {
|
|
94
|
+
// Silent fallback
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Check pnpm-workspace.yaml if no packages found yet
|
|
99
|
+
if (packages.length === 0) {
|
|
100
|
+
const pnpmWorkspacePath = path.join(projectRoot, 'pnpm-workspace.yaml');
|
|
101
|
+
if (existsSync(pnpmWorkspacePath)) {
|
|
102
|
+
try {
|
|
103
|
+
const content = readFileSync(pnpmWorkspacePath, 'utf8');
|
|
104
|
+
const lines = content.split('\n');
|
|
105
|
+
let inPackagesSection = false;
|
|
106
|
+
const patterns: string[] = [];
|
|
107
|
+
|
|
108
|
+
for (const line of lines) {
|
|
109
|
+
const trimmedLine = line.trim();
|
|
110
|
+
|
|
111
|
+
if (trimmedLine === 'packages:') {
|
|
112
|
+
inPackagesSection = true;
|
|
113
|
+
} else if (inPackagesSection) {
|
|
114
|
+
if (trimmedLine && !line.startsWith(' ') && !line.startsWith('\t')) {
|
|
115
|
+
break;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (trimmedLine.startsWith('-')) {
|
|
119
|
+
const pattern = trimmedLine
|
|
120
|
+
.substring(1)
|
|
121
|
+
.trim()
|
|
122
|
+
.replace(/^['"]|['"]$/u, '');
|
|
123
|
+
if (pattern) {
|
|
124
|
+
patterns.push(pattern);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
for (const pattern of patterns) {
|
|
131
|
+
const searchPath = path.join(projectRoot, pattern.replace('/*', ''));
|
|
132
|
+
|
|
133
|
+
if (existsSync(searchPath) && statSync(searchPath).isDirectory()) {
|
|
134
|
+
if (pattern.endsWith('/*')) {
|
|
135
|
+
const entries = readdirSync(searchPath);
|
|
136
|
+
const matchedPackages = entries
|
|
137
|
+
.map((entry) => path.join(searchPath, entry))
|
|
138
|
+
.filter((p) => {
|
|
139
|
+
try {
|
|
140
|
+
return statSync(p).isDirectory() && existsSync(path.join(p, 'package.json'));
|
|
141
|
+
} catch {
|
|
142
|
+
return false;
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
packages.push(...matchedPackages);
|
|
146
|
+
} else if (existsSync(path.join(searchPath, 'package.json'))) {
|
|
147
|
+
packages.push(searchPath);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
} catch {
|
|
152
|
+
// Silent fallback
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return packages;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function checkWorkspacePackages(
|
|
161
|
+
projectRoot: string,
|
|
162
|
+
packageName: string,
|
|
163
|
+
versionRequirement: string,
|
|
164
|
+
workspacePaths: string[],
|
|
165
|
+
): boolean {
|
|
166
|
+
const foundPackages: string[] = [];
|
|
167
|
+
const notFoundPackages: string[] = [];
|
|
168
|
+
|
|
169
|
+
for (const workspacePath of workspacePaths) {
|
|
170
|
+
const packageDir = path.basename(workspacePath);
|
|
171
|
+
const found = checkPackageInDirectory(workspacePath, packageName, versionRequirement, {
|
|
172
|
+
silent: true,
|
|
173
|
+
limitedScope: true,
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
(found ? foundPackages : notFoundPackages).push(packageDir);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
console.log(`📁 Found ${workspacePaths.length} packages to check`);
|
|
180
|
+
|
|
181
|
+
if (foundPackages.length > 0) {
|
|
182
|
+
console.log(
|
|
183
|
+
`✅ Found ${packageName} in ${foundPackages.length}/${workspacePaths.length} packages:`,
|
|
184
|
+
);
|
|
185
|
+
console.log(` 📦 ${foundPackages.join(', ')}`);
|
|
186
|
+
|
|
187
|
+
if (notFoundPackages.length > 0) {
|
|
188
|
+
console.log(`❌ Not found in ${notFoundPackages.length} packages:`);
|
|
189
|
+
console.log(` 📦 ${notFoundPackages.join(', ')}`);
|
|
190
|
+
}
|
|
191
|
+
} else {
|
|
192
|
+
console.log(`❌ Package ${packageName} not found in any of ${workspacePaths.length} packages`);
|
|
193
|
+
if (notFoundPackages.length > 0) {
|
|
194
|
+
console.log(` 📦 Checked: ${notFoundPackages.join(', ')}`);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
return foundPackages.length > 0;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function checkMonorepoPackages(
|
|
202
|
+
packagesPath: string,
|
|
203
|
+
packageName: string,
|
|
204
|
+
versionRequirement: string,
|
|
205
|
+
): boolean {
|
|
206
|
+
console.log(`📦 Checking monorepo packages in: ${packagesPath}`);
|
|
207
|
+
try {
|
|
208
|
+
if (!existsSync(packagesPath)) {
|
|
209
|
+
return false;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const packageDirs = readdirSync(packagesPath).filter((entry) =>
|
|
213
|
+
statSync(path.join(packagesPath, entry)).isDirectory(),
|
|
214
|
+
);
|
|
215
|
+
|
|
216
|
+
console.log(`📁 Found ${packageDirs.length} packages to check`);
|
|
217
|
+
|
|
218
|
+
const foundPackages: string[] = [];
|
|
219
|
+
const notFoundPackages: string[] = [];
|
|
220
|
+
|
|
221
|
+
for (const entry of packageDirs) {
|
|
222
|
+
const packageDir = path.join(packagesPath, entry);
|
|
223
|
+
const found = checkPackageInDirectory(packageDir, packageName, versionRequirement, {
|
|
224
|
+
silent: true,
|
|
225
|
+
limitedScope: true,
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
(found ? foundPackages : notFoundPackages).push(entry);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
if (foundPackages.length > 0) {
|
|
232
|
+
console.log(
|
|
233
|
+
`✅ Found ${packageName} in ${foundPackages.length}/${packageDirs.length} packages:`,
|
|
234
|
+
);
|
|
235
|
+
console.log(` 📦 ${foundPackages.join(', ')}`);
|
|
236
|
+
|
|
237
|
+
if (notFoundPackages.length > 0) {
|
|
238
|
+
console.log(`❌ Not found in ${notFoundPackages.length} packages:`);
|
|
239
|
+
console.log(` 📦 ${notFoundPackages.join(', ')}`);
|
|
240
|
+
}
|
|
241
|
+
} else {
|
|
242
|
+
console.log(`❌ Package ${packageName} not found in any of ${packageDirs.length} packages`);
|
|
243
|
+
console.log(` 📦 Checked: ${notFoundPackages.join(', ')}`);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
return foundPackages.length > 0;
|
|
247
|
+
} catch (error) {
|
|
248
|
+
console.log(`⚠️ Error checking monorepo packages:`, error);
|
|
249
|
+
return false;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
function checkSinglePackage(
|
|
254
|
+
packagePath: string,
|
|
255
|
+
packageName: string,
|
|
256
|
+
versionRequirement: string,
|
|
257
|
+
): boolean {
|
|
258
|
+
const checks = [
|
|
259
|
+
{ type: 'dependencies' as const, msg: 'package.json dependencies' },
|
|
260
|
+
{ type: 'nodeModules' as const, msg: 'node_modules' },
|
|
261
|
+
];
|
|
262
|
+
|
|
263
|
+
for (const { type } of checks) {
|
|
264
|
+
if (
|
|
265
|
+
checkPackageInDirectory(packagePath, packageName, versionRequirement, {
|
|
266
|
+
silent: true,
|
|
267
|
+
limitedScope: false,
|
|
268
|
+
checkType: type,
|
|
269
|
+
})
|
|
270
|
+
) {
|
|
271
|
+
return true;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
return false;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
function checkPackageInDirectory(
|
|
279
|
+
startPath: string,
|
|
280
|
+
packageName: string,
|
|
281
|
+
versionRequirement: string,
|
|
282
|
+
options: CheckOptions,
|
|
283
|
+
): boolean {
|
|
284
|
+
const { checkType } = options;
|
|
285
|
+
const packageJsonResult = checkDirectDependencies(
|
|
286
|
+
startPath,
|
|
287
|
+
packageName,
|
|
288
|
+
versionRequirement,
|
|
289
|
+
options,
|
|
290
|
+
);
|
|
291
|
+
|
|
292
|
+
if (checkType === 'dependencies') return packageJsonResult;
|
|
293
|
+
if (packageJsonResult) return true;
|
|
294
|
+
if (checkType === 'nodeModules')
|
|
295
|
+
return checkNodeModules(startPath, packageName, versionRequirement, options);
|
|
296
|
+
|
|
297
|
+
return checkNodeModules(startPath, packageName, versionRequirement, options);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
function checkDirectDependencies(
|
|
301
|
+
startPath: string,
|
|
302
|
+
packageName: string,
|
|
303
|
+
versionRequirement: string,
|
|
304
|
+
{ limitedScope }: CheckOptions,
|
|
305
|
+
): boolean {
|
|
306
|
+
const checker = (dir: string) =>
|
|
307
|
+
checkPackageJsonInDirectory(dir, packageName, versionRequirement);
|
|
308
|
+
return limitedScope ? checker(startPath) : walkDirectoryTree(startPath, checker);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
function checkNodeModules(
|
|
312
|
+
startPath: string,
|
|
313
|
+
packageName: string,
|
|
314
|
+
versionRequirement: string,
|
|
315
|
+
{ limitedScope }: CheckOptions,
|
|
316
|
+
): boolean {
|
|
317
|
+
const checker = (dir: string) =>
|
|
318
|
+
checkStandardNodeModules(dir, packageName, versionRequirement) ||
|
|
319
|
+
checkPnpmNodeModules(dir, packageName, versionRequirement);
|
|
320
|
+
|
|
321
|
+
return limitedScope ? checker(startPath) : walkDirectoryTree(startPath, checker);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
function walkDirectoryTree(startPath: string, checkFunction: (dir: string) => boolean): boolean {
|
|
325
|
+
let currentDir =
|
|
326
|
+
existsSync(startPath) && statSync(startPath).isFile() ? path.dirname(startPath) : startPath;
|
|
327
|
+
|
|
328
|
+
currentDir = path.resolve(currentDir);
|
|
329
|
+
|
|
330
|
+
const { root } = path.parse(currentDir);
|
|
331
|
+
let dirCount = 0;
|
|
332
|
+
let previousDir = '';
|
|
333
|
+
|
|
334
|
+
while (currentDir !== root && currentDir !== previousDir && dirCount < 10) {
|
|
335
|
+
dirCount += 1;
|
|
336
|
+
|
|
337
|
+
if (checkFunction(currentDir)) {
|
|
338
|
+
return true;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
previousDir = currentDir;
|
|
342
|
+
currentDir = path.dirname(currentDir);
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
return false;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
function checkPackageJsonInDirectory(
|
|
349
|
+
dir: string,
|
|
350
|
+
packageName: string,
|
|
351
|
+
versionRequirement: string,
|
|
352
|
+
): boolean {
|
|
353
|
+
const packageJsonPath = path.join(dir, 'package.json');
|
|
354
|
+
|
|
355
|
+
if (!existsSync(packageJsonPath)) return false;
|
|
356
|
+
|
|
357
|
+
try {
|
|
358
|
+
const packageJson: PackageJson = JSON.parse(
|
|
359
|
+
readFileSync(packageJsonPath, 'utf8'),
|
|
360
|
+
) as PackageJson;
|
|
361
|
+
const allDeps = {
|
|
362
|
+
...(packageJson.dependencies ?? {}),
|
|
363
|
+
...(packageJson.devDependencies ?? {}),
|
|
364
|
+
...(packageJson.peerDependencies ?? {}),
|
|
365
|
+
...(packageJson.optionalDependencies ?? {}),
|
|
366
|
+
};
|
|
367
|
+
|
|
368
|
+
const installedVersion = allDeps[packageName];
|
|
369
|
+
return installedVersion ? isVersionSatisfied(installedVersion, versionRequirement) : false;
|
|
370
|
+
} catch {
|
|
371
|
+
return false;
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
function isVersionSatisfied(installedVersion: string, versionRequirement: string): boolean {
|
|
376
|
+
if (semver.valid(installedVersion) && semver.satisfies(installedVersion, versionRequirement)) {
|
|
377
|
+
return true;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
const cleanVersion = semver.coerce(installedVersion);
|
|
381
|
+
return cleanVersion ? semver.satisfies(cleanVersion.version, versionRequirement) : false;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
function checkStandardNodeModules(
|
|
385
|
+
baseDir: string,
|
|
386
|
+
packageName: string,
|
|
387
|
+
versionRequirement: string,
|
|
388
|
+
): boolean {
|
|
389
|
+
const packagePath = path.join(baseDir, 'node_modules', packageName, 'package.json');
|
|
390
|
+
if (!existsSync(packagePath)) return false;
|
|
391
|
+
|
|
392
|
+
try {
|
|
393
|
+
const packageJson: PackageJson = JSON.parse(readFileSync(packagePath, 'utf8')) as PackageJson;
|
|
394
|
+
return packageJson.version ? semver.satisfies(packageJson.version, versionRequirement) : false;
|
|
395
|
+
} catch {
|
|
396
|
+
return false;
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
function checkPnpmNodeModules(
|
|
401
|
+
baseDir: string,
|
|
402
|
+
packageName: string,
|
|
403
|
+
versionRequirement: string,
|
|
404
|
+
): boolean {
|
|
405
|
+
const pnpmDir = path.join(baseDir, 'node_modules', '.pnpm');
|
|
406
|
+
if (!existsSync(pnpmDir)) return false;
|
|
407
|
+
|
|
408
|
+
try {
|
|
409
|
+
const entries = readdirSync(pnpmDir);
|
|
410
|
+
|
|
411
|
+
for (const entry of entries) {
|
|
412
|
+
if (entry.startsWith(packageName.replace('/', '+')) || entry.includes(`${packageName}@`)) {
|
|
413
|
+
const packagePath = path.join(pnpmDir, entry, 'node_modules', packageName, 'package.json');
|
|
414
|
+
|
|
415
|
+
if (existsSync(packagePath)) {
|
|
416
|
+
const packageJson: PackageJson = JSON.parse(
|
|
417
|
+
readFileSync(packagePath, 'utf8'),
|
|
418
|
+
) as PackageJson;
|
|
419
|
+
if (packageJson.version && semver.satisfies(packageJson.version, versionRequirement)) {
|
|
420
|
+
return true;
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
} catch {
|
|
426
|
+
// Silent
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
return false;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
export function findClosestGitignore(startPath: string): string | null {
|
|
433
|
+
let currentDir =
|
|
434
|
+
existsSync(startPath) && statSync(startPath).isFile() ? path.dirname(startPath) : startPath;
|
|
435
|
+
|
|
436
|
+
currentDir = path.resolve(currentDir);
|
|
437
|
+
|
|
438
|
+
const { root } = path.parse(currentDir);
|
|
439
|
+
let dirCount = 0;
|
|
440
|
+
let previousDir = '';
|
|
441
|
+
|
|
442
|
+
while (currentDir !== root && currentDir !== previousDir && dirCount < 10) {
|
|
443
|
+
dirCount += 1;
|
|
444
|
+
const gitignorePath = path.join(currentDir, '.gitignore');
|
|
445
|
+
|
|
446
|
+
if (existsSync(gitignorePath)) {
|
|
447
|
+
return gitignorePath;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
previousDir = currentDir;
|
|
451
|
+
currentDir = path.dirname(currentDir);
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
return null;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
export function findProjectRoot(startPath: string): string | null {
|
|
458
|
+
let currentDir =
|
|
459
|
+
existsSync(startPath) && statSync(startPath).isFile() ? path.dirname(startPath) : startPath;
|
|
460
|
+
|
|
461
|
+
currentDir = path.resolve(currentDir);
|
|
462
|
+
|
|
463
|
+
const { root } = path.parse(currentDir);
|
|
464
|
+
let dirCount = 0;
|
|
465
|
+
let previousDir = '';
|
|
466
|
+
|
|
467
|
+
while (currentDir !== root && currentDir !== previousDir && dirCount < 10) {
|
|
468
|
+
dirCount += 1;
|
|
469
|
+
const packageJsonPath = path.join(currentDir, 'package.json');
|
|
470
|
+
const gitPath = path.join(currentDir, '.git');
|
|
471
|
+
const pnpmWorkspacePath = path.join(currentDir, 'pnpm-workspace.yaml');
|
|
472
|
+
|
|
473
|
+
if (existsSync(packageJsonPath) || existsSync(gitPath) || existsSync(pnpmWorkspacePath)) {
|
|
474
|
+
return currentDir;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
previousDir = currentDir;
|
|
478
|
+
currentDir = path.dirname(currentDir);
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
return null;
|
|
482
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { promises as fs } from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
|
|
4
|
+
interface TransformModule {
|
|
5
|
+
default: {
|
|
6
|
+
default: unknown;
|
|
7
|
+
};
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
async function loadTransformModules(transformsDir: string) {
|
|
11
|
+
let transformModules: Record<string, unknown> = {};
|
|
12
|
+
|
|
13
|
+
const files = await fs.readdir(transformsDir);
|
|
14
|
+
const transformFiles = await Promise.all(
|
|
15
|
+
files
|
|
16
|
+
.filter((file) => file.endsWith('.js'))
|
|
17
|
+
.map(async (file) => {
|
|
18
|
+
const transformPath = path.join(transformsDir, file);
|
|
19
|
+
const transformModule = (await import(transformPath)) as TransformModule;
|
|
20
|
+
transformModules = { ...transformModules, [file]: transformModule.default.default };
|
|
21
|
+
return file.replace('.js', '');
|
|
22
|
+
}),
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
return { transformModules, transformFiles };
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export default loadTransformModules;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
|
|
3
|
+
import path from 'path';
|
|
4
|
+
|
|
5
|
+
const REPORT_PATH = path.resolve(process.cwd(), 'codemod-report.txt');
|
|
6
|
+
|
|
7
|
+
const reportManualReview = async (filePath: string, message: string): Promise<void> => {
|
|
8
|
+
const lineMatch = /at line (\d+)/u.exec(message);
|
|
9
|
+
const lineNumber = lineMatch?.[1];
|
|
10
|
+
|
|
11
|
+
const cleanMessage = message.replace(/ at line \d+/u, '');
|
|
12
|
+
const lineInfo = lineNumber ? `:${lineNumber}` : '';
|
|
13
|
+
|
|
14
|
+
await fs.appendFile(REPORT_PATH, `[${filePath}${lineInfo}] ${cleanMessage}\n`, 'utf8');
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export default reportManualReview;
|