react-doctor 0.9.11 → 0.9.12-dev.05ef989
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 +1 -1
- package/bin/react-doctor.js +1 -9
- package/dist/{build-section-divider-sLcWe1hO.js → build-section-divider-BNQmYjME.js} +9 -10
- package/dist/{ci-DsrrD52i.js → ci-B9ir-2Up.js} +12 -12
- package/dist/cli.js +30 -31
- package/dist/{design-CP25gyyg.js → design-DrPr5wfS.js} +4 -4
- package/dist/{devtools-DPRVRu3S.js → devtools-D8NTVj81.js} +4 -4
- package/dist/{exit-gracefully-CVVkBxDS.js → exit-gracefully-C-_jVMT3.js} +6 -6
- package/dist/index.d.ts +38 -61
- package/dist/index.js +54948 -54701
- package/dist/{inspect-D6y8oLtg.js → inspect-BfdplEq6.js} +25 -24
- package/dist/{install-afMFGeTB.js → install-CcPaQeBb.js} +8 -8
- package/dist/{install-doctor-script-Be7Mv3-l.js → install-doctor-script-BlIhuJW6.js} +4 -4
- package/dist/{install-github-workflow-Dev2F6Ea.js → install-github-workflow-DbnsoR9k.js} +6 -6
- package/dist/{install-react-doctor-DIPVunY4.js → install-react-doctor-COZnJROS.js} +47 -45
- package/dist/{print-branded-header-D5jLdo4p.js → print-branded-header-B_Cgff5S.js} +4 -4
- package/dist/project-analysis-worker.d.ts +1 -0
- package/dist/project-analysis-worker.js +51900 -0
- package/dist/{read-object-file-CEh1CqnD.js → read-object-file-NNLnIQKt.js} +4 -4
- package/dist/{record-metric-CJxSHfJD.js → record-metric-fdCvvcjz.js} +2668 -5698
- package/dist/{report-error-CAEixMg1.js → report-error-BH7DfHGY.js} +5 -5
- package/dist/{report-workflow-result-C0jbKH8p.js → report-workflow-result-B3YeoXsK.js} +5 -5
- package/dist/{resolve-scope-DS7rcRRz.js → resolve-scope-gLUuBxjI.js} +6 -6
- package/dist/{rules-BJynymuU.js → rules-B5VC89MR.js} +6 -6
- package/dist/{run-scan-app-BGzlyiIB.js → run-scan-app-Cl2mqLLB.js} +194 -145
- package/dist/{scan-CgPRG6_z.js → scan-7OBudBct.js} +8 -8
- package/dist/semver-DFtnEmN-.js +3219 -0
- package/dist/{should-fail-scan-gate-4jf7ZRdL.js → should-fail-scan-gate-Dch9vbPz.js} +197 -94
- package/dist/{should-skip-prompts-C78r7Ww7.js → should-skip-prompts-CyNdARgB.js} +4 -4
- package/dist/{unref-stdin-Dc8ipkaw.js → unref-stdin-Cynhx9N1.js} +6 -12
- package/dist/{version-DDrwsTas.js → version-axnZCkCZ.js} +4 -4
- package/dist/{why-BRNrKY1b.js → why-Ducgj7Hu.js} +4 -4
- package/package.json +6 -12
- package/dist/chunk-q7NCDQ7-.js +0 -28
- package/dist/lsp.js +0 -81114
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
|
|
2
|
-
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="
|
|
3
|
-
import { $
|
|
4
|
-
import { a as setActiveRunTrace, i as recordRunTraceId, o as getTelemetryContext } from "./unref-stdin-
|
|
5
|
-
import { n as buildFooterLinkLines, r as activeScanAbortRegistry, t as buildSectionDivider } from "./build-section-divider-
|
|
6
|
-
import { D as getPackageJsonPath, L as cliLogger, O as isRecord, P as runGit, a as installReactDoctorScriptStep, c as spinner, n as findNearestPackageDirectory, o as isSpinnerSilent, s as setSpinnerSilent } from "./install-doctor-script-
|
|
7
|
-
import { d as detectDefaultBranch, l as prompts, r as installReactDoctorWorkflow, u as isCommandAvailable } from "./install-github-workflow-
|
|
8
|
-
import { a as isValidBlockingLevel, c as openWorkflowPullRequest, l as stageWorkflowFile, o as pickBlockingLevel, u as toForwardSlashes } from "./resolve-scope-
|
|
9
|
-
import { h as detectAvailableAgents, t as reportWorkflowResult } from "./report-workflow-result-
|
|
2
|
+
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="19bff337-5a1f-5558-b6b7-bd4274f53f38")}catch(e){}}();
|
|
3
|
+
import { $ as Maintainability, $t as listSourceFiles, An as makeSpan, At as filterDiagnosticsForSurface, Bn as try_, Ct as computeConfigFingerprint, Dn as decodeUnknownSync, Dt as createOxlintSpawnSlots, E as NANOSECONDS_PER_MILLISECOND, En as Diagnostic, Et as createNodeReadFileLinesSync, Fn as provideContext, G as isCiOrCodingAgentEnvironment, Gn as succeed, Gt as highlighter, H as ACTION_INPUT_ENVIRONMENT_VARIABLES, Ht as hasPublishedFixRecipe, In as provideService, J as Files, Jn as fail, Kt as isMonorepoRoot, Ln as runPromise, Mn as orElseSucceed, Mt as filterSourceFiles, Nn as promise, Pn as provide, Q as Linter, R as CliInputError, Rn as runSync, Rt as getDiagnosticRuleIdentity, Sn as error, St as collectSourceFileCountsByDirectory, Tn as warn, Tt as countOptInProjectRuleSelections, U as detectRunnerOs, Un as mergeAll, Vt as groupBy, W as isCiEnvironment, Wt as hashFileContents, Xn as ParentSpan, Y as Git, Yn as void_, Yt as isReactDoctorError, Z as LintPartialFailures, Zt as isScanComplete, _ as makeNoopConsole, _n as summarizeDiagnostics, _t as buildSkippedChecks, an as mergeReactDoctorConfigs, at as Project, bn as yieldToEventLoop, br as isFile, bt as classifyFileContext, c as setSentryProjectInfo, cn as resolveLintBatchOrdering, cr as GIT_SHOW_MAX_BUFFER_BYTES, ct as SidecarLintCacheEnabled, d as isJsonModeActive, dn as resolveScanTarget, dr as JSX_FILE_PATTERN, en as listSourceFilesCooperative, er as CI_URL, et as NodeResolver, fn as restoreLegacyThrow, fr as MILLISECONDS_PER_SECOND, g as VERSION, gn as shouldUseMaintainabilityLayer, gt as buildRuleSeverityControls, ht as buildRuleDocsUrl, in as materializeSourceTree, ir as DIAGNOSTIC_CATEGORY_BUCKETS, it as Progress, jn as map, jt as filterPathsOutsideDirectories, k as SCAN_RESULT_CACHE_FILENAME, kn as gen, kt as discoverSupportedSubprojects, l as getRunId, ln as resolveReactDoctorCacheDir, lr as HTML_FILE_PATTERN, mr as SHARE_BASE_URL, n as recordDistribution, nt as OxlintSpawnSlots, o as buildSentryScope, on as remainingDeadlineBudgetMs, or as ENTERPRISE_CONTACT_URL, ot as Reporter, pn as runInspect, pr as OXLINT_NODE_REQUIREMENT, pt as atomicWriteJson, q as Config, qt as isPathInsideDirectory, r as toSpanAttributes, rn as mapWithConcurrency, rt as PerFileLintCacheEnabled, s as buildSentryProjectContext, sn as resolveGithubActionsScoreMetadata, st as Score, t as recordCount, tn as listWorkspacePackages, tt as OxlintConcurrency, un as resolveScanConcurrency, ur as JSX_DUPLICATION_SOURCE_FILE_PATTERN, ut as SupplyChain, vr as isDirectory, vt as calculateScore, w as METRIC, wn as log, wt as computeDiagnosticDelta, x as BASELINE_FILES_TEMP_DIR_PREFIX, xn as Console, yt as canonicalizeUserRuleKey, z as isNonInteractiveEnvironment, zn as sync } from "./record-metric-fdCvvcjz.js";
|
|
4
|
+
import { a as setActiveRunTrace, i as recordRunTraceId, o as getTelemetryContext } from "./unref-stdin-Cynhx9N1.js";
|
|
5
|
+
import { n as buildFooterLinkLines, r as activeScanAbortRegistry, t as buildSectionDivider } from "./build-section-divider-BNQmYjME.js";
|
|
6
|
+
import { D as getPackageJsonPath, L as cliLogger, O as isRecord, P as runGit, a as installReactDoctorScriptStep, c as spinner, n as findNearestPackageDirectory, o as isSpinnerSilent, s as setSpinnerSilent } from "./install-doctor-script-BlIhuJW6.js";
|
|
7
|
+
import { d as detectDefaultBranch, l as prompts, r as installReactDoctorWorkflow, u as isCommandAvailable } from "./install-github-workflow-DbnsoR9k.js";
|
|
8
|
+
import { a as isValidBlockingLevel, c as openWorkflowPullRequest, l as stageWorkflowFile, o as pickBlockingLevel, u as toForwardSlashes } from "./resolve-scope-gLUuBxjI.js";
|
|
9
|
+
import { h as detectAvailableAgents, t as reportWorkflowResult } from "./report-workflow-result-B3YeoXsK.js";
|
|
10
10
|
import { createRequire } from "node:module";
|
|
11
11
|
import { spawn } from "node:child_process";
|
|
12
12
|
import * as path$1 from "node:path";
|
|
13
13
|
import path from "node:path";
|
|
14
|
-
import * as fs$
|
|
14
|
+
import * as fs$2 from "node:fs";
|
|
15
15
|
import fs, { mkdtempSync, rmSync } from "node:fs";
|
|
16
|
-
import crypto, { randomUUID } from "node:crypto";
|
|
17
16
|
import ts from "typescript";
|
|
17
|
+
import crypto, { randomUUID } from "node:crypto";
|
|
18
18
|
import { tmpdir } from "node:os";
|
|
19
|
+
import * as fs$1 from "node:fs/promises";
|
|
19
20
|
import { performance } from "node:perf_hooks";
|
|
20
|
-
import { codeFrameColumns } from "@babel/code-frame";
|
|
21
21
|
//#region src/cli/utils/apply-observability.ts
|
|
22
22
|
/**
|
|
23
23
|
* Installs the tracing backend for the inspect program.
|
|
@@ -84,7 +84,10 @@ const buildSpinnerProgressHandle = (text) => {
|
|
|
84
84
|
*/
|
|
85
85
|
const buildRuntimeLayers = (input) => {
|
|
86
86
|
const linterLayer = input.shouldSkipLint ? Linter.layerOf([]) : Linter.layerOxlint;
|
|
87
|
-
const
|
|
87
|
+
const maintainabilityLayer = shouldUseMaintainabilityLayer({
|
|
88
|
+
shouldRunDuplicateJsx: input.shouldRunDeadCode,
|
|
89
|
+
userConfig: input.userConfig
|
|
90
|
+
}) ? Maintainability.layerNode : Maintainability.layerOf([]);
|
|
88
91
|
const scoreLayer = input.shouldComputeScore ? Score.layerHttp : Score.layerOf(null);
|
|
89
92
|
const supplyChainLayer = input.shouldRunSupplyChain ? SupplyChain.layerNode : SupplyChain.layerOf([]);
|
|
90
93
|
const projectLayer = input.projectInfoOverride === void 0 ? Project.layerNode : Project.layerOf(input.projectInfoOverride);
|
|
@@ -94,7 +97,7 @@ const buildRuntimeLayers = (input) => {
|
|
|
94
97
|
config: input.userConfig,
|
|
95
98
|
resolvedDirectory: input.directory,
|
|
96
99
|
configSourceDirectory: input.configSourceDirectory
|
|
97
|
-
}) : Config.layerNode, Files.layerNode, Git.layerNode, linterLayer, LintPartialFailures.layerLive,
|
|
100
|
+
}) : Config.layerNode, Files.layerNode, Git.layerNode, linterLayer, LintPartialFailures.layerLive, maintainabilityLayer, progressLayer, reporterLayer, scoreLayer, supplyChainLayer);
|
|
98
101
|
const layersWithConcurrency = input.oxlintConcurrency === void 0 ? baseLayers : mergeAll(baseLayers, succeed(OxlintConcurrency, input.oxlintConcurrency));
|
|
99
102
|
return input.oxlintSpawnSlots === void 0 ? layersWithConcurrency : mergeAll(layersWithConcurrency, succeed(OxlintSpawnSlots, input.oxlintSpawnSlots));
|
|
100
103
|
};
|
|
@@ -310,7 +313,7 @@ const recordScanMetrics = (input) => {
|
|
|
310
313
|
recordCount(METRIC.scanCompleted, 1, {
|
|
311
314
|
mode: input.mode,
|
|
312
315
|
lint: input.lint,
|
|
313
|
-
|
|
316
|
+
maintainability: input.deadCode,
|
|
314
317
|
parallel: input.parallel,
|
|
315
318
|
scoreOnly: input.scoreOnly,
|
|
316
319
|
didLintFail: input.didLintFail,
|
|
@@ -350,7 +353,7 @@ const recordScanMetrics = (input) => {
|
|
|
350
353
|
else if (!input.noScore && !input.didLintFail && !input.didDeadCodeFail) recordCount(METRIC.scoreUnavailable, 1, { mode: input.mode });
|
|
351
354
|
if (input.baselineDegraded) recordCount(METRIC.baselineDegraded, 1);
|
|
352
355
|
if (input.didLintFail) recordCount(METRIC.lintFailed, 1, { reasonKind: input.lintFailureReasonKind });
|
|
353
|
-
if (input.didDeadCodeFail) recordCount(METRIC.
|
|
356
|
+
if (input.didDeadCodeFail) recordCount(METRIC.maintainabilityFailed, 1);
|
|
354
357
|
for (const check of result.skippedChecks) recordCount(METRIC.scanCheckSkipped, 1, {
|
|
355
358
|
check,
|
|
356
359
|
reason: result.skippedCheckReasons?.[check] ?? null
|
|
@@ -494,10 +497,16 @@ const CACHE_DISABLED_VALUES = new Set(["1", "true"]);
|
|
|
494
497
|
const TOOLCHAIN_PACKAGE_SPECIFIERS = [
|
|
495
498
|
"oxlint/package.json",
|
|
496
499
|
"oxlint-plugin-react-doctor/package.json",
|
|
497
|
-
"deslop-js/package.json",
|
|
498
500
|
"eslint-plugin-react-hooks/package.json"
|
|
499
501
|
];
|
|
500
502
|
const bundledRequire = createRequire(import.meta.url);
|
|
503
|
+
const RULE_PLUGIN_CONTENT_FINGERPRINT = (() => {
|
|
504
|
+
try {
|
|
505
|
+
return `oxlint-plugin-react-doctor#fingerprint=${hashFileContents(bundledRequire.resolve("oxlint-plugin-react-doctor")) ?? "unreadable"}`;
|
|
506
|
+
} catch {
|
|
507
|
+
return "oxlint-plugin-react-doctor#fingerprint=unresolved";
|
|
508
|
+
}
|
|
509
|
+
})();
|
|
501
510
|
const normalizeForStableJson = (value) => {
|
|
502
511
|
if (value === null) return null;
|
|
503
512
|
if (value === void 0) return void 0;
|
|
@@ -633,12 +642,12 @@ const resolveCacheFilePath = (projectDirectory) => path.join(resolveReactDoctorC
|
|
|
633
642
|
const readPersistedCache = (cacheFilePath) => {
|
|
634
643
|
try {
|
|
635
644
|
const parsed = JSON.parse(fs.readFileSync(cacheFilePath, "utf8"));
|
|
636
|
-
if (!isRecord(parsed) || parsed.version !==
|
|
637
|
-
version:
|
|
645
|
+
if (!isRecord(parsed) || parsed.version !== 7) return {
|
|
646
|
+
version: 7,
|
|
638
647
|
entries: []
|
|
639
648
|
};
|
|
640
649
|
if (!Array.isArray(parsed.entries)) return {
|
|
641
|
-
version:
|
|
650
|
+
version: 7,
|
|
642
651
|
entries: []
|
|
643
652
|
};
|
|
644
653
|
const entries = [];
|
|
@@ -653,12 +662,12 @@ const readPersistedCache = (cacheFilePath) => {
|
|
|
653
662
|
});
|
|
654
663
|
}
|
|
655
664
|
return {
|
|
656
|
-
version:
|
|
665
|
+
version: 7,
|
|
657
666
|
entries
|
|
658
667
|
};
|
|
659
668
|
} catch {
|
|
660
669
|
return {
|
|
661
|
-
version:
|
|
670
|
+
version: 7,
|
|
662
671
|
entries: []
|
|
663
672
|
};
|
|
664
673
|
}
|
|
@@ -666,7 +675,7 @@ const readPersistedCache = (cacheFilePath) => {
|
|
|
666
675
|
/**
|
|
667
676
|
* The global cache off-switch (`REACT_DOCTOR_NO_CACHE`), which disables every
|
|
668
677
|
* cache subsystem: this whole-repo scan cache plus core's per-file lint,
|
|
669
|
-
* sidecar
|
|
678
|
+
* sidecar caches (their `Context.Reference` defaults read the
|
|
670
679
|
* same variable). Exported for the wide event's `cache.temperature`
|
|
671
680
|
* derivation, which reports `"disabled"` instead of `"cold"` when the switch
|
|
672
681
|
* is on. Granular knobs (`REACT_DOCTOR_NO_FILE_CACHE`, …) are deliberately
|
|
@@ -689,7 +698,7 @@ const fileFingerprint = (filePath) => {
|
|
|
689
698
|
return null;
|
|
690
699
|
}
|
|
691
700
|
};
|
|
692
|
-
const
|
|
701
|
+
const resolveScanResultToolchainFingerprint = (nodeBinaryPath) => {
|
|
693
702
|
const fingerprints = [];
|
|
694
703
|
if (nodeBinaryPath !== null) fingerprints.push(nodeBinaryPath === process.execPath ? `node=${process.version}` : `node=${fileFingerprint(nodeBinaryPath) ?? "unreadable"}`);
|
|
695
704
|
for (const specifier of TOOLCHAIN_PACKAGE_SPECIFIERS) try {
|
|
@@ -699,6 +708,7 @@ const resolveToolchainFingerprint = (nodeBinaryPath) => {
|
|
|
699
708
|
} catch {
|
|
700
709
|
fingerprints.push(`${specifier}=missing`);
|
|
701
710
|
}
|
|
711
|
+
fingerprints.push(RULE_PLUGIN_CONTENT_FINGERPRINT);
|
|
702
712
|
return fingerprints;
|
|
703
713
|
};
|
|
704
714
|
const buildScanResultCacheKey = (input) => {
|
|
@@ -708,14 +718,14 @@ const buildScanResultCacheKey = (input) => {
|
|
|
708
718
|
if (repositoryIdentity === null) return null;
|
|
709
719
|
if (stringifyStableJson(input.userConfig) === null) return null;
|
|
710
720
|
const cacheKeyJson = stringifyStableJson({
|
|
711
|
-
schemaVersion:
|
|
721
|
+
schemaVersion: 7,
|
|
712
722
|
projectIdentity: resolveProjectIdentity(input.projectDirectory),
|
|
713
723
|
headSha: repositoryIdentity.headSha,
|
|
714
724
|
worktreeFingerprint: repositoryIdentity.worktreeFingerprint,
|
|
715
725
|
dotenvFingerprint: resolveDotenvFingerprint(input.projectDirectory),
|
|
716
726
|
reactDoctorVersion: input.version,
|
|
717
727
|
nodeVersion: process.version,
|
|
718
|
-
toolchainFingerprint:
|
|
728
|
+
toolchainFingerprint: resolveScanResultToolchainFingerprint(input.nodeBinaryPath),
|
|
719
729
|
configFingerprint: computeConfigFingerprint(input.projectDirectory, input.version),
|
|
720
730
|
hasConfigOverride: input.hasConfigOverride,
|
|
721
731
|
configSourceDirectory: input.configSourceDirectory,
|
|
@@ -756,7 +766,7 @@ const createScanResultCache = (projectDirectory) => {
|
|
|
756
766
|
for (const entry of readPersistedCache(cacheFilePath).entries) mergedEntries.set(entry.key, entry);
|
|
757
767
|
for (const entry of entries.values()) mergedEntries.set(entry.key, entry);
|
|
758
768
|
atomicWriteJson(cacheFilePath, {
|
|
759
|
-
version:
|
|
769
|
+
version: 7,
|
|
760
770
|
entries: [...mergedEntries.values()].sort((firstEntry, secondEntry) => secondEntry.createdAtMs - firstEntry.createdAtMs).slice(0, 20)
|
|
761
771
|
});
|
|
762
772
|
};
|
|
@@ -791,8 +801,7 @@ const shouldBlockCi = (diagnostics, blockingLevel) => {
|
|
|
791
801
|
/**
|
|
792
802
|
* Lowercase, key-safe form of a rule category for the `diag.category.*`
|
|
793
803
|
* telemetry attribute namespace (categories carry spaces / capitals, e.g.
|
|
794
|
-
* "Performance" → `performance`, "Dead Code" → `dead_code`).
|
|
795
|
-
* CLI run event and the editor LSP wide event so both namespace identically.
|
|
804
|
+
* "Performance" → `performance`, "Dead Code" → `dead_code`).
|
|
796
805
|
*/
|
|
797
806
|
const toCategoryKey = (category) => category.toLowerCase().replace(/[^a-z0-9]+/g, "_");
|
|
798
807
|
//#endregion
|
|
@@ -822,12 +831,6 @@ const readEnvBoolean = (name) => {
|
|
|
822
831
|
return value.toLowerCase() === "true" || value === "1";
|
|
823
832
|
};
|
|
824
833
|
const ratioOf = (numerator, denominator) => denominator != null && denominator > 0 ? (numerator ?? 0) / denominator : null;
|
|
825
|
-
const resolveDeadCodeReuseRatio = (result) => {
|
|
826
|
-
if (result.deadCodeCacheHit === true) return 1;
|
|
827
|
-
const summaryTotal = (result.deadCodeSummaryCacheHits ?? 0) + (result.deadCodeSummaryCacheMisses ?? 0);
|
|
828
|
-
if (summaryTotal > 0) return (result.deadCodeSummaryCacheHits ?? 0) / summaryTotal;
|
|
829
|
-
return result.deadCodeCacheHit === false ? 0 : null;
|
|
830
|
-
};
|
|
831
834
|
/**
|
|
832
835
|
* One queryable cache temperature per scan, derived from the whole stack:
|
|
833
836
|
*
|
|
@@ -835,8 +838,7 @@ const resolveDeadCodeReuseRatio = (result) => {
|
|
|
835
838
|
* `wholeRepoCacheHit` flag from the CLI's cachedPayload
|
|
836
839
|
* branch; no scan work ran).
|
|
837
840
|
* - `"warm"` — any incremental reuse: per-file lint hits, sidecar
|
|
838
|
-
* replays
|
|
839
|
-
* summary-cache hits.
|
|
841
|
+
* replays.
|
|
840
842
|
* - `"disabled"` — zero reuse because the global `REACT_DOCTOR_NO_CACHE`
|
|
841
843
|
* off-switch is on. Granular per-cache opt-outs
|
|
842
844
|
* (`REACT_DOCTOR_NO_FILE_CACHE`, …) still read warm/cold,
|
|
@@ -845,7 +847,7 @@ const resolveDeadCodeReuseRatio = (result) => {
|
|
|
845
847
|
*
|
|
846
848
|
* `cache.warmth` is the headline reuse magnitude in [0, 1]: the plain mean of
|
|
847
849
|
* the subsystem reuse fractions known this run (lint hit ratio, sidecar
|
|
848
|
-
* replay ratio
|
|
850
|
+
* replay ratio), skipping subsystems that never consulted a
|
|
849
851
|
* cache; `1` on turbo, dropped when nothing consulted any cache. Deliberately
|
|
850
852
|
* unweighted — the per-subsystem dims stay the precise signal; warmth is the
|
|
851
853
|
* p50/p90-able summary. Emitted only on the success path.
|
|
@@ -858,11 +860,7 @@ const buildCacheAttributes = (input) => {
|
|
|
858
860
|
warmth: 1
|
|
859
861
|
});
|
|
860
862
|
const result = input.result;
|
|
861
|
-
const subsystemReuseRatios = [
|
|
862
|
-
ratioOf(result.lintCacheHitFileCount, result.lintCacheTotalFileCount),
|
|
863
|
-
ratioOf(result.lintSidecarReplayedFileCount, result.lintSidecarTotalFileCount),
|
|
864
|
-
resolveDeadCodeReuseRatio(result)
|
|
865
|
-
];
|
|
863
|
+
const subsystemReuseRatios = [ratioOf(result.lintCacheHitFileCount, result.lintCacheTotalFileCount), ratioOf(result.lintSidecarReplayedFileCount, result.lintSidecarTotalFileCount)];
|
|
866
864
|
let knownSubsystemCount = 0;
|
|
867
865
|
let reuseRatioSum = 0;
|
|
868
866
|
for (const reuseRatio of subsystemReuseRatios) {
|
|
@@ -1014,13 +1012,7 @@ const buildOutcomeAttributes = (input) => {
|
|
|
1014
1012
|
sidecarTotalFiles: result.lintSidecarTotalFileCount,
|
|
1015
1013
|
sidecarReplayRatio: ratioOf(result.lintSidecarReplayedFileCount, result.lintSidecarTotalFileCount)
|
|
1016
1014
|
}),
|
|
1017
|
-
...withNamespace("
|
|
1018
|
-
failed: input.didDeadCodeFail,
|
|
1019
|
-
overlapped: input.deadCodeOverlapped,
|
|
1020
|
-
cacheHit: result.deadCodeCacheHit,
|
|
1021
|
-
summaryCacheHits: result.deadCodeSummaryCacheHits,
|
|
1022
|
-
summaryCacheMisses: result.deadCodeSummaryCacheMisses
|
|
1023
|
-
}),
|
|
1015
|
+
...withNamespace("maintainability", { failed: input.didDeadCodeFail }),
|
|
1024
1016
|
...withNamespace("supplyChain", { overlapTimedOut: input.supplyChainOverlapTimedOut }),
|
|
1025
1017
|
...withNamespace("securityScan", { failed: input.securityScanFailed }),
|
|
1026
1018
|
...withNamespace("timing", {
|
|
@@ -1063,7 +1055,7 @@ const buildScanAttributes = (input) => {
|
|
|
1063
1055
|
workerCount: input.workerCount,
|
|
1064
1056
|
maxDurationMs: input.maxDurationMs,
|
|
1065
1057
|
lint: input.lint,
|
|
1066
|
-
|
|
1058
|
+
maintainability: input.deadCode,
|
|
1067
1059
|
supplyChain: input.supplyChain,
|
|
1068
1060
|
scoreOnly: input.scoreOnly,
|
|
1069
1061
|
noScore: input.noScore,
|
|
@@ -1074,6 +1066,7 @@ const buildScanAttributes = (input) => {
|
|
|
1074
1066
|
hasCustomConfig: input.hasCustomConfig,
|
|
1075
1067
|
rulesConfigured: ruleKeys.length,
|
|
1076
1068
|
rulesDisabled: ruleKeys.filter((key) => ruleOverrides[key] === "off").length,
|
|
1069
|
+
projectAnalysisRuleCount: countOptInProjectRuleSelections(buildRuleSeverityControls(input.userConfig)),
|
|
1077
1070
|
fileCount: input.result?.scannedFileCount,
|
|
1078
1071
|
nonJsxFileCount: input.result?.analyzedFiles?.filter((filePath) => !JSX_FILE_PATTERN.test(filePath)).length,
|
|
1079
1072
|
htmlFileCount: input.result?.analyzedFiles?.filter((filePath) => HTML_FILE_PATTERN.test(filePath)).length,
|
|
@@ -1084,7 +1077,7 @@ const buildScanAttributes = (input) => {
|
|
|
1084
1077
|
* Projects a scan into the namespaced attribute set for its root span — the
|
|
1085
1078
|
* canonical per-scan "wide event". Every attribute carries a dotted namespace
|
|
1086
1079
|
* that groups it by concept (`scan.*` config, `action.*` CI knobs, `outcome.*`
|
|
1087
|
-
* verdict, `diag.*` findings, `score.*`, `lint.*`, `
|
|
1080
|
+
* verdict, `diag.*` findings, `score.*`, `lint.*`, `maintainability.*`, `cache.*`,
|
|
1088
1081
|
* `supplyChain.*`, `timing.*`, `migration.*`, `baseline.*`) so the attributes
|
|
1089
1082
|
* tree up in Sentry's attribute browser and stay filter-/group-/aggregate-able
|
|
1090
1083
|
* in the Spans dataset. Pure and exported so the projection (outcome
|
|
@@ -1323,7 +1316,7 @@ const buildNoScoreMessage = (input) => {
|
|
|
1323
1316
|
reason = input.disabledMessage ?? "Score disabled by --no-score.";
|
|
1324
1317
|
break;
|
|
1325
1318
|
case "analysis-incomplete":
|
|
1326
|
-
reason = "Score not shown because lint or
|
|
1319
|
+
reason = "Score not shown because lint or maintainability analysis could not complete.";
|
|
1327
1320
|
break;
|
|
1328
1321
|
case "api-unavailable":
|
|
1329
1322
|
reason = "Score unavailable (could not reach the score API).";
|
|
@@ -1371,7 +1364,7 @@ const formatRuleSummary = (ruleKey, ruleDiagnostics) => {
|
|
|
1371
1364
|
const ruleDumpFileName = (ruleKey) => ruleKey.replace(/\//g, "--") + ".txt";
|
|
1372
1365
|
const readPreviousRuleDumpFileNames = (directory) => {
|
|
1373
1366
|
try {
|
|
1374
|
-
const previous = JSON.parse(fs$
|
|
1367
|
+
const previous = JSON.parse(fs$2.readFileSync(path$1.join(directory, "diagnostics.json"), "utf8"));
|
|
1375
1368
|
return new Set(previous.filter((d) => typeof d?.plugin === "string" && typeof d?.rule === "string").map((d) => ruleDumpFileName(`${d.plugin}/${d.rule}`)));
|
|
1376
1369
|
} catch {
|
|
1377
1370
|
return /* @__PURE__ */ new Set();
|
|
@@ -1379,10 +1372,10 @@ const readPreviousRuleDumpFileNames = (directory) => {
|
|
|
1379
1372
|
};
|
|
1380
1373
|
const writeDiagnosticsDirectory = (diagnostics, outputDirectory) => {
|
|
1381
1374
|
const directory = outputDirectory ? path$1.resolve(outputDirectory) : path$1.join(tmpdir(), `react-doctor-${randomUUID()}`);
|
|
1382
|
-
if (outputDirectory) for (const fileName of readPreviousRuleDumpFileNames(directory)) fs$
|
|
1383
|
-
fs$
|
|
1384
|
-
for (const [ruleKey, ruleDiagnostics] of buildSortedRuleGroups(diagnostics)) fs$
|
|
1385
|
-
fs$
|
|
1375
|
+
if (outputDirectory) for (const fileName of readPreviousRuleDumpFileNames(directory)) fs$2.rmSync(path$1.join(directory, fileName), { force: true });
|
|
1376
|
+
fs$2.mkdirSync(directory, { recursive: true });
|
|
1377
|
+
for (const [ruleKey, ruleDiagnostics] of buildSortedRuleGroups(diagnostics)) fs$2.writeFileSync(path$1.join(directory, ruleDumpFileName(ruleKey)), formatRuleSummary(ruleKey, ruleDiagnostics));
|
|
1378
|
+
fs$2.writeFileSync(path$1.join(directory, "diagnostics.json"), JSON.stringify(diagnostics));
|
|
1386
1379
|
return directory;
|
|
1387
1380
|
};
|
|
1388
1381
|
//#endregion
|
|
@@ -1426,6 +1419,9 @@ const formatElapsedTime = (elapsedMilliseconds) => {
|
|
|
1426
1419
|
return `${(elapsedMilliseconds / MILLISECONDS_PER_SECOND).toFixed(1)}s`;
|
|
1427
1420
|
};
|
|
1428
1421
|
//#endregion
|
|
1422
|
+
//#region src/cli/utils/format-skipped-check-label.ts
|
|
1423
|
+
const formatSkippedCheckLabel = (skippedCheck) => skippedCheck === "dead-code" ? "maintainability" : skippedCheck;
|
|
1424
|
+
//#endregion
|
|
1429
1425
|
//#region src/cli/utils/pluralize.ts
|
|
1430
1426
|
const pluralize = (count, singularNoun) => `${count} ${singularNoun}${count === 1 ? "" : "s"}`;
|
|
1431
1427
|
//#endregion
|
|
@@ -1474,7 +1470,7 @@ const printHeadlessReport = (input) => gen(function* () {
|
|
|
1474
1470
|
}
|
|
1475
1471
|
if (input.skippedChecks.length > 0) {
|
|
1476
1472
|
yield* log("");
|
|
1477
|
-
yield* warn(highlighter.warn(`Results are incomplete: ${input.skippedChecks.join(" and ")} checks failed.`));
|
|
1473
|
+
yield* warn(highlighter.warn(`Results are incomplete: ${input.skippedChecks.map(formatSkippedCheckLabel).join(" and ")} checks failed.`));
|
|
1478
1474
|
}
|
|
1479
1475
|
});
|
|
1480
1476
|
//#endregion
|
|
@@ -1674,12 +1670,39 @@ const renderAndRecordScan = async (input) => {
|
|
|
1674
1670
|
didDeadCodeFail: input.payload.didDeadCodeFail,
|
|
1675
1671
|
supplyChainOverlapTimedOut: input.payload.supplyChainOverlapTimedOut,
|
|
1676
1672
|
securityScanFailed: input.payload.securityScanFailed,
|
|
1677
|
-
deadCodeOverlapped: input.payload.deadCodeOverlapped,
|
|
1678
1673
|
suppressedRuleCounts: input.payload.suppressedRuleCounts ?? []
|
|
1679
1674
|
});
|
|
1680
1675
|
return result;
|
|
1681
1676
|
};
|
|
1682
1677
|
//#endregion
|
|
1678
|
+
//#region src/cli/utils/copy-unchanged-baseline-sources.ts
|
|
1679
|
+
const copyUnchangedBaselineSources = async (input) => {
|
|
1680
|
+
const baseMaterializedFiles = new Set(input.baseMaterializedFiles.map(toForwardSlashes));
|
|
1681
|
+
const headChangedFiles = new Set(input.headChangedFiles.map(toForwardSlashes));
|
|
1682
|
+
const untrackedFiles = new Set(input.untrackedFiles.map(toForwardSlashes));
|
|
1683
|
+
const sourceDirectory = path$1.resolve(input.directory);
|
|
1684
|
+
const targetDirectory = path$1.resolve(input.tempDirectory);
|
|
1685
|
+
return (await mapWithConcurrency(input.sourceFiles.filter((filePath) => {
|
|
1686
|
+
const normalizedPath = toForwardSlashes(filePath);
|
|
1687
|
+
return !baseMaterializedFiles.has(normalizedPath) && !headChangedFiles.has(normalizedPath) && !untrackedFiles.has(normalizedPath);
|
|
1688
|
+
}), 32, async (filePath) => {
|
|
1689
|
+
input.signal?.throwIfAborted();
|
|
1690
|
+
if (input.deadlineEpochMs !== null && Date.now() >= input.deadlineEpochMs) return false;
|
|
1691
|
+
const sourcePath = path$1.resolve(sourceDirectory, filePath);
|
|
1692
|
+
const targetPath = path$1.resolve(targetDirectory, filePath);
|
|
1693
|
+
if (!isPathInsideDirectory(sourcePath, sourceDirectory) || !isPathInsideDirectory(targetPath, targetDirectory)) return false;
|
|
1694
|
+
try {
|
|
1695
|
+
if (!(await fs$1.lstat(sourcePath)).isFile()) return false;
|
|
1696
|
+
await fs$1.mkdir(path$1.dirname(targetPath), { recursive: true });
|
|
1697
|
+
await fs$1.copyFile(sourcePath, targetPath);
|
|
1698
|
+
return true;
|
|
1699
|
+
} catch {
|
|
1700
|
+
input.signal?.throwIfAborted();
|
|
1701
|
+
return false;
|
|
1702
|
+
}
|
|
1703
|
+
})).every(Boolean);
|
|
1704
|
+
};
|
|
1705
|
+
//#endregion
|
|
1683
1706
|
//#region src/cli/utils/read-diagnostic-evidence.ts
|
|
1684
1707
|
const SOURCE_EXTENSIONS = [
|
|
1685
1708
|
".ts",
|
|
@@ -2097,7 +2120,6 @@ const resolveMergeBaseRef = async (directory, ref) => {
|
|
|
2097
2120
|
const silentConsole$1 = makeNoopConsole();
|
|
2098
2121
|
const countIncompleteLintFiles = (lintPartialFailures) => countDroppedLintFiles(lintPartialFailures) + countDeadlineSkippedFiles(lintPartialFailures);
|
|
2099
2122
|
const runBaselineComparison = async (input) => {
|
|
2100
|
-
const temporaryDirectory = mkdtempSync(path.join(tmpdir(), BASELINE_FILES_TEMP_DIR_PREFIX));
|
|
2101
2123
|
const baselineIncludePaths = filterPathsOutsideDirectories({
|
|
2102
2124
|
rootDirectory: input.directory,
|
|
2103
2125
|
relativePaths: input.options.includePaths,
|
|
@@ -2113,12 +2135,24 @@ const runBaselineComparison = async (input) => {
|
|
|
2113
2135
|
relativePaths: input.headFiles,
|
|
2114
2136
|
excludedDirectories: input.options.excludedProjectDirectories
|
|
2115
2137
|
}) : void 0;
|
|
2138
|
+
const remainingBaselineBudgetMs = input.deadlineEpochMs === null ? null : remainingDeadlineBudgetMs(input.deadlineEpochMs);
|
|
2139
|
+
if (remainingBaselineBudgetMs === 0) return null;
|
|
2140
|
+
const baselineDeadlineSignal = remainingBaselineBudgetMs === null ? void 0 : AbortSignal.timeout(remainingBaselineBudgetMs);
|
|
2141
|
+
const baselineListingSignal = baselineDeadlineSignal === void 0 ? input.oxlintRuntime.abortSignal : input.oxlintRuntime.abortSignal === void 0 ? baselineDeadlineSignal : AbortSignal.any([input.oxlintRuntime.abortSignal, baselineDeadlineSignal]);
|
|
2142
|
+
let maintainabilitySourceFiles = [];
|
|
2143
|
+
if (input.options.deadCode) try {
|
|
2144
|
+
maintainabilitySourceFiles = (await listSourceFilesCooperative(input.directory, baselineListingSignal)).filter((filePath) => JSX_DUPLICATION_SOURCE_FILE_PATTERN.test(filePath) && classifyFileContext(filePath) === "production");
|
|
2145
|
+
} catch (error) {
|
|
2146
|
+
if (baselineDeadlineSignal?.aborted) return null;
|
|
2147
|
+
throw error;
|
|
2148
|
+
}
|
|
2149
|
+
const temporaryDirectory = mkdtempSync(path.join(tmpdir(), BASELINE_FILES_TEMP_DIR_PREFIX));
|
|
2116
2150
|
const snapshot = await materializeBaselineFiles({
|
|
2117
2151
|
directory: input.directory,
|
|
2118
2152
|
ref: input.baselineRef,
|
|
2119
|
-
files: baselineIncludePaths,
|
|
2120
|
-
baseFiles: baselineBaseFiles,
|
|
2121
|
-
headFiles: baselineHeadFiles,
|
|
2153
|
+
files: input.options.deadCode ? input.options.includePaths : baselineIncludePaths,
|
|
2154
|
+
baseFiles: input.options.deadCode ? input.baseFiles : baselineBaseFiles,
|
|
2155
|
+
headFiles: input.options.deadCode ? input.headFiles : baselineHeadFiles,
|
|
2122
2156
|
tempDirectory: temporaryDirectory
|
|
2123
2157
|
}).catch((error) => {
|
|
2124
2158
|
rmSync(temporaryDirectory, {
|
|
@@ -2136,22 +2170,47 @@ const runBaselineComparison = async (input) => {
|
|
|
2136
2170
|
}
|
|
2137
2171
|
try {
|
|
2138
2172
|
if (!snapshot.isComplete) return null;
|
|
2173
|
+
if (input.options.deadCode && !await copyUnchangedBaselineSources({
|
|
2174
|
+
directory: input.directory,
|
|
2175
|
+
sourceFiles: maintainabilitySourceFiles,
|
|
2176
|
+
baseMaterializedFiles: snapshot.materializedFiles,
|
|
2177
|
+
headChangedFiles: snapshot.headFiles,
|
|
2178
|
+
untrackedFiles: snapshot.untrackedFiles,
|
|
2179
|
+
tempDirectory: snapshot.tempDirectory,
|
|
2180
|
+
deadlineEpochMs: input.deadlineEpochMs,
|
|
2181
|
+
signal: input.oxlintRuntime.abortSignal
|
|
2182
|
+
})) return null;
|
|
2183
|
+
const filteredSnapshotBaseFiles = filterPathsOutsideDirectories({
|
|
2184
|
+
rootDirectory: input.directory,
|
|
2185
|
+
relativePaths: snapshot.baseFiles,
|
|
2186
|
+
excludedDirectories: input.options.excludedProjectDirectories
|
|
2187
|
+
});
|
|
2188
|
+
const filteredSnapshotHeadFiles = filterPathsOutsideDirectories({
|
|
2189
|
+
rootDirectory: input.directory,
|
|
2190
|
+
relativePaths: snapshot.headFiles,
|
|
2191
|
+
excludedDirectories: input.options.excludedProjectDirectories
|
|
2192
|
+
});
|
|
2139
2193
|
const analyzedHeadFiles = new Set(input.headAnalyzedFiles.map(toForwardSlashes));
|
|
2140
|
-
const baseFiles = new Set(
|
|
2141
|
-
const expectedHeadFiles = new Set(
|
|
2194
|
+
const baseFiles = new Set((baselineBaseFiles ?? filteredSnapshotBaseFiles).map(toForwardSlashes));
|
|
2195
|
+
const expectedHeadFiles = new Set((baselineHeadFiles ?? filteredSnapshotHeadFiles).map(toForwardSlashes));
|
|
2142
2196
|
for (const filePath of baselineIncludePaths) {
|
|
2143
2197
|
const normalizedFilePath = toForwardSlashes(filePath);
|
|
2144
2198
|
if (!baseFiles.has(normalizedFilePath)) expectedHeadFiles.add(normalizedFilePath);
|
|
2145
2199
|
}
|
|
2146
|
-
if (filterSourceFiles([...expectedHeadFiles]).some((filePath) => !analyzedHeadFiles.has(filePath))) return null;
|
|
2200
|
+
if (input.options.lint && filterSourceFiles([...expectedHeadFiles]).some((filePath) => !analyzedHeadFiles.has(filePath))) return null;
|
|
2201
|
+
const baselineLintPaths = new Set([...baselineIncludePaths, ...filteredSnapshotBaseFiles].map(toForwardSlashes));
|
|
2202
|
+
const materializedLintPaths = snapshot.materializedFiles.filter((filePath) => baselineLintPaths.has(toForwardSlashes(filePath)));
|
|
2203
|
+
const maintainabilityFocusPaths = [...new Set([...input.options.includePaths, ...snapshot.baseFiles].map(toForwardSlashes))];
|
|
2204
|
+
const baselineExcludedProjectDirectories = input.options.excludedProjectDirectories.map((excludedDirectory) => path.resolve(excludedDirectory)).filter((excludedDirectory) => isPathInsideDirectory(excludedDirectory, input.directory)).map((excludedDirectory) => path.resolve(snapshot.tempDirectory, path.relative(input.directory, excludedDirectory)));
|
|
2205
|
+
const baseIncludePaths = materializedLintPaths.length > 0 ? materializedLintPaths : maintainabilityFocusPaths;
|
|
2147
2206
|
const runtimeLayers = buildRuntimeLayers({
|
|
2148
2207
|
directory: snapshot.tempDirectory,
|
|
2149
2208
|
hasConfigOverride: true,
|
|
2150
2209
|
userConfig: input.userConfig,
|
|
2151
2210
|
configSourceDirectory: input.configSourceDirectory,
|
|
2152
2211
|
projectInfoOverride: input.headProjectInfo,
|
|
2153
|
-
shouldSkipLint: !input.options.lint || !input.resolvedNodeBinaryPath,
|
|
2154
|
-
shouldRunDeadCode:
|
|
2212
|
+
shouldSkipLint: !input.options.lint || !input.resolvedNodeBinaryPath || materializedLintPaths.length === 0,
|
|
2213
|
+
shouldRunDeadCode: input.options.deadCode,
|
|
2155
2214
|
shouldRunSupplyChain: input.options.supplyChain,
|
|
2156
2215
|
shouldComputeScore: false,
|
|
2157
2216
|
shouldShowProgressSpinners: false,
|
|
@@ -2160,7 +2219,8 @@ const runBaselineComparison = async (input) => {
|
|
|
2160
2219
|
});
|
|
2161
2220
|
const baseOutput = await runPromise(restoreLegacyThrow(runInspect({
|
|
2162
2221
|
directory: snapshot.tempDirectory,
|
|
2163
|
-
includePaths:
|
|
2222
|
+
includePaths: baseIncludePaths,
|
|
2223
|
+
maintainabilityFocusPaths,
|
|
2164
2224
|
customRulesOnly: input.options.customRulesOnly,
|
|
2165
2225
|
respectInlineDisables: input.options.respectInlineDisables,
|
|
2166
2226
|
warnings: input.options.warnings,
|
|
@@ -2169,7 +2229,7 @@ const runBaselineComparison = async (input) => {
|
|
|
2169
2229
|
includedTags: input.options.includedTags,
|
|
2170
2230
|
includeTagDefaults: input.options.includeTagDefaults,
|
|
2171
2231
|
nodeBinaryPath: input.resolvedNodeBinaryPath ?? void 0,
|
|
2172
|
-
runDeadCode:
|
|
2232
|
+
runDeadCode: input.options.deadCode,
|
|
2173
2233
|
isCi: input.options.isCi,
|
|
2174
2234
|
doctorVersion: VERSION,
|
|
2175
2235
|
runId: getRunId(),
|
|
@@ -2177,10 +2237,16 @@ const runBaselineComparison = async (input) => {
|
|
|
2177
2237
|
suppressScanSummary: true,
|
|
2178
2238
|
supplyChainManifestChanged: input.options.supplyChainManifestChanged,
|
|
2179
2239
|
deadlineEpochMs: input.deadlineEpochMs ?? void 0,
|
|
2180
|
-
signal: input.oxlintRuntime.abortSignal
|
|
2240
|
+
signal: input.oxlintRuntime.abortSignal,
|
|
2241
|
+
excludedProjectDirectories: baselineExcludedProjectDirectories,
|
|
2242
|
+
retainExcludedProjectDeadCodeDiagnostics: input.options.retainExcludedProjectDeadCodeDiagnostics
|
|
2181
2243
|
}, {}).pipe(provide(runtimeLayers), provideService(PerFileLintCacheEnabled, false), provideService(SidecarLintCacheEnabled, false), provideService(Console, silentConsole$1))), { signal: input.oxlintRuntime.abortSignal });
|
|
2182
|
-
if (baseOutput.didLintFail || countIncompleteLintFiles(baseOutput.lintPartialFailures) > 0) return null;
|
|
2183
|
-
const hasUnscannedUntrackedSourceFiles = filterSourceFiles(
|
|
2244
|
+
if (baseOutput.didLintFail || baseOutput.didDeadCodeFail || countIncompleteLintFiles(baseOutput.lintPartialFailures) > 0) return null;
|
|
2245
|
+
const hasUnscannedUntrackedSourceFiles = filterSourceFiles(filterPathsOutsideDirectories({
|
|
2246
|
+
rootDirectory: input.directory,
|
|
2247
|
+
relativePaths: snapshot.untrackedFiles,
|
|
2248
|
+
excludedDirectories: input.options.excludedProjectDirectories
|
|
2249
|
+
}).map(toForwardSlashes)).some((filePath) => !analyzedHeadFiles.has(filePath));
|
|
2184
2250
|
const diagnosticDelta = computeDiagnosticDelta({
|
|
2185
2251
|
headDiagnostics: input.headDiagnostics,
|
|
2186
2252
|
baseDiagnostics: baseOutput.diagnostics,
|
|
@@ -2353,6 +2419,7 @@ const runInspectWithRuntime = async (directory, options, userConfig, hasConfigOv
|
|
|
2353
2419
|
directory,
|
|
2354
2420
|
precomputedSourceFileCount: options.precomputedSourceFileCount,
|
|
2355
2421
|
includePaths: options.includePaths,
|
|
2422
|
+
changedLineRanges: options.changedLineRanges ?? void 0,
|
|
2356
2423
|
customRulesOnly: options.customRulesOnly,
|
|
2357
2424
|
respectInlineDisables: options.respectInlineDisables,
|
|
2358
2425
|
warnings: options.warnings,
|
|
@@ -2383,7 +2450,7 @@ const runInspectWithRuntime = async (directory, options, userConfig, hasConfigOv
|
|
|
2383
2450
|
else runSync(error(highlighter.error(lintFailureReason)));
|
|
2384
2451
|
let inspectDiagnostics = output.diagnostics;
|
|
2385
2452
|
let baselineDelta;
|
|
2386
|
-
if (options.baseline && isDiffMode && !didLintFail && countIncompleteLintFiles(output.lintPartialFailures) === 0) {
|
|
2453
|
+
if (options.baseline && isDiffMode && !didLintFail && !output.didDeadCodeFail && countIncompleteLintFiles(output.lintPartialFailures) === 0) {
|
|
2387
2454
|
const comparison = await runBaselineComparison({
|
|
2388
2455
|
directory,
|
|
2389
2456
|
options,
|
|
@@ -2536,13 +2603,13 @@ const resolveCursorBundledNode = () => {
|
|
|
2536
2603
|
if (!localAppData) return null;
|
|
2537
2604
|
const cursorAgentRoot = path$1.join(localAppData, "cursor-agent", "versions");
|
|
2538
2605
|
try {
|
|
2539
|
-
const versions = fs$
|
|
2606
|
+
const versions = fs$2.readdirSync(cursorAgentRoot);
|
|
2540
2607
|
if (versions.length === 0) return null;
|
|
2541
2608
|
const sortedVersions = versions.sort((a, b) => b.localeCompare(a, void 0, { numeric: true }));
|
|
2542
2609
|
for (const version of sortedVersions) try {
|
|
2543
2610
|
const bundledNodePath = path$1.join(cursorAgentRoot, version, "node.exe");
|
|
2544
2611
|
const entryScriptPath = path$1.join(cursorAgentRoot, version, "index.js");
|
|
2545
|
-
if (fs$
|
|
2612
|
+
if (fs$2.statSync(bundledNodePath).isFile() && fs$2.statSync(entryScriptPath).isFile()) return {
|
|
2546
2613
|
command: bundledNodePath,
|
|
2547
2614
|
args: [entryScriptPath]
|
|
2548
2615
|
};
|
|
@@ -2556,15 +2623,15 @@ const resolveWindowsCmdEntryScript = (command) => {
|
|
|
2556
2623
|
for (const directory of pathDirectories) {
|
|
2557
2624
|
const cmdFilePath = path$1.join(directory, `${command}.cmd`);
|
|
2558
2625
|
try {
|
|
2559
|
-
if (!fs$
|
|
2626
|
+
if (!fs$2.statSync(cmdFilePath).isFile()) continue;
|
|
2560
2627
|
} catch {
|
|
2561
2628
|
continue;
|
|
2562
2629
|
}
|
|
2563
2630
|
try {
|
|
2564
|
-
const entryScriptMatch = fs$
|
|
2631
|
+
const entryScriptMatch = fs$2.readFileSync(cmdFilePath, "utf8").match(/"%(?:~dp0|dp0%)\\([^"]+\.(?:m?js|cjs))"/);
|
|
2565
2632
|
if (!entryScriptMatch) continue;
|
|
2566
2633
|
const resolvedScriptPath = path$1.resolve(directory, entryScriptMatch[1]);
|
|
2567
|
-
if (fs$
|
|
2634
|
+
if (fs$2.statSync(resolvedScriptPath).isFile()) return resolvedScriptPath;
|
|
2568
2635
|
} catch {}
|
|
2569
2636
|
}
|
|
2570
2637
|
return null;
|
|
@@ -2829,6 +2896,45 @@ const resolveProjectScan = async (rootScanTarget, projectDirectory) => {
|
|
|
2829
2896
|
};
|
|
2830
2897
|
};
|
|
2831
2898
|
//#endregion
|
|
2899
|
+
//#region src/cli/utils/highlight-code-line.ts
|
|
2900
|
+
const CODE_TOKEN_PATTERN = /\/\/.*|\/\*.*?\*\/|"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|`(?:\\.|[^`\\])*`|\b(?:as|async|await|break|case|catch|class|const|continue|debugger|default|delete|do|else|export|extends|false|finally|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|set|static|super|switch|this|throw|true|try|type|typeof|undefined|var|void|while|with|yield)\b|\b(?:0[xob][\da-f]+|\d+(?:\.\d+)?)\b/giu;
|
|
2901
|
+
const highlightCodeLine = (codeLine) => codeLine.replace(CODE_TOKEN_PATTERN, (token) => {
|
|
2902
|
+
if (token.startsWith("/")) return highlighter.gray(token);
|
|
2903
|
+
if (token.startsWith("\"") || token.startsWith("'") || token.startsWith("`")) return highlighter.success(token);
|
|
2904
|
+
if (/^\d|^0[xob]/i.test(token)) return highlighter.warn(token);
|
|
2905
|
+
return highlighter.info(token);
|
|
2906
|
+
});
|
|
2907
|
+
//#endregion
|
|
2908
|
+
//#region src/cli/utils/render-code-frame.ts
|
|
2909
|
+
const SOURCE_LINE_BREAK_PATTERN = /\r\n|[\n\r\u2028\u2029]/;
|
|
2910
|
+
const renderCodeFrame = ({ source, line, column, endLine, message, linesAbove, linesBelow, maximumLineLength }) => {
|
|
2911
|
+
const sourceLines = source.split(SOURCE_LINE_BREAK_PATTERN);
|
|
2912
|
+
if (line < 1 || line > sourceLines.length) return null;
|
|
2913
|
+
const offendingLine = sourceLines[line - 1];
|
|
2914
|
+
if (offendingLine === void 0 || offendingLine.length > maximumLineLength) return null;
|
|
2915
|
+
const lastMarkedLine = Math.min(Math.max(endLine ?? line, line), sourceLines.length);
|
|
2916
|
+
const firstDisplayedLine = Math.max(line - linesAbove, 1);
|
|
2917
|
+
const lastDisplayedLine = Math.min(lastMarkedLine + linesBelow, sourceLines.length);
|
|
2918
|
+
const lineNumberWidth = String(lastDisplayedLine).length;
|
|
2919
|
+
const hasCaret = lastMarkedLine === line && column !== void 0;
|
|
2920
|
+
const outputLines = [];
|
|
2921
|
+
if (message && !hasCaret) outputLines.push(`${" ".repeat(lineNumberWidth + 2)}${highlighter.error(message)}`);
|
|
2922
|
+
for (let displayedLineNumber = firstDisplayedLine; displayedLineNumber <= lastDisplayedLine; displayedLineNumber += 1) {
|
|
2923
|
+
const sourceLine = sourceLines[displayedLineNumber - 1] ?? "";
|
|
2924
|
+
const isMarked = displayedLineNumber >= line && displayedLineNumber <= lastMarkedLine;
|
|
2925
|
+
const gutter = ` ${String(displayedLineNumber).padStart(lineNumberWidth)} |`;
|
|
2926
|
+
const marker = isMarked ? highlighter.error(">") : " ";
|
|
2927
|
+
outputLines.push(`${marker}${highlighter.dim(gutter)}${sourceLine.length > 0 ? ` ${highlightCodeLine(sourceLine)}` : ""}`);
|
|
2928
|
+
if (isMarked && hasCaret) {
|
|
2929
|
+
const markerSpacing = sourceLine.slice(0, Math.max(column - 1, 0)).replace(/[^\t]/g, " ");
|
|
2930
|
+
const emptyGutter = gutter.replace(/\d/g, " ");
|
|
2931
|
+
const markerMessage = message ? ` ${highlighter.error(message)}` : "";
|
|
2932
|
+
outputLines.push(` ${highlighter.dim(emptyGutter)} ${markerSpacing}${highlighter.error("^")}${markerMessage}`);
|
|
2933
|
+
}
|
|
2934
|
+
}
|
|
2935
|
+
return outputLines.join("\n");
|
|
2936
|
+
};
|
|
2937
|
+
//#endregion
|
|
2832
2938
|
//#region src/cli/utils/resolve-absolute-path.ts
|
|
2833
2939
|
/**
|
|
2834
2940
|
* Resolves a diagnostic's `filePath` (relative to its project root, or
|
|
@@ -2851,23 +2957,20 @@ const buildCodeFrame = (input) => {
|
|
|
2851
2957
|
const absolutePath = resolveAbsolutePath(input.filePath, input.rootDirectory);
|
|
2852
2958
|
let source;
|
|
2853
2959
|
try {
|
|
2854
|
-
source = fs$
|
|
2960
|
+
source = fs$2.readFileSync(absolutePath, "utf8");
|
|
2855
2961
|
} catch {
|
|
2856
2962
|
return null;
|
|
2857
2963
|
}
|
|
2858
|
-
|
|
2859
|
-
|
|
2860
|
-
|
|
2861
|
-
end: { line: input.endLine }
|
|
2862
|
-
} : { start: {
|
|
2964
|
+
const endLine = input.endLine != null && input.endLine > input.line ? input.endLine : void 0;
|
|
2965
|
+
return renderCodeFrame({
|
|
2966
|
+
source,
|
|
2863
2967
|
line: input.line,
|
|
2864
|
-
column: input.column > 0 ? input.column : void 0
|
|
2865
|
-
|
|
2866
|
-
|
|
2867
|
-
highlightCode: true,
|
|
2968
|
+
column: endLine === void 0 && input.column > 0 ? input.column : void 0,
|
|
2969
|
+
endLine,
|
|
2970
|
+
message: input.message,
|
|
2868
2971
|
linesAbove: 1,
|
|
2869
2972
|
linesBelow: 1,
|
|
2870
|
-
|
|
2973
|
+
maximumLineLength: 200
|
|
2871
2974
|
});
|
|
2872
2975
|
};
|
|
2873
2976
|
//#endregion
|
|
@@ -2875,7 +2978,7 @@ const buildCodeFrame = (input) => {
|
|
|
2875
2978
|
const discoverWorkspacePackages = (rootDirectory) => {
|
|
2876
2979
|
const hasRootPackageJson = isFile(path$1.join(rootDirectory, "package.json"));
|
|
2877
2980
|
const packages = listWorkspacePackages(rootDirectory);
|
|
2878
|
-
if (packages.length === 0 && (!hasRootPackageJson || isMonorepoRoot(rootDirectory))) return
|
|
2981
|
+
if (packages.length === 0 && (!hasRootPackageJson || isMonorepoRoot(rootDirectory))) return discoverSupportedSubprojects(rootDirectory);
|
|
2879
2982
|
return packages;
|
|
2880
2983
|
};
|
|
2881
2984
|
const selectProjects = async (rootDirectory, projectFlag, skipPrompts, configProjects) => {
|
|
@@ -2991,7 +3094,7 @@ const shouldFailScanGate = (input) => {
|
|
|
2991
3094
|
return shouldBlockCi(filterScansForSurface(input.scans, "ciFailure"), input.blockingLevel);
|
|
2992
3095
|
};
|
|
2993
3096
|
//#endregion
|
|
2994
|
-
export { resolveMergeBaseRef as A,
|
|
3097
|
+
export { resolveMergeBaseRef as A, buildNoScoreMessage as B, copyToClipboard as C, deduplicateProjectScans as D, collectProjectSourceFileCounts as E, printFooter as F, formatFixRecipeLine as G, hasLintHardFailure as H, printDiagnosticsDump as I, formatLearnMoreLine as K, formatDiagnosticSite as L, pluralize as M, formatSkippedCheckLabel as N, createInvocationInspect as O, formatElapsedTime as P, hasIncompleteScoreAnalysis as R, CLI_AGENT_BINARIES as S, buildHandoffPayload as T, buildRulePriorityMap as U, buildEmptyReportMessage as V, buildSortedRuleGroups as W, resolveCliInspectOptions as _, projectManifestChanged as a, setUpGitHubActions as b, selectProjects as c, resolveProjectChangedLineRanges as d, resolveProjectDiffIncludePaths as f, resolveWorkspaceDeadCodeOwner as g, retryMissingProjectScores as h, validateModeFlags as i, printHeadlessReport as j, inspect as k, buildCodeFrame as l, resolveProjectRelativeDirectory as m, formatSkippedProjectsMessage as n, partitionProjectScanOutcomes as o, resolveProjectSourceFilePaths as p, validateIncludeUntrackedScope as r, discoverWorkspacePackages as s, shouldFailScanGate as t, resolveProjectScan as u, filterScansForSurface as v, launchCliAgent as w, detectLaunchableAgents as x, countUniqueScannedFiles as y, filterDiagnosticsByCategories as z };
|
|
2995
3098
|
|
|
2996
|
-
//# sourceMappingURL=should-fail-scan-gate-
|
|
2997
|
-
//# debugId=
|
|
3099
|
+
//# sourceMappingURL=should-fail-scan-gate-Dch9vbPz.js.map
|
|
3100
|
+
//# debugId=19bff337-5a1f-5558-b6b7-bd4274f53f38
|