react-doctor 0.5.8-dev.80e3093 → 0.5.8-dev.8232e96
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +575 -410
- package/dist/index.d.ts +12 -0
- package/dist/index.js +265 -227
- package/dist/lsp.js +287 -250
- package/package.json +5 -5
package/dist/lsp.js
CHANGED
|
@@ -19400,6 +19400,15 @@ var JsonReportV1 = class extends Class("JsonReportV1")({
|
|
|
19400
19400
|
ok: Boolean$1,
|
|
19401
19401
|
directory: String$1,
|
|
19402
19402
|
mode: JsonReportMode,
|
|
19403
|
+
/**
|
|
19404
|
+
* True when a `changed` (compare) run was intended but the base couldn't be
|
|
19405
|
+
* resolved — a shallow CI checkout with no merge base, or a failed base/head
|
|
19406
|
+
* lint. The report then lists every finding in the changed files (mode
|
|
19407
|
+
* downgrades to `diff`, the `baseline` block is dropped, the CI gate is
|
|
19408
|
+
* skipped) rather than only the ones the change introduced. Absent on a
|
|
19409
|
+
* successful `baseline` run (`schemaVersion: 2`) and on non-compare scopes.
|
|
19410
|
+
*/
|
|
19411
|
+
baselineDegraded: optional(Boolean$1),
|
|
19403
19412
|
diff: NullOr(JsonReportDiffInfo),
|
|
19404
19413
|
projects: ArraySchema(JsonReportProjectEntry),
|
|
19405
19414
|
diagnostics: ArraySchema(Diagnostic),
|
|
@@ -32816,12 +32825,31 @@ const GENERATED_BUNDLE_FILE_PATTERN = /\.(iife|umd|global|min)\.m?js$/i;
|
|
|
32816
32825
|
const MINIFIED_SNIFF_BYTES = 65536;
|
|
32817
32826
|
const GIT_LS_FILES_MAX_BUFFER_BYTES = 50 * 1024 * 1024;
|
|
32818
32827
|
const IGNORED_DIRECTORIES = new Set([
|
|
32828
|
+
".angular",
|
|
32829
|
+
".astro",
|
|
32830
|
+
".cache",
|
|
32831
|
+
".contentlayer",
|
|
32832
|
+
".direnv",
|
|
32833
|
+
".docusaurus",
|
|
32834
|
+
".expo",
|
|
32835
|
+
".firebase",
|
|
32819
32836
|
".git",
|
|
32837
|
+
".gradle",
|
|
32838
|
+
".hg",
|
|
32820
32839
|
".next",
|
|
32821
32840
|
".nuxt",
|
|
32841
|
+
".nx",
|
|
32822
32842
|
".output",
|
|
32843
|
+
".parcel-cache",
|
|
32844
|
+
".serverless",
|
|
32823
32845
|
".svelte-kit",
|
|
32846
|
+
".svn",
|
|
32847
|
+
".terraform",
|
|
32824
32848
|
".turbo",
|
|
32849
|
+
".venv",
|
|
32850
|
+
".vercel",
|
|
32851
|
+
".wrangler",
|
|
32852
|
+
".yarn",
|
|
32825
32853
|
"build",
|
|
32826
32854
|
"coverage",
|
|
32827
32855
|
"dist",
|
|
@@ -32829,6 +32857,142 @@ const IGNORED_DIRECTORIES = new Set([
|
|
|
32829
32857
|
"out",
|
|
32830
32858
|
"storybook-static"
|
|
32831
32859
|
]);
|
|
32860
|
+
const JSX_FILE_PATTERN = /\.(tsx|jsx)$/;
|
|
32861
|
+
const MILLISECONDS_PER_SECOND = 1e3;
|
|
32862
|
+
const SCORE_API_URL = "https://www.react.doctor/api/score";
|
|
32863
|
+
const FETCH_TIMEOUT_MS = 1e4;
|
|
32864
|
+
const GITHUB_VIEWER_PERMISSION_TIMEOUT_MS = 2e3;
|
|
32865
|
+
const SPAWN_ARGS_MAX_LENGTH_CHARS = 24e3;
|
|
32866
|
+
const SPAWN_ARGS_MAX_LENGTH_CHARS_POSIX = 15e5;
|
|
32867
|
+
const SPAWN_ARGS_MAX_LENGTH_CHARS_DARWIN = 8e5;
|
|
32868
|
+
const NODE_VERSION_PROBE_TIMEOUT_MS = 5e3;
|
|
32869
|
+
const PER_WORKER_MEM_BUDGET_BYTES = 1024 * 1024 * 1024;
|
|
32870
|
+
const DEFAULT_BRANCH_CANDIDATES = ["main", "master"];
|
|
32871
|
+
const ADOPTABLE_LINT_CONFIG_FILENAMES = [".oxlintrc.json", ".eslintrc.json"];
|
|
32872
|
+
const GIT_SHOW_MAX_BUFFER_BYTES = 10 * 1024 * 1024;
|
|
32873
|
+
const ES_TARGET_YEAR_BY_NAME = {
|
|
32874
|
+
es3: 1999,
|
|
32875
|
+
es5: 2009,
|
|
32876
|
+
es6: 2015,
|
|
32877
|
+
es2015: 2015,
|
|
32878
|
+
es2016: 2016,
|
|
32879
|
+
es2017: 2017,
|
|
32880
|
+
es2018: 2018,
|
|
32881
|
+
es2019: 2019,
|
|
32882
|
+
es2020: 2020,
|
|
32883
|
+
es2021: 2021,
|
|
32884
|
+
es2022: 2022,
|
|
32885
|
+
es2023: 2023,
|
|
32886
|
+
es2024: 2024,
|
|
32887
|
+
es2025: 2025,
|
|
32888
|
+
esnext: 9999
|
|
32889
|
+
};
|
|
32890
|
+
/**
|
|
32891
|
+
* tsconfig filenames probed when resolving a project's TypeScript
|
|
32892
|
+
* compiler options — the root config first, then a monorepo base config.
|
|
32893
|
+
*/
|
|
32894
|
+
const TSCONFIG_FILENAMES = ["tsconfig.json", "tsconfig.base.json"];
|
|
32895
|
+
/**
|
|
32896
|
+
* Project-config files that `StagedFiles.materialize` copies into
|
|
32897
|
+
* the temp directory alongside staged sources so oxlint resolves
|
|
32898
|
+
* `tsconfig` / `package.json` / lint configs the same way it would
|
|
32899
|
+
* in the working tree. Hoisted out of the staged-files helper so
|
|
32900
|
+
* the constant lives next to the rest of the IO budget knobs.
|
|
32901
|
+
*/
|
|
32902
|
+
const STAGED_FILES_PROJECT_CONFIG_FILENAMES = [
|
|
32903
|
+
"tsconfig.json",
|
|
32904
|
+
"tsconfig.base.json",
|
|
32905
|
+
"package.json",
|
|
32906
|
+
"doctor.config.ts",
|
|
32907
|
+
"doctor.config.mts",
|
|
32908
|
+
"doctor.config.cts",
|
|
32909
|
+
"doctor.config.js",
|
|
32910
|
+
"doctor.config.mjs",
|
|
32911
|
+
"doctor.config.cjs",
|
|
32912
|
+
"doctor.config.json",
|
|
32913
|
+
"doctor.config.jsonc",
|
|
32914
|
+
"oxlint.json",
|
|
32915
|
+
".oxlintrc.json"
|
|
32916
|
+
];
|
|
32917
|
+
const CONFIG_FINGERPRINT_FILENAMES = [
|
|
32918
|
+
"doctor.config.ts",
|
|
32919
|
+
"doctor.config.mts",
|
|
32920
|
+
"doctor.config.cts",
|
|
32921
|
+
"doctor.config.js",
|
|
32922
|
+
"doctor.config.mjs",
|
|
32923
|
+
"doctor.config.cjs",
|
|
32924
|
+
"doctor.config.json",
|
|
32925
|
+
"doctor.config.jsonc",
|
|
32926
|
+
"react-doctor.config.json",
|
|
32927
|
+
"package.json",
|
|
32928
|
+
"pnpm-workspace.yaml",
|
|
32929
|
+
"tsconfig.json",
|
|
32930
|
+
"tsconfig.base.json",
|
|
32931
|
+
".oxlintrc.json",
|
|
32932
|
+
".eslintrc.json",
|
|
32933
|
+
"pnpm-lock.yaml",
|
|
32934
|
+
"package-lock.json",
|
|
32935
|
+
"yarn.lock",
|
|
32936
|
+
"bun.lock",
|
|
32937
|
+
"bun.lockb",
|
|
32938
|
+
".gitignore"
|
|
32939
|
+
];
|
|
32940
|
+
const OXLINT_OUTPUT_MAX_BYTES = 50 * 1024 * 1024;
|
|
32941
|
+
const OXLINT_SPAWN_TIMEOUT_MS = 6e4;
|
|
32942
|
+
const NODE_COMPILE_CACHE_DIR_NAME = "node-compile-cache";
|
|
32943
|
+
const DEAD_CODE_WORKER_TIMEOUT_MS = 12e4;
|
|
32944
|
+
const OXLINT_SPLIT_TOTAL_BUDGET_MS = 18e4;
|
|
32945
|
+
const DEAD_CODE_PHASE_TIMEOUT_MS = 15e4;
|
|
32946
|
+
const LINT_PHASE_TIMEOUT_MS = 3e5;
|
|
32947
|
+
const SCAN_TOTAL_DEADLINE_MS = 9e5;
|
|
32948
|
+
const DEAD_CODE_WORKER_MAX_OLD_SPACE_MB = 8192;
|
|
32949
|
+
const DEAD_CODE_WORKER_MEM_BUDGET_BYTES = 2 * 1024 * 1024 * 1024;
|
|
32950
|
+
const DEAD_CODE_TIMEOUT_CEILING_MS = 6e5;
|
|
32951
|
+
const DEAD_CODE_PHASE_TIMEOUT_OVER_WORKER_MS = 3e4;
|
|
32952
|
+
const DEAD_CODE_OVERLAP_PARSE_SHARE = .4;
|
|
32953
|
+
const RECOMMENDED_PNPM_MINIMUM_RELEASE_AGE_MINUTES = 10080;
|
|
32954
|
+
const REACT_SERVER_DOM_PACKAGES = [
|
|
32955
|
+
"react-server-dom-webpack",
|
|
32956
|
+
"react-server-dom-parcel",
|
|
32957
|
+
"react-server-dom-turbopack"
|
|
32958
|
+
];
|
|
32959
|
+
const REACT_BLOG_RSC_ADVISORY_URL = "https://react.dev/blog/2025/12/03/critical-security-vulnerability-in-react-server-components";
|
|
32960
|
+
const VERCEL_NEXTJS_SECURITY_RELEASE_URL = "https://vercel.com/changelog/next-js-may-2026-security-release";
|
|
32961
|
+
const DIAGNOSTIC_CATEGORY_BUCKETS = [
|
|
32962
|
+
"Security",
|
|
32963
|
+
"Bugs",
|
|
32964
|
+
"Performance",
|
|
32965
|
+
"Accessibility",
|
|
32966
|
+
"Maintainability"
|
|
32967
|
+
];
|
|
32968
|
+
const APP_ONLY_RULE_KEYS = new Set([
|
|
32969
|
+
"react-hooks-js/static-components",
|
|
32970
|
+
"react-doctor/no-render-prop-children",
|
|
32971
|
+
"react-doctor/prefer-explicit-variants"
|
|
32972
|
+
]);
|
|
32973
|
+
const COMPILER_CLEANUP_BUCKET = "compiler-cleanup";
|
|
32974
|
+
const COMPILER_CLEANUP_RULE_KEYS = new Set(["react-doctor/react-compiler-no-manual-memoization"]);
|
|
32975
|
+
const ROOT_CAUSE_GROUPABLE_RULE_KEYS = new Set([
|
|
32976
|
+
"react-doctor/no-derived-state",
|
|
32977
|
+
"react-doctor/no-derived-state-effect",
|
|
32978
|
+
"react-doctor/no-derived-useState",
|
|
32979
|
+
"react-doctor/no-adjust-state-on-prop-change",
|
|
32980
|
+
"react-doctor/no-reset-all-state-on-prop-change"
|
|
32981
|
+
]);
|
|
32982
|
+
const MAX_GLOB_PATTERN_LENGTH_CHARS = 1024;
|
|
32983
|
+
const CONFIG_CACHE_TTL_MS = 300 * 1e3;
|
|
32984
|
+
const SOCKET_FREE_PURL_API_BASE = "https://firewall-api.socket.dev/purl";
|
|
32985
|
+
const SOCKET_PACKAGE_PAGE_BASE = "https://socket.dev/npm/package";
|
|
32986
|
+
const SOCKET_FREE_USER_AGENT = "react-doctor-supply-chain";
|
|
32987
|
+
const FILE_LINT_CACHE_FILENAME = "file-lint-cache.json";
|
|
32988
|
+
const FILE_LINT_CACHE_MAX_FILE_COUNT = 5e4;
|
|
32989
|
+
const SUPPLY_CHAIN_PLUGIN = "socket";
|
|
32990
|
+
const SUPPLY_CHAIN_RULE = "low-supply-chain-score";
|
|
32991
|
+
const SUPPLY_CHAIN_CATEGORY = "Security";
|
|
32992
|
+
const SUPPLY_CHAIN_OVERLAP_TIMEOUT_MS = 9e4;
|
|
32993
|
+
const SUPPLY_CHAIN_CACHE_SUBDIR = "supply-chain";
|
|
32994
|
+
const SUPPLY_CHAIN_IGNORED_PACKAGES = new Set(["next"]);
|
|
32995
|
+
const hasIgnoredPathSegment = (relativePath) => relativePath.split(/[/\\]/).slice(0, -1).some((segment) => IGNORED_DIRECTORIES.has(segment));
|
|
32832
32996
|
const isLintableSourceFile = (filePath) => SOURCE_FILE_PATTERN.test(filePath) && !GENERATED_BUNDLE_FILE_PATTERN.test(filePath);
|
|
32833
32997
|
const isMinifiedSource = (absolutePath) => {
|
|
32834
32998
|
let fileDescriptor;
|
|
@@ -32885,23 +33049,38 @@ const readDirectoryEntries = (directoryPath) => {
|
|
|
32885
33049
|
throw error;
|
|
32886
33050
|
}
|
|
32887
33051
|
};
|
|
32888
|
-
|
|
32889
|
-
let count = 0;
|
|
33052
|
+
function* walkSourceTreeFiles(rootDirectory) {
|
|
32890
33053
|
const stack = [rootDirectory];
|
|
32891
33054
|
while (stack.length > 0) {
|
|
32892
33055
|
const currentDirectory = stack.pop();
|
|
32893
|
-
|
|
32894
|
-
for (const entry of
|
|
33056
|
+
if (currentDirectory === void 0) continue;
|
|
33057
|
+
for (const entry of readDirectoryEntries(currentDirectory)) {
|
|
33058
|
+
const absolutePath = Path.join(currentDirectory, entry.name);
|
|
32895
33059
|
if (entry.isDirectory()) {
|
|
32896
|
-
if (!
|
|
33060
|
+
if (!IGNORED_DIRECTORIES.has(entry.name)) stack.push(absolutePath);
|
|
32897
33061
|
continue;
|
|
32898
33062
|
}
|
|
32899
|
-
if (entry.isFile()
|
|
33063
|
+
if (entry.isFile()) yield {
|
|
33064
|
+
absolutePath,
|
|
33065
|
+
name: entry.name
|
|
33066
|
+
};
|
|
32900
33067
|
}
|
|
32901
33068
|
}
|
|
32902
|
-
|
|
33069
|
+
}
|
|
33070
|
+
const collectSizedSourceFiles = (rootDirectory, relativePaths) => {
|
|
33071
|
+
const entries = [];
|
|
33072
|
+
for (const relativePath of relativePaths) {
|
|
33073
|
+
const absolutePath = Path.resolve(rootDirectory, relativePath);
|
|
33074
|
+
const sizeBytes = statSourceFileSize(absolutePath);
|
|
33075
|
+
if (isLargeMinifiedFile(absolutePath, sizeBytes)) continue;
|
|
33076
|
+
entries.push({
|
|
33077
|
+
path: relativePath,
|
|
33078
|
+
sizeBytes: sizeBytes ?? 0
|
|
33079
|
+
});
|
|
33080
|
+
}
|
|
33081
|
+
return entries;
|
|
32903
33082
|
};
|
|
32904
|
-
const
|
|
33083
|
+
const listSourceFilesViaGit = (rootDirectory) => {
|
|
32905
33084
|
const result = spawnSync("git", [
|
|
32906
33085
|
"ls-files",
|
|
32907
33086
|
"-z",
|
|
@@ -32914,9 +33093,16 @@ const countSourceFilesViaGit = (rootDirectory) => {
|
|
|
32914
33093
|
maxBuffer: GIT_LS_FILES_MAX_BUFFER_BYTES
|
|
32915
33094
|
});
|
|
32916
33095
|
if (result.error || result.status !== 0) return null;
|
|
32917
|
-
return result.stdout.split("\0").filter((filePath) => filePath.length > 0 && isLintableSourceFile(filePath) && !
|
|
33096
|
+
return result.stdout.split("\0").filter((filePath) => filePath.length > 0 && isLintableSourceFile(filePath) && !hasIgnoredPathSegment(filePath));
|
|
33097
|
+
};
|
|
33098
|
+
const listSourceFilesViaFilesystem = (rootDirectory) => {
|
|
33099
|
+
const filePaths = [];
|
|
33100
|
+
for (const { absolutePath, name } of walkSourceTreeFiles(rootDirectory)) if (isLintableSourceFile(name)) filePaths.push(Path.relative(rootDirectory, absolutePath).replace(/\\/g, "/"));
|
|
33101
|
+
return filePaths;
|
|
32918
33102
|
};
|
|
32919
|
-
const
|
|
33103
|
+
const listSourceFilesWithSize = (rootDirectory) => collectSizedSourceFiles(rootDirectory, (listSourceFilesViaGit(rootDirectory) ?? listSourceFilesViaFilesystem(rootDirectory)).sort());
|
|
33104
|
+
const listSourceFiles = (rootDirectory) => listSourceFilesWithSize(rootDirectory).map((entry) => entry.path);
|
|
33105
|
+
const countSourceFiles = (rootDirectory) => listSourceFilesWithSize(rootDirectory).length;
|
|
32920
33106
|
const cachedPackageJsons = /* @__PURE__ */ new Map();
|
|
32921
33107
|
const clearPackageJsonCache = () => {
|
|
32922
33108
|
cachedPackageJsons.clear();
|
|
@@ -33725,141 +33911,6 @@ const resolveEffectiveReactMajor = (reactVersion, packageJson) => {
|
|
|
33725
33911
|
if (peerFloor === null) return hasUpperBoundOnlyPeerRange(peerReactRange) ? null : installedReactMajor;
|
|
33726
33912
|
return installedReactMajor !== null ? Math.min(installedReactMajor, peerFloor) : peerFloor;
|
|
33727
33913
|
};
|
|
33728
|
-
const JSX_FILE_PATTERN = /\.(tsx|jsx)$/;
|
|
33729
|
-
const MILLISECONDS_PER_SECOND = 1e3;
|
|
33730
|
-
const SCORE_API_URL = "https://www.react.doctor/api/score";
|
|
33731
|
-
const FETCH_TIMEOUT_MS = 1e4;
|
|
33732
|
-
const GITHUB_VIEWER_PERMISSION_TIMEOUT_MS = 2e3;
|
|
33733
|
-
const SPAWN_ARGS_MAX_LENGTH_CHARS = 24e3;
|
|
33734
|
-
const SPAWN_ARGS_MAX_LENGTH_CHARS_POSIX = 15e5;
|
|
33735
|
-
const SPAWN_ARGS_MAX_LENGTH_CHARS_DARWIN = 8e5;
|
|
33736
|
-
const NODE_VERSION_PROBE_TIMEOUT_MS = 5e3;
|
|
33737
|
-
const PER_WORKER_MEM_BUDGET_BYTES = 1024 * 1024 * 1024;
|
|
33738
|
-
const DEFAULT_BRANCH_CANDIDATES = ["main", "master"];
|
|
33739
|
-
const ADOPTABLE_LINT_CONFIG_FILENAMES = [".oxlintrc.json", ".eslintrc.json"];
|
|
33740
|
-
const GIT_SHOW_MAX_BUFFER_BYTES = 10 * 1024 * 1024;
|
|
33741
|
-
const ES_TARGET_YEAR_BY_NAME = {
|
|
33742
|
-
es3: 1999,
|
|
33743
|
-
es5: 2009,
|
|
33744
|
-
es6: 2015,
|
|
33745
|
-
es2015: 2015,
|
|
33746
|
-
es2016: 2016,
|
|
33747
|
-
es2017: 2017,
|
|
33748
|
-
es2018: 2018,
|
|
33749
|
-
es2019: 2019,
|
|
33750
|
-
es2020: 2020,
|
|
33751
|
-
es2021: 2021,
|
|
33752
|
-
es2022: 2022,
|
|
33753
|
-
es2023: 2023,
|
|
33754
|
-
es2024: 2024,
|
|
33755
|
-
es2025: 2025,
|
|
33756
|
-
esnext: 9999
|
|
33757
|
-
};
|
|
33758
|
-
/**
|
|
33759
|
-
* tsconfig filenames probed when resolving a project's TypeScript
|
|
33760
|
-
* compiler options — the root config first, then a monorepo base config.
|
|
33761
|
-
*/
|
|
33762
|
-
const TSCONFIG_FILENAMES = ["tsconfig.json", "tsconfig.base.json"];
|
|
33763
|
-
/**
|
|
33764
|
-
* Project-config files that `StagedFiles.materialize` copies into
|
|
33765
|
-
* the temp directory alongside staged sources so oxlint resolves
|
|
33766
|
-
* `tsconfig` / `package.json` / lint configs the same way it would
|
|
33767
|
-
* in the working tree. Hoisted out of the staged-files helper so
|
|
33768
|
-
* the constant lives next to the rest of the IO budget knobs.
|
|
33769
|
-
*/
|
|
33770
|
-
const STAGED_FILES_PROJECT_CONFIG_FILENAMES = [
|
|
33771
|
-
"tsconfig.json",
|
|
33772
|
-
"tsconfig.base.json",
|
|
33773
|
-
"package.json",
|
|
33774
|
-
"doctor.config.ts",
|
|
33775
|
-
"doctor.config.mts",
|
|
33776
|
-
"doctor.config.cts",
|
|
33777
|
-
"doctor.config.js",
|
|
33778
|
-
"doctor.config.mjs",
|
|
33779
|
-
"doctor.config.cjs",
|
|
33780
|
-
"doctor.config.json",
|
|
33781
|
-
"doctor.config.jsonc",
|
|
33782
|
-
"oxlint.json",
|
|
33783
|
-
".oxlintrc.json"
|
|
33784
|
-
];
|
|
33785
|
-
const CONFIG_FINGERPRINT_FILENAMES = [
|
|
33786
|
-
"doctor.config.ts",
|
|
33787
|
-
"doctor.config.mts",
|
|
33788
|
-
"doctor.config.cts",
|
|
33789
|
-
"doctor.config.js",
|
|
33790
|
-
"doctor.config.mjs",
|
|
33791
|
-
"doctor.config.cjs",
|
|
33792
|
-
"doctor.config.json",
|
|
33793
|
-
"doctor.config.jsonc",
|
|
33794
|
-
"react-doctor.config.json",
|
|
33795
|
-
"package.json",
|
|
33796
|
-
"pnpm-workspace.yaml",
|
|
33797
|
-
"tsconfig.json",
|
|
33798
|
-
"tsconfig.base.json",
|
|
33799
|
-
".oxlintrc.json",
|
|
33800
|
-
".eslintrc.json",
|
|
33801
|
-
"pnpm-lock.yaml",
|
|
33802
|
-
"package-lock.json",
|
|
33803
|
-
"yarn.lock",
|
|
33804
|
-
"bun.lock",
|
|
33805
|
-
"bun.lockb",
|
|
33806
|
-
".gitignore"
|
|
33807
|
-
];
|
|
33808
|
-
const OXLINT_OUTPUT_MAX_BYTES = 50 * 1024 * 1024;
|
|
33809
|
-
const OXLINT_SPAWN_TIMEOUT_MS = 6e4;
|
|
33810
|
-
const NODE_COMPILE_CACHE_DIR_NAME = "node-compile-cache";
|
|
33811
|
-
const DEAD_CODE_WORKER_TIMEOUT_MS = 12e4;
|
|
33812
|
-
const OXLINT_SPLIT_TOTAL_BUDGET_MS = 18e4;
|
|
33813
|
-
const DEAD_CODE_PHASE_TIMEOUT_MS = 15e4;
|
|
33814
|
-
const LINT_PHASE_TIMEOUT_MS = 3e5;
|
|
33815
|
-
const SCAN_TOTAL_DEADLINE_MS = 9e5;
|
|
33816
|
-
const DEAD_CODE_WORKER_MAX_OLD_SPACE_MB = 8192;
|
|
33817
|
-
const DEAD_CODE_WORKER_MEM_BUDGET_BYTES = 2 * 1024 * 1024 * 1024;
|
|
33818
|
-
const DEAD_CODE_TIMEOUT_CEILING_MS = 6e5;
|
|
33819
|
-
const DEAD_CODE_PHASE_TIMEOUT_OVER_WORKER_MS = 3e4;
|
|
33820
|
-
const DEAD_CODE_OVERLAP_PARSE_SHARE = .4;
|
|
33821
|
-
const RECOMMENDED_PNPM_MINIMUM_RELEASE_AGE_MINUTES = 10080;
|
|
33822
|
-
const REACT_SERVER_DOM_PACKAGES = [
|
|
33823
|
-
"react-server-dom-webpack",
|
|
33824
|
-
"react-server-dom-parcel",
|
|
33825
|
-
"react-server-dom-turbopack"
|
|
33826
|
-
];
|
|
33827
|
-
const REACT_BLOG_RSC_ADVISORY_URL = "https://react.dev/blog/2025/12/03/critical-security-vulnerability-in-react-server-components";
|
|
33828
|
-
const VERCEL_NEXTJS_SECURITY_RELEASE_URL = "https://vercel.com/changelog/next-js-may-2026-security-release";
|
|
33829
|
-
const DIAGNOSTIC_CATEGORY_BUCKETS = [
|
|
33830
|
-
"Security",
|
|
33831
|
-
"Bugs",
|
|
33832
|
-
"Performance",
|
|
33833
|
-
"Accessibility",
|
|
33834
|
-
"Maintainability"
|
|
33835
|
-
];
|
|
33836
|
-
const APP_ONLY_RULE_KEYS = new Set([
|
|
33837
|
-
"react-hooks-js/static-components",
|
|
33838
|
-
"react-doctor/no-render-prop-children",
|
|
33839
|
-
"react-doctor/prefer-explicit-variants"
|
|
33840
|
-
]);
|
|
33841
|
-
const COMPILER_CLEANUP_BUCKET = "compiler-cleanup";
|
|
33842
|
-
const COMPILER_CLEANUP_RULE_KEYS = new Set(["react-doctor/react-compiler-no-manual-memoization"]);
|
|
33843
|
-
const ROOT_CAUSE_GROUPABLE_RULE_KEYS = new Set([
|
|
33844
|
-
"react-doctor/no-derived-state",
|
|
33845
|
-
"react-doctor/no-derived-state-effect",
|
|
33846
|
-
"react-doctor/no-derived-useState",
|
|
33847
|
-
"react-doctor/no-adjust-state-on-prop-change",
|
|
33848
|
-
"react-doctor/no-reset-all-state-on-prop-change"
|
|
33849
|
-
]);
|
|
33850
|
-
const MAX_GLOB_PATTERN_LENGTH_CHARS = 1024;
|
|
33851
|
-
const CONFIG_CACHE_TTL_MS = 300 * 1e3;
|
|
33852
|
-
const SOCKET_FREE_PURL_API_BASE = "https://firewall-api.socket.dev/purl";
|
|
33853
|
-
const SOCKET_PACKAGE_PAGE_BASE = "https://socket.dev/npm/package";
|
|
33854
|
-
const SOCKET_FREE_USER_AGENT = "react-doctor-supply-chain";
|
|
33855
|
-
const FILE_LINT_CACHE_FILENAME = "file-lint-cache.json";
|
|
33856
|
-
const FILE_LINT_CACHE_MAX_FILE_COUNT = 5e4;
|
|
33857
|
-
const SUPPLY_CHAIN_PLUGIN = "socket";
|
|
33858
|
-
const SUPPLY_CHAIN_RULE = "low-supply-chain-score";
|
|
33859
|
-
const SUPPLY_CHAIN_CATEGORY = "Security";
|
|
33860
|
-
const SUPPLY_CHAIN_OVERLAP_TIMEOUT_MS = 9e4;
|
|
33861
|
-
const SUPPLY_CHAIN_CACHE_SUBDIR = "supply-chain";
|
|
33862
|
-
const SUPPLY_CHAIN_IGNORED_PACKAGES = new Set(["next"]);
|
|
33863
33914
|
const TSCONFIG_FILENAME = "tsconfig.json";
|
|
33864
33915
|
const isRelativeExtendsValue = (extendsValue) => extendsValue.startsWith("./") || extendsValue.startsWith("../") || Path.isAbsolute(extendsValue);
|
|
33865
33916
|
const ensureJsonExtension = (filePath) => Path.extname(filePath) === "" ? `${filePath}.json` : filePath;
|
|
@@ -36733,14 +36784,17 @@ const checkReactServerComponentsAdvisory = (scanDirectory, project) => {
|
|
|
36733
36784
|
return diagnostics;
|
|
36734
36785
|
};
|
|
36735
36786
|
const REDUCED_MOTION_GREP_PATTERN = "prefers-reduced-motion|useReducedMotion|MotionConfig|reducedMotion";
|
|
36736
|
-
const
|
|
36737
|
-
|
|
36738
|
-
"
|
|
36739
|
-
"
|
|
36740
|
-
"
|
|
36741
|
-
"
|
|
36742
|
-
"
|
|
36743
|
-
|
|
36787
|
+
const REDUCED_MOTION_PATTERN = new RegExp(REDUCED_MOTION_GREP_PATTERN);
|
|
36788
|
+
const REDUCED_MOTION_FILE_EXTENSIONS = new Set([
|
|
36789
|
+
".ts",
|
|
36790
|
+
".tsx",
|
|
36791
|
+
".js",
|
|
36792
|
+
".jsx",
|
|
36793
|
+
".css",
|
|
36794
|
+
".scss"
|
|
36795
|
+
]);
|
|
36796
|
+
const REDUCED_MOTION_FILE_GLOBS = [...REDUCED_MOTION_FILE_EXTENSIONS].map((extension) => `*${extension}`);
|
|
36797
|
+
const GIT_GREP_NO_MATCH_STATUS = 1;
|
|
36744
36798
|
const MISSING_REDUCED_MOTION_DIAGNOSTIC = {
|
|
36745
36799
|
filePath: "package.json",
|
|
36746
36800
|
plugin: "react-doctor",
|
|
@@ -36752,6 +36806,19 @@ const MISSING_REDUCED_MOTION_DIAGNOSTIC = {
|
|
|
36752
36806
|
column: 0,
|
|
36753
36807
|
category: "Accessibility"
|
|
36754
36808
|
};
|
|
36809
|
+
const hasReducedMotionHandlingViaFilesystem = (rootDirectory) => {
|
|
36810
|
+
for (const { absolutePath, name } of walkSourceTreeFiles(rootDirectory)) {
|
|
36811
|
+
if (!REDUCED_MOTION_FILE_EXTENSIONS.has(Path.extname(name))) continue;
|
|
36812
|
+
let content;
|
|
36813
|
+
try {
|
|
36814
|
+
content = NFS.readFileSync(absolutePath, "utf-8");
|
|
36815
|
+
} catch {
|
|
36816
|
+
continue;
|
|
36817
|
+
}
|
|
36818
|
+
if (REDUCED_MOTION_PATTERN.test(content)) return true;
|
|
36819
|
+
}
|
|
36820
|
+
return false;
|
|
36821
|
+
};
|
|
36755
36822
|
const checkReducedMotion = (rootDirectory) => {
|
|
36756
36823
|
const packageJsonPath = Path.join(rootDirectory, "package.json");
|
|
36757
36824
|
if (!isFile(packageJsonPath)) return [];
|
|
@@ -36770,22 +36837,21 @@ const checkReducedMotion = (rootDirectory) => {
|
|
|
36770
36837
|
const result = spawnSync("git", [
|
|
36771
36838
|
"grep",
|
|
36772
36839
|
"--untracked",
|
|
36773
|
-
"-
|
|
36774
|
-
"-E",
|
|
36840
|
+
"-lE",
|
|
36775
36841
|
REDUCED_MOTION_GREP_PATTERN,
|
|
36776
36842
|
"--",
|
|
36777
36843
|
...REDUCED_MOTION_FILE_GLOBS
|
|
36778
36844
|
], {
|
|
36779
36845
|
cwd: rootDirectory,
|
|
36846
|
+
encoding: "utf-8",
|
|
36780
36847
|
stdio: [
|
|
36781
36848
|
"ignore",
|
|
36782
36849
|
"pipe",
|
|
36783
36850
|
"pipe"
|
|
36784
36851
|
]
|
|
36785
36852
|
});
|
|
36786
|
-
if (result.error) return [MISSING_REDUCED_MOTION_DIAGNOSTIC];
|
|
36787
|
-
|
|
36788
|
-
return [MISSING_REDUCED_MOTION_DIAGNOSTIC];
|
|
36853
|
+
if (!(!result.error && (result.status === 0 || result.status === GIT_GREP_NO_MATCH_STATUS))) return hasReducedMotionHandlingViaFilesystem(rootDirectory) ? [] : [MISSING_REDUCED_MOTION_DIAGNOSTIC];
|
|
36854
|
+
return result.status === 0 && result.stdout.split("\n").some((filePath) => filePath.length > 0 && !hasIgnoredPathSegment(filePath)) ? [] : [MISSING_REDUCED_MOTION_DIAGNOSTIC];
|
|
36789
36855
|
};
|
|
36790
36856
|
const buildSecurityScanDiagnostic = (finding, entry, relativePath) => ({
|
|
36791
36857
|
filePath: relativePath,
|
|
@@ -37681,6 +37747,7 @@ const isDiagnosticOnSurface = (diagnostic, surface, config) => {
|
|
|
37681
37747
|
return true;
|
|
37682
37748
|
};
|
|
37683
37749
|
const filterDiagnosticsForSurface = (diagnostics, surface, config) => diagnostics.filter((diagnostic) => isDiagnosticOnSurface(diagnostic, surface, config));
|
|
37750
|
+
const remainingDeadlineBudgetMs = (deadlineEpochMs) => Math.max(deadlineEpochMs - Date.now(), 0);
|
|
37684
37751
|
/**
|
|
37685
37752
|
* Budget for the dead-code phase, scaled to the work. deslop's graph build is
|
|
37686
37753
|
* CPU-bound and roughly linear in file count, so a fixed 120s cap is too tight
|
|
@@ -37699,53 +37766,6 @@ const resolveDeadCodeTimeout = (input) => {
|
|
|
37699
37766
|
phaseTimeoutMs: workerTimeoutMs + DEAD_CODE_PHASE_TIMEOUT_OVER_WORKER_MS
|
|
37700
37767
|
};
|
|
37701
37768
|
};
|
|
37702
|
-
const collectSizedSourceFiles = (rootDirectory, relativePaths) => {
|
|
37703
|
-
const entries = [];
|
|
37704
|
-
for (const relativePath of relativePaths) {
|
|
37705
|
-
const absolutePath = Path.resolve(rootDirectory, relativePath);
|
|
37706
|
-
const sizeBytes = statSourceFileSize(absolutePath);
|
|
37707
|
-
if (isLargeMinifiedFile(absolutePath, sizeBytes)) continue;
|
|
37708
|
-
entries.push({
|
|
37709
|
-
path: relativePath,
|
|
37710
|
-
sizeBytes: sizeBytes ?? 0
|
|
37711
|
-
});
|
|
37712
|
-
}
|
|
37713
|
-
return entries;
|
|
37714
|
-
};
|
|
37715
|
-
const listSourceFilesViaGit = (rootDirectory) => {
|
|
37716
|
-
const result = spawnSync("git", [
|
|
37717
|
-
"ls-files",
|
|
37718
|
-
"-z",
|
|
37719
|
-
"--cached",
|
|
37720
|
-
"--others",
|
|
37721
|
-
"--exclude-standard"
|
|
37722
|
-
], {
|
|
37723
|
-
cwd: rootDirectory,
|
|
37724
|
-
encoding: "utf-8",
|
|
37725
|
-
maxBuffer: GIT_LS_FILES_MAX_BUFFER_BYTES
|
|
37726
|
-
});
|
|
37727
|
-
if (result.error || result.status !== 0) return null;
|
|
37728
|
-
return result.stdout.split("\0").filter((filePath) => filePath.length > 0 && isLintableSourceFile(filePath));
|
|
37729
|
-
};
|
|
37730
|
-
const listSourceFilesViaFilesystem = (rootDirectory) => {
|
|
37731
|
-
const filePaths = [];
|
|
37732
|
-
const stack = [rootDirectory];
|
|
37733
|
-
while (stack.length > 0) {
|
|
37734
|
-
const currentDirectory = stack.pop();
|
|
37735
|
-
const entries = readDirectoryEntries(currentDirectory);
|
|
37736
|
-
for (const entry of entries) {
|
|
37737
|
-
const absolutePath = Path.join(currentDirectory, entry.name);
|
|
37738
|
-
if (entry.isDirectory()) {
|
|
37739
|
-
if (!entry.name.startsWith(".") && !IGNORED_DIRECTORIES.has(entry.name)) stack.push(absolutePath);
|
|
37740
|
-
continue;
|
|
37741
|
-
}
|
|
37742
|
-
if (entry.isFile() && isLintableSourceFile(entry.name)) filePaths.push(Path.relative(rootDirectory, absolutePath).replace(/\\/g, "/"));
|
|
37743
|
-
}
|
|
37744
|
-
}
|
|
37745
|
-
return filePaths;
|
|
37746
|
-
};
|
|
37747
|
-
const listSourceFilesWithSize = (rootDirectory) => collectSizedSourceFiles(rootDirectory, listSourceFilesViaGit(rootDirectory) ?? listSourceFilesViaFilesystem(rootDirectory));
|
|
37748
|
-
const listSourceFiles = (rootDirectory) => listSourceFilesWithSize(rootDirectory).map((entry) => entry.path);
|
|
37749
37769
|
const resolveLintIncludePaths = (rootDirectory, userConfig, project) => {
|
|
37750
37770
|
if (!Array.isArray(userConfig?.ignore?.files) || userConfig.ignore.files.length === 0) return;
|
|
37751
37771
|
const ignoredPatterns = compileIgnoredFilePatterns(userConfig);
|
|
@@ -38442,12 +38462,12 @@ const findFilesWithDisableDirectivesViaGit = async (rootDirectory, includePaths)
|
|
|
38442
38462
|
return null;
|
|
38443
38463
|
}
|
|
38444
38464
|
if (grepResult === null) return null;
|
|
38445
|
-
return grepResult.stdout.split("\n").filter((filePath) => filePath.length > 0 && isLintableSourceFile(filePath));
|
|
38465
|
+
return grepResult.stdout.split("\n").filter((filePath) => filePath.length > 0 && isLintableSourceFile(filePath) && !hasIgnoredPathSegment(filePath));
|
|
38446
38466
|
};
|
|
38447
38467
|
const findFilesWithDisableDirectivesViaFilesystem = (rootDirectory, includePaths) => {
|
|
38448
38468
|
const matches = [];
|
|
38449
38469
|
const checkFile = (relativePath) => {
|
|
38450
|
-
if (!isLintableSourceFile(relativePath)) return;
|
|
38470
|
+
if (!isLintableSourceFile(relativePath) || hasIgnoredPathSegment(relativePath)) return;
|
|
38451
38471
|
const absolutePath = Path.join(rootDirectory, relativePath);
|
|
38452
38472
|
let content;
|
|
38453
38473
|
try {
|
|
@@ -38461,22 +38481,7 @@ const findFilesWithDisableDirectivesViaFilesystem = (rootDirectory, includePaths
|
|
|
38461
38481
|
for (const candidate of includePaths) checkFile(candidate);
|
|
38462
38482
|
return matches;
|
|
38463
38483
|
}
|
|
38464
|
-
const
|
|
38465
|
-
while (stack.length > 0) {
|
|
38466
|
-
const current = stack.pop();
|
|
38467
|
-
if (current === void 0) continue;
|
|
38468
|
-
const entries = readDirectoryEntries(current);
|
|
38469
|
-
for (const entry of entries) {
|
|
38470
|
-
if (entry.isDirectory()) {
|
|
38471
|
-
if (entry.name.startsWith(".") || IGNORED_DIRECTORIES.has(entry.name)) continue;
|
|
38472
|
-
stack.push(Path.join(current, entry.name));
|
|
38473
|
-
continue;
|
|
38474
|
-
}
|
|
38475
|
-
if (!entry.isFile()) continue;
|
|
38476
|
-
const absolute = Path.join(current, entry.name);
|
|
38477
|
-
checkFile(Path.relative(rootDirectory, absolute));
|
|
38478
|
-
}
|
|
38479
|
-
}
|
|
38484
|
+
for (const { absolutePath } of walkSourceTreeFiles(rootDirectory)) checkFile(Path.relative(rootDirectory, absolutePath));
|
|
38480
38485
|
return matches;
|
|
38481
38486
|
};
|
|
38482
38487
|
const findFilesWithDisableDirectives = async (rootDirectory, includePaths) => await findFilesWithDisableDirectivesViaGit(rootDirectory, includePaths) ?? findFilesWithDisableDirectivesViaFilesystem(rootDirectory, includePaths);
|
|
@@ -38693,12 +38698,12 @@ const createOxlintConfig = ({ pluginPath, project, customRulesOnly = false, exte
|
|
|
38693
38698
|
if (customRulesOnly && registryEntry.originallyExternal) continue;
|
|
38694
38699
|
if (rule.framework !== "global" && !rule.requires) continue;
|
|
38695
38700
|
if (!shouldEnableRule(rule.requires, rule.tags, capabilities, ignoredTags, rule.disabledBy)) continue;
|
|
38696
|
-
const
|
|
38701
|
+
const explicitRuleOverride = resolveRuleSeverityOverride({ ruleKey: registryEntry.key }, severityControls);
|
|
38702
|
+
if (rule.defaultEnabled === false && explicitRuleOverride === void 0) continue;
|
|
38703
|
+
const severity = resolveRuleSeverityOverride({
|
|
38697
38704
|
ruleKey: registryEntry.key,
|
|
38698
38705
|
category: rule.category
|
|
38699
|
-
}, severityControls);
|
|
38700
|
-
if (rule.defaultEnabled === false && explicitSeverity === void 0) continue;
|
|
38701
|
-
const severity = explicitSeverity ?? resolveCompilerCleanupBucketSeverity(registryEntry.key, severityControls) ?? rule.severity;
|
|
38706
|
+
}, severityControls) ?? resolveCompilerCleanupBucketSeverity(registryEntry.key, severityControls) ?? rule.severity;
|
|
38702
38707
|
if (severity === "off") continue;
|
|
38703
38708
|
enabledReactDoctorRules[registryEntry.key] = severity;
|
|
38704
38709
|
}
|
|
@@ -39680,21 +39685,33 @@ const isParallelismRelatedSpawnError = (error) => {
|
|
|
39680
39685
|
* loop with a slimmer config in that case.
|
|
39681
39686
|
*/
|
|
39682
39687
|
const spawnLintBatches = async (input) => {
|
|
39683
|
-
const { baseArgs, fileBatches, rootDirectory, nodeBinaryPath, project, onPartialFailure, onFileProgress, spawnTimeoutMs, outputMaxBytes, splitTotalBudgetMs = OXLINT_SPLIT_TOTAL_BUDGET_MS, splitMaxDepth = 8, signal } = input;
|
|
39688
|
+
const { baseArgs, fileBatches, rootDirectory, nodeBinaryPath, project, onPartialFailure, onFileProgress, spawnTimeoutMs, outputMaxBytes, splitTotalBudgetMs = OXLINT_SPLIT_TOTAL_BUDGET_MS, splitMaxDepth = 8, signal, deadlineEpochMs } = input;
|
|
39684
39689
|
const requestedConcurrency = resolveScanConcurrency(input.concurrency ?? 1);
|
|
39685
39690
|
const totalFileCount = fileBatches.reduce((sum, batch) => sum + batch.length, 0);
|
|
39686
39691
|
const runBatchPass = async (concurrency) => {
|
|
39687
39692
|
const allDiagnostics = [];
|
|
39688
39693
|
const droppedFiles = [];
|
|
39694
|
+
const deadlineSkippedFiles = [];
|
|
39695
|
+
const isPastDeadline = () => deadlineEpochMs !== void 0 && remainingDeadlineBudgetMs(deadlineEpochMs) === 0;
|
|
39689
39696
|
let firstDropReason = null;
|
|
39690
|
-
const spawnLintBatch = async (batch, depth,
|
|
39697
|
+
const spawnLintBatch = async (batch, depth, batchState) => {
|
|
39698
|
+
if (isPastDeadline()) {
|
|
39699
|
+
deadlineSkippedFiles.push(...batch);
|
|
39700
|
+
batchState.deadlineSkippedFileCount += batch.length;
|
|
39701
|
+
return [];
|
|
39702
|
+
}
|
|
39691
39703
|
const batchArgs = [...baseArgs, ...batch];
|
|
39692
39704
|
try {
|
|
39693
39705
|
return parseOxlintOutput(await spawnOxlint(batchArgs, rootDirectory, nodeBinaryPath, spawnTimeoutMs, outputMaxBytes, signal), project, rootDirectory);
|
|
39694
39706
|
} catch (error) {
|
|
39695
39707
|
if (!isSplittableReactDoctorError(error)) throw error;
|
|
39696
|
-
|
|
39697
|
-
|
|
39708
|
+
if (isPastDeadline()) {
|
|
39709
|
+
deadlineSkippedFiles.push(...batch);
|
|
39710
|
+
batchState.deadlineSkippedFileCount += batch.length;
|
|
39711
|
+
return [];
|
|
39712
|
+
}
|
|
39713
|
+
batchState.deadlineMs ??= Date.now() + splitTotalBudgetMs;
|
|
39714
|
+
const isBudgetElapsed = Date.now() >= batchState.deadlineMs;
|
|
39698
39715
|
const isDepthCapReached = depth >= splitMaxDepth;
|
|
39699
39716
|
if (batch.length <= 1 || isBudgetElapsed || isDepthCapReached) {
|
|
39700
39717
|
droppedFiles.push(...batch);
|
|
@@ -39707,7 +39724,7 @@ const spawnLintBatches = async (input) => {
|
|
|
39707
39724
|
return [];
|
|
39708
39725
|
}
|
|
39709
39726
|
const splitIndex = Math.ceil(batch.length / 2);
|
|
39710
|
-
return [...await spawnLintBatch(batch.slice(0, splitIndex), depth + 1,
|
|
39727
|
+
return [...await spawnLintBatch(batch.slice(0, splitIndex), depth + 1, batchState), ...await spawnLintBatch(batch.slice(splitIndex), depth + 1, batchState)];
|
|
39711
39728
|
}
|
|
39712
39729
|
};
|
|
39713
39730
|
let startedFileCount = 0;
|
|
@@ -39723,9 +39740,17 @@ const spawnLintBatches = async (input) => {
|
|
|
39723
39740
|
progressTimer?.unref?.();
|
|
39724
39741
|
try {
|
|
39725
39742
|
const batchResults = await mapWithConcurrency(fileBatches, concurrency, async (batch) => {
|
|
39743
|
+
if (isPastDeadline()) {
|
|
39744
|
+
deadlineSkippedFiles.push(...batch);
|
|
39745
|
+
return [];
|
|
39746
|
+
}
|
|
39726
39747
|
startedFileCount += batch.length;
|
|
39727
|
-
const
|
|
39728
|
-
|
|
39748
|
+
const batchState = {
|
|
39749
|
+
deadlineMs: null,
|
|
39750
|
+
deadlineSkippedFileCount: 0
|
|
39751
|
+
};
|
|
39752
|
+
const batchDiagnostics = await spawnLintBatch(batch, 0, batchState);
|
|
39753
|
+
scannedFileCount += batch.length - batchState.deadlineSkippedFileCount;
|
|
39729
39754
|
if (onFileProgress) {
|
|
39730
39755
|
displayedFileCount = Math.min(Math.max(displayedFileCount, scannedFileCount), totalFileCount);
|
|
39731
39756
|
onFileProgress(displayedFileCount, totalFileCount);
|
|
@@ -39736,12 +39761,13 @@ const spawnLintBatches = async (input) => {
|
|
|
39736
39761
|
} finally {
|
|
39737
39762
|
if (progressTimer !== null) clearInterval(progressTimer);
|
|
39738
39763
|
}
|
|
39739
|
-
|
|
39740
|
-
|
|
39741
|
-
|
|
39742
|
-
|
|
39743
|
-
|
|
39744
|
-
}
|
|
39764
|
+
const reportSkippedFiles = (skippedFiles, buildMessage) => {
|
|
39765
|
+
if (skippedFiles.length === 0 || !onPartialFailure) return;
|
|
39766
|
+
onPartialFailure(buildMessage(`${skippedFiles.slice(0, 3).join(", ")}${skippedFiles.length > 3 ? `, +${skippedFiles.length - 3} more` : ""}`));
|
|
39767
|
+
};
|
|
39768
|
+
const reasonHint = firstDropReason ? ` — first failure: ${firstDropReason}` : "";
|
|
39769
|
+
reportSkippedFiles(droppedFiles, (fileListPreview) => `${droppedFiles.length} file(s) failed to lint and were skipped (${fileListPreview})${reasonHint}`);
|
|
39770
|
+
reportSkippedFiles(deadlineSkippedFiles, (fileListPreview) => `${deadlineSkippedFiles.length} file(s) skipped — max scan duration reached before they were linted (${fileListPreview})`);
|
|
39745
39771
|
return allDiagnostics;
|
|
39746
39772
|
};
|
|
39747
39773
|
let diagnostics;
|
|
@@ -39945,7 +39971,8 @@ const runOxlint = async (options) => {
|
|
|
39945
39971
|
spawnTimeoutMs,
|
|
39946
39972
|
outputMaxBytes,
|
|
39947
39973
|
concurrency: options.concurrency,
|
|
39948
|
-
signal: options.signal
|
|
39974
|
+
signal: options.signal,
|
|
39975
|
+
deadlineEpochMs: options.deadlineEpochMs
|
|
39949
39976
|
});
|
|
39950
39977
|
writeOxlintConfig(passConfigPath, buildConfigForPass({}));
|
|
39951
39978
|
try {
|
|
@@ -40044,7 +40071,8 @@ const runOxlint = async (options) => {
|
|
|
40044
40071
|
spawnTimeoutMs,
|
|
40045
40072
|
outputMaxBytes,
|
|
40046
40073
|
concurrency: options.concurrency,
|
|
40047
|
-
signal: options.signal
|
|
40074
|
+
signal: options.signal,
|
|
40075
|
+
deadlineEpochMs: options.deadlineEpochMs
|
|
40048
40076
|
});
|
|
40049
40077
|
writeOxlintConfig(configPath, buildConfig({ extendsPaths }));
|
|
40050
40078
|
try {
|
|
@@ -40148,7 +40176,8 @@ var Linter = class Linter extends Service()("react-doctor/Linter") {
|
|
|
40148
40176
|
outputMaxBytes,
|
|
40149
40177
|
concurrency,
|
|
40150
40178
|
signal,
|
|
40151
|
-
lintBatchOrdering
|
|
40179
|
+
lintBatchOrdering,
|
|
40180
|
+
deadlineEpochMs: input.deadlineEpochMs
|
|
40152
40181
|
}),
|
|
40153
40182
|
catch: ensureReactDoctorError
|
|
40154
40183
|
});
|
|
@@ -40908,6 +40937,7 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
40908
40937
|
const deadCodePhaseTimeoutMs = yield* DeadCodePhaseTimeoutMs;
|
|
40909
40938
|
const resolveDeadCodePhaseTimeoutMs = (scaledPhaseTimeoutMs) => deadCodePhaseTimeoutMs === 15e4 ? scaledPhaseTimeoutMs : deadCodePhaseTimeoutMs;
|
|
40910
40939
|
const workerCountSuffix = scanConcurrency > 1 ? ` ${highlighter.dim(`[~${scanConcurrency} workers]`)}` : "";
|
|
40940
|
+
const capToDeadline = (phaseTimeoutMs) => input.deadlineEpochMs === void 0 ? phaseTimeoutMs : Math.min(phaseTimeoutMs, remainingDeadlineBudgetMs(input.deadlineEpochMs));
|
|
40911
40941
|
const shouldRunDeadCode = input.runDeadCode && !isDiffMode && (showWarnings || deadCodeMaySurfaceWhenWarningsHidden(resolvedConfig.config));
|
|
40912
40942
|
const deadCodeOverlapMode = yield* DeadCodeOverlap;
|
|
40913
40943
|
const shouldOverlapDeadCode = shouldRunDeadCode && deadCodeOverlapMode === "on";
|
|
@@ -40937,7 +40967,7 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
40937
40967
|
});
|
|
40938
40968
|
const deadCodeFiber = shouldOverlapDeadCode ? yield* forkChild(buildCollectDeadCode({
|
|
40939
40969
|
workerTimeoutMs: overlapDeadCodeTimeout.workerTimeoutMs,
|
|
40940
|
-
phaseTimeoutMs: resolveDeadCodePhaseTimeoutMs(overlapDeadCodeTimeout.phaseTimeoutMs)
|
|
40970
|
+
phaseTimeoutMs: capToDeadline(resolveDeadCodePhaseTimeoutMs(overlapDeadCodeTimeout.phaseTimeoutMs))
|
|
40941
40971
|
})) : null;
|
|
40942
40972
|
const scanProgress = yield* progressService.start("Scanning...");
|
|
40943
40973
|
const scanStartTime = Date.now();
|
|
@@ -40962,7 +40992,8 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
40962
40992
|
onCacheStats: (cacheHitFileCount, totalConsideredFileCount) => {
|
|
40963
40993
|
lintCacheHitFileCount = cacheHitFileCount;
|
|
40964
40994
|
lintCacheTotalFileCount = totalConsideredFileCount;
|
|
40965
|
-
}
|
|
40995
|
+
},
|
|
40996
|
+
deadlineEpochMs: input.deadlineEpochMs
|
|
40966
40997
|
}).pipe(catchTag("ReactDoctorError", (error) => unwrap(gen(function* () {
|
|
40967
40998
|
yield* set(lintFailure, {
|
|
40968
40999
|
didFail: true,
|
|
@@ -40989,7 +41020,13 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
40989
41020
|
let deadCodeCollected = [];
|
|
40990
41021
|
if (lintFailureState.didFail) {
|
|
40991
41022
|
if (deadCodeFiber !== null) yield* interrupt(deadCodeFiber);
|
|
40992
|
-
} else if (shouldRunDeadCode) {
|
|
41023
|
+
} else if (shouldRunDeadCode) if (input.deadlineEpochMs !== void 0 && remainingDeadlineBudgetMs(input.deadlineEpochMs) === 0) {
|
|
41024
|
+
if (deadCodeFiber !== null) yield* interrupt(deadCodeFiber);
|
|
41025
|
+
yield* set(deadCodeFailure, {
|
|
41026
|
+
didFail: true,
|
|
41027
|
+
reason: "Dead-code analysis skipped — max scan duration reached."
|
|
41028
|
+
});
|
|
41029
|
+
} else {
|
|
40993
41030
|
yield* scanProgress.update(`Scanned ${scannedFilesLabel}, analyzing dead code...`);
|
|
40994
41031
|
const sequentialDeadCodeTimeout = resolveDeadCodeTimeout({
|
|
40995
41032
|
sourceFileCount: totalFileCount,
|
|
@@ -40998,7 +41035,7 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
40998
41035
|
});
|
|
40999
41036
|
deadCodeCollected = deadCodeFiber !== null ? yield* join(deadCodeFiber) : yield* buildCollectDeadCode({
|
|
41000
41037
|
workerTimeoutMs: sequentialDeadCodeTimeout.workerTimeoutMs,
|
|
41001
|
-
phaseTimeoutMs: resolveDeadCodePhaseTimeoutMs(sequentialDeadCodeTimeout.phaseTimeoutMs)
|
|
41038
|
+
phaseTimeoutMs: capToDeadline(resolveDeadCodePhaseTimeoutMs(sequentialDeadCodeTimeout.phaseTimeoutMs))
|
|
41002
41039
|
});
|
|
41003
41040
|
}
|
|
41004
41041
|
const deadCodeFailureState = lintFailureState.didFail ? {
|
|
@@ -41036,7 +41073,7 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
41036
41073
|
};
|
|
41037
41074
|
const scoreSurface = input.scoreSurface ?? "score";
|
|
41038
41075
|
const scoreDiagnostics = filterDiagnosticsForSurface([...finalDiagnostics], scoreSurface, resolvedConfig.config);
|
|
41039
|
-
const score = lintFailureState.didFail ? null : yield* scoreService.compute({
|
|
41076
|
+
const score = lintFailureState.didFail || deadCodeFailureState.didFail ? null : yield* scoreService.compute({
|
|
41040
41077
|
diagnostics: scoreDiagnostics,
|
|
41041
41078
|
isCi: input.isCi,
|
|
41042
41079
|
metadata: scoreMetadata
|
|
@@ -43445,5 +43482,5 @@ const startLanguageServer = () => {
|
|
|
43445
43482
|
};
|
|
43446
43483
|
//#endregion
|
|
43447
43484
|
export { startLanguageServer };
|
|
43448
|
-
!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]="
|
|
43449
|
-
//# debugId=
|
|
43485
|
+
!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]="6811672b-baa6-5b7c-b676-5121c592d4a0")}catch(e){}}();
|
|
43486
|
+
//# debugId=6811672b-baa6-5b7c-b676-5121c592d4a0
|