react-doctor 0.8.1-dev.fc1b3a3 → 0.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/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]="d92b0964-cd34-5965-a1bf-ce1d06faf742")}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]="941df082-c8b4-5084-baae-33ec22d4e55f")}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";
@@ -36022,6 +36022,44 @@ var AmbiguousProjectError = class extends Error {
36022
36022
  }
36023
36023
  };
36024
36024
  const isProjectDiscoveryError = (value) => value instanceof ProjectNotFoundError || value instanceof NoReactDependencyError || value instanceof PackageJsonNotFoundError || value instanceof NotADirectoryError || value instanceof AmbiguousProjectError;
36025
+ const isErrnoException = (error) => error instanceof Error && "code" in error;
36026
+ const IGNORABLE_READDIR_ERROR_CODES = new Set([
36027
+ "EACCES",
36028
+ "EPERM",
36029
+ "ENOENT",
36030
+ "ENOTDIR",
36031
+ "EINVAL",
36032
+ "ELOOP",
36033
+ "ENAMETOOLONG"
36034
+ ]);
36035
+ const isIgnorableReaddirError = (error) => isErrnoException(error) && typeof error.code === "string" && IGNORABLE_READDIR_ERROR_CODES.has(error.code);
36036
+ const readDirectoryEntries = (directoryPath) => {
36037
+ try {
36038
+ return NFS.readdirSync(directoryPath, { withFileTypes: true });
36039
+ } catch (error) {
36040
+ if (isIgnorableReaddirError(error)) return [];
36041
+ throw error;
36042
+ }
36043
+ };
36044
+ const isFile = (filePath) => {
36045
+ try {
36046
+ return NFS.statSync(filePath).isFile();
36047
+ } catch {
36048
+ return false;
36049
+ }
36050
+ };
36051
+ const isDirectory = (directoryPath) => {
36052
+ try {
36053
+ return NFS.statSync(directoryPath).isDirectory();
36054
+ } catch {
36055
+ return false;
36056
+ }
36057
+ };
36058
+ const isPlainObject = (value) => {
36059
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
36060
+ const prototype = Object.getPrototypeOf(value);
36061
+ return prototype === null || prototype === Object.prototype;
36062
+ };
36025
36063
  const SOURCE_FILE_PATTERN = /\.(tsx?|jsx?|mts|mjs)$/;
36026
36064
  const GENERATED_BUNDLE_FILE_PATTERN = /\.(iife|umd|global|min)\.m?js$/i;
36027
36065
  const GENERATED_SOURCE_DIRECTORY_PATTERN = /(?:^|\/)__generated__\//;
@@ -36214,44 +36252,6 @@ const SUPPLY_CHAIN_OVERLAP_TIMEOUT_MS = 9e4;
36214
36252
  const SUPPLY_CHAIN_CACHE_TTL_MS = 864e5;
36215
36253
  const SUPPLY_CHAIN_CACHE_SUBDIR = "supply-chain";
36216
36254
  const SUPPLY_CHAIN_IGNORED_PACKAGES = new Set(["next"]);
36217
- const isErrnoException = (error) => error instanceof Error && "code" in error;
36218
- const IGNORABLE_READDIR_ERROR_CODES = new Set([
36219
- "EACCES",
36220
- "EPERM",
36221
- "ENOENT",
36222
- "ENOTDIR",
36223
- "EINVAL",
36224
- "ELOOP",
36225
- "ENAMETOOLONG"
36226
- ]);
36227
- const isIgnorableReaddirError = (error) => isErrnoException(error) && typeof error.code === "string" && IGNORABLE_READDIR_ERROR_CODES.has(error.code);
36228
- const readDirectoryEntries = (directoryPath) => {
36229
- try {
36230
- return NFS.readdirSync(directoryPath, { withFileTypes: true });
36231
- } catch (error) {
36232
- if (isIgnorableReaddirError(error)) return [];
36233
- throw error;
36234
- }
36235
- };
36236
- const isFile = (filePath) => {
36237
- try {
36238
- return NFS.statSync(filePath).isFile();
36239
- } catch {
36240
- return false;
36241
- }
36242
- };
36243
- const isDirectory = (directoryPath) => {
36244
- try {
36245
- return NFS.statSync(directoryPath).isDirectory();
36246
- } catch {
36247
- return false;
36248
- }
36249
- };
36250
- const isPlainObject = (value) => {
36251
- if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
36252
- const prototype = Object.getPrototypeOf(value);
36253
- return prototype === null || prototype === Object.prototype;
36254
- };
36255
36255
  const lastSourceMappingUrl = (fileContent) => {
36256
36256
  const lastReference = [...fileContent.matchAll(/\/\/# sourceMappingURL=(\S+)/g)].at(-1);
36257
36257
  return lastReference ? lastReference[1] : null;
@@ -36571,10 +36571,10 @@ const FALLBACK_TSCONFIG_FILENAMES = ["tsconfig.app.json", "tsconfig.build.json"]
36571
36571
  const FRAMEWORK_PACKAGES = {
36572
36572
  next: "nextjs",
36573
36573
  "@tanstack/react-start": "tanstack-start",
36574
- "@remix-run/react": "remix",
36575
- gatsby: "gatsby",
36576
36574
  vite: "vite",
36577
36575
  "react-scripts": "cra",
36576
+ "@remix-run/react": "remix",
36577
+ gatsby: "gatsby",
36578
36578
  expo: "expo",
36579
36579
  "react-native": "react-native"
36580
36580
  };
@@ -36598,17 +36598,11 @@ const NEXT_CONFIG_FILENAMES = [
36598
36598
  ];
36599
36599
  const BABEL_CONFIG_FILENAMES = [
36600
36600
  ".babelrc",
36601
- ".babelrc.js",
36602
36601
  ".babelrc.json",
36603
- ".babelrc.cjs",
36604
- ".babelrc.mjs",
36605
- ".babelrc.cts",
36606
36602
  "babel.config.js",
36607
36603
  "babel.config.json",
36608
36604
  "babel.config.cjs",
36609
- "babel.config.mjs",
36610
- "babel.config.ts",
36611
- "babel.config.cts"
36605
+ "babel.config.mjs"
36612
36606
  ];
36613
36607
  const VITE_CONFIG_FILENAMES = [
36614
36608
  "vite.config.js",
@@ -36620,28 +36614,12 @@ const VITE_CONFIG_FILENAMES = [
36620
36614
  "vitest.config.ts",
36621
36615
  "vitest.config.js"
36622
36616
  ];
36623
- const RSBUILD_CONFIG_FILENAMES = [
36624
- "rsbuild.config.ts",
36625
- "rsbuild.config.js",
36626
- "rsbuild.config.mts",
36627
- "rsbuild.config.mjs",
36628
- "rsbuild.config.cts",
36629
- "rsbuild.config.cjs"
36630
- ];
36631
- const RSPACK_CONFIG_FILENAMES = [
36632
- "rspack.config.ts",
36633
- "rspack.config.js",
36634
- "rspack.config.mts",
36635
- "rspack.config.mjs",
36636
- "rspack.config.cts",
36637
- "rspack.config.cjs"
36638
- ];
36639
36617
  const EXPO_APP_CONFIG_FILENAMES = [
36640
36618
  "app.json",
36641
36619
  "app.config.js",
36642
36620
  "app.config.ts"
36643
36621
  ];
36644
- const REACT_COMPILER_PACKAGE_REFERENCE_PATTERN = /babel-plugin-react-compiler|react-compiler-runtime|reactCompilerPreset\s*\(|["']react-compiler["']/;
36622
+ const REACT_COMPILER_PACKAGE_REFERENCE_PATTERN = /babel-plugin-react-compiler|react-compiler-runtime|["']react-compiler["']/;
36645
36623
  const REACT_COMPILER_ENABLED_FLAG_PATTERN = /["']?reactCompiler["']?\s*:\s*(?:true\b|\{)/;
36646
36624
  const STATIC_EXPORT_OUTPUT_PATTERN = /(?:^|[^.\w])["']?output["']?\s*:\s*["']export["']/m;
36647
36625
  const hasCompilerPackage = (packageJson, compilerPackages) => {
@@ -36676,8 +36654,6 @@ const detectReactCompiler = (directory, packageJson) => {
36676
36654
  if (hasCompilerInConfigFiles(directory, NEXT_CONFIG_FILENAMES)) return true;
36677
36655
  if (hasCompilerInConfigFiles(directory, BABEL_CONFIG_FILENAMES)) return true;
36678
36656
  if (hasCompilerInConfigFiles(directory, VITE_CONFIG_FILENAMES)) return true;
36679
- if (hasCompilerInConfigFiles(directory, RSBUILD_CONFIG_FILENAMES)) return true;
36680
- if (hasCompilerInConfigFiles(directory, RSPACK_CONFIG_FILENAMES)) return true;
36681
36657
  if (hasCompilerInConfigFiles(directory, EXPO_APP_CONFIG_FILENAMES)) return true;
36682
36658
  return hasCompilerPackageInAncestors(directory, REACT_COMPILER_TRANSFORM_PACKAGES);
36683
36659
  };
@@ -36819,16 +36795,6 @@ const getLowestDependencyMajor = (version) => {
36819
36795
  }
36820
36796
  return lowestMajor;
36821
36797
  };
36822
- const getDependencyMajorWithinSupportedRange = (version, latestSupportedMajor) => {
36823
- const normalizedVersion = normalizeDependencyVersion(version);
36824
- if (normalizedVersion === null) return null;
36825
- const validRange = import_semver.validRange(normalizedVersion);
36826
- if (validRange === null) return null;
36827
- const minimumVersion = import_semver.minVersion(validRange);
36828
- if (minimumVersion === null || minimumVersion.major > latestSupportedMajor) return null;
36829
- if (import_semver.intersects(validRange, `>=${latestSupportedMajor + 1}.0.0`)) return null;
36830
- return minimumVersion.major;
36831
- };
36832
36798
  const isConcreteDependencyVersion = (version) => {
36833
36799
  const normalizedVersion = normalizeDependencyVersion(version);
36834
36800
  return normalizedVersion !== null && /\d/.test(normalizedVersion);
@@ -36872,9 +36838,9 @@ const parseReactMajorMinor = (reactVersion) => {
36872
36838
  };
36873
36839
  };
36874
36840
  const parseLowerBoundVersion = (versionSpec) => import_semver.validRange(versionSpec) !== null ? import_semver.minVersion(versionSpec) : import_semver.coerce(versionSpec);
36875
- const parseDependencyMajorMinor = (dependencyVersion) => {
36876
- if (typeof dependencyVersion !== "string") return null;
36877
- const trimmed = dependencyVersion.trim();
36841
+ const parseTailwindMajorMinor = (tailwindVersion) => {
36842
+ if (typeof tailwindVersion !== "string") return null;
36843
+ const trimmed = tailwindVersion.trim();
36878
36844
  if (trimmed.length === 0) return null;
36879
36845
  const lowerBound = parseLowerBoundVersion(trimmed);
36880
36846
  if (lowerBound === null || lowerBound.major <= 0) return null;
@@ -36883,7 +36849,6 @@ const parseDependencyMajorMinor = (dependencyVersion) => {
36883
36849
  minor: lowerBound.minor
36884
36850
  };
36885
36851
  };
36886
- const parseTailwindMajorMinor = (tailwindVersion) => parseDependencyMajorMinor(tailwindVersion);
36887
36852
  const hasUpperBoundOnlyPeerRange = (range) => {
36888
36853
  if (typeof range !== "string") return false;
36889
36854
  const normalizedRange = normalizeDependencyVersion(range);
@@ -37136,6 +37101,19 @@ const getPreactVersion = (packageJson) => {
37136
37101
  ...packageJson.devDependencies
37137
37102
  }.preact ?? null;
37138
37103
  };
37104
+ const TANSTACK_QUERY_PACKAGES = new Set([
37105
+ "@tanstack/react-query",
37106
+ "@tanstack/query-core",
37107
+ "react-query"
37108
+ ]);
37109
+ const hasTanStackQuery = (packageJson) => {
37110
+ const allDependencies = {
37111
+ ...packageJson.peerDependencies,
37112
+ ...packageJson.dependencies,
37113
+ ...packageJson.devDependencies
37114
+ };
37115
+ return Object.keys(allDependencies).some((packageName) => TANSTACK_QUERY_PACKAGES.has(packageName));
37116
+ };
37139
37117
  const resolveCatalogBackedDependencyVersion = ({ rootDirectory, rootPackageJson, packageName, version }) => {
37140
37118
  if (version === null || !isCatalogReference(version)) return version;
37141
37119
  const catalogName = extractCatalogName(version);
@@ -37338,60 +37316,7 @@ const listWorkspacePackages = (rootDirectory) => {
37338
37316
  }
37339
37317
  return packages;
37340
37318
  };
37341
- const getPreferredDependencyVersion = ({ packageJson, packageNames }) => {
37342
- const allDependencies = {
37343
- ...packageJson.devDependencies,
37344
- ...packageJson.peerDependencies,
37345
- ...packageJson.dependencies
37346
- };
37347
- for (const packageName of packageNames) {
37348
- const version = allDependencies[packageName];
37349
- if (version !== void 0) return version;
37350
- }
37351
- return null;
37352
- };
37353
- const TANSTACK_REACT_QUERY_PACKAGES = ["@tanstack/react-query", "react-query"];
37354
- const getTanStackQueryVersion = (packageJson) => {
37355
- return getPreferredDependencyVersion({
37356
- packageJson,
37357
- packageNames: TANSTACK_REACT_QUERY_PACKAGES
37358
- });
37359
- };
37360
- const STYLED_COMPONENTS_PACKAGES = ["styled-components"];
37361
- const getStyledComponentsVersion = (packageJson) => {
37362
- return getPreferredDependencyVersion({
37363
- packageJson,
37364
- packageNames: STYLED_COMPONENTS_PACKAGES
37365
- });
37366
- };
37367
- const I18N_PACKAGES = [
37368
- "i18next",
37369
- "react-i18next",
37370
- "next-i18next",
37371
- "react-intl",
37372
- "next-intl",
37373
- "use-intl",
37374
- "@formatjs/intl",
37375
- "@lingui/core",
37376
- "@lingui/react",
37377
- "typesafe-i18n",
37378
- "@tolgee/react",
37379
- "react-intl-universal"
37380
- ];
37381
- const hasI18nDependency = (packageJson) => {
37382
- const allDependencies = {
37383
- ...packageJson.peerDependencies,
37384
- ...packageJson.dependencies,
37385
- ...packageJson.devDependencies,
37386
- ...packageJson.optionalDependencies
37387
- };
37388
- return I18N_PACKAGES.some((packageName) => allDependencies[packageName] !== void 0);
37389
- };
37390
37319
  const REANIMATED_DEPENDENCY_NAME = "react-native-reanimated";
37391
- const MOBX_REACT_PACKAGE_NAME = "mobx-react";
37392
- const MOBX_REACT_LITE_PACKAGE_NAME = "mobx-react-lite";
37393
- const MOBX_STATE_TREE_PACKAGE_NAME = "mobx-state-tree";
37394
- const MOBX_REACT_OBSERVER_PACKAGE_NAME = "mobx-react-observer";
37395
37320
  const SHOPIFY_FLASH_LIST_PACKAGE_NAME = "@shopify/flash-list";
37396
37321
  const resolveWorkspaceDependencyVersion = ({ concreteVersion, packageName, rootDirectory, rootPackageJson, sections, workspaceDirectory, workspacePackageJson }) => {
37397
37322
  const dependencyDeclaration = getDependencyDeclaration({
@@ -37410,62 +37335,7 @@ const shouldReplaceReactVersion = (currentVersion, nextVersion) => {
37410
37335
  if (nextMajor === null) return false;
37411
37336
  return nextMajor < currentMajor;
37412
37337
  };
37413
- const shouldReplaceSupportedDependencyFact = (currentFact, nextFact, latestSupportedMajor) => {
37414
- if (currentFact.version === null) return true;
37415
- if (nextFact.version === null) return false;
37416
- const currentMajor = getLowestDependencyMajor(currentFact.version);
37417
- const nextMajor = getLowestDependencyMajor(nextFact.version);
37418
- if (currentMajor === null) return false;
37419
- if (nextMajor === null) return true;
37420
- if (currentMajor > latestSupportedMajor) return false;
37421
- if (nextMajor > latestSupportedMajor) return true;
37422
- return nextMajor < currentMajor;
37423
- };
37424
- const shouldReplaceStyledComponentsVersion = (currentVersion, nextVersion) => {
37425
- if (!currentVersion) return true;
37426
- const currentMajor = getLowestDependencyMajor(currentVersion);
37427
- const nextMajor = getLowestDependencyMajor(nextVersion);
37428
- if (currentMajor === null) return nextMajor !== null && nextMajor < 6;
37429
- if (nextMajor === null) return currentMajor >= 6;
37430
- return nextMajor < currentMajor;
37431
- };
37432
- const selectOldestDependencyVersion = (currentVersion, nextVersion) => {
37433
- if (currentVersion === null || nextVersion === null) return null;
37434
- const current = parseDependencyMajorMinor(currentVersion);
37435
- const next = parseDependencyMajorMinor(nextVersion);
37436
- if (current === null) return currentVersion;
37437
- if (next === null) return nextVersion;
37438
- if (next.major !== current.major) return next.major < current.major ? nextVersion : currentVersion;
37439
- return next.minor < current.minor ? nextVersion : currentVersion;
37440
- };
37441
- const shouldReplaceMobxFact = (currentFact, nextFact) => {
37442
- if (currentFact.sourceDirectory === null) return true;
37443
- if (currentFact.version === null) return false;
37444
- if (nextFact.version === null) return true;
37445
- const currentMajor = getDependencyMajorWithinSupportedRange(currentFact.version, 6);
37446
- const nextMajor = getDependencyMajorWithinSupportedRange(nextFact.version, 6);
37447
- if (currentMajor === null) return false;
37448
- if (nextMajor === null) return true;
37449
- return selectOldestDependencyVersion(currentFact.version, nextFact.version) === nextFact.version;
37450
- };
37451
- const collectBindingVersion = (facts, packageJson, directory, packageName) => {
37452
- const version = getDependencySpec(packageJson, packageName);
37453
- if (version === null) return;
37454
- const resolvedVersion = resolveCatalogBackedDependencyVersion({
37455
- rootDirectory: directory,
37456
- rootPackageJson: packageJson,
37457
- packageName,
37458
- version
37459
- });
37460
- if (packageName === MOBX_REACT_PACKAGE_NAME) {
37461
- facts.mobxReactVersion = facts.hasMobxReact ? selectOldestDependencyVersion(facts.mobxReactVersion, resolvedVersion) : resolvedVersion;
37462
- facts.hasMobxReact = true;
37463
- return;
37464
- }
37465
- facts.mobxReactLiteVersion = facts.hasMobxReactLite ? selectOldestDependencyVersion(facts.mobxReactLiteVersion, resolvedVersion) : resolvedVersion;
37466
- facts.hasMobxReactLite = true;
37467
- };
37468
- const evaluateManifestFacts = (facts, packageJson, directory, rootDirectory, rootPackageJson) => {
37338
+ const evaluateManifestFacts = (facts, packageJson, directory) => {
37469
37339
  if (facts.expo.version === null) {
37470
37340
  const spec = getDependencySpec(packageJson, "expo");
37471
37341
  if (spec !== null) facts.expo = {
@@ -37487,74 +37357,13 @@ const evaluateManifestFacts = (facts, packageJson, directory, rootDirectory, roo
37487
37357
  sourceDirectory: directory
37488
37358
  };
37489
37359
  }
37490
- const mobxSpec = getDependencySpec(packageJson, "mobx");
37491
- if (mobxSpec !== null) {
37492
- const nextMobxFact = {
37493
- version: resolveCatalogBackedDependencyVersion({
37494
- rootDirectory: directory,
37495
- rootPackageJson: packageJson,
37496
- packageName: "mobx",
37497
- version: mobxSpec
37498
- }),
37499
- sourceDirectory: directory
37500
- };
37501
- if (shouldReplaceMobxFact(facts.mobx, nextMobxFact)) facts.mobx = nextMobxFact;
37502
- }
37503
- collectBindingVersion(facts, packageJson, directory, MOBX_REACT_PACKAGE_NAME);
37504
- collectBindingVersion(facts, packageJson, directory, MOBX_REACT_LITE_PACKAGE_NAME);
37505
- facts.hasMobxStateTree = facts.hasMobxStateTree || getDependencySpec(packageJson, MOBX_STATE_TREE_PACKAGE_NAME) !== null;
37506
- facts.hasMobxReactObserver = facts.hasMobxReactObserver || getDependencySpec(packageJson, MOBX_REACT_OBSERVER_PACKAGE_NAME) !== null;
37507
- const zustandSpec = getDependencySpec(packageJson, "zustand");
37508
- if (zustandSpec !== null) {
37509
- const nextZustandFact = {
37510
- version: resolveCatalogBackedDependencyVersion({
37511
- rootDirectory: directory,
37512
- rootPackageJson: packageJson,
37513
- packageName: "zustand",
37514
- version: zustandSpec
37515
- }),
37516
- sourceDirectory: directory
37517
- };
37518
- if (shouldReplaceSupportedDependencyFact(facts.zustand, nextZustandFact, 5)) facts.zustand = nextZustandFact;
37519
- }
37520
37360
  if (facts.reanimatedVersion === null) {
37521
37361
  const spec = getDependencySpec(packageJson, REANIMATED_DEPENDENCY_NAME);
37522
37362
  if (spec !== null) facts.reanimatedVersion = spec;
37523
37363
  }
37524
- if (facts.valtioVersion === null) {
37525
- const spec = getDependencySpec(packageJson, "valtio");
37526
- if (spec !== null) facts.valtioVersion = spec;
37527
- }
37528
- facts.tanstackQueryVersion ??= getTanStackQueryVersion(packageJson);
37529
- const styledComponentsVersion = resolveCatalogBackedDependencyVersion({
37530
- rootDirectory: directory,
37531
- rootPackageJson: packageJson,
37532
- packageName: "styled-components",
37533
- version: getStyledComponentsVersion(packageJson)
37534
- });
37535
- if (styledComponentsVersion && shouldReplaceStyledComponentsVersion(facts.styledComponentsVersion, styledComponentsVersion)) facts.styledComponentsVersion = styledComponentsVersion;
37536
- facts.hasI18nLibrary = facts.hasI18nLibrary || hasI18nDependency(packageJson);
37537
37364
  facts.hasReactNativeAwarePackage = facts.hasReactNativeAwarePackage || isPackageJsonReactNativeAware(packageJson);
37538
37365
  facts.hasReanimatedAwarePackage = facts.hasReanimatedAwarePackage || isPackageJsonReanimatedAware(packageJson);
37539
37366
  facts.hasSsrDependency = facts.hasSsrDependency || isPackageJsonSsrAware(packageJson);
37540
- const remotionSpec = getDependencySpec(packageJson, "remotion");
37541
- if (remotionSpec !== null) {
37542
- facts.hasRemotionDependency = true;
37543
- const resolvedRemotionVersion = resolveCatalogBackedDependencyVersion({
37544
- rootDirectory,
37545
- rootPackageJson,
37546
- packageName: "remotion",
37547
- version: remotionSpec
37548
- });
37549
- const remotionMajorVersion = resolvedRemotionVersion === null ? null : getLowestDependencyMajor(resolvedRemotionVersion);
37550
- if (remotionMajorVersion === null) {
37551
- facts.hasUnknownRemotionVersion = true;
37552
- facts.remotionVersion = null;
37553
- } else if (!facts.hasUnknownRemotionVersion) {
37554
- const currentRemotionMajorVersion = facts.remotionVersion === null ? null : getLowestDependencyMajor(facts.remotionVersion);
37555
- if (currentRemotionMajorVersion === null || remotionMajorVersion < currentRemotionMajorVersion) facts.remotionVersion = resolvedRemotionVersion;
37556
- }
37557
- }
37558
37367
  };
37559
37368
  const collectWorkspaceFacts = (rootDirectory, rootPackageJson, { collectReactGroup }) => {
37560
37369
  const facts = {
@@ -37574,33 +37383,12 @@ const collectWorkspaceFacts = (rootDirectory, rootPackageJson, { collectReactGro
37574
37383
  version: null,
37575
37384
  sourceDirectory: null
37576
37385
  },
37577
- valtioVersion: null,
37578
- mobx: {
37579
- version: null,
37580
- sourceDirectory: null
37581
- },
37582
- hasMobxReact: false,
37583
- mobxReactVersion: null,
37584
- hasMobxReactLite: false,
37585
- mobxReactLiteVersion: null,
37586
- hasMobxStateTree: false,
37587
- hasMobxReactObserver: false,
37588
- zustand: {
37589
- version: null,
37590
- sourceDirectory: null
37591
- },
37592
- tanstackQueryVersion: null,
37593
- styledComponentsVersion: null,
37594
- hasI18nLibrary: false,
37595
37386
  hasReactNativeAwarePackage: false,
37596
37387
  hasReanimatedAwarePackage: false,
37597
37388
  hasSsrDependency: false,
37598
- hasRemotionDependency: false,
37599
- hasUnknownRemotionVersion: false,
37600
- remotionVersion: null,
37601
37389
  reanimatedVersion: null
37602
37390
  };
37603
- evaluateManifestFacts(facts, rootPackageJson, rootDirectory, rootDirectory, rootPackageJson);
37391
+ evaluateManifestFacts(facts, rootPackageJson, rootDirectory);
37604
37392
  let isReactGroupSettled = !collectReactGroup;
37605
37393
  const visitedDirectories = /* @__PURE__ */ new Set();
37606
37394
  for (const pattern of getWorkspacePatterns(rootDirectory, rootPackageJson)) {
@@ -37609,7 +37397,7 @@ const collectWorkspaceFacts = (rootDirectory, rootPackageJson, { collectReactGro
37609
37397
  if (visitedDirectories.has(workspaceDirectory)) continue;
37610
37398
  visitedDirectories.add(workspaceDirectory);
37611
37399
  const workspacePackageJson = readPackageJson$1(Path.join(workspaceDirectory, "package.json"));
37612
- evaluateManifestFacts(facts, workspacePackageJson, workspaceDirectory, rootDirectory, rootPackageJson);
37400
+ evaluateManifestFacts(facts, workspacePackageJson, workspaceDirectory);
37613
37401
  const info = extractDependencyInfo(workspacePackageJson);
37614
37402
  if (info.framework !== "unknown" && frameworkMergeRank(info.framework) < frameworkMergeRank(facts.framework)) facts.framework = info.framework;
37615
37403
  if (isReactGroupSettled) continue;
@@ -37830,29 +37618,11 @@ const discoverProjectWithoutPackageJson = (directory) => {
37830
37618
  tailwindVersion: null,
37831
37619
  zodVersion: null,
37832
37620
  zodMajorVersion: null,
37833
- mobxVersion: null,
37834
- mobxMajorVersion: null,
37835
- hasMobxReact: false,
37836
- mobxReactVersion: null,
37837
- hasMobxReactLite: false,
37838
- mobxReactLiteVersion: null,
37839
- hasMobxStateTree: false,
37840
- hasMobxReactObserver: false,
37841
- zustandVersion: null,
37842
- zustandMajorVersion: null,
37843
37621
  framework: "unknown",
37844
37622
  hasTypeScript: hasOwnTsConfig,
37845
37623
  hasReactCompiler: false,
37846
37624
  hasReactCompilerLintPlugin: false,
37847
37625
  hasTanStackQuery: false,
37848
- valtioVersion: null,
37849
- valtioMajorVersion: null,
37850
- hasRemotion: false,
37851
- remotionVersion: null,
37852
- remotionMajorVersion: null,
37853
- hasI18nLibrary: false,
37854
- tanstackQueryVersion: null,
37855
- styledComponentsVersion: null,
37856
37626
  hasSsrDependency: false,
37857
37627
  preactVersion: null,
37858
37628
  preactMajorVersion: null,
@@ -37951,12 +37721,6 @@ const discoverProject = (directory) => {
37951
37721
  packageName: SHOPIFY_FLASH_LIST_PACKAGE_NAME,
37952
37722
  version: workspaceFacts.shopifyFlashList.version
37953
37723
  }) : null;
37954
- const zustandVersion = resolveCatalogBackedDependencyVersion({
37955
- rootDirectory: directory,
37956
- rootPackageJson: packageJson,
37957
- packageName: "zustand",
37958
- version: workspaceFacts.zustand.version
37959
- });
37960
37724
  const hasReanimated = hasReactNativeWorkspace && workspaceFacts.hasReanimatedAwarePackage;
37961
37725
  const reanimatedVersion = hasReanimated ? workspaceFacts.reanimatedVersion : null;
37962
37726
  const nextjsVersion = framework === "nextjs" ? resolveCatalogBackedDependencyVersion({
@@ -37965,21 +37729,7 @@ const discoverProject = (directory) => {
37965
37729
  packageName: "next",
37966
37730
  version: workspaceFacts.next.version
37967
37731
  }) : null;
37968
- const valtioVersion = resolveCatalogBackedDependencyVersion({
37969
- rootDirectory: directory,
37970
- rootPackageJson: packageJson,
37971
- packageName: "valtio",
37972
- version: workspaceFacts.valtioVersion
37973
- });
37974
- const mobxVersion = resolveCatalogBackedDependencyVersion({
37975
- rootDirectory: directory,
37976
- rootPackageJson: packageJson,
37977
- packageName: "mobx",
37978
- version: workspaceFacts.mobx.version
37979
- });
37980
37732
  const preactVersion = getPreactVersion(packageJson);
37981
- const remotionVersion = workspaceFacts.remotionVersion;
37982
- const tanstackQueryVersion = getTanStackQueryVersion(packageJson) ?? workspaceFacts.tanstackQueryVersion;
37983
37733
  const isPreES2023Target = hasTypeScript && detectPreES2023Target(directory);
37984
37734
  const projectInfo = {
37985
37735
  rootDirectory: directory,
@@ -37989,29 +37739,11 @@ const discoverProject = (directory) => {
37989
37739
  tailwindVersion,
37990
37740
  zodVersion,
37991
37741
  zodMajorVersion: zodVersion === null ? null : getLowestDependencyMajor(zodVersion),
37992
- mobxVersion,
37993
- mobxMajorVersion: mobxVersion === null ? null : getDependencyMajorWithinSupportedRange(mobxVersion, 6),
37994
- hasMobxReact: workspaceFacts.hasMobxReact,
37995
- mobxReactVersion: workspaceFacts.mobxReactVersion,
37996
- hasMobxReactLite: workspaceFacts.hasMobxReactLite,
37997
- mobxReactLiteVersion: workspaceFacts.mobxReactLiteVersion,
37998
- hasMobxStateTree: workspaceFacts.hasMobxStateTree,
37999
- hasMobxReactObserver: workspaceFacts.hasMobxReactObserver,
38000
- zustandVersion,
38001
- zustandMajorVersion: zustandVersion === null ? null : getLowestDependencyMajor(zustandVersion),
38002
37742
  framework,
38003
37743
  hasTypeScript,
38004
37744
  hasReactCompiler: detectReactCompiler(directory, packageJson),
38005
37745
  hasReactCompilerLintPlugin: detectReactCompilerLintPlugin(directory, packageJson),
38006
- hasTanStackQuery: tanstackQueryVersion !== null,
38007
- hasI18nLibrary: workspaceFacts.hasI18nLibrary,
38008
- tanstackQueryVersion,
38009
- styledComponentsVersion: workspaceFacts.styledComponentsVersion,
38010
- valtioVersion,
38011
- valtioMajorVersion: valtioVersion === null ? null : getLowestDependencyMajor(valtioVersion),
38012
- hasRemotion: workspaceFacts.hasRemotionDependency,
38013
- remotionVersion,
38014
- remotionMajorVersion: remotionVersion === null ? null : getLowestDependencyMajor(remotionVersion),
37746
+ hasTanStackQuery: hasTanStackQuery(packageJson),
38015
37747
  hasSsrDependency: workspaceFacts.hasSsrDependency,
38016
37748
  preactVersion,
38017
37749
  preactMajorVersion: parseReactMajor(preactVersion),
@@ -38245,6 +37977,7 @@ const LEGACY_RULE_KEY_TO_NATIVE_RULE_KEY = {
38245
37977
  "react/jsx-no-new-function-as-prop": "react-doctor/jsx-no-new-function-as-prop",
38246
37978
  "react/jsx-no-new-object-as-prop": "react-doctor/jsx-no-new-object-as-prop",
38247
37979
  "react/jsx-no-script-url": "react-doctor/jsx-no-script-url",
37980
+ "react/jsx-no-target-blank": "react-doctor/jsx-no-target-blank",
38248
37981
  "react/jsx-no-undef": "react-doctor/jsx-no-undef",
38249
37982
  "react/jsx-no-useless-fragment": "react-doctor/jsx-no-useless-fragment",
38250
37983
  "react/jsx-pascal-case": "react-doctor/jsx-pascal-case",
@@ -40566,25 +40299,9 @@ const BABEL_CONFIG_FILE_NAMES = [
40566
40299
  ".babelrc.json"
40567
40300
  ];
40568
40301
  const LEGACY_PRESET_SPEC = "module:metro-react-native-babel-preset";
40569
- const LEGACY_PRESET_PACKAGE = "metro-react-native-babel-preset";
40570
- const REACT_NATIVE_PACKAGE = "react-native";
40571
- const PRESET_RENAME_MINIMUM_REACT_NATIVE_VERSION = "0.73.0";
40572
- const LEGACY_PRESET_REFERENCE = new RegExp(`['"]${LEGACY_PRESET_SPEC}['"]`);
40573
40302
  const MODERN_PRESET_REFERENCE = new RegExp(`['"]module:@react-native/babel-preset['"]`);
40574
40303
  const ENABLE_BABEL_RUNTIME_VERSION = /enableBabelRuntime["']?\s*:\s*['"]/;
40575
- const isReactNativeVersionAtLeastPresetRename = (rootDirectory, packageJson) => {
40576
- const installedReactNativeManifestPath = Path.join(rootDirectory, "node_modules", REACT_NATIVE_PACKAGE, "package.json");
40577
- const reactNativeVersionSpec = (isFile(installedReactNativeManifestPath) ? readPackageJson$1(installedReactNativeManifestPath).version : void 0) ?? getDependencySpec(packageJson, REACT_NATIVE_PACKAGE);
40578
- if (reactNativeVersionSpec === null || reactNativeVersionSpec === void 0) return false;
40579
- const normalizedVersionRange = import_semver.validRange(reactNativeVersionSpec);
40580
- if (normalizedVersionRange === null) return false;
40581
- const minimumVersion = import_semver.minVersion(normalizedVersionRange);
40582
- return minimumVersion !== null && import_semver.gte(minimumVersion, PRESET_RENAME_MINIMUM_REACT_NATIVE_VERSION);
40583
- };
40584
- const canResolveLegacyPreset = (rootDirectory, packageJson) => getDependencySpec(packageJson, LEGACY_PRESET_PACKAGE) !== null || isFile(Path.join(rootDirectory, "node_modules", LEGACY_PRESET_PACKAGE, "package.json"));
40585
40304
  const checkReactNativeMetroBabelPreset = (rootDirectory) => {
40586
- const packageJson = readPackageJson$1(Path.join(rootDirectory, "package.json"));
40587
- const shouldReportLegacyPreset = isReactNativeVersionAtLeastPresetRename(rootDirectory, packageJson) && !canResolveLegacyPreset(rootDirectory, packageJson);
40588
40305
  for (const fileName of BABEL_CONFIG_FILE_NAMES) {
40589
40306
  const filePath = Path.join(rootDirectory, fileName);
40590
40307
  if (!isFile(filePath)) continue;
@@ -40594,7 +40311,7 @@ const checkReactNativeMetroBabelPreset = (rootDirectory) => {
40594
40311
  } catch {
40595
40312
  continue;
40596
40313
  }
40597
- if (shouldReportLegacyPreset && LEGACY_PRESET_REFERENCE.test(contents)) return [buildReactNativeDiagnostic({
40314
+ if (contents.includes(LEGACY_PRESET_SPEC)) return [buildReactNativeDiagnostic({
40598
40315
  filePath: fileName,
40599
40316
  rule: "rn-no-metro-babel-preset",
40600
40317
  severity: "error",
@@ -40617,6 +40334,12 @@ const checkReactNativeProject = (rootDirectory, project) => {
40617
40334
  return [...checkReactNativeMetroBabelPreset(rootDirectory), ...checkReactNativeLibraryDependencies(rootDirectory)];
40618
40335
  };
40619
40336
  const RULE_KEY = "no-vulnerable-react-server-components";
40337
+ const DEPENDENCY_SECTIONS = [
40338
+ "dependencies",
40339
+ "devDependencies",
40340
+ "peerDependencies",
40341
+ "optionalDependencies"
40342
+ ];
40620
40343
  const REACT_RSC_ADVISORIES_BY_MINOR = {
40621
40344
  "19.0": {
40622
40345
  rceFixedVersion: "19.0.1",
@@ -40663,13 +40386,20 @@ const enumerateWorkspaceDirectories = (workspaceRoot) => {
40663
40386
  for (const pattern of patterns) for (const directory of resolveWorkspaceDirectories(workspaceRoot, pattern)) directories.add(directory);
40664
40387
  return [...directories];
40665
40388
  };
40389
+ const readDeclaredSpec = (packageJson, packageName) => {
40390
+ for (const section of DEPENDENCY_SECTIONS) {
40391
+ const spec = packageJson[section]?.[packageName];
40392
+ if (typeof spec === "string") return spec;
40393
+ }
40394
+ return null;
40395
+ };
40666
40396
  const resolveVersionInDirectory = (directory, packageName, declaredSpecOverride) => {
40667
40397
  const manifestPath = Path.join(directory, "node_modules", packageName, "package.json");
40668
40398
  if (isFile(manifestPath)) {
40669
40399
  const installedVersion = import_semver.valid(readPackageJson$1(manifestPath).version ?? null);
40670
40400
  if (installedVersion !== null) return installedVersion;
40671
40401
  }
40672
- const candidateSpecs = [getDependencySpec(readPackageJson$1(Path.join(directory, "package.json")), packageName), declaredSpecOverride];
40402
+ const candidateSpecs = [readDeclaredSpec(readPackageJson$1(Path.join(directory, "package.json")), packageName), declaredSpecOverride];
40673
40403
  for (const spec of candidateSpecs) {
40674
40404
  const pinnedVersion = spec === null ? null : import_semver.valid(spec);
40675
40405
  if (pinnedVersion !== null) return pinnedVersion;
@@ -41337,68 +41067,21 @@ const buildCapabilities = (project) => {
41337
41067
  if (CLIENT_ONLY_FRAMEWORKS.has(project.framework)) capabilities.add("client-only");
41338
41068
  if (project.isStaticExport) capabilities.add("nextjs:static-export");
41339
41069
  if (project.nextjsMajorVersion !== null && project.nextjsMajorVersion >= 15) capabilities.add("nextjs:15");
41340
- if (project.nextjsMajorVersion !== null && project.nextjsMajorVersion >= 16) capabilities.add("nextjs:16");
41341
41070
  addMajorLadder(capabilities, "react", project.reactMajorVersion, 17, 30);
41342
41071
  if (project.reactMajorVersion !== null && project.reactMajorVersion >= 19 && isMajorMinorAtLeast(parseReactMajorMinor(project.reactVersion), {
41343
41072
  major: 19,
41344
41073
  minor: 2
41345
41074
  })) capabilities.add("react:19.2");
41346
- if (project.tailwindVersion !== null) {
41347
- capabilities.add("tailwind");
41348
- const tailwindVersion = parseTailwindMajorMinor(project.tailwindVersion);
41349
- if (isMajorMinorAtLeast(tailwindVersion, {
41350
- major: 3,
41351
- minor: 4
41352
- })) capabilities.add("tailwind:3.4");
41353
- if (tailwindVersion !== null && isMajorMinorAtLeast(tailwindVersion, {
41354
- major: 4,
41355
- minor: 0
41356
- })) capabilities.add("tailwind:4");
41357
- }
41075
+ if (project.tailwindVersion !== null) capabilities.add("tailwind");
41076
+ if (project.tailwindVersion !== null && isMajorMinorAtLeast(parseTailwindMajorMinor(project.tailwindVersion), {
41077
+ major: 3,
41078
+ minor: 4
41079
+ })) capabilities.add("tailwind:3.4");
41358
41080
  if (project.zodVersion !== null) capabilities.add("zod");
41359
41081
  if (project.zodMajorVersion !== null && project.zodMajorVersion >= 4) capabilities.add("zod:4");
41360
- if (project.mobxVersion !== void 0 && project.mobxVersion !== null || project.hasMobxReact === true || project.hasMobxReactLite === true || project.hasMobxStateTree === true || project.hasMobxReactObserver === true) capabilities.add("mobx");
41361
- if (project.hasMobxReact === true) capabilities.add("mobx-react");
41362
- if (project.hasMobxReactLite === true) capabilities.add("mobx-react-lite");
41363
- if (project.hasMobxReact === true || project.hasMobxReactLite === true) capabilities.add("mobx-react-binding");
41364
- const mobxReactVersion = parseDependencyMajorMinor(project.mobxReactVersion);
41365
- const mobxReactLiteVersion = parseDependencyMajorMinor(project.mobxReactLiteVersion);
41366
- const hasMobxReactObserverMemoGuard = mobxReactVersion !== null && isMajorMinorAtLeast(mobxReactVersion, {
41367
- major: 7,
41368
- minor: 3
41369
- });
41370
- const hasMobxReactLiteObserverMemoGuard = mobxReactLiteVersion !== null && isMajorMinorAtLeast(mobxReactLiteVersion, {
41371
- major: 3,
41372
- minor: 3
41373
- });
41374
- if (hasMobxReactObserverMemoGuard) capabilities.add("mobx-react-observer-memo-guard");
41375
- if (hasMobxReactLiteObserverMemoGuard) capabilities.add("mobx-react-lite-observer-memo-guard");
41376
- if (hasMobxReactObserverMemoGuard || hasMobxReactLiteObserverMemoGuard) capabilities.add("mobx-react-binding-observer-memo-guard");
41377
- if (project.hasMobxStateTree === true) capabilities.add("mobx-state-tree");
41378
- if (project.hasMobxReactObserver === true) capabilities.add("mobx-react-observer");
41379
- if (project.mobxMajorVersion !== void 0 && project.mobxMajorVersion !== null && project.mobxMajorVersion >= 4 && project.mobxMajorVersion <= 6) addMajorLadder(capabilities, "mobx", project.mobxMajorVersion, 4, 6);
41380
- const mobxVersion = parseDependencyMajorMinor(project.mobxVersion);
41381
- if (project.mobxMajorVersion === 6 && mobxVersion !== null && isMajorMinorAtLeast(mobxVersion, {
41382
- major: 6,
41383
- minor: 10
41384
- })) capabilities.add("mobx:6.10");
41385
- if (project.zustandVersion !== void 0 && project.zustandVersion !== null) capabilities.add("zustand");
41386
- if (project.zustandMajorVersion !== void 0 && project.zustandMajorVersion !== null && project.zustandMajorVersion >= 1 && project.zustandMajorVersion <= 5) addMajorLadder(capabilities, "zustand", project.zustandMajorVersion, 1, 5);
41387
41082
  if (project.isPreES2023Target) capabilities.add("pre-es2023");
41388
41083
  if (project.hasReactCompiler) capabilities.add("react-compiler");
41389
- if (Boolean(project.hasTanStackQuery) || Boolean(project.tanstackQueryVersion)) capabilities.add("tanstack-query");
41390
- if (project.styledComponentsVersion) {
41391
- capabilities.add("styled-components");
41392
- const styledComponentsMajor = getLowestDependencyMajor(project.styledComponentsVersion);
41393
- if (styledComponentsMajor !== null && styledComponentsMajor >= 6) capabilities.add("styled-components:6");
41394
- }
41395
- if (project.hasI18nLibrary) capabilities.add("i18n");
41396
- if (project.valtioVersion !== null) capabilities.add("valtio");
41397
- addMajorLadder(capabilities, "valtio", project.valtioMajorVersion, 1, 10);
41398
- if (project.hasRemotion) {
41399
- capabilities.add("remotion");
41400
- addMajorLadder(capabilities, "remotion", project.remotionMajorVersion ?? null, 4, 30);
41401
- }
41084
+ if (project.hasTanStackQuery) capabilities.add("tanstack-query");
41402
41085
  if (project.hasTypeScript) capabilities.add("typescript");
41403
41086
  if (project.preactVersion !== null) capabilities.add("preact");
41404
41087
  addMajorLadder(capabilities, "preact", project.preactMajorVersion, 10, 20);
@@ -41413,7 +41096,7 @@ const getCapabilities = (project) => {
41413
41096
  capabilitiesByProject.set(project, capabilities);
41414
41097
  return capabilities;
41415
41098
  };
41416
- const shouldEnableRule = (requires, tags, capabilities, ignoredTags, disabledWhen, includedTags) => {
41099
+ const shouldEnableRule = (requires, tags, capabilities, ignoredTags, disabledWhen) => {
41417
41100
  if (requires) {
41418
41101
  for (const capability of requires) if (!capabilities.has(capability)) return false;
41419
41102
  }
@@ -41424,29 +41107,20 @@ const shouldEnableRule = (requires, tags, capabilities, ignoredTags, disabledWhe
41424
41107
  if (tags) {
41425
41108
  for (const tag of tags) if (ignoredTags.has(tag)) return false;
41426
41109
  }
41427
- if (includedTags && includedTags.size > 0) {
41428
- if (!tags?.some((tag) => includedTags.has(tag))) return false;
41429
- }
41430
41110
  return true;
41431
41111
  };
41432
- const shouldEnableRuleByDefaultStatus = (options) => options.defaultEnabled !== false || options.hasExplicitOverride === true || options.includeTagDefaults && options.hasIncludedTags;
41433
41112
  const yieldToEventLoop = () => new Promise((resolve) => {
41434
41113
  setImmediate(resolve);
41435
41114
  });
41436
41115
  const createSecurityScanSession = (rootDirectory, options) => {
41437
41116
  const capabilities = options.project ? getCapabilities(options.project) : /* @__PURE__ */ new Set();
41438
41117
  const ignoredTags = options.ignoredTags ?? /* @__PURE__ */ new Set();
41439
- const includedTags = options.includedTags ?? /* @__PURE__ */ new Set();
41440
41118
  const enabledScanRules = REACT_DOCTOR_RULES.flatMap((entry) => {
41441
41119
  const rule = entry.rule;
41442
41120
  const scan = rule.scan;
41443
41121
  if (typeof scan !== "function") return [];
41444
- if (!shouldEnableRuleByDefaultStatus({
41445
- defaultEnabled: rule.defaultEnabled,
41446
- includeTagDefaults: options.includeTagDefaults === true,
41447
- hasIncludedTags: includedTags.size > 0
41448
- })) return [];
41449
- if (!shouldEnableRule(rule.requires, rule.tags, capabilities, ignoredTags, rule.disabledWhen, includedTags)) return [];
41122
+ if (rule.defaultEnabled === false) return [];
41123
+ if (!shouldEnableRule(rule.requires, rule.tags, capabilities, ignoredTags, rule.disabledWhen)) return [];
41450
41124
  return [{
41451
41125
  entry,
41452
41126
  scan,
@@ -42660,35 +42334,6 @@ const parseGithubViewerPermission = (stdout) => {
42660
42334
  return /^[A-Z_]+$/.test(value) ? value.toLowerCase() : null;
42661
42335
  };
42662
42336
  const splitNullSeparated = (value) => value.split("\0").filter((entry) => entry.length > 0);
42663
- const parseBaselineDiffPlan = (value) => {
42664
- const entries = splitNullSeparated(value);
42665
- const baseFiles = /* @__PURE__ */ new Set();
42666
- const headFiles = /* @__PURE__ */ new Set();
42667
- for (let entryIndex = 0; entryIndex < entries.length; entryIndex += 2) {
42668
- const status = entries[entryIndex];
42669
- const filePath = entries[entryIndex + 1];
42670
- if (status === void 0 || filePath === void 0 || status.length !== 1) return null;
42671
- if (status === "A") {
42672
- headFiles.add(filePath);
42673
- continue;
42674
- }
42675
- if (status === "D") {
42676
- baseFiles.add(filePath);
42677
- continue;
42678
- }
42679
- if (status === "M" || status === "T") {
42680
- baseFiles.add(filePath);
42681
- headFiles.add(filePath);
42682
- continue;
42683
- }
42684
- return null;
42685
- }
42686
- return {
42687
- baseFiles: [...baseFiles],
42688
- headFiles: [...headFiles],
42689
- untrackedFiles: []
42690
- };
42691
- };
42692
42337
  const UNTRACKED_FILE_LAST_LINE = Number.MAX_SAFE_INTEGER;
42693
42338
  /**
42694
42339
  * `Git` wraps every `git`-via-subprocess call react-doctor makes
@@ -42891,45 +42536,6 @@ var Git = class Git extends Service()("react-doctor/Git") {
42891
42536
  githubViewerPermission,
42892
42537
  branchExists,
42893
42538
  mergeBase,
42894
- baselineDiffPlan: (input) => {
42895
- if (!isSafeGitRevision(input.ref)) return succeed$2(null);
42896
- return gen(function* () {
42897
- const unmerged = yield* runGit(input.directory, [
42898
- "diff",
42899
- "--no-ext-diff",
42900
- "-z",
42901
- "--name-only",
42902
- "--diff-filter=U",
42903
- "--relative"
42904
- ]);
42905
- if (unmerged.status !== 0 || unmerged.stdout.length > 0) return null;
42906
- const result = yield* runGit(input.directory, [
42907
- "diff",
42908
- "--no-ext-diff",
42909
- "--no-textconv",
42910
- "--no-renames",
42911
- "-z",
42912
- "--name-status",
42913
- "--relative",
42914
- input.ref
42915
- ]);
42916
- if (result.status !== 0) return null;
42917
- const plan = parseBaselineDiffPlan(result.stdout);
42918
- if (plan === null) return null;
42919
- const untracked = yield* runGit(input.directory, [
42920
- "ls-files",
42921
- "--others",
42922
- "--exclude-standard",
42923
- "-z"
42924
- ]);
42925
- if (untracked.status !== 0) return null;
42926
- return {
42927
- baseFiles: plan.baseFiles,
42928
- headFiles: plan.headFiles,
42929
- untrackedFiles: splitNullSeparated(untracked.stdout)
42930
- };
42931
- }).pipe(catch_$1(() => succeed$2(null)), withSpan("Git.baselineDiffPlan"));
42932
- },
42933
42539
  diffSelection: ({ directory, explicitBaseBranch, includeUntracked = false }) => gen(function* () {
42934
42540
  if (explicitBaseBranch !== void 0 && explicitBaseBranch.trim().length === 0) return yield* fail$4(new ReactDoctorError({ reason: new GitBaseBranchInvalid({ detail: "Diff base branch cannot be empty." }) }));
42935
42541
  if (explicitBaseBranch !== void 0) {
@@ -43079,7 +42685,6 @@ var Git = class Git extends Service()("react-doctor/Git") {
43079
42685
  githubViewerPermission: () => succeed$2(snapshot.githubViewerPermission ?? null),
43080
42686
  branchExists: (_directory, branch) => succeed$2(snapshot.branchExists?.get(branch) ?? false),
43081
42687
  mergeBase: ({ ref }) => succeed$2(snapshot.mergeBase?.get(ref) ?? null),
43082
- baselineDiffPlan: () => succeed$2(snapshot.baselineDiffPlan ?? null),
43083
42688
  diffSelection: () => succeed$2(snapshot.diffSelection ?? null),
43084
42689
  stagedFilePaths: () => succeed$2(snapshot.stagedFiles ?? []),
43085
42690
  showStagedContent: (_directory, relativePath) => succeed$2(snapshot.stagedContent?.get(relativePath) ?? null),
@@ -43448,9 +43053,8 @@ const buildUserPluginRules = (userPlugin, severityControls) => {
43448
43053
  }
43449
43054
  return enabled;
43450
43055
  };
43451
- const createOxlintConfig = ({ pluginPath, project, customRulesOnly = false, extendsPaths = [], ignoredTags = /* @__PURE__ */ new Set(), includedTags = /* @__PURE__ */ new Set(), includeTagDefaults = false, serverAuthFunctionNames, severityControls, userPlugins = [], disableReactHooksJsPlugin = false, ruleSelection, sidecarRuleIdFilter }) => {
43452
- const hasIncludedTags = includedTags.size > 0;
43453
- const reactHooksJsPlugin = disableReactHooksJsPlugin || ruleSelection === "sidecar" || hasIncludedTags ? null : resolveReactHooksJsPlugin(project.hasReactCompiler || project.hasReactCompilerLintPlugin === true, customRulesOnly);
43056
+ const createOxlintConfig = ({ pluginPath, project, customRulesOnly = false, extendsPaths = [], ignoredTags = /* @__PURE__ */ new Set(), serverAuthFunctionNames, severityControls, userPlugins = [], disableReactHooksJsPlugin = false, ruleSelection, sidecarRuleIdFilter }) => {
43057
+ const reactHooksJsPlugin = disableReactHooksJsPlugin || ruleSelection === "sidecar" ? null : resolveReactHooksJsPlugin(project.hasReactCompiler || project.hasReactCompilerLintPlugin === true, customRulesOnly);
43454
43058
  const reactCompilerRules = reactHooksJsPlugin ? applyRuleSeverityControls(filterRulesToAvailable(REACT_COMPILER_RULES, "react-hooks-js", reactHooksJsPlugin.availableRuleNames), severityControls) : {};
43455
43059
  const jsPlugins = [];
43456
43060
  if (reactHooksJsPlugin) jsPlugins.push(reactHooksJsPlugin.entry);
@@ -43465,14 +43069,9 @@ const createOxlintConfig = ({ pluginPath, project, customRulesOnly = false, exte
43465
43069
  if (rule.scan !== void 0) continue;
43466
43070
  if (customRulesOnly && registryEntry.originallyExternal) continue;
43467
43071
  if (rule.framework !== "global" && !rule.requires) continue;
43468
- if (!shouldEnableRule(rule.requires, rule.tags, capabilities, ignoredTags, rule.disabledWhen, includedTags)) continue;
43072
+ if (!shouldEnableRule(rule.requires, rule.tags, capabilities, ignoredTags, rule.disabledWhen)) continue;
43469
43073
  const explicitRuleOverride = resolveRuleSeverityOverride({ ruleKey: registryEntry.key }, severityControls);
43470
- if (!shouldEnableRuleByDefaultStatus({
43471
- defaultEnabled: rule.defaultEnabled,
43472
- includeTagDefaults,
43473
- hasIncludedTags,
43474
- hasExplicitOverride: explicitRuleOverride !== void 0
43475
- })) continue;
43074
+ if (rule.defaultEnabled === false && explicitRuleOverride === void 0) continue;
43476
43075
  const severity = resolveRuleSeverityOverride({
43477
43076
  ruleKey: registryEntry.key,
43478
43077
  category: rule.category
@@ -43481,7 +43080,7 @@ const createOxlintConfig = ({ pluginPath, project, customRulesOnly = false, exte
43481
43080
  enabledReactDoctorRules[registryEntry.key] = severity;
43482
43081
  }
43483
43082
  const userPluginRules = {};
43484
- if (ruleSelection !== "sidecar" && !hasIncludedTags) for (const userPlugin of userPlugins) {
43083
+ if (ruleSelection !== "sidecar") for (const userPlugin of userPlugins) {
43485
43084
  Object.assign(userPluginRules, buildUserPluginRules(userPlugin, severityControls));
43486
43085
  jsPlugins.push(userPlugin.entry);
43487
43086
  }
@@ -43698,19 +43297,6 @@ const createSidecarLintCache = (cacheDirectory, rulesetHash) => {
43698
43297
  }
43699
43298
  };
43700
43299
  };
43701
- const fingerprintCacheByFilePath = /* @__PURE__ */ new Map();
43702
- const fingerprintFileContents = (filePath, fingerprintLength) => {
43703
- const fileStats = NFS.statSync(filePath, { bigint: true });
43704
- const cachedFingerprint = fingerprintCacheByFilePath.get(filePath);
43705
- if (cachedFingerprint?.modifiedTimeNanoseconds === fileStats.mtimeNs && cachedFingerprint.sizeBytes === fileStats.size) return cachedFingerprint.fingerprint;
43706
- const fingerprint = crypto.createHash("sha256").update(NFS.readFileSync(filePath)).digest("hex").slice(0, fingerprintLength);
43707
- fingerprintCacheByFilePath.set(filePath, {
43708
- fingerprint,
43709
- modifiedTimeNanoseconds: fileStats.mtimeNs,
43710
- sizeBytes: fileStats.size
43711
- });
43712
- return fingerprint;
43713
- };
43714
43300
  const bundledRequire$3 = createRequire(import.meta.url);
43715
43301
  const childNodeVersionByBinaryPath = /* @__PURE__ */ new Map();
43716
43302
  const resolveChildNodeVersion = (nodeBinaryPath) => {
@@ -43734,13 +43320,6 @@ const TOOLCHAIN_PACKAGE_SPECIFIERS$1 = [
43734
43320
  "oxlint-plugin-react-doctor/package.json",
43735
43321
  "eslint-plugin-react-hooks/package.json"
43736
43322
  ];
43737
- const resolvePluginFingerprint = () => {
43738
- try {
43739
- return fingerprintFileContents(bundledRequire$3.resolve("oxlint-plugin-react-doctor"), 16);
43740
- } catch {
43741
- return "unresolved";
43742
- }
43743
- };
43744
43323
  const resolveOxlintToolchainVersions = (nodeBinaryPath = process.execPath) => {
43745
43324
  const versions = [`node=${resolveChildNodeVersion(nodeBinaryPath)}`];
43746
43325
  for (const specifier of TOOLCHAIN_PACKAGE_SPECIFIERS$1) try {
@@ -43750,7 +43329,6 @@ const resolveOxlintToolchainVersions = (nodeBinaryPath = process.execPath) => {
43750
43329
  } catch {
43751
43330
  versions.push(`${specifier}=missing`);
43752
43331
  }
43753
- versions.push(`oxlint-plugin-react-doctor#fingerprint=${resolvePluginFingerprint()}`);
43754
43332
  return versions;
43755
43333
  };
43756
43334
  const esmRequire = createRequire(import.meta.url);
@@ -44444,17 +44022,12 @@ const REACT_ERROR_BOUNDARY_MESSAGE = "This try/catch cannot catch errors thrown
44444
44022
  const REACT_COMPILER_IMPACT = "This component misses React Compiler's automatic memoization & re-renders more than it should";
44445
44023
  const REACT_COMPILER_ACTION = "Rewrite the flagged code so the compiler can optimize it.";
44446
44024
  const REACT_COMPILER_INCOMPATIBLE_LIBRARY_ACTION = "It's how the library works, not a bug in your code. Memoize values you pass from it into other memoized components, or suppress it with `// react-doctor-disable-next-line react-hooks-js/incompatible-library`.";
44447
- const SET_STATE_IN_EFFECT_ACTION = "Prefer deriving or initializing the value before render. If the effect must read a browser API after mount, treat this as advisory or suppress it with `// react-doctor-disable-next-line react-hooks-js/set-state-in-effect`.";
44448
44025
  const REACT_COMPILER_GENERIC_MESSAGE = `${REACT_COMPILER_IMPACT}. ${REACT_COMPILER_ACTION}`;
44449
44026
  const buildReactCompilerMessage = (reasonSummary, action = REACT_COMPILER_ACTION) => {
44450
44027
  const normalizedSummary = reasonSummary.replace(TRAILING_PERIOD_PATTERN, "");
44451
44028
  if (!normalizedSummary) return `${REACT_COMPILER_IMPACT}. ${action}`;
44452
44029
  return `${REACT_COMPILER_IMPACT}: ${normalizedSummary}. ${action}`;
44453
44030
  };
44454
- const buildSetStateInEffectMessage = (reasonSummary) => {
44455
- const normalizedSummary = reasonSummary.replace(TRAILING_PERIOD_PATTERN, "");
44456
- return `This synchronous effect update causes an extra render${normalizedSummary ? `: ${normalizedSummary}` : ""}. ${SET_STATE_IN_EFFECT_ACTION}`;
44457
- };
44458
44031
  const PLUGIN_CATEGORY_MAP = {
44459
44032
  react: "Bugs",
44460
44033
  "react-hooks": "Bugs",
@@ -44512,10 +44085,6 @@ const resolveCleanedDiagnostic = (message, help, plugin, rule, project) => {
44512
44085
  message: REACT_ERROR_BOUNDARY_MESSAGE,
44513
44086
  help: reasonDetail || help
44514
44087
  };
44515
- if (rule === "set-state-in-effect") return {
44516
- message: buildSetStateInEffectMessage(reasonSummary.trim()),
44517
- help: reasonDetail || help
44518
- };
44519
44088
  return {
44520
44089
  message: buildReactCompilerMessage(reasonSummary.trim(), rule === "incompatible-library" ? REACT_COMPILER_INCOMPATIBLE_LIBRARY_ACTION : REACT_COMPILER_ACTION),
44521
44090
  help: appendReanimatedSharedValueHint(reasonDetail || help, rule, project)
@@ -45226,7 +44795,7 @@ const reactHooksJsPluginDropNote = (error) => {
45226
44795
  * 6. always restore disable directives + clean up the temp dir
45227
44796
  */
45228
44797
  const runOxlint = async (options) => {
45229
- const { rootDirectory, project, includePaths, nodeBinaryPath = process.execPath, customRulesOnly = false, respectInlineDisables = true, adoptExistingLintConfig = true, ignoredTags = /* @__PURE__ */ new Set(), includedTags = /* @__PURE__ */ new Set(), includeTagDefaults = false, userConfig, configSourceDirectory = rootDirectory, onPartialFailure, onFileCoverage, perFileLintCacheEnabled = false, sidecarLintCacheEnabled = false, onCacheStats, onSidecarStats, spawnTimeoutMs, outputMaxBytes, lintBatchOrdering = "cost" } = options;
44798
+ const { rootDirectory, project, includePaths, nodeBinaryPath = process.execPath, customRulesOnly = false, respectInlineDisables = true, adoptExistingLintConfig = true, ignoredTags = /* @__PURE__ */ new Set(), userConfig, configSourceDirectory = rootDirectory, onPartialFailure, onFileCoverage, perFileLintCacheEnabled = false, sidecarLintCacheEnabled = false, onCacheStats, onSidecarStats, spawnTimeoutMs, outputMaxBytes, lintBatchOrdering = "cost" } = options;
45230
44799
  const serverAuthFunctionNames = Array.isArray(userConfig?.serverAuthFunctionNames) ? userConfig.serverAuthFunctionNames.filter((entry) => typeof entry === "string" && entry.length > 0) : void 0;
45231
44800
  const severityControls = buildRuleSeverityControls(userConfig);
45232
44801
  validateRuleRegistration();
@@ -45240,15 +44809,13 @@ const runOxlint = async (options) => {
45240
44809
  }
45241
44810
  const pluginPath = resolvePluginPath();
45242
44811
  const extendsPaths = (adoptExistingLintConfig && !customRulesOnly ? detectUserLintConfigPaths(rootDirectory) : []).filter(canOxlintExtendConfig);
45243
- const userPlugins = includedTags.size > 0 ? [] : resolveUserPlugins(userConfig?.plugins, configSourceDirectory);
44812
+ const userPlugins = resolveUserPlugins(userConfig?.plugins, configSourceDirectory);
45244
44813
  const buildConfig = (overrides) => createOxlintConfig({
45245
44814
  pluginPath,
45246
44815
  project,
45247
44816
  customRulesOnly,
45248
44817
  extendsPaths: overrides.extendsPaths,
45249
44818
  ignoredTags,
45250
- includedTags,
45251
- includeTagDefaults,
45252
44819
  serverAuthFunctionNames,
45253
44820
  severityControls,
45254
44821
  userPlugins,
@@ -45637,8 +45204,6 @@ var Linter = class Linter extends Service()("react-doctor/Linter") {
45637
45204
  respectInlineDisables: input.respectInlineDisables,
45638
45205
  adoptExistingLintConfig: input.adoptExistingLintConfig,
45639
45206
  ignoredTags: input.ignoredTags,
45640
- includedTags: input.includedTags,
45641
- includeTagDefaults: input.includeTagDefaults,
45642
45207
  userConfig: input.userConfig ?? null,
45643
45208
  configSourceDirectory: input.configSourceDirectory,
45644
45209
  onPartialFailure: (reason) => {
@@ -46487,9 +46052,7 @@ const runInspect = (input, hooks = {}) => gen(function* () {
46487
46052
  const securityScanFailedRef = yield* make$13(false);
46488
46053
  const securityScanFiber = yield* forkChild(runCollect(applyPerElementPipeline(isDiffMode ? empty$4 : unwrap(tryPromise(() => checkSecurityScanCooperative(scanDirectory, {
46489
46054
  project,
46490
- ignoredTags: input.ignoredTags,
46491
- includedTags: input.includedTags,
46492
- includeTagDefaults: input.includeTagDefaults
46055
+ ignoredTags: input.ignoredTags
46493
46056
  })).pipe(map$3((diagnostics) => fromIterable$1(diagnostics)), catch_$1(() => set(securityScanFailedRef, true).pipe(as(empty$4))))))).pipe(withSpan("SecurityScan.run")));
46494
46057
  const shouldRunSupplyChain = !isDiffMode || (input.supplyChainManifestChanged ?? false);
46495
46058
  const supplyChainOverlapTimeout = yield* SupplyChainOverlapTimeoutMs;
@@ -46580,8 +46143,6 @@ const runInspect = (input, hooks = {}) => gen(function* () {
46580
46143
  respectInlineDisables: input.respectInlineDisables,
46581
46144
  adoptExistingLintConfig: input.adoptExistingLintConfig,
46582
46145
  ignoredTags: input.ignoredTags,
46583
- includedTags: input.includedTags,
46584
- includeTagDefaults: input.includeTagDefaults,
46585
46146
  userConfig: resolvedConfig.config ?? void 0,
46586
46147
  configSourceDirectory: resolvedConfig.configSourceDirectory ?? void 0,
46587
46148
  onFileProgress: (scannedFileCount, totalFileCount) => {
@@ -46860,19 +46421,12 @@ const isPathInsideDirectory = (childAbsolutePath, parentAbsolutePath) => {
46860
46421
  */
46861
46422
  const materializeSourceTree = (input) => gen(function* () {
46862
46423
  const materializedFiles = [];
46863
- const unmaterializedFiles = [];
46864
46424
  const resolvedTempDirectory = path.resolve(input.tempDirectory);
46865
46425
  for (const relativePath of input.files) {
46866
46426
  const content = yield* input.readContent(relativePath).pipe(orElseSucceed(() => null));
46867
- if (content === null) {
46868
- unmaterializedFiles.push(relativePath);
46869
- continue;
46870
- }
46427
+ if (content === null) continue;
46871
46428
  const candidateTargetPath = path.resolve(resolvedTempDirectory, relativePath);
46872
- if (!isPathInsideDirectory(candidateTargetPath, resolvedTempDirectory)) {
46873
- unmaterializedFiles.push(relativePath);
46874
- continue;
46875
- }
46429
+ if (!isPathInsideDirectory(candidateTargetPath, resolvedTempDirectory)) continue;
46876
46430
  yield* sync(() => {
46877
46431
  fs.mkdirSync(path.dirname(candidateTargetPath), { recursive: true });
46878
46432
  fs.writeFileSync(candidateTargetPath, content);
@@ -46889,7 +46443,6 @@ const materializeSourceTree = (input) => gen(function* () {
46889
46443
  return {
46890
46444
  tempDirectory: input.tempDirectory,
46891
46445
  materializedFiles,
46892
- unmaterializedFiles,
46893
46446
  cleanup: () => {
46894
46447
  try {
46895
46448
  fs.rmSync(input.tempDirectory, {
@@ -47159,80 +46712,54 @@ const buildSkippedChecks = (input) => {
47159
46712
  skippedCheckReasons
47160
46713
  };
47161
46714
  };
47162
- const fingerprintText = (text) => createHash("sha256").update(text).digest("hex");
47163
- const normalizeEvidence = (evidence) => evidence.replace(/\s+/g, " ").trim();
47164
- const getDiagnosticMatchKeys = (diagnostic, evidence) => {
46715
+ const fingerprintDiagnostic = (diagnostic, lineText) => {
47165
46716
  const ruleKey = `${diagnostic.plugin}/${diagnostic.rule}`;
47166
- const messageFingerprint = fingerprintText(`${diagnostic.title ?? ""}\0${diagnostic.message}`);
47167
- const normalizedEvidence = evidence === null ? "" : normalizeEvidence(evidence);
47168
- const stableEvidenceKey = normalizedEvidence.length > 0 ? `evidence\0${ruleKey}\0${messageFingerprint}\0${fingerprintText(normalizedEvidence)}` : null;
47169
- return {
47170
- stableEvidenceKey,
47171
- sameFileStableEvidenceKey: stableEvidenceKey === null ? null : `${diagnostic.filePath}\0${stableEvidenceKey}`,
47172
- sameFileFallbackKey: diagnostic.matchByOccurrence || normalizedEvidence.length === 0 ? `fallback\0${diagnostic.filePath}\0${ruleKey}\0${messageFingerprint}` : null
47173
- };
47174
- };
47175
- const addDiagnosticIndex = (buckets, key, diagnosticIndex) => {
47176
- if (key === null) return;
47177
- const diagnosticIndexes = buckets.get(key) ?? [];
47178
- diagnosticIndexes.push(diagnosticIndex);
47179
- buckets.set(key, diagnosticIndexes);
47180
- };
47181
- const takeMatchingDiagnosticIndex = (buckets, key, matchedDiagnosticIndexes) => {
47182
- if (key === null) return null;
47183
- for (const diagnosticIndex of buckets.get(key) ?? []) if (!matchedDiagnosticIndexes.has(diagnosticIndex)) return diagnosticIndex;
47184
- return null;
47185
- };
47186
- const readDiagnosticEvidence = (diagnostic, readEvidence, readLine) => readEvidence?.(diagnostic) ?? readLine(diagnostic.filePath, diagnostic.line);
47187
- const buildMatchCandidates = (diagnostics, readEvidence, readLine) => diagnostics.map((diagnostic, diagnosticIndex) => ({
47188
- diagnosticIndex,
47189
- ...getDiagnosticMatchKeys(diagnostic, readDiagnosticEvidence(diagnostic, readEvidence, readLine))
47190
- }));
47191
- /**
47192
- * Diffs a head scan against a base scan using a multiset of construct-level
47193
- * evidence. Stable identities combine plugin/rule, the diagnostic message,
47194
- * and normalized diagnosed source, so unchanged findings can move across
47195
- * files while changed constructs or messages remain new. Cardinality is
47196
- * retained for identical findings. Diagnostics explicitly marked
47197
- * `matchByOccurrence` may fall back to same-file plugin/rule/message matching
47198
- * after same-file strict evidence matching. Cross-file evidence matching runs
47199
- * last so a copy cannot consume a reformatted local occurrence. Unreadable
47200
- * evidence uses the same conservative fallback rather than matching across
47201
- * files without proof.
46717
+ const snippet = lineText === null || diagnostic.matchByOccurrence ? "" : createHash("sha1").update(lineText.trim()).digest("hex");
46718
+ return `${diagnostic.filePath}\u0000${ruleKey}\u0000${snippet}`;
46719
+ };
46720
+ /**
46721
+ * Diffs a head scan against a base scan to isolate the diagnostics a change
46722
+ * introduced (and count the ones it resolved). Matching is a multiset over a
46723
+ * position-independent fingerprint — `(filePath, plugin/rule, hash(flagged
46724
+ * line text))` — so inserting lines above an existing issue doesn't make it
46725
+ * look new, while a genuinely new occurrence (new line text, or one more of
46726
+ * the same) surfaces. Identical repeated findings are matched by count.
46727
+ *
46728
+ * Diagnostics carrying `matchByOccurrence` (resolved at diagnostic creation:
46729
+ * every Accessibility-category finding, plus rules opting in via their
46730
+ * `matchByOccurrence` metadata flag) drop the line-text snippet and match by
46731
+ * `(filePath, plugin/rule)` occurrence count alone. Their identity is the
46732
+ * flagged element, not the line's text, so editing the line (reindentation,
46733
+ * prettier reflow, collapsing a multi-line JSX element) doesn't reclassify a
46734
+ * pre-existing finding as new — while one MORE occurrence of the same rule in
46735
+ * the file still surfaces. Expression-level rules keep the line-text snippet:
46736
+ * there the flagged expression IS the finding, so changed text means new +
46737
+ * fixed.
46738
+ *
46739
+ * v1 limitation: the fingerprint keys on the head-relative `filePath`, and base
46740
+ * content is read at that same path. A file renamed by the change therefore has
46741
+ * no base match, so its pre-existing findings are reported as new. This
46742
+ * over-reports (never hides a real issue) and is rare; rename-aware base
46743
+ * resolution is a follow-up.
47202
46744
  */
47203
46745
  const computeDiagnosticDelta = (input) => {
47204
- const baseCandidates = buildMatchCandidates(input.baseDiagnostics, input.readBaseEvidence, input.readBaseLine);
47205
- const headCandidates = buildMatchCandidates(input.headDiagnostics, input.readHeadEvidence, input.readHeadLine);
47206
- const baseByStableEvidence = /* @__PURE__ */ new Map();
47207
- const baseBySameFileStableEvidence = /* @__PURE__ */ new Map();
47208
- const baseBySameFileFallback = /* @__PURE__ */ new Map();
47209
- for (const candidate of baseCandidates) {
47210
- addDiagnosticIndex(baseByStableEvidence, candidate.stableEvidenceKey, candidate.diagnosticIndex);
47211
- addDiagnosticIndex(baseBySameFileStableEvidence, candidate.sameFileStableEvidenceKey, candidate.diagnosticIndex);
47212
- addDiagnosticIndex(baseBySameFileFallback, candidate.sameFileFallbackKey, candidate.diagnosticIndex);
47213
- }
47214
- const matchedHeadDiagnosticIndexes = /* @__PURE__ */ new Set();
47215
- const matchedBaseDiagnosticIndexes = /* @__PURE__ */ new Set();
47216
- const matchCandidates = (baseBuckets, getKey, onMatch) => {
47217
- for (const candidate of headCandidates) {
47218
- if (matchedHeadDiagnosticIndexes.has(candidate.diagnosticIndex)) continue;
47219
- const baseDiagnosticIndex = takeMatchingDiagnosticIndex(baseBuckets, getKey(candidate), matchedBaseDiagnosticIndexes);
47220
- if (baseDiagnosticIndex === null) continue;
47221
- matchedHeadDiagnosticIndexes.add(candidate.diagnosticIndex);
47222
- matchedBaseDiagnosticIndexes.add(baseDiagnosticIndex);
47223
- onMatch?.(candidate.diagnosticIndex, baseDiagnosticIndex);
47224
- }
47225
- };
47226
- matchCandidates(baseBySameFileStableEvidence, (candidate) => candidate.sameFileStableEvidenceKey);
47227
- matchCandidates(baseBySameFileFallback, (candidate) => candidate.sameFileFallbackKey);
47228
- let crossFileMatchCount = 0;
47229
- matchCandidates(baseByStableEvidence, (candidate) => candidate.stableEvidenceKey, (head, base) => {
47230
- if (input.headDiagnostics[head]?.filePath !== input.baseDiagnostics[base]?.filePath) crossFileMatchCount += 1;
47231
- });
46746
+ const unmatchedBaseByFingerprint = /* @__PURE__ */ new Map();
46747
+ for (const diagnostic of input.baseDiagnostics) {
46748
+ const key = fingerprintDiagnostic(diagnostic, input.readBaseLine(diagnostic.filePath, diagnostic.line));
46749
+ unmatchedBaseByFingerprint.set(key, (unmatchedBaseByFingerprint.get(key) ?? 0) + 1);
46750
+ }
46751
+ const newDiagnostics = [];
46752
+ for (const diagnostic of input.headDiagnostics) {
46753
+ const key = fingerprintDiagnostic(diagnostic, input.readHeadLine(diagnostic.filePath, diagnostic.line));
46754
+ const availableMatches = unmatchedBaseByFingerprint.get(key) ?? 0;
46755
+ if (availableMatches > 0) unmatchedBaseByFingerprint.set(key, availableMatches - 1);
46756
+ else newDiagnostics.push(diagnostic);
46757
+ }
46758
+ let fixedCount = 0;
46759
+ for (const remaining of unmatchedBaseByFingerprint.values()) fixedCount += remaining;
47232
46760
  return {
47233
- newDiagnostics: input.headDiagnostics.filter((_diagnostic, diagnosticIndex) => !matchedHeadDiagnosticIndexes.has(diagnosticIndex)),
47234
- fixedCount: input.baseDiagnostics.length - matchedBaseDiagnosticIndexes.size,
47235
- crossFileMatchCount
46761
+ newDiagnostics,
46762
+ fixedCount
47236
46763
  };
47237
46764
  };
47238
46765
  /**
@@ -47264,12 +46791,6 @@ const getDiffInfo = (directory, explicitBaseBranch, includeUntracked) => runProm
47264
46791
  };
47265
46792
  }).pipe(provide(Git.layerNode)));
47266
46793
  const filterSourceFiles = (filePaths) => filePaths.filter(isLintableSourceFile);
47267
- const getBaselineDiffPlan = (directory, ref) => runPromise(gen(function* () {
47268
- return yield* (yield* Git).baselineDiffPlan({
47269
- directory,
47270
- ref
47271
- });
47272
- }).pipe(provide(Git.layerNode)));
47273
46794
  /**
47274
46795
  * Programmatic façade over `Git.changedLineRanges` (the `lines` scope). Diffs
47275
46796
  * `files` with `--unified=0` against `baseRef` (or the index when `cached`),
@@ -47726,7 +47247,7 @@ const makeNoopConsole = () => ({
47726
47247
  });
47727
47248
  //#endregion
47728
47249
  //#region src/cli/utils/version.ts
47729
- const VERSION = "0.8.1-dev.fc1b3a3";
47250
+ const VERSION = "0.8.1";
47730
47251
  //#endregion
47731
47252
  //#region src/cli/utils/json-mode.ts
47732
47253
  let context = null;
@@ -47813,11 +47334,7 @@ const getRunId = () => {
47813
47334
  };
47814
47335
  //#endregion
47815
47336
  //#region src/cli/utils/build-run-context.ts
47816
- const ROOT_SUBCOMMANDS = new Set([
47817
- "design",
47818
- "install",
47819
- "setup"
47820
- ]);
47337
+ const ROOT_SUBCOMMANDS = new Set(["install", "setup"]);
47821
47338
  const detectInvokedVia = () => {
47822
47339
  const userAgent = process.env.npm_config_user_agent;
47823
47340
  if (!userAgent) return "unknown";
@@ -47901,9 +47418,7 @@ const buildSentryProjectContext = (projectInfo) => ({
47901
47418
  reactMajorVersion: projectInfo.reactMajorVersion,
47902
47419
  hasTypeScript: projectInfo.hasTypeScript,
47903
47420
  hasReactCompiler: projectInfo.hasReactCompiler,
47904
- tanstackQueryVersion: projectInfo.tanstackQueryVersion,
47905
- mobxVersion: projectInfo.mobxVersion,
47906
- styledComponentsVersion: projectInfo.styledComponentsVersion,
47421
+ hasTanStackQuery: projectInfo.hasTanStackQuery,
47907
47422
  tailwindVersion: projectInfo.tailwindVersion,
47908
47423
  zodVersion: projectInfo.zodVersion,
47909
47424
  preactVersion: projectInfo.preactVersion,
@@ -48084,13 +47599,13 @@ const isDevVersion = (version) => version === "0.0.0" || version.includes("-");
48084
47599
  * uploads source-map artifacts under, so stack frames symbolicate. Honors the
48085
47600
  * standard `SENTRY_RELEASE` override.
48086
47601
  */
48087
- const resolveSentryRelease = () => process.env.SENTRY_RELEASE || `react-doctor@0.8.1-dev.fc1b3a3`;
47602
+ const resolveSentryRelease = () => process.env.SENTRY_RELEASE || `react-doctor@0.8.1`;
48088
47603
  /**
48089
47604
  * Deployment environment shown in Sentry's environment filter. Defaults to
48090
47605
  * `production` for tagged releases and `development` for dev/unbuilt versions,
48091
47606
  * overridable via the standard `SENTRY_ENVIRONMENT` env var.
48092
47607
  */
48093
- const resolveSentryEnvironment = () => process.env.SENTRY_ENVIRONMENT || (isDevVersion("0.8.1-dev.fc1b3a3") ? "development" : "production");
47608
+ const resolveSentryEnvironment = () => process.env.SENTRY_ENVIRONMENT || (isDevVersion("0.8.1") ? "development" : "production");
48094
47609
  /**
48095
47610
  * Performance-tracing sample rate in `[0, 1]`. Reads `SENTRY_TRACES_SAMPLE_RATE`
48096
47611
  * (set to `0` to disable tracing) and falls back to
@@ -60416,8 +59931,6 @@ const buildScanResultCacheKey = (input) => {
60416
59931
  warnings: input.options.warnings,
60417
59932
  adoptExistingLintConfig: input.options.adoptExistingLintConfig,
60418
59933
  ignoredTags: [...input.options.ignoredTags].sort(),
60419
- includedTags: [...input.options.includedTags].sort(),
60420
- includeTagDefaults: input.options.includeTagDefaults,
60421
59934
  concurrency: input.options.concurrency,
60422
59935
  lintBatchOrdering: resolveLintBatchOrdering(),
60423
59936
  baselineRef: input.options.baseline?.ref,
@@ -60715,7 +60228,6 @@ const buildOutcomeAttributes = (input) => {
60715
60228
  new: summary.totalDiagnosticCount,
60716
60229
  fixed: result.baselineDelta.fixedCount,
60717
60230
  baseTotal: result.baselineDelta.baseTotalCount,
60718
- crossFileMatches: result.baselineDelta.crossFileMatchCount ?? null,
60719
60231
  degraded: false
60720
60232
  }));
60721
60233
  else if (input.gateExempt) attributes["baseline.degraded"] = true;
@@ -60829,49 +60341,28 @@ const diagnosticIntersectsLineRanges = (diagnostic, lineRanges) => {
60829
60341
  return lineRanges.some(([rangeStartLine, rangeEndLine]) => diagnostic.line <= rangeEndLine && diagnosticEndLine >= rangeStartLine);
60830
60342
  };
60831
60343
  //#endregion
60832
- //#region src/cli/utils/is-materializable-git-source.ts
60833
- const GIT_LFS_POINTER_HEADER = "version https://git-lfs.github.com/spec/v1";
60834
- const isMaterializableGitSource = (content) => !content.includes("\0") && !content.startsWith(GIT_LFS_POINTER_HEADER);
60835
- //#endregion
60836
60344
  //#region src/cli/utils/materialize-baseline-files.ts
60837
60345
  /**
60838
- * Materializes the base side of the `ref` → worktree diff into
60839
- * `tempDirectory`, mirroring the project layout plus head's config files so
60840
- * both sides lint under the same rules. Git rename heuristics are disabled by
60841
- * the diff planner, so an old path is retained as a base deletion while its
60842
- * new path is a head addition. Missing head-only additions are expected;
60843
- * missing paths that the plan says must exist at base make `isComplete` false.
60346
+ * Materializes the `ref` version of `files` into `tempDirectory` (via
60347
+ * `git show <ref>:<path>`), mirroring the project layout + head's config files
60348
+ * so the base lints under the same rules as head. Reuses the shared,
60349
+ * zip-slip-guarded `materializeSourceTree`. Per-file read misses (e.g. a file
60350
+ * the PR added, absent at base) are skipped, which is exactly what we want:
60351
+ * a brand-new file has no base counterpart, so all its findings are "new".
60844
60352
  */
60845
60353
  const materializeBaselineFiles = (input) => runPromise(gen(function* () {
60846
60354
  const git = yield* Git;
60847
- const diffPlan = yield* git.baselineDiffPlan({
60355
+ return yield* materializeSourceTree({
60848
60356
  directory: input.directory,
60849
- ref: input.ref
60850
- });
60851
- const baseFiles = input.baseFiles ?? diffPlan?.baseFiles;
60852
- const headFiles = input.headFiles ?? diffPlan?.headFiles;
60853
- const untrackedFiles = diffPlan?.untrackedFiles ?? [];
60854
- if (baseFiles === void 0 || headFiles === void 0) return null;
60855
- const files = [...new Set([...input.files, ...baseFiles])];
60856
- const tree = yield* materializeSourceTree({
60857
- directory: input.directory,
60858
- files,
60357
+ files: input.files,
60859
60358
  tempDirectory: input.tempDirectory,
60860
60359
  readContent: (relativePath) => git.showRefContent({
60861
60360
  directory: input.directory,
60862
60361
  ref: input.ref,
60863
60362
  relativePath,
60864
60363
  options: { maxBufferBytes: GIT_SHOW_MAX_BUFFER_BYTES }
60865
- }).pipe(map$3((content) => content !== null && isMaterializableGitSource(content) ? content : null))
60364
+ })
60866
60365
  });
60867
- const materializedFiles = new Set(tree.materializedFiles);
60868
- return {
60869
- ...tree,
60870
- baseFiles,
60871
- headFiles,
60872
- isComplete: baseFiles.every((filePath) => materializedFiles.has(filePath)),
60873
- untrackedFiles
60874
- };
60875
60366
  }).pipe(provide(Git.layerNode)));
60876
60367
  /**
60877
60368
  * Resolves the commit a baseline scan should read base content from — the
@@ -60894,8 +60385,8 @@ const resolveMergeBaseRef = async (directory, ref) => {
60894
60385
  //#region src/cli/utils/read-source-line.ts
60895
60386
  /**
60896
60387
  * Builds a cached `(filePath, line) -> source text` reader rooted at
60897
- * `rootDirectory`, used as the fallback when construct-level diagnostic
60898
- * evidence cannot be read. Wraps core's
60388
+ * `rootDirectory`, used to fingerprint a diagnostic by the content of its
60389
+ * flagged line (see `computeDiagnosticDelta`). Wraps core's
60899
60390
  * `createNodeReadFileLinesSync` with per-file memoization + 1-indexed line
60900
60391
  * lookup; unreadable files / out-of-range lines return `null`.
60901
60392
  */
@@ -60910,342 +60401,10 @@ const createSourceLineReader = (rootDirectory) => {
60910
60401
  };
60911
60402
  };
60912
60403
  //#endregion
60913
- //#region src/cli/utils/read-diagnostic-evidence.ts
60914
- const SOURCE_EXTENSIONS = [
60915
- ".ts",
60916
- ".tsx",
60917
- ".js",
60918
- ".jsx",
60919
- ".mts",
60920
- ".cts",
60921
- ".mjs",
60922
- ".cjs"
60923
- ];
60924
- const COMPONENT_WRAPPER_NAMES = new Set(["memo", "forwardRef"]);
60925
- const getScriptKind = (filePath) => {
60926
- const extension = path.extname(filePath).toLowerCase();
60927
- if (extension === ".tsx") return ts.ScriptKind.TSX;
60928
- if (extension === ".jsx") return ts.ScriptKind.JSX;
60929
- if (extension === ".js" || extension === ".mjs" || extension === ".cjs") return ts.ScriptKind.JS;
60930
- return ts.ScriptKind.TS;
60931
- };
60932
- const resolveSafeSourcePath = (rootDirectory, filePath) => {
60933
- try {
60934
- const resolvedRootDirectory = fs.realpathSync(rootDirectory);
60935
- const absolutePath = path.resolve(resolvedRootDirectory, filePath);
60936
- const relativePath = path.relative(resolvedRootDirectory, absolutePath);
60937
- if (relativePath.length === 0 || relativePath === ".." || relativePath.startsWith(`..${path.sep}`) || path.isAbsolute(relativePath)) return null;
60938
- const stats = fs.lstatSync(absolutePath);
60939
- if (!stats.isFile() || stats.isSymbolicLink()) return null;
60940
- const realPath = fs.realpathSync(absolutePath);
60941
- const realRelativePath = path.relative(resolvedRootDirectory, realPath);
60942
- return realRelativePath !== ".." && !realRelativePath.startsWith(`..${path.sep}`) && !path.isAbsolute(realRelativePath) ? realPath : null;
60943
- } catch {
60944
- return null;
60945
- }
60946
- };
60947
- const readSourceRecord = (rootDirectory, filePath, state) => {
60948
- const normalizedFilePath = filePath.replace(/\\/g, "/");
60949
- if (state.recordByFilePath.has(normalizedFilePath)) return state.recordByFilePath.get(normalizedFilePath) ?? null;
60950
- const absolutePath = resolveSafeSourcePath(rootDirectory, normalizedFilePath);
60951
- if (absolutePath === null) {
60952
- state.recordByFilePath.set(normalizedFilePath, null);
60953
- return null;
60954
- }
60955
- try {
60956
- const sourceText = fs.readFileSync(absolutePath, "utf-8");
60957
- const record = {
60958
- absolutePath,
60959
- filePath: normalizedFilePath,
60960
- sourceFile: ts.createSourceFile(absolutePath, sourceText, ts.ScriptTarget.Latest, true, getScriptKind(absolutePath)),
60961
- sourceText
60962
- };
60963
- state.recordByFilePath.set(normalizedFilePath, record);
60964
- return record;
60965
- } catch {
60966
- state.recordByFilePath.set(normalizedFilePath, null);
60967
- return null;
60968
- }
60969
- };
60970
- const listSourceRecords = (rootDirectory, state) => {
60971
- if (state.sourceRecords !== null) return state.sourceRecords;
60972
- state.sourceRecords = listSourceFiles(rootDirectory).flatMap((filePath) => {
60973
- const record = readSourceRecord(rootDirectory, filePath, state);
60974
- return record === null ? [] : [record];
60975
- });
60976
- return state.sourceRecords;
60977
- };
60978
- const unwrapExpression = (expression) => {
60979
- let currentExpression = expression;
60980
- while (ts.isParenthesizedExpression(currentExpression) || ts.isAsExpression(currentExpression) || ts.isTypeAssertionExpression(currentExpression) || ts.isNonNullExpression(currentExpression) || ts.isSatisfiesExpression(currentExpression)) currentExpression = currentExpression.expression;
60981
- return currentExpression;
60982
- };
60983
- const getCalleeName = (expression) => {
60984
- const unwrappedExpression = unwrapExpression(expression);
60985
- if (ts.isIdentifier(unwrappedExpression)) return unwrappedExpression.text;
60986
- if (ts.isPropertyAccessExpression(unwrappedExpression)) return unwrappedExpression.name.text;
60987
- return null;
60988
- };
60989
- const isComponentWrapperCall = (node) => {
60990
- const wrapperName = getCalleeName(node.expression);
60991
- return wrapperName !== null && COMPONENT_WRAPPER_NAMES.has(wrapperName);
60992
- };
60993
- const getFunctionName = (node) => {
60994
- if (ts.isFunctionDeclaration(node) && node.name !== void 0) return node.name.text;
60995
- if (ts.isArrowFunction(node) || ts.isFunctionExpression(node)) {
60996
- let componentExpression = node;
60997
- while (true) {
60998
- const parent = componentExpression.parent;
60999
- if (ts.isParenthesizedExpression(parent) || ts.isAsExpression(parent) || ts.isTypeAssertionExpression(parent) || ts.isNonNullExpression(parent) || ts.isSatisfiesExpression(parent)) {
61000
- componentExpression = parent;
61001
- continue;
61002
- }
61003
- if (ts.isCallExpression(parent) && parent.arguments[0] === componentExpression && isComponentWrapperCall(parent)) {
61004
- componentExpression = parent;
61005
- continue;
61006
- }
61007
- if (ts.isVariableDeclaration(parent) && ts.isIdentifier(parent.name)) return parent.name.text;
61008
- return node.name?.text ?? null;
61009
- }
61010
- }
61011
- return null;
61012
- };
61013
- const isComponentFunction = (node) => ts.isFunctionDeclaration(node) || ts.isFunctionExpression(node) || ts.isArrowFunction(node);
61014
- const isDefaultExportedComponent = (record, node, componentName) => {
61015
- if (ts.canHaveModifiers(node) && ts.getModifiers(node)?.some((modifier) => modifier.kind === ts.SyntaxKind.DefaultKeyword)) return true;
61016
- return record.sourceFile.statements.some((statement) => ts.isExportAssignment(statement) && ts.isIdentifier(statement.expression) && statement.expression.text === componentName);
61017
- };
61018
- const findEnclosingComponent = (record, diagnostic) => {
61019
- if (!Number.isInteger(diagnostic.line) || diagnostic.line < 1) return null;
61020
- const lineIndex = diagnostic.line - 1;
61021
- const lineStarts = record.sourceFile.getLineStarts();
61022
- const lineStart = lineStarts[lineIndex];
61023
- if (lineStart === void 0) return null;
61024
- const nextLineStart = lineStarts[lineIndex + 1] ?? record.sourceFile.end;
61025
- const lineEnd = Math.max(lineStart, nextLineStart - 1);
61026
- const columnIndex = Number.isInteger(diagnostic.column) ? Math.max(0, diagnostic.column - 1) : 0;
61027
- const diagnosticPosition = Math.min(lineStart + columnIndex, lineEnd);
61028
- let component = null;
61029
- let componentWidth = Number.POSITIVE_INFINITY;
61030
- const visit = (node) => {
61031
- if (diagnosticPosition < node.getStart(record.sourceFile) || diagnosticPosition > node.end) return;
61032
- if (isComponentFunction(node)) {
61033
- const name = getFunctionName(node);
61034
- const width = node.end - node.getStart(record.sourceFile);
61035
- if (name !== null && /^[A-Z]/.test(name) && width < componentWidth) {
61036
- component = {
61037
- isDefaultExport: isDefaultExportedComponent(record, node, name),
61038
- name,
61039
- node,
61040
- record
61041
- };
61042
- componentWidth = width;
61043
- }
61044
- }
61045
- ts.forEachChild(node, visit);
61046
- };
61047
- visit(record.sourceFile);
61048
- return component;
61049
- };
61050
- const moduleSpecifierTargetsComponent = (callerRecord, moduleSpecifier, componentRecord) => {
61051
- if (!moduleSpecifier.startsWith(".")) return false;
61052
- const unresolvedPath = path.resolve(path.dirname(callerRecord.absolutePath), moduleSpecifier);
61053
- const specifiedExtension = path.extname(unresolvedPath).toLowerCase();
61054
- const sourcePathWithoutExtension = SOURCE_EXTENSIONS.includes(specifiedExtension) ? unresolvedPath.slice(0, -specifiedExtension.length) : unresolvedPath;
61055
- return [
61056
- unresolvedPath,
61057
- ...SOURCE_EXTENSIONS.map((extension) => `${sourcePathWithoutExtension}${extension}`),
61058
- ...sourcePathWithoutExtension === unresolvedPath ? SOURCE_EXTENSIONS.map((extension) => path.join(unresolvedPath, `index${extension}`)) : []
61059
- ].includes(componentRecord.absolutePath);
61060
- };
61061
- const getComponentTagNames = (record, component) => {
61062
- if (record.absolutePath === component.record.absolutePath) return new Set([component.name]);
61063
- const tagNames = /* @__PURE__ */ new Set();
61064
- for (const statement of record.sourceFile.statements) {
61065
- if (!ts.isImportDeclaration(statement) || !ts.isStringLiteral(statement.moduleSpecifier) || !moduleSpecifierTargetsComponent(record, statement.moduleSpecifier.text, component.record)) continue;
61066
- const importClause = statement.importClause;
61067
- if (component.isDefaultExport && importClause?.name !== void 0) tagNames.add(importClause.name.text);
61068
- const namedBindings = importClause?.namedBindings;
61069
- if (namedBindings !== void 0 && ts.isNamedImports(namedBindings)) {
61070
- for (const element of namedBindings.elements) if ((element.propertyName?.text ?? element.name.text) === component.name) tagNames.add(element.name.text);
61071
- }
61072
- }
61073
- return tagNames;
61074
- };
61075
- const getTransparentCall = (functionNode) => {
61076
- if (functionNode.body === void 0) return null;
61077
- if (!ts.isBlock(functionNode.body)) {
61078
- const expression = unwrapExpression(functionNode.body);
61079
- return ts.isCallExpression(expression) ? expression : null;
61080
- }
61081
- if (functionNode.body.statements.length !== 1) return null;
61082
- const statement = functionNode.body.statements[0];
61083
- if (statement === void 0) return null;
61084
- const expression = ts.isExpressionStatement(statement) || ts.isReturnStatement(statement) ? statement.expression : void 0;
61085
- if (expression === void 0) return null;
61086
- const unwrappedExpression = unwrapExpression(expression);
61087
- return ts.isCallExpression(unwrappedExpression) ? unwrappedExpression : null;
61088
- };
61089
- const resolveTransparentTarget = (functionNode) => {
61090
- const call = getTransparentCall(functionNode);
61091
- if (call === null) return null;
61092
- const target = unwrapExpression(call.expression);
61093
- if (!ts.isIdentifier(target) || call.arguments.length !== functionNode.parameters.length) return null;
61094
- for (const [parameterIndex, parameter] of functionNode.parameters.entries()) {
61095
- const argument = call.arguments[parameterIndex];
61096
- const unwrappedArgument = argument === void 0 ? null : unwrapExpression(argument);
61097
- if (!ts.isIdentifier(parameter.name) || unwrappedArgument === null || !ts.isIdentifier(unwrappedArgument) || unwrappedArgument.text !== parameter.name.text) return null;
61098
- }
61099
- return target.text;
61100
- };
61101
- const getForwardingFunction = (declaration) => {
61102
- if (ts.isFunctionDeclaration(declaration)) return declaration;
61103
- if (!ts.isVariableDeclaration(declaration) || declaration.initializer === void 0) return null;
61104
- const initializer = unwrapExpression(declaration.initializer);
61105
- if (ts.isArrowFunction(initializer) || ts.isFunctionExpression(initializer)) return initializer;
61106
- if (ts.isCallExpression(initializer) && getCalleeName(initializer.expression) === "useCallback") {
61107
- const callback = initializer.arguments[0];
61108
- if (callback !== void 0 && (ts.isArrowFunction(callback) || ts.isFunctionExpression(callback))) return callback;
61109
- }
61110
- return null;
61111
- };
61112
- const getDeclaredBindingName = (node) => {
61113
- if ((ts.isVariableDeclaration(node) || ts.isParameter(node) || ts.isBindingElement(node) || ts.isFunctionDeclaration(node)) && node.name !== void 0 && ts.isIdentifier(node.name)) return node.name.text;
61114
- if (ts.isImportClause(node)) return node.name?.text ?? null;
61115
- if (ts.isImportSpecifier(node) || ts.isNamespaceImport(node)) return node.name.text;
61116
- return null;
61117
- };
61118
- const findBindingScope = (node) => {
61119
- let currentNode = node;
61120
- while (currentNode !== void 0) {
61121
- if (ts.isParameter(currentNode) && ts.isFunctionLike(currentNode.parent)) return currentNode.parent;
61122
- if (ts.isBlock(currentNode) || ts.isModuleBlock(currentNode) || ts.isSourceFile(currentNode)) return currentNode;
61123
- currentNode = currentNode.parent;
61124
- }
61125
- return node.getSourceFile();
61126
- };
61127
- const findBindingDeclaration = (record, binding) => {
61128
- const usagePosition = binding.getStart(record.sourceFile);
61129
- const candidates = [];
61130
- const visit = (node) => {
61131
- if (getDeclaredBindingName(node) === binding.text) {
61132
- const scope = findBindingScope(node);
61133
- const isVisibleScope = usagePosition >= scope.getStart(record.sourceFile) && usagePosition <= scope.end;
61134
- const isVisibleDeclaration = ts.isFunctionDeclaration(node) || ts.isParameter(node) || ts.isBindingElement(node) || ts.isImportClause(node) || ts.isImportSpecifier(node) || ts.isNamespaceImport(node) || node.getStart(record.sourceFile) < usagePosition;
61135
- if (isVisibleScope && isVisibleDeclaration) candidates.push({
61136
- declaration: node,
61137
- scope
61138
- });
61139
- }
61140
- ts.forEachChild(node, visit);
61141
- };
61142
- visit(record.sourceFile);
61143
- let nearestCandidate = null;
61144
- let nearestScopeWidth = Number.POSITIVE_INFINITY;
61145
- let isNearestScopeAmbiguous = false;
61146
- for (const candidate of candidates) {
61147
- const scopeWidth = candidate.scope.end - candidate.scope.getStart(record.sourceFile);
61148
- if (scopeWidth < nearestScopeWidth) {
61149
- nearestCandidate = candidate;
61150
- nearestScopeWidth = scopeWidth;
61151
- isNearestScopeAmbiguous = false;
61152
- } else if (scopeWidth === nearestScopeWidth) isNearestScopeAmbiguous = true;
61153
- }
61154
- if (isNearestScopeAmbiguous) return null;
61155
- const declaration = nearestCandidate?.declaration;
61156
- return declaration !== void 0 && (ts.isVariableDeclaration(declaration) || ts.isFunctionDeclaration(declaration)) ? declaration : null;
61157
- };
61158
- const resolveHandlerBinding = (record, binding) => {
61159
- const declaration = findBindingDeclaration(record, binding);
61160
- if (declaration === null) return binding.text;
61161
- const forwardingFunction = getForwardingFunction(declaration);
61162
- const target = forwardingFunction === null ? null : resolveTransparentTarget(forwardingFunction);
61163
- return target === null || target === binding.text ? binding.text : target;
61164
- };
61165
- const getJsxTagName = (node) => ts.isIdentifier(node.tagName) ? node.tagName.text : null;
61166
- const getHandlerBinding = (node, propName) => {
61167
- for (const property of node.attributes.properties) {
61168
- if (!ts.isJsxAttribute(property) || property.name.getText() !== propName) continue;
61169
- const initializer = property.initializer;
61170
- if (initializer === void 0 || !ts.isJsxExpression(initializer)) return null;
61171
- if (initializer.expression === void 0) return null;
61172
- const expression = unwrapExpression(initializer.expression);
61173
- return ts.isIdentifier(expression) ? expression : null;
61174
- }
61175
- return null;
61176
- };
61177
- const getForwardedHandlerAliases = (rootDirectory, component, propNames, state) => {
61178
- const cacheKey = `${component.record.filePath}\0${component.name}\0${[...propNames].sort().join("\0")}`;
61179
- const cachedAliases = state.aliasesByComponent.get(cacheKey);
61180
- if (cachedAliases !== void 0) return cachedAliases;
61181
- const bindingsByPropName = /* @__PURE__ */ new Map();
61182
- const unresolvedPropNames = /* @__PURE__ */ new Set();
61183
- let callsiteCount = 0;
61184
- for (const record of listSourceRecords(rootDirectory, state)) {
61185
- const tagNames = getComponentTagNames(record, component);
61186
- if (tagNames.size === 0) continue;
61187
- const visit = (node) => {
61188
- if (record === component.record && node === component.node) return;
61189
- if (ts.isJsxOpeningElement(node) || ts.isJsxSelfClosingElement(node)) {
61190
- const tagName = getJsxTagName(node);
61191
- if (tagName !== null && tagNames.has(tagName)) {
61192
- callsiteCount += 1;
61193
- for (const propName of propNames) {
61194
- const binding = getHandlerBinding(node, propName);
61195
- if (binding === null) {
61196
- unresolvedPropNames.add(propName);
61197
- continue;
61198
- }
61199
- const bindings = bindingsByPropName.get(propName) ?? /* @__PURE__ */ new Set();
61200
- bindings.add(resolveHandlerBinding(record, binding));
61201
- bindingsByPropName.set(propName, bindings);
61202
- }
61203
- }
61204
- }
61205
- ts.forEachChild(node, visit);
61206
- };
61207
- visit(record.sourceFile);
61208
- }
61209
- const aliases = /* @__PURE__ */ new Map();
61210
- if (callsiteCount > 0) for (const [propName, bindings] of bindingsByPropName) {
61211
- if (bindings.size !== 1 || unresolvedPropNames.has(propName)) continue;
61212
- const [bindingName] = bindings;
61213
- if (bindingName !== void 0) aliases.set(propName, bindingName);
61214
- }
61215
- state.aliasesByComponent.set(cacheKey, aliases);
61216
- return aliases;
61217
- };
61218
- const normalizeForwardedHandlers = (evidence, aliases) => {
61219
- let normalizedEvidence = evidence;
61220
- for (const [propName, bindingName] of aliases) normalizedEvidence = normalizedEvidence.replace(new RegExp(`\\{\\s*${propName}\\s*\\}`, "g"), `{${bindingName}}`).replace(new RegExp(`=>\\s*${propName}\\s*\\(`, "g"), `=> ${bindingName}(`);
61221
- return normalizedEvidence;
61222
- };
61223
- const createDiagnosticEvidenceReader = (rootDirectory, options = {}) => {
61224
- const state = {
61225
- aliasesByComponent: /* @__PURE__ */ new Map(),
61226
- recordByFilePath: /* @__PURE__ */ new Map(),
61227
- sourceRecords: null
61228
- };
61229
- return (diagnostic) => {
61230
- const record = readSourceRecord(rootDirectory, diagnostic.filePath, state);
61231
- if (record === null || !Number.isInteger(diagnostic.line)) return null;
61232
- const sourceLines = record.sourceText.split(/\r?\n/);
61233
- const startLineIndex = Math.max(0, diagnostic.line - 1);
61234
- const endLineIndex = Math.max(startLineIndex, (diagnostic.endLine ?? diagnostic.line) - 1);
61235
- const evidence = sourceLines.slice(startLineIndex, endLineIndex + 1).join("\n");
61236
- if (!options.resolveForwardedHandlers) return evidence;
61237
- const propNames = new Set(evidence.match(/\bon[A-Z]\w*\b/g) ?? []);
61238
- if (propNames.size === 0) return evidence;
61239
- const component = findEnclosingComponent(record, diagnostic);
61240
- if (component === null) return evidence;
61241
- return normalizeForwardedHandlers(evidence, getForwardedHandlerAliases(rootDirectory, component, propNames, state));
61242
- };
61243
- };
61244
- //#endregion
61245
60404
  //#region src/cli/utils/build-no-score-message.ts
61246
60405
  const ENTERPRISE_CONTACT_HINT = `Want something custom to your company? Contact us at ${ENTERPRISE_CONTACT_URL}.`;
61247
- const buildNoScoreMessage = (isScoreDisabled, disabledMessage) => {
61248
- return `${isScoreDisabled ? disabledMessage ?? "Score disabled by --no-score." : "Score unavailable (could not reach the score API)."} ${ENTERPRISE_CONTACT_HINT}`;
60406
+ const buildNoScoreMessage = (isScoreDisabled) => {
60407
+ return `${isScoreDisabled ? "Score disabled by --no-score." : "Score unavailable (could not reach the score API)."} ${ENTERPRISE_CONTACT_HINT}`;
61249
60408
  };
61250
60409
  //#endregion
61251
60410
  //#region src/cli/utils/render-agent-guidance.ts
@@ -62370,47 +61529,40 @@ const buildChangedLineMatcher = (directory, changedLineRanges) => {
62370
61529
  return diagnosticIntersectsLineRanges(diagnostic, ranges);
62371
61530
  };
62372
61531
  };
62373
- const buildIgnoredTags = (userConfig, includedTags) => {
61532
+ const buildIgnoredTags = (userConfig) => {
62374
61533
  const tags = /* @__PURE__ */ new Set();
62375
61534
  if (userConfig?.ignore?.tags) for (const tag of userConfig.ignore.tags) tags.add(tag);
62376
- for (const tag of includedTags) tags.delete(tag);
62377
61535
  return tags;
62378
61536
  };
62379
- const mergeInspectOptions = (inputOptions, userConfig) => {
62380
- const includedTags = inputOptions.includedTags ?? /* @__PURE__ */ new Set();
62381
- return {
62382
- lint: inputOptions.lint ?? userConfig?.lint ?? true,
62383
- deadCode: inputOptions.deadCode ?? userConfig?.deadCode ?? true,
62384
- supplyChain: inputOptions.supplyChain ?? userConfig?.supplyChain?.enabled ?? true,
62385
- verbose: inputOptions.verbose ?? userConfig?.verbose ?? false,
62386
- outputDirectory: inputOptions.outputDirectory || null,
62387
- scoreOnly: inputOptions.scoreOnly ?? false,
62388
- noScore: inputOptions.noScore ?? userConfig?.noScore ?? false,
62389
- isCi: inputOptions.isCi ?? false,
62390
- isCiOrCodingAgentEnvironment: isCiOrCodingAgentEnvironment(),
62391
- isNonInteractiveEnvironment: isNonInteractiveEnvironment(),
62392
- silent: inputOptions.silent ?? false,
62393
- includePaths: inputOptions.includePaths ?? [],
62394
- customRulesOnly: includedTags.size > 0 ? false : userConfig?.customRulesOnly ?? false,
62395
- share: userConfig?.share ?? true,
62396
- respectInlineDisables: inputOptions.respectInlineDisables ?? userConfig?.respectInlineDisables ?? true,
62397
- warnings: inputOptions.warnings ?? userConfig?.warnings ?? true,
62398
- categoryFilters: new Set(resolveCliCategories(inputOptions.categoryFilters) ?? []),
62399
- adoptExistingLintConfig: includedTags.size > 0 ? false : userConfig?.adoptExistingLintConfig ?? true,
62400
- ignoredTags: buildIgnoredTags(userConfig, includedTags),
62401
- includedTags,
62402
- includeTagDefaults: inputOptions.includeTagDefaults ?? false,
62403
- scoreDisabledMessage: inputOptions.scoreDisabledMessage,
62404
- outputSurface: inputOptions.outputSurface ?? "cli",
62405
- suppressRendering: inputOptions.suppressRendering ?? false,
62406
- concurrentScan: inputOptions.concurrentScan ?? false,
62407
- concurrency: inputOptions.concurrency,
62408
- maxDurationMs: inputOptions.maxDurationMs ?? null,
62409
- baseline: inputOptions.baseline ?? null,
62410
- changedLineRanges: inputOptions.changedLineRanges ?? null,
62411
- supplyChainManifestChanged: inputOptions.supplyChainManifestChanged ?? false
62412
- };
62413
- };
61537
+ const mergeInspectOptions = (inputOptions, userConfig) => ({
61538
+ lint: inputOptions.lint ?? userConfig?.lint ?? true,
61539
+ deadCode: inputOptions.deadCode ?? userConfig?.deadCode ?? true,
61540
+ supplyChain: inputOptions.supplyChain ?? userConfig?.supplyChain?.enabled ?? true,
61541
+ verbose: inputOptions.verbose ?? userConfig?.verbose ?? false,
61542
+ outputDirectory: inputOptions.outputDirectory || null,
61543
+ scoreOnly: inputOptions.scoreOnly ?? false,
61544
+ noScore: inputOptions.noScore ?? userConfig?.noScore ?? false,
61545
+ isCi: inputOptions.isCi ?? false,
61546
+ isCiOrCodingAgentEnvironment: isCiOrCodingAgentEnvironment(),
61547
+ isNonInteractiveEnvironment: isNonInteractiveEnvironment(),
61548
+ silent: inputOptions.silent ?? false,
61549
+ includePaths: inputOptions.includePaths ?? [],
61550
+ customRulesOnly: userConfig?.customRulesOnly ?? false,
61551
+ share: userConfig?.share ?? true,
61552
+ respectInlineDisables: inputOptions.respectInlineDisables ?? userConfig?.respectInlineDisables ?? true,
61553
+ warnings: inputOptions.warnings ?? userConfig?.warnings ?? true,
61554
+ categoryFilters: new Set(resolveCliCategories(inputOptions.categoryFilters) ?? []),
61555
+ adoptExistingLintConfig: userConfig?.adoptExistingLintConfig ?? true,
61556
+ ignoredTags: buildIgnoredTags(userConfig),
61557
+ outputSurface: inputOptions.outputSurface ?? "cli",
61558
+ suppressRendering: inputOptions.suppressRendering ?? false,
61559
+ concurrentScan: inputOptions.concurrentScan ?? false,
61560
+ concurrency: inputOptions.concurrency,
61561
+ maxDurationMs: inputOptions.maxDurationMs ?? null,
61562
+ baseline: inputOptions.baseline ?? null,
61563
+ changedLineRanges: inputOptions.changedLineRanges ?? null,
61564
+ supplyChainManifestChanged: inputOptions.supplyChainManifestChanged ?? false
61565
+ });
62414
61566
  const deriveScope = (options) => {
62415
61567
  if (options.baseline) return "changed";
62416
61568
  if (options.changedLineRanges !== null) return "lines";
@@ -62492,8 +61644,6 @@ const runBaselineComparison = async (params) => {
62492
61644
  directory: params.directory,
62493
61645
  ref: params.baselineRef,
62494
61646
  files: params.options.includePaths,
62495
- baseFiles: params.baseFiles,
62496
- headFiles: params.headFiles,
62497
61647
  tempDirectory
62498
61648
  }).catch((error) => {
62499
61649
  rmSync(tempDirectory, {
@@ -62502,24 +61652,7 @@ const runBaselineComparison = async (params) => {
62502
61652
  });
62503
61653
  throw error;
62504
61654
  });
62505
- if (snapshot === null) {
62506
- rmSync(tempDirectory, {
62507
- recursive: true,
62508
- force: true
62509
- });
62510
- return null;
62511
- }
62512
61655
  try {
62513
- if (!snapshot.isComplete) return null;
62514
- const analyzedHeadFiles = new Set(params.headAnalyzedFiles.map(toForwardSlashes));
62515
- const baseFiles = new Set(snapshot.baseFiles.map(toForwardSlashes));
62516
- const trackedHeadFiles = new Set(snapshot.headFiles.map(toForwardSlashes));
62517
- const expectedHeadFiles = new Set(trackedHeadFiles);
62518
- for (const filePath of params.options.includePaths) {
62519
- const normalizedFilePath = toForwardSlashes(filePath);
62520
- if (!baseFiles.has(normalizedFilePath)) expectedHeadFiles.add(normalizedFilePath);
62521
- }
62522
- if (filterSourceFiles([...expectedHeadFiles]).some((filePath) => !analyzedHeadFiles.has(filePath))) return null;
62523
61656
  const baseLayers = buildRuntimeLayers({
62524
61657
  directory: snapshot.tempDirectory,
62525
61658
  hasConfigOverride: true,
@@ -62535,14 +61668,12 @@ const runBaselineComparison = async (params) => {
62535
61668
  });
62536
61669
  const baseOutput = await runPromise(restoreLegacyThrow(runInspect({
62537
61670
  directory: snapshot.tempDirectory,
62538
- includePaths: snapshot.materializedFiles,
61671
+ includePaths: params.options.includePaths,
62539
61672
  customRulesOnly: params.options.customRulesOnly,
62540
61673
  respectInlineDisables: params.options.respectInlineDisables,
62541
61674
  warnings: params.options.warnings,
62542
61675
  adoptExistingLintConfig: params.options.adoptExistingLintConfig,
62543
61676
  ignoredTags: params.options.ignoredTags,
62544
- includedTags: params.options.includedTags,
62545
- includeTagDefaults: params.options.includeTagDefaults,
62546
61677
  nodeBinaryPath: params.resolvedNodeBinaryPath ?? void 0,
62547
61678
  runDeadCode: false,
62548
61679
  isCi: params.options.isCi,
@@ -62554,22 +61685,18 @@ const runBaselineComparison = async (params) => {
62554
61685
  deadlineEpochMs: params.deadlineEpochMs ?? void 0
62555
61686
  }, {}).pipe(provide(baseLayers), provideService$2(PerFileLintCacheEnabled, false), provideService$2(SidecarLintCacheEnabled, false), provideService$2(Console, silentConsole))));
62556
61687
  if (baseOutput.didLintFail || countIncompleteLintFiles(baseOutput.lintPartialFailures) > 0) return null;
62557
- const hasUnscannedUntrackedSourceFiles = filterSourceFiles(snapshot.untrackedFiles.map(toForwardSlashes)).some((filePath) => !analyzedHeadFiles.has(filePath));
62558
61688
  const delta = computeDiagnosticDelta({
62559
61689
  headDiagnostics: params.headDiagnostics,
62560
61690
  baseDiagnostics: baseOutput.diagnostics,
62561
61691
  readHeadLine: createSourceLineReader(params.directory),
62562
- readBaseLine: createSourceLineReader(snapshot.tempDirectory),
62563
- readHeadEvidence: createDiagnosticEvidenceReader(params.directory, { resolveForwardedHandlers: true }),
62564
- readBaseEvidence: createDiagnosticEvidenceReader(snapshot.tempDirectory)
61692
+ readBaseLine: createSourceLineReader(snapshot.tempDirectory)
62565
61693
  });
62566
61694
  return {
62567
61695
  displayDiagnostics: delta.newDiagnostics,
62568
61696
  baselineDelta: {
62569
61697
  baseRef: params.baselineRef,
62570
- fixedCount: hasUnscannedUntrackedSourceFiles ? 0 : delta.fixedCount,
62571
- baseTotalCount: baseOutput.diagnostics.length,
62572
- crossFileMatchCount: delta.crossFileMatchCount
61698
+ fixedCount: delta.fixedCount,
61699
+ baseTotalCount: baseOutput.diagnostics.length
62573
61700
  }
62574
61701
  };
62575
61702
  } finally {
@@ -62636,8 +61763,6 @@ const runInspectWithRuntime = async (directory, options, userConfig, hasConfigOv
62636
61763
  warnings: options.warnings,
62637
61764
  adoptExistingLintConfig: options.adoptExistingLintConfig,
62638
61765
  ignoredTags: options.ignoredTags,
62639
- includedTags: options.includedTags,
62640
- includeTagDefaults: options.includeTagDefaults,
62641
61766
  nodeBinaryPath: resolvedNodeBinaryPath ?? void 0,
62642
61767
  runDeadCode: options.deadCode,
62643
61768
  isCi: options.isCi,
@@ -62678,9 +61803,6 @@ const runInspectWithRuntime = async (directory, options, userConfig, hasConfigOv
62678
61803
  headDiagnostics: output.diagnostics,
62679
61804
  resolvedNodeBinaryPath,
62680
61805
  baselineRef: options.baseline.ref,
62681
- baseFiles: options.baseline.baseFiles,
62682
- headFiles: options.baseline.headFiles,
62683
- headAnalyzedFiles: output.analyzedFiles,
62684
61806
  deadlineEpochMs
62685
61807
  });
62686
61808
  if (comparison) {
@@ -62826,7 +61948,7 @@ const finalizeAndRender = (input) => gen(function* () {
62826
61948
  securityScanFailed
62827
61949
  });
62828
61950
  const hasSkippedChecks = skippedChecks.length > 0;
62829
- const noScoreMessage = buildNoScoreMessage(options.noScore, options.scoreDisabledMessage);
61951
+ const noScoreMessage = buildNoScoreMessage(options.noScore);
62830
61952
  const buildResult = () => ({
62831
61953
  diagnostics: [...diagnostics],
62832
61954
  score,
@@ -64931,25 +64053,21 @@ const resolveParallelFlag = (parallel) => parallel === false ? 1 : void 0;
64931
64053
  */
64932
64054
  const resolveCliInspectOptions = (flags, userConfig) => {
64933
64055
  const wantsWarningGate = pickBlockingLevel(flags, userConfig) === "warning";
64934
- const isDesignScan = flags.design === true;
64935
64056
  return {
64936
64057
  lint: flags.lint,
64937
- deadCode: isDesignScan ? false : flags.deadCode,
64938
- supplyChain: isDesignScan ? false : flags.supplyChain,
64058
+ deadCode: flags.deadCode,
64059
+ supplyChain: flags.supplyChain,
64939
64060
  verbose: flags.verbose,
64940
64061
  outputDirectory: flags.outputDir,
64941
64062
  respectInlineDisables: flags.respectInlineDisables === false ? false : void 0,
64942
64063
  warnings: wantsWarningGate ? true : flags.warnings,
64943
64064
  scoreOnly: flags.score === true,
64944
- noScore: isDesignScan || flags.score === false || flags.telemetry === false ? true : void 0,
64065
+ noScore: flags.score === false || flags.telemetry === false ? true : void 0,
64945
64066
  isCi: isCiEnvironment(),
64946
64067
  silent: Boolean(flags.json),
64947
64068
  concurrency: resolveParallelFlag(flags.parallel),
64948
64069
  maxDurationMs: resolveMaxDurationFlag(flags.maxDuration),
64949
- categoryFilters: resolveCliCategories(flags.category),
64950
- includedTags: isDesignScan ? new Set(["design"]) : void 0,
64951
- includeTagDefaults: isDesignScan ? true : void 0,
64952
- scoreDisabledMessage: isDesignScan ? "Design scans do not affect the React health score." : void 0
64070
+ categoryFilters: resolveCliCategories(flags.category)
64953
64071
  };
64954
64072
  };
64955
64073
  //#endregion
@@ -64969,25 +64087,20 @@ const resolveProjectRelativeDirectory = (rootDirectory, projectDirectory) => {
64969
64087
  return relativeProjectDirectory;
64970
64088
  };
64971
64089
  //#endregion
64972
- //#region src/cli/utils/resolve-project-source-file-paths.ts
64973
- const resolveProjectSourceFilePaths = (rootDirectory, projectDirectory, filePaths) => {
64090
+ //#region src/cli/utils/resolve-project-diff-include-paths.ts
64091
+ const resolveProjectDiffIncludePaths = (rootDirectory, projectDirectory, diffInfo) => {
64974
64092
  const relativeProjectDirectory = resolveProjectRelativeDirectory(rootDirectory, projectDirectory);
64975
64093
  if (relativeProjectDirectory === null) return [];
64976
- const sourceFilePaths = filterSourceFiles([...filePaths]);
64977
- if (relativeProjectDirectory.length === 0) return sourceFilePaths;
64094
+ const changedSourceFiles = filterSourceFiles(diffInfo.changedFiles);
64095
+ if (relativeProjectDirectory.length === 0) return changedSourceFiles;
64978
64096
  const projectPrefix = `${relativeProjectDirectory}/`;
64979
- return sourceFilePaths.flatMap((filePath) => {
64097
+ return changedSourceFiles.flatMap((filePath) => {
64980
64098
  const normalizedFilePath = toForwardSlashes(filePath);
64981
64099
  if (!normalizedFilePath.startsWith(projectPrefix)) return [];
64982
64100
  const projectRelativePath = normalizedFilePath.slice(projectPrefix.length);
64983
64101
  return projectRelativePath.length > 0 ? [projectRelativePath] : [];
64984
64102
  });
64985
64103
  };
64986
- //#endregion
64987
- //#region src/cli/utils/resolve-project-diff-include-paths.ts
64988
- const resolveProjectDiffIncludePaths = (rootDirectory, projectDirectory, diffInfo) => {
64989
- return resolveProjectSourceFilePaths(rootDirectory, projectDirectory, diffInfo.changedFiles);
64990
- };
64991
64104
  /**
64992
64105
  * Re-keys repo-relative changed line ranges (from `getChangedLineRanges`) to
64993
64106
  * the scanned project, mirroring `resolveProjectDiffIncludePaths`'s boundary
@@ -65367,7 +64480,7 @@ const maybeMigrateLegacyConfig = async (requestedDirectory, { isQuiet, isStaged
65367
64480
  if (!(!isQuiet && !isStaged && process.stdout.isTTY === true && !isCiOrCodingAgentEnvironment())) return;
65368
64481
  await runProjectMigrations(requestedDirectory);
65369
64482
  };
65370
- const inspectAction = async (directory, flags, invocationCommand = "inspect") => {
64483
+ const inspectAction = async (directory, flags) => {
65371
64484
  const isScoreOnly = Boolean(flags.score);
65372
64485
  const isJsonMode = Boolean(flags.json);
65373
64486
  const isQuiet = isScoreOnly || isJsonMode;
@@ -65381,7 +64494,7 @@ const inspectAction = async (directory, flags, invocationCommand = "inspect") =>
65381
64494
  });
65382
64495
  if (flags.jsonOut) recordCount(METRIC.jsonOutUsed, 1);
65383
64496
  }
65384
- recordCount(METRIC.cliInvoked, 1, { command: invocationCommand });
64497
+ recordCount(METRIC.cliInvoked, 1, { command: "inspect" });
65385
64498
  try {
65386
64499
  validateModeFlags(flags);
65387
64500
  if (flags.staged) setJsonReportMode("staged");
@@ -65526,7 +64639,6 @@ const inspectAction = async (directory, flags, invocationCommand = "inspect") =>
65526
64639
  const isDiffMode = scope !== "full";
65527
64640
  const comparisonBaseRef = isDiffMode && diffInfo && !diffInfo.isCurrentChanges ? diffInfo.baseSha ? await resolveMergeBaseRef(resolvedDirectory, diffInfo.baseSha) : diffInfo.diffBaseRef ?? await resolveMergeBaseRef(resolvedDirectory, diffInfo.baseBranch) : null;
65528
64641
  const baselineRef = scope === "changed" ? comparisonBaseRef : null;
65529
- const baselineDiffPlan = baselineRef === null ? null : await getBaselineDiffPlan(resolvedDirectory, baselineRef);
65530
64642
  const linesBaseRef = diffInfo?.isCurrentChanges ? "HEAD" : comparisonBaseRef;
65531
64643
  const changedLineRanges = scope === "lines" && diffInfo !== null && (diffInfo.isCurrentChanges || linesBaseRef !== null) && diffInfo !== null ? await getChangedLineRanges({
65532
64644
  directory: resolvedDirectory,
@@ -65557,13 +64669,10 @@ const inspectAction = async (directory, flags, invocationCommand = "inspect") =>
65557
64669
  const supplyChainEnabled = flags.supplyChain ?? projectConfig?.supplyChain?.enabled !== false;
65558
64670
  let includePaths;
65559
64671
  let supplyChainManifestChanged = false;
65560
- const projectBaselineBaseFiles = baselineDiffPlan === null ? null : resolveProjectSourceFilePaths(resolvedDirectory, scanDirectory, baselineDiffPlan.baseFiles);
65561
- const projectBaselineHeadFiles = baselineDiffPlan === null ? null : resolveProjectSourceFilePaths(resolvedDirectory, scanDirectory, baselineDiffPlan.headFiles);
65562
64672
  if (isDiffMode) {
65563
64673
  const changedSourceFiles = diffInfo === null ? [] : resolveProjectDiffIncludePaths(resolvedDirectory, scanDirectory, diffInfo);
65564
64674
  supplyChainManifestChanged = supplyChainEnabled && diffInfo !== null && projectManifestChanged(resolvedDirectory, scanDirectory, diffInfo);
65565
- const hasBaselineOnlyFiles = (projectBaselineBaseFiles?.length ?? 0) > 0;
65566
- if (changedSourceFiles.length === 0 && !supplyChainManifestChanged && !hasBaselineOnlyFiles) {
64675
+ if (changedSourceFiles.length === 0 && !supplyChainManifestChanged) {
65567
64676
  if (!isQuiet) {
65568
64677
  cliLogger.dim(`No changed source files in ${scanDirectory}, skipping.`);
65569
64678
  cliLogger.break();
@@ -65571,7 +64680,6 @@ const inspectAction = async (directory, flags, invocationCommand = "inspect") =>
65571
64680
  return null;
65572
64681
  }
65573
64682
  includePaths = [...changedSourceFiles];
65574
- if (includePaths.length === 0 && hasBaselineOnlyFiles) includePaths.push(...projectBaselineBaseFiles ?? []);
65575
64683
  if (supplyChainManifestChanged) includePaths.push("package.json");
65576
64684
  }
65577
64685
  if (!isQuiet && !isMultiProject) cliLogger.dim(" ");
@@ -65583,11 +64691,7 @@ const inspectAction = async (directory, flags, invocationCommand = "inspect") =>
65583
64691
  configSourceDirectory: projectConfigSourceDirectory ?? void 0,
65584
64692
  suppressRendering: isMultiProject,
65585
64693
  concurrentScan: isMultiProject,
65586
- baseline: baselineRef !== null && projectBaselineBaseFiles !== null && projectBaselineHeadFiles !== null ? {
65587
- ref: baselineRef,
65588
- baseFiles: projectBaselineBaseFiles,
65589
- headFiles: projectBaselineHeadFiles
65590
- } : void 0,
64694
+ baseline: baselineRef ? { ref: baselineRef } : void 0,
65591
64695
  changedLineRanges: scope === "lines" && changedLineRanges !== null ? resolveProjectChangedLineRanges(resolvedDirectory, scanDirectory, changedLineRanges) : void 0,
65592
64696
  supplyChainManifestChanged
65593
64697
  });
@@ -65693,13 +64797,6 @@ const inspectAction = async (directory, flags, invocationCommand = "inspect") =>
65693
64797
  }
65694
64798
  };
65695
64799
  //#endregion
65696
- //#region src/cli/commands/design.ts
65697
- const designAction = async (directory, flags) => inspectAction(directory, {
65698
- ...flags,
65699
- design: true,
65700
- lint: flags.lint ?? true
65701
- }, "design");
65702
- //#endregion
65703
64800
  //#region src/cli/commands/install.ts
65704
64801
  const installAction = async (options, command) => {
65705
64802
  recordCount(METRIC.cliInvoked, 1, { command: "install" });
@@ -66536,7 +65633,6 @@ const INSTALL_FLAG_SPEC = {
66536
65633
  shortOptionsWithRequiredValues: new Set(["-c"])
66537
65634
  };
66538
65635
  const COMMAND_FLAG_SPECS = new Map([
66539
- ["design", ROOT_FLAG_SPEC],
66540
65636
  ["install", INSTALL_FLAG_SPEC],
66541
65637
  ["setup", INSTALL_FLAG_SPEC],
66542
65638
  ["version", {
@@ -66693,7 +65789,6 @@ ${formatExampleLines([
66693
65789
  ["react-doctor --scope changed --base main", "scan only new issues vs. main"],
66694
65790
  ["react-doctor --project modules/a,modules/b", "score each module separately (names or paths)"],
66695
65791
  ["react-doctor --staged", "scan staged files (pre-commit hook)"],
66696
- ["react-doctor design", "run the focused UI design audit"],
66697
65792
  ["react-doctor --category Security", "show only one diagnostic category"],
66698
65793
  ["react-doctor --blocking warning", "fail CI on warnings too (default: error)"],
66699
65794
  ["react-doctor --json > report.json", "write a machine-readable report"],
@@ -66727,20 +65822,6 @@ ${highlighter.dim("Managing CI:")}
66727
65822
  ${highlighter.dim("Learn more:")}
66728
65823
  ${highlighter.info(CANONICAL_GITHUB_URL)}
66729
65824
  `;
66730
- const renderDesignHelpEpilog = () => `
66731
- ${highlighter.dim("Examples:")}
66732
- ${formatExampleLines([
66733
- ["react-doctor design", "audit UI design in the current project"],
66734
- ["react-doctor design ./apps/web", "audit one application"],
66735
- ["react-doctor design --verbose", "show every design finding"],
66736
- ["react-doctor design --json", "write a design-only JSON report"]
66737
- ])}
66738
-
66739
- ${highlighter.dim("Scope:")}
66740
- Runs only rules tagged ${highlighter.info("design")}, including focused rules that stay opt-in during a general health scan.
66741
- Dead-code, supply-chain, external lint-config, custom-plugin, and health-score passes are skipped.
66742
- Standard scan flags such as ${highlighter.info("--scope")}, ${highlighter.info("--project")}, ${highlighter.info("--verbose")}, and ${highlighter.info("--json")} still work.
66743
- `;
66744
65825
  const renderCiHelpEpilog = () => `
66745
65826
  ${highlighter.dim("Examples:")}
66746
65827
  ${formatExampleLines([
@@ -66762,7 +65843,6 @@ ${highlighter.dim("Learn more:")}
66762
65843
  const collectCategoryOption = (value, previousValues) => [...previousValues ?? [], value];
66763
65844
  const program = new Command().name("react-doctor").description("Diagnose React codebase health").version(VERSION, "-v, --version", "display the version number").argument("[directory]", "project directory to scan", ".").option("--lint", "enable linting").option("--no-lint", "skip linting").option("--dead-code", "enable dead-code analysis (default)").option("--no-dead-code", "skip dead-code analysis (unused files / exports / dependencies, circular imports)").option("--supply-chain", "enable the dependency supply-chain scan (default)").option("--no-supply-chain", "skip the dependency supply-chain scan (Socket.dev dependency health checks)").option("--verbose", "show every rule and per-file details (default shows top 3 rules)").option("--debug", "force a Sentry trace and print its id at the end (paste it into a bug report)").option("--output-dir <dir>", "directory for the full diagnostics dump (default: a temp folder)").option("--score", "output only the score").option("--json", "output a single structured JSON report (suppresses other output)").option("--json-compact", "with --json, emit compact JSON (no indentation)").option("--json-out <path>", "with --json, write the report to a file instead of stdout").option("-y, --yes", "skip prompts, scan all workspace projects").option("--no-parallel", "lint serially with one worker (default: parallel across CPU cores; set the worker count with REACT_DOCTOR_PARALLEL)").option("--project <name>", "select projects: workspace names or directory paths (comma-separated for multiple); overrides the `projects` config field").option("--scope <value>", "how much supported JS/TS source to scan/report: full (default), files, changed (only new issues vs base), or lines (issues whose source spans touch changed lines)").option("--base <ref>", "base git ref for files/changed/lines scope (auto-detected when omitted)").option("--include-untracked", "with --scope files/changed/lines, also scan ordinary untracked files (respects .gitignore)").addOption(new Option("--diff [base]", "[deprecated] alias for --scope changed (pass `false` to force a full scan)").hideHelp()).addOption(new Option("--changed-files-from <file>", "scan source files listed in a newline-delimited changed-files file").hideHelp()).option("--no-score", "skip the score API, the share URL, and crash reporting").addOption(new Option("--category <category>", "only show diagnostics in a category (repeatable; e.g. Security)").argParser(collectCategoryOption)).option("--no-telemetry", "alias for --no-score (skip the score API, share URL, and crash reporting)").option("--staged", "scan only staged (git index) files for pre-commit hooks").option("--max-duration <seconds>", "scan time budget for the whole run, shared across workspace projects: past it, remaining lint batches and dead-code are skipped and partial results are reported (skipped files are listed in the JSON report)").option("--blocking <level>", "severity that fails CI: error (default), warning, or none (advisory)").addOption(new Option("--fail-on <level>", "[deprecated] alias for --blocking <level>").hideHelp()).option("--no-respect-inline-disables", "audit mode: neutralize inline lint suppressions before scanning").option("--warnings", "show warning-severity diagnostics (default)").option("--no-warnings", "hide warning-severity diagnostics (errors only)").option("--color", "force colored output").option("--no-color", "disable colored output (also honors NO_COLOR)").addHelpText("after", renderRootHelpEpilog);
66764
65845
  program.action(inspectAction);
66765
- program.command("design [directory]").description("Run only the focused UI design diagnostics").addHelpText("after", renderDesignHelpEpilog).action((directory, _options, command) => designAction(directory ?? ".", command.optsWithGlobals()));
66766
65846
  program.command("why <location>").description("Explain why a rule fired (or why a suppression didn't apply) at a file:line").option("--project <name>", "select projects: workspace names or directory paths (comma-separated for multiple)").option("-c, --cwd <cwd>", "working directory", process.cwd()).option("--color", "force colored output").option("--no-color", "disable colored output (also honors NO_COLOR)").action((location, options) => whyAction(location, options));
66767
65847
  program.command("install").alias("setup").description("Install the react-doctor skill into your coding agents and optional git hook").option("-y, --yes", "skip prompts, install for all detected agents").option("--dry-run", "show what would be installed without writing files").option("--agent-hooks", "install native non-blocking agent hooks for Claude Code and Cursor").option("-c, --cwd <cwd>", "working directory", process.cwd()).option("--color", "force colored output").option("--no-color", "disable colored output (also honors NO_COLOR)").addHelpText("after", renderInstallHelpEpilog).action(installAction);
66768
65848
  const providerOption = ["--provider <name>", "ci provider: github-actions or gitlab-ci (auto-detected by default)"];
@@ -66811,4 +65891,4 @@ Promise.resolve().then(() => assertNoRemovedFlags(process.argv)).then(() => prog
66811
65891
  export {};
66812
65892
 
66813
65893
  //# sourceMappingURL=cli.js.map
66814
- //# debugId=d92b0964-cd34-5965-a1bf-ce1d06faf742
65894
+ //# debugId=941df082-c8b4-5084-baae-33ec22d4e55f