react-doctor 0.0.37 → 0.0.39
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 +22 -8
- package/dist/browser.d.ts +2 -0
- package/dist/browser.js +3 -0
- package/dist/cli.js +139 -86
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +347 -239
- package/dist/index.js.map +1 -1
- package/dist/process-browser-diagnostics-Cahx3_oy.d.ts +131 -0
- package/dist/process-browser-diagnostics-Cahx3_oy.d.ts.map +1 -0
- package/dist/process-browser-diagnostics-DpaZeYLI.js +365 -0
- package/dist/process-browser-diagnostics-DpaZeYLI.js.map +1 -0
- package/dist/worker.d.ts +2 -0
- package/dist/worker.js +3 -0
- package/package.json +9 -1
package/README.md
CHANGED
|
@@ -69,6 +69,8 @@ Supports Claude Code, Codex, GitHub Copilot, Gemini CLI, Cursor, OpenCode, Facto
|
|
|
69
69
|
| `project` | | Workspace project(s) to scan (comma-separated) |
|
|
70
70
|
| `diff` | | Base branch for diff mode. Only changed files are scanned |
|
|
71
71
|
| `github-token` | | When set on `pull_request` events, posts findings as a PR comment |
|
|
72
|
+
| `fail-on` | `error` | Exit with error code on diagnostics: `error`, `warning`, `none` |
|
|
73
|
+
| `offline` | `false` | Skip sending diagnostics to the react.doctor API |
|
|
72
74
|
| `node-version` | `20` | Node.js version to use |
|
|
73
75
|
|
|
74
76
|
The action outputs a `score` (0–100) you can use in subsequent steps.
|
|
@@ -90,6 +92,14 @@ Options:
|
|
|
90
92
|
-h, --help display help for command
|
|
91
93
|
```
|
|
92
94
|
|
|
95
|
+
## Browser API
|
|
96
|
+
|
|
97
|
+
Import `react-doctor/browser` to run the same **diagnostics merge, config-based filtering, timing, and scoring pipeline** as `react-doctor/api`’s `diagnose`, but with **caller-supplied** inputs: `project` metadata, a virtual `projectFiles` map (contents keyed by paths relative to `rootDirectory`) for ignore/suppression resolution, and a `runOxlint` callback that performs linting in your environment (for example a Web Worker with oxlint).
|
|
98
|
+
|
|
99
|
+
Git history, real filesystem discovery, knip, the CLI, staged-file detection, and interactive prompts are **not** available in the browser bundle; treat those as Node-only or supply equivalents yourself. `react-doctor/worker` re-exports the same browser-facing modules for worker targets.
|
|
100
|
+
|
|
101
|
+
If you call **`diagnoseCore`** yourself in the browser, pass **`calculateDiagnosticsScore`** from this package (re-exported as **`calculateScore`** on `react-doctor/browser`) so the bundle never pulls in Node-only proxy code.
|
|
102
|
+
|
|
93
103
|
## Configuration
|
|
94
104
|
|
|
95
105
|
Create a `react-doctor.config.json` in your project root to customize behavior:
|
|
@@ -119,14 +129,18 @@ If both exist, `react-doctor.config.json` takes precedence.
|
|
|
119
129
|
|
|
120
130
|
### Config options
|
|
121
131
|
|
|
122
|
-
| Key
|
|
123
|
-
|
|
|
124
|
-
| `ignore.rules`
|
|
125
|
-
| `ignore.files`
|
|
126
|
-
| `lint`
|
|
127
|
-
| `deadCode`
|
|
128
|
-
| `verbose`
|
|
129
|
-
| `diff`
|
|
132
|
+
| Key | Type | Default | Description |
|
|
133
|
+
| ----------------- | -------------------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------- |
|
|
134
|
+
| `ignore.rules` | `string[]` | `[]` | Rules to suppress, using the `plugin/rule` format shown in diagnostic output (e.g. `react/no-danger`, `knip/exports`, `knip/types`) |
|
|
135
|
+
| `ignore.files` | `string[]` | `[]` | File paths to exclude, supports glob patterns (`src/generated/**`, `**/*.test.tsx`) |
|
|
136
|
+
| `lint` | `boolean` | `true` | Enable/disable lint checks (same as `--no-lint`) |
|
|
137
|
+
| `deadCode` | `boolean` | `true` | Enable/disable dead code detection (same as `--no-dead-code`) |
|
|
138
|
+
| `verbose` | `boolean` | `false` | Show file details per rule (same as `--verbose`) |
|
|
139
|
+
| `diff` | `boolean \| string` | — | Force diff mode (`true`) or pin a base branch (`"main"`). Set to `false` to disable auto-detection. |
|
|
140
|
+
| `failOn` | `"error" \| "warning" \| "none"` | `"none"` | Exit with error code on diagnostics of the given severity or above |
|
|
141
|
+
| `customRulesOnly` | `boolean` | `false` | Disable built-in react/jsx-a11y/compiler rules, keeping only `react-doctor/*` plugin rules |
|
|
142
|
+
| `share` | `boolean` | `true` | Show the share-your-results URL after scanning |
|
|
143
|
+
| `textComponents` | `string[]` | `[]` | React Native only. Component names whose children should not trigger `rn-no-raw-text` (e.g. `["MyText", "Label.Bold"]`) |
|
|
130
144
|
|
|
131
145
|
CLI flags always override config values.
|
|
132
146
|
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { _ as ScoreResult, a as diagnoseBrowser, c as diagnoseCore, d as diagnose, f as calculateScore, g as ReactDoctorConfig, h as ProjectInfo, i as DiagnoseBrowserInput, l as BrowserDiagnoseInput, m as Diagnostic, n as ProcessBrowserDiagnosticsResult, o as DiagnoseCoreOptions, p as calculateScoreLocally, r as processBrowserDiagnostics, s as DiagnoseCoreResult, t as ProcessBrowserDiagnosticsInput, u as BrowserDiagnoseResult } from "./process-browser-diagnostics-Cahx3_oy.js";
|
|
2
|
+
export { type BrowserDiagnoseInput, type BrowserDiagnoseResult, type DiagnoseBrowserInput, type DiagnoseCoreOptions, type DiagnoseCoreResult, type Diagnostic, type ProcessBrowserDiagnosticsInput, type ProcessBrowserDiagnosticsResult, type ProjectInfo, type ReactDoctorConfig, type ScoreResult, calculateScore, calculateScoreLocally, diagnose, diagnoseBrowser, diagnoseCore, processBrowserDiagnostics };
|
package/dist/browser.js
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { a as calculateScore, i as diagnose, n as diagnoseBrowser, o as calculateScoreLocally, r as diagnoseCore, t as processBrowserDiagnostics } from "./process-browser-diagnostics-DpaZeYLI.js";
|
|
2
|
+
|
|
3
|
+
export { calculateScore, calculateScoreLocally, diagnose, diagnoseBrowser, diagnoseCore, processBrowserDiagnostics };
|
package/dist/cli.js
CHANGED
|
@@ -309,55 +309,7 @@ const IGNORED_DIRECTORIES = new Set([
|
|
|
309
309
|
]);
|
|
310
310
|
|
|
311
311
|
//#endregion
|
|
312
|
-
//#region src/
|
|
313
|
-
const readNpmConfigValue = (key) => {
|
|
314
|
-
try {
|
|
315
|
-
const value = execSync(`npm config get ${key}`, {
|
|
316
|
-
encoding: "utf-8",
|
|
317
|
-
stdio: [
|
|
318
|
-
"pipe",
|
|
319
|
-
"pipe",
|
|
320
|
-
"ignore"
|
|
321
|
-
]
|
|
322
|
-
}).trim();
|
|
323
|
-
if (value && value !== "null" && value !== "undefined") return value;
|
|
324
|
-
} catch {}
|
|
325
|
-
};
|
|
326
|
-
const resolveProxyUrl = () => process.env.HTTPS_PROXY ?? process.env.https_proxy ?? process.env.HTTP_PROXY ?? process.env.http_proxy ?? readNpmConfigValue("https-proxy") ?? readNpmConfigValue("proxy");
|
|
327
|
-
let isProxyUrlResolved = false;
|
|
328
|
-
let resolvedProxyUrl;
|
|
329
|
-
const getProxyUrl = () => {
|
|
330
|
-
if (isProxyUrlResolved) return resolvedProxyUrl;
|
|
331
|
-
isProxyUrlResolved = true;
|
|
332
|
-
resolvedProxyUrl = resolveProxyUrl();
|
|
333
|
-
return resolvedProxyUrl;
|
|
334
|
-
};
|
|
335
|
-
const createProxyDispatcher = async (proxyUrl) => {
|
|
336
|
-
try {
|
|
337
|
-
const { ProxyAgent } = await import("undici");
|
|
338
|
-
return new ProxyAgent(proxyUrl);
|
|
339
|
-
} catch {
|
|
340
|
-
return null;
|
|
341
|
-
}
|
|
342
|
-
};
|
|
343
|
-
const proxyFetch = async (url, init) => {
|
|
344
|
-
const controller = new AbortController();
|
|
345
|
-
const timeoutId = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
|
|
346
|
-
try {
|
|
347
|
-
const proxyUrl = getProxyUrl();
|
|
348
|
-
const dispatcher = proxyUrl ? await createProxyDispatcher(proxyUrl) : null;
|
|
349
|
-
return await fetch(url, {
|
|
350
|
-
...init,
|
|
351
|
-
signal: controller.signal,
|
|
352
|
-
...dispatcher ? { dispatcher } : {}
|
|
353
|
-
});
|
|
354
|
-
} finally {
|
|
355
|
-
clearTimeout(timeoutId);
|
|
356
|
-
}
|
|
357
|
-
};
|
|
358
|
-
|
|
359
|
-
//#endregion
|
|
360
|
-
//#region src/utils/calculate-score.ts
|
|
312
|
+
//#region src/core/calculate-score-locally.ts
|
|
361
313
|
const getScoreLabel = (score) => {
|
|
362
314
|
if (score >= SCORE_GOOD_THRESHOLD) return "Great";
|
|
363
315
|
if (score >= SCORE_OK_THRESHOLD) return "Needs work";
|
|
@@ -388,20 +340,90 @@ const calculateScoreLocally = (diagnostics) => {
|
|
|
388
340
|
label: getScoreLabel(score)
|
|
389
341
|
};
|
|
390
342
|
};
|
|
391
|
-
|
|
343
|
+
|
|
344
|
+
//#endregion
|
|
345
|
+
//#region src/core/try-score-from-api.ts
|
|
346
|
+
const parseScoreResult = (value) => {
|
|
347
|
+
if (typeof value !== "object" || value === null) return null;
|
|
348
|
+
if (!("score" in value) || !("label" in value)) return null;
|
|
349
|
+
const scoreValue = Reflect.get(value, "score");
|
|
350
|
+
const labelValue = Reflect.get(value, "label");
|
|
351
|
+
if (typeof scoreValue !== "number" || typeof labelValue !== "string") return null;
|
|
352
|
+
return {
|
|
353
|
+
score: scoreValue,
|
|
354
|
+
label: labelValue
|
|
355
|
+
};
|
|
356
|
+
};
|
|
357
|
+
const tryScoreFromApi = async (diagnostics, fetchImplementation) => {
|
|
358
|
+
const controller = new AbortController();
|
|
359
|
+
const timeoutId = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
|
|
392
360
|
try {
|
|
393
|
-
const response = await
|
|
361
|
+
const response = await fetchImplementation(SCORE_API_URL, {
|
|
394
362
|
method: "POST",
|
|
395
363
|
headers: { "Content-Type": "application/json" },
|
|
396
|
-
body: JSON.stringify({ diagnostics })
|
|
364
|
+
body: JSON.stringify({ diagnostics }),
|
|
365
|
+
signal: controller.signal
|
|
397
366
|
});
|
|
398
|
-
if (!response.ok) return
|
|
399
|
-
return await response.json();
|
|
367
|
+
if (!response.ok) return null;
|
|
368
|
+
return parseScoreResult(await response.json());
|
|
400
369
|
} catch {
|
|
401
|
-
return
|
|
370
|
+
return null;
|
|
371
|
+
} finally {
|
|
372
|
+
clearTimeout(timeoutId);
|
|
402
373
|
}
|
|
403
374
|
};
|
|
404
375
|
|
|
376
|
+
//#endregion
|
|
377
|
+
//#region src/utils/proxy-fetch.ts
|
|
378
|
+
const getGlobalProcess = () => {
|
|
379
|
+
const candidate = globalThis.process;
|
|
380
|
+
return candidate?.versions?.node ? candidate : void 0;
|
|
381
|
+
};
|
|
382
|
+
const readEnvProxy = () => {
|
|
383
|
+
const proc = getGlobalProcess();
|
|
384
|
+
if (!proc?.env) return void 0;
|
|
385
|
+
return proc.env.HTTPS_PROXY ?? proc.env.https_proxy ?? proc.env.HTTP_PROXY ?? proc.env.http_proxy;
|
|
386
|
+
};
|
|
387
|
+
let isProxyUrlResolved = false;
|
|
388
|
+
let resolvedProxyUrl;
|
|
389
|
+
const getProxyUrl = () => {
|
|
390
|
+
if (isProxyUrlResolved) return resolvedProxyUrl;
|
|
391
|
+
isProxyUrlResolved = true;
|
|
392
|
+
resolvedProxyUrl = readEnvProxy();
|
|
393
|
+
return resolvedProxyUrl;
|
|
394
|
+
};
|
|
395
|
+
const createProxyDispatcher = async (proxyUrl) => {
|
|
396
|
+
try {
|
|
397
|
+
const { ProxyAgent } = await import("undici");
|
|
398
|
+
return new ProxyAgent(proxyUrl);
|
|
399
|
+
} catch {
|
|
400
|
+
return null;
|
|
401
|
+
}
|
|
402
|
+
};
|
|
403
|
+
const proxyFetch = async (url, init) => {
|
|
404
|
+
const controller = new AbortController();
|
|
405
|
+
const timeoutId = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
|
|
406
|
+
try {
|
|
407
|
+
const proxyUrl = getProxyUrl();
|
|
408
|
+
const dispatcher = proxyUrl ? await createProxyDispatcher(proxyUrl) : null;
|
|
409
|
+
return await fetch(url, {
|
|
410
|
+
...init,
|
|
411
|
+
signal: controller.signal,
|
|
412
|
+
...dispatcher ? { dispatcher } : {}
|
|
413
|
+
});
|
|
414
|
+
} finally {
|
|
415
|
+
clearTimeout(timeoutId);
|
|
416
|
+
}
|
|
417
|
+
};
|
|
418
|
+
|
|
419
|
+
//#endregion
|
|
420
|
+
//#region src/utils/calculate-score-node.ts
|
|
421
|
+
const calculateScore = async (diagnostics) => {
|
|
422
|
+
const apiScore = await tryScoreFromApi(diagnostics, proxyFetch);
|
|
423
|
+
if (apiScore) return apiScore;
|
|
424
|
+
return calculateScoreLocally(diagnostics);
|
|
425
|
+
};
|
|
426
|
+
|
|
405
427
|
//#endregion
|
|
406
428
|
//#region src/utils/colorize-by-score.ts
|
|
407
429
|
const colorizeByScore = (text, score) => {
|
|
@@ -481,6 +503,19 @@ const checkReducedMotion = (rootDirectory) => {
|
|
|
481
503
|
}
|
|
482
504
|
};
|
|
483
505
|
|
|
506
|
+
//#endregion
|
|
507
|
+
//#region src/utils/read-file-lines-node.ts
|
|
508
|
+
const createNodeReadFileLinesSync = (rootDirectory) => {
|
|
509
|
+
return (filePath) => {
|
|
510
|
+
const absolutePath = path.isAbsolute(filePath) ? filePath : path.join(rootDirectory, filePath);
|
|
511
|
+
try {
|
|
512
|
+
return fs.readFileSync(absolutePath, "utf-8").split("\n");
|
|
513
|
+
} catch {
|
|
514
|
+
return null;
|
|
515
|
+
}
|
|
516
|
+
};
|
|
517
|
+
};
|
|
518
|
+
|
|
484
519
|
//#endregion
|
|
485
520
|
//#region src/utils/match-glob-pattern.ts
|
|
486
521
|
const REGEX_SPECIAL_CHARACTERS = /[.+^${}()|[\]\\]/g;
|
|
@@ -526,23 +561,22 @@ const isFileIgnoredByPatterns = (filePath, rootDirectory, patterns) => {
|
|
|
526
561
|
|
|
527
562
|
//#endregion
|
|
528
563
|
//#region src/utils/filter-diagnostics.ts
|
|
564
|
+
const resolveCandidateReadPath = (rootDirectory, filePath) => {
|
|
565
|
+
const normalizedFile = filePath.replace(/\\/g, "/");
|
|
566
|
+
if (normalizedFile.startsWith("/") || /^[a-zA-Z]:\//.test(normalizedFile) || /^[a-zA-Z]:\\/.test(filePath)) return filePath;
|
|
567
|
+
return `${rootDirectory.replace(/\\/g, "/").replace(/\/$/, "")}/${normalizedFile.replace(/^\.\//, "")}`;
|
|
568
|
+
};
|
|
529
569
|
const OPENING_TAG_PATTERN = /<([A-Z][\w.]*)/;
|
|
530
570
|
const DISABLE_NEXT_LINE_PATTERN = /\/\/\s*react-doctor-disable-next-line\b(?:\s+(.+))?/;
|
|
531
571
|
const DISABLE_LINE_PATTERN = /\/\/\s*react-doctor-disable-line\b(?:\s+(.+))?/;
|
|
532
|
-
const createFileLinesCache = (rootDirectory) => {
|
|
572
|
+
const createFileLinesCache = (rootDirectory, readFileLinesSync) => {
|
|
533
573
|
const cache = /* @__PURE__ */ new Map();
|
|
534
574
|
return (filePath) => {
|
|
535
575
|
const cached = cache.get(filePath);
|
|
536
576
|
if (cached !== void 0) return cached;
|
|
537
|
-
const
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
cache.set(filePath, lines);
|
|
541
|
-
return lines;
|
|
542
|
-
} catch {
|
|
543
|
-
cache.set(filePath, null);
|
|
544
|
-
return null;
|
|
545
|
-
}
|
|
577
|
+
const lines = readFileLinesSync(resolveCandidateReadPath(rootDirectory, filePath));
|
|
578
|
+
cache.set(filePath, lines);
|
|
579
|
+
return lines;
|
|
546
580
|
};
|
|
547
581
|
};
|
|
548
582
|
const isInsideTextComponent = (lines, diagnosticLine, textComponentNames) => {
|
|
@@ -559,12 +593,12 @@ const isRuleSuppressed = (commentRules, ruleId) => {
|
|
|
559
593
|
if (!commentRules?.trim()) return true;
|
|
560
594
|
return commentRules.split(/[,\s]+/).some((rule) => rule.trim() === ruleId);
|
|
561
595
|
};
|
|
562
|
-
const filterIgnoredDiagnostics = (diagnostics, config, rootDirectory) => {
|
|
596
|
+
const filterIgnoredDiagnostics = (diagnostics, config, rootDirectory, readFileLinesSync) => {
|
|
563
597
|
const ignoredRules = new Set(Array.isArray(config.ignore?.rules) ? config.ignore.rules : []);
|
|
564
598
|
const ignoredFilePatterns = compileIgnoredFilePatterns(config);
|
|
565
599
|
const textComponentNames = new Set(Array.isArray(config.textComponents) ? config.textComponents : []);
|
|
566
600
|
const hasTextComponents = textComponentNames.size > 0;
|
|
567
|
-
const getFileLines = createFileLinesCache(rootDirectory);
|
|
601
|
+
const getFileLines = createFileLinesCache(rootDirectory, readFileLinesSync);
|
|
568
602
|
return diagnostics.filter((diagnostic) => {
|
|
569
603
|
const ruleIdentifier = `${diagnostic.plugin}/${diagnostic.rule}`;
|
|
570
604
|
if (ignoredRules.has(ruleIdentifier)) return false;
|
|
@@ -576,8 +610,8 @@ const filterIgnoredDiagnostics = (diagnostics, config, rootDirectory) => {
|
|
|
576
610
|
return true;
|
|
577
611
|
});
|
|
578
612
|
};
|
|
579
|
-
const filterInlineSuppressions = (diagnostics, rootDirectory) => {
|
|
580
|
-
const getFileLines = createFileLinesCache(rootDirectory);
|
|
613
|
+
const filterInlineSuppressions = (diagnostics, rootDirectory, readFileLinesSync) => {
|
|
614
|
+
const getFileLines = createFileLinesCache(rootDirectory, readFileLinesSync);
|
|
581
615
|
return diagnostics.filter((diagnostic) => {
|
|
582
616
|
if (diagnostic.line <= 0) return true;
|
|
583
617
|
const lines = getFileLines(diagnostic.filePath);
|
|
@@ -600,15 +634,24 @@ const filterInlineSuppressions = (diagnostics, rootDirectory) => {
|
|
|
600
634
|
};
|
|
601
635
|
|
|
602
636
|
//#endregion
|
|
603
|
-
//#region src/utils/
|
|
637
|
+
//#region src/utils/merge-and-filter-diagnostics.ts
|
|
638
|
+
const mergeAndFilterDiagnostics = (mergedDiagnostics, directory, userConfig, readFileLinesSync) => {
|
|
639
|
+
return filterInlineSuppressions(userConfig ? filterIgnoredDiagnostics(mergedDiagnostics, userConfig, directory, readFileLinesSync) : mergedDiagnostics, directory, readFileLinesSync);
|
|
640
|
+
};
|
|
641
|
+
|
|
642
|
+
//#endregion
|
|
643
|
+
//#region src/utils/jsx-include-paths.ts
|
|
604
644
|
const computeJsxIncludePaths = (includePaths) => includePaths.length > 0 ? includePaths.filter((filePath) => JSX_FILE_PATTERN.test(filePath)) : void 0;
|
|
605
|
-
|
|
606
|
-
|
|
645
|
+
|
|
646
|
+
//#endregion
|
|
647
|
+
//#region src/utils/combine-diagnostics.ts
|
|
648
|
+
const combineDiagnostics = (lintDiagnostics, deadCodeDiagnostics, directory, isDiffMode, userConfig, readFileLinesSync = createNodeReadFileLinesSync(directory), includeEnvironmentChecks = true) => {
|
|
649
|
+
const extraDiagnostics = isDiffMode || !includeEnvironmentChecks ? [] : checkReducedMotion(directory);
|
|
650
|
+
return mergeAndFilterDiagnostics([
|
|
607
651
|
...lintDiagnostics,
|
|
608
652
|
...deadCodeDiagnostics,
|
|
609
|
-
...
|
|
610
|
-
];
|
|
611
|
-
return filterInlineSuppressions(userConfig ? filterIgnoredDiagnostics(merged, userConfig, directory) : merged, directory);
|
|
653
|
+
...extraDiagnostics
|
|
654
|
+
], directory, userConfig, readFileLinesSync);
|
|
612
655
|
};
|
|
613
656
|
|
|
614
657
|
//#endregion
|
|
@@ -1253,6 +1296,20 @@ const resolveLintIncludePaths = (rootDirectory, userConfig) => {
|
|
|
1253
1296
|
});
|
|
1254
1297
|
};
|
|
1255
1298
|
|
|
1299
|
+
//#endregion
|
|
1300
|
+
//#region src/utils/collect-unused-file-paths.ts
|
|
1301
|
+
const collectUnusedFilePaths = (filesIssues) => {
|
|
1302
|
+
if (filesIssues instanceof Set) return [...filesIssues];
|
|
1303
|
+
if (Array.isArray(filesIssues)) return filesIssues.filter((entry) => typeof entry === "string");
|
|
1304
|
+
if (!isPlainObject(filesIssues)) return [];
|
|
1305
|
+
const unusedFilePaths = [];
|
|
1306
|
+
for (const innerValue of Object.values(filesIssues)) {
|
|
1307
|
+
if (!isPlainObject(innerValue)) continue;
|
|
1308
|
+
for (const issue of Object.values(innerValue)) if (isPlainObject(issue) && typeof issue.filePath === "string") unusedFilePaths.push(issue.filePath);
|
|
1309
|
+
}
|
|
1310
|
+
return unusedFilePaths;
|
|
1311
|
+
};
|
|
1312
|
+
|
|
1256
1313
|
//#endregion
|
|
1257
1314
|
//#region src/utils/run-knip.ts
|
|
1258
1315
|
const KNIP_CATEGORY_MAP = {
|
|
@@ -1350,8 +1407,8 @@ const runKnip = async (rootDirectory) => {
|
|
|
1350
1407
|
} else knipResult = await runKnipWithOptions(rootDirectory);
|
|
1351
1408
|
const { issues } = knipResult;
|
|
1352
1409
|
const diagnostics = [];
|
|
1353
|
-
for (const
|
|
1354
|
-
filePath: path.relative(rootDirectory,
|
|
1410
|
+
for (const unusedFilePath of collectUnusedFilePaths(issues.files)) diagnostics.push({
|
|
1411
|
+
filePath: path.relative(rootDirectory, unusedFilePath),
|
|
1355
1412
|
plugin: "knip",
|
|
1356
1413
|
rule: "files",
|
|
1357
1414
|
severity: KNIP_SEVERITY_MAP["files"],
|
|
@@ -1984,10 +2041,8 @@ const printDiagnostics = (diagnostics, isVerbose) => {
|
|
|
1984
2041
|
if (firstDiagnostic.help) logger.dim(indentMultilineText(firstDiagnostic.help, " "));
|
|
1985
2042
|
if (isVerbose) {
|
|
1986
2043
|
const fileLines = buildFileLineMap(ruleDiagnostics);
|
|
1987
|
-
for (const [filePath, lines] of fileLines) {
|
|
1988
|
-
|
|
1989
|
-
logger.dim(` ${filePath}${lineLabel}`);
|
|
1990
|
-
}
|
|
2044
|
+
for (const [filePath, lines] of fileLines) if (lines.length > 0) for (const line of lines) logger.dim(` ${filePath}:${line}`);
|
|
2045
|
+
else logger.dim(` ${filePath}`);
|
|
1991
2046
|
}
|
|
1992
2047
|
logger.break();
|
|
1993
2048
|
}
|
|
@@ -2009,10 +2064,8 @@ const formatRuleSummary = (ruleKey, ruleDiagnostics) => {
|
|
|
2009
2064
|
];
|
|
2010
2065
|
if (firstDiagnostic.help) sections.push("", `Suggestion: ${firstDiagnostic.help}`);
|
|
2011
2066
|
sections.push("", "Files:");
|
|
2012
|
-
for (const [filePath, lines] of fileLines) {
|
|
2013
|
-
|
|
2014
|
-
sections.push(` ${filePath}${lineLabel}`);
|
|
2015
|
-
}
|
|
2067
|
+
for (const [filePath, lines] of fileLines) if (lines.length > 0) for (const line of lines) sections.push(` ${filePath}:${line}`);
|
|
2068
|
+
else sections.push(` ${filePath}`);
|
|
2016
2069
|
return sections.join("\n") + "\n";
|
|
2017
2070
|
};
|
|
2018
2071
|
const writeDiagnosticsDirectory = (diagnostics) => {
|
|
@@ -2512,7 +2565,7 @@ const promptProjectSelection = async (workspacePackages, rootDirectory) => {
|
|
|
2512
2565
|
|
|
2513
2566
|
//#endregion
|
|
2514
2567
|
//#region src/cli.ts
|
|
2515
|
-
const VERSION = "0.0.
|
|
2568
|
+
const VERSION = "0.0.39";
|
|
2516
2569
|
const VALID_FAIL_ON_LEVELS = new Set([
|
|
2517
2570
|
"error",
|
|
2518
2571
|
"warning",
|