react-doctor 0.2.8 → 0.2.10
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-logger-C35LXalM.js → cli-logger-BRBUS1pE.js} +303 -31
- package/dist/cli.js +224 -90
- package/dist/index.d.ts +64 -7
- package/dist/index.js +321 -58
- package/package.json +5 -5
package/dist/cli.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { i as __toESM, n as __exportAll, r as __require, t as __commonJSMin } from "./rolldown-runtime-uZX_iqCz.js";
|
|
2
|
-
import { A as isReactDoctorError, C as filterSourceFiles, D as groupBy, E as getDiffInfo, F as runInspect, I as toRelativePath, M as listWorkspacePackages, N as resolveScanTarget, O as highlighter, P as restoreLegacyThrow, S as filterDiagnosticsForSurface, T as formatReactDoctorError, _ as Score, a as DeadCode, b as buildJsonReportError, c as LintPartialFailures, d as OXLINT_NODE_REQUIREMENT, f as Progress, g as SKILL_NAME, h as SHARE_BASE_URL, i as Config, j as layerOtlp, k as isMonorepoRoot, l as Linter, m as Reporter, o as Files, p as Project, r as CANONICAL_GITHUB_URL, s as Git, t as cliLogger, u as NodeResolver, v as StagedFiles, w as formatErrorChain, x as discoverReactSubprojects, y as buildJsonReport } from "./cli-logger-
|
|
2
|
+
import { A as isReactDoctorError, C as filterSourceFiles, D as groupBy, E as getDiffInfo, F as runInspect, I as toRelativePath, M as listWorkspacePackages, N as resolveScanTarget, O as highlighter, P as restoreLegacyThrow, S as filterDiagnosticsForSurface, T as formatReactDoctorError, _ as Score, a as DeadCode, b as buildJsonReportError, c as LintPartialFailures, d as OXLINT_NODE_REQUIREMENT, f as Progress, g as SKILL_NAME, h as SHARE_BASE_URL, i as Config, j as layerOtlp, k as isMonorepoRoot, l as Linter, m as Reporter, o as Files, p as Project, r as CANONICAL_GITHUB_URL, s as Git, t as cliLogger, u as NodeResolver, v as StagedFiles, w as formatErrorChain, x as discoverReactSubprojects, y as buildJsonReport } from "./cli-logger-BRBUS1pE.js";
|
|
3
3
|
import { createRequire } from "node:module";
|
|
4
4
|
import { execFileSync, execSync } from "node:child_process";
|
|
5
5
|
import path, { join } from "node:path";
|
|
@@ -6320,9 +6320,15 @@ const colorizeByScore = (text, score) => {
|
|
|
6320
6320
|
return highlighter.error(text);
|
|
6321
6321
|
};
|
|
6322
6322
|
//#endregion
|
|
6323
|
+
//#region src/cli/utils/constants.ts
|
|
6324
|
+
const STAGED_FILES_TEMP_DIR_PREFIX = "react-doctor-staged-";
|
|
6325
|
+
const INTERNAL_ERROR_JSON_FALLBACK = "{\"schemaVersion\":1,\"ok\":false,\"error\":{\"message\":\"Internal error\",\"name\":\"Error\",\"chain\":[]}}\n";
|
|
6326
|
+
//#endregion
|
|
6323
6327
|
//#region src/cli/utils/render-score-header.ts
|
|
6324
|
-
const
|
|
6325
|
-
const
|
|
6328
|
+
const RAINBOW_HUE_SHIFT_PER_FRAME = 9;
|
|
6329
|
+
const RAINBOW_GRADIENT_WIDTH = 80;
|
|
6330
|
+
const RAINBOW_OKLCH_LIGHTNESS = .638;
|
|
6331
|
+
const RAINBOW_OKLCH_CHROMA = .129;
|
|
6326
6332
|
const easeOutCubic = (progress) => 1 - (1 - progress) ** 3;
|
|
6327
6333
|
const sleep = (milliseconds) => Effect.promise(() => new Promise((resolve) => setTimeout(resolve, milliseconds)));
|
|
6328
6334
|
const buildScoreBarSegments = (filledCount) => {
|
|
@@ -6333,6 +6339,36 @@ const buildScoreBarSegments = (filledCount) => {
|
|
|
6333
6339
|
};
|
|
6334
6340
|
};
|
|
6335
6341
|
const getFilledCount = (score) => Math.round(score / 100 * 50);
|
|
6342
|
+
const joinScoreHeaderFrame = (lines) => `${lines[0]}\n\r${lines[1]}\n\r${lines[2]}\n\r${lines[3]}\n`;
|
|
6343
|
+
const buildRawScoreBar = (displayScore) => {
|
|
6344
|
+
const { filledSegment, emptySegment } = buildScoreBarSegments(getFilledCount(displayScore));
|
|
6345
|
+
return filledSegment + emptySegment;
|
|
6346
|
+
};
|
|
6347
|
+
const buildScoreHeaderLine = (faceLine, rightColumnContent) => {
|
|
6348
|
+
return ` ${faceLine}${rightColumnContent.length > 0 ? " " : ""}${rightColumnContent}`;
|
|
6349
|
+
};
|
|
6350
|
+
const getRightColumnOffset = (faceLine) => ` ${faceLine} `.length;
|
|
6351
|
+
const clampColorChannel = (value) => Math.max(0, Math.min(255, Math.round(value)));
|
|
6352
|
+
const encodeSrgb = (value) => value <= .0031308 ? value * 12.92 : 1.055 * value ** (1 / 2.4) - .055;
|
|
6353
|
+
const oklchToRgb = (lightness, chroma, hue) => {
|
|
6354
|
+
const hueRadians = hue * Math.PI / 180;
|
|
6355
|
+
const labA = chroma * Math.cos(hueRadians);
|
|
6356
|
+
const labB = chroma * Math.sin(hueRadians);
|
|
6357
|
+
const longCone = (lightness + .3963377774 * labA + .2158037573 * labB) ** 3;
|
|
6358
|
+
const mediumCone = (lightness - .1055613458 * labA - .0638541728 * labB) ** 3;
|
|
6359
|
+
const shortCone = (lightness - .0894841775 * labA - 1.291485548 * labB) ** 3;
|
|
6360
|
+
return {
|
|
6361
|
+
red: clampColorChannel(encodeSrgb(4.0767416621 * longCone - 3.3077115913 * mediumCone + .2309699292 * shortCone) * 255),
|
|
6362
|
+
green: clampColorChannel(encodeSrgb(-1.2684380046 * longCone + 2.6097574011 * mediumCone - .3413193965 * shortCone) * 255),
|
|
6363
|
+
blue: clampColorChannel(encodeSrgb(-.0041960863 * longCone - .7034186147 * mediumCone + 1.707614701 * shortCone) * 255)
|
|
6364
|
+
};
|
|
6365
|
+
};
|
|
6366
|
+
const colorizeTrueColor = (text, { red, green, blue }) => `\x1b[38;2;${red};${green};${blue}m${text}\x1b[39m`;
|
|
6367
|
+
const colorizeRainbowText = (text, frame, offset = 0) => [...text].map((character, index) => {
|
|
6368
|
+
if (character === " ") return character;
|
|
6369
|
+
return colorizeTrueColor(character, oklchToRgb(RAINBOW_OKLCH_LIGHTNESS, RAINBOW_OKLCH_CHROMA, ((index + offset) / RAINBOW_GRADIENT_WIDTH * 360 + frame * RAINBOW_HUE_SHIFT_PER_FRAME) % 360));
|
|
6370
|
+
}).join("");
|
|
6371
|
+
const buildRainbowHeaderLine = (faceLine, rightColumnContent, frame) => colorizeRainbowText(buildScoreHeaderLine(faceLine, rightColumnContent), frame);
|
|
6336
6372
|
const buildScoreBar = (displayScore, colorScore = displayScore) => {
|
|
6337
6373
|
const { filledSegment, emptySegment } = buildScoreBarSegments(getFilledCount(displayScore));
|
|
6338
6374
|
return colorizeByScore(filledSegment, colorScore) + highlighter.dim(emptySegment);
|
|
@@ -6343,18 +6379,19 @@ const getDoctorFace = (score) => {
|
|
|
6343
6379
|
return ["x x", " ▽ "];
|
|
6344
6380
|
};
|
|
6345
6381
|
const BRANDING_LINE = `React Doctor ${highlighter.dim("(https://react.doctor)")}`;
|
|
6346
|
-
const
|
|
6382
|
+
const RAW_BRANDING_LINE = "React Doctor (https://react.doctor)";
|
|
6383
|
+
const buildRawFaceLines = (score) => {
|
|
6347
6384
|
const [eyes, mouth] = getDoctorFace(score);
|
|
6348
|
-
const colorize = (text) => colorizeByScore(text, score);
|
|
6349
6385
|
return [
|
|
6350
6386
|
"┌─────┐",
|
|
6351
6387
|
`│ ${eyes} │`,
|
|
6352
6388
|
`│ ${mouth} │`,
|
|
6353
6389
|
"└─────┘"
|
|
6354
|
-
]
|
|
6390
|
+
];
|
|
6355
6391
|
};
|
|
6356
|
-
const
|
|
6357
|
-
|
|
6392
|
+
const buildFaceRenderedLines = (score) => {
|
|
6393
|
+
const colorize = (text) => colorizeByScore(text, score);
|
|
6394
|
+
return buildRawFaceLines(score).map(colorize);
|
|
6358
6395
|
};
|
|
6359
6396
|
const writeScoreHeaderLine = (line) => Effect.sync(() => {
|
|
6360
6397
|
process.stdout.write(line);
|
|
@@ -6365,29 +6402,97 @@ const buildScoreLine = (displayScore, finalScore, label, projectName) => {
|
|
|
6365
6402
|
const projectSuffix = projectName ? ` ${highlighter.dim("·")} ${highlighter.dim(projectName)}` : "";
|
|
6366
6403
|
return `${scoreNumber} ${highlighter.dim(`/ 100`)} ${scoreLabel}${projectSuffix}`;
|
|
6367
6404
|
};
|
|
6405
|
+
const buildRawScoreLine = (displayScore, label, projectName) => {
|
|
6406
|
+
return `${displayScore} / 100 ${label}${projectName ? ` · ${projectName}` : ""}`;
|
|
6407
|
+
};
|
|
6408
|
+
const buildRainbowScoreHeaderFrame = ({ score, displayScore, label, frame, projectName }) => {
|
|
6409
|
+
const rawFaceLines = buildRawFaceLines(score);
|
|
6410
|
+
return joinScoreHeaderFrame([
|
|
6411
|
+
buildRainbowHeaderLine(rawFaceLines[0] ?? "", buildRawScoreLine(displayScore, label, projectName), frame),
|
|
6412
|
+
buildRainbowHeaderLine(rawFaceLines[1] ?? "", buildRawScoreBar(displayScore), frame),
|
|
6413
|
+
buildRainbowHeaderLine(rawFaceLines[2] ?? "", RAW_BRANDING_LINE, frame),
|
|
6414
|
+
buildRainbowHeaderLine(rawFaceLines[3] ?? "", "", frame)
|
|
6415
|
+
]);
|
|
6416
|
+
};
|
|
6417
|
+
const buildFinalPerfectScoreHeaderFrame = (score, label, frame, projectName) => {
|
|
6418
|
+
const rawFaceLines = buildRawFaceLines(score);
|
|
6419
|
+
const renderedFaceLines = buildFaceRenderedLines(score);
|
|
6420
|
+
const rainbowBarLine = colorizeRainbowText(buildRawScoreBar(score), frame, getRightColumnOffset(rawFaceLines[1] ?? ""));
|
|
6421
|
+
return joinScoreHeaderFrame([
|
|
6422
|
+
buildScoreHeaderLine(renderedFaceLines[0] ?? "", buildScoreLine(score, score, label, projectName)),
|
|
6423
|
+
buildScoreHeaderLine(renderedFaceLines[1] ?? "", rainbowBarLine),
|
|
6424
|
+
buildScoreHeaderLine(renderedFaceLines[2] ?? "", BRANDING_LINE),
|
|
6425
|
+
buildScoreHeaderLine(renderedFaceLines[3] ?? "", "")
|
|
6426
|
+
]);
|
|
6427
|
+
};
|
|
6428
|
+
const buildInitialScoreHeaderLine = ({ isPerfectScore, shouldAnimate, lineIndex, renderedFaceLine, rawFaceLine, rightColumnContent, rawRightColumnContent, score }) => {
|
|
6429
|
+
if (!isPerfectScore) return buildScoreHeaderLine(renderedFaceLine, rightColumnContent);
|
|
6430
|
+
if (shouldAnimate) return buildRainbowHeaderLine(rawFaceLine, rawRightColumnContent, 0);
|
|
6431
|
+
if (lineIndex !== 1) return buildScoreHeaderLine(renderedFaceLine, rightColumnContent);
|
|
6432
|
+
return buildScoreHeaderLine(renderedFaceLine, colorizeRainbowText(buildRawScoreBar(score), 0, getRightColumnOffset(rawFaceLine)));
|
|
6433
|
+
};
|
|
6368
6434
|
const printAnimatedScore = (scoreFaceLine, barFaceLine, score, label, projectName) => Effect.gen(function* () {
|
|
6369
|
-
|
|
6370
|
-
|
|
6435
|
+
const isPerfectScore = score === 100;
|
|
6436
|
+
for (let frame = 0; frame <= 40; frame += 1) {
|
|
6437
|
+
const progress = easeOutCubic(frame / 40);
|
|
6371
6438
|
const animatedScore = Math.round(score * progress);
|
|
6439
|
+
if (isPerfectScore) {
|
|
6440
|
+
yield* writeScoreHeaderLine(`${frame === 0 ? "" : "\x1B[4A"}\r${buildRainbowScoreHeaderFrame({
|
|
6441
|
+
score,
|
|
6442
|
+
displayScore: animatedScore,
|
|
6443
|
+
label,
|
|
6444
|
+
frame,
|
|
6445
|
+
projectName
|
|
6446
|
+
})}`);
|
|
6447
|
+
if (frame < 40) yield* sleep(50);
|
|
6448
|
+
continue;
|
|
6449
|
+
}
|
|
6372
6450
|
const animatedScoreLine = buildScoreLine(animatedScore, score, label, projectName);
|
|
6373
6451
|
const animatedBarLine = buildScoreBar(animatedScore, score);
|
|
6374
6452
|
yield* writeScoreHeaderLine(`${frame === 0 ? "" : "\x1B[2A"}\r${buildScoreHeaderLine(scoreFaceLine, animatedScoreLine)}\n\r${buildScoreHeaderLine(barFaceLine, animatedBarLine)}\n`);
|
|
6375
|
-
if (frame <
|
|
6453
|
+
if (frame < 40) yield* sleep(50);
|
|
6454
|
+
}
|
|
6455
|
+
if (!isPerfectScore) return;
|
|
6456
|
+
for (let frame = 0; frame < 16; frame += 1) {
|
|
6457
|
+
yield* writeScoreHeaderLine(`\x1b[4A\r${buildRainbowScoreHeaderFrame({
|
|
6458
|
+
score,
|
|
6459
|
+
displayScore: score,
|
|
6460
|
+
label,
|
|
6461
|
+
frame,
|
|
6462
|
+
projectName
|
|
6463
|
+
})}`);
|
|
6464
|
+
yield* sleep(50);
|
|
6376
6465
|
}
|
|
6466
|
+
yield* writeScoreHeaderLine(`\x1b[4A\r${buildFinalPerfectScoreHeaderFrame(score, label, 16, projectName)}\x1b[2A`);
|
|
6377
6467
|
});
|
|
6378
6468
|
const printScoreHeader = (scoreResult, projectName) => Effect.gen(function* () {
|
|
6469
|
+
const isPerfectScore = scoreResult.score === 100;
|
|
6379
6470
|
const renderedFaceLines = buildFaceRenderedLines(scoreResult.score);
|
|
6471
|
+
const rawFaceLines = buildRawFaceLines(scoreResult.score);
|
|
6380
6472
|
const shouldAnimate = !isSpinnerSilent() && isSpinnerInteractive(process.stdout);
|
|
6473
|
+
const displayScore = shouldAnimate ? 0 : scoreResult.score;
|
|
6381
6474
|
const rightColumnLines = [
|
|
6382
|
-
buildScoreLine(
|
|
6475
|
+
buildScoreLine(displayScore, scoreResult.score, scoreResult.label, projectName),
|
|
6383
6476
|
shouldAnimate ? buildScoreBar(0, scoreResult.score) : buildScoreBar(scoreResult.score),
|
|
6384
6477
|
BRANDING_LINE,
|
|
6385
6478
|
""
|
|
6386
6479
|
];
|
|
6387
|
-
|
|
6388
|
-
|
|
6389
|
-
|
|
6390
|
-
|
|
6480
|
+
const rawRightColumnLines = [
|
|
6481
|
+
buildRawScoreLine(displayScore, scoreResult.label, projectName),
|
|
6482
|
+
buildRawScoreBar(displayScore),
|
|
6483
|
+
RAW_BRANDING_LINE,
|
|
6484
|
+
""
|
|
6485
|
+
];
|
|
6486
|
+
for (let lineIndex = 0; lineIndex < renderedFaceLines.length; lineIndex += 1) yield* Console.log(buildInitialScoreHeaderLine({
|
|
6487
|
+
isPerfectScore,
|
|
6488
|
+
shouldAnimate,
|
|
6489
|
+
lineIndex,
|
|
6490
|
+
renderedFaceLine: renderedFaceLines[lineIndex] ?? "",
|
|
6491
|
+
rawFaceLine: rawFaceLines[lineIndex] ?? "",
|
|
6492
|
+
rightColumnContent: rightColumnLines[lineIndex] ?? "",
|
|
6493
|
+
rawRightColumnContent: rawRightColumnLines[lineIndex] ?? "",
|
|
6494
|
+
score: scoreResult.score
|
|
6495
|
+
}));
|
|
6391
6496
|
yield* Console.log("");
|
|
6392
6497
|
if (shouldAnimate) {
|
|
6393
6498
|
yield* writeScoreHeaderLine("\x1B[5A");
|
|
@@ -6561,7 +6666,7 @@ const resolveOxlintNodeEffect = (isLintEnabled, isQuiet) => Effect.gen(function*
|
|
|
6561
6666
|
const resolveOxlintNode = (isLintEnabled, isQuiet) => Effect.runPromise(resolveOxlintNodeEffect(isLintEnabled, isQuiet).pipe(Effect.provide(NodeResolver.layerNode)));
|
|
6562
6667
|
//#endregion
|
|
6563
6668
|
//#region src/cli/utils/version.ts
|
|
6564
|
-
const VERSION = "0.2.
|
|
6669
|
+
const VERSION = "0.2.10";
|
|
6565
6670
|
//#endregion
|
|
6566
6671
|
//#region src/inspect.ts
|
|
6567
6672
|
const silentConsole = makeNoopConsole();
|
|
@@ -6749,10 +6854,6 @@ const finalizeAndRender = (input) => Effect.gen(function* () {
|
|
|
6749
6854
|
return buildResult();
|
|
6750
6855
|
});
|
|
6751
6856
|
//#endregion
|
|
6752
|
-
//#region src/cli/utils/constants.ts
|
|
6753
|
-
const STAGED_FILES_TEMP_DIR_PREFIX = "react-doctor-staged-";
|
|
6754
|
-
const INTERNAL_ERROR_JSON_FALLBACK = "{\"schemaVersion\":1,\"ok\":false,\"error\":{\"message\":\"Internal error\",\"name\":\"Error\",\"chain\":[]}}\n";
|
|
6755
|
-
//#endregion
|
|
6756
6857
|
//#region src/cli/utils/get-staged-files.ts
|
|
6757
6858
|
const stagedFilesLayer = StagedFiles.layerNode.pipe(Layer.provide(Git.layerNode));
|
|
6758
6859
|
const getStagedSourceFiles = async (directory) => {
|
|
@@ -7311,7 +7412,7 @@ const disableSetupPrompt = (projectRoot, storeOptions = {}) => {
|
|
|
7311
7412
|
return true;
|
|
7312
7413
|
};
|
|
7313
7414
|
const shouldPromptInstallSetup = (options) => {
|
|
7314
|
-
if (!options.hasScoredScan) return false;
|
|
7415
|
+
if (!(options.hasCompletedScan ?? options.hasScoredScan ?? false)) return false;
|
|
7315
7416
|
if (options.isJsonMode) return false;
|
|
7316
7417
|
if (options.isScoreOnly) return false;
|
|
7317
7418
|
if (options.isStaged) return false;
|
|
@@ -7321,13 +7422,13 @@ const shouldPromptInstallSetup = (options) => {
|
|
|
7321
7422
|
return !hasDoctorScript(options.projectRoot);
|
|
7322
7423
|
};
|
|
7323
7424
|
const resolveInstallSetupProjectRoot = (options) => {
|
|
7324
|
-
if (options.
|
|
7425
|
+
if (options.scanDirectories.length === 0) return findNearestPackageDirectory(options.scanRoot) ?? options.scanRoot;
|
|
7325
7426
|
const packageDirectories = /* @__PURE__ */ new Set();
|
|
7326
|
-
for (const scanDirectory of options.
|
|
7427
|
+
for (const scanDirectory of options.scanDirectories) {
|
|
7327
7428
|
const packageDirectory = findNearestPackageDirectory(scanDirectory, options.scanRoot) ?? findNearestPackageDirectory(scanDirectory) ?? scanDirectory;
|
|
7328
7429
|
packageDirectories.add(packageDirectory);
|
|
7329
7430
|
}
|
|
7330
|
-
if (packageDirectories.size !== 1) return
|
|
7431
|
+
if (packageDirectories.size !== 1) return findNearestPackageDirectory(options.scanRoot, options.scanRoot);
|
|
7331
7432
|
return [...packageDirectories][0] ?? null;
|
|
7332
7433
|
};
|
|
7333
7434
|
const defaultWait = (milliseconds) => new Promise((resolve) => {
|
|
@@ -7362,7 +7463,7 @@ const warnSetupPromptFailure = async (options, error) => {
|
|
|
7362
7463
|
return;
|
|
7363
7464
|
}
|
|
7364
7465
|
try {
|
|
7365
|
-
const { cliLogger } = await import("./cli-logger-
|
|
7466
|
+
const { cliLogger } = await import("./cli-logger-BRBUS1pE.js").then((n) => n.n);
|
|
7366
7467
|
cliLogger.warn(message);
|
|
7367
7468
|
} catch {}
|
|
7368
7469
|
};
|
|
@@ -7378,7 +7479,7 @@ const promptInstallSetup = async (options) => {
|
|
|
7378
7479
|
writeLine("You can always run `npx react-doctor@latest install` to set it up later.");
|
|
7379
7480
|
return;
|
|
7380
7481
|
}
|
|
7381
|
-
const install = options.install ?? (await Promise.resolve().then(() =>
|
|
7482
|
+
const install = options.install ?? (await Promise.resolve().then(() => install_react_doctor_exports)).runInstallReactDoctor;
|
|
7382
7483
|
const previousExitCode = process.exitCode;
|
|
7383
7484
|
let setupExitCode;
|
|
7384
7485
|
try {
|
|
@@ -7397,7 +7498,7 @@ const promptInstallSetup = async (options) => {
|
|
|
7397
7498
|
}
|
|
7398
7499
|
};
|
|
7399
7500
|
const shouldShowAgentInstallHint = (options) => {
|
|
7400
|
-
if (!options.hasScoredScan) return false;
|
|
7501
|
+
if (!(options.hasCompletedScan ?? options.hasScoredScan ?? false)) return false;
|
|
7401
7502
|
if (options.isJsonMode) return false;
|
|
7402
7503
|
if (options.isScoreOnly) return false;
|
|
7403
7504
|
if (options.isStaged) return false;
|
|
@@ -7462,7 +7563,7 @@ const resolveDiffMode = async (diffInfo, effectiveDiff, shouldSkipPrompts, isQui
|
|
|
7462
7563
|
const { scanScope } = await prompts({
|
|
7463
7564
|
type: "select",
|
|
7464
7565
|
name: "scanScope",
|
|
7465
|
-
message: "
|
|
7566
|
+
message: "Choose what to scan",
|
|
7466
7567
|
choices: [{
|
|
7467
7568
|
title: "Full codebase",
|
|
7468
7569
|
value: "full"
|
|
@@ -7604,7 +7705,7 @@ const selectProjects = async (rootDirectory, projectFlag, skipPrompts) => {
|
|
|
7604
7705
|
}
|
|
7605
7706
|
if (packages.length === 0) return [rootDirectory];
|
|
7606
7707
|
if (packages.length === 1) {
|
|
7607
|
-
cliLogger.log(`${highlighter.success("✔")} Select projects
|
|
7708
|
+
cliLogger.log(`${highlighter.success("✔")} Select projects ${highlighter.dim("›")} ${packages[0].name}`);
|
|
7608
7709
|
return [packages[0].directory];
|
|
7609
7710
|
}
|
|
7610
7711
|
if (projectFlag) return resolveProjectFlag(projectFlag, packages);
|
|
@@ -7628,13 +7729,13 @@ const resolveProjectFlag = (projectFlag, workspacePackages) => {
|
|
|
7628
7729
|
return resolvedDirectories;
|
|
7629
7730
|
};
|
|
7630
7731
|
const printDiscoveredProjects = (packages) => {
|
|
7631
|
-
cliLogger.log(`${highlighter.success("✔")} Select projects
|
|
7732
|
+
cliLogger.log(`${highlighter.success("✔")} Select projects ${highlighter.dim("›")} ${packages.map((workspacePackage) => workspacePackage.name).join(", ")}`);
|
|
7632
7733
|
};
|
|
7633
7734
|
const promptProjectSelection = async (workspacePackages, rootDirectory) => {
|
|
7634
7735
|
const { selectedDirectories } = await prompts({
|
|
7635
7736
|
type: "multiselect",
|
|
7636
7737
|
name: "selectedDirectories",
|
|
7637
|
-
message: "Select projects
|
|
7738
|
+
message: "Select projects",
|
|
7638
7739
|
choices: workspacePackages.map((workspacePackage) => ({
|
|
7639
7740
|
title: workspacePackage.name,
|
|
7640
7741
|
description: path.relative(rootDirectory, workspacePackage.directory),
|
|
@@ -7891,13 +7992,13 @@ const inspectAction = async (directory, flags) => {
|
|
|
7891
7992
|
});
|
|
7892
7993
|
const setupProjectRoot = resolveInstallSetupProjectRoot({
|
|
7893
7994
|
scanRoot: resolvedDirectory,
|
|
7894
|
-
|
|
7995
|
+
scanDirectories: projectDirectories
|
|
7895
7996
|
});
|
|
7896
7997
|
if (setupProjectRoot !== null) {
|
|
7897
|
-
const
|
|
7998
|
+
const hasCompletedScan = completedScans.length > 0;
|
|
7898
7999
|
await promptInstallSetup({
|
|
7899
8000
|
projectRoot: setupProjectRoot,
|
|
7900
|
-
|
|
8001
|
+
hasCompletedScan,
|
|
7901
8002
|
issueCount: filterDiagnosticsForSurface(allDiagnostics, scanOptions.outputSurface ?? "cli", userConfig).length,
|
|
7902
8003
|
isJsonMode,
|
|
7903
8004
|
isScoreOnly,
|
|
@@ -7906,7 +8007,7 @@ const inspectAction = async (directory, flags) => {
|
|
|
7906
8007
|
});
|
|
7907
8008
|
if (shouldShowAgentInstallHint({
|
|
7908
8009
|
projectRoot: setupProjectRoot,
|
|
7909
|
-
|
|
8010
|
+
hasCompletedScan,
|
|
7910
8011
|
isJsonMode,
|
|
7911
8012
|
isScoreOnly,
|
|
7912
8013
|
isStaged: Boolean(flags.staged)
|
|
@@ -8538,8 +8639,12 @@ const installReactDoctorGitHook = (options) => {
|
|
|
8538
8639
|
return installDirectGitHook(options);
|
|
8539
8640
|
};
|
|
8540
8641
|
//#endregion
|
|
8541
|
-
//#region src/cli/utils/install-
|
|
8542
|
-
var
|
|
8642
|
+
//#region src/cli/utils/install-react-doctor.ts
|
|
8643
|
+
var install_react_doctor_exports = /* @__PURE__ */ __exportAll({ runInstallReactDoctor: () => runInstallReactDoctor });
|
|
8644
|
+
const SETUP_OPTION_GIT_HOOK = "git-hook";
|
|
8645
|
+
const SETUP_OPTION_AGENT_HOOKS = "agent-hooks";
|
|
8646
|
+
const SETUP_OPTION_WORKFLOW = "workflow";
|
|
8647
|
+
const SETUP_OPTION_SKIP = "skip";
|
|
8543
8648
|
const CONFIG_ONLY_GIT_HOOK_KINDS = new Set([
|
|
8544
8649
|
"ghooks",
|
|
8545
8650
|
"git-hooks-js",
|
|
@@ -8646,7 +8751,8 @@ const defaultInstallDependencyRunner = (input) => {
|
|
|
8646
8751
|
env: {
|
|
8647
8752
|
...process.env,
|
|
8648
8753
|
REACT_DOCTOR_INSTALL: "1"
|
|
8649
|
-
}
|
|
8754
|
+
},
|
|
8755
|
+
shell: process.platform === "win32"
|
|
8650
8756
|
});
|
|
8651
8757
|
};
|
|
8652
8758
|
const installReactDoctorDependency = async (options) => {
|
|
@@ -8719,7 +8825,30 @@ const getSkillSourceDirectory = () => {
|
|
|
8719
8825
|
const distDirectory = path.dirname(fileURLToPath(import.meta.url));
|
|
8720
8826
|
return path.join(distDirectory, "skills", SKILL_NAME);
|
|
8721
8827
|
};
|
|
8722
|
-
const
|
|
8828
|
+
const canInstallNativeAgentHooks = (agents) => agents.some((agent) => agent === "claude-code" || agent === "cursor");
|
|
8829
|
+
const buildWorkflowContent = () => [
|
|
8830
|
+
"name: React Doctor",
|
|
8831
|
+
"",
|
|
8832
|
+
"on:",
|
|
8833
|
+
" pull_request:",
|
|
8834
|
+
" branches: [main]",
|
|
8835
|
+
"",
|
|
8836
|
+
"permissions:",
|
|
8837
|
+
" contents: read",
|
|
8838
|
+
" pull-requests: write",
|
|
8839
|
+
"",
|
|
8840
|
+
"jobs:",
|
|
8841
|
+
" react-doctor:",
|
|
8842
|
+
" runs-on: ubuntu-latest",
|
|
8843
|
+
" steps:",
|
|
8844
|
+
" - uses: actions/checkout@v4",
|
|
8845
|
+
" - uses: millionco/react-doctor@main",
|
|
8846
|
+
" with:",
|
|
8847
|
+
" github-token: ${{ secrets.GITHUB_TOKEN }}",
|
|
8848
|
+
" diff: main",
|
|
8849
|
+
""
|
|
8850
|
+
].join("\n");
|
|
8851
|
+
const runInstallReactDoctor = async (options = {}) => {
|
|
8723
8852
|
const requestedProjectRoot = options.projectRoot ?? process.cwd();
|
|
8724
8853
|
const projectRoot = findNearestPackageDirectory(requestedProjectRoot) ?? requestedProjectRoot;
|
|
8725
8854
|
const sourceDir = options.sourceDir ?? getSkillSourceDirectory();
|
|
@@ -8740,7 +8869,8 @@ const runInstallSkill = async (options = {}) => {
|
|
|
8740
8869
|
const gitHookTarget = options.gitHookPath === void 0 ? detectGitHookTarget(projectRoot) : options.gitHookPath === null ? null : buildManualGitHookTarget(options.gitHookPath, projectRoot);
|
|
8741
8870
|
const gitHookPath = gitHookTarget?.hookPath;
|
|
8742
8871
|
const promptOptions = options.onPromptCancel === void 0 ? {} : { onCancel: options.onPromptCancel };
|
|
8743
|
-
const
|
|
8872
|
+
const prompt = options.prompt ?? prompts;
|
|
8873
|
+
const selectedAgents = skipPrompts ? detectedAgents : (await prompt({
|
|
8744
8874
|
type: "multiselect",
|
|
8745
8875
|
name: "agents",
|
|
8746
8876
|
message: `Install the ${highlighter.info(`/react-doctor`)} skill for:`,
|
|
@@ -8753,13 +8883,48 @@ const runInstallSkill = async (options = {}) => {
|
|
|
8753
8883
|
min: 1
|
|
8754
8884
|
}, promptOptions)).agents ?? [];
|
|
8755
8885
|
if (selectedAgents.length === 0) return;
|
|
8756
|
-
const
|
|
8757
|
-
|
|
8758
|
-
|
|
8759
|
-
|
|
8760
|
-
|
|
8761
|
-
|
|
8762
|
-
|
|
8886
|
+
const workflowsDirectory = path.join(projectRoot, ".github", "workflows");
|
|
8887
|
+
const workflowTargetPath = path.join(workflowsDirectory, "react-doctor.yml");
|
|
8888
|
+
const hasExistingWorkflows = existsSync(workflowsDirectory);
|
|
8889
|
+
const canInstallWorkflow = !existsSync(workflowTargetPath);
|
|
8890
|
+
const setupActionChoices = [
|
|
8891
|
+
...gitHookPath === null || gitHookPath === void 0 ? [] : [{
|
|
8892
|
+
title: "Pre-commit hook",
|
|
8893
|
+
description: "Check staged changes before each commit",
|
|
8894
|
+
value: SETUP_OPTION_GIT_HOOK,
|
|
8895
|
+
selected: true
|
|
8896
|
+
}],
|
|
8897
|
+
...canInstallNativeAgentHooks(selectedAgents) ? [{
|
|
8898
|
+
title: "Agent hooks",
|
|
8899
|
+
description: "Ask Claude Code or Cursor to scan after code edits",
|
|
8900
|
+
value: SETUP_OPTION_AGENT_HOOKS,
|
|
8901
|
+
selected: Boolean(options.agentHooks)
|
|
8902
|
+
}] : [],
|
|
8903
|
+
...canInstallWorkflow ? [{
|
|
8904
|
+
title: "GitHub Actions workflow",
|
|
8905
|
+
description: "Scan pull requests in CI",
|
|
8906
|
+
value: SETUP_OPTION_WORKFLOW,
|
|
8907
|
+
selected: hasExistingWorkflows
|
|
8908
|
+
}] : []
|
|
8909
|
+
];
|
|
8910
|
+
const setupChoices = setupActionChoices.length === 0 ? [] : [{
|
|
8911
|
+
title: "Skip optional setup",
|
|
8912
|
+
description: "Install only the agent skill and package setup",
|
|
8913
|
+
value: SETUP_OPTION_SKIP,
|
|
8914
|
+
selected: false
|
|
8915
|
+
}, ...setupActionChoices];
|
|
8916
|
+
const selectedSetupOptions = skipPrompts || setupChoices.length === 0 ? [] : (await prompt({
|
|
8917
|
+
type: "multiselect",
|
|
8918
|
+
name: "setupOptions",
|
|
8919
|
+
message: "Select additional React Doctor setup:",
|
|
8920
|
+
choices: setupChoices,
|
|
8921
|
+
instructions: false
|
|
8922
|
+
}, promptOptions)).setupOptions ?? [];
|
|
8923
|
+
const selectedSetupActions = selectedSetupOptions.filter((setupOption) => setupOption !== SETUP_OPTION_SKIP);
|
|
8924
|
+
const didSkipOptionalSetup = selectedSetupActions.length === 0 && selectedSetupOptions.includes(SETUP_OPTION_SKIP);
|
|
8925
|
+
const shouldInstallGitHook = gitHookPath !== null && gitHookPath !== void 0 && (Boolean(options.yes) || !didSkipOptionalSetup && selectedSetupActions.includes(SETUP_OPTION_GIT_HOOK));
|
|
8926
|
+
const shouldInstallAgentHooks = Boolean(options.agentHooks) || !didSkipOptionalSetup && selectedSetupActions.includes(SETUP_OPTION_AGENT_HOOKS);
|
|
8927
|
+
const shouldInstallWorkflow = !skipPrompts && !didSkipOptionalSetup && canInstallWorkflow && selectedSetupActions.includes(SETUP_OPTION_WORKFLOW);
|
|
8763
8928
|
if (options.dryRun) {
|
|
8764
8929
|
cliLogger.log(`Dry run — would install ${SKILL_NAME} skill for:`);
|
|
8765
8930
|
for (const agent of selectedAgents) cliLogger.dim(` - ${getSkillAgentConfig(agent).displayName}`);
|
|
@@ -8768,6 +8933,7 @@ const runInstallSkill = async (options = {}) => {
|
|
|
8768
8933
|
cliLogger.dim(" Dev dependency: react-doctor");
|
|
8769
8934
|
if (shouldInstallGitHook) cliLogger.dim(` Git hook: ${gitHookPath}`);
|
|
8770
8935
|
if (shouldInstallAgentHooks) cliLogger.dim(" Agent hooks: Claude Code / Cursor when selected");
|
|
8936
|
+
if (shouldInstallWorkflow) cliLogger.dim(` GitHub Actions workflow: ${path.relative(projectRoot, workflowTargetPath)}`);
|
|
8771
8937
|
return;
|
|
8772
8938
|
}
|
|
8773
8939
|
const installSpinner = spinner(`Installing ${SKILL_NAME} skill...`).start();
|
|
@@ -8815,47 +8981,15 @@ const runInstallSkill = async (options = {}) => {
|
|
|
8815
8981
|
throw error;
|
|
8816
8982
|
}
|
|
8817
8983
|
}
|
|
8818
|
-
|
|
8819
|
-
|
|
8820
|
-
|
|
8821
|
-
|
|
8822
|
-
|
|
8823
|
-
|
|
8824
|
-
|
|
8825
|
-
|
|
8826
|
-
|
|
8827
|
-
}, promptOptions);
|
|
8828
|
-
if (shouldInstallWorkflow) {
|
|
8829
|
-
if (!hasExistingWorkflows) mkdirSync(workflowsDirectory, { recursive: true });
|
|
8830
|
-
const workflowSpinner = spinner("Adding GitHub Actions workflow...").start();
|
|
8831
|
-
try {
|
|
8832
|
-
writeFileSync(workflowTargetPath, [
|
|
8833
|
-
"name: React Doctor",
|
|
8834
|
-
"",
|
|
8835
|
-
"on:",
|
|
8836
|
-
" pull_request:",
|
|
8837
|
-
" branches: [main]",
|
|
8838
|
-
"",
|
|
8839
|
-
"permissions:",
|
|
8840
|
-
" contents: read",
|
|
8841
|
-
" pull-requests: write",
|
|
8842
|
-
"",
|
|
8843
|
-
"jobs:",
|
|
8844
|
-
" react-doctor:",
|
|
8845
|
-
" runs-on: ubuntu-latest",
|
|
8846
|
-
" steps:",
|
|
8847
|
-
" - uses: actions/checkout@v4",
|
|
8848
|
-
" - uses: millionco/react-doctor@main",
|
|
8849
|
-
" with:",
|
|
8850
|
-
" github-token: ${{ secrets.GITHUB_TOKEN }}",
|
|
8851
|
-
" diff: main",
|
|
8852
|
-
""
|
|
8853
|
-
].join("\n"));
|
|
8854
|
-
workflowSpinner.succeed(`GitHub Actions workflow added at ${path.relative(projectRoot, workflowTargetPath)}.`);
|
|
8855
|
-
} catch (error) {
|
|
8856
|
-
workflowSpinner.fail("Failed to add GitHub Actions workflow.");
|
|
8857
|
-
throw error;
|
|
8858
|
-
}
|
|
8984
|
+
if (shouldInstallWorkflow) {
|
|
8985
|
+
if (!hasExistingWorkflows) mkdirSync(workflowsDirectory, { recursive: true });
|
|
8986
|
+
const workflowSpinner = spinner("Adding GitHub Actions workflow...").start();
|
|
8987
|
+
try {
|
|
8988
|
+
writeFileSync(workflowTargetPath, buildWorkflowContent());
|
|
8989
|
+
workflowSpinner.succeed(`GitHub Actions workflow added at ${path.relative(projectRoot, workflowTargetPath)}.`);
|
|
8990
|
+
} catch (error) {
|
|
8991
|
+
workflowSpinner.fail("Failed to add GitHub Actions workflow.");
|
|
8992
|
+
throw error;
|
|
8859
8993
|
}
|
|
8860
8994
|
}
|
|
8861
8995
|
};
|
|
@@ -8865,7 +8999,7 @@ const installAction = async (options, command) => {
|
|
|
8865
8999
|
Effect.runSync(printBrandedHeader);
|
|
8866
9000
|
try {
|
|
8867
9001
|
const parentOptions = command?.parent?.opts?.();
|
|
8868
|
-
await
|
|
9002
|
+
await runInstallReactDoctor({
|
|
8869
9003
|
yes: options.yes ?? parentOptions?.yes,
|
|
8870
9004
|
dryRun: options.dryRun,
|
|
8871
9005
|
agentHooks: options.agentHooks,
|
package/dist/index.d.ts
CHANGED
|
@@ -232,7 +232,9 @@ interface ReactDoctorConfig {
|
|
|
232
232
|
* `categories` field, but keyed by React Doctor's display
|
|
233
233
|
* categories (`"Server"`, `"React Native"`, `"Architecture"`,
|
|
234
234
|
* `"Bundle Size"`, `"State & Effects"`, `"Security"`,
|
|
235
|
-
* `"Accessibility"`, `"Performance"`, `"Correctness"`,
|
|
235
|
+
* `"Accessibility"`, `"Performance"`, `"Correctness"`,
|
|
236
|
+
* `"Next.js"`, `"Preact"`, `"TanStack Query"`,
|
|
237
|
+
* `"TanStack Start"`, …).
|
|
236
238
|
*
|
|
237
239
|
* ```json
|
|
238
240
|
* { "categories": { "React Native": "warn", "Server": "off" } }
|
|
@@ -296,7 +298,7 @@ interface Diagnostic {
|
|
|
296
298
|
}
|
|
297
299
|
//#endregion
|
|
298
300
|
//#region src/types/project-info.d.ts
|
|
299
|
-
type Framework = "nextjs" | "vite" | "cra" | "remix" | "gatsby" | "expo" | "react-native" | "tanstack-start" | "unknown";
|
|
301
|
+
type Framework = "nextjs" | "vite" | "cra" | "remix" | "gatsby" | "expo" | "react-native" | "tanstack-start" | "preact" | "unknown";
|
|
300
302
|
interface ProjectInfo {
|
|
301
303
|
rootDirectory: string;
|
|
302
304
|
projectName: string;
|
|
@@ -307,6 +309,16 @@ interface ProjectInfo {
|
|
|
307
309
|
hasTypeScript: boolean;
|
|
308
310
|
hasReactCompiler: boolean;
|
|
309
311
|
hasTanStackQuery: boolean;
|
|
312
|
+
/**
|
|
313
|
+
* `true` when `preact` is declared anywhere in the project's
|
|
314
|
+
* dependency manifest. Drives the `preact` capability in
|
|
315
|
+
* `buildCapabilities`, which gates every `preact-*` rule. Modeled
|
|
316
|
+
* on `hasTanStackQuery` rather than the `framework` field because
|
|
317
|
+
* the dominant Preact setup today is Preact-on-Vite — those
|
|
318
|
+
* projects classify as `framework: "vite"` for build-tool reasons
|
|
319
|
+
* but still need Preact-specific rules to fire.
|
|
320
|
+
*/
|
|
321
|
+
hasPreact: boolean;
|
|
310
322
|
/**
|
|
311
323
|
* `true` when the project (or any of its workspace packages) declares
|
|
312
324
|
* React Native or Expo as a dependency. Enables the `react-native`
|
|
@@ -355,8 +367,14 @@ interface DiagnoseResult {
|
|
|
355
367
|
skippedCheckReasons?: Record<string, string>;
|
|
356
368
|
project: ProjectInfo;
|
|
357
369
|
elapsedMilliseconds: number;
|
|
358
|
-
}
|
|
359
|
-
|
|
370
|
+
}
|
|
371
|
+
/**
|
|
372
|
+
* A single project to scan as part of a `diagnoseProjects()` batch.
|
|
373
|
+
* Scan options (`deadCode`, `lint`, etc.) are flat on the entry and
|
|
374
|
+
* layer on top of the global defaults — omitted fields fall through.
|
|
375
|
+
* `config` is a full `ReactDoctorConfig` override that replaces the
|
|
376
|
+
* on-disk `react-doctor.config.json` for this project's scan.
|
|
377
|
+
*/
|
|
360
378
|
//#endregion
|
|
361
379
|
//#region src/types/inspect.d.ts
|
|
362
380
|
interface InspectResult {
|
|
@@ -492,13 +510,18 @@ declare class PackageJsonNotFoundError extends Error {
|
|
|
492
510
|
readonly directory: string;
|
|
493
511
|
constructor(directory: string, options?: ErrorOptions);
|
|
494
512
|
}
|
|
513
|
+
declare class NotADirectoryError extends Error {
|
|
514
|
+
readonly name = "NotADirectoryError";
|
|
515
|
+
readonly resolvedPath: string;
|
|
516
|
+
constructor(resolvedPath: string, options?: ErrorOptions);
|
|
517
|
+
}
|
|
495
518
|
declare class AmbiguousProjectError extends Error {
|
|
496
519
|
readonly name = "AmbiguousProjectError";
|
|
497
520
|
readonly directory: string;
|
|
498
521
|
readonly candidates: readonly string[];
|
|
499
522
|
constructor(directory: string, candidates: readonly string[], options?: ErrorOptions);
|
|
500
523
|
}
|
|
501
|
-
declare const isProjectDiscoveryError: (value: unknown) => value is ProjectNotFoundError | NoReactDependencyError | PackageJsonNotFoundError | AmbiguousProjectError; //#endregion
|
|
524
|
+
declare const isProjectDiscoveryError: (value: unknown) => value is ProjectNotFoundError | NoReactDependencyError | PackageJsonNotFoundError | NotADirectoryError | AmbiguousProjectError; //#endregion
|
|
502
525
|
//#region src/project-info/utils/is-directory.d.ts
|
|
503
526
|
//#endregion
|
|
504
527
|
//#region src/errors.d.ts
|
|
@@ -651,7 +674,41 @@ declare const summarizeDiagnostics: (diagnostics: Diagnostic[], worstScore?: num
|
|
|
651
674
|
//#endregion
|
|
652
675
|
//#region ../api/dist/index.d.ts
|
|
653
676
|
//#region src/diagnose.d.ts
|
|
654
|
-
declare const diagnose: (directory: string, options?: DiagnoseOptions) => Promise<DiagnoseResult>;
|
|
677
|
+
declare const diagnose: (directory: string, options?: DiagnoseOptions) => Promise<DiagnoseResult>;
|
|
678
|
+
/**
|
|
679
|
+
* Scan multiple projects in parallel and return per-project scores,
|
|
680
|
+
* diagnostics, and an aggregate score (worst-of across all projects).
|
|
681
|
+
*
|
|
682
|
+
* Each project runs its own independent `runInspect` pipeline — the
|
|
683
|
+
* same pipeline `diagnose()` uses — so per-project config overrides,
|
|
684
|
+
* dead-code analysis, and scoring all work identically to a single
|
|
685
|
+
* `diagnose()` call.
|
|
686
|
+
*
|
|
687
|
+
* Projects that fail (e.g. missing `package.json`, no React dependency)
|
|
688
|
+
* are included in the result with `ok: false` rather than aborting the
|
|
689
|
+
* entire batch, so callers always receive partial results.
|
|
690
|
+
*
|
|
691
|
+
* ```ts
|
|
692
|
+
* const result = await diagnoseProjects({
|
|
693
|
+
* projects: [
|
|
694
|
+
* { directory: "packages/app" },
|
|
695
|
+
* { directory: "packages/shared", deadCode: false },
|
|
696
|
+
* { directory: "packages/admin", config: {
|
|
697
|
+
* rules: { "react-doctor/no-array-index-as-key": "off" },
|
|
698
|
+
* }},
|
|
699
|
+
* ],
|
|
700
|
+
* concurrency: 4,
|
|
701
|
+
* });
|
|
702
|
+
*
|
|
703
|
+
* for (const project of result.projects) {
|
|
704
|
+
* if (project.ok) {
|
|
705
|
+
* console.log(project.directory, project.score);
|
|
706
|
+
* } else {
|
|
707
|
+
* console.error(project.directory, project.error);
|
|
708
|
+
* }
|
|
709
|
+
* }
|
|
710
|
+
* ```
|
|
711
|
+
*/
|
|
655
712
|
//#endregion
|
|
656
713
|
//#region src/index.d.ts
|
|
657
714
|
declare const clearCaches: () => void;
|
|
@@ -662,5 +719,5 @@ interface ToJsonReportOptions {
|
|
|
662
719
|
}
|
|
663
720
|
declare const toJsonReport: (result: DiagnoseResult, options: ToJsonReportOptions) => JsonReport;
|
|
664
721
|
//#endregion
|
|
665
|
-
export { AmbiguousProjectError, type DiagnoseOptions, type DiagnoseResult, type Diagnostic, type DiffInfo, type JsonReport, type JsonReportDiffInfo, type JsonReportError, type JsonReportMode, type JsonReportProjectEntry, type JsonReportSummary, NoReactDependencyError, PackageJsonNotFoundError, type ProjectInfo, ProjectNotFoundError, type ReactDoctorConfig, ReactDoctorError, type ScoreResult, buildJsonReport, buildJsonReportError, clearCaches, diagnose, filterSourceFiles, getDiffInfo, isProjectDiscoveryError, isReactDoctorError, summarizeDiagnostics, toJsonReport };
|
|
722
|
+
export { AmbiguousProjectError, type DiagnoseOptions, type DiagnoseResult, type Diagnostic, type DiffInfo, type JsonReport, type JsonReportDiffInfo, type JsonReportError, type JsonReportMode, type JsonReportProjectEntry, type JsonReportSummary, NoReactDependencyError, NotADirectoryError, PackageJsonNotFoundError, type ProjectInfo, ProjectNotFoundError, type ReactDoctorConfig, ReactDoctorError, type ScoreResult, buildJsonReport, buildJsonReportError, clearCaches, diagnose, filterSourceFiles, getDiffInfo, isProjectDiscoveryError, isReactDoctorError, summarizeDiagnostics, toJsonReport };
|
|
666
723
|
//# sourceMappingURL=index.d.ts.map
|