react-doctor 0.5.8-dev.8232e96 → 0.5.8-dev.88a5c3c

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (4) hide show
  1. package/dist/cli.js +272 -333
  2. package/dist/index.js +223 -251
  3. package/dist/lsp.js +246 -274
  4. package/package.json +4 -4
package/dist/cli.js CHANGED
@@ -1,5 +1,5 @@
1
1
 
2
- !function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="56ca0cbd-9f70-5a50-8496-43abdf078431")}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]="8495d0ec-c532-5e58-be11-e0bff7202ba5")}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";
@@ -35957,31 +35957,12 @@ const GENERATED_BUNDLE_FILE_PATTERN = /\.(iife|umd|global|min)\.m?js$/i;
35957
35957
  const MINIFIED_SNIFF_BYTES = 65536;
35958
35958
  const GIT_LS_FILES_MAX_BUFFER_BYTES = 50 * 1024 * 1024;
35959
35959
  const IGNORED_DIRECTORIES = new Set([
35960
- ".angular",
35961
- ".astro",
35962
- ".cache",
35963
- ".contentlayer",
35964
- ".direnv",
35965
- ".docusaurus",
35966
- ".expo",
35967
- ".firebase",
35968
35960
  ".git",
35969
- ".gradle",
35970
- ".hg",
35971
35961
  ".next",
35972
35962
  ".nuxt",
35973
- ".nx",
35974
35963
  ".output",
35975
- ".parcel-cache",
35976
- ".serverless",
35977
35964
  ".svelte-kit",
35978
- ".svn",
35979
- ".terraform",
35980
35965
  ".turbo",
35981
- ".venv",
35982
- ".vercel",
35983
- ".wrangler",
35984
- ".yarn",
35985
35966
  "build",
35986
35967
  "coverage",
35987
35968
  "dist",
@@ -35989,152 +35970,6 @@ const IGNORED_DIRECTORIES = new Set([
35989
35970
  "out",
35990
35971
  "storybook-static"
35991
35972
  ]);
35992
- const JSX_FILE_PATTERN = /\.(tsx|jsx)$/;
35993
- const MILLISECONDS_PER_SECOND = 1e3;
35994
- const SCORE_API_URL = "https://www.react.doctor/api/score";
35995
- const ENTERPRISE_CONTACT_URL = "https://react.doctor/enterprise";
35996
- const SHARE_BASE_URL = "https://react.doctor/share";
35997
- const CI_URL = "https://react.doctor/ci";
35998
- const GITHUB_ACTIONS_SETUP_URL = "https://www.react.doctor/docs/ci-and-prs/github-actions-setup";
35999
- const DOCS_URL = "https://react.doctor/docs";
36000
- const DOCS_RULES_BASE_URL = `${DOCS_URL}/rules`;
36001
- const FETCH_TIMEOUT_MS = 1e4;
36002
- const GITHUB_VIEWER_PERMISSION_TIMEOUT_MS = 2e3;
36003
- const SPAWN_ARGS_MAX_LENGTH_CHARS = 24e3;
36004
- const SPAWN_ARGS_MAX_LENGTH_CHARS_POSIX = 15e5;
36005
- const SPAWN_ARGS_MAX_LENGTH_CHARS_DARWIN = 8e5;
36006
- const NODE_VERSION_PROBE_TIMEOUT_MS = 5e3;
36007
- const PER_WORKER_MEM_BUDGET_BYTES = 1024 * 1024 * 1024;
36008
- const DEFAULT_BRANCH_CANDIDATES = ["main", "master"];
36009
- const ADOPTABLE_LINT_CONFIG_FILENAMES = [".oxlintrc.json", ".eslintrc.json"];
36010
- const OXLINT_NODE_REQUIREMENT = "^20.19.0 || >=22.12.0";
36011
- const GIT_SHOW_MAX_BUFFER_BYTES = 10 * 1024 * 1024;
36012
- const ES_TARGET_YEAR_BY_NAME = {
36013
- es3: 1999,
36014
- es5: 2009,
36015
- es6: 2015,
36016
- es2015: 2015,
36017
- es2016: 2016,
36018
- es2017: 2017,
36019
- es2018: 2018,
36020
- es2019: 2019,
36021
- es2020: 2020,
36022
- es2021: 2021,
36023
- es2022: 2022,
36024
- es2023: 2023,
36025
- es2024: 2024,
36026
- es2025: 2025,
36027
- esnext: 9999
36028
- };
36029
- /**
36030
- * tsconfig filenames probed when resolving a project's TypeScript
36031
- * compiler options — the root config first, then a monorepo base config.
36032
- */
36033
- const TSCONFIG_FILENAMES = ["tsconfig.json", "tsconfig.base.json"];
36034
- /**
36035
- * Project-config files that `StagedFiles.materialize` copies into
36036
- * the temp directory alongside staged sources so oxlint resolves
36037
- * `tsconfig` / `package.json` / lint configs the same way it would
36038
- * in the working tree. Hoisted out of the staged-files helper so
36039
- * the constant lives next to the rest of the IO budget knobs.
36040
- */
36041
- const STAGED_FILES_PROJECT_CONFIG_FILENAMES = [
36042
- "tsconfig.json",
36043
- "tsconfig.base.json",
36044
- "package.json",
36045
- "doctor.config.ts",
36046
- "doctor.config.mts",
36047
- "doctor.config.cts",
36048
- "doctor.config.js",
36049
- "doctor.config.mjs",
36050
- "doctor.config.cjs",
36051
- "doctor.config.json",
36052
- "doctor.config.jsonc",
36053
- "oxlint.json",
36054
- ".oxlintrc.json"
36055
- ];
36056
- const CONFIG_FINGERPRINT_FILENAMES = [
36057
- "doctor.config.ts",
36058
- "doctor.config.mts",
36059
- "doctor.config.cts",
36060
- "doctor.config.js",
36061
- "doctor.config.mjs",
36062
- "doctor.config.cjs",
36063
- "doctor.config.json",
36064
- "doctor.config.jsonc",
36065
- "react-doctor.config.json",
36066
- "package.json",
36067
- "pnpm-workspace.yaml",
36068
- "tsconfig.json",
36069
- "tsconfig.base.json",
36070
- ".oxlintrc.json",
36071
- ".eslintrc.json",
36072
- "pnpm-lock.yaml",
36073
- "package-lock.json",
36074
- "yarn.lock",
36075
- "bun.lock",
36076
- "bun.lockb",
36077
- ".gitignore"
36078
- ];
36079
- const CANONICAL_GITHUB_URL = "https://github.com/millionco/react-doctor";
36080
- const CANONICAL_DISCORD_URL = "https://react.doctor/discord";
36081
- const SKILL_NAME = "react-doctor";
36082
- const OXLINT_OUTPUT_MAX_BYTES = 50 * 1024 * 1024;
36083
- const OXLINT_SPAWN_TIMEOUT_MS = 6e4;
36084
- const NODE_COMPILE_CACHE_DIR_NAME = "node-compile-cache";
36085
- const DEAD_CODE_WORKER_TIMEOUT_MS = 12e4;
36086
- const OXLINT_SPLIT_TOTAL_BUDGET_MS = 18e4;
36087
- const DEAD_CODE_PHASE_TIMEOUT_MS = 15e4;
36088
- const LINT_PHASE_TIMEOUT_MS = 3e5;
36089
- const SCAN_TOTAL_DEADLINE_MS = 9e5;
36090
- const DEAD_CODE_WORKER_MAX_OLD_SPACE_MB = 8192;
36091
- const DEAD_CODE_WORKER_MEM_BUDGET_BYTES = 2 * 1024 * 1024 * 1024;
36092
- const DEAD_CODE_TIMEOUT_CEILING_MS = 6e5;
36093
- const DEAD_CODE_PHASE_TIMEOUT_OVER_WORKER_MS = 3e4;
36094
- const DEAD_CODE_OVERLAP_PARSE_SHARE = .4;
36095
- const RECOMMENDED_PNPM_MINIMUM_RELEASE_AGE_MINUTES = 10080;
36096
- const REACT_SERVER_DOM_PACKAGES = [
36097
- "react-server-dom-webpack",
36098
- "react-server-dom-parcel",
36099
- "react-server-dom-turbopack"
36100
- ];
36101
- const REACT_BLOG_RSC_ADVISORY_URL = "https://react.dev/blog/2025/12/03/critical-security-vulnerability-in-react-server-components";
36102
- const VERCEL_NEXTJS_SECURITY_RELEASE_URL = "https://vercel.com/changelog/next-js-may-2026-security-release";
36103
- const DIAGNOSTIC_CATEGORY_BUCKETS = [
36104
- "Security",
36105
- "Bugs",
36106
- "Performance",
36107
- "Accessibility",
36108
- "Maintainability"
36109
- ];
36110
- const APP_ONLY_RULE_KEYS = new Set([
36111
- "react-hooks-js/static-components",
36112
- "react-doctor/no-render-prop-children",
36113
- "react-doctor/prefer-explicit-variants"
36114
- ]);
36115
- const COMPILER_CLEANUP_BUCKET = "compiler-cleanup";
36116
- const COMPILER_CLEANUP_RULE_KEYS = new Set(["react-doctor/react-compiler-no-manual-memoization"]);
36117
- const ROOT_CAUSE_GROUPABLE_RULE_KEYS = new Set([
36118
- "react-doctor/no-derived-state",
36119
- "react-doctor/no-derived-state-effect",
36120
- "react-doctor/no-derived-useState",
36121
- "react-doctor/no-adjust-state-on-prop-change",
36122
- "react-doctor/no-reset-all-state-on-prop-change"
36123
- ]);
36124
- const MAX_GLOB_PATTERN_LENGTH_CHARS = 1024;
36125
- const CONFIG_CACHE_TTL_MS = 300 * 1e3;
36126
- const SOCKET_FREE_PURL_API_BASE = "https://firewall-api.socket.dev/purl";
36127
- const SOCKET_PACKAGE_PAGE_BASE = "https://socket.dev/npm/package";
36128
- const SOCKET_FREE_USER_AGENT = "react-doctor-supply-chain";
36129
- const FILE_LINT_CACHE_FILENAME = "file-lint-cache.json";
36130
- const FILE_LINT_CACHE_MAX_FILE_COUNT = 5e4;
36131
- const SUPPLY_CHAIN_PLUGIN = "socket";
36132
- const SUPPLY_CHAIN_RULE = "low-supply-chain-score";
36133
- const SUPPLY_CHAIN_CATEGORY = "Security";
36134
- const SUPPLY_CHAIN_OVERLAP_TIMEOUT_MS = 9e4;
36135
- const SUPPLY_CHAIN_CACHE_SUBDIR = "supply-chain";
36136
- const SUPPLY_CHAIN_IGNORED_PACKAGES = new Set(["next"]);
36137
- const hasIgnoredPathSegment = (relativePath) => relativePath.split(/[/\\]/).slice(0, -1).some((segment) => IGNORED_DIRECTORIES.has(segment));
36138
35973
  const isLintableSourceFile = (filePath) => SOURCE_FILE_PATTERN.test(filePath) && !GENERATED_BUNDLE_FILE_PATTERN.test(filePath);
36139
35974
  const isMinifiedSource = (absolutePath) => {
36140
35975
  let fileDescriptor;
@@ -36188,38 +36023,23 @@ const readDirectoryEntries = (directoryPath) => {
36188
36023
  throw error;
36189
36024
  }
36190
36025
  };
36191
- function* walkSourceTreeFiles(rootDirectory) {
36026
+ const countSourceFilesViaFilesystem = (rootDirectory) => {
36027
+ let count = 0;
36192
36028
  const stack = [rootDirectory];
36193
36029
  while (stack.length > 0) {
36194
36030
  const currentDirectory = stack.pop();
36195
- if (currentDirectory === void 0) continue;
36196
- for (const entry of readDirectoryEntries(currentDirectory)) {
36197
- const absolutePath = Path.join(currentDirectory, entry.name);
36031
+ const entries = readDirectoryEntries(currentDirectory);
36032
+ for (const entry of entries) {
36198
36033
  if (entry.isDirectory()) {
36199
- if (!IGNORED_DIRECTORIES.has(entry.name)) stack.push(absolutePath);
36034
+ if (!entry.name.startsWith(".") && !IGNORED_DIRECTORIES.has(entry.name)) stack.push(Path.join(currentDirectory, entry.name));
36200
36035
  continue;
36201
36036
  }
36202
- if (entry.isFile()) yield {
36203
- absolutePath,
36204
- name: entry.name
36205
- };
36037
+ if (entry.isFile() && isLintableSourceFile(entry.name) && !isLargeMinifiedFile(Path.join(currentDirectory, entry.name))) count++;
36206
36038
  }
36207
36039
  }
36208
- }
36209
- const collectSizedSourceFiles = (rootDirectory, relativePaths) => {
36210
- const entries = [];
36211
- for (const relativePath of relativePaths) {
36212
- const absolutePath = Path.resolve(rootDirectory, relativePath);
36213
- const sizeBytes = statSourceFileSize(absolutePath);
36214
- if (isLargeMinifiedFile(absolutePath, sizeBytes)) continue;
36215
- entries.push({
36216
- path: relativePath,
36217
- sizeBytes: sizeBytes ?? 0
36218
- });
36219
- }
36220
- return entries;
36040
+ return count;
36221
36041
  };
36222
- const listSourceFilesViaGit = (rootDirectory) => {
36042
+ const countSourceFilesViaGit = (rootDirectory) => {
36223
36043
  const result = spawnSync("git", [
36224
36044
  "ls-files",
36225
36045
  "-z",
@@ -36232,16 +36052,9 @@ const listSourceFilesViaGit = (rootDirectory) => {
36232
36052
  maxBuffer: GIT_LS_FILES_MAX_BUFFER_BYTES
36233
36053
  });
36234
36054
  if (result.error || result.status !== 0) return null;
36235
- return result.stdout.split("\0").filter((filePath) => filePath.length > 0 && isLintableSourceFile(filePath) && !hasIgnoredPathSegment(filePath));
36055
+ return result.stdout.split("\0").filter((filePath) => filePath.length > 0 && isLintableSourceFile(filePath) && !isLargeMinifiedFile(Path.resolve(rootDirectory, filePath))).length;
36236
36056
  };
36237
- const listSourceFilesViaFilesystem = (rootDirectory) => {
36238
- const filePaths = [];
36239
- for (const { absolutePath, name } of walkSourceTreeFiles(rootDirectory)) if (isLintableSourceFile(name)) filePaths.push(Path.relative(rootDirectory, absolutePath).replace(/\\/g, "/"));
36240
- return filePaths;
36241
- };
36242
- const listSourceFilesWithSize = (rootDirectory) => collectSizedSourceFiles(rootDirectory, (listSourceFilesViaGit(rootDirectory) ?? listSourceFilesViaFilesystem(rootDirectory)).sort());
36243
- const listSourceFiles = (rootDirectory) => listSourceFilesWithSize(rootDirectory).map((entry) => entry.path);
36244
- const countSourceFiles = (rootDirectory) => listSourceFilesWithSize(rootDirectory).length;
36057
+ const countSourceFiles = (rootDirectory) => countSourceFilesViaGit(rootDirectory) ?? countSourceFilesViaFilesystem(rootDirectory);
36245
36058
  const cachedPackageJsons = /* @__PURE__ */ new Map();
36246
36059
  const readPackageJsonUncached = (packageJsonPath) => {
36247
36060
  try {
@@ -37047,6 +36860,151 @@ const resolveEffectiveReactMajor = (reactVersion, packageJson) => {
37047
36860
  if (peerFloor === null) return hasUpperBoundOnlyPeerRange(peerReactRange) ? null : installedReactMajor;
37048
36861
  return installedReactMajor !== null ? Math.min(installedReactMajor, peerFloor) : peerFloor;
37049
36862
  };
36863
+ const JSX_FILE_PATTERN = /\.(tsx|jsx)$/;
36864
+ const MILLISECONDS_PER_SECOND = 1e3;
36865
+ const SCORE_API_URL = "https://www.react.doctor/api/score";
36866
+ const ENTERPRISE_CONTACT_URL = "https://react.doctor/enterprise";
36867
+ const SHARE_BASE_URL = "https://react.doctor/share";
36868
+ const CI_URL = "https://react.doctor/ci";
36869
+ const GITHUB_ACTIONS_SETUP_URL = "https://www.react.doctor/docs/ci-and-prs/github-actions-setup";
36870
+ const DOCS_URL = "https://react.doctor/docs";
36871
+ const DOCS_RULES_BASE_URL = `${DOCS_URL}/rules`;
36872
+ const FETCH_TIMEOUT_MS = 1e4;
36873
+ const GITHUB_VIEWER_PERMISSION_TIMEOUT_MS = 2e3;
36874
+ const SPAWN_ARGS_MAX_LENGTH_CHARS = 24e3;
36875
+ const SPAWN_ARGS_MAX_LENGTH_CHARS_POSIX = 15e5;
36876
+ const SPAWN_ARGS_MAX_LENGTH_CHARS_DARWIN = 8e5;
36877
+ const NODE_VERSION_PROBE_TIMEOUT_MS = 5e3;
36878
+ const PER_WORKER_MEM_BUDGET_BYTES = 1024 * 1024 * 1024;
36879
+ const DEFAULT_BRANCH_CANDIDATES = ["main", "master"];
36880
+ const ADOPTABLE_LINT_CONFIG_FILENAMES = [".oxlintrc.json", ".eslintrc.json"];
36881
+ const OXLINT_NODE_REQUIREMENT = "^20.19.0 || >=22.12.0";
36882
+ const GIT_SHOW_MAX_BUFFER_BYTES = 10 * 1024 * 1024;
36883
+ const ES_TARGET_YEAR_BY_NAME = {
36884
+ es3: 1999,
36885
+ es5: 2009,
36886
+ es6: 2015,
36887
+ es2015: 2015,
36888
+ es2016: 2016,
36889
+ es2017: 2017,
36890
+ es2018: 2018,
36891
+ es2019: 2019,
36892
+ es2020: 2020,
36893
+ es2021: 2021,
36894
+ es2022: 2022,
36895
+ es2023: 2023,
36896
+ es2024: 2024,
36897
+ es2025: 2025,
36898
+ esnext: 9999
36899
+ };
36900
+ /**
36901
+ * tsconfig filenames probed when resolving a project's TypeScript
36902
+ * compiler options — the root config first, then a monorepo base config.
36903
+ */
36904
+ const TSCONFIG_FILENAMES = ["tsconfig.json", "tsconfig.base.json"];
36905
+ /**
36906
+ * Project-config files that `StagedFiles.materialize` copies into
36907
+ * the temp directory alongside staged sources so oxlint resolves
36908
+ * `tsconfig` / `package.json` / lint configs the same way it would
36909
+ * in the working tree. Hoisted out of the staged-files helper so
36910
+ * the constant lives next to the rest of the IO budget knobs.
36911
+ */
36912
+ const STAGED_FILES_PROJECT_CONFIG_FILENAMES = [
36913
+ "tsconfig.json",
36914
+ "tsconfig.base.json",
36915
+ "package.json",
36916
+ "doctor.config.ts",
36917
+ "doctor.config.mts",
36918
+ "doctor.config.cts",
36919
+ "doctor.config.js",
36920
+ "doctor.config.mjs",
36921
+ "doctor.config.cjs",
36922
+ "doctor.config.json",
36923
+ "doctor.config.jsonc",
36924
+ "oxlint.json",
36925
+ ".oxlintrc.json"
36926
+ ];
36927
+ const CONFIG_FINGERPRINT_FILENAMES = [
36928
+ "doctor.config.ts",
36929
+ "doctor.config.mts",
36930
+ "doctor.config.cts",
36931
+ "doctor.config.js",
36932
+ "doctor.config.mjs",
36933
+ "doctor.config.cjs",
36934
+ "doctor.config.json",
36935
+ "doctor.config.jsonc",
36936
+ "react-doctor.config.json",
36937
+ "package.json",
36938
+ "pnpm-workspace.yaml",
36939
+ "tsconfig.json",
36940
+ "tsconfig.base.json",
36941
+ ".oxlintrc.json",
36942
+ ".eslintrc.json",
36943
+ "pnpm-lock.yaml",
36944
+ "package-lock.json",
36945
+ "yarn.lock",
36946
+ "bun.lock",
36947
+ "bun.lockb",
36948
+ ".gitignore"
36949
+ ];
36950
+ const CANONICAL_GITHUB_URL = "https://github.com/millionco/react-doctor";
36951
+ const CANONICAL_DISCORD_URL = "https://react.doctor/discord";
36952
+ const SKILL_NAME = "react-doctor";
36953
+ const OXLINT_OUTPUT_MAX_BYTES = 50 * 1024 * 1024;
36954
+ const OXLINT_SPAWN_TIMEOUT_MS = 6e4;
36955
+ const NODE_COMPILE_CACHE_DIR_NAME = "node-compile-cache";
36956
+ const DEAD_CODE_WORKER_TIMEOUT_MS = 12e4;
36957
+ const OXLINT_SPLIT_TOTAL_BUDGET_MS = 18e4;
36958
+ const DEAD_CODE_PHASE_TIMEOUT_MS = 15e4;
36959
+ const LINT_PHASE_TIMEOUT_MS = 3e5;
36960
+ const SCAN_TOTAL_DEADLINE_MS = 9e5;
36961
+ const DEAD_CODE_WORKER_MAX_OLD_SPACE_MB = 8192;
36962
+ const DEAD_CODE_WORKER_MEM_BUDGET_BYTES = 2 * 1024 * 1024 * 1024;
36963
+ const DEAD_CODE_TIMEOUT_CEILING_MS = 6e5;
36964
+ const DEAD_CODE_PHASE_TIMEOUT_OVER_WORKER_MS = 3e4;
36965
+ const DEAD_CODE_OVERLAP_PARSE_SHARE = .4;
36966
+ const RECOMMENDED_PNPM_MINIMUM_RELEASE_AGE_MINUTES = 10080;
36967
+ const REACT_SERVER_DOM_PACKAGES = [
36968
+ "react-server-dom-webpack",
36969
+ "react-server-dom-parcel",
36970
+ "react-server-dom-turbopack"
36971
+ ];
36972
+ const REACT_BLOG_RSC_ADVISORY_URL = "https://react.dev/blog/2025/12/03/critical-security-vulnerability-in-react-server-components";
36973
+ const VERCEL_NEXTJS_SECURITY_RELEASE_URL = "https://vercel.com/changelog/next-js-may-2026-security-release";
36974
+ const DIAGNOSTIC_CATEGORY_BUCKETS = [
36975
+ "Security",
36976
+ "Bugs",
36977
+ "Performance",
36978
+ "Accessibility",
36979
+ "Maintainability"
36980
+ ];
36981
+ const APP_ONLY_RULE_KEYS = new Set([
36982
+ "react-hooks-js/static-components",
36983
+ "react-doctor/no-render-prop-children",
36984
+ "react-doctor/prefer-explicit-variants"
36985
+ ]);
36986
+ const COMPILER_CLEANUP_BUCKET = "compiler-cleanup";
36987
+ const COMPILER_CLEANUP_RULE_KEYS = new Set(["react-doctor/react-compiler-no-manual-memoization"]);
36988
+ const ROOT_CAUSE_GROUPABLE_RULE_KEYS = new Set([
36989
+ "react-doctor/no-derived-state",
36990
+ "react-doctor/no-derived-state-effect",
36991
+ "react-doctor/no-derived-useState",
36992
+ "react-doctor/no-adjust-state-on-prop-change",
36993
+ "react-doctor/no-reset-all-state-on-prop-change"
36994
+ ]);
36995
+ const MAX_GLOB_PATTERN_LENGTH_CHARS = 1024;
36996
+ const CONFIG_CACHE_TTL_MS = 300 * 1e3;
36997
+ const SOCKET_FREE_PURL_API_BASE = "https://firewall-api.socket.dev/purl";
36998
+ const SOCKET_PACKAGE_PAGE_BASE = "https://socket.dev/npm/package";
36999
+ const SOCKET_FREE_USER_AGENT = "react-doctor-supply-chain";
37000
+ const FILE_LINT_CACHE_FILENAME = "file-lint-cache.json";
37001
+ const FILE_LINT_CACHE_MAX_FILE_COUNT = 5e4;
37002
+ const SUPPLY_CHAIN_PLUGIN = "socket";
37003
+ const SUPPLY_CHAIN_RULE = "low-supply-chain-score";
37004
+ const SUPPLY_CHAIN_CATEGORY = "Security";
37005
+ const SUPPLY_CHAIN_OVERLAP_TIMEOUT_MS = 9e4;
37006
+ const SUPPLY_CHAIN_CACHE_SUBDIR = "supply-chain";
37007
+ const SUPPLY_CHAIN_IGNORED_PACKAGES = new Set(["next"]);
37050
37008
  const TSCONFIG_FILENAME = "tsconfig.json";
37051
37009
  const isRelativeExtendsValue = (extendsValue) => extendsValue.startsWith("./") || extendsValue.startsWith("../") || Path.isAbsolute(extendsValue);
37052
37010
  const ensureJsonExtension = (filePath) => Path.extname(filePath) === "" ? `${filePath}.json` : filePath;
@@ -40026,17 +39984,14 @@ const checkReactServerComponentsAdvisory = (scanDirectory, project) => {
40026
39984
  return diagnostics;
40027
39985
  };
40028
39986
  const REDUCED_MOTION_GREP_PATTERN = "prefers-reduced-motion|useReducedMotion|MotionConfig|reducedMotion";
40029
- const REDUCED_MOTION_PATTERN = new RegExp(REDUCED_MOTION_GREP_PATTERN);
40030
- const REDUCED_MOTION_FILE_EXTENSIONS = new Set([
40031
- ".ts",
40032
- ".tsx",
40033
- ".js",
40034
- ".jsx",
40035
- ".css",
40036
- ".scss"
40037
- ]);
40038
- const REDUCED_MOTION_FILE_GLOBS = [...REDUCED_MOTION_FILE_EXTENSIONS].map((extension) => `*${extension}`);
40039
- const GIT_GREP_NO_MATCH_STATUS = 1;
39987
+ const REDUCED_MOTION_FILE_GLOBS = [
39988
+ "*.ts",
39989
+ "*.tsx",
39990
+ "*.js",
39991
+ "*.jsx",
39992
+ "*.css",
39993
+ "*.scss"
39994
+ ];
40040
39995
  const MISSING_REDUCED_MOTION_DIAGNOSTIC = {
40041
39996
  filePath: "package.json",
40042
39997
  plugin: "react-doctor",
@@ -40048,19 +40003,6 @@ const MISSING_REDUCED_MOTION_DIAGNOSTIC = {
40048
40003
  column: 0,
40049
40004
  category: "Accessibility"
40050
40005
  };
40051
- const hasReducedMotionHandlingViaFilesystem = (rootDirectory) => {
40052
- for (const { absolutePath, name } of walkSourceTreeFiles(rootDirectory)) {
40053
- if (!REDUCED_MOTION_FILE_EXTENSIONS.has(Path.extname(name))) continue;
40054
- let content;
40055
- try {
40056
- content = NFS.readFileSync(absolutePath, "utf-8");
40057
- } catch {
40058
- continue;
40059
- }
40060
- if (REDUCED_MOTION_PATTERN.test(content)) return true;
40061
- }
40062
- return false;
40063
- };
40064
40006
  const checkReducedMotion = (rootDirectory) => {
40065
40007
  const packageJsonPath = Path.join(rootDirectory, "package.json");
40066
40008
  if (!isFile(packageJsonPath)) return [];
@@ -40079,21 +40021,22 @@ const checkReducedMotion = (rootDirectory) => {
40079
40021
  const result = spawnSync("git", [
40080
40022
  "grep",
40081
40023
  "--untracked",
40082
- "-lE",
40024
+ "-ql",
40025
+ "-E",
40083
40026
  REDUCED_MOTION_GREP_PATTERN,
40084
40027
  "--",
40085
40028
  ...REDUCED_MOTION_FILE_GLOBS
40086
40029
  ], {
40087
40030
  cwd: rootDirectory,
40088
- encoding: "utf-8",
40089
40031
  stdio: [
40090
40032
  "ignore",
40091
40033
  "pipe",
40092
40034
  "pipe"
40093
40035
  ]
40094
40036
  });
40095
- if (!(!result.error && (result.status === 0 || result.status === GIT_GREP_NO_MATCH_STATUS))) return hasReducedMotionHandlingViaFilesystem(rootDirectory) ? [] : [MISSING_REDUCED_MOTION_DIAGNOSTIC];
40096
- return result.status === 0 && result.stdout.split("\n").some((filePath) => filePath.length > 0 && !hasIgnoredPathSegment(filePath)) ? [] : [MISSING_REDUCED_MOTION_DIAGNOSTIC];
40037
+ if (result.error) return [MISSING_REDUCED_MOTION_DIAGNOSTIC];
40038
+ if (result.status === 0) return [];
40039
+ return [MISSING_REDUCED_MOTION_DIAGNOSTIC];
40097
40040
  };
40098
40041
  const buildSecurityScanDiagnostic = (finding, entry, relativePath) => ({
40099
40042
  filePath: relativePath,
@@ -41005,7 +40948,6 @@ const isDiagnosticOnSurface = (diagnostic, surface, config) => {
41005
40948
  return true;
41006
40949
  };
41007
40950
  const filterDiagnosticsForSurface = (diagnostics, surface, config) => diagnostics.filter((diagnostic) => isDiagnosticOnSurface(diagnostic, surface, config));
41008
- const remainingDeadlineBudgetMs = (deadlineEpochMs) => Math.max(deadlineEpochMs - Date.now(), 0);
41009
40951
  /**
41010
40952
  * Budget for the dead-code phase, scaled to the work. deslop's graph build is
41011
40953
  * CPU-bound and roughly linear in file count, so a fixed 120s cap is too tight
@@ -41024,6 +40966,53 @@ const resolveDeadCodeTimeout = (input) => {
41024
40966
  phaseTimeoutMs: workerTimeoutMs + DEAD_CODE_PHASE_TIMEOUT_OVER_WORKER_MS
41025
40967
  };
41026
40968
  };
40969
+ const collectSizedSourceFiles = (rootDirectory, relativePaths) => {
40970
+ const entries = [];
40971
+ for (const relativePath of relativePaths) {
40972
+ const absolutePath = Path.resolve(rootDirectory, relativePath);
40973
+ const sizeBytes = statSourceFileSize(absolutePath);
40974
+ if (isLargeMinifiedFile(absolutePath, sizeBytes)) continue;
40975
+ entries.push({
40976
+ path: relativePath,
40977
+ sizeBytes: sizeBytes ?? 0
40978
+ });
40979
+ }
40980
+ return entries;
40981
+ };
40982
+ const listSourceFilesViaGit = (rootDirectory) => {
40983
+ const result = spawnSync("git", [
40984
+ "ls-files",
40985
+ "-z",
40986
+ "--cached",
40987
+ "--others",
40988
+ "--exclude-standard"
40989
+ ], {
40990
+ cwd: rootDirectory,
40991
+ encoding: "utf-8",
40992
+ maxBuffer: GIT_LS_FILES_MAX_BUFFER_BYTES
40993
+ });
40994
+ if (result.error || result.status !== 0) return null;
40995
+ return result.stdout.split("\0").filter((filePath) => filePath.length > 0 && isLintableSourceFile(filePath));
40996
+ };
40997
+ const listSourceFilesViaFilesystem = (rootDirectory) => {
40998
+ const filePaths = [];
40999
+ const stack = [rootDirectory];
41000
+ while (stack.length > 0) {
41001
+ const currentDirectory = stack.pop();
41002
+ const entries = readDirectoryEntries(currentDirectory);
41003
+ for (const entry of entries) {
41004
+ const absolutePath = Path.join(currentDirectory, entry.name);
41005
+ if (entry.isDirectory()) {
41006
+ if (!entry.name.startsWith(".") && !IGNORED_DIRECTORIES.has(entry.name)) stack.push(absolutePath);
41007
+ continue;
41008
+ }
41009
+ if (entry.isFile() && isLintableSourceFile(entry.name)) filePaths.push(Path.relative(rootDirectory, absolutePath).replace(/\\/g, "/"));
41010
+ }
41011
+ }
41012
+ return filePaths;
41013
+ };
41014
+ const listSourceFilesWithSize = (rootDirectory) => collectSizedSourceFiles(rootDirectory, listSourceFilesViaGit(rootDirectory) ?? listSourceFilesViaFilesystem(rootDirectory));
41015
+ const listSourceFiles = (rootDirectory) => listSourceFilesWithSize(rootDirectory).map((entry) => entry.path);
41027
41016
  const resolveLintIncludePaths = (rootDirectory, userConfig, project) => {
41028
41017
  if (!Array.isArray(userConfig?.ignore?.files) || userConfig.ignore.files.length === 0) return;
41029
41018
  const ignoredPatterns = compileIgnoredFilePatterns(userConfig);
@@ -41720,12 +41709,12 @@ const findFilesWithDisableDirectivesViaGit = async (rootDirectory, includePaths)
41720
41709
  return null;
41721
41710
  }
41722
41711
  if (grepResult === null) return null;
41723
- return grepResult.stdout.split("\n").filter((filePath) => filePath.length > 0 && isLintableSourceFile(filePath) && !hasIgnoredPathSegment(filePath));
41712
+ return grepResult.stdout.split("\n").filter((filePath) => filePath.length > 0 && isLintableSourceFile(filePath));
41724
41713
  };
41725
41714
  const findFilesWithDisableDirectivesViaFilesystem = (rootDirectory, includePaths) => {
41726
41715
  const matches = [];
41727
41716
  const checkFile = (relativePath) => {
41728
- if (!isLintableSourceFile(relativePath) || hasIgnoredPathSegment(relativePath)) return;
41717
+ if (!isLintableSourceFile(relativePath)) return;
41729
41718
  const absolutePath = Path.join(rootDirectory, relativePath);
41730
41719
  let content;
41731
41720
  try {
@@ -41739,7 +41728,22 @@ const findFilesWithDisableDirectivesViaFilesystem = (rootDirectory, includePaths
41739
41728
  for (const candidate of includePaths) checkFile(candidate);
41740
41729
  return matches;
41741
41730
  }
41742
- for (const { absolutePath } of walkSourceTreeFiles(rootDirectory)) checkFile(Path.relative(rootDirectory, absolutePath));
41731
+ const stack = [rootDirectory];
41732
+ while (stack.length > 0) {
41733
+ const current = stack.pop();
41734
+ if (current === void 0) continue;
41735
+ const entries = readDirectoryEntries(current);
41736
+ for (const entry of entries) {
41737
+ if (entry.isDirectory()) {
41738
+ if (entry.name.startsWith(".") || IGNORED_DIRECTORIES.has(entry.name)) continue;
41739
+ stack.push(Path.join(current, entry.name));
41740
+ continue;
41741
+ }
41742
+ if (!entry.isFile()) continue;
41743
+ const absolute = Path.join(current, entry.name);
41744
+ checkFile(Path.relative(rootDirectory, absolute));
41745
+ }
41746
+ }
41743
41747
  return matches;
41744
41748
  };
41745
41749
  const findFilesWithDisableDirectives = async (rootDirectory, includePaths) => await findFilesWithDisableDirectivesViaGit(rootDirectory, includePaths) ?? findFilesWithDisableDirectivesViaFilesystem(rootDirectory, includePaths);
@@ -42943,33 +42947,21 @@ const isParallelismRelatedSpawnError = (error) => {
42943
42947
  * loop with a slimmer config in that case.
42944
42948
  */
42945
42949
  const spawnLintBatches = async (input) => {
42946
- const { baseArgs, fileBatches, rootDirectory, nodeBinaryPath, project, onPartialFailure, onFileProgress, spawnTimeoutMs, outputMaxBytes, splitTotalBudgetMs = OXLINT_SPLIT_TOTAL_BUDGET_MS, splitMaxDepth = 8, signal, deadlineEpochMs } = input;
42950
+ const { baseArgs, fileBatches, rootDirectory, nodeBinaryPath, project, onPartialFailure, onFileProgress, spawnTimeoutMs, outputMaxBytes, splitTotalBudgetMs = OXLINT_SPLIT_TOTAL_BUDGET_MS, splitMaxDepth = 8, signal } = input;
42947
42951
  const requestedConcurrency = resolveScanConcurrency(input.concurrency ?? 1);
42948
42952
  const totalFileCount = fileBatches.reduce((sum, batch) => sum + batch.length, 0);
42949
42953
  const runBatchPass = async (concurrency) => {
42950
42954
  const allDiagnostics = [];
42951
42955
  const droppedFiles = [];
42952
- const deadlineSkippedFiles = [];
42953
- const isPastDeadline = () => deadlineEpochMs !== void 0 && remainingDeadlineBudgetMs(deadlineEpochMs) === 0;
42954
42956
  let firstDropReason = null;
42955
- const spawnLintBatch = async (batch, depth, batchState) => {
42956
- if (isPastDeadline()) {
42957
- deadlineSkippedFiles.push(...batch);
42958
- batchState.deadlineSkippedFileCount += batch.length;
42959
- return [];
42960
- }
42957
+ const spawnLintBatch = async (batch, depth, splitBudget) => {
42961
42958
  const batchArgs = [...baseArgs, ...batch];
42962
42959
  try {
42963
42960
  return parseOxlintOutput(await spawnOxlint(batchArgs, rootDirectory, nodeBinaryPath, spawnTimeoutMs, outputMaxBytes, signal), project, rootDirectory);
42964
42961
  } catch (error) {
42965
42962
  if (!isSplittableReactDoctorError(error)) throw error;
42966
- if (isPastDeadline()) {
42967
- deadlineSkippedFiles.push(...batch);
42968
- batchState.deadlineSkippedFileCount += batch.length;
42969
- return [];
42970
- }
42971
- batchState.deadlineMs ??= Date.now() + splitTotalBudgetMs;
42972
- const isBudgetElapsed = Date.now() >= batchState.deadlineMs;
42963
+ splitBudget.deadlineMs ??= Date.now() + splitTotalBudgetMs;
42964
+ const isBudgetElapsed = Date.now() >= splitBudget.deadlineMs;
42973
42965
  const isDepthCapReached = depth >= splitMaxDepth;
42974
42966
  if (batch.length <= 1 || isBudgetElapsed || isDepthCapReached) {
42975
42967
  droppedFiles.push(...batch);
@@ -42982,7 +42974,7 @@ const spawnLintBatches = async (input) => {
42982
42974
  return [];
42983
42975
  }
42984
42976
  const splitIndex = Math.ceil(batch.length / 2);
42985
- return [...await spawnLintBatch(batch.slice(0, splitIndex), depth + 1, batchState), ...await spawnLintBatch(batch.slice(splitIndex), depth + 1, batchState)];
42977
+ return [...await spawnLintBatch(batch.slice(0, splitIndex), depth + 1, splitBudget), ...await spawnLintBatch(batch.slice(splitIndex), depth + 1, splitBudget)];
42986
42978
  }
42987
42979
  };
42988
42980
  let startedFileCount = 0;
@@ -42998,17 +42990,9 @@ const spawnLintBatches = async (input) => {
42998
42990
  progressTimer?.unref?.();
42999
42991
  try {
43000
42992
  const batchResults = await mapWithConcurrency(fileBatches, concurrency, async (batch) => {
43001
- if (isPastDeadline()) {
43002
- deadlineSkippedFiles.push(...batch);
43003
- return [];
43004
- }
43005
42993
  startedFileCount += batch.length;
43006
- const batchState = {
43007
- deadlineMs: null,
43008
- deadlineSkippedFileCount: 0
43009
- };
43010
- const batchDiagnostics = await spawnLintBatch(batch, 0, batchState);
43011
- scannedFileCount += batch.length - batchState.deadlineSkippedFileCount;
42994
+ const batchDiagnostics = await spawnLintBatch(batch, 0, { deadlineMs: null });
42995
+ scannedFileCount += batch.length;
43012
42996
  if (onFileProgress) {
43013
42997
  displayedFileCount = Math.min(Math.max(displayedFileCount, scannedFileCount), totalFileCount);
43014
42998
  onFileProgress(displayedFileCount, totalFileCount);
@@ -43019,13 +43003,12 @@ const spawnLintBatches = async (input) => {
43019
43003
  } finally {
43020
43004
  if (progressTimer !== null) clearInterval(progressTimer);
43021
43005
  }
43022
- const reportSkippedFiles = (skippedFiles, buildMessage) => {
43023
- if (skippedFiles.length === 0 || !onPartialFailure) return;
43024
- onPartialFailure(buildMessage(`${skippedFiles.slice(0, 3).join(", ")}${skippedFiles.length > 3 ? `, +${skippedFiles.length - 3} more` : ""}`));
43025
- };
43026
- const reasonHint = firstDropReason ? ` — first failure: ${firstDropReason}` : "";
43027
- reportSkippedFiles(droppedFiles, (fileListPreview) => `${droppedFiles.length} file(s) failed to lint and were skipped (${fileListPreview})${reasonHint}`);
43028
- reportSkippedFiles(deadlineSkippedFiles, (fileListPreview) => `${deadlineSkippedFiles.length} file(s) skipped — max scan duration reached before they were linted (${fileListPreview})`);
43006
+ if (droppedFiles.length > 0 && onPartialFailure) {
43007
+ const previewFiles = droppedFiles.slice(0, 3).join(", ");
43008
+ const remainderHint = droppedFiles.length > 3 ? `, +${droppedFiles.length - 3} more` : "";
43009
+ const reasonHint = firstDropReason ? ` — first failure: ${firstDropReason}` : "";
43010
+ onPartialFailure(`${droppedFiles.length} file(s) failed to lint and were skipped (${previewFiles}${remainderHint})${reasonHint}`);
43011
+ }
43029
43012
  return allDiagnostics;
43030
43013
  };
43031
43014
  let diagnostics;
@@ -43229,8 +43212,7 @@ const runOxlint = async (options) => {
43229
43212
  spawnTimeoutMs,
43230
43213
  outputMaxBytes,
43231
43214
  concurrency: options.concurrency,
43232
- signal: options.signal,
43233
- deadlineEpochMs: options.deadlineEpochMs
43215
+ signal: options.signal
43234
43216
  });
43235
43217
  writeOxlintConfig(passConfigPath, buildConfigForPass({}));
43236
43218
  try {
@@ -43329,8 +43311,7 @@ const runOxlint = async (options) => {
43329
43311
  spawnTimeoutMs,
43330
43312
  outputMaxBytes,
43331
43313
  concurrency: options.concurrency,
43332
- signal: options.signal,
43333
- deadlineEpochMs: options.deadlineEpochMs
43314
+ signal: options.signal
43334
43315
  });
43335
43316
  writeOxlintConfig(configPath, buildConfig({ extendsPaths }));
43336
43317
  try {
@@ -43434,8 +43415,7 @@ var Linter = class Linter extends Service()("react-doctor/Linter") {
43434
43415
  outputMaxBytes,
43435
43416
  concurrency,
43436
43417
  signal,
43437
- lintBatchOrdering,
43438
- deadlineEpochMs: input.deadlineEpochMs
43418
+ lintBatchOrdering
43439
43419
  }),
43440
43420
  catch: ensureReactDoctorError
43441
43421
  });
@@ -44195,7 +44175,6 @@ const runInspect = (input, hooks = {}) => gen(function* () {
44195
44175
  const deadCodePhaseTimeoutMs = yield* DeadCodePhaseTimeoutMs;
44196
44176
  const resolveDeadCodePhaseTimeoutMs = (scaledPhaseTimeoutMs) => deadCodePhaseTimeoutMs === 15e4 ? scaledPhaseTimeoutMs : deadCodePhaseTimeoutMs;
44197
44177
  const workerCountSuffix = scanConcurrency > 1 ? ` ${highlighter.dim(`[~${scanConcurrency} workers]`)}` : "";
44198
- const capToDeadline = (phaseTimeoutMs) => input.deadlineEpochMs === void 0 ? phaseTimeoutMs : Math.min(phaseTimeoutMs, remainingDeadlineBudgetMs(input.deadlineEpochMs));
44199
44178
  const shouldRunDeadCode = input.runDeadCode && !isDiffMode && (showWarnings || deadCodeMaySurfaceWhenWarningsHidden(resolvedConfig.config));
44200
44179
  const deadCodeOverlapMode = yield* DeadCodeOverlap;
44201
44180
  const shouldOverlapDeadCode = shouldRunDeadCode && deadCodeOverlapMode === "on";
@@ -44225,7 +44204,7 @@ const runInspect = (input, hooks = {}) => gen(function* () {
44225
44204
  });
44226
44205
  const deadCodeFiber = shouldOverlapDeadCode ? yield* forkChild(buildCollectDeadCode({
44227
44206
  workerTimeoutMs: overlapDeadCodeTimeout.workerTimeoutMs,
44228
- phaseTimeoutMs: capToDeadline(resolveDeadCodePhaseTimeoutMs(overlapDeadCodeTimeout.phaseTimeoutMs))
44207
+ phaseTimeoutMs: resolveDeadCodePhaseTimeoutMs(overlapDeadCodeTimeout.phaseTimeoutMs)
44229
44208
  })) : null;
44230
44209
  const scanProgress = yield* progressService.start("Scanning...");
44231
44210
  const scanStartTime = Date.now();
@@ -44250,8 +44229,7 @@ const runInspect = (input, hooks = {}) => gen(function* () {
44250
44229
  onCacheStats: (cacheHitFileCount, totalConsideredFileCount) => {
44251
44230
  lintCacheHitFileCount = cacheHitFileCount;
44252
44231
  lintCacheTotalFileCount = totalConsideredFileCount;
44253
- },
44254
- deadlineEpochMs: input.deadlineEpochMs
44232
+ }
44255
44233
  }).pipe(catchTag("ReactDoctorError", (error) => unwrap(gen(function* () {
44256
44234
  yield* set(lintFailure, {
44257
44235
  didFail: true,
@@ -44278,13 +44256,7 @@ const runInspect = (input, hooks = {}) => gen(function* () {
44278
44256
  let deadCodeCollected = [];
44279
44257
  if (lintFailureState.didFail) {
44280
44258
  if (deadCodeFiber !== null) yield* interrupt(deadCodeFiber);
44281
- } else if (shouldRunDeadCode) if (input.deadlineEpochMs !== void 0 && remainingDeadlineBudgetMs(input.deadlineEpochMs) === 0) {
44282
- if (deadCodeFiber !== null) yield* interrupt(deadCodeFiber);
44283
- yield* set(deadCodeFailure, {
44284
- didFail: true,
44285
- reason: "Dead-code analysis skipped — max scan duration reached."
44286
- });
44287
- } else {
44259
+ } else if (shouldRunDeadCode) {
44288
44260
  yield* scanProgress.update(`Scanned ${scannedFilesLabel}, analyzing dead code...`);
44289
44261
  const sequentialDeadCodeTimeout = resolveDeadCodeTimeout({
44290
44262
  sourceFileCount: totalFileCount,
@@ -44293,7 +44265,7 @@ const runInspect = (input, hooks = {}) => gen(function* () {
44293
44265
  });
44294
44266
  deadCodeCollected = deadCodeFiber !== null ? yield* join(deadCodeFiber) : yield* buildCollectDeadCode({
44295
44267
  workerTimeoutMs: sequentialDeadCodeTimeout.workerTimeoutMs,
44296
- phaseTimeoutMs: capToDeadline(resolveDeadCodePhaseTimeoutMs(sequentialDeadCodeTimeout.phaseTimeoutMs))
44268
+ phaseTimeoutMs: resolveDeadCodePhaseTimeoutMs(sequentialDeadCodeTimeout.phaseTimeoutMs)
44297
44269
  });
44298
44270
  }
44299
44271
  const deadCodeFailureState = lintFailureState.didFail ? {
@@ -44331,7 +44303,7 @@ const runInspect = (input, hooks = {}) => gen(function* () {
44331
44303
  };
44332
44304
  const scoreSurface = input.scoreSurface ?? "score";
44333
44305
  const scoreDiagnostics = filterDiagnosticsForSurface([...finalDiagnostics], scoreSurface, resolvedConfig.config);
44334
- const score = lintFailureState.didFail || deadCodeFailureState.didFail ? null : yield* scoreService.compute({
44306
+ const score = lintFailureState.didFail ? null : yield* scoreService.compute({
44335
44307
  diagnostics: scoreDiagnostics,
44336
44308
  isCi: input.isCi,
44337
44309
  metadata: scoreMetadata
@@ -45232,7 +45204,7 @@ const makeNoopConsole = () => ({
45232
45204
  });
45233
45205
  //#endregion
45234
45206
  //#region src/cli/utils/version.ts
45235
- const VERSION = "0.5.8-dev.8232e96";
45207
+ const VERSION = "0.5.8-dev.88a5c3c";
45236
45208
  //#endregion
45237
45209
  //#region src/cli/utils/json-mode.ts
45238
45210
  let context = null;
@@ -45601,13 +45573,13 @@ const isDevVersion = (version) => version === "0.0.0" || version.includes("-");
45601
45573
  * uploads source-map artifacts under, so stack frames symbolicate. Honors the
45602
45574
  * standard `SENTRY_RELEASE` override.
45603
45575
  */
45604
- const resolveSentryRelease = () => process.env.SENTRY_RELEASE || `react-doctor@0.5.8-dev.8232e96`;
45576
+ const resolveSentryRelease = () => process.env.SENTRY_RELEASE || `react-doctor@0.5.8-dev.88a5c3c`;
45605
45577
  /**
45606
45578
  * Deployment environment shown in Sentry's environment filter. Defaults to
45607
45579
  * `production` for tagged releases and `development` for dev/unbuilt versions,
45608
45580
  * overridable via the standard `SENTRY_ENVIRONMENT` env var.
45609
45581
  */
45610
- const resolveSentryEnvironment = () => process.env.SENTRY_ENVIRONMENT || (isDevVersion("0.5.8-dev.8232e96") ? "development" : "production");
45582
+ const resolveSentryEnvironment = () => process.env.SENTRY_ENVIRONMENT || (isDevVersion("0.5.8-dev.88a5c3c") ? "development" : "production");
45611
45583
  /**
45612
45584
  * Performance-tracing sample rate in `[0, 1]`. Reads `SENTRY_TRACES_SAMPLE_RATE`
45613
45585
  * (set to `0` to disable tracing) and falls back to
@@ -57638,7 +57610,7 @@ const recordScanMetrics = (input) => {
57638
57610
  });
57639
57611
  if (result.diagnostics.length === 0 && !hasSkippedChecks) recordCount(METRIC.scanClean, 1, { mode: input.mode });
57640
57612
  if (result.score) recordDistribution(METRIC.scanScore, result.score.score, { attributes: { mode: input.mode } });
57641
- else if (!input.noScore && !input.didLintFail && !input.didDeadCodeFail) recordCount(METRIC.scoreUnavailable, 1, { mode: input.mode });
57613
+ else if (!input.noScore && !input.didLintFail) recordCount(METRIC.scoreUnavailable, 1, { mode: input.mode });
57642
57614
  if (input.baselineDegraded) recordCount(METRIC.baselineDegraded, 1);
57643
57615
  if (input.didLintFail) recordCount(METRIC.lintFailed, 1, { reasonKind: input.lintFailureReasonKind });
57644
57616
  if (input.didDeadCodeFail) recordCount(METRIC.deadCodeFailed, 1);
@@ -57827,7 +57799,6 @@ const buildOutcomeAttributes = (input) => {
57827
57799
  failureReasonKind: input.lintFailureReasonKind ?? null,
57828
57800
  partialFailureCount: input.lintPartialFailureCount ?? null,
57829
57801
  droppedFileCount: input.lintDroppedFileCount ?? null,
57830
- deadlineSkippedFileCount: input.lintDeadlineSkippedFileCount ?? null,
57831
57802
  cacheHitFiles: result.lintCacheHitFileCount ?? null,
57832
57803
  cacheTotalFiles: result.lintCacheTotalFileCount ?? null,
57833
57804
  cacheHitRatio: result.lintCacheTotalFileCount != null && result.lintCacheTotalFileCount > 0 ? (result.lintCacheHitFileCount ?? 0) / result.lintCacheTotalFileCount : null
@@ -57875,7 +57846,6 @@ const buildScanAttributes = (input) => {
57875
57846
  scope: input.scope,
57876
57847
  parallel: input.parallel,
57877
57848
  workerCount: input.workerCount ?? null,
57878
- maxDurationMs: input.maxDurationMs,
57879
57849
  lint: input.lint,
57880
57850
  deadCode: input.deadCode,
57881
57851
  scoreOnly: input.scoreOnly,
@@ -57941,19 +57911,12 @@ const resolveWorkerTelemetry = (resolvedWorkerCount, pinnedConcurrency) => {
57941
57911
  };
57942
57912
  };
57943
57913
  //#endregion
57944
- //#region src/cli/utils/sum-file-counts-matching.ts
57945
- const sumFileCountsMatching = (messages, countPattern) => messages.reduce((total, message) => {
57946
- const match = countPattern.exec(message);
57947
- return match ? total + Number(match[1]) : total;
57948
- }, 0);
57949
- //#endregion
57950
- //#region src/cli/utils/count-deadline-skipped-files.ts
57951
- const DEADLINE_SKIPPED_MESSAGE_PATTERN = /^(\d+) file\(s\) skipped — max scan duration reached/;
57952
- const countDeadlineSkippedFiles = (lintPartialFailures) => sumFileCountsMatching(lintPartialFailures, DEADLINE_SKIPPED_MESSAGE_PATTERN);
57953
- //#endregion
57954
57914
  //#region src/cli/utils/count-dropped-lint-files.ts
57955
57915
  const DROPPED_FILES_MESSAGE_PATTERN = /^(\d+) file\(s\) failed to lint and were skipped/;
57956
- const countDroppedLintFiles = (lintPartialFailures) => sumFileCountsMatching(lintPartialFailures, DROPPED_FILES_MESSAGE_PATTERN);
57916
+ const countDroppedLintFiles = (lintPartialFailures) => lintPartialFailures.reduce((total, message) => {
57917
+ const match = DROPPED_FILES_MESSAGE_PATTERN.exec(message);
57918
+ return match ? total + Number(match[1]) : total;
57919
+ }, 0);
57957
57920
  //#endregion
57958
57921
  //#region src/cli/utils/materialize-baseline-files.ts
57959
57922
  /**
@@ -59361,7 +59324,6 @@ const mergeInspectOptions = (inputOptions, userConfig) => ({
59361
59324
  suppressRendering: inputOptions.suppressRendering ?? false,
59362
59325
  concurrentScan: inputOptions.concurrentScan ?? false,
59363
59326
  concurrency: inputOptions.concurrency,
59364
- maxDurationMs: inputOptions.maxDurationMs ?? null,
59365
59327
  baseline: inputOptions.baseline ?? null,
59366
59328
  changedLineRanges: inputOptions.changedLineRanges ?? null,
59367
59329
  supplyChainManifestChanged: inputOptions.supplyChainManifestChanged ?? false
@@ -59377,7 +59339,6 @@ const buildRunEventConfig = (options, userConfig, hasCustomConfig, resolvedWorke
59377
59339
  scope: deriveScope(options),
59378
59340
  parallel,
59379
59341
  workerCount,
59380
- maxDurationMs: options.maxDurationMs,
59381
59342
  lint: options.lint,
59382
59343
  deadCode: options.deadCode,
59383
59344
  scoreOnly: options.scoreOnly,
@@ -59392,7 +59353,6 @@ const buildRunEventConfig = (options, userConfig, hasCustomConfig, resolvedWorke
59392
59353
  };
59393
59354
  const inspect = async (directory, inputOptions = {}) => {
59394
59355
  const startTime = performance$1.now();
59395
- const deadlineEpochMs = inputOptions.deadlineEpochMs ?? (inputOptions.maxDurationMs != null ? Date.now() + inputOptions.maxDurationMs : null);
59396
59356
  const isConcurrentScan = inputOptions.concurrentScan === true;
59397
59357
  if (!isConcurrentScan) resetSentryRunState();
59398
59358
  const hasConfigOverride = inputOptions.configOverride !== void 0;
@@ -59416,7 +59376,7 @@ const inspect = async (directory, inputOptions = {}) => {
59416
59376
  try {
59417
59377
  const result = await withSentryRunSpan(async (rootSentrySpan) => {
59418
59378
  try {
59419
- return await runInspectWithRuntime(scanDirectory, options, userConfig, hasConfigOverride, configSourceDirectory, startTime, deadlineEpochMs, rootSentrySpan);
59379
+ return await runInspectWithRuntime(scanDirectory, options, userConfig, hasConfigOverride, configSourceDirectory, startTime, rootSentrySpan);
59420
59380
  } catch (error) {
59421
59381
  recordRunEvent(rootSentrySpan, {
59422
59382
  ...buildRunEventConfig(options, userConfig, userConfig !== null),
@@ -59432,7 +59392,6 @@ const inspect = async (directory, inputOptions = {}) => {
59432
59392
  if (ownsSpinnerSilence) setSpinnerSilent(wasSpinnerSilent);
59433
59393
  }
59434
59394
  };
59435
- const countIncompleteLintFiles = (lintPartialFailures) => countDroppedLintFiles(lintPartialFailures) + countDeadlineSkippedFiles(lintPartialFailures);
59436
59395
  /**
59437
59396
  * Runs a second, lint-only scan over the changed files as they existed at the
59438
59397
  * baseline ref (materialized into a temp tree with head's config) and diffs it
@@ -59482,10 +59441,9 @@ const runBaselineComparison = async (params) => {
59482
59441
  runId: getRunId(),
59483
59442
  resolveLocalGithubViewerPermission: false,
59484
59443
  suppressScanSummary: true,
59485
- supplyChainManifestChanged: params.options.supplyChainManifestChanged,
59486
- deadlineEpochMs: params.deadlineEpochMs ?? void 0
59444
+ supplyChainManifestChanged: params.options.supplyChainManifestChanged
59487
59445
  }, {}).pipe(provide(baseLayers), provideService$2(Console, silentConsole))));
59488
- if (baseOutput.didLintFail || countIncompleteLintFiles(baseOutput.lintPartialFailures) > 0) return null;
59446
+ if (baseOutput.didLintFail) return null;
59489
59447
  const delta = computeDiagnosticDelta({
59490
59448
  headDiagnostics: params.headDiagnostics,
59491
59449
  baseDiagnostics: baseOutput.diagnostics,
@@ -59504,7 +59462,7 @@ const runBaselineComparison = async (params) => {
59504
59462
  snapshot.cleanup();
59505
59463
  }
59506
59464
  };
59507
- const runInspectWithRuntime = async (directory, options, userConfig, hasConfigOverride, configSourceDirectory, startTime, deadlineEpochMs, rootSentrySpan) => {
59465
+ const runInspectWithRuntime = async (directory, options, userConfig, hasConfigOverride, configSourceDirectory, startTime, rootSentrySpan) => {
59508
59466
  const isDiffMode = options.includePaths.length > 0;
59509
59467
  const resolvedNodeBinaryPath = await resolveOxlintNode(options.lint, options.scoreOnly || options.silent);
59510
59468
  const lintBindingMissing = options.lint && !resolvedNodeBinaryPath;
@@ -59570,8 +59528,7 @@ const runInspectWithRuntime = async (directory, options, userConfig, hasConfigOv
59570
59528
  resolveLocalGithubViewerPermission: !options.noScore,
59571
59529
  suppressScanSummary: options.suppressRendering,
59572
59530
  supplyChainManifestChanged: options.supplyChainManifestChanged,
59573
- concurrentScan: options.concurrentScan,
59574
- deadlineEpochMs: deadlineEpochMs ?? void 0
59531
+ concurrentScan: options.concurrentScan
59575
59532
  }, { beforeLint: (projectInfo, lintIncludePaths) => gen(function* () {
59576
59533
  recordSentryProjectContext(projectInfo, rootSentrySpan, { concurrentScan: options.concurrentScan });
59577
59534
  recordCount(METRIC.projectDetected, 1);
@@ -59592,7 +59549,7 @@ const runInspectWithRuntime = async (directory, options, userConfig, hasConfigOv
59592
59549
  else runConsole(error$1(highlighter.error(lintFailureReason)));
59593
59550
  let inspectDiagnostics = output.diagnostics;
59594
59551
  let baselineDelta;
59595
- if (options.baseline && isDiffMode && !didLintFail && countIncompleteLintFiles(output.lintPartialFailures) === 0) {
59552
+ if (options.baseline && isDiffMode && !didLintFail) {
59596
59553
  const comparison = await runBaselineComparison({
59597
59554
  directory,
59598
59555
  options,
@@ -59601,8 +59558,7 @@ const runInspectWithRuntime = async (directory, options, userConfig, hasConfigOv
59601
59558
  headProjectInfo: output.project,
59602
59559
  headDiagnostics: output.diagnostics,
59603
59560
  resolvedNodeBinaryPath,
59604
- baselineRef: options.baseline.ref,
59605
- deadlineEpochMs
59561
+ baselineRef: options.baseline.ref
59606
59562
  });
59607
59563
  if (comparison) {
59608
59564
  inspectDiagnostics = comparison.displayDiagnostics;
@@ -59710,7 +59666,6 @@ const renderAndRecordScan = async (input) => {
59710
59666
  lintFailureReasonKind: input.payload.lintFailureReasonKind,
59711
59667
  lintPartialFailureCount: input.payload.lintPartialFailures.length,
59712
59668
  lintDroppedFileCount: countDroppedLintFiles(input.payload.lintPartialFailures),
59713
- lintDeadlineSkippedFileCount: countDeadlineSkippedFiles(input.payload.lintPartialFailures),
59714
59669
  didDeadCodeFail: input.payload.didDeadCodeFail,
59715
59670
  supplyChainOverlapTimedOut: input.payload.supplyChainOverlapTimedOut,
59716
59671
  securityScanFailed: input.payload.securityScanFailed,
@@ -61787,17 +61742,6 @@ const printAgentInstallHint = (writeLine = defaultWriteLine) => {
61787
61742
  for (const line of AGENT_INSTALL_HINT_LINES) writeLine(line);
61788
61743
  };
61789
61744
  //#endregion
61790
- //#region src/cli/utils/resolve-max-duration-flag.ts
61791
- const resolveMaxDurationFlag = (maxDuration) => {
61792
- if (maxDuration === void 0) return void 0;
61793
- const seconds = Number(maxDuration);
61794
- if (!Number.isFinite(seconds) || seconds <= 0) {
61795
- cliLogger.warn(`Invalid --max-duration "${maxDuration}". Expected a positive number of seconds; running without a budget.`);
61796
- return;
61797
- }
61798
- return seconds * MILLISECONDS_PER_SECOND;
61799
- };
61800
- //#endregion
61801
61745
  //#region src/cli/utils/resolve-parallel-flag.ts
61802
61746
  /**
61803
61747
  * Maps the `--no-parallel` flag to an `InspectOptions.concurrency` value.
@@ -61832,7 +61776,6 @@ const resolveCliInspectOptions = (flags, userConfig) => {
61832
61776
  isCi: isCiEnvironment(),
61833
61777
  silent: Boolean(flags.json),
61834
61778
  concurrency: resolveParallelFlag(flags.parallel),
61835
- maxDurationMs: resolveMaxDurationFlag(flags.maxDuration),
61836
61779
  categoryFilters: resolveCliCategories(flags.category)
61837
61780
  };
61838
61781
  };
@@ -62238,7 +62181,6 @@ const inspectAction = async (directory, flags) => {
62238
62181
  if (!isQuiet) if (!flags.verbose && canAnimateOnboarding(process.stdout)) await runPromise(playWelcomeScene({ speedMultiplier: !isOnboardingForced() && hasCompletedOnboarding() ? 2 : 1 }));
62239
62182
  else runSync(printBrandedHeader);
62240
62183
  const scanOptions = resolveCliInspectOptions(flags, userConfig);
62241
- const scanDeadlineEpochMs = scanOptions.maxDurationMs !== void 0 ? Date.now() + scanOptions.maxDurationMs : void 0;
62242
62184
  const categoryFilters = new Set(scanOptions.categoryFilters ?? []);
62243
62185
  const skipPrompts = shouldSkipPrompts({
62244
62186
  yes: flags.yes,
@@ -62284,7 +62226,6 @@ const inspectAction = async (directory, flags) => {
62284
62226
  try {
62285
62227
  const scanResult = await inspect(snapshot.tempDirectory, {
62286
62228
  ...scanOptions,
62287
- deadlineEpochMs: scanDeadlineEpochMs,
62288
62229
  includePaths: snapshot.stagedFiles,
62289
62230
  configOverride: userConfig,
62290
62231
  configSourceDirectory: scanTarget.configSourceDirectory ?? void 0,
@@ -62387,7 +62328,6 @@ const inspectAction = async (directory, flags) => {
62387
62328
  if (!isQuiet && !isMultiProject) cliLogger.dim(" ");
62388
62329
  const scanResult = await inspect(scanDirectory, {
62389
62330
  ...scanOptions,
62390
- deadlineEpochMs: scanDeadlineEpochMs,
62391
62331
  includePaths,
62392
62332
  configOverride: projectConfig,
62393
62333
  configSourceDirectory: projectConfigSourceDirectory ?? void 0,
@@ -63303,7 +63243,6 @@ const ROOT_FLAG_SPEC = {
63303
63243
  "--changed-files-from",
63304
63244
  "--blocking",
63305
63245
  "--json-out",
63306
- "--max-duration",
63307
63246
  "--output-dir",
63308
63247
  "--fail-on",
63309
63248
  "--project",
@@ -63540,7 +63479,7 @@ ${highlighter.dim("Learn more:")}
63540
63479
  ${highlighter.info(CI_URL)}
63541
63480
  `;
63542
63481
  const collectCategoryOption = (value, previousValues) => [...previousValues ?? [], value];
63543
- 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("--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 to scan/report: full (default), files, changed (only new issues vs base), or lines (only changed lines)").option("--base <ref>", "base git ref for files/changed/lines scope (auto-detected when omitted)").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);
63482
+ 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("--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 to scan/report: full (default), files, changed (only new issues vs base), or lines (only changed lines)").option("--base <ref>", "base git ref for files/changed/lines scope (auto-detected when omitted)").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("--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);
63544
63483
  program.action(inspectAction);
63545
63484
  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));
63546
63485
  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);
@@ -63590,4 +63529,4 @@ Promise.resolve().then(() => assertNoRemovedFlags(process.argv)).then(() => prog
63590
63529
  export {};
63591
63530
 
63592
63531
  //# sourceMappingURL=cli.js.map
63593
- //# debugId=56ca0cbd-9f70-5a50-8496-43abdf078431
63532
+ //# debugId=8495d0ec-c532-5e58-be11-e0bff7202ba5