react-doctor 0.1.3 → 0.1.4
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 +93 -67
- package/dist/eslint-plugin.js +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -214,7 +214,7 @@ const finalize = (method, originalText, displayText) => {
|
|
|
214
214
|
sharedInstance.stop();
|
|
215
215
|
ora({
|
|
216
216
|
text: displayText,
|
|
217
|
-
indent:
|
|
217
|
+
indent: 0
|
|
218
218
|
}).start()[method](displayText);
|
|
219
219
|
const [remainingText] = pendingTexts;
|
|
220
220
|
if (remainingText) sharedInstance.text = remainingText;
|
|
@@ -226,7 +226,7 @@ const spinner = (text) => ({ start() {
|
|
|
226
226
|
pendingTexts.add(text);
|
|
227
227
|
if (!sharedInstance) sharedInstance = ora({
|
|
228
228
|
text,
|
|
229
|
-
indent:
|
|
229
|
+
indent: 0
|
|
230
230
|
}).start();
|
|
231
231
|
else sharedInstance.text = text;
|
|
232
232
|
const handle = {
|
|
@@ -304,28 +304,6 @@ const runInstallSkill = async (options = {}) => {
|
|
|
304
304
|
}
|
|
305
305
|
};
|
|
306
306
|
//#endregion
|
|
307
|
-
//#region src/utils/build-category-breakdown.ts
|
|
308
|
-
const buildCategoryBreakdown = (diagnostics) => {
|
|
309
|
-
const entriesByCategory = /* @__PURE__ */ new Map();
|
|
310
|
-
for (const diagnostic of diagnostics) {
|
|
311
|
-
const existingEntry = entriesByCategory.get(diagnostic.category) ?? {
|
|
312
|
-
category: diagnostic.category,
|
|
313
|
-
totalCount: 0,
|
|
314
|
-
errorCount: 0,
|
|
315
|
-
warningCount: 0
|
|
316
|
-
};
|
|
317
|
-
existingEntry.totalCount += 1;
|
|
318
|
-
if (diagnostic.severity === "error") existingEntry.errorCount += 1;
|
|
319
|
-
else existingEntry.warningCount += 1;
|
|
320
|
-
entriesByCategory.set(diagnostic.category, existingEntry);
|
|
321
|
-
}
|
|
322
|
-
return [...entriesByCategory.values()].sort((entryA, entryB) => {
|
|
323
|
-
if (entryA.errorCount !== entryB.errorCount) return entryB.errorCount - entryA.errorCount;
|
|
324
|
-
if (entryA.totalCount !== entryB.totalCount) return entryB.totalCount - entryA.totalCount;
|
|
325
|
-
return entryA.category.localeCompare(entryB.category);
|
|
326
|
-
});
|
|
327
|
-
};
|
|
328
|
-
//#endregion
|
|
329
307
|
//#region src/utils/build-hidden-diagnostics-summary.ts
|
|
330
308
|
const buildHiddenDiagnosticsSummary = (hiddenDiagnostics) => {
|
|
331
309
|
const errorCount = hiddenDiagnostics.filter((diagnostic) => diagnostic.severity === "error").length;
|
|
@@ -1675,6 +1653,32 @@ const loadConfig = (rootDirectory) => {
|
|
|
1675
1653
|
return null;
|
|
1676
1654
|
};
|
|
1677
1655
|
//#endregion
|
|
1656
|
+
//#region src/utils/wrap-indented-text.ts
|
|
1657
|
+
const wrapLine = (lineText, contentWidth) => {
|
|
1658
|
+
if (lineText.length <= contentWidth) return [lineText];
|
|
1659
|
+
const wrappedLines = [];
|
|
1660
|
+
let remainingText = lineText.trim();
|
|
1661
|
+
while (remainingText.length > contentWidth) {
|
|
1662
|
+
const candidateText = remainingText.slice(0, contentWidth);
|
|
1663
|
+
const breakIndex = candidateText.lastIndexOf(" ");
|
|
1664
|
+
if (breakIndex <= 0) {
|
|
1665
|
+
wrappedLines.push(candidateText);
|
|
1666
|
+
remainingText = remainingText.slice(contentWidth).trimStart();
|
|
1667
|
+
continue;
|
|
1668
|
+
}
|
|
1669
|
+
wrappedLines.push(remainingText.slice(0, breakIndex));
|
|
1670
|
+
remainingText = remainingText.slice(breakIndex + 1).trimStart();
|
|
1671
|
+
}
|
|
1672
|
+
if (remainingText.length > 0) wrappedLines.push(remainingText);
|
|
1673
|
+
return wrappedLines;
|
|
1674
|
+
};
|
|
1675
|
+
const wrapIndentedText = (text, linePrefix, width) => {
|
|
1676
|
+
const contentWidth = width - linePrefix.length;
|
|
1677
|
+
if (contentWidth <= 0) return indentOnly(text, linePrefix);
|
|
1678
|
+
return text.split("\n").flatMap((lineText) => wrapLine(lineText, contentWidth)).map((lineText) => `${linePrefix}${lineText}`).join("\n");
|
|
1679
|
+
};
|
|
1680
|
+
const indentOnly = (text, linePrefix) => text.split("\n").map((lineText) => `${linePrefix}${lineText}`).join("\n");
|
|
1681
|
+
//#endregion
|
|
1678
1682
|
//#region src/utils/resolve-compatible-node.ts
|
|
1679
1683
|
const parseNodeVersion = (versionString) => {
|
|
1680
1684
|
const [major = 0, minor = 0, patch = 0] = versionString.replace(/^v/, "").trim().split(".").map(Number);
|
|
@@ -3154,6 +3158,7 @@ const parseOxlintOutput = (stdout) => {
|
|
|
3154
3158
|
severity: diagnostic.severity,
|
|
3155
3159
|
message: cleaned.message,
|
|
3156
3160
|
help: cleaned.help,
|
|
3161
|
+
url: diagnostic.url,
|
|
3157
3162
|
line: primaryLabel?.span.line ?? 0,
|
|
3158
3163
|
column: primaryLabel?.span.column ?? 0,
|
|
3159
3164
|
category: resolveDiagnosticCategory(plugin, rule)
|
|
@@ -3285,6 +3290,11 @@ const buildVerboseSiteMap = (diagnostics) => {
|
|
|
3285
3290
|
return fileSites;
|
|
3286
3291
|
};
|
|
3287
3292
|
const formatSiteCountBadge = (count) => count > 1 ? `×${count}` : "";
|
|
3293
|
+
const formatIssueCount = (count) => `${count} ${count === 1 ? "issue" : "issues"}`;
|
|
3294
|
+
const toRuleTitle = (ruleName) => {
|
|
3295
|
+
const readableRuleName = ruleName.replace(/^(no|prefer|require|use)-/, "").replace(/^(nextjs|tanstack-start)-/, "").replaceAll("-", " ");
|
|
3296
|
+
return (readableRuleName.charAt(0).toUpperCase() + readableRuleName.slice(1)).replace(/\b(css|html|url|svg|jsx|api|ua)\b/gi, (match) => match.toUpperCase());
|
|
3297
|
+
};
|
|
3288
3298
|
const computeRuleNameColumnWidth = (ruleKeys) => {
|
|
3289
3299
|
const longestRuleNameLength = ruleKeys.reduce((longest, ruleKey) => Math.max(longest, ruleKey.length), 0);
|
|
3290
3300
|
return Math.max(36, longestRuleNameLength);
|
|
@@ -3296,6 +3306,9 @@ const padRuleNameToColumn = (ruleName, columnWidth) => {
|
|
|
3296
3306
|
const grayLine = (text) => {
|
|
3297
3307
|
logger.log(highlighter.gray(text));
|
|
3298
3308
|
};
|
|
3309
|
+
const grayWrappedLine = (text, linePrefix) => {
|
|
3310
|
+
grayLine(wrapIndentedText(text, linePrefix, 88));
|
|
3311
|
+
};
|
|
3299
3312
|
const printCompactRuleGroupLine = (ruleKey, ruleDiagnostics, ruleNameColumnWidth) => {
|
|
3300
3313
|
const firstDiagnostic = ruleDiagnostics[0];
|
|
3301
3314
|
const icon = colorizeBySeverity(firstDiagnostic.severity === "error" ? "✗" : "⚠", firstDiagnostic.severity);
|
|
@@ -3304,13 +3317,38 @@ const printCompactRuleGroupLine = (ruleKey, ruleDiagnostics, ruleNameColumnWidth
|
|
|
3304
3317
|
const trailingBadge = siteCountBadge.length > 0 ? ` ${highlighter.gray(siteCountBadge)}` : "";
|
|
3305
3318
|
logger.log(` ${icon} ${ruleNameRendering}${trailingBadge}`);
|
|
3306
3319
|
};
|
|
3307
|
-
const
|
|
3308
|
-
|
|
3320
|
+
const getWorstSeverity = (diagnostics) => diagnostics.some((diagnostic) => diagnostic.severity === "error") ? "error" : "warning";
|
|
3321
|
+
const buildCategoryDiagnosticGroups = (diagnostics) => {
|
|
3322
|
+
return [...groupBy(diagnostics, (diagnostic) => diagnostic.category).entries()].map(([category, categoryDiagnostics]) => {
|
|
3323
|
+
return {
|
|
3324
|
+
category,
|
|
3325
|
+
diagnostics: categoryDiagnostics,
|
|
3326
|
+
ruleGroups: sortByImportance([...groupBy(categoryDiagnostics, (diagnostic) => `${diagnostic.plugin}/${diagnostic.rule}`).entries()])
|
|
3327
|
+
};
|
|
3328
|
+
}).toSorted((categoryGroupA, categoryGroupB) => {
|
|
3329
|
+
const severityDelta = SEVERITY_ORDER[getWorstSeverity(categoryGroupA.diagnostics)] - SEVERITY_ORDER[getWorstSeverity(categoryGroupB.diagnostics)];
|
|
3330
|
+
if (severityDelta !== 0) return severityDelta;
|
|
3331
|
+
if (categoryGroupA.diagnostics.length !== categoryGroupB.diagnostics.length) return categoryGroupB.diagnostics.length - categoryGroupA.diagnostics.length;
|
|
3332
|
+
return categoryGroupA.category.localeCompare(categoryGroupB.category);
|
|
3333
|
+
});
|
|
3334
|
+
};
|
|
3335
|
+
const printDefaultRuleGroup = (ruleKey, ruleDiagnostics, rootDirectory) => {
|
|
3309
3336
|
const firstDiagnostic = ruleDiagnostics[0];
|
|
3310
|
-
|
|
3311
|
-
|
|
3337
|
+
const ruleTitle = toRuleTitle(firstDiagnostic.rule);
|
|
3338
|
+
const icon = colorizeBySeverity(firstDiagnostic.severity === "error" ? "✗" : "⚠", firstDiagnostic.severity);
|
|
3339
|
+
const siteCountBadge = formatSiteCountBadge(ruleDiagnostics.length);
|
|
3340
|
+
const trailingBadge = siteCountBadge.length > 0 ? ` ${highlighter.gray(siteCountBadge)}` : "";
|
|
3341
|
+
logger.log(` ${icon} ${ruleTitle}${trailingBadge}`);
|
|
3342
|
+
grayWrappedLine(firstDiagnostic.message, " ");
|
|
3343
|
+
if (firstDiagnostic.help) grayWrappedLine(firstDiagnostic.help, " ");
|
|
3344
|
+
if (firstDiagnostic.url) grayLine(` ${firstDiagnostic.url}`);
|
|
3312
3345
|
const firstLocation = ruleDiagnostics.find((diagnostic) => diagnostic.line > 0);
|
|
3313
|
-
if (firstLocation) grayLine(`
|
|
3346
|
+
if (firstLocation) grayLine(` ${toRelativePath(firstLocation.filePath, rootDirectory)}:${firstLocation.line}`);
|
|
3347
|
+
};
|
|
3348
|
+
const printDefaultCategoryGroup = (categoryGroup, visibleRuleGroups, rootDirectory) => {
|
|
3349
|
+
const issueCount = formatIssueCount(categoryGroup.diagnostics.length);
|
|
3350
|
+
logger.log(`${highlighter.bold(categoryGroup.category)} ${highlighter.dim(issueCount)}`);
|
|
3351
|
+
for (const [ruleKey, ruleDiagnostics] of visibleRuleGroups) printDefaultRuleGroup(ruleKey, ruleDiagnostics, rootDirectory);
|
|
3314
3352
|
logger.break();
|
|
3315
3353
|
};
|
|
3316
3354
|
const printVerboseRuleGroup = (ruleKey, ruleDiagnostics, ruleNameColumnWidth) => {
|
|
@@ -3326,22 +3364,36 @@ const printVerboseRuleGroup = (ruleKey, ruleDiagnostics, ruleNameColumnWidth) =>
|
|
|
3326
3364
|
else grayLine(` ${filePath}`);
|
|
3327
3365
|
logger.break();
|
|
3328
3366
|
};
|
|
3367
|
+
const printDefaultDiagnostics = (diagnostics, rootDirectory) => {
|
|
3368
|
+
const categoryGroups = buildCategoryDiagnosticGroups(diagnostics);
|
|
3369
|
+
const hiddenRuleGroups = [];
|
|
3370
|
+
const visibleCategoryGroups = categoryGroups.slice(0, 5);
|
|
3371
|
+
const hiddenCategoryGroups = categoryGroups.slice(5);
|
|
3372
|
+
for (const categoryGroup of visibleCategoryGroups) {
|
|
3373
|
+
const visibleRuleGroups = categoryGroup.ruleGroups.slice(0, 3);
|
|
3374
|
+
const remainingRuleGroups = categoryGroup.ruleGroups.slice(3);
|
|
3375
|
+
printDefaultCategoryGroup(categoryGroup, visibleRuleGroups, rootDirectory);
|
|
3376
|
+
hiddenRuleGroups.push(...remainingRuleGroups);
|
|
3377
|
+
}
|
|
3378
|
+
hiddenRuleGroups.push(...hiddenCategoryGroups.flatMap((categoryGroup) => categoryGroup.ruleGroups));
|
|
3379
|
+
if (hiddenRuleGroups.length > 0) printHiddenDiagnosticsSummary(hiddenRuleGroups);
|
|
3380
|
+
};
|
|
3329
3381
|
const printDiagnostics = (diagnostics, isVerbose, rootDirectory) => {
|
|
3330
|
-
|
|
3331
|
-
|
|
3332
|
-
|
|
3382
|
+
if (!isVerbose) {
|
|
3383
|
+
printDefaultDiagnostics(diagnostics, rootDirectory);
|
|
3384
|
+
return;
|
|
3385
|
+
}
|
|
3386
|
+
const visibleRuleGroups = sortByImportance([...groupBy(diagnostics, (diagnostic) => `${diagnostic.plugin}/${diagnostic.rule}`).entries()]);
|
|
3333
3387
|
const ruleNameColumnWidth = computeRuleNameColumnWidth(visibleRuleGroups.map(([ruleKey]) => ruleKey));
|
|
3334
3388
|
visibleRuleGroups.forEach(([ruleKey, ruleDiagnostics]) => {
|
|
3335
|
-
|
|
3336
|
-
printVerboseRuleGroup(ruleKey, ruleDiagnostics, ruleNameColumnWidth);
|
|
3337
|
-
return;
|
|
3338
|
-
}
|
|
3339
|
-
printDetailedRuleGroup(ruleKey, ruleDiagnostics, rootDirectory, ruleNameColumnWidth);
|
|
3389
|
+
printVerboseRuleGroup(ruleKey, ruleDiagnostics, ruleNameColumnWidth);
|
|
3340
3390
|
});
|
|
3341
|
-
if (hiddenRuleGroups.length > 0) printHiddenDiagnosticsSummary(hiddenRuleGroups);
|
|
3342
3391
|
};
|
|
3343
3392
|
const printHiddenDiagnosticsSummary = (hiddenRuleGroups) => {
|
|
3344
|
-
const renderedParts = buildHiddenDiagnosticsSummary(hiddenRuleGroups.flatMap(([, ruleDiagnostics]) => ruleDiagnostics)).map((part) =>
|
|
3393
|
+
const renderedParts = buildHiddenDiagnosticsSummary(hiddenRuleGroups.flatMap(([, ruleDiagnostics]) => ruleDiagnostics)).map((part) => {
|
|
3394
|
+
const [icon, ...labelParts] = part.text.split(" ");
|
|
3395
|
+
return `${colorizeBySeverity(icon, part.severity)} ${highlighter.dim(labelParts.join(" "))}`;
|
|
3396
|
+
});
|
|
3345
3397
|
logger.log(` ${renderedParts.join(" ")}`);
|
|
3346
3398
|
grayLine(" Run `npx react-doctor@latest . --verbose` to get all details");
|
|
3347
3399
|
logger.break();
|
|
@@ -3361,6 +3413,7 @@ const formatRuleSummary = (ruleKey, ruleDiagnostics) => {
|
|
|
3361
3413
|
firstDiagnostic.message
|
|
3362
3414
|
];
|
|
3363
3415
|
if (firstDiagnostic.help) sections.push("", `Suggestion: ${firstDiagnostic.help}`);
|
|
3416
|
+
if (firstDiagnostic.url) sections.push("", `Docs: ${firstDiagnostic.url}`);
|
|
3364
3417
|
sections.push("", "Files:");
|
|
3365
3418
|
const fileSites = buildVerboseSiteMap(ruleDiagnostics);
|
|
3366
3419
|
for (const [filePath, sites] of fileSites) if (sites.length > 0) for (const site of sites) {
|
|
@@ -3432,32 +3485,6 @@ const printNoScoreHeader = (noScoreMessage) => {
|
|
|
3432
3485
|
logger.log(` ${highlighter.gray(noScoreMessage)}`);
|
|
3433
3486
|
logger.break();
|
|
3434
3487
|
};
|
|
3435
|
-
const buildCategoryBar = (count, maximumCount, useErrorColor) => {
|
|
3436
|
-
if (maximumCount === 0) return highlighter.dim("░".repeat(16));
|
|
3437
|
-
const filledCount = Math.max(1, Math.round(count / maximumCount * 16));
|
|
3438
|
-
const cappedFilledCount = Math.min(filledCount, 16);
|
|
3439
|
-
const emptyCount = 16 - cappedFilledCount;
|
|
3440
|
-
const filledSegment = "█".repeat(cappedFilledCount);
|
|
3441
|
-
const emptySegment = "░".repeat(emptyCount);
|
|
3442
|
-
return `${useErrorColor ? highlighter.error(filledSegment) : highlighter.warn(filledSegment)}${highlighter.dim(emptySegment)}`;
|
|
3443
|
-
};
|
|
3444
|
-
const padCategoryLabel = (categoryLabel) => {
|
|
3445
|
-
if (categoryLabel.length >= 18) return categoryLabel;
|
|
3446
|
-
return categoryLabel + " ".repeat(18 - categoryLabel.length);
|
|
3447
|
-
};
|
|
3448
|
-
const printCategoryBreakdown = (entries) => {
|
|
3449
|
-
if (entries.length === 0) return;
|
|
3450
|
-
const maximumCount = Math.max(...entries.map((entry) => entry.totalCount));
|
|
3451
|
-
logger.dim(" By category");
|
|
3452
|
-
for (const entry of entries) {
|
|
3453
|
-
const paddedLabel = padCategoryLabel(entry.category);
|
|
3454
|
-
const categoryBar = buildCategoryBar(entry.totalCount, maximumCount, entry.errorCount > 0);
|
|
3455
|
-
const totalCountDisplay = String(entry.totalCount);
|
|
3456
|
-
const errorBadge = entry.errorCount > 0 ? ` ${highlighter.error(`${entry.errorCount}×`)}` : "";
|
|
3457
|
-
logger.log(` ${paddedLabel}${categoryBar} ${totalCountDisplay}${errorBadge}`);
|
|
3458
|
-
}
|
|
3459
|
-
logger.break();
|
|
3460
|
-
};
|
|
3461
3488
|
const buildShareUrl = (diagnostics, scoreResult, projectName) => {
|
|
3462
3489
|
const errorCount = diagnostics.filter((diagnostic) => diagnostic.severity === "error").length;
|
|
3463
3490
|
const warningCount = diagnostics.filter((diagnostic) => diagnostic.severity === "warning").length;
|
|
@@ -3483,7 +3510,6 @@ const printCountsSummaryLine = (diagnostics, totalSourceFileCount, elapsedMillis
|
|
|
3483
3510
|
logger.log(` ${issueCountColor(issueCountText)} ${highlighter.dim(`${fileCountText} ${elapsedTimeText}`)}`);
|
|
3484
3511
|
};
|
|
3485
3512
|
const printSummary = (diagnostics, elapsedMilliseconds, scoreResult, projectName, totalSourceFileCount, noScoreMessage, isOffline) => {
|
|
3486
|
-
printCategoryBreakdown(buildCategoryBreakdown(diagnostics));
|
|
3487
3513
|
if (scoreResult) printScoreHeader(scoreResult);
|
|
3488
3514
|
else printNoScoreHeader(noScoreMessage);
|
|
3489
3515
|
printCountsSummaryLine(diagnostics, totalSourceFileCount, elapsedMilliseconds);
|
|
@@ -4102,7 +4128,7 @@ const promptProjectSelection = async (workspacePackages, rootDirectory) => {
|
|
|
4102
4128
|
};
|
|
4103
4129
|
//#endregion
|
|
4104
4130
|
//#region src/cli.ts
|
|
4105
|
-
const VERSION = "0.1.
|
|
4131
|
+
const VERSION = "0.1.4";
|
|
4106
4132
|
const VALID_FAIL_ON_LEVELS = new Set([
|
|
4107
4133
|
"error",
|
|
4108
4134
|
"warning",
|
package/dist/eslint-plugin.js
CHANGED
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -2821,6 +2821,7 @@ const parseOxlintOutput = (stdout) => {
|
|
|
2821
2821
|
severity: diagnostic.severity,
|
|
2822
2822
|
message: cleaned.message,
|
|
2823
2823
|
help: cleaned.help,
|
|
2824
|
+
url: diagnostic.url,
|
|
2824
2825
|
line: primaryLabel?.span.line ?? 0,
|
|
2825
2826
|
column: primaryLabel?.span.column ?? 0,
|
|
2826
2827
|
category: resolveDiagnosticCategory(plugin, rule)
|
package/package.json
CHANGED