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 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.22";
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(shouldSkipPrompts);
1865
- if (!shouldSkipPrompts && !flags.fix) await maybePromptFix(resolvedDirectory, allDiagnostics, flags.offline ? null : await fetchEstimatedScore(allDiagnostics), capturedScanOutput);
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 });