react-doctor 0.9.12-dev.a2b460d → 0.9.12-dev.ac87f7d

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.
Files changed (50) hide show
  1. package/README.md +28 -1
  2. package/bin/react-doctor.js +1 -9
  3. package/dist/active-run-trace-dlIZeEvb.js +18 -0
  4. package/dist/active-scan-abort-registry-DwG6tSPy.js +28 -0
  5. package/dist/{build-section-divider-Vz879zXD.js → build-section-divider-BpENvzwG.js} +10 -25
  6. package/dist/{ci-BVYrYekU.js → ci-BhOBG3T0.js} +13 -12
  7. package/dist/cli.js +84 -32
  8. package/dist/{design-D-TYN_tr.js → design-Bq6di1tM.js} +4 -4
  9. package/dist/{devtools-DPRVRu3S.js → devtools-BiC-AP3B.js} +4 -4
  10. package/dist/{exit-gracefully-Cjv5kC_l.js → exit-gracefully-CYfF0GCJ.js} +15 -9
  11. package/dist/get-east-asian-width-Cglw13G7.js +444 -0
  12. package/dist/{report-error-1l7v6_dE.js → handle-error-CU-FMRyI.js} +18 -68
  13. package/dist/index.d.ts +53 -42
  14. package/dist/index.js +55121 -54716
  15. package/dist/{inspect-kkvDHKDE.js → inspect-B9H_IT4O.js} +22 -20
  16. package/dist/{install-ZeYvD9Rw.js → install-DoihOMNC.js} +9 -8
  17. package/dist/{install-doctor-script-D58Hr1dM.js → install-doctor-script-GGnj8tcj.js} +7 -449
  18. package/dist/{install-github-workflow-DkMicv8E.js → install-github-workflow-DWmRP55H.js} +10 -6
  19. package/dist/{install-react-doctor-CjgE52al.js → install-react-doctor-Gzbw8Ue9.js} +47 -45
  20. package/dist/{run-scan-app-Ciq22jzp.js → jsx-runtime-CMY_Q08d.js} +281 -2759
  21. package/dist/{print-branded-header-D4QYSu-h.js → print-branded-header-CqvClso3.js} +4 -4
  22. package/dist/project-analysis-worker.d.ts +1 -0
  23. package/dist/project-analysis-worker.js +51901 -0
  24. package/dist/prompt-runtime-scan-url-3SOiiFcU.js +240 -0
  25. package/dist/{read-object-file-OFRlvCAL.js → read-object-file-NSp2cOs9.js} +4 -4
  26. package/dist/{record-metric-CMe2d1Xv.js → record-metric-DVm5sVu2.js} +3259 -6046
  27. package/dist/report-error-JXHy8xn6.js +64 -0
  28. package/dist/{report-workflow-result-EagkH_v9.js → report-workflow-result-CPMR84K_.js} +5 -5
  29. package/dist/{resolve-scope-CMNrOdDf.js → resolve-scope-CWo3o4bf.js} +6 -6
  30. package/dist/{rules-CzR1qUru.js → rules-B4Dq5c0s.js} +6 -6
  31. package/dist/run-scan-app-DiVEX1yA.js +2510 -0
  32. package/dist/runtime-scan/browser-probe.iife.js +855 -0
  33. package/dist/runtime-scan-WErSEFfG.js +770 -0
  34. package/dist/sanitize-runtime-url-BB5x484j.js +76 -0
  35. package/dist/{scan-D4m5r4Rs.js → scan-58Yq0rYJ.js} +10 -22
  36. package/dist/semver-DFtnEmN-.js +3219 -0
  37. package/dist/{should-fail-scan-gate-lMTlhEhE.js → should-fail-scan-gate-BCCblBL_.js} +153 -184
  38. package/dist/{should-skip-prompts-mXKZBNCz.js → should-skip-prompts-DzzBAamD.js} +4 -4
  39. package/dist/should-use-tui-DVrQvWL6.js +25 -0
  40. package/dist/skills/improve-threejs/SKILL.md +102 -0
  41. package/dist/skills/performance/SKILL.md +113 -0
  42. package/dist/skills/react-doctor/SKILL.md +6 -0
  43. package/dist/{unref-stdin-Cxhw67r6.js → telemetry-runtime-B45Ns9aS.js} +57 -33
  44. package/dist/unref-stdin-BKT_s38a.js +12 -0
  45. package/dist/{version-CnvVY2VD.js → version-Cad39N0q.js} +4 -4
  46. package/dist/{why-BktDEhhH.js → why-DIEM7rdZ.js} +4 -4
  47. package/dist/with-run-span-B9ScJJx3.js +110 -0
  48. package/package.json +8 -9
  49. package/dist/chunk-q7NCDQ7-.js +0 -28
  50. 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]="d5dfd8c1-ba2c-54bc-b3a6-f6986ed8de06")}catch(e){}}();
3
- import { $n as orElseSucceed, An as resolveReactDoctorCacheDir, Bt as computeConfigFingerprint, Cn as mapWithConcurrency, Ct as SHARE_BASE_URL, Dt as SidecarLintCacheEnabled, E as NANOSECONDS_PER_MILLISECOND, Et as Score, Ft as buildSkippedChecks, G as isCiOrCodingAgentEnvironment, Gn as log, Gt as discoverReactSubprojects, H as ACTION_INPUT_ENVIRONMENT_VARIABLES, Hn as Console, Ht as createNodeReadFileLinesSync, It as calculateScore, Jn as decodeUnknownSync, Jt as filterSourceFiles, Kn as warn, Kt as filterDiagnosticsForSurface, Ln as summarizeDiagnostics, Lt as canonicalizeUserRuleKey, Mn as resolveScanTarget, Mt as atomicWriteJson, Nn as restoreLegacyThrow, On as resolveGithubActionsScoreMetadata, Pn as runInspect, Pt as buildRuleDocsUrl, Qn as map, R as CliInputError, St as Reporter, Tn as mergeReactDoctorConfigs, U as detectRunnerOs, Un as error, Ut as createOxlintSpawnSlots, Vn as yieldToEventLoop, Vt as computeDiagnosticDelta, W as isCiEnvironment, X as CI_URL, Xn as gen, Zn as makeSpan, _ as makeNoopConsole, _r as ParentSpan, _t as OxlintConcurrency, an as hasPublishedFixRecipe, ar as runSync, at as Files, bn as listSourceFiles, bt as Progress, c as setSentryProjectInfo, cn as highlighter, ct as Git, d as isJsonModeActive, dn as isFile, er as promise, et as Config, fn as isMonorepoRoot, fr as succeed, ft as LintPartialFailures, g as VERSION, gn as isReactDoctorError, gr as void_, gt as OXLINT_NODE_REQUIREMENT, hr as fail, ht as NodeResolver, in as groupBy, ir as runPromise, it as ENTERPRISE_CONTACT_URL, jn as resolveScanConcurrency, k as SCAN_RESULT_CACHE_FILENAME, kn as resolveLintBatchOrdering, kt as SupplyChain, l as getRunId, ln as isDirectory, lt as HTML_FILE_PATTERN, mt as MILLISECONDS_PER_SECOND, n as recordDistribution, nr as provideContext, o as buildSentryScope, or as sync, pt as Linter, qn as Diagnostic, qt as filterPathsOutsideDirectories, r as toSpanAttributes, rr as provideService, rt as DeadCode, s as buildSentryProjectContext, sn as hashFileContents, sr as try_, st as GIT_SHOW_MAX_BUFFER_BYTES, t as recordCount, tn as getDiagnosticRuleIdentity, tr as provide, tt as DIAGNOSTIC_CATEGORY_BUCKETS, ur as mergeAll, ut as JSX_FILE_PATTERN, vn as isScanComplete, vt as OxlintSpawnSlots, w as METRIC, wn as materializeSourceTree, x as BASELINE_FILES_TEMP_DIR_PREFIX, xn as listWorkspacePackages, xt as Project, yt as PerFileLintCacheEnabled, z as isNonInteractiveEnvironment, zt as collectSourceFileCountsByDirectory } from "./record-metric-CMe2d1Xv.js";
4
- import { a as setActiveRunTrace, i as recordRunTraceId, o as getTelemetryContext } from "./unref-stdin-Cxhw67r6.js";
5
- import { n as buildFooterLinkLines, r as activeScanAbortRegistry, t as buildSectionDivider } from "./build-section-divider-Vz879zXD.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-D58Hr1dM.js";
7
- import { d as detectDefaultBranch, l as prompts, r as installReactDoctorWorkflow, u as isCommandAvailable } from "./install-github-workflow-DkMicv8E.js";
8
- import { a as isValidBlockingLevel, c as openWorkflowPullRequest, l as stageWorkflowFile, o as pickBlockingLevel, u as toForwardSlashes } from "./resolve-scope-CMNrOdDf.js";
9
- import { h as detectAvailableAgents, t as reportWorkflowResult } from "./report-workflow-result-EagkH_v9.js";
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]="51d26816-54b7-5ecd-9a90-cfa15061e5df")}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-DVm5sVu2.js";
4
+ import { t as getTelemetryContext } from "./telemetry-runtime-B45Ns9aS.js";
5
+ import { n as buildFooterLinkLines, t as buildSectionDivider } from "./build-section-divider-BpENvzwG.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-GGnj8tcj.js";
8
+ import { d as detectDefaultBranch, l as prompts, r as installReactDoctorWorkflow, u as isCommandAvailable } from "./install-github-workflow-DWmRP55H.js";
9
+ import { a as isValidBlockingLevel, c as openWorkflowPullRequest, l as stageWorkflowFile, o as pickBlockingLevel, u as toForwardSlashes } from "./resolve-scope-CWo3o4bf.js";
10
+ import { n as resetSentryRunState, r as withRunSpan, t as recordSentryProjectContext } from "./with-run-span-B9ScJJx3.js";
11
+ import { h as detectAvailableAgents, t as reportWorkflowResult } from "./report-workflow-result-CPMR84K_.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$1 from "node: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 deadCodeLayer = input.shouldRunDeadCode ? DeadCode.layerNode : DeadCode.layerOf([]);
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, deadCodeLayer, progressLayer, reporterLayer, scoreLayer, supplyChainLayer);
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
- deadCode: input.deadCode,
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.deadCodeFailed, 1);
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 !== 6) return {
636
- version: 6,
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: 6,
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: 6,
566
+ version: 7,
656
567
  entries
657
568
  };
658
569
  } catch {
659
570
  return {
660
- version: 6,
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, and dead-code caches (their `Context.Reference` defaults read the
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 resolveToolchainFingerprint = (nodeBinaryPath) => {
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: 6,
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: resolveToolchainFingerprint(input.nodeBinaryPath),
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: 6,
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`). Shared by the
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, a dead-code whole-result hit, or dead-code
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, dead-code reuse), skipping subsystems that never consulted a
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("deadCode", {
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
- deadCode: input.deadCode,
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.*`, `deadCode.*`, `cache.*`,
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 dead-code analysis could not complete.";
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$1.readFileSync(path$1.join(directory, "diagnostics.json"), "utf8"));
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$1.rmSync(path$1.join(directory, fileName), { force: true });
1382
- fs$1.mkdirSync(directory, { recursive: true });
1383
- for (const [ruleKey, ruleDiagnostics] of buildSortedRuleGroups(diagnostics)) fs$1.writeFileSync(path$1.join(directory, ruleDumpFileName(ruleKey)), formatRuleSummary(ruleKey, ruleDiagnostics));
1384
- fs$1.writeFileSync(path$1.join(directory, "diagnostics.json"), JSON.stringify(diagnostics));
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(snapshot.baseFiles.map(toForwardSlashes));
2140
- const expectedHeadFiles = new Set(snapshot.headFiles.map(toForwardSlashes));
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: false,
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: snapshot.materializedFiles,
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: false,
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(snapshot.untrackedFiles.map(toForwardSlashes)).some((filePath) => !analyzedHeadFiles.has(filePath));
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$1.readdirSync(cursorAgentRoot);
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$1.statSync(bundledNodePath).isFile() && fs$1.statSync(entryScriptPath).isFile()) return {
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$1.statSync(cmdFilePath).isFile()) continue;
2527
+ if (!fs$2.statSync(cmdFilePath).isFile()) continue;
2559
2528
  } catch {
2560
2529
  continue;
2561
2530
  }
2562
2531
  try {
2563
- const entryScriptMatch = fs$1.readFileSync(cmdFilePath, "utf8").match(/"%(?:~dp0|dp0%)\\([^"]+\.(?:m?js|cjs))"/);
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$1.statSync(resolvedScriptPath).isFile()) return resolvedScriptPath;
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$1.readFileSync(absolutePath, "utf8");
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 discoverReactSubprojects(rootDirectory);
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, buildEmptyReportMessage as B, copyToClipboard as C, deduplicateProjectScans as D, collectProjectSourceFileCounts as E, printDiagnosticsDump as F, formatLearnMoreLine as G, buildRulePriorityMap as H, formatDiagnosticSite as I, hasIncompleteScoreAnalysis as L, pluralize as M, formatElapsedTime as N, createInvocationInspect as O, printFooter as P, filterDiagnosticsByCategories as R, CLI_AGENT_BINARIES as S, buildHandoffPayload as T, buildSortedRuleGroups as U, hasLintHardFailure as V, formatFixRecipeLine 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, buildNoScoreMessage as z };
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-lMTlhEhE.js.map
3032
- //# debugId=d5dfd8c1-ba2c-54bc-b3a6-f6986ed8de06
3000
+ //# sourceMappingURL=should-fail-scan-gate-BCCblBL_.js.map
3001
+ //# debugId=51d26816-54b7-5ecd-9a90-cfa15061e5df