react-doctor 0.1.0 → 0.1.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/README.md +23 -0
- package/dist/cli.js +163 -128
- package/dist/eslint-plugin.js +1 -1
- package/dist/index.js +3 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -197,6 +197,29 @@ const counts = summarizeDiagnostics(result.diagnostics);
|
|
|
197
197
|
|
|
198
198
|
`react-doctor/api` re-exports `JsonReport`, `JsonReportSummary`, `JsonReportProjectEntry`, `JsonReportMode`, plus the lower-level `buildJsonReport` and `buildJsonReportError` builders. See [`packages/react-doctor/src/api.ts`](https://github.com/millionco/react-doctor/blob/main/packages/react-doctor/src/api.ts) for the full types.
|
|
199
199
|
|
|
200
|
+
## Leaderboard
|
|
201
|
+
|
|
202
|
+
Top React codebases scanned by React Doctor, ranked by score. Updated automatically from [millionco/react-doctor-benchmarks](https://github.com/millionco/react-doctor-benchmarks).
|
|
203
|
+
|
|
204
|
+
<!-- LEADERBOARD:START -->
|
|
205
|
+
<!-- prettier-ignore -->
|
|
206
|
+
| # | Repo | Score |
|
|
207
|
+
| -- | ---- | ----: |
|
|
208
|
+
| 1 | [executor](https://github.com/RhysSullivan/executor) | 96 |
|
|
209
|
+
| 2 | [nodejs.org](https://github.com/nodejs/nodejs.org) | 87 |
|
|
210
|
+
| 3 | [tldraw](https://github.com/tldraw/tldraw) | 76 |
|
|
211
|
+
| 4 | [t3code](https://github.com/pingdotgg/t3code) | 75 |
|
|
212
|
+
| 5 | [mastra](https://github.com/mastra-ai/mastra) | 70 |
|
|
213
|
+
| 6 | [excalidraw](https://github.com/excalidraw/excalidraw) | 69 |
|
|
214
|
+
| 7 | [payload](https://github.com/payloadcms/payload) | 69 |
|
|
215
|
+
| 8 | [better-auth](https://github.com/better-auth/better-auth) | 69 |
|
|
216
|
+
| 9 | [rocket.chat](https://github.com/RocketChat/Rocket.Chat) | 67 |
|
|
217
|
+
| 10 | [typebot](https://github.com/baptisteArno/typebot.io) | 66 |
|
|
218
|
+
|
|
219
|
+
<!-- LEADERBOARD:END -->
|
|
220
|
+
|
|
221
|
+
See the [full leaderboard](https://www.react.doctor/leaderboard).
|
|
222
|
+
|
|
200
223
|
## Resources & Contributing Back
|
|
201
224
|
|
|
202
225
|
Want to try it out? Check out [the demo](https://react.doctor).
|
package/dist/cli.js
CHANGED
|
@@ -89,7 +89,9 @@ const highlighter = {
|
|
|
89
89
|
warn: pc.yellow,
|
|
90
90
|
info: pc.cyan,
|
|
91
91
|
success: pc.green,
|
|
92
|
-
dim: pc.dim
|
|
92
|
+
dim: pc.dim,
|
|
93
|
+
gray: pc.gray,
|
|
94
|
+
bold: pc.bold
|
|
93
95
|
};
|
|
94
96
|
//#endregion
|
|
95
97
|
//#region src/utils/logger.ts
|
|
@@ -296,6 +298,28 @@ const runInstallSkill = async (options = {}) => {
|
|
|
296
298
|
}
|
|
297
299
|
};
|
|
298
300
|
//#endregion
|
|
301
|
+
//#region src/utils/build-category-breakdown.ts
|
|
302
|
+
const buildCategoryBreakdown = (diagnostics) => {
|
|
303
|
+
const entriesByCategory = /* @__PURE__ */ new Map();
|
|
304
|
+
for (const diagnostic of diagnostics) {
|
|
305
|
+
const existingEntry = entriesByCategory.get(diagnostic.category) ?? {
|
|
306
|
+
category: diagnostic.category,
|
|
307
|
+
totalCount: 0,
|
|
308
|
+
errorCount: 0,
|
|
309
|
+
warningCount: 0
|
|
310
|
+
};
|
|
311
|
+
existingEntry.totalCount += 1;
|
|
312
|
+
if (diagnostic.severity === "error") existingEntry.errorCount += 1;
|
|
313
|
+
else existingEntry.warningCount += 1;
|
|
314
|
+
entriesByCategory.set(diagnostic.category, existingEntry);
|
|
315
|
+
}
|
|
316
|
+
return [...entriesByCategory.values()].sort((entryA, entryB) => {
|
|
317
|
+
if (entryA.errorCount !== entryB.errorCount) return entryB.errorCount - entryA.errorCount;
|
|
318
|
+
if (entryA.totalCount !== entryB.totalCount) return entryB.totalCount - entryA.totalCount;
|
|
319
|
+
return entryA.category.localeCompare(entryB.category);
|
|
320
|
+
});
|
|
321
|
+
};
|
|
322
|
+
//#endregion
|
|
299
323
|
//#region src/utils/build-hidden-diagnostics-summary.ts
|
|
300
324
|
const buildHiddenDiagnosticsSummary = (hiddenDiagnostics) => {
|
|
301
325
|
const errorCount = hiddenDiagnostics.filter((diagnostic) => diagnostic.severity === "error").length;
|
|
@@ -1462,32 +1486,6 @@ const formatErrorMessage = (error) => error instanceof Error ? error.message ||
|
|
|
1462
1486
|
const formatErrorChain = (rootError) => collectErrorChain(rootError).map(formatErrorMessage).join(" → ");
|
|
1463
1487
|
const getErrorChainMessages = (rootError) => collectErrorChain(rootError).map(formatErrorMessage);
|
|
1464
1488
|
//#endregion
|
|
1465
|
-
//#region src/utils/framed-box.ts
|
|
1466
|
-
const createFramedLine = (plainText, renderedText = plainText) => ({
|
|
1467
|
-
plainText,
|
|
1468
|
-
renderedText
|
|
1469
|
-
});
|
|
1470
|
-
const renderFramedBoxString = (framedLines) => {
|
|
1471
|
-
if (framedLines.length === 0) return "";
|
|
1472
|
-
const borderColorizer = highlighter.dim;
|
|
1473
|
-
const outerIndent = " ".repeat(2);
|
|
1474
|
-
const horizontalPadding = " ".repeat(1);
|
|
1475
|
-
const maximumLineLength = Math.max(...framedLines.map((framedLine) => framedLine.plainText.length));
|
|
1476
|
-
const borderLine = "─".repeat(maximumLineLength + 2);
|
|
1477
|
-
const lines = [];
|
|
1478
|
-
lines.push(`${outerIndent}${borderColorizer(`┌${borderLine}┐`)}`);
|
|
1479
|
-
for (const framedLine of framedLines) {
|
|
1480
|
-
const trailingSpaces = " ".repeat(maximumLineLength - framedLine.plainText.length);
|
|
1481
|
-
lines.push(`${outerIndent}${borderColorizer("│")}${horizontalPadding}${framedLine.renderedText}${trailingSpaces}${horizontalPadding}${borderColorizer("│")}`);
|
|
1482
|
-
}
|
|
1483
|
-
lines.push(`${outerIndent}${borderColorizer(`└${borderLine}┘`)}`);
|
|
1484
|
-
return lines.join("\n");
|
|
1485
|
-
};
|
|
1486
|
-
const printFramedBox = (framedLines) => {
|
|
1487
|
-
const rendered = renderFramedBoxString(framedLines);
|
|
1488
|
-
if (rendered) logger.log(rendered);
|
|
1489
|
-
};
|
|
1490
|
-
//#endregion
|
|
1491
1489
|
//#region src/utils/group-by.ts
|
|
1492
1490
|
const groupBy = (items, keyFn) => {
|
|
1493
1491
|
const groups = /* @__PURE__ */ new Map();
|
|
@@ -3162,8 +3160,6 @@ const runOxlint = async (options) => {
|
|
|
3162
3160
|
return await spawnLintBatches();
|
|
3163
3161
|
} catch (error) {
|
|
3164
3162
|
if (extendsPaths.length === 0) throw error;
|
|
3165
|
-
const reason = error instanceof Error ? error.message : String(error);
|
|
3166
|
-
process.stderr.write(`[react-doctor] could not adopt existing lint config (${reason.split("\n")[0]}); retrying without extends. Set "adoptExistingLintConfig": false to silence.\n`);
|
|
3167
3163
|
writeOxlintConfig(createOxlintConfig({
|
|
3168
3164
|
pluginPath,
|
|
3169
3165
|
framework,
|
|
@@ -3208,33 +3204,71 @@ const buildVerboseSiteMap = (diagnostics) => {
|
|
|
3208
3204
|
}
|
|
3209
3205
|
return fileSites;
|
|
3210
3206
|
};
|
|
3211
|
-
const
|
|
3207
|
+
const formatSiteCountBadge = (count) => count > 1 ? `×${count}` : "";
|
|
3208
|
+
const computeRuleNameColumnWidth = (ruleKeys) => {
|
|
3209
|
+
const longestRuleNameLength = ruleKeys.reduce((longest, ruleKey) => Math.max(longest, ruleKey.length), 0);
|
|
3210
|
+
return Math.max(36, longestRuleNameLength);
|
|
3211
|
+
};
|
|
3212
|
+
const padRuleNameToColumn = (ruleName, columnWidth) => {
|
|
3213
|
+
if (ruleName.length >= columnWidth) return ruleName;
|
|
3214
|
+
return ruleName + " ".repeat(columnWidth - ruleName.length);
|
|
3215
|
+
};
|
|
3216
|
+
const grayLine = (text) => {
|
|
3217
|
+
logger.log(highlighter.gray(text));
|
|
3218
|
+
};
|
|
3219
|
+
const printCompactRuleGroupLine = (ruleKey, ruleDiagnostics, ruleNameColumnWidth) => {
|
|
3220
|
+
const firstDiagnostic = ruleDiagnostics[0];
|
|
3221
|
+
const icon = colorizeBySeverity(firstDiagnostic.severity === "error" ? "✗" : "⚠", firstDiagnostic.severity);
|
|
3222
|
+
const siteCountBadge = formatSiteCountBadge(ruleDiagnostics.length);
|
|
3223
|
+
const ruleNameRendering = siteCountBadge.length > 0 ? colorizeBySeverity(padRuleNameToColumn(ruleKey, ruleNameColumnWidth), firstDiagnostic.severity) : colorizeBySeverity(ruleKey, firstDiagnostic.severity);
|
|
3224
|
+
const trailingBadge = siteCountBadge.length > 0 ? ` ${highlighter.gray(siteCountBadge)}` : "";
|
|
3225
|
+
logger.log(` ${icon} ${ruleNameRendering}${trailingBadge}`);
|
|
3226
|
+
};
|
|
3227
|
+
const printDetailedRuleGroup = (ruleKey, ruleDiagnostics, rootDirectory, ruleNameColumnWidth) => {
|
|
3228
|
+
printCompactRuleGroupLine(ruleKey, ruleDiagnostics, ruleNameColumnWidth);
|
|
3229
|
+
const firstDiagnostic = ruleDiagnostics[0];
|
|
3230
|
+
grayLine(indentMultilineText(firstDiagnostic.message, " "));
|
|
3231
|
+
if (firstDiagnostic.help) grayLine(indentMultilineText(`→ ${firstDiagnostic.help}`, " "));
|
|
3232
|
+
const firstLocation = ruleDiagnostics.find((diagnostic) => diagnostic.line > 0);
|
|
3233
|
+
if (firstLocation) grayLine(` ${toRelativePath(firstLocation.filePath, rootDirectory)}:${firstLocation.line}`);
|
|
3234
|
+
logger.break();
|
|
3235
|
+
};
|
|
3236
|
+
const printVerboseRuleGroup = (ruleKey, ruleDiagnostics, ruleNameColumnWidth) => {
|
|
3237
|
+
printCompactRuleGroupLine(ruleKey, ruleDiagnostics, ruleNameColumnWidth);
|
|
3238
|
+
const firstDiagnostic = ruleDiagnostics[0];
|
|
3239
|
+
grayLine(indentMultilineText(firstDiagnostic.message, " "));
|
|
3240
|
+
if (firstDiagnostic.help) grayLine(indentMultilineText(`→ ${firstDiagnostic.help}`, " "));
|
|
3241
|
+
const fileSites = buildVerboseSiteMap(ruleDiagnostics);
|
|
3242
|
+
for (const [filePath, sites] of fileSites) if (sites.length > 0) for (const site of sites) {
|
|
3243
|
+
grayLine(` ${filePath}:${site.line}`);
|
|
3244
|
+
if (site.suppressionHint) grayLine(` ↳ ${site.suppressionHint}`);
|
|
3245
|
+
}
|
|
3246
|
+
else grayLine(` ${filePath}`);
|
|
3247
|
+
logger.break();
|
|
3248
|
+
};
|
|
3249
|
+
const printDiagnostics = (diagnostics, isVerbose, rootDirectory) => {
|
|
3212
3250
|
const sortedRuleGroups = sortByImportance([...groupBy(diagnostics, (diagnostic) => `${diagnostic.plugin}/${diagnostic.rule}`).entries()]);
|
|
3213
|
-
const visibleRuleGroups = isVerbose ? sortedRuleGroups : sortedRuleGroups.slice(0,
|
|
3214
|
-
const hiddenRuleGroups = isVerbose ? [] : sortedRuleGroups.slice(
|
|
3215
|
-
|
|
3216
|
-
|
|
3217
|
-
const icon = colorizeBySeverity(firstDiagnostic.severity === "error" ? "✗" : "⚠", firstDiagnostic.severity);
|
|
3218
|
-
const count = ruleDiagnostics.length;
|
|
3219
|
-
const countLabel = count > 1 ? colorizeBySeverity(` (${count})`, firstDiagnostic.severity) : "";
|
|
3220
|
-
logger.log(` ${icon} ${firstDiagnostic.message}${countLabel}`);
|
|
3221
|
-
if (firstDiagnostic.help) logger.dim(indentMultilineText(firstDiagnostic.help, " "));
|
|
3251
|
+
const visibleRuleGroups = isVerbose ? sortedRuleGroups : sortedRuleGroups.slice(0, 5);
|
|
3252
|
+
const hiddenRuleGroups = isVerbose ? [] : sortedRuleGroups.slice(5);
|
|
3253
|
+
const ruleNameColumnWidth = computeRuleNameColumnWidth(visibleRuleGroups.map(([ruleKey]) => ruleKey));
|
|
3254
|
+
visibleRuleGroups.forEach(([ruleKey, ruleDiagnostics], visibleIndex) => {
|
|
3222
3255
|
if (isVerbose) {
|
|
3223
|
-
|
|
3224
|
-
|
|
3225
|
-
logger.dim(` ${filePath}:${site.line}`);
|
|
3226
|
-
if (site.suppressionHint) logger.dim(` ↳ ${site.suppressionHint}`);
|
|
3227
|
-
}
|
|
3228
|
-
else logger.dim(` ${filePath}`);
|
|
3256
|
+
printVerboseRuleGroup(ruleKey, ruleDiagnostics, ruleNameColumnWidth);
|
|
3257
|
+
return;
|
|
3229
3258
|
}
|
|
3230
|
-
|
|
3231
|
-
|
|
3259
|
+
if (visibleIndex < 1) {
|
|
3260
|
+
printDetailedRuleGroup(ruleKey, ruleDiagnostics, rootDirectory, ruleNameColumnWidth);
|
|
3261
|
+
return;
|
|
3262
|
+
}
|
|
3263
|
+
printCompactRuleGroupLine(ruleKey, ruleDiagnostics, ruleNameColumnWidth);
|
|
3264
|
+
});
|
|
3265
|
+
if (visibleRuleGroups.length > 1 && !isVerbose) logger.break();
|
|
3232
3266
|
if (hiddenRuleGroups.length > 0) printHiddenDiagnosticsSummary(hiddenRuleGroups);
|
|
3233
3267
|
};
|
|
3234
3268
|
const printHiddenDiagnosticsSummary = (hiddenRuleGroups) => {
|
|
3235
3269
|
const renderedParts = buildHiddenDiagnosticsSummary(hiddenRuleGroups.flatMap(([, ruleDiagnostics]) => ruleDiagnostics)).map((part) => colorizeBySeverity(part.text, part.severity));
|
|
3236
3270
|
logger.log(` ${renderedParts.join(" ")}`);
|
|
3237
|
-
|
|
3271
|
+
grayLine(" Run `npx react-doctor@latest . --verbose` to get all details");
|
|
3238
3272
|
logger.break();
|
|
3239
3273
|
};
|
|
3240
3274
|
const formatElapsedTime = (elapsedMilliseconds) => {
|
|
@@ -3277,37 +3311,76 @@ const buildScoreBarSegments = (score) => {
|
|
|
3277
3311
|
emptySegment: "░".repeat(emptyCount)
|
|
3278
3312
|
};
|
|
3279
3313
|
};
|
|
3280
|
-
const buildPlainScoreBar = (score) => {
|
|
3281
|
-
const { filledSegment, emptySegment } = buildScoreBarSegments(score);
|
|
3282
|
-
return `${filledSegment}${emptySegment}`;
|
|
3283
|
-
};
|
|
3284
3314
|
const buildScoreBar = (score) => {
|
|
3285
3315
|
const { filledSegment, emptySegment } = buildScoreBarSegments(score);
|
|
3286
3316
|
return colorizeByScore(filledSegment, score) + highlighter.dim(emptySegment);
|
|
3287
3317
|
};
|
|
3288
|
-
const printScoreGauge = (score, label) => {
|
|
3289
|
-
const scoreDisplay = colorizeByScore(`${score}`, score);
|
|
3290
|
-
const labelDisplay = colorizeByScore(label, score);
|
|
3291
|
-
logger.log(` ${scoreDisplay} / 100 ${labelDisplay}`);
|
|
3292
|
-
logger.break();
|
|
3293
|
-
logger.log(` ${buildScoreBar(score)}`);
|
|
3294
|
-
logger.break();
|
|
3295
|
-
};
|
|
3296
3318
|
const getDoctorFace = (score) => {
|
|
3297
3319
|
if (score >= 75) return ["◠ ◠", " ▽ "];
|
|
3298
3320
|
if (score >= 50) return ["• •", " ─ "];
|
|
3299
3321
|
return ["x x", " ▽ "];
|
|
3300
3322
|
};
|
|
3301
|
-
const
|
|
3302
|
-
|
|
3303
|
-
|
|
3304
|
-
|
|
3305
|
-
|
|
3306
|
-
|
|
3307
|
-
|
|
3308
|
-
|
|
3323
|
+
const BRANDING_LINE = `React Doctor ${highlighter.dim("(www.react.doctor)")}`;
|
|
3324
|
+
const buildFaceRenderedLines = (score) => {
|
|
3325
|
+
const [eyes, mouth] = getDoctorFace(score);
|
|
3326
|
+
const colorize = (text) => colorizeByScore(text, score);
|
|
3327
|
+
return [
|
|
3328
|
+
"┌─────┐",
|
|
3329
|
+
`│ ${eyes} │`,
|
|
3330
|
+
`│ ${mouth} │`,
|
|
3331
|
+
"└─────┘"
|
|
3332
|
+
].map(colorize);
|
|
3333
|
+
};
|
|
3334
|
+
const printScoreHeader = (scoreResult) => {
|
|
3335
|
+
const renderedFaceLines = buildFaceRenderedLines(scoreResult.score);
|
|
3336
|
+
const scoreNumber = colorizeByScore(`${scoreResult.score}`, scoreResult.score);
|
|
3337
|
+
const scoreLabel = colorizeByScore(scoreResult.label, scoreResult.score);
|
|
3338
|
+
const rightColumnLines = [
|
|
3339
|
+
`${scoreNumber} ${highlighter.dim(`/ 100`)} ${scoreLabel}`,
|
|
3340
|
+
buildScoreBar(scoreResult.score),
|
|
3341
|
+
BRANDING_LINE,
|
|
3342
|
+
""
|
|
3343
|
+
];
|
|
3344
|
+
for (let lineIndex = 0; lineIndex < renderedFaceLines.length; lineIndex += 1) {
|
|
3345
|
+
const rightColumnContent = rightColumnLines[lineIndex] ?? "";
|
|
3346
|
+
const separator = rightColumnContent.length > 0 ? " " : "";
|
|
3347
|
+
logger.log(` ${renderedFaceLines[lineIndex]}${separator}${rightColumnContent}`);
|
|
3348
|
+
}
|
|
3349
|
+
logger.break();
|
|
3350
|
+
};
|
|
3351
|
+
const printBrandingOnlyHeader = () => {
|
|
3352
|
+
logger.log(` ${BRANDING_LINE}`);
|
|
3353
|
+
logger.break();
|
|
3354
|
+
};
|
|
3355
|
+
const printNoScoreHeader = (noScoreMessage) => {
|
|
3356
|
+
logger.log(` ${BRANDING_LINE}`);
|
|
3357
|
+
logger.log(` ${highlighter.gray(noScoreMessage)}`);
|
|
3358
|
+
logger.break();
|
|
3359
|
+
};
|
|
3360
|
+
const buildCategoryBar = (count, maximumCount, useErrorColor) => {
|
|
3361
|
+
if (maximumCount === 0) return highlighter.dim("░".repeat(16));
|
|
3362
|
+
const filledCount = Math.max(1, Math.round(count / maximumCount * 16));
|
|
3363
|
+
const cappedFilledCount = Math.min(filledCount, 16);
|
|
3364
|
+
const emptyCount = 16 - cappedFilledCount;
|
|
3365
|
+
const filledSegment = "█".repeat(cappedFilledCount);
|
|
3366
|
+
const emptySegment = "░".repeat(emptyCount);
|
|
3367
|
+
return `${useErrorColor ? highlighter.error(filledSegment) : highlighter.warn(filledSegment)}${highlighter.dim(emptySegment)}`;
|
|
3368
|
+
};
|
|
3369
|
+
const padCategoryLabel = (categoryLabel) => {
|
|
3370
|
+
if (categoryLabel.length >= 18) return categoryLabel;
|
|
3371
|
+
return categoryLabel + " ".repeat(18 - categoryLabel.length);
|
|
3372
|
+
};
|
|
3373
|
+
const printCategoryBreakdown = (entries) => {
|
|
3374
|
+
if (entries.length === 0) return;
|
|
3375
|
+
const maximumCount = Math.max(...entries.map((entry) => entry.totalCount));
|
|
3376
|
+
logger.dim(" By category");
|
|
3377
|
+
for (const entry of entries) {
|
|
3378
|
+
const paddedLabel = padCategoryLabel(entry.category);
|
|
3379
|
+
const categoryBar = buildCategoryBar(entry.totalCount, maximumCount, entry.errorCount > 0);
|
|
3380
|
+
const totalCountDisplay = String(entry.totalCount);
|
|
3381
|
+
const errorBadge = entry.errorCount > 0 ? ` ${highlighter.error(`${entry.errorCount}×`)}` : "";
|
|
3382
|
+
logger.log(` ${paddedLabel}${categoryBar} ${totalCountDisplay}${errorBadge}`);
|
|
3309
3383
|
}
|
|
3310
|
-
logger.log(` React Doctor ${highlighter.dim("(www.react.doctor)")}`);
|
|
3311
3384
|
logger.break();
|
|
3312
3385
|
};
|
|
3313
3386
|
const buildShareUrl = (diagnostics, scoreResult, projectName) => {
|
|
@@ -3322,67 +3395,31 @@ const buildShareUrl = (diagnostics, scoreResult, projectName) => {
|
|
|
3322
3395
|
if (affectedFileCount > 0) params.set("f", String(affectedFileCount));
|
|
3323
3396
|
return `${SHARE_BASE_URL}?${params.toString()}`;
|
|
3324
3397
|
};
|
|
3325
|
-
const
|
|
3326
|
-
const lines = [];
|
|
3327
|
-
if (scoreResult) {
|
|
3328
|
-
const [eyes, mouth] = getDoctorFace(scoreResult.score);
|
|
3329
|
-
const scoreColorizer = (text) => colorizeByScore(text, scoreResult.score);
|
|
3330
|
-
lines.push(createFramedLine("┌─────┐", scoreColorizer("┌─────┐")));
|
|
3331
|
-
lines.push(createFramedLine(`│ ${eyes} │`, scoreColorizer(`│ ${eyes} │`)));
|
|
3332
|
-
lines.push(createFramedLine(`│ ${mouth} │`, scoreColorizer(`│ ${mouth} │`)));
|
|
3333
|
-
lines.push(createFramedLine("└─────┘", scoreColorizer("└─────┘")));
|
|
3334
|
-
lines.push(createFramedLine("React Doctor (www.react.doctor)", `React Doctor ${highlighter.dim("(www.react.doctor)")}`));
|
|
3335
|
-
lines.push(createFramedLine(""));
|
|
3336
|
-
const scoreLinePlainText = `${scoreResult.score} / 100 ${scoreResult.label}`;
|
|
3337
|
-
const scoreLineRenderedText = `${colorizeByScore(String(scoreResult.score), scoreResult.score)} / 100 ${colorizeByScore(scoreResult.label, scoreResult.score)}`;
|
|
3338
|
-
lines.push(createFramedLine(scoreLinePlainText, scoreLineRenderedText));
|
|
3339
|
-
lines.push(createFramedLine(""));
|
|
3340
|
-
lines.push(createFramedLine(buildPlainScoreBar(scoreResult.score), buildScoreBar(scoreResult.score)));
|
|
3341
|
-
lines.push(createFramedLine(""));
|
|
3342
|
-
} else {
|
|
3343
|
-
lines.push(createFramedLine("React Doctor (www.react.doctor)", `React Doctor ${highlighter.dim("(www.react.doctor)")}`));
|
|
3344
|
-
lines.push(createFramedLine(""));
|
|
3345
|
-
lines.push(createFramedLine(noScoreMessage, highlighter.dim(noScoreMessage)));
|
|
3346
|
-
lines.push(createFramedLine(""));
|
|
3347
|
-
}
|
|
3348
|
-
return lines;
|
|
3349
|
-
};
|
|
3350
|
-
const buildCountsSummaryLine = (diagnostics, totalSourceFileCount, elapsedMilliseconds) => {
|
|
3398
|
+
const printCountsSummaryLine = (diagnostics, totalSourceFileCount, elapsedMilliseconds) => {
|
|
3351
3399
|
const errorCount = diagnostics.filter((diagnostic) => diagnostic.severity === "error").length;
|
|
3352
3400
|
const warningCount = diagnostics.filter((diagnostic) => diagnostic.severity === "warning").length;
|
|
3353
3401
|
const affectedFileCount = collectAffectedFiles(diagnostics).size;
|
|
3354
|
-
const
|
|
3355
|
-
const
|
|
3356
|
-
const
|
|
3357
|
-
|
|
3358
|
-
const errorText = `✗ ${errorCount} error${errorCount === 1 ? "" : "s"}`;
|
|
3359
|
-
plainParts.push(errorText);
|
|
3360
|
-
renderedParts.push(highlighter.error(errorText));
|
|
3361
|
-
}
|
|
3362
|
-
if (warningCount > 0) {
|
|
3363
|
-
const warningText = `⚠ ${warningCount} warning${warningCount === 1 ? "" : "s"}`;
|
|
3364
|
-
plainParts.push(warningText);
|
|
3365
|
-
renderedParts.push(highlighter.warn(warningText));
|
|
3366
|
-
}
|
|
3402
|
+
const totalIssueCount = diagnostics.length;
|
|
3403
|
+
const elapsedTimeLabel = formatElapsedTime(elapsedMilliseconds);
|
|
3404
|
+
const issueCountColor = errorCount > 0 ? highlighter.error : warningCount > 0 ? highlighter.warn : highlighter.dim;
|
|
3405
|
+
const issueCountText = `${totalIssueCount} ${totalIssueCount === 1 ? "issue" : "issues"}`;
|
|
3367
3406
|
const fileCountText = totalSourceFileCount > 0 ? `across ${affectedFileCount}/${totalSourceFileCount} files` : `across ${affectedFileCount} file${affectedFileCount === 1 ? "" : "s"}`;
|
|
3368
|
-
const elapsedTimeText = `in ${
|
|
3369
|
-
|
|
3370
|
-
renderedParts.push(highlighter.dim(fileCountText), highlighter.dim(elapsedTimeText));
|
|
3371
|
-
return createFramedLine(plainParts.join(" "), renderedParts.join(" "));
|
|
3407
|
+
const elapsedTimeText = `in ${elapsedTimeLabel}`;
|
|
3408
|
+
logger.log(` ${issueCountColor(issueCountText)} ${highlighter.dim(`${fileCountText} ${elapsedTimeText}`)}`);
|
|
3372
3409
|
};
|
|
3373
3410
|
const printSummary = (diagnostics, elapsedMilliseconds, scoreResult, projectName, totalSourceFileCount, noScoreMessage, isOffline) => {
|
|
3374
|
-
|
|
3411
|
+
printCategoryBreakdown(buildCategoryBreakdown(diagnostics));
|
|
3412
|
+
if (scoreResult) printScoreHeader(scoreResult);
|
|
3413
|
+
else printNoScoreHeader(noScoreMessage);
|
|
3414
|
+
printCountsSummaryLine(diagnostics, totalSourceFileCount, elapsedMilliseconds);
|
|
3375
3415
|
try {
|
|
3376
3416
|
const diagnosticsDirectory = writeDiagnosticsDirectory(diagnostics);
|
|
3377
|
-
logger.
|
|
3378
|
-
|
|
3379
|
-
} catch {
|
|
3380
|
-
logger.break();
|
|
3381
|
-
}
|
|
3417
|
+
logger.log(highlighter.gray(` Full diagnostics written to ${diagnosticsDirectory}`));
|
|
3418
|
+
} catch {}
|
|
3382
3419
|
if (!isOffline) {
|
|
3383
|
-
const shareUrl = buildShareUrl(diagnostics, scoreResult, projectName);
|
|
3384
3420
|
logger.break();
|
|
3385
|
-
|
|
3421
|
+
const shareUrl = buildShareUrl(diagnostics, scoreResult, projectName);
|
|
3422
|
+
logger.log(` ${highlighter.bold("→ Share your results:")} ${highlighter.info(shareUrl)}`);
|
|
3386
3423
|
}
|
|
3387
3424
|
};
|
|
3388
3425
|
const resolveOxlintNode = async (isLintEnabled, isQuiet) => {
|
|
@@ -3565,15 +3602,13 @@ const runScan = async (directory, options, userConfig, startTime) => {
|
|
|
3565
3602
|
} else logger.success("No issues found!");
|
|
3566
3603
|
logger.break();
|
|
3567
3604
|
if (hasSkippedChecks) {
|
|
3568
|
-
|
|
3569
|
-
logger.
|
|
3570
|
-
} else if (scoreResult)
|
|
3571
|
-
|
|
3572
|
-
printScoreGauge(scoreResult.score, scoreResult.label);
|
|
3573
|
-
} else logger.dim(` ${noScoreMessage}`);
|
|
3605
|
+
printBrandingOnlyHeader();
|
|
3606
|
+
logger.log(highlighter.gray(" Score not shown — some checks could not complete."));
|
|
3607
|
+
} else if (scoreResult) printScoreHeader(scoreResult);
|
|
3608
|
+
else printNoScoreHeader(noScoreMessage);
|
|
3574
3609
|
return buildResult();
|
|
3575
3610
|
}
|
|
3576
|
-
printDiagnostics(diagnostics, options.verbose);
|
|
3611
|
+
printDiagnostics(diagnostics, options.verbose, directory);
|
|
3577
3612
|
const displayedSourceFileCount = isDiffMode ? includePaths.length : lintSourceFileCount;
|
|
3578
3613
|
const shouldShowShareLink = !options.offline && options.share;
|
|
3579
3614
|
printSummary(diagnostics, elapsedMilliseconds, scoreResult, projectInfo.projectName, displayedSourceFileCount, noScoreMessage, !shouldShowShareLink);
|
|
@@ -3991,7 +4026,7 @@ const promptProjectSelection = async (workspacePackages, rootDirectory) => {
|
|
|
3991
4026
|
};
|
|
3992
4027
|
//#endregion
|
|
3993
4028
|
//#region src/cli.ts
|
|
3994
|
-
const VERSION = "0.1.
|
|
4029
|
+
const VERSION = "0.1.1";
|
|
3995
4030
|
const VALID_FAIL_ON_LEVELS = new Set([
|
|
3996
4031
|
"error",
|
|
3997
4032
|
"warning",
|
package/dist/eslint-plugin.js
CHANGED
package/dist/index.js
CHANGED
|
@@ -487,7 +487,9 @@ const highlighter = {
|
|
|
487
487
|
warn: pc.yellow,
|
|
488
488
|
info: pc.cyan,
|
|
489
489
|
success: pc.green,
|
|
490
|
-
dim: pc.dim
|
|
490
|
+
dim: pc.dim,
|
|
491
|
+
gray: pc.gray,
|
|
492
|
+
bold: pc.bold
|
|
491
493
|
};
|
|
492
494
|
const logger = {
|
|
493
495
|
error(...args) {
|
|
@@ -2831,8 +2833,6 @@ const runOxlint = async (options) => {
|
|
|
2831
2833
|
return await spawnLintBatches();
|
|
2832
2834
|
} catch (error) {
|
|
2833
2835
|
if (extendsPaths.length === 0) throw error;
|
|
2834
|
-
const reason = error instanceof Error ? error.message : String(error);
|
|
2835
|
-
process.stderr.write(`[react-doctor] could not adopt existing lint config (${reason.split("\n")[0]}); retrying without extends. Set "adoptExistingLintConfig": false to silence.\n`);
|
|
2836
2836
|
writeOxlintConfig(createOxlintConfig({
|
|
2837
2837
|
pluginPath,
|
|
2838
2838
|
framework,
|
package/package.json
CHANGED