react-doctor 0.0.22 → 0.0.24
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/README.md +10 -0
- package/dist/cli.js +12 -5
- package/dist/cli.js.map +1 -1
- package/dist/index.js +7 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -15,6 +15,15 @@ One command scans your codebase for security, performance, correctness, and arch
|
|
|
15
15
|
|
|
16
16
|
https://github.com/user-attachments/assets/07cc88d9-9589-44c3-aa73-5d603cb1c570
|
|
17
17
|
|
|
18
|
+
## How it works
|
|
19
|
+
|
|
20
|
+
React Doctor detects your framework (Next.js, Vite, Remix, etc.), React version, and compiler setup, then runs two analysis passes **in parallel**:
|
|
21
|
+
|
|
22
|
+
1. **Lint**: Checks 60+ rules across state & effects, performance, architecture, bundle size, security, correctness, accessibility, and framework-specific categories (Next.js, React Native). Rules are toggled automatically based on your project setup.
|
|
23
|
+
2. **Dead code**: Detects unused files, exports, types, and duplicates.
|
|
24
|
+
|
|
25
|
+
Diagnostics are filtered through your config, then scored by severity (errors weigh more than warnings) to produce a **0–100 health score** (75+ Great, 50–74 Needs work, <50 Critical).
|
|
26
|
+
|
|
18
27
|
## Install
|
|
19
28
|
|
|
20
29
|
Run this at your project root:
|
|
@@ -53,6 +62,7 @@ Options:
|
|
|
53
62
|
-y, --yes skip prompts, scan all workspace projects
|
|
54
63
|
--project <name> select workspace project (comma-separated for multiple)
|
|
55
64
|
--diff [base] scan only files changed vs base branch
|
|
65
|
+
--no-ami skip Ami-related prompts
|
|
56
66
|
--fix open Ami to auto-fix all issues
|
|
57
67
|
--prompt copy latest scan output to clipboard
|
|
58
68
|
-h, --help display help for command
|
package/dist/cli.js
CHANGED
|
@@ -183,6 +183,11 @@ const VITE_CONFIG_FILENAMES = [
|
|
|
183
183
|
"vite.config.mjs",
|
|
184
184
|
"vite.config.cjs"
|
|
185
185
|
];
|
|
186
|
+
const EXPO_APP_CONFIG_FILENAMES = [
|
|
187
|
+
"app.json",
|
|
188
|
+
"app.config.js",
|
|
189
|
+
"app.config.ts"
|
|
190
|
+
];
|
|
186
191
|
const REACT_COMPILER_CONFIG_PATTERN = /react-compiler|reactCompiler/;
|
|
187
192
|
const FRAMEWORK_PACKAGES = {
|
|
188
193
|
next: "nextjs",
|
|
@@ -369,6 +374,7 @@ const detectReactCompiler = (directory, packageJson) => {
|
|
|
369
374
|
if (hasCompilerInConfigFiles(directory, NEXT_CONFIG_FILENAMES)) return true;
|
|
370
375
|
if (hasCompilerInConfigFiles(directory, BABEL_CONFIG_FILENAMES)) return true;
|
|
371
376
|
if (hasCompilerInConfigFiles(directory, VITE_CONFIG_FILENAMES)) return true;
|
|
377
|
+
if (hasCompilerInConfigFiles(directory, EXPO_APP_CONFIG_FILENAMES)) return true;
|
|
372
378
|
let ancestorDirectory = path.dirname(directory);
|
|
373
379
|
while (ancestorDirectory !== path.dirname(ancestorDirectory)) {
|
|
374
380
|
const ancestorPackagePath = path.join(ancestorDirectory, "package.json");
|
|
@@ -446,7 +452,7 @@ const filterIgnoredDiagnostics = (diagnostics, config) => {
|
|
|
446
452
|
return diagnostics.filter((diagnostic) => {
|
|
447
453
|
const ruleIdentifier = `${diagnostic.plugin}/${diagnostic.rule}`;
|
|
448
454
|
if (ignoredRules.has(ruleIdentifier)) return false;
|
|
449
|
-
const normalizedPath = diagnostic.filePath.replace(/\\/g, "/");
|
|
455
|
+
const normalizedPath = diagnostic.filePath.replace(/\\/g, "/").replace(/^\.\//, "");
|
|
450
456
|
if (ignoredFilePatterns.some((pattern) => pattern.test(normalizedPath))) return false;
|
|
451
457
|
return true;
|
|
452
458
|
});
|
|
@@ -1759,7 +1765,7 @@ const maybePromptSkillInstall = async (shouldSkipPrompts) => {
|
|
|
1759
1765
|
|
|
1760
1766
|
//#endregion
|
|
1761
1767
|
//#region src/cli.ts
|
|
1762
|
-
const VERSION = "0.0.
|
|
1768
|
+
const VERSION = "0.0.24";
|
|
1763
1769
|
const exitWithFixHint = () => {
|
|
1764
1770
|
logger.break();
|
|
1765
1771
|
logger.log("Cancelled.");
|
|
@@ -1791,7 +1797,7 @@ const resolveDiffMode = async (diffInfo, effectiveDiff, shouldSkipPrompts, isSco
|
|
|
1791
1797
|
});
|
|
1792
1798
|
return Boolean(shouldScanBranchOnly);
|
|
1793
1799
|
};
|
|
1794
|
-
const program = new Command().name("react-doctor").description("Diagnose React codebase health").version(VERSION, "-v, --version", "display the version number").argument("[directory]", "project directory to scan", ".").option("--no-lint", "skip linting").option("--no-dead-code", "skip dead code detection").option("--verbose", "show file details per rule").option("--score", "output only the score").option("-y, --yes", "skip prompts, scan all workspace projects").option("--project <name>", "select workspace project (comma-separated for multiple)").option("--diff [base]", "scan only files changed vs base branch").option("--offline", "skip telemetry (anonymous, not stored, only used to calculate score)").option("--fix", "open Ami to auto-fix all issues").option("--prompt", "copy latest scan output to clipboard").action(async (directory, flags) => {
|
|
1800
|
+
const program = new Command().name("react-doctor").description("Diagnose React codebase health").version(VERSION, "-v, --version", "display the version number").argument("[directory]", "project directory to scan", ".").option("--no-lint", "skip linting").option("--no-dead-code", "skip dead code detection").option("--verbose", "show file details per rule").option("--score", "output only the score").option("-y, --yes", "skip prompts, scan all workspace projects").option("--project <name>", "select workspace project (comma-separated for multiple)").option("--diff [base]", "scan only files changed vs base branch").option("--offline", "skip telemetry (anonymous, not stored, only used to calculate score)").option("--no-ami", "skip Ami-related prompts").option("--fix", "open Ami to auto-fix all issues").option("--prompt", "copy latest scan output to clipboard").action(async (directory, flags) => {
|
|
1795
1801
|
const isScoreOnly = flags.score && !flags.prompt;
|
|
1796
1802
|
const shouldCopyPromptOutput = flags.prompt;
|
|
1797
1803
|
startLoggerCapture();
|
|
@@ -1820,6 +1826,7 @@ const program = new Command().name("react-doctor").description("Diagnose React c
|
|
|
1820
1826
|
process.env.AMI
|
|
1821
1827
|
].some(Boolean);
|
|
1822
1828
|
const shouldSkipPrompts = flags.yes || isAutomatedEnvironment || !process.stdin.isTTY;
|
|
1829
|
+
const shouldSkipAmiPrompts = shouldSkipPrompts || !flags.ami;
|
|
1823
1830
|
const projectDirectories = await selectProjects(resolvedDirectory, flags.project, shouldSkipPrompts);
|
|
1824
1831
|
const effectiveDiff = isCliOverride("diff") ? flags.diff : userConfig?.diff;
|
|
1825
1832
|
const explicitBaseBranch = typeof effectiveDiff === "string" ? effectiveDiff : void 0;
|
|
@@ -1861,8 +1868,8 @@ const program = new Command().name("react-doctor").description("Diagnose React c
|
|
|
1861
1868
|
if (flags.fix) openAmiToFix(resolvedDirectory);
|
|
1862
1869
|
if (shouldCopyPromptOutput) copyPromptToClipboard(capturedScanOutput, !isScoreOnly);
|
|
1863
1870
|
else if (!isScoreOnly) {
|
|
1864
|
-
await maybePromptSkillInstall(
|
|
1865
|
-
if (!
|
|
1871
|
+
await maybePromptSkillInstall(shouldSkipAmiPrompts);
|
|
1872
|
+
if (!shouldSkipAmiPrompts && !flags.fix) await maybePromptFix(resolvedDirectory, allDiagnostics, flags.offline ? null : await fetchEstimatedScore(allDiagnostics), capturedScanOutput);
|
|
1866
1873
|
}
|
|
1867
1874
|
} catch (error) {
|
|
1868
1875
|
handleError(error, { shouldExit: !shouldCopyPromptOutput });
|