react-doctor 0.5.8-dev.80e3093 → 0.5.8-dev.8232e96

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/index.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]="83529d8f-2ca3-50ba-a3b9-44622f913549")}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]="62cf4740-7938-5db0-8b80-dc81b60af36a")}catch(e){}}();
3
3
  import { r as __toESM$1, t as __commonJSMin$1 } from "./chunk-N93fKeF6.js";
4
4
  import { createRequire } from "node:module";
5
5
  import * as NFS from "node:fs";
@@ -19364,6 +19364,15 @@ var JsonReportV1 = class extends Class("JsonReportV1")({
19364
19364
  ok: Boolean$1,
19365
19365
  directory: String$1,
19366
19366
  mode: JsonReportMode,
19367
+ /**
19368
+ * True when a `changed` (compare) run was intended but the base couldn't be
19369
+ * resolved — a shallow CI checkout with no merge base, or a failed base/head
19370
+ * lint. The report then lists every finding in the changed files (mode
19371
+ * downgrades to `diff`, the `baseline` block is dropped, the CI gate is
19372
+ * skipped) rather than only the ones the change introduced. Absent on a
19373
+ * successful `baseline` run (`schemaVersion: 2`) and on non-compare scopes.
19374
+ */
19375
+ baselineDegraded: optional(Boolean$1),
19367
19376
  diff: NullOr(JsonReportDiffInfo),
19368
19377
  projects: ArraySchema(JsonReportProjectEntry),
19369
19378
  diagnostics: ArraySchema(Diagnostic),
@@ -32780,12 +32789,31 @@ const GENERATED_BUNDLE_FILE_PATTERN = /\.(iife|umd|global|min)\.m?js$/i;
32780
32789
  const MINIFIED_SNIFF_BYTES = 65536;
32781
32790
  const GIT_LS_FILES_MAX_BUFFER_BYTES = 50 * 1024 * 1024;
32782
32791
  const IGNORED_DIRECTORIES = new Set([
32792
+ ".angular",
32793
+ ".astro",
32794
+ ".cache",
32795
+ ".contentlayer",
32796
+ ".direnv",
32797
+ ".docusaurus",
32798
+ ".expo",
32799
+ ".firebase",
32783
32800
  ".git",
32801
+ ".gradle",
32802
+ ".hg",
32784
32803
  ".next",
32785
32804
  ".nuxt",
32805
+ ".nx",
32786
32806
  ".output",
32807
+ ".parcel-cache",
32808
+ ".serverless",
32787
32809
  ".svelte-kit",
32810
+ ".svn",
32811
+ ".terraform",
32788
32812
  ".turbo",
32813
+ ".venv",
32814
+ ".vercel",
32815
+ ".wrangler",
32816
+ ".yarn",
32789
32817
  "build",
32790
32818
  "coverage",
32791
32819
  "dist",
@@ -32793,6 +32821,119 @@ const IGNORED_DIRECTORIES = new Set([
32793
32821
  "out",
32794
32822
  "storybook-static"
32795
32823
  ]);
32824
+ const JSX_FILE_PATTERN = /\.(tsx|jsx)$/;
32825
+ const MILLISECONDS_PER_SECOND = 1e3;
32826
+ const SCORE_API_URL = "https://www.react.doctor/api/score";
32827
+ const FETCH_TIMEOUT_MS = 1e4;
32828
+ const GITHUB_VIEWER_PERMISSION_TIMEOUT_MS = 2e3;
32829
+ const SPAWN_ARGS_MAX_LENGTH_CHARS = 24e3;
32830
+ const SPAWN_ARGS_MAX_LENGTH_CHARS_POSIX = 15e5;
32831
+ const SPAWN_ARGS_MAX_LENGTH_CHARS_DARWIN = 8e5;
32832
+ const NODE_VERSION_PROBE_TIMEOUT_MS = 5e3;
32833
+ const PER_WORKER_MEM_BUDGET_BYTES = 1024 * 1024 * 1024;
32834
+ const DEFAULT_BRANCH_CANDIDATES = ["main", "master"];
32835
+ const ADOPTABLE_LINT_CONFIG_FILENAMES = [".oxlintrc.json", ".eslintrc.json"];
32836
+ const GIT_SHOW_MAX_BUFFER_BYTES = 10 * 1024 * 1024;
32837
+ const ES_TARGET_YEAR_BY_NAME = {
32838
+ es3: 1999,
32839
+ es5: 2009,
32840
+ es6: 2015,
32841
+ es2015: 2015,
32842
+ es2016: 2016,
32843
+ es2017: 2017,
32844
+ es2018: 2018,
32845
+ es2019: 2019,
32846
+ es2020: 2020,
32847
+ es2021: 2021,
32848
+ es2022: 2022,
32849
+ es2023: 2023,
32850
+ es2024: 2024,
32851
+ es2025: 2025,
32852
+ esnext: 9999
32853
+ };
32854
+ /**
32855
+ * tsconfig filenames probed when resolving a project's TypeScript
32856
+ * compiler options — the root config first, then a monorepo base config.
32857
+ */
32858
+ const TSCONFIG_FILENAMES = ["tsconfig.json", "tsconfig.base.json"];
32859
+ /**
32860
+ * Project-config files that `StagedFiles.materialize` copies into
32861
+ * the temp directory alongside staged sources so oxlint resolves
32862
+ * `tsconfig` / `package.json` / lint configs the same way it would
32863
+ * in the working tree. Hoisted out of the staged-files helper so
32864
+ * the constant lives next to the rest of the IO budget knobs.
32865
+ */
32866
+ const STAGED_FILES_PROJECT_CONFIG_FILENAMES = [
32867
+ "tsconfig.json",
32868
+ "tsconfig.base.json",
32869
+ "package.json",
32870
+ "doctor.config.ts",
32871
+ "doctor.config.mts",
32872
+ "doctor.config.cts",
32873
+ "doctor.config.js",
32874
+ "doctor.config.mjs",
32875
+ "doctor.config.cjs",
32876
+ "doctor.config.json",
32877
+ "doctor.config.jsonc",
32878
+ "oxlint.json",
32879
+ ".oxlintrc.json"
32880
+ ];
32881
+ const OXLINT_OUTPUT_MAX_BYTES = 50 * 1024 * 1024;
32882
+ const OXLINT_SPAWN_TIMEOUT_MS = 6e4;
32883
+ const NODE_COMPILE_CACHE_DIR_NAME = "node-compile-cache";
32884
+ const DEAD_CODE_WORKER_TIMEOUT_MS = 12e4;
32885
+ const OXLINT_SPLIT_TOTAL_BUDGET_MS = 18e4;
32886
+ const DEAD_CODE_PHASE_TIMEOUT_MS = 15e4;
32887
+ const LINT_PHASE_TIMEOUT_MS = 3e5;
32888
+ const SCAN_TOTAL_DEADLINE_MS = 9e5;
32889
+ const DEAD_CODE_WORKER_MAX_OLD_SPACE_MB = 8192;
32890
+ const DEAD_CODE_WORKER_MEM_BUDGET_BYTES = 2 * 1024 * 1024 * 1024;
32891
+ const DEAD_CODE_TIMEOUT_CEILING_MS = 6e5;
32892
+ const DEAD_CODE_PHASE_TIMEOUT_OVER_WORKER_MS = 3e4;
32893
+ const DEAD_CODE_OVERLAP_PARSE_SHARE = .4;
32894
+ const RECOMMENDED_PNPM_MINIMUM_RELEASE_AGE_MINUTES = 10080;
32895
+ const REACT_SERVER_DOM_PACKAGES = [
32896
+ "react-server-dom-webpack",
32897
+ "react-server-dom-parcel",
32898
+ "react-server-dom-turbopack"
32899
+ ];
32900
+ const REACT_BLOG_RSC_ADVISORY_URL = "https://react.dev/blog/2025/12/03/critical-security-vulnerability-in-react-server-components";
32901
+ const VERCEL_NEXTJS_SECURITY_RELEASE_URL = "https://vercel.com/changelog/next-js-may-2026-security-release";
32902
+ const DIAGNOSTIC_CATEGORY_BUCKETS = [
32903
+ "Security",
32904
+ "Bugs",
32905
+ "Performance",
32906
+ "Accessibility",
32907
+ "Maintainability"
32908
+ ];
32909
+ const APP_ONLY_RULE_KEYS = new Set([
32910
+ "react-hooks-js/static-components",
32911
+ "react-doctor/no-render-prop-children",
32912
+ "react-doctor/prefer-explicit-variants"
32913
+ ]);
32914
+ const COMPILER_CLEANUP_BUCKET = "compiler-cleanup";
32915
+ const COMPILER_CLEANUP_RULE_KEYS = new Set(["react-doctor/react-compiler-no-manual-memoization"]);
32916
+ const ROOT_CAUSE_GROUPABLE_RULE_KEYS = new Set([
32917
+ "react-doctor/no-derived-state",
32918
+ "react-doctor/no-derived-state-effect",
32919
+ "react-doctor/no-derived-useState",
32920
+ "react-doctor/no-adjust-state-on-prop-change",
32921
+ "react-doctor/no-reset-all-state-on-prop-change"
32922
+ ]);
32923
+ const MAX_GLOB_PATTERN_LENGTH_CHARS = 1024;
32924
+ const CONFIG_CACHE_TTL_MS = 300 * 1e3;
32925
+ const SOCKET_FREE_PURL_API_BASE = "https://firewall-api.socket.dev/purl";
32926
+ const SOCKET_PACKAGE_PAGE_BASE = "https://socket.dev/npm/package";
32927
+ const SOCKET_FREE_USER_AGENT = "react-doctor-supply-chain";
32928
+ const FILE_LINT_CACHE_FILENAME = "file-lint-cache.json";
32929
+ const FILE_LINT_CACHE_MAX_FILE_COUNT = 5e4;
32930
+ const SUPPLY_CHAIN_PLUGIN = "socket";
32931
+ const SUPPLY_CHAIN_RULE = "low-supply-chain-score";
32932
+ const SUPPLY_CHAIN_CATEGORY = "Security";
32933
+ const SUPPLY_CHAIN_OVERLAP_TIMEOUT_MS = 9e4;
32934
+ const SUPPLY_CHAIN_CACHE_SUBDIR = "supply-chain";
32935
+ const SUPPLY_CHAIN_IGNORED_PACKAGES = new Set(["next"]);
32936
+ const hasIgnoredPathSegment = (relativePath) => relativePath.split(/[/\\]/).slice(0, -1).some((segment) => IGNORED_DIRECTORIES.has(segment));
32796
32937
  const isLintableSourceFile = (filePath) => SOURCE_FILE_PATTERN.test(filePath) && !GENERATED_BUNDLE_FILE_PATTERN.test(filePath);
32797
32938
  const isMinifiedSource = (absolutePath) => {
32798
32939
  let fileDescriptor;
@@ -32849,23 +32990,38 @@ const readDirectoryEntries = (directoryPath) => {
32849
32990
  throw error;
32850
32991
  }
32851
32992
  };
32852
- const countSourceFilesViaFilesystem = (rootDirectory) => {
32853
- let count = 0;
32993
+ function* walkSourceTreeFiles(rootDirectory) {
32854
32994
  const stack = [rootDirectory];
32855
32995
  while (stack.length > 0) {
32856
32996
  const currentDirectory = stack.pop();
32857
- const entries = readDirectoryEntries(currentDirectory);
32858
- for (const entry of entries) {
32997
+ if (currentDirectory === void 0) continue;
32998
+ for (const entry of readDirectoryEntries(currentDirectory)) {
32999
+ const absolutePath = Path.join(currentDirectory, entry.name);
32859
33000
  if (entry.isDirectory()) {
32860
- if (!entry.name.startsWith(".") && !IGNORED_DIRECTORIES.has(entry.name)) stack.push(Path.join(currentDirectory, entry.name));
33001
+ if (!IGNORED_DIRECTORIES.has(entry.name)) stack.push(absolutePath);
32861
33002
  continue;
32862
33003
  }
32863
- if (entry.isFile() && isLintableSourceFile(entry.name) && !isLargeMinifiedFile(Path.join(currentDirectory, entry.name))) count++;
33004
+ if (entry.isFile()) yield {
33005
+ absolutePath,
33006
+ name: entry.name
33007
+ };
32864
33008
  }
32865
33009
  }
32866
- return count;
33010
+ }
33011
+ const collectSizedSourceFiles = (rootDirectory, relativePaths) => {
33012
+ const entries = [];
33013
+ for (const relativePath of relativePaths) {
33014
+ const absolutePath = Path.resolve(rootDirectory, relativePath);
33015
+ const sizeBytes = statSourceFileSize(absolutePath);
33016
+ if (isLargeMinifiedFile(absolutePath, sizeBytes)) continue;
33017
+ entries.push({
33018
+ path: relativePath,
33019
+ sizeBytes: sizeBytes ?? 0
33020
+ });
33021
+ }
33022
+ return entries;
32867
33023
  };
32868
- const countSourceFilesViaGit = (rootDirectory) => {
33024
+ const listSourceFilesViaGit = (rootDirectory) => {
32869
33025
  const result = spawnSync("git", [
32870
33026
  "ls-files",
32871
33027
  "-z",
@@ -32878,9 +33034,16 @@ const countSourceFilesViaGit = (rootDirectory) => {
32878
33034
  maxBuffer: GIT_LS_FILES_MAX_BUFFER_BYTES
32879
33035
  });
32880
33036
  if (result.error || result.status !== 0) return null;
32881
- return result.stdout.split("\0").filter((filePath) => filePath.length > 0 && isLintableSourceFile(filePath) && !isLargeMinifiedFile(Path.resolve(rootDirectory, filePath))).length;
33037
+ return result.stdout.split("\0").filter((filePath) => filePath.length > 0 && isLintableSourceFile(filePath) && !hasIgnoredPathSegment(filePath));
32882
33038
  };
32883
- const countSourceFiles = (rootDirectory) => countSourceFilesViaGit(rootDirectory) ?? countSourceFilesViaFilesystem(rootDirectory);
33039
+ const listSourceFilesViaFilesystem = (rootDirectory) => {
33040
+ const filePaths = [];
33041
+ for (const { absolutePath, name } of walkSourceTreeFiles(rootDirectory)) if (isLintableSourceFile(name)) filePaths.push(Path.relative(rootDirectory, absolutePath).replace(/\\/g, "/"));
33042
+ return filePaths;
33043
+ };
33044
+ const listSourceFilesWithSize = (rootDirectory) => collectSizedSourceFiles(rootDirectory, (listSourceFilesViaGit(rootDirectory) ?? listSourceFilesViaFilesystem(rootDirectory)).sort());
33045
+ const listSourceFiles = (rootDirectory) => listSourceFilesWithSize(rootDirectory).map((entry) => entry.path);
33046
+ const countSourceFiles = (rootDirectory) => listSourceFilesWithSize(rootDirectory).length;
32884
33047
  const cachedPackageJsons = /* @__PURE__ */ new Map();
32885
33048
  const clearPackageJsonCache = () => {
32886
33049
  cachedPackageJsons.clear();
@@ -33689,118 +33852,6 @@ const resolveEffectiveReactMajor = (reactVersion, packageJson) => {
33689
33852
  if (peerFloor === null) return hasUpperBoundOnlyPeerRange(peerReactRange) ? null : installedReactMajor;
33690
33853
  return installedReactMajor !== null ? Math.min(installedReactMajor, peerFloor) : peerFloor;
33691
33854
  };
33692
- const JSX_FILE_PATTERN = /\.(tsx|jsx)$/;
33693
- const MILLISECONDS_PER_SECOND = 1e3;
33694
- const SCORE_API_URL = "https://www.react.doctor/api/score";
33695
- const FETCH_TIMEOUT_MS = 1e4;
33696
- const GITHUB_VIEWER_PERMISSION_TIMEOUT_MS = 2e3;
33697
- const SPAWN_ARGS_MAX_LENGTH_CHARS = 24e3;
33698
- const SPAWN_ARGS_MAX_LENGTH_CHARS_POSIX = 15e5;
33699
- const SPAWN_ARGS_MAX_LENGTH_CHARS_DARWIN = 8e5;
33700
- const NODE_VERSION_PROBE_TIMEOUT_MS = 5e3;
33701
- const PER_WORKER_MEM_BUDGET_BYTES = 1024 * 1024 * 1024;
33702
- const DEFAULT_BRANCH_CANDIDATES = ["main", "master"];
33703
- const ADOPTABLE_LINT_CONFIG_FILENAMES = [".oxlintrc.json", ".eslintrc.json"];
33704
- const GIT_SHOW_MAX_BUFFER_BYTES = 10 * 1024 * 1024;
33705
- const ES_TARGET_YEAR_BY_NAME = {
33706
- es3: 1999,
33707
- es5: 2009,
33708
- es6: 2015,
33709
- es2015: 2015,
33710
- es2016: 2016,
33711
- es2017: 2017,
33712
- es2018: 2018,
33713
- es2019: 2019,
33714
- es2020: 2020,
33715
- es2021: 2021,
33716
- es2022: 2022,
33717
- es2023: 2023,
33718
- es2024: 2024,
33719
- es2025: 2025,
33720
- esnext: 9999
33721
- };
33722
- /**
33723
- * tsconfig filenames probed when resolving a project's TypeScript
33724
- * compiler options — the root config first, then a monorepo base config.
33725
- */
33726
- const TSCONFIG_FILENAMES = ["tsconfig.json", "tsconfig.base.json"];
33727
- /**
33728
- * Project-config files that `StagedFiles.materialize` copies into
33729
- * the temp directory alongside staged sources so oxlint resolves
33730
- * `tsconfig` / `package.json` / lint configs the same way it would
33731
- * in the working tree. Hoisted out of the staged-files helper so
33732
- * the constant lives next to the rest of the IO budget knobs.
33733
- */
33734
- const STAGED_FILES_PROJECT_CONFIG_FILENAMES = [
33735
- "tsconfig.json",
33736
- "tsconfig.base.json",
33737
- "package.json",
33738
- "doctor.config.ts",
33739
- "doctor.config.mts",
33740
- "doctor.config.cts",
33741
- "doctor.config.js",
33742
- "doctor.config.mjs",
33743
- "doctor.config.cjs",
33744
- "doctor.config.json",
33745
- "doctor.config.jsonc",
33746
- "oxlint.json",
33747
- ".oxlintrc.json"
33748
- ];
33749
- const OXLINT_OUTPUT_MAX_BYTES = 50 * 1024 * 1024;
33750
- const OXLINT_SPAWN_TIMEOUT_MS = 6e4;
33751
- const NODE_COMPILE_CACHE_DIR_NAME = "node-compile-cache";
33752
- const DEAD_CODE_WORKER_TIMEOUT_MS = 12e4;
33753
- const OXLINT_SPLIT_TOTAL_BUDGET_MS = 18e4;
33754
- const DEAD_CODE_PHASE_TIMEOUT_MS = 15e4;
33755
- const LINT_PHASE_TIMEOUT_MS = 3e5;
33756
- const SCAN_TOTAL_DEADLINE_MS = 9e5;
33757
- const DEAD_CODE_WORKER_MAX_OLD_SPACE_MB = 8192;
33758
- const DEAD_CODE_WORKER_MEM_BUDGET_BYTES = 2 * 1024 * 1024 * 1024;
33759
- const DEAD_CODE_TIMEOUT_CEILING_MS = 6e5;
33760
- const DEAD_CODE_PHASE_TIMEOUT_OVER_WORKER_MS = 3e4;
33761
- const DEAD_CODE_OVERLAP_PARSE_SHARE = .4;
33762
- const RECOMMENDED_PNPM_MINIMUM_RELEASE_AGE_MINUTES = 10080;
33763
- const REACT_SERVER_DOM_PACKAGES = [
33764
- "react-server-dom-webpack",
33765
- "react-server-dom-parcel",
33766
- "react-server-dom-turbopack"
33767
- ];
33768
- const REACT_BLOG_RSC_ADVISORY_URL = "https://react.dev/blog/2025/12/03/critical-security-vulnerability-in-react-server-components";
33769
- const VERCEL_NEXTJS_SECURITY_RELEASE_URL = "https://vercel.com/changelog/next-js-may-2026-security-release";
33770
- const DIAGNOSTIC_CATEGORY_BUCKETS = [
33771
- "Security",
33772
- "Bugs",
33773
- "Performance",
33774
- "Accessibility",
33775
- "Maintainability"
33776
- ];
33777
- const APP_ONLY_RULE_KEYS = new Set([
33778
- "react-hooks-js/static-components",
33779
- "react-doctor/no-render-prop-children",
33780
- "react-doctor/prefer-explicit-variants"
33781
- ]);
33782
- const COMPILER_CLEANUP_BUCKET = "compiler-cleanup";
33783
- const COMPILER_CLEANUP_RULE_KEYS = new Set(["react-doctor/react-compiler-no-manual-memoization"]);
33784
- const ROOT_CAUSE_GROUPABLE_RULE_KEYS = new Set([
33785
- "react-doctor/no-derived-state",
33786
- "react-doctor/no-derived-state-effect",
33787
- "react-doctor/no-derived-useState",
33788
- "react-doctor/no-adjust-state-on-prop-change",
33789
- "react-doctor/no-reset-all-state-on-prop-change"
33790
- ]);
33791
- const MAX_GLOB_PATTERN_LENGTH_CHARS = 1024;
33792
- const CONFIG_CACHE_TTL_MS = 300 * 1e3;
33793
- const SOCKET_FREE_PURL_API_BASE = "https://firewall-api.socket.dev/purl";
33794
- const SOCKET_PACKAGE_PAGE_BASE = "https://socket.dev/npm/package";
33795
- const SOCKET_FREE_USER_AGENT = "react-doctor-supply-chain";
33796
- const FILE_LINT_CACHE_FILENAME = "file-lint-cache.json";
33797
- const FILE_LINT_CACHE_MAX_FILE_COUNT = 5e4;
33798
- const SUPPLY_CHAIN_PLUGIN = "socket";
33799
- const SUPPLY_CHAIN_RULE = "low-supply-chain-score";
33800
- const SUPPLY_CHAIN_CATEGORY = "Security";
33801
- const SUPPLY_CHAIN_OVERLAP_TIMEOUT_MS = 9e4;
33802
- const SUPPLY_CHAIN_CACHE_SUBDIR = "supply-chain";
33803
- const SUPPLY_CHAIN_IGNORED_PACKAGES = new Set(["next"]);
33804
33855
  const TSCONFIG_FILENAME = "tsconfig.json";
33805
33856
  const isRelativeExtendsValue = (extendsValue) => extendsValue.startsWith("./") || extendsValue.startsWith("../") || Path.isAbsolute(extendsValue);
33806
33857
  const ensureJsonExtension = (filePath) => Path.extname(filePath) === "" ? `${filePath}.json` : filePath;
@@ -36748,14 +36799,17 @@ const checkReactServerComponentsAdvisory = (scanDirectory, project) => {
36748
36799
  return diagnostics;
36749
36800
  };
36750
36801
  const REDUCED_MOTION_GREP_PATTERN = "prefers-reduced-motion|useReducedMotion|MotionConfig|reducedMotion";
36751
- const REDUCED_MOTION_FILE_GLOBS = [
36752
- "*.ts",
36753
- "*.tsx",
36754
- "*.js",
36755
- "*.jsx",
36756
- "*.css",
36757
- "*.scss"
36758
- ];
36802
+ const REDUCED_MOTION_PATTERN = new RegExp(REDUCED_MOTION_GREP_PATTERN);
36803
+ const REDUCED_MOTION_FILE_EXTENSIONS = new Set([
36804
+ ".ts",
36805
+ ".tsx",
36806
+ ".js",
36807
+ ".jsx",
36808
+ ".css",
36809
+ ".scss"
36810
+ ]);
36811
+ const REDUCED_MOTION_FILE_GLOBS = [...REDUCED_MOTION_FILE_EXTENSIONS].map((extension) => `*${extension}`);
36812
+ const GIT_GREP_NO_MATCH_STATUS = 1;
36759
36813
  const MISSING_REDUCED_MOTION_DIAGNOSTIC = {
36760
36814
  filePath: "package.json",
36761
36815
  plugin: "react-doctor",
@@ -36767,6 +36821,19 @@ const MISSING_REDUCED_MOTION_DIAGNOSTIC = {
36767
36821
  column: 0,
36768
36822
  category: "Accessibility"
36769
36823
  };
36824
+ const hasReducedMotionHandlingViaFilesystem = (rootDirectory) => {
36825
+ for (const { absolutePath, name } of walkSourceTreeFiles(rootDirectory)) {
36826
+ if (!REDUCED_MOTION_FILE_EXTENSIONS.has(Path.extname(name))) continue;
36827
+ let content;
36828
+ try {
36829
+ content = NFS.readFileSync(absolutePath, "utf-8");
36830
+ } catch {
36831
+ continue;
36832
+ }
36833
+ if (REDUCED_MOTION_PATTERN.test(content)) return true;
36834
+ }
36835
+ return false;
36836
+ };
36770
36837
  const checkReducedMotion = (rootDirectory) => {
36771
36838
  const packageJsonPath = Path.join(rootDirectory, "package.json");
36772
36839
  if (!isFile(packageJsonPath)) return [];
@@ -36785,22 +36852,21 @@ const checkReducedMotion = (rootDirectory) => {
36785
36852
  const result = spawnSync("git", [
36786
36853
  "grep",
36787
36854
  "--untracked",
36788
- "-ql",
36789
- "-E",
36855
+ "-lE",
36790
36856
  REDUCED_MOTION_GREP_PATTERN,
36791
36857
  "--",
36792
36858
  ...REDUCED_MOTION_FILE_GLOBS
36793
36859
  ], {
36794
36860
  cwd: rootDirectory,
36861
+ encoding: "utf-8",
36795
36862
  stdio: [
36796
36863
  "ignore",
36797
36864
  "pipe",
36798
36865
  "pipe"
36799
36866
  ]
36800
36867
  });
36801
- if (result.error) return [MISSING_REDUCED_MOTION_DIAGNOSTIC];
36802
- if (result.status === 0) return [];
36803
- return [MISSING_REDUCED_MOTION_DIAGNOSTIC];
36868
+ if (!(!result.error && (result.status === 0 || result.status === GIT_GREP_NO_MATCH_STATUS))) return hasReducedMotionHandlingViaFilesystem(rootDirectory) ? [] : [MISSING_REDUCED_MOTION_DIAGNOSTIC];
36869
+ return result.status === 0 && result.stdout.split("\n").some((filePath) => filePath.length > 0 && !hasIgnoredPathSegment(filePath)) ? [] : [MISSING_REDUCED_MOTION_DIAGNOSTIC];
36804
36870
  };
36805
36871
  const buildSecurityScanDiagnostic = (finding, entry, relativePath) => ({
36806
36872
  filePath: relativePath,
@@ -37696,6 +37762,7 @@ const isDiagnosticOnSurface = (diagnostic, surface, config) => {
37696
37762
  return true;
37697
37763
  };
37698
37764
  const filterDiagnosticsForSurface = (diagnostics, surface, config) => diagnostics.filter((diagnostic) => isDiagnosticOnSurface(diagnostic, surface, config));
37765
+ const remainingDeadlineBudgetMs = (deadlineEpochMs) => Math.max(deadlineEpochMs - Date.now(), 0);
37699
37766
  /**
37700
37767
  * Budget for the dead-code phase, scaled to the work. deslop's graph build is
37701
37768
  * CPU-bound and roughly linear in file count, so a fixed 120s cap is too tight
@@ -37714,53 +37781,6 @@ const resolveDeadCodeTimeout = (input) => {
37714
37781
  phaseTimeoutMs: workerTimeoutMs + DEAD_CODE_PHASE_TIMEOUT_OVER_WORKER_MS
37715
37782
  };
37716
37783
  };
37717
- const collectSizedSourceFiles = (rootDirectory, relativePaths) => {
37718
- const entries = [];
37719
- for (const relativePath of relativePaths) {
37720
- const absolutePath = Path.resolve(rootDirectory, relativePath);
37721
- const sizeBytes = statSourceFileSize(absolutePath);
37722
- if (isLargeMinifiedFile(absolutePath, sizeBytes)) continue;
37723
- entries.push({
37724
- path: relativePath,
37725
- sizeBytes: sizeBytes ?? 0
37726
- });
37727
- }
37728
- return entries;
37729
- };
37730
- const listSourceFilesViaGit = (rootDirectory) => {
37731
- const result = spawnSync("git", [
37732
- "ls-files",
37733
- "-z",
37734
- "--cached",
37735
- "--others",
37736
- "--exclude-standard"
37737
- ], {
37738
- cwd: rootDirectory,
37739
- encoding: "utf-8",
37740
- maxBuffer: GIT_LS_FILES_MAX_BUFFER_BYTES
37741
- });
37742
- if (result.error || result.status !== 0) return null;
37743
- return result.stdout.split("\0").filter((filePath) => filePath.length > 0 && isLintableSourceFile(filePath));
37744
- };
37745
- const listSourceFilesViaFilesystem = (rootDirectory) => {
37746
- const filePaths = [];
37747
- const stack = [rootDirectory];
37748
- while (stack.length > 0) {
37749
- const currentDirectory = stack.pop();
37750
- const entries = readDirectoryEntries(currentDirectory);
37751
- for (const entry of entries) {
37752
- const absolutePath = Path.join(currentDirectory, entry.name);
37753
- if (entry.isDirectory()) {
37754
- if (!entry.name.startsWith(".") && !IGNORED_DIRECTORIES.has(entry.name)) stack.push(absolutePath);
37755
- continue;
37756
- }
37757
- if (entry.isFile() && isLintableSourceFile(entry.name)) filePaths.push(Path.relative(rootDirectory, absolutePath).replace(/\\/g, "/"));
37758
- }
37759
- }
37760
- return filePaths;
37761
- };
37762
- const listSourceFilesWithSize = (rootDirectory) => collectSizedSourceFiles(rootDirectory, listSourceFilesViaGit(rootDirectory) ?? listSourceFilesViaFilesystem(rootDirectory));
37763
- const listSourceFiles = (rootDirectory) => listSourceFilesWithSize(rootDirectory).map((entry) => entry.path);
37764
37784
  const resolveLintIncludePaths = (rootDirectory, userConfig, project) => {
37765
37785
  if (!Array.isArray(userConfig?.ignore?.files) || userConfig.ignore.files.length === 0) return;
37766
37786
  const ignoredPatterns = compileIgnoredFilePatterns(userConfig);
@@ -38457,12 +38477,12 @@ const findFilesWithDisableDirectivesViaGit = async (rootDirectory, includePaths)
38457
38477
  return null;
38458
38478
  }
38459
38479
  if (grepResult === null) return null;
38460
- return grepResult.stdout.split("\n").filter((filePath) => filePath.length > 0 && isLintableSourceFile(filePath));
38480
+ return grepResult.stdout.split("\n").filter((filePath) => filePath.length > 0 && isLintableSourceFile(filePath) && !hasIgnoredPathSegment(filePath));
38461
38481
  };
38462
38482
  const findFilesWithDisableDirectivesViaFilesystem = (rootDirectory, includePaths) => {
38463
38483
  const matches = [];
38464
38484
  const checkFile = (relativePath) => {
38465
- if (!isLintableSourceFile(relativePath)) return;
38485
+ if (!isLintableSourceFile(relativePath) || hasIgnoredPathSegment(relativePath)) return;
38466
38486
  const absolutePath = Path.join(rootDirectory, relativePath);
38467
38487
  let content;
38468
38488
  try {
@@ -38476,22 +38496,7 @@ const findFilesWithDisableDirectivesViaFilesystem = (rootDirectory, includePaths
38476
38496
  for (const candidate of includePaths) checkFile(candidate);
38477
38497
  return matches;
38478
38498
  }
38479
- const stack = [rootDirectory];
38480
- while (stack.length > 0) {
38481
- const current = stack.pop();
38482
- if (current === void 0) continue;
38483
- const entries = readDirectoryEntries(current);
38484
- for (const entry of entries) {
38485
- if (entry.isDirectory()) {
38486
- if (entry.name.startsWith(".") || IGNORED_DIRECTORIES.has(entry.name)) continue;
38487
- stack.push(Path.join(current, entry.name));
38488
- continue;
38489
- }
38490
- if (!entry.isFile()) continue;
38491
- const absolute = Path.join(current, entry.name);
38492
- checkFile(Path.relative(rootDirectory, absolute));
38493
- }
38494
- }
38499
+ for (const { absolutePath } of walkSourceTreeFiles(rootDirectory)) checkFile(Path.relative(rootDirectory, absolutePath));
38495
38500
  return matches;
38496
38501
  };
38497
38502
  const findFilesWithDisableDirectives = async (rootDirectory, includePaths) => await findFilesWithDisableDirectivesViaGit(rootDirectory, includePaths) ?? findFilesWithDisableDirectivesViaFilesystem(rootDirectory, includePaths);
@@ -38708,12 +38713,12 @@ const createOxlintConfig = ({ pluginPath, project, customRulesOnly = false, exte
38708
38713
  if (customRulesOnly && registryEntry.originallyExternal) continue;
38709
38714
  if (rule.framework !== "global" && !rule.requires) continue;
38710
38715
  if (!shouldEnableRule(rule.requires, rule.tags, capabilities, ignoredTags, rule.disabledBy)) continue;
38711
- const explicitSeverity = resolveRuleSeverityOverride({
38716
+ const explicitRuleOverride = resolveRuleSeverityOverride({ ruleKey: registryEntry.key }, severityControls);
38717
+ if (rule.defaultEnabled === false && explicitRuleOverride === void 0) continue;
38718
+ const severity = resolveRuleSeverityOverride({
38712
38719
  ruleKey: registryEntry.key,
38713
38720
  category: rule.category
38714
- }, severityControls);
38715
- if (rule.defaultEnabled === false && explicitSeverity === void 0) continue;
38716
- const severity = explicitSeverity ?? resolveCompilerCleanupBucketSeverity(registryEntry.key, severityControls) ?? rule.severity;
38721
+ }, severityControls) ?? resolveCompilerCleanupBucketSeverity(registryEntry.key, severityControls) ?? rule.severity;
38717
38722
  if (severity === "off") continue;
38718
38723
  enabledReactDoctorRules[registryEntry.key] = severity;
38719
38724
  }
@@ -39695,21 +39700,33 @@ const isParallelismRelatedSpawnError = (error) => {
39695
39700
  * loop with a slimmer config in that case.
39696
39701
  */
39697
39702
  const spawnLintBatches = async (input) => {
39698
- const { baseArgs, fileBatches, rootDirectory, nodeBinaryPath, project, onPartialFailure, onFileProgress, spawnTimeoutMs, outputMaxBytes, splitTotalBudgetMs = OXLINT_SPLIT_TOTAL_BUDGET_MS, splitMaxDepth = 8, signal } = input;
39703
+ const { baseArgs, fileBatches, rootDirectory, nodeBinaryPath, project, onPartialFailure, onFileProgress, spawnTimeoutMs, outputMaxBytes, splitTotalBudgetMs = OXLINT_SPLIT_TOTAL_BUDGET_MS, splitMaxDepth = 8, signal, deadlineEpochMs } = input;
39699
39704
  const requestedConcurrency = resolveScanConcurrency(input.concurrency ?? 1);
39700
39705
  const totalFileCount = fileBatches.reduce((sum, batch) => sum + batch.length, 0);
39701
39706
  const runBatchPass = async (concurrency) => {
39702
39707
  const allDiagnostics = [];
39703
39708
  const droppedFiles = [];
39709
+ const deadlineSkippedFiles = [];
39710
+ const isPastDeadline = () => deadlineEpochMs !== void 0 && remainingDeadlineBudgetMs(deadlineEpochMs) === 0;
39704
39711
  let firstDropReason = null;
39705
- const spawnLintBatch = async (batch, depth, splitBudget) => {
39712
+ const spawnLintBatch = async (batch, depth, batchState) => {
39713
+ if (isPastDeadline()) {
39714
+ deadlineSkippedFiles.push(...batch);
39715
+ batchState.deadlineSkippedFileCount += batch.length;
39716
+ return [];
39717
+ }
39706
39718
  const batchArgs = [...baseArgs, ...batch];
39707
39719
  try {
39708
39720
  return parseOxlintOutput(await spawnOxlint(batchArgs, rootDirectory, nodeBinaryPath, spawnTimeoutMs, outputMaxBytes, signal), project, rootDirectory);
39709
39721
  } catch (error) {
39710
39722
  if (!isSplittableReactDoctorError(error)) throw error;
39711
- splitBudget.deadlineMs ??= Date.now() + splitTotalBudgetMs;
39712
- const isBudgetElapsed = Date.now() >= splitBudget.deadlineMs;
39723
+ if (isPastDeadline()) {
39724
+ deadlineSkippedFiles.push(...batch);
39725
+ batchState.deadlineSkippedFileCount += batch.length;
39726
+ return [];
39727
+ }
39728
+ batchState.deadlineMs ??= Date.now() + splitTotalBudgetMs;
39729
+ const isBudgetElapsed = Date.now() >= batchState.deadlineMs;
39713
39730
  const isDepthCapReached = depth >= splitMaxDepth;
39714
39731
  if (batch.length <= 1 || isBudgetElapsed || isDepthCapReached) {
39715
39732
  droppedFiles.push(...batch);
@@ -39722,7 +39739,7 @@ const spawnLintBatches = async (input) => {
39722
39739
  return [];
39723
39740
  }
39724
39741
  const splitIndex = Math.ceil(batch.length / 2);
39725
- return [...await spawnLintBatch(batch.slice(0, splitIndex), depth + 1, splitBudget), ...await spawnLintBatch(batch.slice(splitIndex), depth + 1, splitBudget)];
39742
+ return [...await spawnLintBatch(batch.slice(0, splitIndex), depth + 1, batchState), ...await spawnLintBatch(batch.slice(splitIndex), depth + 1, batchState)];
39726
39743
  }
39727
39744
  };
39728
39745
  let startedFileCount = 0;
@@ -39738,9 +39755,17 @@ const spawnLintBatches = async (input) => {
39738
39755
  progressTimer?.unref?.();
39739
39756
  try {
39740
39757
  const batchResults = await mapWithConcurrency(fileBatches, concurrency, async (batch) => {
39758
+ if (isPastDeadline()) {
39759
+ deadlineSkippedFiles.push(...batch);
39760
+ return [];
39761
+ }
39741
39762
  startedFileCount += batch.length;
39742
- const batchDiagnostics = await spawnLintBatch(batch, 0, { deadlineMs: null });
39743
- scannedFileCount += batch.length;
39763
+ const batchState = {
39764
+ deadlineMs: null,
39765
+ deadlineSkippedFileCount: 0
39766
+ };
39767
+ const batchDiagnostics = await spawnLintBatch(batch, 0, batchState);
39768
+ scannedFileCount += batch.length - batchState.deadlineSkippedFileCount;
39744
39769
  if (onFileProgress) {
39745
39770
  displayedFileCount = Math.min(Math.max(displayedFileCount, scannedFileCount), totalFileCount);
39746
39771
  onFileProgress(displayedFileCount, totalFileCount);
@@ -39751,12 +39776,13 @@ const spawnLintBatches = async (input) => {
39751
39776
  } finally {
39752
39777
  if (progressTimer !== null) clearInterval(progressTimer);
39753
39778
  }
39754
- if (droppedFiles.length > 0 && onPartialFailure) {
39755
- const previewFiles = droppedFiles.slice(0, 3).join(", ");
39756
- const remainderHint = droppedFiles.length > 3 ? `, +${droppedFiles.length - 3} more` : "";
39757
- const reasonHint = firstDropReason ? ` — first failure: ${firstDropReason}` : "";
39758
- onPartialFailure(`${droppedFiles.length} file(s) failed to lint and were skipped (${previewFiles}${remainderHint})${reasonHint}`);
39759
- }
39779
+ const reportSkippedFiles = (skippedFiles, buildMessage) => {
39780
+ if (skippedFiles.length === 0 || !onPartialFailure) return;
39781
+ onPartialFailure(buildMessage(`${skippedFiles.slice(0, 3).join(", ")}${skippedFiles.length > 3 ? `, +${skippedFiles.length - 3} more` : ""}`));
39782
+ };
39783
+ const reasonHint = firstDropReason ? ` — first failure: ${firstDropReason}` : "";
39784
+ reportSkippedFiles(droppedFiles, (fileListPreview) => `${droppedFiles.length} file(s) failed to lint and were skipped (${fileListPreview})${reasonHint}`);
39785
+ reportSkippedFiles(deadlineSkippedFiles, (fileListPreview) => `${deadlineSkippedFiles.length} file(s) skipped — max scan duration reached before they were linted (${fileListPreview})`);
39760
39786
  return allDiagnostics;
39761
39787
  };
39762
39788
  let diagnostics;
@@ -39960,7 +39986,8 @@ const runOxlint = async (options) => {
39960
39986
  spawnTimeoutMs,
39961
39987
  outputMaxBytes,
39962
39988
  concurrency: options.concurrency,
39963
- signal: options.signal
39989
+ signal: options.signal,
39990
+ deadlineEpochMs: options.deadlineEpochMs
39964
39991
  });
39965
39992
  writeOxlintConfig(passConfigPath, buildConfigForPass({}));
39966
39993
  try {
@@ -40059,7 +40086,8 @@ const runOxlint = async (options) => {
40059
40086
  spawnTimeoutMs,
40060
40087
  outputMaxBytes,
40061
40088
  concurrency: options.concurrency,
40062
- signal: options.signal
40089
+ signal: options.signal,
40090
+ deadlineEpochMs: options.deadlineEpochMs
40063
40091
  });
40064
40092
  writeOxlintConfig(configPath, buildConfig({ extendsPaths }));
40065
40093
  try {
@@ -40163,7 +40191,8 @@ var Linter = class Linter extends Service()("react-doctor/Linter") {
40163
40191
  outputMaxBytes,
40164
40192
  concurrency,
40165
40193
  signal,
40166
- lintBatchOrdering
40194
+ lintBatchOrdering,
40195
+ deadlineEpochMs: input.deadlineEpochMs
40167
40196
  }),
40168
40197
  catch: ensureReactDoctorError
40169
40198
  });
@@ -40923,6 +40952,7 @@ const runInspect = (input, hooks = {}) => gen(function* () {
40923
40952
  const deadCodePhaseTimeoutMs = yield* DeadCodePhaseTimeoutMs;
40924
40953
  const resolveDeadCodePhaseTimeoutMs = (scaledPhaseTimeoutMs) => deadCodePhaseTimeoutMs === 15e4 ? scaledPhaseTimeoutMs : deadCodePhaseTimeoutMs;
40925
40954
  const workerCountSuffix = scanConcurrency > 1 ? ` ${highlighter.dim(`[~${scanConcurrency} workers]`)}` : "";
40955
+ const capToDeadline = (phaseTimeoutMs) => input.deadlineEpochMs === void 0 ? phaseTimeoutMs : Math.min(phaseTimeoutMs, remainingDeadlineBudgetMs(input.deadlineEpochMs));
40926
40956
  const shouldRunDeadCode = input.runDeadCode && !isDiffMode && (showWarnings || deadCodeMaySurfaceWhenWarningsHidden(resolvedConfig.config));
40927
40957
  const deadCodeOverlapMode = yield* DeadCodeOverlap;
40928
40958
  const shouldOverlapDeadCode = shouldRunDeadCode && deadCodeOverlapMode === "on";
@@ -40952,7 +40982,7 @@ const runInspect = (input, hooks = {}) => gen(function* () {
40952
40982
  });
40953
40983
  const deadCodeFiber = shouldOverlapDeadCode ? yield* forkChild(buildCollectDeadCode({
40954
40984
  workerTimeoutMs: overlapDeadCodeTimeout.workerTimeoutMs,
40955
- phaseTimeoutMs: resolveDeadCodePhaseTimeoutMs(overlapDeadCodeTimeout.phaseTimeoutMs)
40985
+ phaseTimeoutMs: capToDeadline(resolveDeadCodePhaseTimeoutMs(overlapDeadCodeTimeout.phaseTimeoutMs))
40956
40986
  })) : null;
40957
40987
  const scanProgress = yield* progressService.start("Scanning...");
40958
40988
  const scanStartTime = Date.now();
@@ -40977,7 +41007,8 @@ const runInspect = (input, hooks = {}) => gen(function* () {
40977
41007
  onCacheStats: (cacheHitFileCount, totalConsideredFileCount) => {
40978
41008
  lintCacheHitFileCount = cacheHitFileCount;
40979
41009
  lintCacheTotalFileCount = totalConsideredFileCount;
40980
- }
41010
+ },
41011
+ deadlineEpochMs: input.deadlineEpochMs
40981
41012
  }).pipe(catchTag("ReactDoctorError", (error) => unwrap(gen(function* () {
40982
41013
  yield* set(lintFailure, {
40983
41014
  didFail: true,
@@ -41004,7 +41035,13 @@ const runInspect = (input, hooks = {}) => gen(function* () {
41004
41035
  let deadCodeCollected = [];
41005
41036
  if (lintFailureState.didFail) {
41006
41037
  if (deadCodeFiber !== null) yield* interrupt(deadCodeFiber);
41007
- } else if (shouldRunDeadCode) {
41038
+ } else if (shouldRunDeadCode) if (input.deadlineEpochMs !== void 0 && remainingDeadlineBudgetMs(input.deadlineEpochMs) === 0) {
41039
+ if (deadCodeFiber !== null) yield* interrupt(deadCodeFiber);
41040
+ yield* set(deadCodeFailure, {
41041
+ didFail: true,
41042
+ reason: "Dead-code analysis skipped — max scan duration reached."
41043
+ });
41044
+ } else {
41008
41045
  yield* scanProgress.update(`Scanned ${scannedFilesLabel}, analyzing dead code...`);
41009
41046
  const sequentialDeadCodeTimeout = resolveDeadCodeTimeout({
41010
41047
  sourceFileCount: totalFileCount,
@@ -41013,7 +41050,7 @@ const runInspect = (input, hooks = {}) => gen(function* () {
41013
41050
  });
41014
41051
  deadCodeCollected = deadCodeFiber !== null ? yield* join(deadCodeFiber) : yield* buildCollectDeadCode({
41015
41052
  workerTimeoutMs: sequentialDeadCodeTimeout.workerTimeoutMs,
41016
- phaseTimeoutMs: resolveDeadCodePhaseTimeoutMs(sequentialDeadCodeTimeout.phaseTimeoutMs)
41053
+ phaseTimeoutMs: capToDeadline(resolveDeadCodePhaseTimeoutMs(sequentialDeadCodeTimeout.phaseTimeoutMs))
41017
41054
  });
41018
41055
  }
41019
41056
  const deadCodeFailureState = lintFailureState.didFail ? {
@@ -41051,7 +41088,7 @@ const runInspect = (input, hooks = {}) => gen(function* () {
41051
41088
  };
41052
41089
  const scoreSurface = input.scoreSurface ?? "score";
41053
41090
  const scoreDiagnostics = filterDiagnosticsForSurface([...finalDiagnostics], scoreSurface, resolvedConfig.config);
41054
- const score = lintFailureState.didFail ? null : yield* scoreService.compute({
41091
+ const score = lintFailureState.didFail || deadCodeFailureState.didFail ? null : yield* scoreService.compute({
41055
41092
  diagnostics: scoreDiagnostics,
41056
41093
  isCi: input.isCi,
41057
41094
  metadata: scoreMetadata
@@ -41420,6 +41457,7 @@ const buildJsonReport = (input) => {
41420
41457
  return {
41421
41458
  schemaVersion: 1,
41422
41459
  mode: input.mode,
41460
+ ...input.baselineDegraded ? { baselineDegraded: true } : {},
41423
41461
  ...shared
41424
41462
  };
41425
41463
  };
@@ -41720,4 +41758,4 @@ const toJsonReport = (result, options) => buildJsonReport({
41720
41758
  export { AmbiguousProjectError, NoReactDependencyError, NotADirectoryError, PackageJsonNotFoundError, ProjectNotFoundError, ReactDoctorError, buildJsonReport, buildJsonReportError, clearCaches, defineConfig, diagnose, filterSourceFiles, getDiffInfo, isProjectDiscoveryError, isReactDoctorError, summarizeDiagnostics, toJsonReport };
41721
41759
 
41722
41760
  //# sourceMappingURL=index.js.map
41723
- //# debugId=83529d8f-2ca3-50ba-a3b9-44622f913549
41761
+ //# debugId=62cf4740-7938-5db0-8b80-dc81b60af36a