react-doctor 0.9.12-dev.a2b460d → 0.9.12-dev.adc3a91
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 +28 -1
- package/bin/react-doctor.js +1 -9
- package/dist/active-run-trace-dlIZeEvb.js +18 -0
- package/dist/active-scan-abort-registry-DwG6tSPy.js +28 -0
- package/dist/{build-section-divider-Vz879zXD.js → build-section-divider-Dv6m6j69.js} +10 -25
- package/dist/{ci-BVYrYekU.js → ci-Dcd3uL9T.js} +13 -12
- package/dist/cli.js +84 -32
- package/dist/{design-D-TYN_tr.js → design-BNyZe3w-.js} +4 -4
- package/dist/{devtools-DPRVRu3S.js → devtools-BiC-AP3B.js} +4 -4
- package/dist/{exit-gracefully-Cjv5kC_l.js → exit-gracefully-Dh5YXDbT.js} +15 -9
- package/dist/get-east-asian-width-Cglw13G7.js +444 -0
- package/dist/{report-error-1l7v6_dE.js → handle-error-dBFOogZg.js} +18 -68
- package/dist/index.d.ts +53 -42
- package/dist/index.js +55135 -54706
- package/dist/{inspect-kkvDHKDE.js → inspect-CyuCR1pv.js} +22 -20
- package/dist/{install-doctor-script-D58Hr1dM.js → install-doctor-script-CbqTpviA.js} +7 -449
- package/dist/{install-github-workflow-DkMicv8E.js → install-github-workflow-DLw7rmkB.js} +10 -6
- package/dist/{install-ZeYvD9Rw.js → install-hA22sIab.js} +9 -8
- package/dist/{install-react-doctor-CjgE52al.js → install-react-doctor-tvBM6Rfq.js} +47 -45
- package/dist/{run-scan-app-Ciq22jzp.js → jsx-runtime-Ckwofk1u.js} +281 -2759
- package/dist/{print-branded-header-D4QYSu-h.js → print-branded-header-C12WC-t7.js} +4 -4
- package/dist/project-analysis-worker.d.ts +1 -0
- package/dist/project-analysis-worker.js +51901 -0
- package/dist/prompt-runtime-scan-url-jie2F0Cp.js +240 -0
- package/dist/{read-object-file-OFRlvCAL.js → read-object-file-D3nE_zn5.js} +4 -4
- package/dist/{record-metric-CMe2d1Xv.js → record-metric-0CLCYZUM.js} +3273 -6036
- package/dist/report-error-BQQg-7YD.js +64 -0
- package/dist/{report-workflow-result-EagkH_v9.js → report-workflow-result-DtFfzMv7.js} +5 -5
- package/dist/{resolve-scope-CMNrOdDf.js → resolve-scope-Vfpv9BNM.js} +6 -6
- package/dist/{rules-CzR1qUru.js → rules--f7blLSe.js} +6 -6
- package/dist/run-scan-app-DmrHa81A.js +2510 -0
- package/dist/runtime-scan/browser-probe.iife.js +855 -0
- package/dist/runtime-scan-DhL9erRS.js +770 -0
- package/dist/sanitize-runtime-url-r1wId2Bs.js +76 -0
- package/dist/{scan-D4m5r4Rs.js → scan-B0UeUfKj.js} +10 -22
- package/dist/semver-DFtnEmN-.js +3219 -0
- package/dist/{should-fail-scan-gate-lMTlhEhE.js → should-fail-scan-gate-BscVoE6r.js} +153 -184
- package/dist/{should-skip-prompts-mXKZBNCz.js → should-skip-prompts-CLybciiZ.js} +4 -4
- package/dist/should-use-tui-Bxxe6pqm.js +25 -0
- package/dist/skills/improve-threejs/SKILL.md +102 -0
- package/dist/skills/performance/SKILL.md +113 -0
- package/dist/skills/react-doctor/SKILL.md +6 -0
- package/dist/{unref-stdin-Cxhw67r6.js → telemetry-runtime-k0WVe-zH.js} +57 -33
- package/dist/unref-stdin-BKT_s38a.js +12 -0
- package/dist/{version-CnvVY2VD.js → version-CpTdauSi.js} +4 -4
- package/dist/{why-BktDEhhH.js → why-CvFMs9O1.js} +4 -4
- package/dist/with-run-span-DYghG7i4.js +110 -0
- package/package.json +8 -9
- package/dist/chunk-q7NCDQ7-.js +0 -28
- package/dist/lsp.js +0 -81145
|
@@ -1,21 +1,24 @@
|
|
|
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 {
|
|
5
|
-
import { n as buildFooterLinkLines,
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
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]="083957d2-1e94-5e65-8051-7a4bad072e84")}catch(e){}}();
|
|
3
|
+
import { $ as Linter, A as SCAN_RESULT_CACHE_FILENAME, An as gen, At as discoverSupportedSubprojects, B as isNonInteractiveEnvironment, Bn as sync, Cn as error, Ct as collectSourceFileCountsByDirectory, Dn as Diagnostic, Dt as createNodeReadFileLinesSync, En as warn, Et as countOptInProjectRuleSelections, Fn as provide, G as isCiEnvironment, Gt as hashFileContents, Ht as groupBy, In as provideContext, J as Config, Jt as isPathInsideDirectory, K as isCiOrCodingAgentEnvironment, Kn as succeed, Kt as highlighter, Ln as provideService, Mn as map, Mt as filterPathsOutsideDirectories, Nn as orElseSucceed, Nt as filterSourceFiles, On as decodeUnknownSync, Ot as createOxlintSpawnSlots, Pn as promise, Q as LintPartialFailures, Qt as isScanComplete, Rn as runPromise, S as BASELINE_FILES_TEMP_DIR_PREFIX, Sn as Console, T as METRIC, Tn as log, Tt as computeDiagnosticDelta, U as ACTION_INPUT_ENVIRONMENT_VARIABLES, Ut as hasPublishedFixRecipe, Vn as try_, W as detectRunnerOs, Wn as mergeAll, X as Git, Xt as isReactDoctorError, Y as Files, Zn as ParentSpan, _ as VERSION, _n as shouldUseMaintainabilityLayer, _t as buildRuleSeverityControls, an as materializeSourceTree, ar as DIAGNOSTIC_CATEGORY_BUCKETS, at as Progress, bt as canonicalizeUserRuleKey, cn as resolveGithubActionsScoreMetadata, ct as Score, d as isJsonModeActive, dn as resolveScanConcurrency, dr as JSX_DUPLICATION_SOURCE_FILE_PATTERN, dt as SupplyChain, en as listSourceFiles, et as Maintainability, fn as resolveScanTarget, fr as JSX_FILE_PATTERN, gt as buildRuleDocsUrl, hr as SHARE_BASE_URL, in as mapWithConcurrency, it as PerFileLintCacheEnabled, jt as filterDiagnosticsForSurface, l as getRunId, ln as resolveLintBatchOrdering, lr as GIT_SHOW_MAX_BUFFER_BYTES, lt as SidecarLintCacheEnabled, mn as runInspect, mr as OXLINT_NODE_REQUIREMENT, mt as atomicWriteJson, n as recordDistribution, nn as listWorkspacePackages, nt as OxlintConcurrency, on as mergeReactDoctorConfigs, ot as Project, pn as restoreLegacyThrow, pr as MILLISECONDS_PER_SECOND, qt as isMonorepoRoot, r as toSpanAttributes, rt as OxlintSpawnSlots, sn as remainingDeadlineBudgetMs, sr as ENTERPRISE_CONTACT_URL, st as Reporter, t as recordCount, tn as listSourceFilesCooperative, tr as CI_URL, tt as NodeResolver, un as resolveReactDoctorCacheDir, ur as HTML_FILE_PATTERN, v as makeNoopConsole, vn as summarizeDiagnostics, vt as buildSkippedChecks, wt as computeConfigFingerprint, xn as yieldToEventLoop, xr as isFile, xt as classifyFileContext, yr as isDirectory, yt as calculateScore, z as CliInputError, zn as runSync, zt as getDiagnosticRuleIdentity } from "./record-metric-0CLCYZUM.js";
|
|
4
|
+
import { t as getTelemetryContext } from "./telemetry-runtime-k0WVe-zH.js";
|
|
5
|
+
import { n as buildFooterLinkLines, t as buildSectionDivider } from "./build-section-divider-Dv6m6j69.js";
|
|
6
|
+
import { t as activeScanAbortRegistry } from "./active-scan-abort-registry-DwG6tSPy.js";
|
|
7
|
+
import { C as isRecord, M as cliLogger, S as getPackageJsonPath, a as installReactDoctorScriptStep, c as spinner, k as runGit, n as findNearestPackageDirectory, o as isSpinnerSilent, s as setSpinnerSilent } from "./install-doctor-script-CbqTpviA.js";
|
|
8
|
+
import { d as detectDefaultBranch, l as prompts, r as installReactDoctorWorkflow, u as isCommandAvailable } from "./install-github-workflow-DLw7rmkB.js";
|
|
9
|
+
import { a as isValidBlockingLevel, c as openWorkflowPullRequest, l as stageWorkflowFile, o as pickBlockingLevel, u as toForwardSlashes } from "./resolve-scope-Vfpv9BNM.js";
|
|
10
|
+
import { n as resetSentryRunState, r as withRunSpan, t as recordSentryProjectContext } from "./with-run-span-DYghG7i4.js";
|
|
11
|
+
import { h as detectAvailableAgents, t as reportWorkflowResult } from "./report-workflow-result-DtFfzMv7.js";
|
|
10
12
|
import { createRequire } from "node:module";
|
|
11
13
|
import { spawn } from "node:child_process";
|
|
12
14
|
import * as path$1 from "node:path";
|
|
13
15
|
import path from "node:path";
|
|
14
|
-
import * as fs$
|
|
16
|
+
import * as fs$2 from "node:fs";
|
|
15
17
|
import fs, { mkdtempSync, rmSync } from "node:fs";
|
|
16
|
-
import crypto, { randomUUID } from "node:crypto";
|
|
17
18
|
import ts from "typescript";
|
|
19
|
+
import crypto, { randomUUID } from "node:crypto";
|
|
18
20
|
import { tmpdir } from "node:os";
|
|
21
|
+
import * as fs$1 from "node:fs/promises";
|
|
19
22
|
import { performance } from "node:perf_hooks";
|
|
20
23
|
//#region src/cli/utils/apply-observability.ts
|
|
21
24
|
/**
|
|
@@ -83,7 +86,10 @@ const buildSpinnerProgressHandle = (text) => {
|
|
|
83
86
|
*/
|
|
84
87
|
const buildRuntimeLayers = (input) => {
|
|
85
88
|
const linterLayer = input.shouldSkipLint ? Linter.layerOf([]) : Linter.layerOxlint;
|
|
86
|
-
const
|
|
89
|
+
const maintainabilityLayer = shouldUseMaintainabilityLayer({
|
|
90
|
+
shouldRunDuplicateJsx: input.shouldRunDeadCode,
|
|
91
|
+
userConfig: input.userConfig
|
|
92
|
+
}) ? Maintainability.layerNode : Maintainability.layerOf([]);
|
|
87
93
|
const scoreLayer = input.shouldComputeScore ? Score.layerHttp : Score.layerOf(null);
|
|
88
94
|
const supplyChainLayer = input.shouldRunSupplyChain ? SupplyChain.layerNode : SupplyChain.layerOf([]);
|
|
89
95
|
const projectLayer = input.projectInfoOverride === void 0 ? Project.layerNode : Project.layerOf(input.projectInfoOverride);
|
|
@@ -93,112 +99,11 @@ const buildRuntimeLayers = (input) => {
|
|
|
93
99
|
config: input.userConfig,
|
|
94
100
|
resolvedDirectory: input.directory,
|
|
95
101
|
configSourceDirectory: input.configSourceDirectory
|
|
96
|
-
}) : Config.layerNode, Files.layerNode, Git.layerNode, linterLayer, LintPartialFailures.layerLive,
|
|
102
|
+
}) : Config.layerNode, Files.layerNode, Git.layerNode, linterLayer, LintPartialFailures.layerLive, maintainabilityLayer, progressLayer, reporterLayer, scoreLayer, supplyChainLayer);
|
|
97
103
|
const layersWithConcurrency = input.oxlintConcurrency === void 0 ? baseLayers : mergeAll(baseLayers, succeed(OxlintConcurrency, input.oxlintConcurrency));
|
|
98
104
|
return input.oxlintSpawnSlots === void 0 ? layersWithConcurrency : mergeAll(layersWithConcurrency, succeed(OxlintSpawnSlots, input.oxlintSpawnSlots));
|
|
99
105
|
};
|
|
100
106
|
//#endregion
|
|
101
|
-
//#region src/cli/utils/with-run-span.ts
|
|
102
|
-
/**
|
|
103
|
-
* Ends the run span without letting a telemetry failure change the run's
|
|
104
|
-
* outcome — the invariant every other emit site here already holds.
|
|
105
|
-
*
|
|
106
|
-
* The failure path is the reason this matters most: a throw from `end` there
|
|
107
|
-
* would replace the user's original scan error, so a project-config mistake
|
|
108
|
-
* could surface as an exporter stack trace, be reclassified as a crash, and get
|
|
109
|
-
* reported as one.
|
|
110
|
-
*/
|
|
111
|
-
const endSpanSafely = (span, exit) => {
|
|
112
|
-
try {
|
|
113
|
-
span.end(BigInt(Date.now()) * NANOSECONDS_PER_MILLISECOND, exit);
|
|
114
|
-
} catch {}
|
|
115
|
-
};
|
|
116
|
-
/**
|
|
117
|
-
* Clears the module-level run-scoped telemetry state — the current scanned
|
|
118
|
-
* project and the active run trace. `inspect()` calls this at the start of every
|
|
119
|
-
* run and again after a clean one, so a prior or just-finished scan can't attach
|
|
120
|
-
* its project tags / trace to a later run or to a non-scan error (e.g.
|
|
121
|
-
* inspectAction's post-loop finalize/handoff steps). A thrown scan error skips
|
|
122
|
-
* the post-run reset, leaving the state for the command catch to attribute and
|
|
123
|
-
* link the crash. Concurrent batch members (`concurrentScan`) never touch this
|
|
124
|
-
* state — they neither write nor reset it. Safe to call when telemetry is off
|
|
125
|
-
* (the refs are read only when an event is built).
|
|
126
|
-
*/
|
|
127
|
-
const resetSentryRunState = () => {
|
|
128
|
-
setSentryProjectInfo(null);
|
|
129
|
-
setActiveRunTrace(null);
|
|
130
|
-
};
|
|
131
|
-
/**
|
|
132
|
-
* Runs an inspect invocation inside a root span so each `react-doctor` run is a
|
|
133
|
-
* first-class trace with timing and the run snapshot as attributes. The span is
|
|
134
|
-
* handed to `run` so the scan's Effect spans parent under it and so the wide
|
|
135
|
-
* event can be stamped onto it once the outcome is known.
|
|
136
|
-
*
|
|
137
|
-
* A no-op pass-through when telemetry is off (`--no-telemetry`, tests, or an
|
|
138
|
-
* unconfigured build) — `run` receives `undefined` and no span is created, so
|
|
139
|
-
* there's no added exit latency.
|
|
140
|
-
*
|
|
141
|
-
* The span is created against the shared telemetry context rather than a
|
|
142
|
-
* freshly-provided layer, so it lives in the same tracer as the scan program's
|
|
143
|
-
* spans; see `telemetry-runtime.ts` for why that matters. It is started and
|
|
144
|
-
* ended manually because the body it wraps is a promise-returning imperative
|
|
145
|
-
* function, not an Effect — the same shape the Sentry inactive span had.
|
|
146
|
-
*
|
|
147
|
-
* While the span runs, its trace context is recorded as the active run trace so
|
|
148
|
-
* `reportErrorToSentry` can attach a crash thrown during the scan back to this
|
|
149
|
-
* trace (errors surface in the command catch, after the span has ended).
|
|
150
|
-
*
|
|
151
|
-
* A `concurrentScan` (one member of the CLI's multi-project pool) still gets its
|
|
152
|
-
* own root span, but skips recording the active run trace — the module-level
|
|
153
|
-
* handle has single-scan semantics, and overlapping writers would link a crash
|
|
154
|
-
* to an arbitrary sibling's trace.
|
|
155
|
-
*/
|
|
156
|
-
const withRunSpan = async (run, options = {}) => {
|
|
157
|
-
const telemetryContext = getTelemetryContext();
|
|
158
|
-
if (telemetryContext === null) return run(void 0);
|
|
159
|
-
const { tags } = buildSentryScope();
|
|
160
|
-
const command = typeof tags.command === "string" ? tags.command : "inspect";
|
|
161
|
-
let rootSpan;
|
|
162
|
-
try {
|
|
163
|
-
rootSpan = runSync(makeSpan(`react-doctor ${command}`, { attributes: toSpanAttributes(tags) }).pipe(provideContext(telemetryContext)));
|
|
164
|
-
} catch {
|
|
165
|
-
return run(void 0);
|
|
166
|
-
}
|
|
167
|
-
recordRunTraceId(rootSpan.traceId);
|
|
168
|
-
if (options.concurrentScan !== true) setActiveRunTrace({
|
|
169
|
-
traceId: rootSpan.traceId,
|
|
170
|
-
spanId: rootSpan.spanId,
|
|
171
|
-
sampled: rootSpan.sampled
|
|
172
|
-
});
|
|
173
|
-
try {
|
|
174
|
-
const result = await run(rootSpan);
|
|
175
|
-
endSpanSafely(rootSpan, void_);
|
|
176
|
-
return result;
|
|
177
|
-
} catch (error) {
|
|
178
|
-
endSpanSafely(rootSpan, fail(error));
|
|
179
|
-
throw error;
|
|
180
|
-
}
|
|
181
|
-
};
|
|
182
|
-
/**
|
|
183
|
-
* Records the scanned project (discovered in the `beforeLint` hook): remembers
|
|
184
|
-
* it for the lazy Sentry error-capture path (`buildSentryScope` folds it into
|
|
185
|
-
* exception events) and for per-emit metric attributes, and sets it on the run's
|
|
186
|
-
* root span so the trace carries the project shape too. Always cheap — the span
|
|
187
|
-
* attributes are skipped when `rootSpan` is absent (telemetry off), and storing
|
|
188
|
-
* the info is a plain assignment.
|
|
189
|
-
*
|
|
190
|
-
* A `concurrentScan` only sets the span attributes: the module-level project ref
|
|
191
|
-
* has single-scan semantics, and overlapping writers would stamp events and
|
|
192
|
-
* metrics with an arbitrary sibling's project. Wide events keep full attribution
|
|
193
|
-
* (they ride the span); per-emit metrics simply omit the project shape during a
|
|
194
|
-
* concurrent batch (absent, never wrong).
|
|
195
|
-
*/
|
|
196
|
-
const recordSentryProjectContext = (projectInfo, rootSpan, options = {}) => {
|
|
197
|
-
if (options.concurrentScan !== true) setSentryProjectInfo(projectInfo);
|
|
198
|
-
if (rootSpan === void 0) return;
|
|
199
|
-
for (const [key, value] of Object.entries(toSpanAttributes(buildSentryProjectContext(projectInfo).tags))) rootSpan.attribute(key, value);
|
|
200
|
-
};
|
|
201
|
-
//#endregion
|
|
202
107
|
//#region src/cli/utils/diagnostic-grouping.ts
|
|
203
108
|
const buildRulePriorityMap = (scores) => {
|
|
204
109
|
const rulePriority = /* @__PURE__ */ new Map();
|
|
@@ -309,7 +214,7 @@ const recordScanMetrics = (input) => {
|
|
|
309
214
|
recordCount(METRIC.scanCompleted, 1, {
|
|
310
215
|
mode: input.mode,
|
|
311
216
|
lint: input.lint,
|
|
312
|
-
|
|
217
|
+
maintainability: input.deadCode,
|
|
313
218
|
parallel: input.parallel,
|
|
314
219
|
scoreOnly: input.scoreOnly,
|
|
315
220
|
didLintFail: input.didLintFail,
|
|
@@ -349,7 +254,7 @@ const recordScanMetrics = (input) => {
|
|
|
349
254
|
else if (!input.noScore && !input.didLintFail && !input.didDeadCodeFail) recordCount(METRIC.scoreUnavailable, 1, { mode: input.mode });
|
|
350
255
|
if (input.baselineDegraded) recordCount(METRIC.baselineDegraded, 1);
|
|
351
256
|
if (input.didLintFail) recordCount(METRIC.lintFailed, 1, { reasonKind: input.lintFailureReasonKind });
|
|
352
|
-
if (input.didDeadCodeFail) recordCount(METRIC.
|
|
257
|
+
if (input.didDeadCodeFail) recordCount(METRIC.maintainabilityFailed, 1);
|
|
353
258
|
for (const check of result.skippedChecks) recordCount(METRIC.scanCheckSkipped, 1, {
|
|
354
259
|
check,
|
|
355
260
|
reason: result.skippedCheckReasons?.[check] ?? null
|
|
@@ -493,10 +398,16 @@ const CACHE_DISABLED_VALUES = new Set(["1", "true"]);
|
|
|
493
398
|
const TOOLCHAIN_PACKAGE_SPECIFIERS = [
|
|
494
399
|
"oxlint/package.json",
|
|
495
400
|
"oxlint-plugin-react-doctor/package.json",
|
|
496
|
-
"deslop-js/package.json",
|
|
497
401
|
"eslint-plugin-react-hooks/package.json"
|
|
498
402
|
];
|
|
499
403
|
const bundledRequire = createRequire(import.meta.url);
|
|
404
|
+
const RULE_PLUGIN_CONTENT_FINGERPRINT = (() => {
|
|
405
|
+
try {
|
|
406
|
+
return `oxlint-plugin-react-doctor#fingerprint=${hashFileContents(bundledRequire.resolve("oxlint-plugin-react-doctor")) ?? "unreadable"}`;
|
|
407
|
+
} catch {
|
|
408
|
+
return "oxlint-plugin-react-doctor#fingerprint=unresolved";
|
|
409
|
+
}
|
|
410
|
+
})();
|
|
500
411
|
const normalizeForStableJson = (value) => {
|
|
501
412
|
if (value === null) return null;
|
|
502
413
|
if (value === void 0) return void 0;
|
|
@@ -632,12 +543,12 @@ const resolveCacheFilePath = (projectDirectory) => path.join(resolveReactDoctorC
|
|
|
632
543
|
const readPersistedCache = (cacheFilePath) => {
|
|
633
544
|
try {
|
|
634
545
|
const parsed = JSON.parse(fs.readFileSync(cacheFilePath, "utf8"));
|
|
635
|
-
if (!isRecord(parsed) || parsed.version !==
|
|
636
|
-
version:
|
|
546
|
+
if (!isRecord(parsed) || parsed.version !== 7) return {
|
|
547
|
+
version: 7,
|
|
637
548
|
entries: []
|
|
638
549
|
};
|
|
639
550
|
if (!Array.isArray(parsed.entries)) return {
|
|
640
|
-
version:
|
|
551
|
+
version: 7,
|
|
641
552
|
entries: []
|
|
642
553
|
};
|
|
643
554
|
const entries = [];
|
|
@@ -652,12 +563,12 @@ const readPersistedCache = (cacheFilePath) => {
|
|
|
652
563
|
});
|
|
653
564
|
}
|
|
654
565
|
return {
|
|
655
|
-
version:
|
|
566
|
+
version: 7,
|
|
656
567
|
entries
|
|
657
568
|
};
|
|
658
569
|
} catch {
|
|
659
570
|
return {
|
|
660
|
-
version:
|
|
571
|
+
version: 7,
|
|
661
572
|
entries: []
|
|
662
573
|
};
|
|
663
574
|
}
|
|
@@ -665,7 +576,7 @@ const readPersistedCache = (cacheFilePath) => {
|
|
|
665
576
|
/**
|
|
666
577
|
* The global cache off-switch (`REACT_DOCTOR_NO_CACHE`), which disables every
|
|
667
578
|
* cache subsystem: this whole-repo scan cache plus core's per-file lint,
|
|
668
|
-
* sidecar
|
|
579
|
+
* sidecar caches (their `Context.Reference` defaults read the
|
|
669
580
|
* same variable). Exported for the wide event's `cache.temperature`
|
|
670
581
|
* derivation, which reports `"disabled"` instead of `"cold"` when the switch
|
|
671
582
|
* is on. Granular knobs (`REACT_DOCTOR_NO_FILE_CACHE`, …) are deliberately
|
|
@@ -688,7 +599,7 @@ const fileFingerprint = (filePath) => {
|
|
|
688
599
|
return null;
|
|
689
600
|
}
|
|
690
601
|
};
|
|
691
|
-
const
|
|
602
|
+
const resolveScanResultToolchainFingerprint = (nodeBinaryPath) => {
|
|
692
603
|
const fingerprints = [];
|
|
693
604
|
if (nodeBinaryPath !== null) fingerprints.push(nodeBinaryPath === process.execPath ? `node=${process.version}` : `node=${fileFingerprint(nodeBinaryPath) ?? "unreadable"}`);
|
|
694
605
|
for (const specifier of TOOLCHAIN_PACKAGE_SPECIFIERS) try {
|
|
@@ -698,6 +609,7 @@ const resolveToolchainFingerprint = (nodeBinaryPath) => {
|
|
|
698
609
|
} catch {
|
|
699
610
|
fingerprints.push(`${specifier}=missing`);
|
|
700
611
|
}
|
|
612
|
+
fingerprints.push(RULE_PLUGIN_CONTENT_FINGERPRINT);
|
|
701
613
|
return fingerprints;
|
|
702
614
|
};
|
|
703
615
|
const buildScanResultCacheKey = (input) => {
|
|
@@ -707,14 +619,14 @@ const buildScanResultCacheKey = (input) => {
|
|
|
707
619
|
if (repositoryIdentity === null) return null;
|
|
708
620
|
if (stringifyStableJson(input.userConfig) === null) return null;
|
|
709
621
|
const cacheKeyJson = stringifyStableJson({
|
|
710
|
-
schemaVersion:
|
|
622
|
+
schemaVersion: 7,
|
|
711
623
|
projectIdentity: resolveProjectIdentity(input.projectDirectory),
|
|
712
624
|
headSha: repositoryIdentity.headSha,
|
|
713
625
|
worktreeFingerprint: repositoryIdentity.worktreeFingerprint,
|
|
714
626
|
dotenvFingerprint: resolveDotenvFingerprint(input.projectDirectory),
|
|
715
627
|
reactDoctorVersion: input.version,
|
|
716
628
|
nodeVersion: process.version,
|
|
717
|
-
toolchainFingerprint:
|
|
629
|
+
toolchainFingerprint: resolveScanResultToolchainFingerprint(input.nodeBinaryPath),
|
|
718
630
|
configFingerprint: computeConfigFingerprint(input.projectDirectory, input.version),
|
|
719
631
|
hasConfigOverride: input.hasConfigOverride,
|
|
720
632
|
configSourceDirectory: input.configSourceDirectory,
|
|
@@ -755,7 +667,7 @@ const createScanResultCache = (projectDirectory) => {
|
|
|
755
667
|
for (const entry of readPersistedCache(cacheFilePath).entries) mergedEntries.set(entry.key, entry);
|
|
756
668
|
for (const entry of entries.values()) mergedEntries.set(entry.key, entry);
|
|
757
669
|
atomicWriteJson(cacheFilePath, {
|
|
758
|
-
version:
|
|
670
|
+
version: 7,
|
|
759
671
|
entries: [...mergedEntries.values()].sort((firstEntry, secondEntry) => secondEntry.createdAtMs - firstEntry.createdAtMs).slice(0, 20)
|
|
760
672
|
});
|
|
761
673
|
};
|
|
@@ -790,8 +702,7 @@ const shouldBlockCi = (diagnostics, blockingLevel) => {
|
|
|
790
702
|
/**
|
|
791
703
|
* Lowercase, key-safe form of a rule category for the `diag.category.*`
|
|
792
704
|
* telemetry attribute namespace (categories carry spaces / capitals, e.g.
|
|
793
|
-
* "Performance" → `performance`, "Dead Code" → `dead_code`).
|
|
794
|
-
* CLI run event and the editor LSP wide event so both namespace identically.
|
|
705
|
+
* "Performance" → `performance`, "Dead Code" → `dead_code`).
|
|
795
706
|
*/
|
|
796
707
|
const toCategoryKey = (category) => category.toLowerCase().replace(/[^a-z0-9]+/g, "_");
|
|
797
708
|
//#endregion
|
|
@@ -821,12 +732,6 @@ const readEnvBoolean = (name) => {
|
|
|
821
732
|
return value.toLowerCase() === "true" || value === "1";
|
|
822
733
|
};
|
|
823
734
|
const ratioOf = (numerator, denominator) => denominator != null && denominator > 0 ? (numerator ?? 0) / denominator : null;
|
|
824
|
-
const resolveDeadCodeReuseRatio = (result) => {
|
|
825
|
-
if (result.deadCodeCacheHit === true) return 1;
|
|
826
|
-
const summaryTotal = (result.deadCodeSummaryCacheHits ?? 0) + (result.deadCodeSummaryCacheMisses ?? 0);
|
|
827
|
-
if (summaryTotal > 0) return (result.deadCodeSummaryCacheHits ?? 0) / summaryTotal;
|
|
828
|
-
return result.deadCodeCacheHit === false ? 0 : null;
|
|
829
|
-
};
|
|
830
735
|
/**
|
|
831
736
|
* One queryable cache temperature per scan, derived from the whole stack:
|
|
832
737
|
*
|
|
@@ -834,8 +739,7 @@ const resolveDeadCodeReuseRatio = (result) => {
|
|
|
834
739
|
* `wholeRepoCacheHit` flag from the CLI's cachedPayload
|
|
835
740
|
* branch; no scan work ran).
|
|
836
741
|
* - `"warm"` — any incremental reuse: per-file lint hits, sidecar
|
|
837
|
-
* replays
|
|
838
|
-
* summary-cache hits.
|
|
742
|
+
* replays.
|
|
839
743
|
* - `"disabled"` — zero reuse because the global `REACT_DOCTOR_NO_CACHE`
|
|
840
744
|
* off-switch is on. Granular per-cache opt-outs
|
|
841
745
|
* (`REACT_DOCTOR_NO_FILE_CACHE`, …) still read warm/cold,
|
|
@@ -844,7 +748,7 @@ const resolveDeadCodeReuseRatio = (result) => {
|
|
|
844
748
|
*
|
|
845
749
|
* `cache.warmth` is the headline reuse magnitude in [0, 1]: the plain mean of
|
|
846
750
|
* the subsystem reuse fractions known this run (lint hit ratio, sidecar
|
|
847
|
-
* replay ratio
|
|
751
|
+
* replay ratio), skipping subsystems that never consulted a
|
|
848
752
|
* cache; `1` on turbo, dropped when nothing consulted any cache. Deliberately
|
|
849
753
|
* unweighted — the per-subsystem dims stay the precise signal; warmth is the
|
|
850
754
|
* p50/p90-able summary. Emitted only on the success path.
|
|
@@ -857,11 +761,7 @@ const buildCacheAttributes = (input) => {
|
|
|
857
761
|
warmth: 1
|
|
858
762
|
});
|
|
859
763
|
const result = input.result;
|
|
860
|
-
const subsystemReuseRatios = [
|
|
861
|
-
ratioOf(result.lintCacheHitFileCount, result.lintCacheTotalFileCount),
|
|
862
|
-
ratioOf(result.lintSidecarReplayedFileCount, result.lintSidecarTotalFileCount),
|
|
863
|
-
resolveDeadCodeReuseRatio(result)
|
|
864
|
-
];
|
|
764
|
+
const subsystemReuseRatios = [ratioOf(result.lintCacheHitFileCount, result.lintCacheTotalFileCount), ratioOf(result.lintSidecarReplayedFileCount, result.lintSidecarTotalFileCount)];
|
|
865
765
|
let knownSubsystemCount = 0;
|
|
866
766
|
let reuseRatioSum = 0;
|
|
867
767
|
for (const reuseRatio of subsystemReuseRatios) {
|
|
@@ -1013,13 +913,7 @@ const buildOutcomeAttributes = (input) => {
|
|
|
1013
913
|
sidecarTotalFiles: result.lintSidecarTotalFileCount,
|
|
1014
914
|
sidecarReplayRatio: ratioOf(result.lintSidecarReplayedFileCount, result.lintSidecarTotalFileCount)
|
|
1015
915
|
}),
|
|
1016
|
-
...withNamespace("
|
|
1017
|
-
failed: input.didDeadCodeFail,
|
|
1018
|
-
overlapped: input.deadCodeOverlapped,
|
|
1019
|
-
cacheHit: result.deadCodeCacheHit,
|
|
1020
|
-
summaryCacheHits: result.deadCodeSummaryCacheHits,
|
|
1021
|
-
summaryCacheMisses: result.deadCodeSummaryCacheMisses
|
|
1022
|
-
}),
|
|
916
|
+
...withNamespace("maintainability", { failed: input.didDeadCodeFail }),
|
|
1023
917
|
...withNamespace("supplyChain", { overlapTimedOut: input.supplyChainOverlapTimedOut }),
|
|
1024
918
|
...withNamespace("securityScan", { failed: input.securityScanFailed }),
|
|
1025
919
|
...withNamespace("timing", {
|
|
@@ -1062,7 +956,7 @@ const buildScanAttributes = (input) => {
|
|
|
1062
956
|
workerCount: input.workerCount,
|
|
1063
957
|
maxDurationMs: input.maxDurationMs,
|
|
1064
958
|
lint: input.lint,
|
|
1065
|
-
|
|
959
|
+
maintainability: input.deadCode,
|
|
1066
960
|
supplyChain: input.supplyChain,
|
|
1067
961
|
scoreOnly: input.scoreOnly,
|
|
1068
962
|
noScore: input.noScore,
|
|
@@ -1073,6 +967,7 @@ const buildScanAttributes = (input) => {
|
|
|
1073
967
|
hasCustomConfig: input.hasCustomConfig,
|
|
1074
968
|
rulesConfigured: ruleKeys.length,
|
|
1075
969
|
rulesDisabled: ruleKeys.filter((key) => ruleOverrides[key] === "off").length,
|
|
970
|
+
projectAnalysisRuleCount: countOptInProjectRuleSelections(buildRuleSeverityControls(input.userConfig)),
|
|
1076
971
|
fileCount: input.result?.scannedFileCount,
|
|
1077
972
|
nonJsxFileCount: input.result?.analyzedFiles?.filter((filePath) => !JSX_FILE_PATTERN.test(filePath)).length,
|
|
1078
973
|
htmlFileCount: input.result?.analyzedFiles?.filter((filePath) => HTML_FILE_PATTERN.test(filePath)).length,
|
|
@@ -1083,7 +978,7 @@ const buildScanAttributes = (input) => {
|
|
|
1083
978
|
* Projects a scan into the namespaced attribute set for its root span — the
|
|
1084
979
|
* canonical per-scan "wide event". Every attribute carries a dotted namespace
|
|
1085
980
|
* that groups it by concept (`scan.*` config, `action.*` CI knobs, `outcome.*`
|
|
1086
|
-
* verdict, `diag.*` findings, `score.*`, `lint.*`, `
|
|
981
|
+
* verdict, `diag.*` findings, `score.*`, `lint.*`, `maintainability.*`, `cache.*`,
|
|
1087
982
|
* `supplyChain.*`, `timing.*`, `migration.*`, `baseline.*`) so the attributes
|
|
1088
983
|
* tree up in Sentry's attribute browser and stay filter-/group-/aggregate-able
|
|
1089
984
|
* in the Spans dataset. Pure and exported so the projection (outcome
|
|
@@ -1322,7 +1217,7 @@ const buildNoScoreMessage = (input) => {
|
|
|
1322
1217
|
reason = input.disabledMessage ?? "Score disabled by --no-score.";
|
|
1323
1218
|
break;
|
|
1324
1219
|
case "analysis-incomplete":
|
|
1325
|
-
reason = "Score not shown because lint or
|
|
1220
|
+
reason = "Score not shown because lint or maintainability analysis could not complete.";
|
|
1326
1221
|
break;
|
|
1327
1222
|
case "api-unavailable":
|
|
1328
1223
|
reason = "Score unavailable (could not reach the score API).";
|
|
@@ -1370,7 +1265,7 @@ const formatRuleSummary = (ruleKey, ruleDiagnostics) => {
|
|
|
1370
1265
|
const ruleDumpFileName = (ruleKey) => ruleKey.replace(/\//g, "--") + ".txt";
|
|
1371
1266
|
const readPreviousRuleDumpFileNames = (directory) => {
|
|
1372
1267
|
try {
|
|
1373
|
-
const previous = JSON.parse(fs$
|
|
1268
|
+
const previous = JSON.parse(fs$2.readFileSync(path$1.join(directory, "diagnostics.json"), "utf8"));
|
|
1374
1269
|
return new Set(previous.filter((d) => typeof d?.plugin === "string" && typeof d?.rule === "string").map((d) => ruleDumpFileName(`${d.plugin}/${d.rule}`)));
|
|
1375
1270
|
} catch {
|
|
1376
1271
|
return /* @__PURE__ */ new Set();
|
|
@@ -1378,10 +1273,10 @@ const readPreviousRuleDumpFileNames = (directory) => {
|
|
|
1378
1273
|
};
|
|
1379
1274
|
const writeDiagnosticsDirectory = (diagnostics, outputDirectory) => {
|
|
1380
1275
|
const directory = outputDirectory ? path$1.resolve(outputDirectory) : path$1.join(tmpdir(), `react-doctor-${randomUUID()}`);
|
|
1381
|
-
if (outputDirectory) for (const fileName of readPreviousRuleDumpFileNames(directory)) fs$
|
|
1382
|
-
fs$
|
|
1383
|
-
for (const [ruleKey, ruleDiagnostics] of buildSortedRuleGroups(diagnostics)) fs$
|
|
1384
|
-
fs$
|
|
1276
|
+
if (outputDirectory) for (const fileName of readPreviousRuleDumpFileNames(directory)) fs$2.rmSync(path$1.join(directory, fileName), { force: true });
|
|
1277
|
+
fs$2.mkdirSync(directory, { recursive: true });
|
|
1278
|
+
for (const [ruleKey, ruleDiagnostics] of buildSortedRuleGroups(diagnostics)) fs$2.writeFileSync(path$1.join(directory, ruleDumpFileName(ruleKey)), formatRuleSummary(ruleKey, ruleDiagnostics));
|
|
1279
|
+
fs$2.writeFileSync(path$1.join(directory, "diagnostics.json"), JSON.stringify(diagnostics));
|
|
1385
1280
|
return directory;
|
|
1386
1281
|
};
|
|
1387
1282
|
//#endregion
|
|
@@ -1425,6 +1320,9 @@ const formatElapsedTime = (elapsedMilliseconds) => {
|
|
|
1425
1320
|
return `${(elapsedMilliseconds / MILLISECONDS_PER_SECOND).toFixed(1)}s`;
|
|
1426
1321
|
};
|
|
1427
1322
|
//#endregion
|
|
1323
|
+
//#region src/cli/utils/format-skipped-check-label.ts
|
|
1324
|
+
const formatSkippedCheckLabel = (skippedCheck) => skippedCheck === "dead-code" ? "maintainability" : skippedCheck;
|
|
1325
|
+
//#endregion
|
|
1428
1326
|
//#region src/cli/utils/pluralize.ts
|
|
1429
1327
|
const pluralize = (count, singularNoun) => `${count} ${singularNoun}${count === 1 ? "" : "s"}`;
|
|
1430
1328
|
//#endregion
|
|
@@ -1473,7 +1371,7 @@ const printHeadlessReport = (input) => gen(function* () {
|
|
|
1473
1371
|
}
|
|
1474
1372
|
if (input.skippedChecks.length > 0) {
|
|
1475
1373
|
yield* log("");
|
|
1476
|
-
yield* warn(highlighter.warn(`Results are incomplete: ${input.skippedChecks.join(" and ")} checks failed.`));
|
|
1374
|
+
yield* warn(highlighter.warn(`Results are incomplete: ${input.skippedChecks.map(formatSkippedCheckLabel).join(" and ")} checks failed.`));
|
|
1477
1375
|
}
|
|
1478
1376
|
});
|
|
1479
1377
|
//#endregion
|
|
@@ -1673,12 +1571,39 @@ const renderAndRecordScan = async (input) => {
|
|
|
1673
1571
|
didDeadCodeFail: input.payload.didDeadCodeFail,
|
|
1674
1572
|
supplyChainOverlapTimedOut: input.payload.supplyChainOverlapTimedOut,
|
|
1675
1573
|
securityScanFailed: input.payload.securityScanFailed,
|
|
1676
|
-
deadCodeOverlapped: input.payload.deadCodeOverlapped,
|
|
1677
1574
|
suppressedRuleCounts: input.payload.suppressedRuleCounts ?? []
|
|
1678
1575
|
});
|
|
1679
1576
|
return result;
|
|
1680
1577
|
};
|
|
1681
1578
|
//#endregion
|
|
1579
|
+
//#region src/cli/utils/copy-unchanged-baseline-sources.ts
|
|
1580
|
+
const copyUnchangedBaselineSources = async (input) => {
|
|
1581
|
+
const baseMaterializedFiles = new Set(input.baseMaterializedFiles.map(toForwardSlashes));
|
|
1582
|
+
const headChangedFiles = new Set(input.headChangedFiles.map(toForwardSlashes));
|
|
1583
|
+
const untrackedFiles = new Set(input.untrackedFiles.map(toForwardSlashes));
|
|
1584
|
+
const sourceDirectory = path$1.resolve(input.directory);
|
|
1585
|
+
const targetDirectory = path$1.resolve(input.tempDirectory);
|
|
1586
|
+
return (await mapWithConcurrency(input.sourceFiles.filter((filePath) => {
|
|
1587
|
+
const normalizedPath = toForwardSlashes(filePath);
|
|
1588
|
+
return !baseMaterializedFiles.has(normalizedPath) && !headChangedFiles.has(normalizedPath) && !untrackedFiles.has(normalizedPath);
|
|
1589
|
+
}), 32, async (filePath) => {
|
|
1590
|
+
input.signal?.throwIfAborted();
|
|
1591
|
+
if (input.deadlineEpochMs !== null && Date.now() >= input.deadlineEpochMs) return false;
|
|
1592
|
+
const sourcePath = path$1.resolve(sourceDirectory, filePath);
|
|
1593
|
+
const targetPath = path$1.resolve(targetDirectory, filePath);
|
|
1594
|
+
if (!isPathInsideDirectory(sourcePath, sourceDirectory) || !isPathInsideDirectory(targetPath, targetDirectory)) return false;
|
|
1595
|
+
try {
|
|
1596
|
+
if (!(await fs$1.lstat(sourcePath)).isFile()) return false;
|
|
1597
|
+
await fs$1.mkdir(path$1.dirname(targetPath), { recursive: true });
|
|
1598
|
+
await fs$1.copyFile(sourcePath, targetPath);
|
|
1599
|
+
return true;
|
|
1600
|
+
} catch {
|
|
1601
|
+
input.signal?.throwIfAborted();
|
|
1602
|
+
return false;
|
|
1603
|
+
}
|
|
1604
|
+
})).every(Boolean);
|
|
1605
|
+
};
|
|
1606
|
+
//#endregion
|
|
1682
1607
|
//#region src/cli/utils/read-diagnostic-evidence.ts
|
|
1683
1608
|
const SOURCE_EXTENSIONS = [
|
|
1684
1609
|
".ts",
|
|
@@ -2096,7 +2021,6 @@ const resolveMergeBaseRef = async (directory, ref) => {
|
|
|
2096
2021
|
const silentConsole$1 = makeNoopConsole();
|
|
2097
2022
|
const countIncompleteLintFiles = (lintPartialFailures) => countDroppedLintFiles(lintPartialFailures) + countDeadlineSkippedFiles(lintPartialFailures);
|
|
2098
2023
|
const runBaselineComparison = async (input) => {
|
|
2099
|
-
const temporaryDirectory = mkdtempSync(path.join(tmpdir(), BASELINE_FILES_TEMP_DIR_PREFIX));
|
|
2100
2024
|
const baselineIncludePaths = filterPathsOutsideDirectories({
|
|
2101
2025
|
rootDirectory: input.directory,
|
|
2102
2026
|
relativePaths: input.options.includePaths,
|
|
@@ -2112,12 +2036,24 @@ const runBaselineComparison = async (input) => {
|
|
|
2112
2036
|
relativePaths: input.headFiles,
|
|
2113
2037
|
excludedDirectories: input.options.excludedProjectDirectories
|
|
2114
2038
|
}) : void 0;
|
|
2039
|
+
const remainingBaselineBudgetMs = input.deadlineEpochMs === null ? null : remainingDeadlineBudgetMs(input.deadlineEpochMs);
|
|
2040
|
+
if (remainingBaselineBudgetMs === 0) return null;
|
|
2041
|
+
const baselineDeadlineSignal = remainingBaselineBudgetMs === null ? void 0 : AbortSignal.timeout(remainingBaselineBudgetMs);
|
|
2042
|
+
const baselineListingSignal = baselineDeadlineSignal === void 0 ? input.oxlintRuntime.abortSignal : input.oxlintRuntime.abortSignal === void 0 ? baselineDeadlineSignal : AbortSignal.any([input.oxlintRuntime.abortSignal, baselineDeadlineSignal]);
|
|
2043
|
+
let maintainabilitySourceFiles = [];
|
|
2044
|
+
if (input.options.deadCode) try {
|
|
2045
|
+
maintainabilitySourceFiles = (await listSourceFilesCooperative(input.directory, baselineListingSignal)).filter((filePath) => JSX_DUPLICATION_SOURCE_FILE_PATTERN.test(filePath) && classifyFileContext(filePath) === "production");
|
|
2046
|
+
} catch (error) {
|
|
2047
|
+
if (baselineDeadlineSignal?.aborted) return null;
|
|
2048
|
+
throw error;
|
|
2049
|
+
}
|
|
2050
|
+
const temporaryDirectory = mkdtempSync(path.join(tmpdir(), BASELINE_FILES_TEMP_DIR_PREFIX));
|
|
2115
2051
|
const snapshot = await materializeBaselineFiles({
|
|
2116
2052
|
directory: input.directory,
|
|
2117
2053
|
ref: input.baselineRef,
|
|
2118
|
-
files: baselineIncludePaths,
|
|
2119
|
-
baseFiles: baselineBaseFiles,
|
|
2120
|
-
headFiles: baselineHeadFiles,
|
|
2054
|
+
files: input.options.deadCode ? input.options.includePaths : baselineIncludePaths,
|
|
2055
|
+
baseFiles: input.options.deadCode ? input.baseFiles : baselineBaseFiles,
|
|
2056
|
+
headFiles: input.options.deadCode ? input.headFiles : baselineHeadFiles,
|
|
2121
2057
|
tempDirectory: temporaryDirectory
|
|
2122
2058
|
}).catch((error) => {
|
|
2123
2059
|
rmSync(temporaryDirectory, {
|
|
@@ -2135,22 +2071,47 @@ const runBaselineComparison = async (input) => {
|
|
|
2135
2071
|
}
|
|
2136
2072
|
try {
|
|
2137
2073
|
if (!snapshot.isComplete) return null;
|
|
2074
|
+
if (input.options.deadCode && !await copyUnchangedBaselineSources({
|
|
2075
|
+
directory: input.directory,
|
|
2076
|
+
sourceFiles: maintainabilitySourceFiles,
|
|
2077
|
+
baseMaterializedFiles: snapshot.materializedFiles,
|
|
2078
|
+
headChangedFiles: snapshot.headFiles,
|
|
2079
|
+
untrackedFiles: snapshot.untrackedFiles,
|
|
2080
|
+
tempDirectory: snapshot.tempDirectory,
|
|
2081
|
+
deadlineEpochMs: input.deadlineEpochMs,
|
|
2082
|
+
signal: input.oxlintRuntime.abortSignal
|
|
2083
|
+
})) return null;
|
|
2084
|
+
const filteredSnapshotBaseFiles = filterPathsOutsideDirectories({
|
|
2085
|
+
rootDirectory: input.directory,
|
|
2086
|
+
relativePaths: snapshot.baseFiles,
|
|
2087
|
+
excludedDirectories: input.options.excludedProjectDirectories
|
|
2088
|
+
});
|
|
2089
|
+
const filteredSnapshotHeadFiles = filterPathsOutsideDirectories({
|
|
2090
|
+
rootDirectory: input.directory,
|
|
2091
|
+
relativePaths: snapshot.headFiles,
|
|
2092
|
+
excludedDirectories: input.options.excludedProjectDirectories
|
|
2093
|
+
});
|
|
2138
2094
|
const analyzedHeadFiles = new Set(input.headAnalyzedFiles.map(toForwardSlashes));
|
|
2139
|
-
const baseFiles = new Set(
|
|
2140
|
-
const expectedHeadFiles = new Set(
|
|
2095
|
+
const baseFiles = new Set((baselineBaseFiles ?? filteredSnapshotBaseFiles).map(toForwardSlashes));
|
|
2096
|
+
const expectedHeadFiles = new Set((baselineHeadFiles ?? filteredSnapshotHeadFiles).map(toForwardSlashes));
|
|
2141
2097
|
for (const filePath of baselineIncludePaths) {
|
|
2142
2098
|
const normalizedFilePath = toForwardSlashes(filePath);
|
|
2143
2099
|
if (!baseFiles.has(normalizedFilePath)) expectedHeadFiles.add(normalizedFilePath);
|
|
2144
2100
|
}
|
|
2145
|
-
if (filterSourceFiles([...expectedHeadFiles]).some((filePath) => !analyzedHeadFiles.has(filePath))) return null;
|
|
2101
|
+
if (input.options.lint && filterSourceFiles([...expectedHeadFiles]).some((filePath) => !analyzedHeadFiles.has(filePath))) return null;
|
|
2102
|
+
const baselineLintPaths = new Set([...baselineIncludePaths, ...filteredSnapshotBaseFiles].map(toForwardSlashes));
|
|
2103
|
+
const materializedLintPaths = snapshot.materializedFiles.filter((filePath) => baselineLintPaths.has(toForwardSlashes(filePath)));
|
|
2104
|
+
const maintainabilityFocusPaths = [...new Set([...input.options.includePaths, ...snapshot.baseFiles].map(toForwardSlashes))];
|
|
2105
|
+
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)));
|
|
2106
|
+
const baseIncludePaths = materializedLintPaths.length > 0 ? materializedLintPaths : maintainabilityFocusPaths;
|
|
2146
2107
|
const runtimeLayers = buildRuntimeLayers({
|
|
2147
2108
|
directory: snapshot.tempDirectory,
|
|
2148
2109
|
hasConfigOverride: true,
|
|
2149
2110
|
userConfig: input.userConfig,
|
|
2150
2111
|
configSourceDirectory: input.configSourceDirectory,
|
|
2151
2112
|
projectInfoOverride: input.headProjectInfo,
|
|
2152
|
-
shouldSkipLint: !input.options.lint || !input.resolvedNodeBinaryPath,
|
|
2153
|
-
shouldRunDeadCode:
|
|
2113
|
+
shouldSkipLint: !input.options.lint || !input.resolvedNodeBinaryPath || materializedLintPaths.length === 0,
|
|
2114
|
+
shouldRunDeadCode: input.options.deadCode,
|
|
2154
2115
|
shouldRunSupplyChain: input.options.supplyChain,
|
|
2155
2116
|
shouldComputeScore: false,
|
|
2156
2117
|
shouldShowProgressSpinners: false,
|
|
@@ -2159,7 +2120,8 @@ const runBaselineComparison = async (input) => {
|
|
|
2159
2120
|
});
|
|
2160
2121
|
const baseOutput = await runPromise(restoreLegacyThrow(runInspect({
|
|
2161
2122
|
directory: snapshot.tempDirectory,
|
|
2162
|
-
includePaths:
|
|
2123
|
+
includePaths: baseIncludePaths,
|
|
2124
|
+
maintainabilityFocusPaths,
|
|
2163
2125
|
customRulesOnly: input.options.customRulesOnly,
|
|
2164
2126
|
respectInlineDisables: input.options.respectInlineDisables,
|
|
2165
2127
|
warnings: input.options.warnings,
|
|
@@ -2168,7 +2130,7 @@ const runBaselineComparison = async (input) => {
|
|
|
2168
2130
|
includedTags: input.options.includedTags,
|
|
2169
2131
|
includeTagDefaults: input.options.includeTagDefaults,
|
|
2170
2132
|
nodeBinaryPath: input.resolvedNodeBinaryPath ?? void 0,
|
|
2171
|
-
runDeadCode:
|
|
2133
|
+
runDeadCode: input.options.deadCode,
|
|
2172
2134
|
isCi: input.options.isCi,
|
|
2173
2135
|
doctorVersion: VERSION,
|
|
2174
2136
|
runId: getRunId(),
|
|
@@ -2176,10 +2138,16 @@ const runBaselineComparison = async (input) => {
|
|
|
2176
2138
|
suppressScanSummary: true,
|
|
2177
2139
|
supplyChainManifestChanged: input.options.supplyChainManifestChanged,
|
|
2178
2140
|
deadlineEpochMs: input.deadlineEpochMs ?? void 0,
|
|
2179
|
-
signal: input.oxlintRuntime.abortSignal
|
|
2141
|
+
signal: input.oxlintRuntime.abortSignal,
|
|
2142
|
+
excludedProjectDirectories: baselineExcludedProjectDirectories,
|
|
2143
|
+
retainExcludedProjectDeadCodeDiagnostics: input.options.retainExcludedProjectDeadCodeDiagnostics
|
|
2180
2144
|
}, {}).pipe(provide(runtimeLayers), provideService(PerFileLintCacheEnabled, false), provideService(SidecarLintCacheEnabled, false), provideService(Console, silentConsole$1))), { signal: input.oxlintRuntime.abortSignal });
|
|
2181
|
-
if (baseOutput.didLintFail || countIncompleteLintFiles(baseOutput.lintPartialFailures) > 0) return null;
|
|
2182
|
-
const hasUnscannedUntrackedSourceFiles = filterSourceFiles(
|
|
2145
|
+
if (baseOutput.didLintFail || baseOutput.didDeadCodeFail || countIncompleteLintFiles(baseOutput.lintPartialFailures) > 0) return null;
|
|
2146
|
+
const hasUnscannedUntrackedSourceFiles = filterSourceFiles(filterPathsOutsideDirectories({
|
|
2147
|
+
rootDirectory: input.directory,
|
|
2148
|
+
relativePaths: snapshot.untrackedFiles,
|
|
2149
|
+
excludedDirectories: input.options.excludedProjectDirectories
|
|
2150
|
+
}).map(toForwardSlashes)).some((filePath) => !analyzedHeadFiles.has(filePath));
|
|
2183
2151
|
const diagnosticDelta = computeDiagnosticDelta({
|
|
2184
2152
|
headDiagnostics: input.headDiagnostics,
|
|
2185
2153
|
baseDiagnostics: baseOutput.diagnostics,
|
|
@@ -2352,6 +2320,7 @@ const runInspectWithRuntime = async (directory, options, userConfig, hasConfigOv
|
|
|
2352
2320
|
directory,
|
|
2353
2321
|
precomputedSourceFileCount: options.precomputedSourceFileCount,
|
|
2354
2322
|
includePaths: options.includePaths,
|
|
2323
|
+
changedLineRanges: options.changedLineRanges ?? void 0,
|
|
2355
2324
|
customRulesOnly: options.customRulesOnly,
|
|
2356
2325
|
respectInlineDisables: options.respectInlineDisables,
|
|
2357
2326
|
warnings: options.warnings,
|
|
@@ -2382,7 +2351,7 @@ const runInspectWithRuntime = async (directory, options, userConfig, hasConfigOv
|
|
|
2382
2351
|
else runSync(error(highlighter.error(lintFailureReason)));
|
|
2383
2352
|
let inspectDiagnostics = output.diagnostics;
|
|
2384
2353
|
let baselineDelta;
|
|
2385
|
-
if (options.baseline && isDiffMode && !didLintFail && countIncompleteLintFiles(output.lintPartialFailures) === 0) {
|
|
2354
|
+
if (options.baseline && isDiffMode && !didLintFail && !output.didDeadCodeFail && countIncompleteLintFiles(output.lintPartialFailures) === 0) {
|
|
2386
2355
|
const comparison = await runBaselineComparison({
|
|
2387
2356
|
directory,
|
|
2388
2357
|
options,
|
|
@@ -2535,13 +2504,13 @@ const resolveCursorBundledNode = () => {
|
|
|
2535
2504
|
if (!localAppData) return null;
|
|
2536
2505
|
const cursorAgentRoot = path$1.join(localAppData, "cursor-agent", "versions");
|
|
2537
2506
|
try {
|
|
2538
|
-
const versions = fs$
|
|
2507
|
+
const versions = fs$2.readdirSync(cursorAgentRoot);
|
|
2539
2508
|
if (versions.length === 0) return null;
|
|
2540
2509
|
const sortedVersions = versions.sort((a, b) => b.localeCompare(a, void 0, { numeric: true }));
|
|
2541
2510
|
for (const version of sortedVersions) try {
|
|
2542
2511
|
const bundledNodePath = path$1.join(cursorAgentRoot, version, "node.exe");
|
|
2543
2512
|
const entryScriptPath = path$1.join(cursorAgentRoot, version, "index.js");
|
|
2544
|
-
if (fs$
|
|
2513
|
+
if (fs$2.statSync(bundledNodePath).isFile() && fs$2.statSync(entryScriptPath).isFile()) return {
|
|
2545
2514
|
command: bundledNodePath,
|
|
2546
2515
|
args: [entryScriptPath]
|
|
2547
2516
|
};
|
|
@@ -2555,15 +2524,15 @@ const resolveWindowsCmdEntryScript = (command) => {
|
|
|
2555
2524
|
for (const directory of pathDirectories) {
|
|
2556
2525
|
const cmdFilePath = path$1.join(directory, `${command}.cmd`);
|
|
2557
2526
|
try {
|
|
2558
|
-
if (!fs$
|
|
2527
|
+
if (!fs$2.statSync(cmdFilePath).isFile()) continue;
|
|
2559
2528
|
} catch {
|
|
2560
2529
|
continue;
|
|
2561
2530
|
}
|
|
2562
2531
|
try {
|
|
2563
|
-
const entryScriptMatch = fs$
|
|
2532
|
+
const entryScriptMatch = fs$2.readFileSync(cmdFilePath, "utf8").match(/"%(?:~dp0|dp0%)\\([^"]+\.(?:m?js|cjs))"/);
|
|
2564
2533
|
if (!entryScriptMatch) continue;
|
|
2565
2534
|
const resolvedScriptPath = path$1.resolve(directory, entryScriptMatch[1]);
|
|
2566
|
-
if (fs$
|
|
2535
|
+
if (fs$2.statSync(resolvedScriptPath).isFile()) return resolvedScriptPath;
|
|
2567
2536
|
} catch {}
|
|
2568
2537
|
}
|
|
2569
2538
|
return null;
|
|
@@ -2889,7 +2858,7 @@ const buildCodeFrame = (input) => {
|
|
|
2889
2858
|
const absolutePath = resolveAbsolutePath(input.filePath, input.rootDirectory);
|
|
2890
2859
|
let source;
|
|
2891
2860
|
try {
|
|
2892
|
-
source = fs$
|
|
2861
|
+
source = fs$2.readFileSync(absolutePath, "utf8");
|
|
2893
2862
|
} catch {
|
|
2894
2863
|
return null;
|
|
2895
2864
|
}
|
|
@@ -2910,7 +2879,7 @@ const buildCodeFrame = (input) => {
|
|
|
2910
2879
|
const discoverWorkspacePackages = (rootDirectory) => {
|
|
2911
2880
|
const hasRootPackageJson = isFile(path$1.join(rootDirectory, "package.json"));
|
|
2912
2881
|
const packages = listWorkspacePackages(rootDirectory);
|
|
2913
|
-
if (packages.length === 0 && (!hasRootPackageJson || isMonorepoRoot(rootDirectory))) return
|
|
2882
|
+
if (packages.length === 0 && (!hasRootPackageJson || isMonorepoRoot(rootDirectory))) return discoverSupportedSubprojects(rootDirectory);
|
|
2914
2883
|
return packages;
|
|
2915
2884
|
};
|
|
2916
2885
|
const selectProjects = async (rootDirectory, projectFlag, skipPrompts, configProjects) => {
|
|
@@ -3026,7 +2995,7 @@ const shouldFailScanGate = (input) => {
|
|
|
3026
2995
|
return shouldBlockCi(filterScansForSurface(input.scans, "ciFailure"), input.blockingLevel);
|
|
3027
2996
|
};
|
|
3028
2997
|
//#endregion
|
|
3029
|
-
export { resolveMergeBaseRef as A,
|
|
2998
|
+
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 };
|
|
3030
2999
|
|
|
3031
|
-
//# sourceMappingURL=should-fail-scan-gate-
|
|
3032
|
-
//# debugId=
|
|
3000
|
+
//# sourceMappingURL=should-fail-scan-gate-BscVoE6r.js.map
|
|
3001
|
+
//# debugId=083957d2-1e94-5e65-8051-7a4bad072e84
|