react-doctor 0.9.3-dev.ba04952 → 0.9.3-dev.c825c59

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 (4) hide show
  1. package/dist/cli.js +228 -304
  2. package/dist/index.js +177 -227
  3. package/dist/lsp.js +181 -204
  4. package/package.json +4 -4
package/dist/cli.js CHANGED
@@ -1,5 +1,5 @@
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]="80fc688d-8e21-59d6-abcd-27ce2265a7d3")}catch(e){}}();
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]="d2fd99d7-caaf-5245-aff8-0ff8d0a163e3")}catch(e){}}();
3
3
  import { createRequire } from "node:module";
4
4
  import * as NodeChildProcess from "node:child_process";
5
5
  import { execFile, execFileSync, spawn, spawnSync } from "node:child_process";
@@ -67402,13 +67402,7 @@ const hasReactDependency = (packageJson) => {
67402
67402
  };
67403
67403
  return Object.keys(allDependencies).some((packageName) => REACT_DEPENDENCY_NAMES.has(packageName));
67404
67404
  };
67405
- const getPreactVersion = (packageJson) => {
67406
- return {
67407
- ...packageJson.peerDependencies,
67408
- ...packageJson.dependencies,
67409
- ...packageJson.devDependencies
67410
- }.preact ?? null;
67411
- };
67405
+ const getPreactVersion = (packageJson) => getDependencySpec(packageJson, "preact");
67412
67406
  const resolveCatalogBackedDependencyVersion = ({ rootDirectory, rootPackageJson, packageName, sourceDirectory = rootDirectory, sourcePackageJson = rootPackageJson, version }) => {
67413
67407
  if (version === null || !isCatalogReference(version)) return version;
67414
67408
  const catalogName = extractCatalogName(version);
@@ -68267,7 +68261,7 @@ const cachedProjectInfos = /* @__PURE__ */ new Map();
68267
68261
  */
68268
68262
  const discoverProjectWithoutPackageJson = (directory, options) => {
68269
68263
  const sourceFileCount = options.sourceFileCount ?? countSourceFiles(directory);
68270
- const hasOwnTsConfig = NFS.existsSync(Path.join(directory, "tsconfig.json"));
68264
+ const hasOwnTsConfig = isFile(Path.join(directory, "tsconfig.json"));
68271
68265
  const enclosingProjectRoot = findNearestAncestorPackageJson(directory);
68272
68266
  const enclosingProject = enclosingProjectRoot !== null ? discoverProject(enclosingProjectRoot) : null;
68273
68267
  if (enclosingProject !== null) return {
@@ -68403,7 +68397,7 @@ const discoverProject = (directory, options = {}) => {
68403
68397
  const tailwindVersion = tailwindcss.version;
68404
68398
  const zodVersion = zod.version;
68405
68399
  const projectName = packageJson.name ?? Path.basename(directory);
68406
- const hasTypeScript = NFS.existsSync(Path.join(directory, "tsconfig.json"));
68400
+ const hasTypeScript = isFile(Path.join(directory, "tsconfig.json"));
68407
68401
  const sourceFileCount = options.sourceFileCount ?? countSourceFiles(directory);
68408
68402
  const hasReactNativeWorkspace = framework === "expo" || framework === "react-native" || workspaceFacts.hasReactNativeAwarePackage;
68409
68403
  const expoVersion = hasReactNativeWorkspace ? resolveCatalogBackedDependencyVersion({
@@ -68533,134 +68527,6 @@ const discoverProject = (directory, options = {}) => {
68533
68527
  * single source file has nothing to analyze.
68534
68528
  */
68535
68529
  const isAnalyzableProject = (project) => project.reactVersion !== null || project.preactVersion !== null || project.sourceFileCount > 0;
68536
- const messageFromUnknown = (error) => error instanceof Error ? error.message : String(error);
68537
- var InvalidGlobPatternError = class extends Error {
68538
- pattern;
68539
- reason;
68540
- constructor(pattern, reason) {
68541
- super(`Invalid glob pattern ${JSON.stringify(pattern)}: ${reason}`);
68542
- this.name = "InvalidGlobPatternError";
68543
- this.pattern = pattern;
68544
- this.reason = reason;
68545
- }
68546
- };
68547
- const assertGlobPattern = (condition, pattern, reason) => {
68548
- if (!condition) throw new InvalidGlobPatternError(pattern, reason);
68549
- };
68550
- const countGlobWildcards = (pattern) => (pattern.match(/[*?]/g) ?? []).length;
68551
- const normalizeGlobPattern = (pattern) => pattern.replace(/\\/g, "/").replace(/^\//, "");
68552
- const PICOMATCH_OPTIONS = {
68553
- dot: true,
68554
- strictSlashes: false,
68555
- windows: false
68556
- };
68557
- const compileGlobPattern = (rawPattern) => {
68558
- assertGlobPattern(typeof rawPattern === "string" && rawPattern.length > 0, String(rawPattern), "pattern must be a non-empty string.");
68559
- assertGlobPattern(rawPattern.length <= MAX_GLOB_PATTERN_LENGTH_CHARS, rawPattern, `pattern length ${rawPattern.length} exceeds the maximum of ${MAX_GLOB_PATTERN_LENGTH_CHARS} characters.`);
68560
- const wildcardCount = countGlobWildcards(rawPattern);
68561
- assertGlobPattern(wildcardCount <= 24, rawPattern, `pattern uses ${wildcardCount} wildcards (\`*\` / \`?\`), exceeding the maximum of 24. This guards against catastrophic backtracking from pathological patterns; split the pattern into multiple smaller entries.`);
68562
- try {
68563
- return import_picomatch.default.makeRe(normalizeGlobPattern(rawPattern), PICOMATCH_OPTIONS);
68564
- } catch (caughtError) {
68565
- throw new InvalidGlobPatternError(rawPattern, messageFromUnknown(caughtError));
68566
- }
68567
- };
68568
- const compileGlobPatternsLenient = (patterns, onInvalid) => {
68569
- const compiled = [];
68570
- for (const pattern of patterns) try {
68571
- compiled.push(compileGlobPattern(pattern));
68572
- } catch (caughtError) {
68573
- if (!(caughtError instanceof InvalidGlobPatternError)) throw caughtError;
68574
- onInvalid(caughtError);
68575
- }
68576
- return compiled;
68577
- };
68578
- const toRelativePath = (filePath, rootDirectory) => {
68579
- const normalizedFilePath = filePath.replace(/\\/g, "/");
68580
- const normalizedRoot = rootDirectory.replace(/\\/g, "/").replace(/\/$/, "") + "/";
68581
- if (normalizedFilePath.startsWith(normalizedRoot)) return normalizedFilePath.slice(normalizedRoot.length);
68582
- return normalizedFilePath.replace(/^\.\//, "");
68583
- };
68584
- const warnConfigIssue = (message) => {
68585
- process.stderr.write(`[react-doctor] ${message}\n`);
68586
- };
68587
- const isStringArray = (value) => Array.isArray(value) && value.every((entry) => typeof entry === "string");
68588
- const collectStringList = (value) => Array.isArray(value) ? value.filter((entry) => typeof entry === "string") : [];
68589
- const validateOverrideEntry = (entry, index) => {
68590
- if (!isPlainObject(entry)) {
68591
- warnConfigIssue(`ignore.overrides[${index}] must be an object with { files, rules }; ignoring this entry.`);
68592
- return null;
68593
- }
68594
- if (!isStringArray(entry.files)) {
68595
- warnConfigIssue(`ignore.overrides[${index}].files must be an array of strings; ignoring this entry.`);
68596
- return null;
68597
- }
68598
- if (entry.rules !== void 0 && !isStringArray(entry.rules)) {
68599
- warnConfigIssue(`ignore.overrides[${index}].rules must be an array of "plugin/rule" strings or omitted; treating as missing (override would suppress every rule for the matched files).`);
68600
- return { files: entry.files };
68601
- }
68602
- return entry.rules === void 0 ? { files: entry.files } : {
68603
- files: entry.files,
68604
- rules: entry.rules
68605
- };
68606
- };
68607
- const compileIgnoreOverrides = (userConfig) => {
68608
- const overrides = userConfig?.ignore?.overrides;
68609
- if (overrides === void 0) return [];
68610
- if (!Array.isArray(overrides)) {
68611
- warnConfigIssue(`ignore.overrides must be an array of { files, rules } entries; ignoring.`);
68612
- return [];
68613
- }
68614
- return overrides.flatMap((entry, index) => {
68615
- const validated = validateOverrideEntry(entry, index);
68616
- if (!validated) return [];
68617
- const filePatterns = compileGlobPatternsLenient(collectStringList(validated.files), (error) => warnConfigIssue(`ignore.overrides[${index}]: ${error.message}`));
68618
- if (filePatterns.length === 0) return [];
68619
- return [{
68620
- filePatterns,
68621
- ruleIds: new Set(collectStringList(validated.rules))
68622
- }];
68623
- });
68624
- };
68625
- const isDiagnosticIgnoredByOverrides = (diagnostic, rootDirectory, overrides) => {
68626
- if (overrides.length === 0) return false;
68627
- const relativeFilePath = toRelativePath(diagnostic.filePath, rootDirectory);
68628
- const ruleIdentifier = `${diagnostic.plugin}/${diagnostic.rule}`;
68629
- return overrides.some((override) => override.filePatterns.some((pattern) => pattern.test(relativeFilePath)) && (override.ruleIds.size === 0 || override.ruleIds.has(ruleIdentifier)));
68630
- };
68631
- const SEVERITY_FOR_OVERRIDE = {
68632
- error: "error",
68633
- warn: "warning"
68634
- };
68635
- const restampSeverity = (diagnostic, override) => {
68636
- const targetSeverity = SEVERITY_FOR_OVERRIDE[override];
68637
- if (diagnostic.severity === targetSeverity) return diagnostic;
68638
- return {
68639
- ...diagnostic,
68640
- severity: targetSeverity
68641
- };
68642
- };
68643
- /**
68644
- * Assembles the internal `RuleSeverityControls` shape from a user
68645
- * config's top-level `rules` and `categories` fields — the
68646
- * ESLint / oxlint-shaped severity surface.
68647
- *
68648
- * Returns `undefined` when neither field is present so the common
68649
- * path (no severity config at all) stays allocation-free for
68650
- * downstream consumers.
68651
- */
68652
- const buildRuleSeverityControls = (config) => {
68653
- if (!config) return void 0;
68654
- if (config.rules === void 0 && config.categories === void 0 && config.buckets === void 0) return;
68655
- return {
68656
- ...config.rules !== void 0 ? { rules: config.rules } : {},
68657
- ...config.categories !== void 0 ? { categories: config.categories } : {},
68658
- ...config.buckets !== void 0 ? { buckets: config.buckets } : {}
68659
- };
68660
- };
68661
- const FOREIGN_INLINE_DISABLE_PATTERN = /(?:\/\/|\/\*)[ \t]*(eslint|oxlint)-disable-(next-line|line)(?![\w-])([^\r\n]*)/;
68662
- const FOREIGN_BLOCK_DISABLE_PATTERN = /\/\*[ \t]*(eslint|oxlint)-disable(?![\w-])([^*\r\n]*)/;
68663
- const FOREIGN_BLOCK_ENABLE_PATTERN = /\/\*[ \t]*(?:eslint|oxlint)-enable(?![\w-])([^*\r\n]*)/;
68664
68530
  const LEGACY_RULE_KEY_TO_NATIVE_RULE_KEY = {
68665
68531
  "effect/no-adjust-state-on-prop-change": "react-doctor/no-adjust-state-on-prop-change",
68666
68532
  "effect/no-chain-state-updates": "react-doctor/no-chain-state-updates",
@@ -68810,6 +68676,139 @@ const getEquivalentRuleKeys = (ruleKey) => {
68810
68676
  const nativeRuleKey = canonicalizeRuleKey(ruleKey);
68811
68677
  return [nativeRuleKey, ...getLegacyRuleKeysForNative(nativeRuleKey)];
68812
68678
  };
68679
+ const messageFromUnknown = (error) => error instanceof Error ? error.message : String(error);
68680
+ var InvalidGlobPatternError = class extends Error {
68681
+ pattern;
68682
+ reason;
68683
+ constructor(pattern, reason) {
68684
+ super(`Invalid glob pattern ${JSON.stringify(pattern)}: ${reason}`);
68685
+ this.name = "InvalidGlobPatternError";
68686
+ this.pattern = pattern;
68687
+ this.reason = reason;
68688
+ }
68689
+ };
68690
+ const assertGlobPattern = (condition, pattern, reason) => {
68691
+ if (!condition) throw new InvalidGlobPatternError(pattern, reason);
68692
+ };
68693
+ const countGlobWildcards = (pattern) => (pattern.match(/[*?]/g) ?? []).length;
68694
+ const normalizeGlobPattern = (pattern) => pattern.replace(/\\/g, "/").replace(/^\//, "");
68695
+ const PICOMATCH_OPTIONS = {
68696
+ dot: true,
68697
+ strictSlashes: false,
68698
+ windows: false
68699
+ };
68700
+ const compileGlobPattern = (rawPattern) => {
68701
+ assertGlobPattern(typeof rawPattern === "string" && rawPattern.length > 0, String(rawPattern), "pattern must be a non-empty string.");
68702
+ assertGlobPattern(rawPattern.length <= MAX_GLOB_PATTERN_LENGTH_CHARS, rawPattern, `pattern length ${rawPattern.length} exceeds the maximum of ${MAX_GLOB_PATTERN_LENGTH_CHARS} characters.`);
68703
+ const wildcardCount = countGlobWildcards(rawPattern);
68704
+ assertGlobPattern(wildcardCount <= 24, rawPattern, `pattern uses ${wildcardCount} wildcards (\`*\` / \`?\`), exceeding the maximum of 24. This guards against catastrophic backtracking from pathological patterns; split the pattern into multiple smaller entries.`);
68705
+ try {
68706
+ return import_picomatch.default.makeRe(normalizeGlobPattern(rawPattern), PICOMATCH_OPTIONS);
68707
+ } catch (caughtError) {
68708
+ throw new InvalidGlobPatternError(rawPattern, messageFromUnknown(caughtError));
68709
+ }
68710
+ };
68711
+ const compileGlobPatternsLenient = (patterns, onInvalid) => {
68712
+ const compiled = [];
68713
+ for (const pattern of patterns) try {
68714
+ compiled.push(compileGlobPattern(pattern));
68715
+ } catch (caughtError) {
68716
+ if (!(caughtError instanceof InvalidGlobPatternError)) throw caughtError;
68717
+ onInvalid(caughtError);
68718
+ }
68719
+ return compiled;
68720
+ };
68721
+ const toRelativePath = (filePath, rootDirectory) => {
68722
+ const normalizedFilePath = filePath.replace(/\\/g, "/");
68723
+ const normalizedRoot = rootDirectory.replace(/\\/g, "/").replace(/\/$/, "") + "/";
68724
+ if (normalizedFilePath.startsWith(normalizedRoot)) return normalizedFilePath.slice(normalizedRoot.length);
68725
+ return normalizedFilePath.replace(/^\.\//, "");
68726
+ };
68727
+ const warnConfigIssue = (message) => {
68728
+ process.stderr.write(`[react-doctor] ${message}\n`);
68729
+ };
68730
+ const isStringArray = (value) => Array.isArray(value) && value.every((entry) => typeof entry === "string");
68731
+ const collectStringList = (value) => Array.isArray(value) ? value.filter((entry) => typeof entry === "string") : [];
68732
+ const hasMatchingRuleOverride = (ruleIds, ruleIdentifier) => {
68733
+ if (ruleIds.size === 0) return true;
68734
+ for (const ruleId of ruleIds) if (isSameRuleKey(ruleId, ruleIdentifier)) return true;
68735
+ return false;
68736
+ };
68737
+ const validateOverrideEntry = (entry, index) => {
68738
+ if (!isPlainObject(entry)) {
68739
+ warnConfigIssue(`ignore.overrides[${index}] must be an object with { files, rules }; ignoring this entry.`);
68740
+ return null;
68741
+ }
68742
+ if (!isStringArray(entry.files)) {
68743
+ warnConfigIssue(`ignore.overrides[${index}].files must be an array of strings; ignoring this entry.`);
68744
+ return null;
68745
+ }
68746
+ if (entry.rules !== void 0 && !isStringArray(entry.rules)) {
68747
+ warnConfigIssue(`ignore.overrides[${index}].rules must be an array of "plugin/rule" strings or omitted; treating as missing (override would suppress every rule for the matched files).`);
68748
+ return { files: entry.files };
68749
+ }
68750
+ return entry.rules === void 0 ? { files: entry.files } : {
68751
+ files: entry.files,
68752
+ rules: entry.rules
68753
+ };
68754
+ };
68755
+ const compileIgnoreOverrides = (userConfig) => {
68756
+ const overrides = userConfig?.ignore?.overrides;
68757
+ if (overrides === void 0) return [];
68758
+ if (!Array.isArray(overrides)) {
68759
+ warnConfigIssue(`ignore.overrides must be an array of { files, rules } entries; ignoring.`);
68760
+ return [];
68761
+ }
68762
+ return overrides.flatMap((entry, index) => {
68763
+ const validated = validateOverrideEntry(entry, index);
68764
+ if (!validated) return [];
68765
+ const filePatterns = compileGlobPatternsLenient(collectStringList(validated.files), (error) => warnConfigIssue(`ignore.overrides[${index}]: ${error.message}`));
68766
+ if (filePatterns.length === 0) return [];
68767
+ return [{
68768
+ filePatterns,
68769
+ ruleIds: new Set(collectStringList(validated.rules))
68770
+ }];
68771
+ });
68772
+ };
68773
+ const isDiagnosticIgnoredByOverrides = (diagnostic, rootDirectory, overrides) => {
68774
+ if (overrides.length === 0) return false;
68775
+ const relativeFilePath = toRelativePath(diagnostic.filePath, rootDirectory);
68776
+ const ruleIdentifier = `${diagnostic.plugin}/${diagnostic.rule}`;
68777
+ return overrides.some((override) => override.filePatterns.some((pattern) => pattern.test(relativeFilePath)) && hasMatchingRuleOverride(override.ruleIds, ruleIdentifier));
68778
+ };
68779
+ const SEVERITY_FOR_OVERRIDE = {
68780
+ error: "error",
68781
+ warn: "warning"
68782
+ };
68783
+ const restampSeverity = (diagnostic, override) => {
68784
+ const targetSeverity = SEVERITY_FOR_OVERRIDE[override];
68785
+ if (diagnostic.severity === targetSeverity) return diagnostic;
68786
+ return {
68787
+ ...diagnostic,
68788
+ severity: targetSeverity
68789
+ };
68790
+ };
68791
+ /**
68792
+ * Assembles the internal `RuleSeverityControls` shape from a user
68793
+ * config's top-level `rules` and `categories` fields — the
68794
+ * ESLint / oxlint-shaped severity surface.
68795
+ *
68796
+ * Returns `undefined` when neither field is present so the common
68797
+ * path (no severity config at all) stays allocation-free for
68798
+ * downstream consumers.
68799
+ */
68800
+ const buildRuleSeverityControls = (config) => {
68801
+ if (!config) return void 0;
68802
+ if (config.rules === void 0 && config.categories === void 0 && config.buckets === void 0) return;
68803
+ return {
68804
+ ...config.rules !== void 0 ? { rules: config.rules } : {},
68805
+ ...config.categories !== void 0 ? { categories: config.categories } : {},
68806
+ ...config.buckets !== void 0 ? { buckets: config.buckets } : {}
68807
+ };
68808
+ };
68809
+ const FOREIGN_INLINE_DISABLE_PATTERN = /(?:\/\/|\/\*)[ \t]*(eslint|oxlint)-disable-(next-line|line)(?![\w-])([^\r\n]*)/;
68810
+ const FOREIGN_BLOCK_DISABLE_PATTERN = /\/\*[ \t]*(eslint|oxlint)-disable(?![\w-])([^*\r\n]*)/;
68811
+ const FOREIGN_BLOCK_ENABLE_PATTERN = /\/\*[ \t]*(?:eslint|oxlint)-enable(?![\w-])([^*\r\n]*)/;
68813
68812
  const stripDescriptionTail = (ruleList) => {
68814
68813
  const descriptionMatch = ruleList.match(/(?:^|\s)--\s/);
68815
68814
  if (!descriptionMatch || descriptionMatch.index === void 0) return ruleList;
@@ -69456,12 +69455,8 @@ const buildDiagnosticPipeline = (input) => {
69456
69455
  if (rule.tags.includes("migration-hint")) return false;
69457
69456
  return getFileContext(diagnostic.filePath) !== "production";
69458
69457
  };
69459
- const isRuleIgnored = (ruleIdentifier) => {
69460
- for (const ignored of ignoredRules) if (isSameRuleKey(ignored, ruleIdentifier)) return true;
69461
- return false;
69462
- };
69463
- const isAppOnlyRule = (ruleIdentifier) => {
69464
- for (const appOnlyRuleKey of APP_ONLY_RULE_KEYS) if (isSameRuleKey(appOnlyRuleKey, ruleIdentifier)) return true;
69458
+ const matchesRuleKey = (ruleIdentifier, candidates) => {
69459
+ for (const candidate of candidates) if (isSameRuleKey(candidate, ruleIdentifier)) return true;
69465
69460
  return false;
69466
69461
  };
69467
69462
  const isRnRawTextSuppressedByConfig = (diagnostic) => {
@@ -69503,12 +69498,13 @@ const buildDiagnosticPipeline = (input) => {
69503
69498
  if (explicitSeverityOverride === "off") return suppress(current, "config");
69504
69499
  if (explicitSeverityOverride !== void 0) current = restampSeverity(current, explicitSeverityOverride);
69505
69500
  }
69501
+ const ruleIdentifier = `${current.plugin}/${current.rule}`;
69506
69502
  if (explicitRuleOverride === void 0) {
69507
- if (isAppOnlyRule(`${current.plugin}/${current.rule}`) && isLibraryFile(current.filePath)) return null;
69503
+ if (matchesRuleKey(ruleIdentifier, APP_ONLY_RULE_KEYS) && isLibraryFile(current.filePath)) return null;
69508
69504
  }
69509
69505
  if (!showWarnings && current.severity === "warning" && explicitSeverityOverride !== "warn") return null;
69510
69506
  if (userConfig) {
69511
- if (isRuleIgnored(`${current.plugin}/${current.rule}`)) return suppress(current, "config");
69507
+ if (matchesRuleKey(ruleIdentifier, ignoredRules)) return suppress(current, "config");
69512
69508
  if (isFileIgnoredByPatterns(current.filePath, rootDirectory, ignoredFilePatterns)) return null;
69513
69509
  if (isDiagnosticIgnoredByOverrides(current, rootDirectory, compiledOverrides)) return suppress(current, "override");
69514
69510
  if (isRnRawTextSuppressedByConfig(current)) return null;
@@ -69517,7 +69513,6 @@ const buildDiagnosticPipeline = (input) => {
69517
69513
  if (respectInlineDisables && current.line > 0) {
69518
69514
  const lines = getFileLines(current.filePath);
69519
69515
  if (lines) {
69520
- const ruleIdentifier = `${current.plugin}/${current.rule}`;
69521
69516
  const evaluation = evaluateSuppression(lines, current.line - 1, ruleIdentifier);
69522
69517
  if (evaluation.isSuppressed) return suppress(current, evaluation.isForeignDirective ? "foreign-inline" : "inline");
69523
69518
  if (evaluation.nearMissHint) current = {
@@ -70013,6 +70008,7 @@ const DEFAULT_SURFACE_EXCLUDED_TAGS = {
70013
70008
  score: ["design"],
70014
70009
  ciFailure: ["design"]
70015
70010
  };
70011
+ const isRecord$1 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
70016
70012
  const VALID_RULE_SEVERITIES = [
70017
70013
  "error",
70018
70014
  "warn",
@@ -70055,7 +70051,6 @@ const SURFACE_CONTROL_FIELD_NAMES = [
70055
70051
  ];
70056
70052
  const isIncludedFileContext = (value) => VALID_INCLUDED_FILE_CONTEXTS.some((fileContext) => fileContext === value);
70057
70053
  const SEVERITY_FIELD_NAMES = ["rules", "categories"];
70058
- const isPlainObject$1 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
70059
70054
  const formatType = (value) => typeof value === "string" ? `"${value}"` : typeof value;
70060
70055
  const isRuleSeverity = (value) => typeof value === "string" && VALID_RULE_SEVERITIES.includes(value);
70061
70056
  const coerceMaybeBooleanString = (fieldName, value) => {
@@ -70083,7 +70078,7 @@ const validateStringArrayField = (fieldName, value) => {
70083
70078
  });
70084
70079
  };
70085
70080
  const validateSurfaceControls = (surface, rawControls) => {
70086
- if (!isPlainObject$1(rawControls)) {
70081
+ if (!isRecord$1(rawControls)) {
70087
70082
  warnConfigIssue(`config field "surfaces.${surface}" must be an object (got ${typeof rawControls}); ignoring this surface.`);
70088
70083
  return;
70089
70084
  }
@@ -70107,7 +70102,7 @@ const validateSurfaceControls = (surface, rawControls) => {
70107
70102
  return validatedSurfaceControls;
70108
70103
  };
70109
70104
  const validateSurfacesField = (rawSurfaces) => {
70110
- if (!isPlainObject$1(rawSurfaces)) {
70105
+ if (!isRecord$1(rawSurfaces)) {
70111
70106
  warnConfigIssue(`config field "surfaces" must be an object (got ${typeof rawSurfaces}); ignoring this field.`);
70112
70107
  return;
70113
70108
  }
@@ -70123,7 +70118,7 @@ const validateSurfacesField = (rawSurfaces) => {
70123
70118
  return validated;
70124
70119
  };
70125
70120
  const validateSeverityMap = (fieldName, rawMap, keysAreCategories = false) => {
70126
- if (!isPlainObject$1(rawMap)) {
70121
+ if (!isRecord$1(rawMap)) {
70127
70122
  warnConfigIssue(`config field "${fieldName}" must be an object (got ${typeof rawMap}); ignoring this field.`);
70128
70123
  return;
70129
70124
  }
@@ -72521,25 +72516,18 @@ const collectIgnorePatterns = (rootDirectory) => {
72521
72516
  cachedPatternsByRoot.set(rootDirectory, patterns);
72522
72517
  return patterns;
72523
72518
  };
72524
- const isRecord$1 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
72525
- const KNIP_JSON_FILENAME = "knip.json";
72526
- const readJsonFileSafe = (filePath) => {
72527
- let rawContents;
72528
- try {
72529
- rawContents = fs.readFileSync(filePath, "utf-8");
72530
- } catch {
72531
- return null;
72532
- }
72519
+ const failOpenReadJson = (filePath, fallback) => {
72533
72520
  try {
72534
- return JSON.parse(rawContents);
72521
+ return JSON.parse(NFS.readFileSync(filePath, "utf8"));
72535
72522
  } catch {
72536
- return null;
72523
+ return fallback;
72537
72524
  }
72538
72525
  };
72526
+ const KNIP_JSON_FILENAME = "knip.json";
72539
72527
  const readKnipConfig = (rootDirectory) => {
72540
- const knipJson = readJsonFileSafe(path.join(rootDirectory, KNIP_JSON_FILENAME));
72528
+ const knipJson = failOpenReadJson(path.join(rootDirectory, KNIP_JSON_FILENAME), null);
72541
72529
  if (isRecord$1(knipJson)) return knipJson;
72542
- const packageJson = readJsonFileSafe(path.join(rootDirectory, "package.json"));
72530
+ const packageJson = failOpenReadJson(path.join(rootDirectory, "package.json"), null);
72543
72531
  const packageKnipConfig = isRecord$1(packageJson) ? packageJson.knip : null;
72544
72532
  return isRecord$1(packageKnipConfig) ? packageKnipConfig : null;
72545
72533
  };
@@ -72587,13 +72575,6 @@ const atomicWriteJson = (filePath, value) => {
72587
72575
  return;
72588
72576
  }
72589
72577
  };
72590
- const failOpenReadJson = (filePath, fallback) => {
72591
- try {
72592
- return JSON.parse(NFS.readFileSync(filePath, "utf8"));
72593
- } catch {
72594
- return fallback;
72595
- }
72596
- };
72597
72578
  const hashFileContents = (filePath) => {
72598
72579
  try {
72599
72580
  return crypto.createHash("sha1").update(NFS.readFileSync(filePath)).digest("hex");
@@ -74635,11 +74616,17 @@ const collectUnpluginAutoImportGlobalScopes = ({ rootDirectory, candidateFiles }
74635
74616
  return scopes.some((scope) => scope.names.length > 0) ? scopes : [];
74636
74617
  };
74637
74618
  const validateDiagnostic$1 = decodeUnknownSync(Diagnostic);
74619
+ const decodeDiagnostic = (raw) => {
74620
+ const diagnostic = validateDiagnostic$1(raw);
74621
+ return {
74622
+ ...diagnostic,
74623
+ relatedLocations: diagnostic.relatedLocations?.map((location) => ({ ...location }))
74624
+ };
74625
+ };
74638
74626
  const decodeFileDiagnostics = (raw) => {
74639
74627
  if (!Array.isArray(raw)) return null;
74640
74628
  try {
74641
- for (const entry of raw) validateDiagnostic$1(entry);
74642
- return raw;
74629
+ return raw.map((entry) => decodeDiagnostic(entry));
74643
74630
  } catch {
74644
74631
  return null;
74645
74632
  }
@@ -77934,10 +77921,6 @@ const NO_HOOKS = {
77934
77921
  beforeLint: () => void_,
77935
77922
  afterLint: () => void_
77936
77923
  };
77937
- const filterMapNullable = (transform) => fromPredicateOption((value) => {
77938
- const result = transform(value);
77939
- return result === null ? none() : some(result);
77940
- });
77941
77924
  const fileReader = (filesService, rootDirectory) => (filePath) => {
77942
77925
  const lines = runSync(filesService.readLines({
77943
77926
  filePath,
@@ -77961,7 +77944,7 @@ const formatLintFailText = (reasonTag, nodeVersion) => {
77961
77944
  * The GitHub viewer-permission lookup is forked onto a background
77962
77945
  * fiber here and joined late (it feeds score metadata, not
77963
77946
  * diagnostics).
77964
- * 2. beforeLint hook (e.g. CLI renders the project-detection block)
77947
+ * 2. beforeLint hook (e.g. CLI records project telemetry)
77965
77948
  * 3. environment checks (reduced-motion + pnpm hardening +
77966
77949
  * expo/react-native), collected synchronously. The heavier
77967
77950
  * content-regex security scan is forked instead (like supply-chain
@@ -77974,13 +77957,11 @@ const formatLintFailText = (reasonTag, nodeVersion) => {
77974
77957
  * `SupplyChainOverlapTimeoutMs` (measured from fork) so a hung
77975
77958
  * socket can't drag out its join; on timeout it fails open to no
77976
77959
  * diagnostics — the same outcome class as a Socket outage.
77977
- * 5. Linter.run runs; DeadCode.run runs concurrently (forked child
77978
- * fiber) ONLY when the memory gate has headroom to run the 8 GB
77979
- * dead-code child alongside the oxlint workers — or when overlap is
77980
- * forced via REACT_DOCTOR_DEAD_CODE_OVERLAP. Otherwise dead-code
77981
- * runs sequentially after lint, exactly as it did pre-overlap. The
77982
- * fiber is joined (or interrupted, SIGKILLing its worker, on lint
77983
- * failure) before diagnostics are concatenated. The afterLint hook
77960
+ * 5. Linter.run runs; DeadCode.run forks only when
77961
+ * REACT_DOCTOR_DEAD_CODE_OVERLAP=on. The default "auto" and explicit
77962
+ * "off" paths stay sequential. The fiber is joined (or interrupted,
77963
+ * SIGKILLing its worker, on lint failure) before diagnostics are
77964
+ * concatenated. The afterLint hook
77984
77965
  * fires between lint and dead-code. Progress spinner labels AND the
77985
77966
  * final diagnostic / score order stay independent of execution
77986
77967
  * order, so terminal output is identical either way; supply-chain
@@ -78056,7 +78037,7 @@ const runInspect = (input, hooks = {}) => gen(function* () {
78056
78037
  const fallbackScannedFilePaths = input.suppressScanSummary ? (lintIncludePaths ?? includedPrecomputedSourceFiles ?? (yield* filesService.listSourceFiles(scanDirectory))).map((relativePath) => Path.resolve(scanDirectory, relativePath)) : [];
78057
78038
  const beforeLint = hooks.beforeLint ?? NO_HOOKS.beforeLint;
78058
78039
  const afterLint = hooks.afterLint ?? NO_HOOKS.afterLint;
78059
- yield* beforeLint(project, lintIncludePaths ?? void 0);
78040
+ yield* beforeLint(project);
78060
78041
  const isDiffMode = input.includePaths.length > 0;
78061
78042
  const showWarnings = input.warnings ?? resolvedConfig.config?.warnings ?? true;
78062
78043
  const transform = buildDiagnosticPipeline({
@@ -78066,7 +78047,10 @@ const runInspect = (input, hooks = {}) => gen(function* () {
78066
78047
  respectInlineDisables: input.respectInlineDisables,
78067
78048
  showWarnings
78068
78049
  });
78069
- const filterPerElementPipeline = (rawStream) => rawStream.pipe(filterMap(filterMapNullable(transform.apply)));
78050
+ const filterPerElementPipeline = (rawStream) => rawStream.pipe(filterMap(fromPredicateOption((diagnostic) => {
78051
+ const filteredDiagnostic = transform.apply(diagnostic);
78052
+ return filteredDiagnostic === null ? none() : some(filteredDiagnostic);
78053
+ })));
78070
78054
  const applyPerElementPipeline = (rawStream) => filterPerElementPipeline(rawStream).pipe(tap((diagnostic) => reporterService.emit(diagnostic)));
78071
78055
  const envCollected = yield* runCollect(applyPerElementPipeline(fromIterable$1(isDiffMode ? [] : [
78072
78056
  ...checkReducedMotion(scanDirectory),
@@ -78954,59 +78938,34 @@ const createOxlintSpawnSlots = (concurrency) => createWorkerSlots({
78954
78938
  slotCount: resolveScanConcurrency(concurrency),
78955
78939
  createAbortError: createLintPhaseAbortError
78956
78940
  });
78957
- const AI_TRAINING_ENV_VARS = [
78958
- ["HF_DATASETS_CACHE", "huggingface"],
78959
- ["HF_HOME", "huggingface"],
78960
- ["HUGGINGFACE_HUB_CACHE", "huggingface"],
78961
- ["SPACE_ID", "huggingface-spaces"],
78962
- ["CUDA_VISIBLE_DEVICES", "cuda"],
78963
- ["NVIDIA_VISIBLE_DEVICES", "nvidia"],
78964
- ["ROCR_VISIBLE_DEVICES", "rocm"],
78965
- ["TPU_NAME", "google-tpu"],
78941
+ const AI_TRAINING_BY_ENVIRONMENT_VARIABLE = [
78966
78942
  ["WANDB_RUN_ID", "wandb"],
78967
78943
  ["WANDB_SWEEP_ID", "wandb"],
78968
78944
  ["MLFLOW_RUN_ID", "mlflow"],
78969
- ["MLFLOW_TRACKING_URI", "mlflow"],
78970
78945
  ["COMET_EXPERIMENT_KEY", "comet"],
78971
78946
  ["NEPTUNE_RUN_ID", "neptune"],
78972
78947
  ["CLEARML_TASK_ID", "clearml"],
78973
78948
  ["DVC_STAGE", "dvc"],
78974
78949
  ["RAY_WORKER_PROCESS", "ray"],
78975
- ["RAY_ADDRESS", "ray"],
78976
- ["MUJOCO_GL", "mujoco"],
78977
- ["MUJOCO_PATH", "mujoco"],
78978
- ["GYM_DISABLE_ENV_CHECKER", "gymnasium"],
78979
78950
  ["SM_TRAINING_ENV", "sagemaker"],
78980
78951
  ["TRAINING_JOB_ARN", "sagemaker"],
78981
- ["SAGEMAKER_BASE_DIR", "sagemaker"],
78982
78952
  ["AZUREML_RUN_ID", "azure-ml"],
78983
- ["AZURE_ML_MODEL_DIR", "azure-ml"],
78984
- ["CLOUD_ML_PROJECT_ID", "vertex-ai"],
78985
- ["VERTEX_AI_LOG_LEVEL", "vertex-ai"],
78986
- ["DET_MASTER", "determined-ai"],
78987
- ["LIGHTNING_USER_ID", "lightning-ai"],
78988
78953
  ["FLYTE_INTERNAL_EXECUTION_ID", "flyte"],
78989
- ["ARGO_WORKFLOW_NAME", "argo-workflows"],
78990
78954
  ["KFP_POD_NAME", "kubeflow-pipelines"],
78991
78955
  ["KAGGLE_KERNEL_RUN_TYPE", "kaggle"],
78992
78956
  ["COLAB_BACKEND_VERSION", "google-colab"],
78993
- ["DATABRICKS_RUNTIME_VERSION", "databricks"],
78994
78957
  ["DAYTONA_WS_ID", "daytona"],
78995
- ["DAYTONA_WS_NAME", "daytona"],
78996
78958
  ["E2B_SANDBOX_ID", "e2b"],
78997
78959
  ["MODAL_FUNCTION_ID", "modal"],
78998
78960
  ["MODAL_TASK_ID", "modal"],
78999
78961
  ["RUNPOD_POD_ID", "runpod"],
79000
- ["REPLICATE_USERNAME", "replicate"],
79001
78962
  ["VAST_CONTAINERLABEL", "vast-ai"],
79002
- ["HARBOR_URL", "harbor"],
79003
- ["HARBOR_HOSTNAME", "harbor"],
79004
78963
  ["SWE_BENCH_TASK", "swe-bench"],
79005
78964
  ["SWEBENCH_TASK", "swe-bench"],
79006
78965
  ["SWE_AGENT_MODEL", "swe-agent"]
79007
78966
  ];
79008
- const detectAiTrainingEnvironment = () => {
79009
- for (const [envVar, label] of AI_TRAINING_ENV_VARS) if (process.env[envVar]) return label;
78967
+ const detectAiTrainingEnvironment = (environment = process.env) => {
78968
+ for (const [environmentVariable, label] of AI_TRAINING_BY_ENVIRONMENT_VARIABLE) if (environment[environmentVariable]?.trim()) return label;
79010
78969
  return null;
79011
78970
  };
79012
78971
  const groupBy = (items, keyFn) => {
@@ -79389,7 +79348,7 @@ const makeNoopConsole = () => ({
79389
79348
  });
79390
79349
  //#endregion
79391
79350
  //#region src/cli/utils/version.ts
79392
- const VERSION = "0.9.3-dev.ba04952";
79351
+ const VERSION = "0.9.3-dev.c825c59";
79393
79352
  //#endregion
79394
79353
  //#region src/cli/utils/json-mode.ts
79395
79354
  let context = null;
@@ -79626,6 +79585,9 @@ const buildSentryScope = (runContext = buildRunContext()) => {
79626
79585
  };
79627
79586
  };
79628
79587
  //#endregion
79588
+ //#region src/cli/utils/is-env-flag-enabled.ts
79589
+ const isEnvFlagEnabled = (value) => value === "1" || value?.toLowerCase() === "true";
79590
+ //#endregion
79629
79591
  //#region src/cli/utils/anonymize-text.ts
79630
79592
  /**
79631
79593
  * Free-text fields can carry both a home-directory path (the OS username) and a
@@ -79747,13 +79709,13 @@ const isDevVersion = (version) => version === "0.0.0" || version.includes("-");
79747
79709
  * uploads source-map artifacts under, so stack frames symbolicate. Honors the
79748
79710
  * standard `SENTRY_RELEASE` override.
79749
79711
  */
79750
- const resolveSentryRelease = () => process.env.SENTRY_RELEASE || `react-doctor@0.9.3-dev.ba04952`;
79712
+ const resolveSentryRelease = () => process.env.SENTRY_RELEASE || `react-doctor@0.9.3-dev.c825c59`;
79751
79713
  /**
79752
79714
  * Deployment environment shown in Sentry's environment filter. Defaults to
79753
79715
  * `production` for tagged releases and `development` for dev/unbuilt versions,
79754
79716
  * overridable via the standard `SENTRY_ENVIRONMENT` env var.
79755
79717
  */
79756
- const resolveSentryEnvironment = () => process.env.SENTRY_ENVIRONMENT || (isDevVersion("0.9.3-dev.ba04952") ? "development" : "production");
79718
+ const resolveSentryEnvironment = () => process.env.SENTRY_ENVIRONMENT || (isDevVersion("0.9.3-dev.c825c59") ? "development" : "production");
79757
79719
  /**
79758
79720
  * Performance-tracing sample rate in `[0, 1]`. Reads `SENTRY_TRACES_SAMPLE_RATE`
79759
79721
  * (set to `0` to disable tracing) and falls back to
@@ -79776,7 +79738,6 @@ const shouldEnableSentry = () => {
79776
79738
  if (process.env.VITEST || process.env.NODE_ENV === "test") return false;
79777
79739
  return true;
79778
79740
  };
79779
- const isEnvFlagEnabled = (value) => value === "1" || value?.toLowerCase() === "true";
79780
79741
  /**
79781
79742
  * Whether performance traces will actually be recorded — Sentry is live and the
79782
79743
  * resolved sample rate is above zero. Used to gate the per-run root span and
@@ -83392,27 +83353,21 @@ const prompts = (questions, options = {}) => {
83392
83353
  //#endregion
83393
83354
  //#region src/cli/utils/to-span-attributes.ts
83394
83355
  /**
83395
- * Converts a Sentry tag map (which permits `null` to denote an absent signal)
83396
- * into Sentry/OTel span attributes, which only accept primitives. `null` values
83397
- * are dropped rather than coerced, so an absent signal doesn't become a
83398
- * misleading `"null"` attribute.
83356
+ * Converts a Sentry tag map (which permits `null`/`undefined` to denote an
83357
+ * absent signal) into Sentry/OTel span attributes, which only accept primitives.
83358
+ * Missing values are dropped rather than coerced, so an absent signal doesn't
83359
+ * become a misleading `"null"` or `"undefined"` attribute.
83399
83360
  */
83400
83361
  const toSpanAttributes = (tags) => {
83401
83362
  const attributes = {};
83402
- for (const [key, value] of Object.entries(tags)) if (value !== null) attributes[key] = value;
83363
+ for (const [key, value] of Object.entries(tags)) if (value != null) attributes[key] = value;
83403
83364
  return attributes;
83404
83365
  };
83405
83366
  //#endregion
83406
83367
  //#region src/cli/utils/record-metric.ts
83407
- const cleanAttributes = (attributes) => {
83408
- const cleaned = {};
83409
- if (!attributes) return cleaned;
83410
- for (const [key, value] of Object.entries(attributes)) if (value !== null && value !== void 0) cleaned[key] = value;
83411
- return cleaned;
83412
- };
83413
83368
  const withRunAttributes = (attributes) => ({
83414
83369
  ...toSpanAttributes(buildSentryScope().tags),
83415
- ...cleanAttributes(attributes)
83370
+ ...toSpanAttributes(attributes ?? {})
83416
83371
  });
83417
83372
  /**
83418
83373
  * Emits a Sentry counter. A guarded, swallow-on-throw no-op unless the CLI's
@@ -92185,8 +92140,8 @@ const toCategoryKey = (category) => category.toLowerCase().replace(/[^a-z0-9]+/g
92185
92140
  * browser and stay group-/filter-/aggregate-able in the Spans dataset.
92186
92141
  *
92187
92142
  * Value types are preserved verbatim (numbers stay numbers so Sentry infers a
92188
- * numeric attribute and `p75(...)`/`avg(...)` work; `null` is kept so
92189
- * `toSpanAttributes` can drop absent signals rather than coerce them).
92143
+ * numeric attribute and `p75(...)`/`avg(...)` work; `null`/`undefined` are kept
92144
+ * so `toSpanAttributes` can drop absent signals rather than coerce them).
92190
92145
  */
92191
92146
  const withNamespace = (namespace, attributes) => {
92192
92147
  const namespaced = {};
@@ -92252,7 +92207,7 @@ const buildCacheAttributes = (input) => {
92252
92207
  const warmth = knownSubsystemCount > 0 ? reuseRatioSum / knownSubsystemCount : null;
92253
92208
  const temperature = warmth !== null && warmth > 0 ? "warm" : isCacheGloballyDisabled() ? "disabled" : "cold";
92254
92209
  return withNamespace("cache", {
92255
- wholeRepoHit: input.wholeRepoCacheHit ?? null,
92210
+ wholeRepoHit: input.wholeRepoCacheHit,
92256
92211
  temperature,
92257
92212
  warmth
92258
92213
  });
@@ -92381,30 +92336,30 @@ const buildOutcomeAttributes = (input) => {
92381
92336
  }) : null
92382
92337
  }),
92383
92338
  ...withNamespace("lint", {
92384
- failed: input.didLintFail ?? null,
92385
- failureReasonKind: input.lintFailureReasonKind ?? null,
92386
- partialFailureCount: input.lintPartialFailureCount ?? null,
92387
- droppedFileCount: input.lintDroppedFileCount ?? null,
92388
- deadlineSkippedFileCount: input.lintDeadlineSkippedFileCount ?? null,
92389
- cacheHitFiles: result.lintCacheHitFileCount ?? null,
92390
- cacheTotalFiles: result.lintCacheTotalFileCount ?? null,
92339
+ failed: input.didLintFail,
92340
+ failureReasonKind: input.lintFailureReasonKind,
92341
+ partialFailureCount: input.lintPartialFailureCount,
92342
+ droppedFileCount: input.lintDroppedFileCount,
92343
+ deadlineSkippedFileCount: input.lintDeadlineSkippedFileCount,
92344
+ cacheHitFiles: result.lintCacheHitFileCount,
92345
+ cacheTotalFiles: result.lintCacheTotalFileCount,
92391
92346
  cacheHitRatio: ratioOf(result.lintCacheHitFileCount, result.lintCacheTotalFileCount),
92392
- sidecarReplayedFiles: result.lintSidecarReplayedFileCount ?? null,
92393
- sidecarTotalFiles: result.lintSidecarTotalFileCount ?? null,
92347
+ sidecarReplayedFiles: result.lintSidecarReplayedFileCount,
92348
+ sidecarTotalFiles: result.lintSidecarTotalFileCount,
92394
92349
  sidecarReplayRatio: ratioOf(result.lintSidecarReplayedFileCount, result.lintSidecarTotalFileCount)
92395
92350
  }),
92396
92351
  ...withNamespace("deadCode", {
92397
- failed: input.didDeadCodeFail ?? null,
92398
- overlapped: input.deadCodeOverlapped ?? null,
92399
- cacheHit: result.deadCodeCacheHit ?? null,
92400
- summaryCacheHits: result.deadCodeSummaryCacheHits ?? null,
92401
- summaryCacheMisses: result.deadCodeSummaryCacheMisses ?? null
92352
+ failed: input.didDeadCodeFail,
92353
+ overlapped: input.deadCodeOverlapped,
92354
+ cacheHit: result.deadCodeCacheHit,
92355
+ summaryCacheHits: result.deadCodeSummaryCacheHits,
92356
+ summaryCacheMisses: result.deadCodeSummaryCacheMisses
92402
92357
  }),
92403
- ...withNamespace("supplyChain", { overlapTimedOut: input.supplyChainOverlapTimedOut ?? null }),
92404
- ...withNamespace("securityScan", { failed: input.securityScanFailed ?? null }),
92358
+ ...withNamespace("supplyChain", { overlapTimedOut: input.supplyChainOverlapTimedOut }),
92359
+ ...withNamespace("securityScan", { failed: input.securityScanFailed }),
92405
92360
  ...withNamespace("timing", {
92406
92361
  elapsedMs: result.elapsedMilliseconds,
92407
- scanMs: result.scanElapsedMilliseconds ?? null
92362
+ scanMs: result.scanElapsedMilliseconds
92408
92363
  }),
92409
92364
  ...withNamespace("migration", {
92410
92365
  largestRuleBucketFiles: largestRuleBucket ? largestRuleBucket.fileCount : null,
@@ -92416,7 +92371,7 @@ const buildOutcomeAttributes = (input) => {
92416
92371
  new: summary.totalDiagnosticCount,
92417
92372
  fixed: result.baselineDelta.fixedCount,
92418
92373
  baseTotal: result.baselineDelta.baseTotalCount,
92419
- crossFileMatches: result.baselineDelta.crossFileMatchCount ?? null,
92374
+ crossFileMatches: result.baselineDelta.crossFileMatchCount,
92420
92375
  degraded: false
92421
92376
  }));
92422
92377
  else if (input.gateExempt) attributes["baseline.degraded"] = true;
@@ -92425,7 +92380,7 @@ const buildOutcomeAttributes = (input) => {
92425
92380
  const buildActionAttributes = () => {
92426
92381
  const { githubActorAssociation } = resolveGithubActionsScoreMetadata();
92427
92382
  return withNamespace("action", {
92428
- actorAssociation: githubActorAssociation ?? null,
92383
+ actorAssociation: githubActorAssociation,
92429
92384
  runnerOs: detectRunnerOs(),
92430
92385
  comment: readEnvBoolean(ACTION_INPUT_ENVIRONMENT_VARIABLES.comment),
92431
92386
  reviewComments: readEnvBoolean(ACTION_INPUT_ENVIRONMENT_VARIABLES.reviewComments),
@@ -92439,7 +92394,7 @@ const buildScanAttributes = (input) => {
92439
92394
  mode: input.mode,
92440
92395
  scope: input.scope,
92441
92396
  parallel: input.parallel,
92442
- workerCount: input.workerCount ?? null,
92397
+ workerCount: input.workerCount,
92443
92398
  maxDurationMs: input.maxDurationMs,
92444
92399
  lint: input.lint,
92445
92400
  deadCode: input.deadCode,
@@ -92453,10 +92408,10 @@ const buildScanAttributes = (input) => {
92453
92408
  hasCustomConfig: input.hasCustomConfig,
92454
92409
  rulesConfigured: ruleKeys.length,
92455
92410
  rulesDisabled: ruleKeys.filter((key) => ruleOverrides[key] === "off").length,
92456
- fileCount: input.result?.scannedFileCount ?? null,
92457
- nonJsxFileCount: input.result?.analyzedFiles?.filter((filePath) => !JSX_FILE_PATTERN.test(filePath)).length ?? null,
92458
- htmlFileCount: input.result?.analyzedFiles?.filter((filePath) => HTML_FILE_PATTERN.test(filePath)).length ?? null,
92459
- multilineDiagnosticCount: input.result?.diagnostics.filter((diagnostic) => (diagnostic.endLine ?? diagnostic.line) > diagnostic.line).length ?? null
92411
+ fileCount: input.result?.scannedFileCount,
92412
+ nonJsxFileCount: input.result?.analyzedFiles?.filter((filePath) => !JSX_FILE_PATTERN.test(filePath)).length,
92413
+ htmlFileCount: input.result?.analyzedFiles?.filter((filePath) => HTML_FILE_PATTERN.test(filePath)).length,
92414
+ multilineDiagnosticCount: input.result?.diagnostics.filter((diagnostic) => (diagnostic.endLine ?? diagnostic.line) > diagnostic.line).length
92460
92415
  });
92461
92416
  };
92462
92417
  /**
@@ -92468,11 +92423,11 @@ const buildScanAttributes = (input) => {
92468
92423
  * tree up in Sentry's attribute browser and stay filter-/group-/aggregate-able
92469
92424
  * in the Spans dataset. Pure and exported so the projection (outcome
92470
92425
  * precedence, rule/category rollups, CI knobs, config shape) is unit-testable
92471
- * without a live Sentry client. `null` values are dropped so absent signals
92472
- * never become misleading `"null"` attributes. The run + project base context
92473
- * (version, command, ci/provider, framework, …) is already on the span from
92474
- * `withSentryRunSpan` / `recordSentryProjectContext`, so this adds only what
92475
- * those don't carry.
92426
+ * without a live Sentry client. `null`/`undefined` values are dropped so absent
92427
+ * signals never become misleading `"null"`/`"undefined"` attributes. The run +
92428
+ * project base context (version, command, ci/provider, framework, …) is already
92429
+ * on the span from `withSentryRunSpan` / `recordSentryProjectContext`, so this
92430
+ * adds only what those don't carry.
92476
92431
  */
92477
92432
  const buildRunEventAttributes = (input) => toSpanAttributes({
92478
92433
  ...buildScanAttributes(input),
@@ -93721,9 +93676,6 @@ const markOnboardingComplete = (options = {}) => {
93721
93676
  if (isGatePending(ONBOARDING_GATE, {}, options)) recordGate(ONBOARDING_GATE, {}, options);
93722
93677
  };
93723
93678
  //#endregion
93724
- //#region src/cli/utils/render-project-detection.ts
93725
- const printProjectDetection = (_input) => void_;
93726
- //#endregion
93727
93679
  //#region src/cli/utils/colorize-by-score.ts
93728
93680
  const colorizeByScore = (text, score) => {
93729
93681
  if (score >= 75) return highlighter.success(text);
@@ -94091,9 +94043,6 @@ const resolveCliCategories = (categoryFlag) => {
94091
94043
  //#endregion
94092
94044
  //#region src/inspect.ts
94093
94045
  const silentConsole = makeNoopConsole();
94094
- const runConsole = (effect) => {
94095
- runSync(effect);
94096
- };
94097
94046
  const recordOnboardingCompletion = (options) => {
94098
94047
  const forceOnboarding = isOnboardingForced();
94099
94048
  if (shouldRecordOnboarding({
@@ -94188,13 +94137,14 @@ const inspectWithOxlintRuntime = async (directory, inputOptions, oxlintRuntime)
94188
94137
  const deadlineEpochMs = inputOptions.deadlineEpochMs ?? (inputOptions.maxDurationMs != null ? Date.now() + inputOptions.maxDurationMs : null);
94189
94138
  const isConcurrentScan = inputOptions.concurrentScan === true;
94190
94139
  if (!isConcurrentScan) resetSentryRunState();
94191
- const hasConfigOverride = inputOptions.configOverride !== void 0;
94140
+ const configOverride = inputOptions.configOverride;
94141
+ const hasConfigOverride = configOverride !== void 0;
94192
94142
  let scanDirectory;
94193
94143
  let userConfig;
94194
94144
  let configSourceDirectory;
94195
94145
  if (hasConfigOverride) {
94196
94146
  scanDirectory = directory;
94197
- userConfig = inputOptions.configOverride ?? null;
94147
+ userConfig = configOverride;
94198
94148
  configSourceDirectory = inputOptions.configSourceDirectory ?? null;
94199
94149
  } else {
94200
94150
  const scanTarget = await resolveScanTarget(directory);
@@ -94376,16 +94326,10 @@ const runInspectWithRuntime = async (directory, options, userConfig, hasConfigOv
94376
94326
  invocationState: oxlintRuntime.scanResultCacheInvocationState
94377
94327
  });
94378
94328
  const scanResultCache = cacheKey === null ? null : createScanResultCache(directory);
94379
- const cachedPayload = cacheKey === null ? null : scanResultCache?.lookup(cacheKey) ?? null;
94329
+ const cachedPayload = cacheKey === null || scanResultCache === null ? null : scanResultCache.lookup(cacheKey);
94380
94330
  if (cachedPayload) {
94381
94331
  recordSentryProjectContext(cachedPayload.project, rootSentrySpan, { concurrentScan: options.concurrentScan });
94382
94332
  recordCount(METRIC.projectDetected, 1);
94383
- await renderCachedProjectDetection({
94384
- payload: cachedPayload,
94385
- options,
94386
- userConfig,
94387
- isDiffMode
94388
- });
94389
94333
  const baselineDegraded = Boolean(options.baseline) && isDiffMode && cachedPayload.baselineDelta === void 0;
94390
94334
  const result = await renderAndRecordScan({
94391
94335
  payload: cachedPayload,
@@ -94436,29 +94380,19 @@ const runInspectWithRuntime = async (directory, options, userConfig, hasConfigOv
94436
94380
  resolveLocalGithubViewerPermission: !options.noScore,
94437
94381
  suppressScanSummary: options.suppressRendering,
94438
94382
  supplyChainManifestChanged: options.supplyChainManifestChanged,
94439
- concurrentScan: options.concurrentScan,
94440
94383
  deadlineEpochMs: deadlineEpochMs ?? void 0,
94441
94384
  signal: oxlintRuntime.abortSignal,
94442
94385
  excludedProjectDirectories: options.excludedProjectDirectories,
94443
94386
  retainExcludedProjectDeadCodeDiagnostics: options.retainExcludedProjectDeadCodeDiagnostics
94444
- }, { beforeLint: (projectInfo, lintIncludePaths) => gen(function* () {
94387
+ }, { beforeLint: (projectInfo) => sync(() => {
94445
94388
  recordSentryProjectContext(projectInfo, rootSentrySpan, { concurrentScan: options.concurrentScan });
94446
94389
  recordCount(METRIC.projectDetected, 1);
94447
- if (options.scoreOnly || options.suppressRendering) return;
94448
- const lintSourceFileCount = lintIncludePaths?.length ?? projectInfo.sourceFileCount;
94449
- yield* printProjectDetection({
94450
- projectInfo,
94451
- userConfig,
94452
- isDiffMode,
94453
- includePaths: options.includePaths,
94454
- lintSourceFileCount
94455
- });
94456
94390
  }) });
94457
94391
  const output = await runPromise(restoreLegacyThrow(applyObservability(options.silent ? program.pipe(provide(layers), provideService$2(Console, silentConsole)) : program.pipe(provide(layers)), rootSentrySpan)), { signal: oxlintRuntime.abortSignal });
94458
94392
  const didLintFail = lintBindingMissing || output.didLintFail;
94459
94393
  const lintFailureReason = lintBindingMissing ? `oxlint native binding not found for Node ${process.version}; expected one matching ${OXLINT_NODE_REQUIREMENT}` : output.lintFailureReason;
94460
- if (!options.scoreOnly && !options.uiLayers && !lintBindingMissing && output.didLintFail && lintFailureReason !== null) if (output.lintFailureReasonKind === "native-binding-missing") runConsole(log(highlighter.gray(` Upgrade to Node ${OXLINT_NODE_REQUIREMENT} or run: npx -p oxlint@latest react-doctor@latest`)));
94461
- else runConsole(error$1(highlighter.error(lintFailureReason)));
94394
+ if (!options.scoreOnly && !options.uiLayers && !lintBindingMissing && output.didLintFail && lintFailureReason !== null) if (output.lintFailureReasonKind === "native-binding-missing") runSync(log(highlighter.gray(` Upgrade to Node ${OXLINT_NODE_REQUIREMENT} or run: npx -p oxlint@latest react-doctor@latest`)));
94395
+ else runSync(error$1(highlighter.error(lintFailureReason)));
94462
94396
  let inspectDiagnostics = output.diagnostics;
94463
94397
  let baselineDelta;
94464
94398
  if (options.baseline && isDiffMode && !didLintFail && countIncompleteLintFiles(output.lintPartialFailures) === 0) {
@@ -94534,16 +94468,6 @@ const runInspectWithRuntime = async (directory, options, userConfig, hasConfigOv
94534
94468
  return result;
94535
94469
  };
94536
94470
  const runMaybeSilent = (effect, silent) => silent ? effect.pipe(provideService$2(Console, silentConsole)) : effect;
94537
- const renderCachedProjectDetection = async (input) => {
94538
- if (input.options.scoreOnly || input.options.suppressRendering) return;
94539
- await runPromise(runMaybeSilent(printProjectDetection({
94540
- projectInfo: input.payload.project,
94541
- userConfig: input.userConfig,
94542
- isDiffMode: input.isDiffMode,
94543
- includePaths: input.options.includePaths,
94544
- lintSourceFileCount: input.payload.scannedFileCount
94545
- }), input.options.silent));
94546
- };
94547
94471
  const renderAndRecordScan = async (input) => {
94548
94472
  const result = await runPromise(runMaybeSilent(finalizeAndRender({
94549
94473
  options: input.options,
@@ -96183,7 +96107,7 @@ const runInstallReactDoctor = async (options = {}) => {
96183
96107
  const shouldInstallGitHook = gitHookPath != null && (Boolean(options.yes) || !didSkipOptionalSetup && selectedSetupActions.includes(SETUP_OPTION_GIT_HOOK));
96184
96108
  const shouldInstallAgentHooks = Boolean(options.agentHooks) || !didSkipOptionalSetup && selectedSetupActions.includes(SETUP_OPTION_AGENT_HOOKS);
96185
96109
  if (!options.dryRun) {
96186
- if (shouldInstallGitHook && gitHookTarget !== null && gitHookTarget !== void 0) installReactDoctorGitHookStep(gitHookTarget);
96110
+ if (shouldInstallGitHook && gitHookTarget != null) installReactDoctorGitHookStep(gitHookTarget);
96187
96111
  if (shouldInstallAgentHooks) installReactDoctorAgentHooksStep(projectRoot, selectedAgents);
96188
96112
  }
96189
96113
  if (options.dryRun) {
@@ -97034,7 +96958,7 @@ const resolveExplainTargetDirectory = async (filePath, context) => {
97034
96958
  if (context.projectFlag) {
97035
96959
  const matchedDirectories = await selectProjects(context.resolvedDirectory, context.projectFlag, true);
97036
96960
  if (matchedDirectories.length === 0) return context.resolvedDirectory;
97037
- if (matchedDirectories.length > 1) throw new Error(`--explain takes a single project; --project resolved to ${matchedDirectories.length} projects.`);
96961
+ if (matchedDirectories.length > 1) throw new Error(`why takes a single project; --project resolved to ${matchedDirectories.length} projects.`);
97038
96962
  return matchedDirectories[0];
97039
96963
  }
97040
96964
  return findOwningProjectDirectory(context.resolvedDirectory, filePath);
@@ -99058,4 +98982,4 @@ Promise.resolve().then(() => assertNoRemovedFlags(process.argv)).then(() => prog
99058
98982
  export { TUI_REPORT_DETAIL_WIDTH_FRACTION as $, shouldRecordOnboarding as A, buildSortedRuleGroups as B, createInvocationInspect as C, markOnboardingComplete as D, doctorFace as E, buildEmptyReportMessage as F, recordCount as G, formatLearnMoreLine as H, hasIncompleteScoreAnalysis as I, findNearestPackageDirectory as J, recordDistribution as K, buildNoScoreMessage as L, computeProjectedScore as M, formatElapsedTime as N, canAnimateOnboarding as O, buildCodeFrame as P, TUI_REPORT_COMPACT_MAX_ROWS as Q, hasLintHardFailure as R, deduplicateProjectScans as S, easeOutCubic as T, resolveBlockingLevel as U, formatFixRecipeLine as V, isReactDoctorWorkflowInstalled as W, CI_TRUST_COMPANIES as X, isUnicodeSupported as Y, METRIC as Z, launchCliAgent as _, succeed$3 as _t, selectProjects as a, buildRuleDocsUrl as at, formatSkippedProjectsMessage as b, resolveWorkspaceDeadCodeOwner as c, highlighter as ct, countUniqueScannedFiles as d, remainingDeadlineBudgetMs as dt, isCiEnvironment as et, setUpGitHubActions as f, resolveScanTarget as ft, copyToClipboard as g, void_ as gt, CLI_AGENT_BINARIES as h, sync as ht, discoverWorkspacePackages as i, Reporter as it, filterDiagnosticsByCategories as j, isOnboardingForced as k, resolveCliInspectOptions as l, isPathInsideDirectory as lt, detectLaunchableAgents as m, runPromise as mt, registerActiveTuiRenderer as n, GITHUB_ACTIONS_SETUP_URL as nt, resolveProjectScan as o, getCategoryImpact as ot, openUrl as p, yieldToEventLoop as pt, toForwardSlashes as q, shouldFailScanGate as r, Progress as rt, retryMissingProjectScores as s, hasPublishedFixRecipe as st, exitGracefully as t, DIAGNOSTIC_CATEGORY_BUCKETS as tt, filterScansForSurface as u, mapWithConcurrency as ut, buildHandoffPayload as v, printFooter as w, collectProjectSourceFileCounts as x, isShareOptedOut as y, buildRulePriorityMap as z };
99059
98983
 
99060
98984
  //# sourceMappingURL=cli.js.map
99061
- //# debugId=80fc688d-8e21-59d6-abcd-27ce2265a7d3
98985
+ //# debugId=d2fd99d7-caaf-5245-aff8-0ff8d0a163e3