react-doctor 0.5.8 → 0.6.0-dev.0748a4b
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/LICENSE +14 -1
- package/README.md +9 -3
- package/dist/cli.js +10097 -1857
- package/dist/index.d.ts +23 -2
- package/dist/index.js +650 -361
- package/dist/lsp.js +619 -386
- package/package.json +11 -9
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]="
|
|
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";
|
|
@@ -7,7 +7,7 @@ import fs from "node:fs";
|
|
|
7
7
|
import * as Path from "node:path";
|
|
8
8
|
import path from "node:path";
|
|
9
9
|
import * as NodeChildProcess from "node:child_process";
|
|
10
|
-
import { spawn, spawnSync } from "node:child_process";
|
|
10
|
+
import { execFileSync, spawn, spawnSync } from "node:child_process";
|
|
11
11
|
import * as ts from "typescript";
|
|
12
12
|
import reactDoctorPlugin, { ALL_REACT_DOCTOR_RULE_KEYS, CROSS_FILE_RULE_IDS, FRAMEWORK_SPECIFIC_RULE_KEYS, MOTION_LIBRARY_PACKAGES, REACT_COMPILER_RULES, REACT_DOCTOR_RULES, classifySecurityScanFile, shouldReadSecurityScanContent } from "oxlint-plugin-react-doctor";
|
|
13
13
|
import * as OS from "node:os";
|
|
@@ -5996,7 +5996,7 @@ const composePassthrough = /* @__PURE__ */ dual(2, (left, right) => (input) => {
|
|
|
5996
5996
|
* @since 2.0.0
|
|
5997
5997
|
*/
|
|
5998
5998
|
const Scheduler = /* @__PURE__ */ Reference("effect/Scheduler", { defaultValue: () => new MixedScheduler() });
|
|
5999
|
-
const setImmediate = "setImmediate" in globalThis ? (f) => {
|
|
5999
|
+
const setImmediate$1 = "setImmediate" in globalThis ? (f) => {
|
|
6000
6000
|
const timer = globalThis.setImmediate(f);
|
|
6001
6001
|
return () => globalThis.clearImmediate(timer);
|
|
6002
6002
|
} : (f) => {
|
|
@@ -6040,7 +6040,7 @@ var PriorityBuckets = class {
|
|
|
6040
6040
|
var MixedScheduler = class {
|
|
6041
6041
|
executionMode;
|
|
6042
6042
|
setImmediate;
|
|
6043
|
-
constructor(executionMode = "async", setImmediateFn = setImmediate) {
|
|
6043
|
+
constructor(executionMode = "async", setImmediateFn = setImmediate$1) {
|
|
6044
6044
|
this.executionMode = executionMode;
|
|
6045
6045
|
this.setImmediate = setImmediateFn;
|
|
6046
6046
|
}
|
|
@@ -6065,7 +6065,7 @@ var MixedSchedulerDispatcher = class {
|
|
|
6065
6065
|
tasks = /* @__PURE__ */ new PriorityBuckets();
|
|
6066
6066
|
running = void 0;
|
|
6067
6067
|
setImmediate;
|
|
6068
|
-
constructor(setImmediateFn = setImmediate) {
|
|
6068
|
+
constructor(setImmediateFn = setImmediate$1) {
|
|
6069
6069
|
this.setImmediate = setImmediateFn;
|
|
6070
6070
|
}
|
|
6071
6071
|
/**
|
|
@@ -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
|
-
|
|
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
|
-
|
|
32858
|
-
for (const entry of
|
|
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 (!
|
|
33001
|
+
if (!IGNORED_DIRECTORIES.has(entry.name)) stack.push(absolutePath);
|
|
32861
33002
|
continue;
|
|
32862
33003
|
}
|
|
32863
|
-
if (entry.isFile()
|
|
33004
|
+
if (entry.isFile()) yield {
|
|
33005
|
+
absolutePath,
|
|
33006
|
+
name: entry.name
|
|
33007
|
+
};
|
|
32864
33008
|
}
|
|
32865
33009
|
}
|
|
32866
|
-
|
|
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
|
|
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) && !
|
|
33037
|
+
return result.stdout.split("\0").filter((filePath) => filePath.length > 0 && isLintableSourceFile(filePath) && !hasIgnoredPathSegment(filePath));
|
|
33038
|
+
};
|
|
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;
|
|
32882
33043
|
};
|
|
32883
|
-
const
|
|
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,113 +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 PER_WORKER_MEM_BUDGET_BYTES = 1024 * 1024 * 1024;
|
|
33698
|
-
const DEFAULT_BRANCH_CANDIDATES = ["main", "master"];
|
|
33699
|
-
const ADOPTABLE_LINT_CONFIG_FILENAMES = [".oxlintrc.json", ".eslintrc.json"];
|
|
33700
|
-
const GIT_SHOW_MAX_BUFFER_BYTES = 10 * 1024 * 1024;
|
|
33701
|
-
const ES_TARGET_YEAR_BY_NAME = {
|
|
33702
|
-
es3: 1999,
|
|
33703
|
-
es5: 2009,
|
|
33704
|
-
es6: 2015,
|
|
33705
|
-
es2015: 2015,
|
|
33706
|
-
es2016: 2016,
|
|
33707
|
-
es2017: 2017,
|
|
33708
|
-
es2018: 2018,
|
|
33709
|
-
es2019: 2019,
|
|
33710
|
-
es2020: 2020,
|
|
33711
|
-
es2021: 2021,
|
|
33712
|
-
es2022: 2022,
|
|
33713
|
-
es2023: 2023,
|
|
33714
|
-
es2024: 2024,
|
|
33715
|
-
es2025: 2025,
|
|
33716
|
-
esnext: 9999
|
|
33717
|
-
};
|
|
33718
|
-
/**
|
|
33719
|
-
* tsconfig filenames probed when resolving a project's TypeScript
|
|
33720
|
-
* compiler options — the root config first, then a monorepo base config.
|
|
33721
|
-
*/
|
|
33722
|
-
const TSCONFIG_FILENAMES = ["tsconfig.json", "tsconfig.base.json"];
|
|
33723
|
-
/**
|
|
33724
|
-
* Project-config files that `StagedFiles.materialize` copies into
|
|
33725
|
-
* the temp directory alongside staged sources so oxlint resolves
|
|
33726
|
-
* `tsconfig` / `package.json` / lint configs the same way it would
|
|
33727
|
-
* in the working tree. Hoisted out of the staged-files helper so
|
|
33728
|
-
* the constant lives next to the rest of the IO budget knobs.
|
|
33729
|
-
*/
|
|
33730
|
-
const STAGED_FILES_PROJECT_CONFIG_FILENAMES = [
|
|
33731
|
-
"tsconfig.json",
|
|
33732
|
-
"tsconfig.base.json",
|
|
33733
|
-
"package.json",
|
|
33734
|
-
"doctor.config.ts",
|
|
33735
|
-
"doctor.config.mts",
|
|
33736
|
-
"doctor.config.cts",
|
|
33737
|
-
"doctor.config.js",
|
|
33738
|
-
"doctor.config.mjs",
|
|
33739
|
-
"doctor.config.cjs",
|
|
33740
|
-
"doctor.config.json",
|
|
33741
|
-
"doctor.config.jsonc",
|
|
33742
|
-
"oxlint.json",
|
|
33743
|
-
".oxlintrc.json"
|
|
33744
|
-
];
|
|
33745
|
-
const OXLINT_OUTPUT_MAX_BYTES = 50 * 1024 * 1024;
|
|
33746
|
-
const OXLINT_SPAWN_TIMEOUT_MS = 6e4;
|
|
33747
|
-
const NODE_COMPILE_CACHE_DIR_NAME = "node-compile-cache";
|
|
33748
|
-
const DEAD_CODE_WORKER_TIMEOUT_MS = 12e4;
|
|
33749
|
-
const OXLINT_SPLIT_TOTAL_BUDGET_MS = 18e4;
|
|
33750
|
-
const DEAD_CODE_PHASE_TIMEOUT_MS = 15e4;
|
|
33751
|
-
const LINT_PHASE_TIMEOUT_MS = 3e5;
|
|
33752
|
-
const SCAN_TOTAL_DEADLINE_MS = 9e5;
|
|
33753
|
-
const DEAD_CODE_WORKER_MAX_OLD_SPACE_MB = 8192;
|
|
33754
|
-
const DEAD_CODE_TIMEOUT_CEILING_MS = 6e5;
|
|
33755
|
-
const DEAD_CODE_PHASE_TIMEOUT_OVER_WORKER_MS = 3e4;
|
|
33756
|
-
const DEAD_CODE_OVERLAP_PARSE_SHARE = .4;
|
|
33757
|
-
const RECOMMENDED_PNPM_MINIMUM_RELEASE_AGE_MINUTES = 10080;
|
|
33758
|
-
const REACT_SERVER_DOM_PACKAGES = [
|
|
33759
|
-
"react-server-dom-webpack",
|
|
33760
|
-
"react-server-dom-parcel",
|
|
33761
|
-
"react-server-dom-turbopack"
|
|
33762
|
-
];
|
|
33763
|
-
const REACT_BLOG_RSC_ADVISORY_URL = "https://react.dev/blog/2025/12/03/critical-security-vulnerability-in-react-server-components";
|
|
33764
|
-
const VERCEL_NEXTJS_SECURITY_RELEASE_URL = "https://vercel.com/changelog/next-js-may-2026-security-release";
|
|
33765
|
-
const DIAGNOSTIC_CATEGORY_BUCKETS = [
|
|
33766
|
-
"Security",
|
|
33767
|
-
"Bugs",
|
|
33768
|
-
"Performance",
|
|
33769
|
-
"Accessibility",
|
|
33770
|
-
"Maintainability"
|
|
33771
|
-
];
|
|
33772
|
-
const APP_ONLY_RULE_KEYS = new Set([
|
|
33773
|
-
"react-hooks-js/static-components",
|
|
33774
|
-
"react-doctor/no-render-prop-children",
|
|
33775
|
-
"react-doctor/prefer-explicit-variants"
|
|
33776
|
-
]);
|
|
33777
|
-
const COMPILER_CLEANUP_BUCKET = "compiler-cleanup";
|
|
33778
|
-
const COMPILER_CLEANUP_RULE_KEYS = new Set(["react-doctor/react-compiler-no-manual-memoization"]);
|
|
33779
|
-
const ROOT_CAUSE_GROUPABLE_RULE_KEYS = new Set([
|
|
33780
|
-
"react-doctor/no-derived-state",
|
|
33781
|
-
"react-doctor/no-derived-state-effect",
|
|
33782
|
-
"react-doctor/no-derived-useState",
|
|
33783
|
-
"react-doctor/no-adjust-state-on-prop-change",
|
|
33784
|
-
"react-doctor/no-reset-all-state-on-prop-change"
|
|
33785
|
-
]);
|
|
33786
|
-
const MAX_GLOB_PATTERN_LENGTH_CHARS = 1024;
|
|
33787
|
-
const CONFIG_CACHE_TTL_MS = 300 * 1e3;
|
|
33788
|
-
const SOCKET_FREE_PURL_API_BASE = "https://firewall-api.socket.dev/purl";
|
|
33789
|
-
const SOCKET_PACKAGE_PAGE_BASE = "https://socket.dev/npm/package";
|
|
33790
|
-
const SOCKET_FREE_USER_AGENT = "react-doctor-supply-chain";
|
|
33791
|
-
const FILE_LINT_CACHE_FILENAME = "file-lint-cache.json";
|
|
33792
|
-
const FILE_LINT_CACHE_MAX_FILE_COUNT = 5e4;
|
|
33793
|
-
const SUPPLY_CHAIN_PLUGIN = "socket";
|
|
33794
|
-
const SUPPLY_CHAIN_RULE = "low-supply-chain-score";
|
|
33795
|
-
const SUPPLY_CHAIN_CATEGORY = "Security";
|
|
33796
|
-
const SUPPLY_CHAIN_OVERLAP_TIMEOUT_MS = 9e4;
|
|
33797
|
-
const SUPPLY_CHAIN_CACHE_SUBDIR = "supply-chain";
|
|
33798
|
-
const SUPPLY_CHAIN_IGNORED_PACKAGES = new Set(["next"]);
|
|
33799
33855
|
const TSCONFIG_FILENAME = "tsconfig.json";
|
|
33800
33856
|
const isRelativeExtendsValue = (extendsValue) => extendsValue.startsWith("./") || extendsValue.startsWith("../") || Path.isAbsolute(extendsValue);
|
|
33801
33857
|
const ensureJsonExtension = (filePath) => Path.extname(filePath) === "" ? `${filePath}.json` : filePath;
|
|
@@ -34487,7 +34543,10 @@ for (const [legacyRuleKey, nativeRuleKey] of Object.entries(LEGACY_RULE_KEY_TO_N
|
|
|
34487
34543
|
NATIVE_RULE_KEY_TO_LEGACY_RULE_KEYS.set(nativeRuleKey, aliases);
|
|
34488
34544
|
}
|
|
34489
34545
|
const getLegacyRuleKeysForNative = (ruleKey) => NATIVE_RULE_KEY_TO_LEGACY_RULE_KEYS.get(ruleKey) ?? [];
|
|
34490
|
-
const canonicalizeRuleKey = (ruleKey) =>
|
|
34546
|
+
const canonicalizeRuleKey = (ruleKey) => {
|
|
34547
|
+
const nativeRuleKey = LEGACY_RULE_KEY_TO_NATIVE_RULE_KEY[ruleKey];
|
|
34548
|
+
return typeof nativeRuleKey === "string" ? nativeRuleKey : ruleKey;
|
|
34549
|
+
};
|
|
34491
34550
|
const isReactDoctorShortIdOf = (bareRuleKey, qualifiedRuleKey) => !bareRuleKey.includes("/") && qualifiedRuleKey === `react-doctor/${bareRuleKey}`;
|
|
34492
34551
|
const isSameRuleKey = (candidateRuleKey, targetRuleKey) => {
|
|
34493
34552
|
const canonicalCandidate = canonicalizeRuleKey(candidateRuleKey);
|
|
@@ -34514,7 +34573,7 @@ const tokenizeRuleList = (ruleList) => {
|
|
|
34514
34573
|
const FOREIGN_INLINE_DISABLE_PATTERN = /(?:\/\/|\/\*)[ \t]*(eslint|oxlint)-disable-(next-line|line)(?![\w-])([^\r\n]*)/;
|
|
34515
34574
|
const FOREIGN_BLOCK_DISABLE_PATTERN = /\/\*[ \t]*(eslint|oxlint)-disable(?![\w-])([^*\r\n]*)/;
|
|
34516
34575
|
const FOREIGN_BLOCK_ENABLE_PATTERN = /\/\*[ \t]*(?:eslint|oxlint)-enable(?![\w-])([^*\r\n]*)/;
|
|
34517
|
-
const buildHint = (tool, token, ruleId) => `oxlint matches plugin rules only by their full name, so \`${token}\` in your ${tool}-disable comment does not silence \`${ruleId}\` —
|
|
34576
|
+
const buildHint = (tool, token, ruleId) => `oxlint matches plugin rules only by their full name, so \`${token}\` in your ${tool}-disable comment does not silence \`${ruleId}\` — add \`${ruleId}\` to the comment (e.g. \`${token}, ${ruleId}\`).`;
|
|
34518
34577
|
const tokenMisnamesRule = (token, ruleId) => token !== ruleId && isSameRuleKey(token, ruleId);
|
|
34519
34578
|
const detectInlineNearMiss = (lines, diagnosticLineIndex, ruleId) => {
|
|
34520
34579
|
const candidates = [{
|
|
@@ -34994,6 +35053,7 @@ const collectStringSet = (values) => {
|
|
|
34994
35053
|
if (!Array.isArray(values)) return /* @__PURE__ */ new Set();
|
|
34995
35054
|
return new Set(values.filter((value) => typeof value === "string"));
|
|
34996
35055
|
};
|
|
35056
|
+
const JSX_NO_UNDEF_IDENTIFIER_PATTERN = /^`([^`]+)`/;
|
|
34997
35057
|
/**
|
|
34998
35058
|
* Pre-compiles every stateful filter and returns a single
|
|
34999
35059
|
* `apply(diagnostic)` closure that runs (in order):
|
|
@@ -35027,11 +35087,28 @@ const buildDiagnosticPipeline = (input) => {
|
|
|
35027
35087
|
const compiledOverrides = compileIgnoreOverrides(userConfig);
|
|
35028
35088
|
const textComponentNames = collectStringSet(userConfig?.textComponents);
|
|
35029
35089
|
const rawTextWrapperComponentNames = collectStringSet(userConfig?.rawTextWrapperComponents);
|
|
35090
|
+
const runtimeGlobalNames = collectStringSet(userConfig?.runtimeGlobals);
|
|
35030
35091
|
const hasTextComponents = textComponentNames.size > 0;
|
|
35031
35092
|
const hasRawTextWrappers = rawTextWrapperComponentNames.size > 0;
|
|
35093
|
+
const hasRuntimeGlobals = runtimeGlobalNames.size > 0;
|
|
35032
35094
|
const fileLinesCache = /* @__PURE__ */ new Map();
|
|
35033
35095
|
const fileContextCache = /* @__PURE__ */ new Map();
|
|
35034
35096
|
const libraryFileCache = /* @__PURE__ */ new Map();
|
|
35097
|
+
const suppressions = /* @__PURE__ */ new Map();
|
|
35098
|
+
const suppress = (diagnostic, source) => {
|
|
35099
|
+
const { ruleKey } = getDiagnosticRuleIdentity(diagnostic);
|
|
35100
|
+
const suppressionKey = `${ruleKey}\u0000${source}`;
|
|
35101
|
+
const existing = suppressions.get(suppressionKey);
|
|
35102
|
+
suppressions.set(suppressionKey, existing ? {
|
|
35103
|
+
...existing,
|
|
35104
|
+
count: existing.count + 1
|
|
35105
|
+
} : {
|
|
35106
|
+
rule: ruleKey,
|
|
35107
|
+
source,
|
|
35108
|
+
count: 1
|
|
35109
|
+
});
|
|
35110
|
+
return null;
|
|
35111
|
+
};
|
|
35035
35112
|
const isLibraryFile = (filePath) => {
|
|
35036
35113
|
let cached = libraryFileCache.get(filePath);
|
|
35037
35114
|
if (cached === void 0) {
|
|
@@ -35080,50 +35157,60 @@ const buildDiagnosticPipeline = (input) => {
|
|
|
35080
35157
|
if (hasRawTextWrappers && isInsideStringOnlyWrapper(lines, diagnostic.line, diagnostic.column, rawTextWrapperComponentNames)) return true;
|
|
35081
35158
|
return false;
|
|
35082
35159
|
};
|
|
35083
|
-
|
|
35084
|
-
if (
|
|
35085
|
-
|
|
35086
|
-
|
|
35087
|
-
|
|
35088
|
-
|
|
35089
|
-
|
|
35090
|
-
|
|
35091
|
-
|
|
35092
|
-
|
|
35093
|
-
|
|
35094
|
-
|
|
35095
|
-
if (
|
|
35096
|
-
|
|
35097
|
-
|
|
35098
|
-
|
|
35099
|
-
|
|
35100
|
-
|
|
35101
|
-
|
|
35102
|
-
|
|
35103
|
-
|
|
35104
|
-
if (isFileIgnoredByPatterns(current.filePath, rootDirectory, ignoredFilePatterns)) return null;
|
|
35105
|
-
if (isDiagnosticIgnoredByOverrides(current, rootDirectory, compiledOverrides)) return null;
|
|
35106
|
-
if (isRnRawTextSuppressedByConfig(current)) return null;
|
|
35107
|
-
}
|
|
35108
|
-
if (respectInlineDisables && current.line > 0) {
|
|
35109
|
-
const lines = getFileLines(current.filePath);
|
|
35110
|
-
if (lines) {
|
|
35111
|
-
const ruleIdentifier = `${current.plugin}/${current.rule}`;
|
|
35112
|
-
const evaluation = evaluateSuppression(lines, current.line - 1, ruleIdentifier);
|
|
35113
|
-
if (evaluation.isSuppressed) return null;
|
|
35114
|
-
if (evaluation.nearMissHint) current = {
|
|
35115
|
-
...current,
|
|
35116
|
-
suppressionHint: evaluation.nearMissHint
|
|
35117
|
-
};
|
|
35160
|
+
const isJsxNoUndefSuppressedByConfig = (diagnostic) => {
|
|
35161
|
+
if (!hasRuntimeGlobals) return false;
|
|
35162
|
+
if (diagnostic.rule !== "jsx-no-undef") return false;
|
|
35163
|
+
const identifierMatch = JSX_NO_UNDEF_IDENTIFIER_PATTERN.exec(diagnostic.message);
|
|
35164
|
+
return identifierMatch !== null && runtimeGlobalNames.has(identifierMatch[1]);
|
|
35165
|
+
};
|
|
35166
|
+
return {
|
|
35167
|
+
apply: (diagnostic) => {
|
|
35168
|
+
if (shouldAutoSuppress(diagnostic)) return null;
|
|
35169
|
+
let current = diagnostic;
|
|
35170
|
+
let explicitSeverityOverride;
|
|
35171
|
+
let explicitRuleOverride;
|
|
35172
|
+
if (severityControls) {
|
|
35173
|
+
const { ruleKey, category } = getDiagnosticRuleIdentity(current);
|
|
35174
|
+
explicitRuleOverride = resolveRuleSeverityOverride({ ruleKey }, severityControls);
|
|
35175
|
+
explicitSeverityOverride = resolveRuleSeverityOverride({
|
|
35176
|
+
ruleKey,
|
|
35177
|
+
category
|
|
35178
|
+
}, severityControls);
|
|
35179
|
+
if (explicitSeverityOverride === "off") return suppress(current, "config");
|
|
35180
|
+
if (explicitSeverityOverride !== void 0) current = restampSeverity(current, explicitSeverityOverride);
|
|
35118
35181
|
}
|
|
35119
|
-
|
|
35120
|
-
|
|
35121
|
-
|
|
35122
|
-
|
|
35123
|
-
|
|
35124
|
-
|
|
35125
|
-
|
|
35126
|
-
|
|
35182
|
+
if (explicitRuleOverride === void 0) {
|
|
35183
|
+
if (isAppOnlyRule(`${current.plugin}/${current.rule}`) && isLibraryFile(current.filePath)) return null;
|
|
35184
|
+
}
|
|
35185
|
+
if (!showWarnings && current.severity === "warning" && explicitSeverityOverride !== "warn") return null;
|
|
35186
|
+
if (userConfig) {
|
|
35187
|
+
if (isRuleIgnored(`${current.plugin}/${current.rule}`)) return suppress(current, "config");
|
|
35188
|
+
if (isFileIgnoredByPatterns(current.filePath, rootDirectory, ignoredFilePatterns)) return null;
|
|
35189
|
+
if (isDiagnosticIgnoredByOverrides(current, rootDirectory, compiledOverrides)) return suppress(current, "override");
|
|
35190
|
+
if (isRnRawTextSuppressedByConfig(current)) return null;
|
|
35191
|
+
if (isJsxNoUndefSuppressedByConfig(current)) return null;
|
|
35192
|
+
}
|
|
35193
|
+
if (respectInlineDisables && current.line > 0) {
|
|
35194
|
+
const lines = getFileLines(current.filePath);
|
|
35195
|
+
if (lines) {
|
|
35196
|
+
const ruleIdentifier = `${current.plugin}/${current.rule}`;
|
|
35197
|
+
const evaluation = evaluateSuppression(lines, current.line - 1, ruleIdentifier);
|
|
35198
|
+
if (evaluation.isSuppressed) return suppress(current, "inline");
|
|
35199
|
+
if (evaluation.nearMissHint) current = {
|
|
35200
|
+
...current,
|
|
35201
|
+
suppressionHint: evaluation.nearMissHint
|
|
35202
|
+
};
|
|
35203
|
+
}
|
|
35204
|
+
}
|
|
35205
|
+
const fileContext = getFileContext(current.filePath);
|
|
35206
|
+
if (fileContext !== "production") current = {
|
|
35207
|
+
...current,
|
|
35208
|
+
fileContext
|
|
35209
|
+
};
|
|
35210
|
+
return current;
|
|
35211
|
+
},
|
|
35212
|
+
summarizeSuppressions: () => [...suppressions.values()]
|
|
35213
|
+
};
|
|
35127
35214
|
};
|
|
35128
35215
|
const OxlintUnavailableKind = Literals(["binary-not-found", "native-binding-missing"]);
|
|
35129
35216
|
var OxlintUnavailable = class extends TaggedErrorClass()("OxlintUnavailable", {
|
|
@@ -35365,7 +35452,7 @@ const resolveScanConcurrency = (requested) => {
|
|
|
35365
35452
|
if (!Number.isFinite(requested) || requested < 1) return 1;
|
|
35366
35453
|
return Math.min(Math.floor(requested), 32);
|
|
35367
35454
|
};
|
|
35368
|
-
const readSystemFacts = () => ({
|
|
35455
|
+
const readSystemFacts$1 = () => ({
|
|
35369
35456
|
availableCores: os.availableParallelism(),
|
|
35370
35457
|
totalMemoryBytes: os.totalmem(),
|
|
35371
35458
|
cgroupMemoryLimitBytes: readCgroupMemoryLimitBytes()
|
|
@@ -35386,7 +35473,7 @@ const readSystemFacts = () => ({
|
|
|
35386
35473
|
* `facts` is injectable so tests exercise core-bound, memory-bound, cgroup-
|
|
35387
35474
|
* limited, and ceiling cases without mocking `os` or the filesystem.
|
|
35388
35475
|
*/
|
|
35389
|
-
const resolveAutoScanConcurrency = (facts = readSystemFacts()) => {
|
|
35476
|
+
const resolveAutoScanConcurrency = (facts = readSystemFacts$1()) => {
|
|
35390
35477
|
const availableMemoryBytes = Math.min(facts.totalMemoryBytes, facts.cgroupMemoryLimitBytes ?? Number.POSITIVE_INFINITY);
|
|
35391
35478
|
const memoryBoundedWorkers = Math.floor(availableMemoryBytes / PER_WORKER_MEM_BUDGET_BYTES);
|
|
35392
35479
|
return resolveScanConcurrency(Math.min(facts.availableCores, memoryBoundedWorkers));
|
|
@@ -35427,13 +35514,7 @@ var ScanDeadlineMs = class extends Reference("react-doctor/ScanDeadlineMs", { de
|
|
|
35427
35514
|
* without recompiling react-doctor. Tests override via
|
|
35428
35515
|
* `Layer.succeed(SupplyChainOverlapTimeoutMs, ...)`.
|
|
35429
35516
|
*/
|
|
35430
|
-
var SupplyChainOverlapTimeoutMs = class extends Reference("react-doctor/SupplyChainOverlapTimeoutMs", { defaultValue: () => {
|
|
35431
|
-
const raw = process.env["REACT_DOCTOR_SUPPLY_CHAIN_TIMEOUT_MS"];
|
|
35432
|
-
if (raw === void 0) return SUPPLY_CHAIN_OVERLAP_TIMEOUT_MS;
|
|
35433
|
-
const parsed = Number(raw);
|
|
35434
|
-
if (!Number.isFinite(parsed) || parsed <= 0) return SUPPLY_CHAIN_OVERLAP_TIMEOUT_MS;
|
|
35435
|
-
return parsed;
|
|
35436
|
-
} }) {};
|
|
35517
|
+
var SupplyChainOverlapTimeoutMs = class extends Reference("react-doctor/SupplyChainOverlapTimeoutMs", { defaultValue: () => readPositiveEnvMs("REACT_DOCTOR_SUPPLY_CHAIN_TIMEOUT_MS", SUPPLY_CHAIN_OVERLAP_TIMEOUT_MS) }) {};
|
|
35437
35518
|
/**
|
|
35438
35519
|
* Hard cap on combined stdout+stderr bytes per oxlint batch. The
|
|
35439
35520
|
* subprocess gets SIGKILL'd if it produces more; the recovery path
|
|
@@ -35577,6 +35658,13 @@ const BOOLEAN_FIELD_NAMES = [
|
|
|
35577
35658
|
"adoptExistingLintConfig"
|
|
35578
35659
|
];
|
|
35579
35660
|
const STRING_FIELD_NAMES = ["rootDir"];
|
|
35661
|
+
const STRING_ARRAY_FIELD_NAMES = [
|
|
35662
|
+
"projects",
|
|
35663
|
+
"textComponents",
|
|
35664
|
+
"rawTextWrapperComponents",
|
|
35665
|
+
"runtimeGlobals",
|
|
35666
|
+
"serverAuthFunctionNames"
|
|
35667
|
+
];
|
|
35580
35668
|
const SURFACE_CONTROL_FIELD_NAMES = [
|
|
35581
35669
|
"includeTags",
|
|
35582
35670
|
"excludeTags",
|
|
@@ -35678,6 +35766,7 @@ const validateConfigTypes = (config) => {
|
|
|
35678
35766
|
const validated = { ...config };
|
|
35679
35767
|
for (const fieldName of BOOLEAN_FIELD_NAMES) applyFieldValidator(config, validated, fieldName, (value) => coerceMaybeBooleanString(fieldName, value));
|
|
35680
35768
|
for (const fieldName of STRING_FIELD_NAMES) applyFieldValidator(config, validated, fieldName, (value) => validateString(fieldName, value));
|
|
35769
|
+
for (const fieldName of STRING_ARRAY_FIELD_NAMES) applyFieldValidator(config, validated, fieldName, (value) => validateStringArrayField(fieldName, value));
|
|
35681
35770
|
applyFieldValidator(config, validated, "surfaces", validateSurfacesField);
|
|
35682
35771
|
for (const fieldName of SEVERITY_FIELD_NAMES) applyFieldValidator(config, validated, fieldName, (value) => validateSeverityMap(fieldName, value, fieldName === "categories"));
|
|
35683
35772
|
applyFieldValidator(config, validated, "plugins", (value) => validateStringArrayField("plugins", value));
|
|
@@ -36710,14 +36799,17 @@ const checkReactServerComponentsAdvisory = (scanDirectory, project) => {
|
|
|
36710
36799
|
return diagnostics;
|
|
36711
36800
|
};
|
|
36712
36801
|
const REDUCED_MOTION_GREP_PATTERN = "prefers-reduced-motion|useReducedMotion|MotionConfig|reducedMotion";
|
|
36713
|
-
const
|
|
36714
|
-
|
|
36715
|
-
"
|
|
36716
|
-
"
|
|
36717
|
-
"
|
|
36718
|
-
"
|
|
36719
|
-
"
|
|
36720
|
-
|
|
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;
|
|
36721
36813
|
const MISSING_REDUCED_MOTION_DIAGNOSTIC = {
|
|
36722
36814
|
filePath: "package.json",
|
|
36723
36815
|
plugin: "react-doctor",
|
|
@@ -36729,6 +36821,19 @@ const MISSING_REDUCED_MOTION_DIAGNOSTIC = {
|
|
|
36729
36821
|
column: 0,
|
|
36730
36822
|
category: "Accessibility"
|
|
36731
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
|
+
};
|
|
36732
36837
|
const checkReducedMotion = (rootDirectory) => {
|
|
36733
36838
|
const packageJsonPath = Path.join(rootDirectory, "package.json");
|
|
36734
36839
|
if (!isFile(packageJsonPath)) return [];
|
|
@@ -36747,22 +36852,21 @@ const checkReducedMotion = (rootDirectory) => {
|
|
|
36747
36852
|
const result = spawnSync("git", [
|
|
36748
36853
|
"grep",
|
|
36749
36854
|
"--untracked",
|
|
36750
|
-
"-
|
|
36751
|
-
"-E",
|
|
36855
|
+
"-lE",
|
|
36752
36856
|
REDUCED_MOTION_GREP_PATTERN,
|
|
36753
36857
|
"--",
|
|
36754
36858
|
...REDUCED_MOTION_FILE_GLOBS
|
|
36755
36859
|
], {
|
|
36756
36860
|
cwd: rootDirectory,
|
|
36861
|
+
encoding: "utf-8",
|
|
36757
36862
|
stdio: [
|
|
36758
36863
|
"ignore",
|
|
36759
36864
|
"pipe",
|
|
36760
36865
|
"pipe"
|
|
36761
36866
|
]
|
|
36762
36867
|
});
|
|
36763
|
-
if (result.error) return [MISSING_REDUCED_MOTION_DIAGNOSTIC];
|
|
36764
|
-
|
|
36765
|
-
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];
|
|
36766
36870
|
};
|
|
36767
36871
|
const buildSecurityScanDiagnostic = (finding, entry, relativePath) => ({
|
|
36768
36872
|
filePath: relativePath,
|
|
@@ -36912,7 +37016,10 @@ const shouldEnableRule = (requires, tags, capabilities, ignoredTags, disabledBy)
|
|
|
36912
37016
|
}
|
|
36913
37017
|
return true;
|
|
36914
37018
|
};
|
|
36915
|
-
const
|
|
37019
|
+
const yieldToEventLoop = () => new Promise((resolve) => {
|
|
37020
|
+
setImmediate(resolve);
|
|
37021
|
+
});
|
|
37022
|
+
const createSecurityScanSession = (rootDirectory, options) => {
|
|
36916
37023
|
const capabilities = options.project ? buildCapabilities(options.project) : /* @__PURE__ */ new Set();
|
|
36917
37024
|
const ignoredTags = options.ignoredTags ?? /* @__PURE__ */ new Set();
|
|
36918
37025
|
const enabledScanRules = REACT_DOCTOR_RULES.flatMap((entry) => {
|
|
@@ -36927,7 +37034,7 @@ const checkSecurityScan = (rootDirectory, options = {}) => {
|
|
|
36927
37034
|
committedFilesOnly: rule.committedFilesOnly === true
|
|
36928
37035
|
}];
|
|
36929
37036
|
});
|
|
36930
|
-
if (enabledScanRules.length === 0) return
|
|
37037
|
+
if (enabledScanRules.length === 0) return null;
|
|
36931
37038
|
const diagnostics = [];
|
|
36932
37039
|
const seen = /* @__PURE__ */ new Set();
|
|
36933
37040
|
const gitIgnoredCache = /* @__PURE__ */ new Map();
|
|
@@ -36939,15 +37046,34 @@ const checkSecurityScan = (rootDirectory, options = {}) => {
|
|
|
36939
37046
|
}
|
|
36940
37047
|
return status === true;
|
|
36941
37048
|
};
|
|
36942
|
-
|
|
36943
|
-
|
|
36944
|
-
|
|
36945
|
-
|
|
36946
|
-
|
|
36947
|
-
|
|
36948
|
-
|
|
37049
|
+
const scanFile = (file) => {
|
|
37050
|
+
for (const { entry, scan, committedFilesOnly } of enabledScanRules) for (const finding of scan(file)) {
|
|
37051
|
+
if (committedFilesOnly && isFileGitIgnored(file)) continue;
|
|
37052
|
+
const diagnostic = buildSecurityScanDiagnostic(finding, entry, file.relativePath);
|
|
37053
|
+
const key = `${diagnostic.rule}:${diagnostic.filePath}:${diagnostic.line}:${diagnostic.column}:${diagnostic.message}`;
|
|
37054
|
+
if (seen.has(key)) continue;
|
|
37055
|
+
seen.add(key);
|
|
37056
|
+
diagnostics.push(diagnostic);
|
|
37057
|
+
}
|
|
37058
|
+
};
|
|
37059
|
+
return {
|
|
37060
|
+
scanFile,
|
|
37061
|
+
diagnostics
|
|
37062
|
+
};
|
|
37063
|
+
};
|
|
37064
|
+
const checkSecurityScanCooperative = async (rootDirectory, options = {}) => {
|
|
37065
|
+
const session = createSecurityScanSession(rootDirectory, options);
|
|
37066
|
+
if (session === null) return [];
|
|
37067
|
+
let filesSinceYield = 0;
|
|
37068
|
+
for (const file of collectSecurityScanFiles(rootDirectory)) {
|
|
37069
|
+
session.scanFile(file);
|
|
37070
|
+
filesSinceYield += 1;
|
|
37071
|
+
if (filesSinceYield >= 16) {
|
|
37072
|
+
filesSinceYield = 0;
|
|
37073
|
+
await yieldToEventLoop();
|
|
37074
|
+
}
|
|
36949
37075
|
}
|
|
36950
|
-
return diagnostics;
|
|
37076
|
+
return session.diagnostics;
|
|
36951
37077
|
};
|
|
36952
37078
|
var import_picocolors = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
36953
37079
|
let p = process || {}, argv = p.argv || [], env = p.env || {};
|
|
@@ -37164,6 +37290,74 @@ const collectDeadCodeIgnorePatterns = (rootDirectory) => {
|
|
|
37164
37290
|
return [...seen].filter((pattern) => pattern.length > 0);
|
|
37165
37291
|
};
|
|
37166
37292
|
const collectDeadCodeEntryPatterns = (rootDirectory) => [...new Set(collectKnipPatterns(rootDirectory, "entry"))].filter((pattern) => pattern.length > 0);
|
|
37293
|
+
const readSystemFacts = () => ({
|
|
37294
|
+
availableCores: os.availableParallelism(),
|
|
37295
|
+
totalMemoryBytes: os.totalmem(),
|
|
37296
|
+
cgroupMemoryLimitBytes: readCgroupMemoryLimitBytes()
|
|
37297
|
+
});
|
|
37298
|
+
/**
|
|
37299
|
+
* How many real deslop dead-code child processes may run at once, across the
|
|
37300
|
+
* concurrent per-project `runInspect` fibers of one CLI run. The cap is the
|
|
37301
|
+
* smaller of the core count and the number of `DEAD_CODE_WORKER_MEM_BUDGET_BYTES`
|
|
37302
|
+
* workers that fit in available memory, floored at 1.
|
|
37303
|
+
*
|
|
37304
|
+
* On a roomy dev box / CI runner this resolves high enough that every
|
|
37305
|
+
* concurrently-scanned project still spawns its own worker (no serialization vs
|
|
37306
|
+
* the prior uncapped behavior); on a memory-constrained runner it collapses
|
|
37307
|
+
* toward 1, so the `withDeadCodeWorkerSlot` semaphore serializes the spawns
|
|
37308
|
+
* instead of oversubscribing memory with N simultaneous children — the global
|
|
37309
|
+
* cap the per-project spawn path lacked.
|
|
37310
|
+
*
|
|
37311
|
+
* Mirrors `resolveAutoScanConcurrency` (lint), but budgets memory per the
|
|
37312
|
+
* heavier dead-code worker. `facts` is injectable for tests.
|
|
37313
|
+
*/
|
|
37314
|
+
const resolveDeadCodeConcurrency = (facts = readSystemFacts()) => {
|
|
37315
|
+
const availableMemoryBytes = Math.min(facts.totalMemoryBytes, facts.cgroupMemoryLimitBytes ?? Number.POSITIVE_INFINITY);
|
|
37316
|
+
const memoryBoundedWorkers = Math.floor(availableMemoryBytes / DEAD_CODE_WORKER_MEM_BUDGET_BYTES);
|
|
37317
|
+
return Math.max(1, Math.min(facts.availableCores, memoryBoundedWorkers));
|
|
37318
|
+
};
|
|
37319
|
+
let availableSlots = -1;
|
|
37320
|
+
const waiters = [];
|
|
37321
|
+
const releaseSlot = () => {
|
|
37322
|
+
const nextWaiter = waiters.shift();
|
|
37323
|
+
if (nextWaiter !== void 0) nextWaiter();
|
|
37324
|
+
else availableSlots += 1;
|
|
37325
|
+
};
|
|
37326
|
+
/**
|
|
37327
|
+
* Runs `task` once a dead-code worker slot is free, releasing the slot when the
|
|
37328
|
+
* task settles (success or failure). With a high cap (roomy machine) every
|
|
37329
|
+
* caller proceeds immediately; with a low cap (constrained runner) callers
|
|
37330
|
+
* queue and run as slots free.
|
|
37331
|
+
*
|
|
37332
|
+
* `abortSignal` short-circuits the WAIT: if it's already aborted, or fires while
|
|
37333
|
+
* this caller is queued, the call rejects without acquiring a slot or running
|
|
37334
|
+
* `task` — so a cancelled scan (e.g. lint failed) doesn't sit in the queue and
|
|
37335
|
+
* then spawn a child only to tear it down. A queued caller that aborts removes
|
|
37336
|
+
* its own waiter so a later release never hands a slot to a dead request.
|
|
37337
|
+
*/
|
|
37338
|
+
const withDeadCodeWorkerSlot = async (task, abortSignal) => {
|
|
37339
|
+
if (abortSignal?.aborted) throw new Error("Dead-code worker aborted.");
|
|
37340
|
+
if (availableSlots < 0) availableSlots = resolveDeadCodeConcurrency();
|
|
37341
|
+
if (availableSlots > 0) availableSlots -= 1;
|
|
37342
|
+
else await new Promise((resolve, reject) => {
|
|
37343
|
+
const waiter = () => {
|
|
37344
|
+
abortSignal?.removeEventListener("abort", onAbort);
|
|
37345
|
+
resolve();
|
|
37346
|
+
};
|
|
37347
|
+
const onAbort = () => {
|
|
37348
|
+
const queuedIndex = waiters.indexOf(waiter);
|
|
37349
|
+
if (queuedIndex !== -1) waiters.splice(queuedIndex, 1);
|
|
37350
|
+
reject(/* @__PURE__ */ new Error("Dead-code worker aborted."));
|
|
37351
|
+
};
|
|
37352
|
+
waiters.push(waiter);
|
|
37353
|
+
abortSignal?.addEventListener("abort", onAbort, { once: true });
|
|
37354
|
+
});
|
|
37355
|
+
try {
|
|
37356
|
+
return await task();
|
|
37357
|
+
} finally {
|
|
37358
|
+
releaseSlot();
|
|
37359
|
+
}
|
|
37360
|
+
};
|
|
37167
37361
|
/**
|
|
37168
37362
|
* Resolves a path to its canonical, symlink-free form, falling back to
|
|
37169
37363
|
* the input when it cannot be realpath'd (broken symlink, permission
|
|
@@ -37188,6 +37382,11 @@ const toCanonicalPath = (filePath) => {
|
|
|
37188
37382
|
};
|
|
37189
37383
|
const DEAD_CODE_PLUGIN = "deslop";
|
|
37190
37384
|
const DEAD_CODE_CATEGORY = "Maintainability";
|
|
37385
|
+
const REACT_DOCTOR_TOOLCHAIN_PACKAGES = new Set([
|
|
37386
|
+
"react-doctor",
|
|
37387
|
+
"eslint-plugin-react-doctor",
|
|
37388
|
+
"oxlint-plugin-react-doctor"
|
|
37389
|
+
]);
|
|
37191
37390
|
const DEAD_CODE_WORKER_SCRIPT = `
|
|
37192
37391
|
const inputChunks = [];
|
|
37193
37392
|
process.stdin.on("data", (chunk) => inputChunks.push(chunk));
|
|
@@ -37454,14 +37653,17 @@ const checkDeadCode = async (options) => {
|
|
|
37454
37653
|
if (!NFS.existsSync(Path.join(rootDirectory, "package.json"))) return [];
|
|
37455
37654
|
const entryPatterns = collectDeadCodeEntryPatterns(rootDirectory);
|
|
37456
37655
|
const ignorePatterns = collectDeadCodeIgnorePatterns(rootDirectory);
|
|
37457
|
-
const
|
|
37458
|
-
|
|
37459
|
-
|
|
37460
|
-
|
|
37461
|
-
|
|
37462
|
-
|
|
37463
|
-
|
|
37464
|
-
|
|
37656
|
+
const spawnAndRun = () => {
|
|
37657
|
+
return runDeadCodeWorkerWithTimeout((options.createWorker ?? createDeadCodeWorker)({
|
|
37658
|
+
rootDirectory,
|
|
37659
|
+
entryPatterns,
|
|
37660
|
+
tsConfigPath: resolveTsConfigPath(rootDirectory),
|
|
37661
|
+
ignorePatterns,
|
|
37662
|
+
deslopJsModuleSpecifier: options.deslopJsModuleSpecifier ?? import.meta.resolve("deslop-js"),
|
|
37663
|
+
parseConcurrency: options.parseConcurrency
|
|
37664
|
+
}), options.workerTimeoutMs ?? 12e4, options.abortSignal);
|
|
37665
|
+
};
|
|
37666
|
+
const result = parseDeadCodeWorkerResult(options.createWorker === void 0 ? await withDeadCodeWorkerSlot(spawnAndRun, options.abortSignal) : await spawnAndRun());
|
|
37465
37667
|
const toRelative = (filePath) => toRelativeFilePath(rootDirectory, filePath);
|
|
37466
37668
|
const diagnostics = [];
|
|
37467
37669
|
for (const unusedFile of result.unusedFiles) diagnostics.push({
|
|
@@ -37490,6 +37692,7 @@ const checkDeadCode = async (options) => {
|
|
|
37490
37692
|
});
|
|
37491
37693
|
}
|
|
37492
37694
|
for (const unusedDependency of result.unusedDependencies) {
|
|
37695
|
+
if (REACT_DOCTOR_TOOLCHAIN_PACKAGES.has(unusedDependency.name)) continue;
|
|
37493
37696
|
const label = unusedDependency.isDevDependency ? "devDependency" : "dependency";
|
|
37494
37697
|
diagnostics.push({
|
|
37495
37698
|
filePath: "package.json",
|
|
@@ -37559,6 +37762,7 @@ const isDiagnosticOnSurface = (diagnostic, surface, config) => {
|
|
|
37559
37762
|
return true;
|
|
37560
37763
|
};
|
|
37561
37764
|
const filterDiagnosticsForSurface = (diagnostics, surface, config) => diagnostics.filter((diagnostic) => isDiagnosticOnSurface(diagnostic, surface, config));
|
|
37765
|
+
const remainingDeadlineBudgetMs = (deadlineEpochMs) => Math.max(deadlineEpochMs - Date.now(), 0);
|
|
37562
37766
|
/**
|
|
37563
37767
|
* Budget for the dead-code phase, scaled to the work. deslop's graph build is
|
|
37564
37768
|
* CPU-bound and roughly linear in file count, so a fixed 120s cap is too tight
|
|
@@ -37577,53 +37781,6 @@ const resolveDeadCodeTimeout = (input) => {
|
|
|
37577
37781
|
phaseTimeoutMs: workerTimeoutMs + DEAD_CODE_PHASE_TIMEOUT_OVER_WORKER_MS
|
|
37578
37782
|
};
|
|
37579
37783
|
};
|
|
37580
|
-
const collectSizedSourceFiles = (rootDirectory, relativePaths) => {
|
|
37581
|
-
const entries = [];
|
|
37582
|
-
for (const relativePath of relativePaths) {
|
|
37583
|
-
const absolutePath = Path.resolve(rootDirectory, relativePath);
|
|
37584
|
-
const sizeBytes = statSourceFileSize(absolutePath);
|
|
37585
|
-
if (isLargeMinifiedFile(absolutePath, sizeBytes)) continue;
|
|
37586
|
-
entries.push({
|
|
37587
|
-
path: relativePath,
|
|
37588
|
-
sizeBytes: sizeBytes ?? 0
|
|
37589
|
-
});
|
|
37590
|
-
}
|
|
37591
|
-
return entries;
|
|
37592
|
-
};
|
|
37593
|
-
const listSourceFilesViaGit = (rootDirectory) => {
|
|
37594
|
-
const result = spawnSync("git", [
|
|
37595
|
-
"ls-files",
|
|
37596
|
-
"-z",
|
|
37597
|
-
"--cached",
|
|
37598
|
-
"--others",
|
|
37599
|
-
"--exclude-standard"
|
|
37600
|
-
], {
|
|
37601
|
-
cwd: rootDirectory,
|
|
37602
|
-
encoding: "utf-8",
|
|
37603
|
-
maxBuffer: GIT_LS_FILES_MAX_BUFFER_BYTES
|
|
37604
|
-
});
|
|
37605
|
-
if (result.error || result.status !== 0) return null;
|
|
37606
|
-
return result.stdout.split("\0").filter((filePath) => filePath.length > 0 && isLintableSourceFile(filePath));
|
|
37607
|
-
};
|
|
37608
|
-
const listSourceFilesViaFilesystem = (rootDirectory) => {
|
|
37609
|
-
const filePaths = [];
|
|
37610
|
-
const stack = [rootDirectory];
|
|
37611
|
-
while (stack.length > 0) {
|
|
37612
|
-
const currentDirectory = stack.pop();
|
|
37613
|
-
const entries = readDirectoryEntries(currentDirectory);
|
|
37614
|
-
for (const entry of entries) {
|
|
37615
|
-
const absolutePath = Path.join(currentDirectory, entry.name);
|
|
37616
|
-
if (entry.isDirectory()) {
|
|
37617
|
-
if (!entry.name.startsWith(".") && !IGNORED_DIRECTORIES.has(entry.name)) stack.push(absolutePath);
|
|
37618
|
-
continue;
|
|
37619
|
-
}
|
|
37620
|
-
if (entry.isFile() && isLintableSourceFile(entry.name)) filePaths.push(Path.relative(rootDirectory, absolutePath).replace(/\\/g, "/"));
|
|
37621
|
-
}
|
|
37622
|
-
}
|
|
37623
|
-
return filePaths;
|
|
37624
|
-
};
|
|
37625
|
-
const listSourceFilesWithSize = (rootDirectory) => collectSizedSourceFiles(rootDirectory, listSourceFilesViaGit(rootDirectory) ?? listSourceFilesViaFilesystem(rootDirectory));
|
|
37626
|
-
const listSourceFiles = (rootDirectory) => listSourceFilesWithSize(rootDirectory).map((entry) => entry.path);
|
|
37627
37784
|
const resolveLintIncludePaths = (rootDirectory, userConfig, project) => {
|
|
37628
37785
|
if (!Array.isArray(userConfig?.ignore?.files) || userConfig.ignore.files.length === 0) return;
|
|
37629
37786
|
const ignoredPatterns = compileIgnoredFilePatterns(userConfig);
|
|
@@ -37668,7 +37825,6 @@ var DeadCode = class DeadCode extends Service()("react-doctor/DeadCode") {
|
|
|
37668
37825
|
return yield* tryPromise({
|
|
37669
37826
|
try: (signal) => checkDeadCode({
|
|
37670
37827
|
rootDirectory: input.rootDirectory,
|
|
37671
|
-
userConfig: input.userConfig,
|
|
37672
37828
|
parseConcurrency: input.parseConcurrency,
|
|
37673
37829
|
workerTimeoutMs: input.workerTimeoutMs,
|
|
37674
37830
|
abortSignal: signal
|
|
@@ -37791,6 +37947,11 @@ const isSafeGitRevision = (candidate) => {
|
|
|
37791
37947
|
if (candidate.includes("..") || candidate.includes("@{")) return false;
|
|
37792
37948
|
return SAFE_GIT_REVISION_PATTERN.test(candidate);
|
|
37793
37949
|
};
|
|
37950
|
+
const resolveSpawnArgsLengthCap = () => {
|
|
37951
|
+
if (process.platform === "win32") return SPAWN_ARGS_MAX_LENGTH_CHARS;
|
|
37952
|
+
if (process.platform === "darwin") return SPAWN_ARGS_MAX_LENGTH_CHARS_DARWIN;
|
|
37953
|
+
return SPAWN_ARGS_MAX_LENGTH_CHARS_POSIX;
|
|
37954
|
+
};
|
|
37794
37955
|
/**
|
|
37795
37956
|
* Splits a git revision range into its endpoints: three-dot `A...B`
|
|
37796
37957
|
* (symmetric, merge-base) or two-dot `A..B` (direct). Returns `null`
|
|
@@ -37862,43 +38023,47 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
37862
38023
|
* reason: GitInvocationFailed })` so the rest of the codebase
|
|
37863
38024
|
* sees a single failure channel.
|
|
37864
38025
|
*/
|
|
37865
|
-
const runCommand = (input) =>
|
|
37866
|
-
const
|
|
37867
|
-
cwd: input.directory,
|
|
37868
|
-
env: input.env,
|
|
37869
|
-
extendEnv: true
|
|
37870
|
-
}));
|
|
37871
|
-
const maxStdoutBytes = input.maxStdoutBytes;
|
|
37872
|
-
const stdoutByteCount = yield* make$13(0);
|
|
37873
|
-
const [stdout, stderr, status] = yield* all([
|
|
37874
|
-
mkString(decodeText(maxStdoutBytes === void 0 ? handle.stdout : handle.stdout.pipe(tap((chunk) => updateAndGet(stdoutByteCount, (total) => total + chunk.length).pipe(flatMap$2((total) => total > maxStdoutBytes ? fail$4(new ReactDoctorError({ reason: new GitInvocationFailed({
|
|
37875
|
-
args: [...input.args],
|
|
37876
|
-
directory: input.directory,
|
|
37877
|
-
cause: /* @__PURE__ */ new Error(`git stdout exceeded ${maxStdoutBytes} bytes`)
|
|
37878
|
-
}) })) : void_)))))),
|
|
37879
|
-
mkString(decodeText(handle.stderr)),
|
|
37880
|
-
handle.exitCode
|
|
37881
|
-
], { concurrency: 3 });
|
|
37882
|
-
return {
|
|
37883
|
-
status,
|
|
37884
|
-
stdout,
|
|
37885
|
-
stderr
|
|
37886
|
-
};
|
|
37887
|
-
})).pipe(catchTag$1("PlatformError", (cause) => {
|
|
37888
|
-
if (input.command !== "git") return succeed$2({
|
|
38026
|
+
const runCommand = (input) => {
|
|
38027
|
+
const foldSpawnFailure = (cause) => input.command !== "git" ? succeed$2({
|
|
37889
38028
|
status: 127,
|
|
37890
38029
|
stdout: "",
|
|
37891
38030
|
stderr: String(cause)
|
|
37892
|
-
})
|
|
37893
|
-
return new ReactDoctorError({ reason: new GitInvocationFailed({
|
|
38031
|
+
}) : fail$4(new ReactDoctorError({ reason: new GitInvocationFailed({
|
|
37894
38032
|
args: [...input.args],
|
|
37895
38033
|
directory: input.directory,
|
|
37896
38034
|
cause
|
|
37897
|
-
}) });
|
|
37898
|
-
|
|
37899
|
-
|
|
37900
|
-
|
|
37901
|
-
|
|
38035
|
+
}) }));
|
|
38036
|
+
return scoped(gen(function* () {
|
|
38037
|
+
if (!isDirectory(input.directory)) return yield* foldSpawnFailure(`spawn ENOTDIR (cwd is not a directory: ${input.directory})`);
|
|
38038
|
+
const argvLengthChars = input.command.length + 1 + input.args.reduce((total, arg) => total + arg.length + 1, 0);
|
|
38039
|
+
const spawnArgsLengthCap = resolveSpawnArgsLengthCap();
|
|
38040
|
+
if (argvLengthChars > spawnArgsLengthCap) return yield* foldSpawnFailure(`spawn ENAMETOOLONG (${argvLengthChars} argv chars exceed ${spawnArgsLengthCap})`);
|
|
38041
|
+
const handle = yield* spawner.spawn(make$1(input.command, [...input.args], {
|
|
38042
|
+
cwd: input.directory,
|
|
38043
|
+
env: input.env,
|
|
38044
|
+
extendEnv: true
|
|
38045
|
+
}));
|
|
38046
|
+
const maxStdoutBytes = input.maxStdoutBytes;
|
|
38047
|
+
const stdoutByteCount = yield* make$13(0);
|
|
38048
|
+
const [stdout, stderr, status] = yield* all([
|
|
38049
|
+
mkString(decodeText(maxStdoutBytes === void 0 ? handle.stdout : handle.stdout.pipe(tap((chunk) => updateAndGet(stdoutByteCount, (total) => total + chunk.length).pipe(flatMap$2((total) => total > maxStdoutBytes ? fail$4(new ReactDoctorError({ reason: new GitInvocationFailed({
|
|
38050
|
+
args: [...input.args],
|
|
38051
|
+
directory: input.directory,
|
|
38052
|
+
cause: /* @__PURE__ */ new Error(`git stdout exceeded ${maxStdoutBytes} bytes`)
|
|
38053
|
+
}) })) : void_)))))),
|
|
38054
|
+
mkString(decodeText(handle.stderr)),
|
|
38055
|
+
handle.exitCode
|
|
38056
|
+
], { concurrency: 3 });
|
|
38057
|
+
return {
|
|
38058
|
+
status,
|
|
38059
|
+
stdout,
|
|
38060
|
+
stderr
|
|
38061
|
+
};
|
|
38062
|
+
})).pipe(catchTag$1("PlatformError", foldSpawnFailure), withSpan("git.exec", { attributes: {
|
|
38063
|
+
"git.command": input.command,
|
|
38064
|
+
"git.subcommand": input.args[0] ?? ""
|
|
38065
|
+
} }));
|
|
38066
|
+
};
|
|
37902
38067
|
const runGit = (directory, args) => runCommand({
|
|
37903
38068
|
command: "git",
|
|
37904
38069
|
args,
|
|
@@ -37931,7 +38096,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
37931
38096
|
"rev-parse",
|
|
37932
38097
|
"--verify",
|
|
37933
38098
|
branch
|
|
37934
|
-
]).pipe(map$3((result) => result.status === 0));
|
|
38099
|
+
]).pipe(map$3((result) => result.status === 0), catch_$1((error) => error.reason._tag === "GitInvocationFailed" ? succeed$2(false) : fail$4(error)));
|
|
37935
38100
|
const headSha = (directory) => runGit(directory, ["rev-parse", "HEAD"]).pipe(map$3((result) => result.status === 0 ? trimOrNull(result.stdout) : null));
|
|
37936
38101
|
const mergeBase = (input) => isSafeGitRevision(input.ref) ? runGit(input.directory, [
|
|
37937
38102
|
"merge-base",
|
|
@@ -38041,6 +38206,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
38041
38206
|
if (resolvedCurrentBranch === null && explicitBaseBranch === void 0) return null;
|
|
38042
38207
|
const baseBranch = explicitBaseBranch ?? (yield* defaultBranch(directory));
|
|
38043
38208
|
if (baseBranch === null) return null;
|
|
38209
|
+
if (!isSafeGitRevision(baseBranch)) return null;
|
|
38044
38210
|
if (explicitBaseBranch !== void 0) {
|
|
38045
38211
|
if (!(yield* branchExists(directory, explicitBaseBranch))) return yield* fail$4(new ReactDoctorError({ reason: new GitBaseBranchMissing({ branch: explicitBaseBranch }) }));
|
|
38046
38212
|
}
|
|
@@ -38145,7 +38311,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
38145
38311
|
]);
|
|
38146
38312
|
if (result.status !== 0) return null;
|
|
38147
38313
|
return parseChangedLineRanges(result.stdout);
|
|
38148
|
-
}).pipe(withSpan("Git.changedLineRanges"))
|
|
38314
|
+
}).pipe(catch_$1((error) => error.reason._tag === "GitInvocationFailed" ? succeed$2(null) : fail$4(error)), withSpan("Git.changedLineRanges"))
|
|
38149
38315
|
});
|
|
38150
38316
|
})).pipe(provide$2(layer$2.pipe(provide$2(mergeAll$1(layer$1, layer)))));
|
|
38151
38317
|
/**
|
|
@@ -38311,12 +38477,12 @@ const findFilesWithDisableDirectivesViaGit = async (rootDirectory, includePaths)
|
|
|
38311
38477
|
return null;
|
|
38312
38478
|
}
|
|
38313
38479
|
if (grepResult === null) return null;
|
|
38314
|
-
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));
|
|
38315
38481
|
};
|
|
38316
38482
|
const findFilesWithDisableDirectivesViaFilesystem = (rootDirectory, includePaths) => {
|
|
38317
38483
|
const matches = [];
|
|
38318
38484
|
const checkFile = (relativePath) => {
|
|
38319
|
-
if (!isLintableSourceFile(relativePath)) return;
|
|
38485
|
+
if (!isLintableSourceFile(relativePath) || hasIgnoredPathSegment(relativePath)) return;
|
|
38320
38486
|
const absolutePath = Path.join(rootDirectory, relativePath);
|
|
38321
38487
|
let content;
|
|
38322
38488
|
try {
|
|
@@ -38330,22 +38496,7 @@ const findFilesWithDisableDirectivesViaFilesystem = (rootDirectory, includePaths
|
|
|
38330
38496
|
for (const candidate of includePaths) checkFile(candidate);
|
|
38331
38497
|
return matches;
|
|
38332
38498
|
}
|
|
38333
|
-
const
|
|
38334
|
-
while (stack.length > 0) {
|
|
38335
|
-
const current = stack.pop();
|
|
38336
|
-
if (current === void 0) continue;
|
|
38337
|
-
const entries = readDirectoryEntries(current);
|
|
38338
|
-
for (const entry of entries) {
|
|
38339
|
-
if (entry.isDirectory()) {
|
|
38340
|
-
if (entry.name.startsWith(".") || IGNORED_DIRECTORIES.has(entry.name)) continue;
|
|
38341
|
-
stack.push(Path.join(current, entry.name));
|
|
38342
|
-
continue;
|
|
38343
|
-
}
|
|
38344
|
-
if (!entry.isFile()) continue;
|
|
38345
|
-
const absolute = Path.join(current, entry.name);
|
|
38346
|
-
checkFile(Path.relative(rootDirectory, absolute));
|
|
38347
|
-
}
|
|
38348
|
-
}
|
|
38499
|
+
for (const { absolutePath } of walkSourceTreeFiles(rootDirectory)) checkFile(Path.relative(rootDirectory, absolutePath));
|
|
38349
38500
|
return matches;
|
|
38350
38501
|
};
|
|
38351
38502
|
const findFilesWithDisableDirectives = async (rootDirectory, includePaths) => await findFilesWithDisableDirectivesViaGit(rootDirectory, includePaths) ?? findFilesWithDisableDirectivesViaFilesystem(rootDirectory, includePaths);
|
|
@@ -38391,7 +38542,7 @@ const normalizeConfigForHash = (config) => {
|
|
|
38391
38542
|
if (Array.isArray(clone?.jsPlugins)) clone.jsPlugins = clone.jsPlugins.map((_, index) => `<plugin:${index}>`);
|
|
38392
38543
|
return clone;
|
|
38393
38544
|
};
|
|
38394
|
-
const computeRulesetHash = (input) => crypto.createHash("sha1").update(JSON.stringify(normalizeConfigForHash(input.config))).update("\0").update([...input.toolchainVersions].join("\0")).update("\0").update([...input.ignorePatterns].join("\n")).update("
|
|
38545
|
+
const computeRulesetHash = (input) => crypto.createHash("sha1").update(JSON.stringify(normalizeConfigForHash(input.config))).update("\0").update([...input.toolchainVersions].join("\0")).update("\0").update([...input.ignorePatterns].join("\n")).update("\0").update(input.tsconfigContent ?? "").digest("hex");
|
|
38395
38546
|
/**
|
|
38396
38547
|
* Loads a plugin module via the local require resolver and extracts
|
|
38397
38548
|
* `(name, ruleNames)` from either `module.exports.meta + rules` or
|
|
@@ -38562,12 +38713,12 @@ const createOxlintConfig = ({ pluginPath, project, customRulesOnly = false, exte
|
|
|
38562
38713
|
if (customRulesOnly && registryEntry.originallyExternal) continue;
|
|
38563
38714
|
if (rule.framework !== "global" && !rule.requires) continue;
|
|
38564
38715
|
if (!shouldEnableRule(rule.requires, rule.tags, capabilities, ignoredTags, rule.disabledBy)) continue;
|
|
38565
|
-
const
|
|
38716
|
+
const explicitRuleOverride = resolveRuleSeverityOverride({ ruleKey: registryEntry.key }, severityControls);
|
|
38717
|
+
if (rule.defaultEnabled === false && explicitRuleOverride === void 0) continue;
|
|
38718
|
+
const severity = resolveRuleSeverityOverride({
|
|
38566
38719
|
ruleKey: registryEntry.key,
|
|
38567
38720
|
category: rule.category
|
|
38568
|
-
}, severityControls);
|
|
38569
|
-
if (rule.defaultEnabled === false && explicitSeverity === void 0) continue;
|
|
38570
|
-
const severity = explicitSeverity ?? resolveCompilerCleanupBucketSeverity(registryEntry.key, severityControls) ?? rule.severity;
|
|
38721
|
+
}, severityControls) ?? resolveCompilerCleanupBucketSeverity(registryEntry.key, severityControls) ?? rule.severity;
|
|
38571
38722
|
if (severity === "off") continue;
|
|
38572
38723
|
enabledReactDoctorRules[registryEntry.key] = severity;
|
|
38573
38724
|
}
|
|
@@ -38669,24 +38820,38 @@ const createFileLintCache = (cacheDirectory, rulesetHash) => {
|
|
|
38669
38820
|
updatedAtMs: Date.now(),
|
|
38670
38821
|
files: Object.fromEntries(cappedEntries)
|
|
38671
38822
|
};
|
|
38672
|
-
const keptHashes = Object.entries(rulesets).sort(([, first], [, second]) => second.updatedAtMs - first.updatedAtMs).slice(0, 8).map(([hash]) => hash);
|
|
38673
|
-
const prunedRulesets = {};
|
|
38674
|
-
for (const hash of keptHashes) prunedRulesets[hash] = rulesets[hash];
|
|
38675
38823
|
atomicWriteJson(cacheFilePath, {
|
|
38676
38824
|
version: 1,
|
|
38677
|
-
rulesets:
|
|
38825
|
+
rulesets: Object.fromEntries(Object.entries(rulesets).filter(([, bucket]) => isRecord(bucket) && typeof bucket.updatedAtMs === "number").sort(([, first], [, second]) => second.updatedAtMs - first.updatedAtMs).slice(0, 8))
|
|
38678
38826
|
});
|
|
38679
38827
|
}
|
|
38680
38828
|
};
|
|
38681
38829
|
};
|
|
38682
38830
|
const bundledRequire = createRequire(import.meta.url);
|
|
38831
|
+
const childNodeVersionByBinaryPath = /* @__PURE__ */ new Map();
|
|
38832
|
+
const resolveChildNodeVersion = (nodeBinaryPath) => {
|
|
38833
|
+
if (nodeBinaryPath === process.execPath) return process.version;
|
|
38834
|
+
const cachedVersion = childNodeVersionByBinaryPath.get(nodeBinaryPath);
|
|
38835
|
+
if (cachedVersion !== void 0) return cachedVersion;
|
|
38836
|
+
let version;
|
|
38837
|
+
try {
|
|
38838
|
+
version = execFileSync(nodeBinaryPath, ["--version"], {
|
|
38839
|
+
encoding: "utf8",
|
|
38840
|
+
timeout: NODE_VERSION_PROBE_TIMEOUT_MS
|
|
38841
|
+
}).trim();
|
|
38842
|
+
} catch {
|
|
38843
|
+
version = `unknown:${nodeBinaryPath}`;
|
|
38844
|
+
}
|
|
38845
|
+
childNodeVersionByBinaryPath.set(nodeBinaryPath, version);
|
|
38846
|
+
return version;
|
|
38847
|
+
};
|
|
38683
38848
|
const TOOLCHAIN_PACKAGE_SPECIFIERS = [
|
|
38684
38849
|
"oxlint/package.json",
|
|
38685
38850
|
"oxlint-plugin-react-doctor/package.json",
|
|
38686
38851
|
"eslint-plugin-react-hooks/package.json"
|
|
38687
38852
|
];
|
|
38688
|
-
const resolveOxlintToolchainVersions = () => {
|
|
38689
|
-
const versions = [`node=${
|
|
38853
|
+
const resolveOxlintToolchainVersions = (nodeBinaryPath = process.execPath) => {
|
|
38854
|
+
const versions = [`node=${resolveChildNodeVersion(nodeBinaryPath)}`];
|
|
38690
38855
|
for (const specifier of TOOLCHAIN_PACKAGE_SPECIFIERS) try {
|
|
38691
38856
|
const packageJson = bundledRequire(specifier);
|
|
38692
38857
|
const version = typeof packageJson.version === "string" ? packageJson.version : "unknown";
|
|
@@ -39220,11 +39385,12 @@ const REACT_COMPILER_TITLE = "React Compiler can't optimize this";
|
|
|
39220
39385
|
const REACT_COMPILER_TODO_TITLE = "React Compiler doesn't support this syntax";
|
|
39221
39386
|
const REACT_COMPILER_IMPACT = "This component misses React Compiler's automatic memoization & re-renders more than it should";
|
|
39222
39387
|
const REACT_COMPILER_ACTION = "Rewrite the flagged code so the compiler can optimize it.";
|
|
39388
|
+
const REACT_COMPILER_INCOMPATIBLE_LIBRARY_ACTION = "It's how the library works, not a bug in your code. Memoize values you pass from it into other memoized components, or suppress it with `// react-doctor-disable-next-line react-hooks-js/incompatible-library`.";
|
|
39223
39389
|
const REACT_COMPILER_GENERIC_MESSAGE = `${REACT_COMPILER_IMPACT}. ${REACT_COMPILER_ACTION}`;
|
|
39224
|
-
const buildReactCompilerMessage = (reasonSummary) => {
|
|
39390
|
+
const buildReactCompilerMessage = (reasonSummary, action = REACT_COMPILER_ACTION) => {
|
|
39225
39391
|
const normalizedSummary = reasonSummary.replace(TRAILING_PERIOD_PATTERN, "");
|
|
39226
|
-
if (!normalizedSummary) return
|
|
39227
|
-
return `${REACT_COMPILER_IMPACT}: ${normalizedSummary}. ${
|
|
39392
|
+
if (!normalizedSummary) return `${REACT_COMPILER_IMPACT}. ${action}`;
|
|
39393
|
+
return `${REACT_COMPILER_IMPACT}: ${normalizedSummary}. ${action}`;
|
|
39228
39394
|
};
|
|
39229
39395
|
const PLUGIN_CATEGORY_MAP = {
|
|
39230
39396
|
react: "Bugs",
|
|
@@ -39273,7 +39439,7 @@ const resolveCleanedDiagnostic = (message, help, plugin, rule, project) => {
|
|
|
39273
39439
|
const [reasonSummary = "", ...reasonDetailLines] = bailoutReason.split("\n");
|
|
39274
39440
|
const reasonDetail = reasonDetailLines.join("\n").trim();
|
|
39275
39441
|
return {
|
|
39276
|
-
message: buildReactCompilerMessage(reasonSummary.trim()),
|
|
39442
|
+
message: buildReactCompilerMessage(reasonSummary.trim(), rule === "incompatible-library" ? REACT_COMPILER_INCOMPATIBLE_LIBRARY_ACTION : REACT_COMPILER_ACTION),
|
|
39277
39443
|
help: appendReanimatedSharedValueHint(reasonDetail || help, rule, project)
|
|
39278
39444
|
};
|
|
39279
39445
|
}
|
|
@@ -39534,28 +39700,46 @@ const isParallelismRelatedSpawnError = (error) => {
|
|
|
39534
39700
|
* loop with a slimmer config in that case.
|
|
39535
39701
|
*/
|
|
39536
39702
|
const spawnLintBatches = async (input) => {
|
|
39537
|
-
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;
|
|
39538
39704
|
const requestedConcurrency = resolveScanConcurrency(input.concurrency ?? 1);
|
|
39539
39705
|
const totalFileCount = fileBatches.reduce((sum, batch) => sum + batch.length, 0);
|
|
39540
39706
|
const runBatchPass = async (concurrency) => {
|
|
39541
39707
|
const allDiagnostics = [];
|
|
39542
39708
|
const droppedFiles = [];
|
|
39709
|
+
const deadlineSkippedFiles = [];
|
|
39710
|
+
const isPastDeadline = () => deadlineEpochMs !== void 0 && remainingDeadlineBudgetMs(deadlineEpochMs) === 0;
|
|
39543
39711
|
let firstDropReason = null;
|
|
39544
|
-
const
|
|
39545
|
-
|
|
39712
|
+
const spawnLintBatch = async (batch, depth, batchState) => {
|
|
39713
|
+
if (isPastDeadline()) {
|
|
39714
|
+
deadlineSkippedFiles.push(...batch);
|
|
39715
|
+
batchState.deadlineSkippedFileCount += batch.length;
|
|
39716
|
+
return [];
|
|
39717
|
+
}
|
|
39546
39718
|
const batchArgs = [...baseArgs, ...batch];
|
|
39547
39719
|
try {
|
|
39548
39720
|
return parseOxlintOutput(await spawnOxlint(batchArgs, rootDirectory, nodeBinaryPath, spawnTimeoutMs, outputMaxBytes, signal), project, rootDirectory);
|
|
39549
39721
|
} catch (error) {
|
|
39550
39722
|
if (!isSplittableReactDoctorError(error)) throw error;
|
|
39551
|
-
|
|
39552
|
-
|
|
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;
|
|
39730
|
+
const isDepthCapReached = depth >= splitMaxDepth;
|
|
39731
|
+
if (batch.length <= 1 || isBudgetElapsed || isDepthCapReached) {
|
|
39553
39732
|
droppedFiles.push(...batch);
|
|
39554
|
-
if (firstDropReason === null)
|
|
39733
|
+
if (firstDropReason === null) {
|
|
39734
|
+
let limitHint = "";
|
|
39735
|
+
if (isDepthCapReached) limitHint = ` (split depth cap of ${splitMaxDepth} levels reached)`;
|
|
39736
|
+
else if (isBudgetElapsed) limitHint = ` (split budget of ${splitTotalBudgetMs / MILLISECONDS_PER_SECOND}s exhausted at depth ${depth})`;
|
|
39737
|
+
firstDropReason = batch.length > 1 ? `${error.message}${limitHint}` : error.message;
|
|
39738
|
+
}
|
|
39555
39739
|
return [];
|
|
39556
39740
|
}
|
|
39557
39741
|
const splitIndex = Math.ceil(batch.length / 2);
|
|
39558
|
-
return [...await spawnLintBatch(batch.slice(0, splitIndex), depth + 1), ...await spawnLintBatch(batch.slice(splitIndex), depth + 1)];
|
|
39742
|
+
return [...await spawnLintBatch(batch.slice(0, splitIndex), depth + 1, batchState), ...await spawnLintBatch(batch.slice(splitIndex), depth + 1, batchState)];
|
|
39559
39743
|
}
|
|
39560
39744
|
};
|
|
39561
39745
|
let startedFileCount = 0;
|
|
@@ -39571,9 +39755,17 @@ const spawnLintBatches = async (input) => {
|
|
|
39571
39755
|
progressTimer?.unref?.();
|
|
39572
39756
|
try {
|
|
39573
39757
|
const batchResults = await mapWithConcurrency(fileBatches, concurrency, async (batch) => {
|
|
39758
|
+
if (isPastDeadline()) {
|
|
39759
|
+
deadlineSkippedFiles.push(...batch);
|
|
39760
|
+
return [];
|
|
39761
|
+
}
|
|
39574
39762
|
startedFileCount += batch.length;
|
|
39575
|
-
const
|
|
39576
|
-
|
|
39763
|
+
const batchState = {
|
|
39764
|
+
deadlineMs: null,
|
|
39765
|
+
deadlineSkippedFileCount: 0
|
|
39766
|
+
};
|
|
39767
|
+
const batchDiagnostics = await spawnLintBatch(batch, 0, batchState);
|
|
39768
|
+
scannedFileCount += batch.length - batchState.deadlineSkippedFileCount;
|
|
39577
39769
|
if (onFileProgress) {
|
|
39578
39770
|
displayedFileCount = Math.min(Math.max(displayedFileCount, scannedFileCount), totalFileCount);
|
|
39579
39771
|
onFileProgress(displayedFileCount, totalFileCount);
|
|
@@ -39584,12 +39776,13 @@ const spawnLintBatches = async (input) => {
|
|
|
39584
39776
|
} finally {
|
|
39585
39777
|
if (progressTimer !== null) clearInterval(progressTimer);
|
|
39586
39778
|
}
|
|
39587
|
-
|
|
39588
|
-
|
|
39589
|
-
|
|
39590
|
-
|
|
39591
|
-
|
|
39592
|
-
}
|
|
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})`);
|
|
39593
39786
|
return allDiagnostics;
|
|
39594
39787
|
};
|
|
39595
39788
|
let diagnostics;
|
|
@@ -39646,7 +39839,14 @@ const resolveReactDoctorCacheDir = (projectDirectory) => {
|
|
|
39646
39839
|
if (cacheDirOverride) return Path.join(cacheDirOverride, projectCacheSubdir(projectDirectory));
|
|
39647
39840
|
const nodeModulesDirectory = Path.join(projectDirectory, "node_modules");
|
|
39648
39841
|
if (NFS.existsSync(nodeModulesDirectory)) return Path.join(nodeModulesDirectory, ".cache", "react-doctor");
|
|
39649
|
-
return Path.join(os.tmpdir(),
|
|
39842
|
+
return Path.join(os.tmpdir(), `react-doctor-cache-${resolveUserCacheScope()}`, projectCacheSubdir(projectDirectory));
|
|
39843
|
+
};
|
|
39844
|
+
const resolveUserCacheScope = () => {
|
|
39845
|
+
try {
|
|
39846
|
+
return String(process.getuid?.() ?? os.userInfo().username);
|
|
39847
|
+
} catch {
|
|
39848
|
+
return "shared";
|
|
39849
|
+
}
|
|
39650
39850
|
};
|
|
39651
39851
|
const sortSourceFilesByCost = (entries) => [...entries].sort((left, right) => right.sizeBytes - left.sizeBytes).map((entry) => entry.path);
|
|
39652
39852
|
/**
|
|
@@ -39786,7 +39986,8 @@ const runOxlint = async (options) => {
|
|
|
39786
39986
|
spawnTimeoutMs,
|
|
39787
39987
|
outputMaxBytes,
|
|
39788
39988
|
concurrency: options.concurrency,
|
|
39789
|
-
signal: options.signal
|
|
39989
|
+
signal: options.signal,
|
|
39990
|
+
deadlineEpochMs: options.deadlineEpochMs
|
|
39790
39991
|
});
|
|
39791
39992
|
writeOxlintConfig(passConfigPath, buildConfigForPass({}));
|
|
39792
39993
|
try {
|
|
@@ -39814,7 +40015,7 @@ const runOxlint = async (options) => {
|
|
|
39814
40015
|
extendsPaths: [],
|
|
39815
40016
|
ruleSelection: "cacheable"
|
|
39816
40017
|
}),
|
|
39817
|
-
toolchainVersions: resolveOxlintToolchainVersions(),
|
|
40018
|
+
toolchainVersions: resolveOxlintToolchainVersions(nodeBinaryPath),
|
|
39818
40019
|
ignorePatterns: combinedPatterns,
|
|
39819
40020
|
tsconfigContent
|
|
39820
40021
|
});
|
|
@@ -39828,7 +40029,7 @@ const runOxlint = async (options) => {
|
|
|
39828
40029
|
missFiles.push(candidateFile);
|
|
39829
40030
|
continue;
|
|
39830
40031
|
}
|
|
39831
|
-
const cacheKey = `${candidateFile.replaceAll("\\", "/")}
|
|
40032
|
+
const cacheKey = `${candidateFile.replaceAll("\\", "/")}\u0000${contentHash}`;
|
|
39832
40033
|
cacheKeyByFile.set(candidateFile, cacheKey);
|
|
39833
40034
|
const cachedDiagnostics = cache.lookup(cacheKey);
|
|
39834
40035
|
if (cachedDiagnostics === null) missFiles.push(candidateFile);
|
|
@@ -39885,7 +40086,8 @@ const runOxlint = async (options) => {
|
|
|
39885
40086
|
spawnTimeoutMs,
|
|
39886
40087
|
outputMaxBytes,
|
|
39887
40088
|
concurrency: options.concurrency,
|
|
39888
|
-
signal: options.signal
|
|
40089
|
+
signal: options.signal,
|
|
40090
|
+
deadlineEpochMs: options.deadlineEpochMs
|
|
39889
40091
|
});
|
|
39890
40092
|
writeOxlintConfig(configPath, buildConfig({ extendsPaths }));
|
|
39891
40093
|
try {
|
|
@@ -39989,7 +40191,8 @@ var Linter = class Linter extends Service()("react-doctor/Linter") {
|
|
|
39989
40191
|
outputMaxBytes,
|
|
39990
40192
|
concurrency,
|
|
39991
40193
|
signal,
|
|
39992
|
-
lintBatchOrdering
|
|
40194
|
+
lintBatchOrdering,
|
|
40195
|
+
deadlineEpochMs: input.deadlineEpochMs
|
|
39993
40196
|
}),
|
|
39994
40197
|
catch: ensureReactDoctorError
|
|
39995
40198
|
});
|
|
@@ -40144,7 +40347,7 @@ const ScoreApiResponseSchema = Struct({
|
|
|
40144
40347
|
})))
|
|
40145
40348
|
});
|
|
40146
40349
|
const parseScoreResult = (value) => getOrNull(decodeUnknownOption(ScoreApiResponseSchema)(value));
|
|
40147
|
-
const stripLocalFileFields = (diagnostics) => diagnostics.map(({ filePath: _filePath, fileContext: _fileContext, ...rest }) => rest);
|
|
40350
|
+
const stripLocalFileFields = (diagnostics) => diagnostics.map(({ filePath: _filePath, fileContext: _fileContext, fixGroupId: _fixGroupId, ...rest }) => rest);
|
|
40148
40351
|
const isAbortError = (error) => error instanceof Error && (error.name === "AbortError" || error.name === "TimeoutError");
|
|
40149
40352
|
const describeFailure = (error) => {
|
|
40150
40353
|
if (isAbortError(error)) return `timed out after ${FETCH_TIMEOUT_MS / 1e3}s`;
|
|
@@ -40448,7 +40651,10 @@ const ALERT_SEVERITY_RANK = {
|
|
|
40448
40651
|
medium: 2,
|
|
40449
40652
|
low: 1
|
|
40450
40653
|
};
|
|
40451
|
-
const severityRank = (severity) =>
|
|
40654
|
+
const severityRank = (severity) => {
|
|
40655
|
+
const normalized = severity.toLowerCase();
|
|
40656
|
+
return Object.hasOwn(ALERT_SEVERITY_RANK, normalized) ? ALERT_SEVERITY_RANK[normalized] : 0;
|
|
40657
|
+
};
|
|
40452
40658
|
const displaySeverity = (severity) => {
|
|
40453
40659
|
const normalized = sanitizeTerminalText(severity.toLowerCase());
|
|
40454
40660
|
return normalized === "middle" ? "medium" : normalized;
|
|
@@ -40465,7 +40671,7 @@ const ALERT_TYPE_LABELS = {
|
|
|
40465
40671
|
typosquat: "possible typosquat"
|
|
40466
40672
|
};
|
|
40467
40673
|
const humanizeAlertType = (type) => type.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/[_-]+/g, " ").toLowerCase().trim();
|
|
40468
|
-
const friendlyAlertType = (type) => ALERT_TYPE_LABELS[type]
|
|
40674
|
+
const friendlyAlertType = (type) => Object.hasOwn(ALERT_TYPE_LABELS, type) ? ALERT_TYPE_LABELS[type] : sanitizeTerminalText(humanizeAlertType(type));
|
|
40469
40675
|
const summarizeAlertNote = (note) => {
|
|
40470
40676
|
const collapsed = sanitizeTerminalText(note.replace(/\s+/g, " ").trim());
|
|
40471
40677
|
const firstSentence = collapsed.split(/(?<=\.)\s/)[0] || collapsed;
|
|
@@ -40560,7 +40766,10 @@ const checkSupplyChain = (input) => gen(function* () {
|
|
|
40560
40766
|
* outage.
|
|
40561
40767
|
*/
|
|
40562
40768
|
var SupplyChain = class SupplyChain extends Service()("react-doctor/SupplyChain") {
|
|
40563
|
-
static layerNode = succeed$3(SupplyChain, SupplyChain.of({ run: (input) => unwrap(
|
|
40769
|
+
static layerNode = succeed$3(SupplyChain, SupplyChain.of({ run: (input) => unwrap(flatMap$2(SupplyChainOverlapTimeoutMs, (totalTimeoutMs) => checkSupplyChain({
|
|
40770
|
+
...input,
|
|
40771
|
+
totalTimeoutMs
|
|
40772
|
+
})).pipe(map$3((diagnostics) => fromIterable$1(diagnostics)), withSpan("SupplyChain.run"))) }));
|
|
40564
40773
|
static layerOf = (diagnostics) => succeed$3(SupplyChain, SupplyChain.of({ run: () => fromIterable$1(diagnostics) }));
|
|
40565
40774
|
};
|
|
40566
40775
|
const getObjectProperty = (value, propertyName) => {
|
|
@@ -40622,7 +40831,10 @@ const formatLintFailText = (reasonTag, nodeVersion) => {
|
|
|
40622
40831
|
* diagnostics).
|
|
40623
40832
|
* 2. beforeLint hook (e.g. CLI renders the project-detection block)
|
|
40624
40833
|
* 3. environment checks (reduced-motion + pnpm hardening +
|
|
40625
|
-
* expo/react-native
|
|
40834
|
+
* expo/react-native), collected synchronously. The heavier
|
|
40835
|
+
* content-regex security scan is forked instead (like supply-chain
|
|
40836
|
+
* below) and joined before the concat, so its CPU overlaps lint
|
|
40837
|
+
* rather than blocking the event loop before it.
|
|
40626
40838
|
* 4. The supply-chain check (Socket.dev) is forked onto a background
|
|
40627
40839
|
* fiber so its ~100% network-bound time overlaps the ~100%
|
|
40628
40840
|
* CPU/subprocess-bound lint pass below, collapsing two serial
|
|
@@ -40642,7 +40854,7 @@ const formatLintFailText = (reasonTag, nodeVersion) => {
|
|
|
40642
40854
|
* order, so terminal output is identical either way; supply-chain
|
|
40643
40855
|
* rides alongside without a spinner.
|
|
40644
40856
|
* 6. Join the supply-chain fiber, then assemble the diagnostics in a
|
|
40645
|
-
* FIXED order (env, supply-chain, lint, dead-code) so the output is
|
|
40857
|
+
* FIXED order (env, security-scan, supply-chain, lint, dead-code) so the output is
|
|
40646
40858
|
* byte-identical regardless of which fiber settled first. The
|
|
40647
40859
|
* viewer-permission fiber is joined later, during score-metadata
|
|
40648
40860
|
* assembly (it feeds score metadata, not diagnostics). The per-element
|
|
@@ -40703,12 +40915,13 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
40703
40915
|
...checkPnpmHardening(scanDirectory),
|
|
40704
40916
|
...checkReactServerComponentsAdvisory(scanDirectory, project),
|
|
40705
40917
|
...checkExpoProject(scanDirectory, project),
|
|
40706
|
-
...checkReactNativeProject(scanDirectory, project)
|
|
40707
|
-
...checkSecurityScan(scanDirectory, {
|
|
40708
|
-
project,
|
|
40709
|
-
ignoredTags: input.ignoredTags
|
|
40710
|
-
})
|
|
40918
|
+
...checkReactNativeProject(scanDirectory, project)
|
|
40711
40919
|
])));
|
|
40920
|
+
const securityScanFailedRef = yield* make$13(false);
|
|
40921
|
+
const securityScanFiber = yield* forkChild(runCollect(applyPerElementPipeline(isDiffMode ? empty$4 : unwrap(tryPromise(() => checkSecurityScanCooperative(scanDirectory, {
|
|
40922
|
+
project,
|
|
40923
|
+
ignoredTags: input.ignoredTags
|
|
40924
|
+
})).pipe(map$3((diagnostics) => fromIterable$1(diagnostics)), catch_$1(() => set(securityScanFailedRef, true).pipe(as(empty$4))))))).pipe(withSpan("SecurityScan.run")));
|
|
40712
40925
|
const shouldRunSupplyChain = !isDiffMode || (input.supplyChainManifestChanged ?? false);
|
|
40713
40926
|
const supplyChainOverlapTimeout = yield* SupplyChainOverlapTimeoutMs;
|
|
40714
40927
|
const supplyChainFiber = yield* forkChild(shouldRunSupplyChain ? runCollect(applyPerElementPipeline(supplyChainService.run({
|
|
@@ -40739,6 +40952,7 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
40739
40952
|
const deadCodePhaseTimeoutMs = yield* DeadCodePhaseTimeoutMs;
|
|
40740
40953
|
const resolveDeadCodePhaseTimeoutMs = (scaledPhaseTimeoutMs) => deadCodePhaseTimeoutMs === 15e4 ? scaledPhaseTimeoutMs : deadCodePhaseTimeoutMs;
|
|
40741
40954
|
const workerCountSuffix = scanConcurrency > 1 ? ` ${highlighter.dim(`[~${scanConcurrency} workers]`)}` : "";
|
|
40955
|
+
const capToDeadline = (phaseTimeoutMs) => input.deadlineEpochMs === void 0 ? phaseTimeoutMs : Math.min(phaseTimeoutMs, remainingDeadlineBudgetMs(input.deadlineEpochMs));
|
|
40742
40956
|
const shouldRunDeadCode = input.runDeadCode && !isDiffMode && (showWarnings || deadCodeMaySurfaceWhenWarningsHidden(resolvedConfig.config));
|
|
40743
40957
|
const deadCodeOverlapMode = yield* DeadCodeOverlap;
|
|
40744
40958
|
const shouldOverlapDeadCode = shouldRunDeadCode && deadCodeOverlapMode === "on";
|
|
@@ -40746,7 +40960,6 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
40746
40960
|
const lintConcurrency = deadCodeParseConcurrency === void 0 ? scanConcurrency : Math.max(1, scanConcurrency - deadCodeParseConcurrency);
|
|
40747
40961
|
const buildCollectDeadCode = (deadCodeTimeout) => runCollect(applyPerElementPipeline(deadCodeService.run({
|
|
40748
40962
|
rootDirectory: scanDirectory,
|
|
40749
|
-
userConfig: resolvedConfig.config,
|
|
40750
40963
|
parseConcurrency: deadCodeParseConcurrency,
|
|
40751
40964
|
workerTimeoutMs: deadCodeTimeout.workerTimeoutMs
|
|
40752
40965
|
}).pipe(catchTag("ReactDoctorError", (error) => unwrap(gen(function* () {
|
|
@@ -40769,7 +40982,7 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
40769
40982
|
});
|
|
40770
40983
|
const deadCodeFiber = shouldOverlapDeadCode ? yield* forkChild(buildCollectDeadCode({
|
|
40771
40984
|
workerTimeoutMs: overlapDeadCodeTimeout.workerTimeoutMs,
|
|
40772
|
-
phaseTimeoutMs: resolveDeadCodePhaseTimeoutMs(overlapDeadCodeTimeout.phaseTimeoutMs)
|
|
40985
|
+
phaseTimeoutMs: capToDeadline(resolveDeadCodePhaseTimeoutMs(overlapDeadCodeTimeout.phaseTimeoutMs))
|
|
40773
40986
|
})) : null;
|
|
40774
40987
|
const scanProgress = yield* progressService.start("Scanning...");
|
|
40775
40988
|
const scanStartTime = Date.now();
|
|
@@ -40794,7 +41007,8 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
40794
41007
|
onCacheStats: (cacheHitFileCount, totalConsideredFileCount) => {
|
|
40795
41008
|
lintCacheHitFileCount = cacheHitFileCount;
|
|
40796
41009
|
lintCacheTotalFileCount = totalConsideredFileCount;
|
|
40797
|
-
}
|
|
41010
|
+
},
|
|
41011
|
+
deadlineEpochMs: input.deadlineEpochMs
|
|
40798
41012
|
}).pipe(catchTag("ReactDoctorError", (error) => unwrap(gen(function* () {
|
|
40799
41013
|
yield* set(lintFailure, {
|
|
40800
41014
|
didFail: true,
|
|
@@ -40821,7 +41035,13 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
40821
41035
|
let deadCodeCollected = [];
|
|
40822
41036
|
if (lintFailureState.didFail) {
|
|
40823
41037
|
if (deadCodeFiber !== null) yield* interrupt(deadCodeFiber);
|
|
40824
|
-
} 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 {
|
|
40825
41045
|
yield* scanProgress.update(`Scanned ${scannedFilesLabel}, analyzing dead code...`);
|
|
40826
41046
|
const sequentialDeadCodeTimeout = resolveDeadCodeTimeout({
|
|
40827
41047
|
sourceFileCount: totalFileCount,
|
|
@@ -40830,7 +41050,7 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
40830
41050
|
});
|
|
40831
41051
|
deadCodeCollected = deadCodeFiber !== null ? yield* join(deadCodeFiber) : yield* buildCollectDeadCode({
|
|
40832
41052
|
workerTimeoutMs: sequentialDeadCodeTimeout.workerTimeoutMs,
|
|
40833
|
-
phaseTimeoutMs: resolveDeadCodePhaseTimeoutMs(sequentialDeadCodeTimeout.phaseTimeoutMs)
|
|
41053
|
+
phaseTimeoutMs: capToDeadline(resolveDeadCodePhaseTimeoutMs(sequentialDeadCodeTimeout.phaseTimeoutMs))
|
|
40834
41054
|
});
|
|
40835
41055
|
}
|
|
40836
41056
|
const deadCodeFailureState = lintFailureState.didFail ? {
|
|
@@ -40844,9 +41064,11 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
40844
41064
|
else yield* scanProgress.succeed(`Scanned ${scannedFilesLabel} in ${scanElapsedSeconds}s${workerCountSuffix}`);
|
|
40845
41065
|
const supplyChainResult = yield* join(supplyChainFiber);
|
|
40846
41066
|
const supplyChainCollected = supplyChainResult.diagnostics;
|
|
41067
|
+
const securityScanCollected = yield* join(securityScanFiber);
|
|
40847
41068
|
yield* reporterService.finalize;
|
|
40848
41069
|
const finalDiagnostics = sortDiagnosticsStable(assignFixGroups([
|
|
40849
41070
|
...envCollected,
|
|
41071
|
+
...securityScanCollected,
|
|
40850
41072
|
...supplyChainCollected,
|
|
40851
41073
|
...lintCollected,
|
|
40852
41074
|
...deadCodeCollected
|
|
@@ -40866,12 +41088,13 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
40866
41088
|
};
|
|
40867
41089
|
const scoreSurface = input.scoreSurface ?? "score";
|
|
40868
41090
|
const scoreDiagnostics = filterDiagnosticsForSurface([...finalDiagnostics], scoreSurface, resolvedConfig.config);
|
|
40869
|
-
const score = lintFailureState.didFail ? null : yield* scoreService.compute({
|
|
41091
|
+
const score = lintFailureState.didFail || deadCodeFailureState.didFail ? null : yield* scoreService.compute({
|
|
40870
41092
|
diagnostics: scoreDiagnostics,
|
|
40871
41093
|
isCi: input.isCi,
|
|
40872
41094
|
metadata: scoreMetadata
|
|
40873
41095
|
});
|
|
40874
41096
|
const lintPartialFailures = yield* get$2(partialFailuresRef);
|
|
41097
|
+
const securityScanFailed = yield* get$2(securityScanFailedRef);
|
|
40875
41098
|
return {
|
|
40876
41099
|
project,
|
|
40877
41100
|
userConfig: resolvedConfig.config,
|
|
@@ -40892,8 +41115,10 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
40892
41115
|
scanElapsedMilliseconds,
|
|
40893
41116
|
scanConcurrency,
|
|
40894
41117
|
supplyChainOverlapTimedOut: supplyChainResult.timedOut,
|
|
41118
|
+
securityScanFailed,
|
|
40895
41119
|
lintCacheHitFileCount,
|
|
40896
|
-
lintCacheTotalFileCount
|
|
41120
|
+
lintCacheTotalFileCount,
|
|
41121
|
+
suppressedRuleCounts: transform.summarizeSuppressions()
|
|
40897
41122
|
};
|
|
40898
41123
|
}).pipe(withSpan("runInspect", { attributes: {
|
|
40899
41124
|
"inspect.directory": input.directory,
|
|
@@ -41232,6 +41457,7 @@ const buildJsonReport = (input) => {
|
|
|
41232
41457
|
return {
|
|
41233
41458
|
schemaVersion: 1,
|
|
41234
41459
|
mode: input.mode,
|
|
41460
|
+
...input.baselineDegraded ? { baselineDegraded: true } : {},
|
|
41235
41461
|
...shared
|
|
41236
41462
|
};
|
|
41237
41463
|
};
|
|
@@ -41298,6 +41524,61 @@ const clearAutoSuppressionCaches = () => {};
|
|
|
41298
41524
|
* untouched so the loader sees the same plain object either way.
|
|
41299
41525
|
*/
|
|
41300
41526
|
const defineConfig = (config) => config;
|
|
41527
|
+
const AI_TRAINING_ENV_VARS = [
|
|
41528
|
+
["HF_DATASETS_CACHE", "huggingface"],
|
|
41529
|
+
["HF_HOME", "huggingface"],
|
|
41530
|
+
["HUGGINGFACE_HUB_CACHE", "huggingface"],
|
|
41531
|
+
["SPACE_ID", "huggingface-spaces"],
|
|
41532
|
+
["CUDA_VISIBLE_DEVICES", "cuda"],
|
|
41533
|
+
["NVIDIA_VISIBLE_DEVICES", "nvidia"],
|
|
41534
|
+
["ROCR_VISIBLE_DEVICES", "rocm"],
|
|
41535
|
+
["TPU_NAME", "google-tpu"],
|
|
41536
|
+
["WANDB_RUN_ID", "wandb"],
|
|
41537
|
+
["WANDB_SWEEP_ID", "wandb"],
|
|
41538
|
+
["MLFLOW_RUN_ID", "mlflow"],
|
|
41539
|
+
["MLFLOW_TRACKING_URI", "mlflow"],
|
|
41540
|
+
["COMET_EXPERIMENT_KEY", "comet"],
|
|
41541
|
+
["NEPTUNE_RUN_ID", "neptune"],
|
|
41542
|
+
["CLEARML_TASK_ID", "clearml"],
|
|
41543
|
+
["DVC_STAGE", "dvc"],
|
|
41544
|
+
["RAY_WORKER_PROCESS", "ray"],
|
|
41545
|
+
["RAY_ADDRESS", "ray"],
|
|
41546
|
+
["MUJOCO_GL", "mujoco"],
|
|
41547
|
+
["MUJOCO_PATH", "mujoco"],
|
|
41548
|
+
["GYM_DISABLE_ENV_CHECKER", "gymnasium"],
|
|
41549
|
+
["SM_TRAINING_ENV", "sagemaker"],
|
|
41550
|
+
["TRAINING_JOB_ARN", "sagemaker"],
|
|
41551
|
+
["SAGEMAKER_BASE_DIR", "sagemaker"],
|
|
41552
|
+
["AZUREML_RUN_ID", "azure-ml"],
|
|
41553
|
+
["AZURE_ML_MODEL_DIR", "azure-ml"],
|
|
41554
|
+
["CLOUD_ML_PROJECT_ID", "vertex-ai"],
|
|
41555
|
+
["VERTEX_AI_LOG_LEVEL", "vertex-ai"],
|
|
41556
|
+
["DET_MASTER", "determined-ai"],
|
|
41557
|
+
["LIGHTNING_USER_ID", "lightning-ai"],
|
|
41558
|
+
["FLYTE_INTERNAL_EXECUTION_ID", "flyte"],
|
|
41559
|
+
["ARGO_WORKFLOW_NAME", "argo-workflows"],
|
|
41560
|
+
["KFP_POD_NAME", "kubeflow-pipelines"],
|
|
41561
|
+
["KAGGLE_KERNEL_RUN_TYPE", "kaggle"],
|
|
41562
|
+
["COLAB_BACKEND_VERSION", "google-colab"],
|
|
41563
|
+
["DATABRICKS_RUNTIME_VERSION", "databricks"],
|
|
41564
|
+
["DAYTONA_WS_ID", "daytona"],
|
|
41565
|
+
["DAYTONA_WS_NAME", "daytona"],
|
|
41566
|
+
["E2B_SANDBOX_ID", "e2b"],
|
|
41567
|
+
["MODAL_FUNCTION_ID", "modal"],
|
|
41568
|
+
["MODAL_TASK_ID", "modal"],
|
|
41569
|
+
["RUNPOD_POD_ID", "runpod"],
|
|
41570
|
+
["REPLICATE_USERNAME", "replicate"],
|
|
41571
|
+
["VAST_CONTAINERLABEL", "vast-ai"],
|
|
41572
|
+
["HARBOR_URL", "harbor"],
|
|
41573
|
+
["HARBOR_HOSTNAME", "harbor"],
|
|
41574
|
+
["SWE_BENCH_TASK", "swe-bench"],
|
|
41575
|
+
["SWEBENCH_TASK", "swe-bench"],
|
|
41576
|
+
["SWE_AGENT_MODEL", "swe-agent"]
|
|
41577
|
+
];
|
|
41578
|
+
const detectAiTrainingEnvironment = () => {
|
|
41579
|
+
for (const [envVar, label] of AI_TRAINING_ENV_VARS) if (process.env[envVar]) return label;
|
|
41580
|
+
return null;
|
|
41581
|
+
};
|
|
41301
41582
|
const unionValues = (baseValues, overrideValues) => [...new Set([...baseValues, ...overrideValues])];
|
|
41302
41583
|
const mergeIgnores = (baseIgnore, overrideIgnore) => {
|
|
41303
41584
|
const mergedIgnore = {
|
|
@@ -41342,6 +41623,12 @@ const mergeReactDoctorConfigs = (baseConfig, overrideConfig) => {
|
|
|
41342
41623
|
};
|
|
41343
41624
|
//#endregion
|
|
41344
41625
|
//#region ../api/dist/index.js
|
|
41626
|
+
let didWarnAiTraining = false;
|
|
41627
|
+
const warnIfAiTrainingEnvironment = () => {
|
|
41628
|
+
if (didWarnAiTraining || detectAiTrainingEnvironment() === null) return;
|
|
41629
|
+
didWarnAiTraining = true;
|
|
41630
|
+
console.warn("[react-doctor] Use in an AI or ML pipeline requires written permission under the react-doctor license. Contact founders@million.dev to request access.");
|
|
41631
|
+
};
|
|
41345
41632
|
const buildDiagnoseLayer = (config, configOverrideTarget) => {
|
|
41346
41633
|
const configLayer = configOverrideTarget === void 0 ? Config.layerNode : Config.layerOf({
|
|
41347
41634
|
config,
|
|
@@ -41379,6 +41666,7 @@ const outputToDiagnoseResult = (output, elapsedMilliseconds) => {
|
|
|
41379
41666
|
};
|
|
41380
41667
|
};
|
|
41381
41668
|
const diagnoseDirectory = async (directory, options) => {
|
|
41669
|
+
warnIfAiTrainingEnvironment();
|
|
41382
41670
|
const startTime = globalThis.performance.now();
|
|
41383
41671
|
const scanTarget = await resolveScanTarget(directory);
|
|
41384
41672
|
return outputToDiagnoseResult(await runPromise(restoreLegacyThrow(buildInspectProgram(scanTarget, options).pipe(provide(buildDiagnoseLayer(scanTarget.userConfig)), provide(layerOtlp)))), globalThis.performance.now() - startTime);
|
|
@@ -41425,6 +41713,7 @@ const diagnoseProject = async (projectDefinition, baseOptions, batchConfig) => {
|
|
|
41425
41713
|
}
|
|
41426
41714
|
};
|
|
41427
41715
|
const diagnoseProjectBatch = async (input) => {
|
|
41716
|
+
warnIfAiTrainingEnvironment();
|
|
41428
41717
|
const startTime = globalThis.performance.now();
|
|
41429
41718
|
const { projects, concurrency, config: batchConfig, ...baseOptions } = input;
|
|
41430
41719
|
const projectResults = await mapWithConcurrency(projects, concurrency ?? 4, (projectDefinition) => diagnoseProject(projectDefinition, baseOptions, batchConfig));
|
|
@@ -41469,4 +41758,4 @@ const toJsonReport = (result, options) => buildJsonReport({
|
|
|
41469
41758
|
export { AmbiguousProjectError, NoReactDependencyError, NotADirectoryError, PackageJsonNotFoundError, ProjectNotFoundError, ReactDoctorError, buildJsonReport, buildJsonReportError, clearCaches, defineConfig, diagnose, filterSourceFiles, getDiffInfo, isProjectDiscoveryError, isReactDoctorError, summarizeDiagnostics, toJsonReport };
|
|
41470
41759
|
|
|
41471
41760
|
//# sourceMappingURL=index.js.map
|
|
41472
|
-
//# debugId=
|
|
41761
|
+
//# debugId=62cf4740-7938-5db0-8b80-dc81b60af36a
|