react-doctor 0.2.0 → 0.2.1
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/cli.js +41 -20
- package/dist/index.d.ts +2 -1
- package/dist/index.js +40 -19
- package/package.json +5 -5
package/dist/cli.js
CHANGED
|
@@ -101,12 +101,31 @@ const IGNORED_DIRECTORIES = new Set([
|
|
|
101
101
|
"out",
|
|
102
102
|
"storybook-static"
|
|
103
103
|
]);
|
|
104
|
+
const IGNORABLE_READDIR_ERROR_CODES = new Set([
|
|
105
|
+
"EACCES",
|
|
106
|
+
"EPERM",
|
|
107
|
+
"ENOENT",
|
|
108
|
+
"ENOTDIR"
|
|
109
|
+
]);
|
|
110
|
+
const isIgnorableReaddirError = (error) => {
|
|
111
|
+
if (typeof error !== "object" || error === null) return false;
|
|
112
|
+
const errorCode = error.code;
|
|
113
|
+
return typeof errorCode === "string" && IGNORABLE_READDIR_ERROR_CODES.has(errorCode);
|
|
114
|
+
};
|
|
115
|
+
const readDirectoryEntries = (directoryPath) => {
|
|
116
|
+
try {
|
|
117
|
+
return fs.readdirSync(directoryPath, { withFileTypes: true });
|
|
118
|
+
} catch (error) {
|
|
119
|
+
if (isIgnorableReaddirError(error)) return [];
|
|
120
|
+
throw error;
|
|
121
|
+
}
|
|
122
|
+
};
|
|
104
123
|
const countSourceFilesViaFilesystem = (rootDirectory) => {
|
|
105
124
|
let count = 0;
|
|
106
125
|
const stack = [rootDirectory];
|
|
107
126
|
while (stack.length > 0) {
|
|
108
127
|
const currentDirectory = stack.pop();
|
|
109
|
-
const entries =
|
|
128
|
+
const entries = readDirectoryEntries(currentDirectory);
|
|
110
129
|
for (const entry of entries) {
|
|
111
130
|
if (entry.isDirectory()) {
|
|
112
131
|
if (!entry.name.startsWith(".") && !IGNORED_DIRECTORIES.has(entry.name)) stack.push(path.join(currentDirectory, entry.name));
|
|
@@ -141,7 +160,7 @@ const readPackageJsonUncached = (packageJsonPath) => {
|
|
|
141
160
|
if (error instanceof SyntaxError) return {};
|
|
142
161
|
if (error instanceof Error && "code" in error) {
|
|
143
162
|
const { code } = error;
|
|
144
|
-
if (code === "EISDIR" || code === "EACCES") return {};
|
|
163
|
+
if (code === "EISDIR" || code === "EACCES" || code === "EPERM" || code === "ENOENT") return {};
|
|
145
164
|
}
|
|
146
165
|
throw error;
|
|
147
166
|
}
|
|
@@ -512,6 +531,13 @@ const getDependencyDeclaration = ({ packageJson, packageName, sections }) => {
|
|
|
512
531
|
version: null
|
|
513
532
|
};
|
|
514
533
|
};
|
|
534
|
+
const isDirectory = (directoryPath) => {
|
|
535
|
+
try {
|
|
536
|
+
return fs.statSync(directoryPath).isDirectory();
|
|
537
|
+
} catch {
|
|
538
|
+
return false;
|
|
539
|
+
}
|
|
540
|
+
};
|
|
515
541
|
const NX_PROJECT_DISCOVERY_DIRS = [
|
|
516
542
|
"apps",
|
|
517
543
|
"libs",
|
|
@@ -522,8 +548,8 @@ const getNxWorkspaceDirectories = (rootDirectory) => {
|
|
|
522
548
|
const collected = [];
|
|
523
549
|
for (const candidate of NX_PROJECT_DISCOVERY_DIRS) {
|
|
524
550
|
const candidatePath = path.join(rootDirectory, candidate);
|
|
525
|
-
if (!
|
|
526
|
-
for (const entry of
|
|
551
|
+
if (!isDirectory(candidatePath)) continue;
|
|
552
|
+
for (const entry of readDirectoryEntries(candidatePath)) {
|
|
527
553
|
if (!entry.isDirectory()) continue;
|
|
528
554
|
const projectDirectory = path.join(candidatePath, entry.name);
|
|
529
555
|
if (isFile(path.join(projectDirectory, "project.json")) || isFile(path.join(projectDirectory, "package.json"))) collected.push(`${candidate}/${entry.name}`);
|
|
@@ -565,17 +591,17 @@ const resolveWorkspaceDirectories = (rootDirectory, pattern) => {
|
|
|
565
591
|
const cleanPattern = pattern.replace(/["']/g, "").replace(/\/\*\*$/, "/*");
|
|
566
592
|
if (!cleanPattern.includes("*")) {
|
|
567
593
|
const directoryPath = path.join(rootDirectory, cleanPattern);
|
|
568
|
-
if (
|
|
594
|
+
if (isDirectory(directoryPath) && isFile(path.join(directoryPath, "package.json"))) return [directoryPath];
|
|
569
595
|
return [];
|
|
570
596
|
}
|
|
571
597
|
const wildcardIndex = cleanPattern.indexOf("*");
|
|
572
598
|
const baseDirectory = path.join(rootDirectory, cleanPattern.slice(0, wildcardIndex));
|
|
573
599
|
const suffixAfterWildcard = cleanPattern.slice(wildcardIndex + 1);
|
|
574
|
-
if (!
|
|
600
|
+
if (!isDirectory(baseDirectory)) return [];
|
|
575
601
|
const resolved = [];
|
|
576
|
-
for (const entry of
|
|
577
|
-
const entryPath = path.join(baseDirectory, entry, suffixAfterWildcard);
|
|
578
|
-
if (
|
|
602
|
+
for (const entry of readDirectoryEntries(baseDirectory)) {
|
|
603
|
+
const entryPath = path.join(baseDirectory, entry.name, suffixAfterWildcard);
|
|
604
|
+
if (isDirectory(entryPath) && isFile(path.join(entryPath, "package.json"))) resolved.push(entryPath);
|
|
579
605
|
}
|
|
580
606
|
return resolved;
|
|
581
607
|
};
|
|
@@ -820,7 +846,7 @@ const discoverReactSubprojectsByFilesystem = (rootDirectory) => {
|
|
|
820
846
|
});
|
|
821
847
|
}
|
|
822
848
|
}
|
|
823
|
-
const entries =
|
|
849
|
+
const entries = readDirectoryEntries(currentDirectory).toSorted((firstEntry, secondEntry) => firstEntry.name.localeCompare(secondEntry.name));
|
|
824
850
|
for (const entry of entries) {
|
|
825
851
|
if (!entry.isDirectory() || entry.name.startsWith(".") || IGNORED_DIRECTORIES.has(entry.name)) continue;
|
|
826
852
|
pendingDirectories.push(path.join(currentDirectory, entry.name));
|
|
@@ -829,7 +855,7 @@ const discoverReactSubprojectsByFilesystem = (rootDirectory) => {
|
|
|
829
855
|
return packages;
|
|
830
856
|
};
|
|
831
857
|
const discoverReactSubprojects = (rootDirectory) => {
|
|
832
|
-
if (!
|
|
858
|
+
if (!isDirectory(rootDirectory)) return [];
|
|
833
859
|
const manifestPackages = listManifestWorkspacePackages(rootDirectory);
|
|
834
860
|
if (manifestPackages.length > 0) return manifestPackages;
|
|
835
861
|
return discoverReactSubprojectsByFilesystem(rootDirectory);
|
|
@@ -4289,12 +4315,7 @@ const findFilesWithDisableDirectivesViaFilesystem = (rootDirectory, includePaths
|
|
|
4289
4315
|
while (stack.length > 0) {
|
|
4290
4316
|
const current = stack.pop();
|
|
4291
4317
|
if (current === void 0) continue;
|
|
4292
|
-
|
|
4293
|
-
try {
|
|
4294
|
-
entries = fs.readdirSync(current, { withFileTypes: true });
|
|
4295
|
-
} catch {
|
|
4296
|
-
continue;
|
|
4297
|
-
}
|
|
4318
|
+
const entries = readDirectoryEntries(current);
|
|
4298
4319
|
for (const entry of entries) {
|
|
4299
4320
|
if (entry.isDirectory()) {
|
|
4300
4321
|
if (entry.name.startsWith(".") || IGNORED_DIRECTORIES.has(entry.name)) continue;
|
|
@@ -4426,7 +4447,7 @@ const resolveConfigRootDir = (config, configSourceDirectory) => {
|
|
|
4426
4447
|
if (trimmedRootDir.length === 0) return null;
|
|
4427
4448
|
const resolvedRootDir = path.isAbsolute(trimmedRootDir) ? trimmedRootDir : path.resolve(configSourceDirectory, trimmedRootDir);
|
|
4428
4449
|
if (resolvedRootDir === configSourceDirectory) return null;
|
|
4429
|
-
if (!
|
|
4450
|
+
if (!isDirectory(resolvedRootDir)) {
|
|
4430
4451
|
logger.warn(`react-doctor config "rootDir" points to "${rawRootDir}" (resolved to ${resolvedRootDir}), which is not a directory. Ignoring.`);
|
|
4431
4452
|
return null;
|
|
4432
4453
|
}
|
|
@@ -4452,7 +4473,7 @@ const listSourceFilesViaFilesystem = (rootDirectory) => {
|
|
|
4452
4473
|
const stack = [rootDirectory];
|
|
4453
4474
|
while (stack.length > 0) {
|
|
4454
4475
|
const currentDirectory = stack.pop();
|
|
4455
|
-
const entries =
|
|
4476
|
+
const entries = readDirectoryEntries(currentDirectory);
|
|
4456
4477
|
for (const entry of entries) {
|
|
4457
4478
|
const absolutePath = path.join(currentDirectory, entry.name);
|
|
4458
4479
|
if (entry.isDirectory()) {
|
|
@@ -5977,7 +5998,7 @@ const CI_ENVIRONMENT_VARIABLES = [
|
|
|
5977
5998
|
const isCiEnvironment = () => CI_ENVIRONMENT_VARIABLES.some((envVariable) => Boolean(process.env[envVariable])) || process.env.CI === "true";
|
|
5978
5999
|
//#endregion
|
|
5979
6000
|
//#region src/cli/utils/version.ts
|
|
5980
|
-
const VERSION = "0.2.
|
|
6001
|
+
const VERSION = "0.2.1";
|
|
5981
6002
|
//#endregion
|
|
5982
6003
|
//#region src/cli/utils/json-mode.ts
|
|
5983
6004
|
let context = null;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
1
2
|
//#region ../types/dist/index.d.ts
|
|
2
3
|
//#region src/config.d.ts
|
|
3
4
|
type FailOnLevel = "error" | "warning" | "none";
|
|
@@ -401,7 +402,7 @@ declare class AmbiguousProjectError extends ReactDoctorError {
|
|
|
401
402
|
constructor(directory: string, candidates: readonly string[], options?: ErrorOptions);
|
|
402
403
|
}
|
|
403
404
|
declare const isReactDoctorError: (value: unknown) => value is ReactDoctorError; //#endregion
|
|
404
|
-
//#region src/utils/is-
|
|
405
|
+
//#region src/utils/is-directory.d.ts
|
|
405
406
|
//#endregion
|
|
406
407
|
//#region ../core/dist/index.d.ts
|
|
407
408
|
//#endregion
|
package/dist/index.js
CHANGED
|
@@ -113,12 +113,31 @@ const IGNORED_DIRECTORIES = new Set([
|
|
|
113
113
|
"out",
|
|
114
114
|
"storybook-static"
|
|
115
115
|
]);
|
|
116
|
+
const IGNORABLE_READDIR_ERROR_CODES = new Set([
|
|
117
|
+
"EACCES",
|
|
118
|
+
"EPERM",
|
|
119
|
+
"ENOENT",
|
|
120
|
+
"ENOTDIR"
|
|
121
|
+
]);
|
|
122
|
+
const isIgnorableReaddirError = (error) => {
|
|
123
|
+
if (typeof error !== "object" || error === null) return false;
|
|
124
|
+
const errorCode = error.code;
|
|
125
|
+
return typeof errorCode === "string" && IGNORABLE_READDIR_ERROR_CODES.has(errorCode);
|
|
126
|
+
};
|
|
127
|
+
const readDirectoryEntries = (directoryPath) => {
|
|
128
|
+
try {
|
|
129
|
+
return fs.readdirSync(directoryPath, { withFileTypes: true });
|
|
130
|
+
} catch (error) {
|
|
131
|
+
if (isIgnorableReaddirError(error)) return [];
|
|
132
|
+
throw error;
|
|
133
|
+
}
|
|
134
|
+
};
|
|
116
135
|
const countSourceFilesViaFilesystem = (rootDirectory) => {
|
|
117
136
|
let count = 0;
|
|
118
137
|
const stack = [rootDirectory];
|
|
119
138
|
while (stack.length > 0) {
|
|
120
139
|
const currentDirectory = stack.pop();
|
|
121
|
-
const entries =
|
|
140
|
+
const entries = readDirectoryEntries(currentDirectory);
|
|
122
141
|
for (const entry of entries) {
|
|
123
142
|
if (entry.isDirectory()) {
|
|
124
143
|
if (!entry.name.startsWith(".") && !IGNORED_DIRECTORIES.has(entry.name)) stack.push(path.join(currentDirectory, entry.name));
|
|
@@ -156,7 +175,7 @@ const readPackageJsonUncached = (packageJsonPath) => {
|
|
|
156
175
|
if (error instanceof SyntaxError) return {};
|
|
157
176
|
if (error instanceof Error && "code" in error) {
|
|
158
177
|
const { code } = error;
|
|
159
|
-
if (code === "EISDIR" || code === "EACCES") return {};
|
|
178
|
+
if (code === "EISDIR" || code === "EACCES" || code === "EPERM" || code === "ENOENT") return {};
|
|
160
179
|
}
|
|
161
180
|
throw error;
|
|
162
181
|
}
|
|
@@ -527,6 +546,13 @@ const getDependencyDeclaration = ({ packageJson, packageName, sections }) => {
|
|
|
527
546
|
version: null
|
|
528
547
|
};
|
|
529
548
|
};
|
|
549
|
+
const isDirectory = (directoryPath) => {
|
|
550
|
+
try {
|
|
551
|
+
return fs.statSync(directoryPath).isDirectory();
|
|
552
|
+
} catch {
|
|
553
|
+
return false;
|
|
554
|
+
}
|
|
555
|
+
};
|
|
530
556
|
const NX_PROJECT_DISCOVERY_DIRS = [
|
|
531
557
|
"apps",
|
|
532
558
|
"libs",
|
|
@@ -537,8 +563,8 @@ const getNxWorkspaceDirectories = (rootDirectory) => {
|
|
|
537
563
|
const collected = [];
|
|
538
564
|
for (const candidate of NX_PROJECT_DISCOVERY_DIRS) {
|
|
539
565
|
const candidatePath = path.join(rootDirectory, candidate);
|
|
540
|
-
if (!
|
|
541
|
-
for (const entry of
|
|
566
|
+
if (!isDirectory(candidatePath)) continue;
|
|
567
|
+
for (const entry of readDirectoryEntries(candidatePath)) {
|
|
542
568
|
if (!entry.isDirectory()) continue;
|
|
543
569
|
const projectDirectory = path.join(candidatePath, entry.name);
|
|
544
570
|
if (isFile(path.join(projectDirectory, "project.json")) || isFile(path.join(projectDirectory, "package.json"))) collected.push(`${candidate}/${entry.name}`);
|
|
@@ -580,17 +606,17 @@ const resolveWorkspaceDirectories = (rootDirectory, pattern) => {
|
|
|
580
606
|
const cleanPattern = pattern.replace(/["']/g, "").replace(/\/\*\*$/, "/*");
|
|
581
607
|
if (!cleanPattern.includes("*")) {
|
|
582
608
|
const directoryPath = path.join(rootDirectory, cleanPattern);
|
|
583
|
-
if (
|
|
609
|
+
if (isDirectory(directoryPath) && isFile(path.join(directoryPath, "package.json"))) return [directoryPath];
|
|
584
610
|
return [];
|
|
585
611
|
}
|
|
586
612
|
const wildcardIndex = cleanPattern.indexOf("*");
|
|
587
613
|
const baseDirectory = path.join(rootDirectory, cleanPattern.slice(0, wildcardIndex));
|
|
588
614
|
const suffixAfterWildcard = cleanPattern.slice(wildcardIndex + 1);
|
|
589
|
-
if (!
|
|
615
|
+
if (!isDirectory(baseDirectory)) return [];
|
|
590
616
|
const resolved = [];
|
|
591
|
-
for (const entry of
|
|
592
|
-
const entryPath = path.join(baseDirectory, entry, suffixAfterWildcard);
|
|
593
|
-
if (
|
|
617
|
+
for (const entry of readDirectoryEntries(baseDirectory)) {
|
|
618
|
+
const entryPath = path.join(baseDirectory, entry.name, suffixAfterWildcard);
|
|
619
|
+
if (isDirectory(entryPath) && isFile(path.join(entryPath, "package.json"))) resolved.push(entryPath);
|
|
594
620
|
}
|
|
595
621
|
return resolved;
|
|
596
622
|
};
|
|
@@ -835,7 +861,7 @@ const discoverReactSubprojectsByFilesystem = (rootDirectory) => {
|
|
|
835
861
|
});
|
|
836
862
|
}
|
|
837
863
|
}
|
|
838
|
-
const entries =
|
|
864
|
+
const entries = readDirectoryEntries(currentDirectory).toSorted((firstEntry, secondEntry) => firstEntry.name.localeCompare(secondEntry.name));
|
|
839
865
|
for (const entry of entries) {
|
|
840
866
|
if (!entry.isDirectory() || entry.name.startsWith(".") || IGNORED_DIRECTORIES.has(entry.name)) continue;
|
|
841
867
|
pendingDirectories.push(path.join(currentDirectory, entry.name));
|
|
@@ -844,7 +870,7 @@ const discoverReactSubprojectsByFilesystem = (rootDirectory) => {
|
|
|
844
870
|
return packages;
|
|
845
871
|
};
|
|
846
872
|
const discoverReactSubprojects = (rootDirectory) => {
|
|
847
|
-
if (!
|
|
873
|
+
if (!isDirectory(rootDirectory)) return [];
|
|
848
874
|
const manifestPackages = listManifestWorkspacePackages(rootDirectory);
|
|
849
875
|
if (manifestPackages.length > 0) return manifestPackages;
|
|
850
876
|
return discoverReactSubprojectsByFilesystem(rootDirectory);
|
|
@@ -4249,12 +4275,7 @@ const findFilesWithDisableDirectivesViaFilesystem = (rootDirectory, includePaths
|
|
|
4249
4275
|
while (stack.length > 0) {
|
|
4250
4276
|
const current = stack.pop();
|
|
4251
4277
|
if (current === void 0) continue;
|
|
4252
|
-
|
|
4253
|
-
try {
|
|
4254
|
-
entries = fs.readdirSync(current, { withFileTypes: true });
|
|
4255
|
-
} catch {
|
|
4256
|
-
continue;
|
|
4257
|
-
}
|
|
4278
|
+
const entries = readDirectoryEntries(current);
|
|
4258
4279
|
for (const entry of entries) {
|
|
4259
4280
|
if (entry.isDirectory()) {
|
|
4260
4281
|
if (entry.name.startsWith(".") || IGNORED_DIRECTORIES.has(entry.name)) continue;
|
|
@@ -4312,7 +4333,7 @@ const resolveConfigRootDir = (config, configSourceDirectory) => {
|
|
|
4312
4333
|
if (trimmedRootDir.length === 0) return null;
|
|
4313
4334
|
const resolvedRootDir = path.isAbsolute(trimmedRootDir) ? trimmedRootDir : path.resolve(configSourceDirectory, trimmedRootDir);
|
|
4314
4335
|
if (resolvedRootDir === configSourceDirectory) return null;
|
|
4315
|
-
if (!
|
|
4336
|
+
if (!isDirectory(resolvedRootDir)) {
|
|
4316
4337
|
logger.warn(`react-doctor config "rootDir" points to "${rawRootDir}" (resolved to ${resolvedRootDir}), which is not a directory. Ignoring.`);
|
|
4317
4338
|
return null;
|
|
4318
4339
|
}
|
|
@@ -4345,7 +4366,7 @@ const listSourceFilesViaFilesystem = (rootDirectory) => {
|
|
|
4345
4366
|
const stack = [rootDirectory];
|
|
4346
4367
|
while (stack.length > 0) {
|
|
4347
4368
|
const currentDirectory = stack.pop();
|
|
4348
|
-
const entries =
|
|
4369
|
+
const entries = readDirectoryEntries(currentDirectory);
|
|
4349
4370
|
for (const entry of entries) {
|
|
4350
4371
|
const absolutePath = path.join(currentDirectory, entry.name);
|
|
4351
4372
|
if (entry.isDirectory()) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-doctor",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "Diagnose and fix React codebases for security, performance, correctness, accessibility, bundle-size, and architecture issues",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"accessibility",
|
|
@@ -56,15 +56,15 @@
|
|
|
56
56
|
"picocolors": "^1.1.1",
|
|
57
57
|
"prompts": "^2.4.2",
|
|
58
58
|
"typescript": ">=5.0.4 <7",
|
|
59
|
-
"oxlint-plugin-react-doctor": "0.2.
|
|
59
|
+
"oxlint-plugin-react-doctor": "0.2.1"
|
|
60
60
|
},
|
|
61
61
|
"devDependencies": {
|
|
62
62
|
"@types/prompts": "^2.4.9",
|
|
63
63
|
"eslint-plugin-react-hooks": "^7.1.1",
|
|
64
64
|
"eslint-plugin-react-you-might-not-need-an-effect": "^0.10.1",
|
|
65
|
-
"@react-doctor/core": "0.2.
|
|
66
|
-
"@react-doctor/
|
|
67
|
-
"@react-doctor/
|
|
65
|
+
"@react-doctor/core": "0.2.1",
|
|
66
|
+
"@react-doctor/types": "0.2.1",
|
|
67
|
+
"@react-doctor/project-info": "0.2.1"
|
|
68
68
|
},
|
|
69
69
|
"peerDependencies": {
|
|
70
70
|
"eslint-plugin-react-hooks": "^6 || ^7",
|