react-doctor 0.5.8-dev.986557d → 0.5.8-dev.988ce57
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 +10035 -1853
- package/dist/index.d.ts +23 -2
- package/dist/index.js +650 -361
- package/dist/lsp.js +618 -385
- package/package.json +8 -6
package/dist/lsp.js
CHANGED
|
@@ -6,7 +6,7 @@ import os from "node:os";
|
|
|
6
6
|
import * as Path from "node:path";
|
|
7
7
|
import path from "node:path";
|
|
8
8
|
import * as NodeChildProcess from "node:child_process";
|
|
9
|
-
import { spawn, spawnSync } from "node:child_process";
|
|
9
|
+
import { execFileSync, spawn, spawnSync } from "node:child_process";
|
|
10
10
|
import * as ts from "typescript";
|
|
11
11
|
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";
|
|
12
12
|
import { parseJSON5 } from "confbox";
|
|
@@ -6021,7 +6021,7 @@ const composePassthrough = /* @__PURE__ */ dual(2, (left, right) => (input) => {
|
|
|
6021
6021
|
* @since 2.0.0
|
|
6022
6022
|
*/
|
|
6023
6023
|
const Scheduler = /* @__PURE__ */ Reference("effect/Scheduler", { defaultValue: () => new MixedScheduler() });
|
|
6024
|
-
const setImmediate = "setImmediate" in globalThis ? (f) => {
|
|
6024
|
+
const setImmediate$1 = "setImmediate" in globalThis ? (f) => {
|
|
6025
6025
|
const timer = globalThis.setImmediate(f);
|
|
6026
6026
|
return () => globalThis.clearImmediate(timer);
|
|
6027
6027
|
} : (f) => {
|
|
@@ -6065,7 +6065,7 @@ var PriorityBuckets = class {
|
|
|
6065
6065
|
var MixedScheduler = class {
|
|
6066
6066
|
executionMode;
|
|
6067
6067
|
setImmediate;
|
|
6068
|
-
constructor(executionMode = "async", setImmediateFn = setImmediate) {
|
|
6068
|
+
constructor(executionMode = "async", setImmediateFn = setImmediate$1) {
|
|
6069
6069
|
this.executionMode = executionMode;
|
|
6070
6070
|
this.setImmediate = setImmediateFn;
|
|
6071
6071
|
}
|
|
@@ -6090,7 +6090,7 @@ var MixedSchedulerDispatcher = class {
|
|
|
6090
6090
|
tasks = /* @__PURE__ */ new PriorityBuckets();
|
|
6091
6091
|
running = void 0;
|
|
6092
6092
|
setImmediate;
|
|
6093
|
-
constructor(setImmediateFn = setImmediate) {
|
|
6093
|
+
constructor(setImmediateFn = setImmediate$1) {
|
|
6094
6094
|
this.setImmediate = setImmediateFn;
|
|
6095
6095
|
}
|
|
6096
6096
|
/**
|
|
@@ -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,136 +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 PER_WORKER_MEM_BUDGET_BYTES = 1024 * 1024 * 1024;
|
|
33734
|
-
const DEFAULT_BRANCH_CANDIDATES = ["main", "master"];
|
|
33735
|
-
const ADOPTABLE_LINT_CONFIG_FILENAMES = [".oxlintrc.json", ".eslintrc.json"];
|
|
33736
|
-
const GIT_SHOW_MAX_BUFFER_BYTES = 10 * 1024 * 1024;
|
|
33737
|
-
const ES_TARGET_YEAR_BY_NAME = {
|
|
33738
|
-
es3: 1999,
|
|
33739
|
-
es5: 2009,
|
|
33740
|
-
es6: 2015,
|
|
33741
|
-
es2015: 2015,
|
|
33742
|
-
es2016: 2016,
|
|
33743
|
-
es2017: 2017,
|
|
33744
|
-
es2018: 2018,
|
|
33745
|
-
es2019: 2019,
|
|
33746
|
-
es2020: 2020,
|
|
33747
|
-
es2021: 2021,
|
|
33748
|
-
es2022: 2022,
|
|
33749
|
-
es2023: 2023,
|
|
33750
|
-
es2024: 2024,
|
|
33751
|
-
es2025: 2025,
|
|
33752
|
-
esnext: 9999
|
|
33753
|
-
};
|
|
33754
|
-
/**
|
|
33755
|
-
* tsconfig filenames probed when resolving a project's TypeScript
|
|
33756
|
-
* compiler options — the root config first, then a monorepo base config.
|
|
33757
|
-
*/
|
|
33758
|
-
const TSCONFIG_FILENAMES = ["tsconfig.json", "tsconfig.base.json"];
|
|
33759
|
-
/**
|
|
33760
|
-
* Project-config files that `StagedFiles.materialize` copies into
|
|
33761
|
-
* the temp directory alongside staged sources so oxlint resolves
|
|
33762
|
-
* `tsconfig` / `package.json` / lint configs the same way it would
|
|
33763
|
-
* in the working tree. Hoisted out of the staged-files helper so
|
|
33764
|
-
* the constant lives next to the rest of the IO budget knobs.
|
|
33765
|
-
*/
|
|
33766
|
-
const STAGED_FILES_PROJECT_CONFIG_FILENAMES = [
|
|
33767
|
-
"tsconfig.json",
|
|
33768
|
-
"tsconfig.base.json",
|
|
33769
|
-
"package.json",
|
|
33770
|
-
"doctor.config.ts",
|
|
33771
|
-
"doctor.config.mts",
|
|
33772
|
-
"doctor.config.cts",
|
|
33773
|
-
"doctor.config.js",
|
|
33774
|
-
"doctor.config.mjs",
|
|
33775
|
-
"doctor.config.cjs",
|
|
33776
|
-
"doctor.config.json",
|
|
33777
|
-
"doctor.config.jsonc",
|
|
33778
|
-
"oxlint.json",
|
|
33779
|
-
".oxlintrc.json"
|
|
33780
|
-
];
|
|
33781
|
-
const CONFIG_FINGERPRINT_FILENAMES = [
|
|
33782
|
-
"doctor.config.ts",
|
|
33783
|
-
"doctor.config.mts",
|
|
33784
|
-
"doctor.config.cts",
|
|
33785
|
-
"doctor.config.js",
|
|
33786
|
-
"doctor.config.mjs",
|
|
33787
|
-
"doctor.config.cjs",
|
|
33788
|
-
"doctor.config.json",
|
|
33789
|
-
"doctor.config.jsonc",
|
|
33790
|
-
"react-doctor.config.json",
|
|
33791
|
-
"package.json",
|
|
33792
|
-
"pnpm-workspace.yaml",
|
|
33793
|
-
"tsconfig.json",
|
|
33794
|
-
"tsconfig.base.json",
|
|
33795
|
-
".oxlintrc.json",
|
|
33796
|
-
".eslintrc.json",
|
|
33797
|
-
"pnpm-lock.yaml",
|
|
33798
|
-
"package-lock.json",
|
|
33799
|
-
"yarn.lock",
|
|
33800
|
-
"bun.lock",
|
|
33801
|
-
"bun.lockb",
|
|
33802
|
-
".gitignore"
|
|
33803
|
-
];
|
|
33804
|
-
const OXLINT_OUTPUT_MAX_BYTES = 50 * 1024 * 1024;
|
|
33805
|
-
const OXLINT_SPAWN_TIMEOUT_MS = 6e4;
|
|
33806
|
-
const NODE_COMPILE_CACHE_DIR_NAME = "node-compile-cache";
|
|
33807
|
-
const DEAD_CODE_WORKER_TIMEOUT_MS = 12e4;
|
|
33808
|
-
const OXLINT_SPLIT_TOTAL_BUDGET_MS = 18e4;
|
|
33809
|
-
const DEAD_CODE_PHASE_TIMEOUT_MS = 15e4;
|
|
33810
|
-
const LINT_PHASE_TIMEOUT_MS = 3e5;
|
|
33811
|
-
const SCAN_TOTAL_DEADLINE_MS = 9e5;
|
|
33812
|
-
const DEAD_CODE_WORKER_MAX_OLD_SPACE_MB = 8192;
|
|
33813
|
-
const DEAD_CODE_TIMEOUT_CEILING_MS = 6e5;
|
|
33814
|
-
const DEAD_CODE_PHASE_TIMEOUT_OVER_WORKER_MS = 3e4;
|
|
33815
|
-
const DEAD_CODE_OVERLAP_PARSE_SHARE = .4;
|
|
33816
|
-
const RECOMMENDED_PNPM_MINIMUM_RELEASE_AGE_MINUTES = 10080;
|
|
33817
|
-
const REACT_SERVER_DOM_PACKAGES = [
|
|
33818
|
-
"react-server-dom-webpack",
|
|
33819
|
-
"react-server-dom-parcel",
|
|
33820
|
-
"react-server-dom-turbopack"
|
|
33821
|
-
];
|
|
33822
|
-
const REACT_BLOG_RSC_ADVISORY_URL = "https://react.dev/blog/2025/12/03/critical-security-vulnerability-in-react-server-components";
|
|
33823
|
-
const VERCEL_NEXTJS_SECURITY_RELEASE_URL = "https://vercel.com/changelog/next-js-may-2026-security-release";
|
|
33824
|
-
const DIAGNOSTIC_CATEGORY_BUCKETS = [
|
|
33825
|
-
"Security",
|
|
33826
|
-
"Bugs",
|
|
33827
|
-
"Performance",
|
|
33828
|
-
"Accessibility",
|
|
33829
|
-
"Maintainability"
|
|
33830
|
-
];
|
|
33831
|
-
const APP_ONLY_RULE_KEYS = new Set([
|
|
33832
|
-
"react-hooks-js/static-components",
|
|
33833
|
-
"react-doctor/no-render-prop-children",
|
|
33834
|
-
"react-doctor/prefer-explicit-variants"
|
|
33835
|
-
]);
|
|
33836
|
-
const COMPILER_CLEANUP_BUCKET = "compiler-cleanup";
|
|
33837
|
-
const COMPILER_CLEANUP_RULE_KEYS = new Set(["react-doctor/react-compiler-no-manual-memoization"]);
|
|
33838
|
-
const ROOT_CAUSE_GROUPABLE_RULE_KEYS = new Set([
|
|
33839
|
-
"react-doctor/no-derived-state",
|
|
33840
|
-
"react-doctor/no-derived-state-effect",
|
|
33841
|
-
"react-doctor/no-derived-useState",
|
|
33842
|
-
"react-doctor/no-adjust-state-on-prop-change",
|
|
33843
|
-
"react-doctor/no-reset-all-state-on-prop-change"
|
|
33844
|
-
]);
|
|
33845
|
-
const MAX_GLOB_PATTERN_LENGTH_CHARS = 1024;
|
|
33846
|
-
const CONFIG_CACHE_TTL_MS = 300 * 1e3;
|
|
33847
|
-
const SOCKET_FREE_PURL_API_BASE = "https://firewall-api.socket.dev/purl";
|
|
33848
|
-
const SOCKET_PACKAGE_PAGE_BASE = "https://socket.dev/npm/package";
|
|
33849
|
-
const SOCKET_FREE_USER_AGENT = "react-doctor-supply-chain";
|
|
33850
|
-
const FILE_LINT_CACHE_FILENAME = "file-lint-cache.json";
|
|
33851
|
-
const FILE_LINT_CACHE_MAX_FILE_COUNT = 5e4;
|
|
33852
|
-
const SUPPLY_CHAIN_PLUGIN = "socket";
|
|
33853
|
-
const SUPPLY_CHAIN_RULE = "low-supply-chain-score";
|
|
33854
|
-
const SUPPLY_CHAIN_CATEGORY = "Security";
|
|
33855
|
-
const SUPPLY_CHAIN_OVERLAP_TIMEOUT_MS = 9e4;
|
|
33856
|
-
const SUPPLY_CHAIN_CACHE_SUBDIR = "supply-chain";
|
|
33857
|
-
const SUPPLY_CHAIN_IGNORED_PACKAGES = new Set(["next"]);
|
|
33858
33914
|
const TSCONFIG_FILENAME = "tsconfig.json";
|
|
33859
33915
|
const isRelativeExtendsValue = (extendsValue) => extendsValue.startsWith("./") || extendsValue.startsWith("../") || Path.isAbsolute(extendsValue);
|
|
33860
33916
|
const ensureJsonExtension = (filePath) => Path.extname(filePath) === "" ? `${filePath}.json` : filePath;
|
|
@@ -34546,7 +34602,10 @@ for (const [legacyRuleKey, nativeRuleKey] of Object.entries(LEGACY_RULE_KEY_TO_N
|
|
|
34546
34602
|
NATIVE_RULE_KEY_TO_LEGACY_RULE_KEYS.set(nativeRuleKey, aliases);
|
|
34547
34603
|
}
|
|
34548
34604
|
const getLegacyRuleKeysForNative = (ruleKey) => NATIVE_RULE_KEY_TO_LEGACY_RULE_KEYS.get(ruleKey) ?? [];
|
|
34549
|
-
const canonicalizeRuleKey = (ruleKey) =>
|
|
34605
|
+
const canonicalizeRuleKey = (ruleKey) => {
|
|
34606
|
+
const nativeRuleKey = LEGACY_RULE_KEY_TO_NATIVE_RULE_KEY[ruleKey];
|
|
34607
|
+
return typeof nativeRuleKey === "string" ? nativeRuleKey : ruleKey;
|
|
34608
|
+
};
|
|
34550
34609
|
const isReactDoctorShortIdOf = (bareRuleKey, qualifiedRuleKey) => !bareRuleKey.includes("/") && qualifiedRuleKey === `react-doctor/${bareRuleKey}`;
|
|
34551
34610
|
const isSameRuleKey = (candidateRuleKey, targetRuleKey) => {
|
|
34552
34611
|
const canonicalCandidate = canonicalizeRuleKey(candidateRuleKey);
|
|
@@ -34573,7 +34632,7 @@ const tokenizeRuleList = (ruleList) => {
|
|
|
34573
34632
|
const FOREIGN_INLINE_DISABLE_PATTERN = /(?:\/\/|\/\*)[ \t]*(eslint|oxlint)-disable-(next-line|line)(?![\w-])([^\r\n]*)/;
|
|
34574
34633
|
const FOREIGN_BLOCK_DISABLE_PATTERN = /\/\*[ \t]*(eslint|oxlint)-disable(?![\w-])([^*\r\n]*)/;
|
|
34575
34634
|
const FOREIGN_BLOCK_ENABLE_PATTERN = /\/\*[ \t]*(?:eslint|oxlint)-enable(?![\w-])([^*\r\n]*)/;
|
|
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}\` —
|
|
34635
|
+
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}\`).`;
|
|
34577
34636
|
const tokenMisnamesRule = (token, ruleId) => token !== ruleId && isSameRuleKey(token, ruleId);
|
|
34578
34637
|
const detectInlineNearMiss = (lines, diagnosticLineIndex, ruleId) => {
|
|
34579
34638
|
const candidates = [{
|
|
@@ -35049,6 +35108,7 @@ const collectStringSet = (values) => {
|
|
|
35049
35108
|
if (!Array.isArray(values)) return /* @__PURE__ */ new Set();
|
|
35050
35109
|
return new Set(values.filter((value) => typeof value === "string"));
|
|
35051
35110
|
};
|
|
35111
|
+
const JSX_NO_UNDEF_IDENTIFIER_PATTERN = /^`([^`]+)`/;
|
|
35052
35112
|
/**
|
|
35053
35113
|
* Pre-compiles every stateful filter and returns a single
|
|
35054
35114
|
* `apply(diagnostic)` closure that runs (in order):
|
|
@@ -35082,11 +35142,28 @@ const buildDiagnosticPipeline = (input) => {
|
|
|
35082
35142
|
const compiledOverrides = compileIgnoreOverrides(userConfig);
|
|
35083
35143
|
const textComponentNames = collectStringSet(userConfig?.textComponents);
|
|
35084
35144
|
const rawTextWrapperComponentNames = collectStringSet(userConfig?.rawTextWrapperComponents);
|
|
35145
|
+
const runtimeGlobalNames = collectStringSet(userConfig?.runtimeGlobals);
|
|
35085
35146
|
const hasTextComponents = textComponentNames.size > 0;
|
|
35086
35147
|
const hasRawTextWrappers = rawTextWrapperComponentNames.size > 0;
|
|
35148
|
+
const hasRuntimeGlobals = runtimeGlobalNames.size > 0;
|
|
35087
35149
|
const fileLinesCache = /* @__PURE__ */ new Map();
|
|
35088
35150
|
const fileContextCache = /* @__PURE__ */ new Map();
|
|
35089
35151
|
const libraryFileCache = /* @__PURE__ */ new Map();
|
|
35152
|
+
const suppressions = /* @__PURE__ */ new Map();
|
|
35153
|
+
const suppress = (diagnostic, source) => {
|
|
35154
|
+
const { ruleKey } = getDiagnosticRuleIdentity(diagnostic);
|
|
35155
|
+
const suppressionKey = `${ruleKey}\u0000${source}`;
|
|
35156
|
+
const existing = suppressions.get(suppressionKey);
|
|
35157
|
+
suppressions.set(suppressionKey, existing ? {
|
|
35158
|
+
...existing,
|
|
35159
|
+
count: existing.count + 1
|
|
35160
|
+
} : {
|
|
35161
|
+
rule: ruleKey,
|
|
35162
|
+
source,
|
|
35163
|
+
count: 1
|
|
35164
|
+
});
|
|
35165
|
+
return null;
|
|
35166
|
+
};
|
|
35090
35167
|
const isLibraryFile = (filePath) => {
|
|
35091
35168
|
let cached = libraryFileCache.get(filePath);
|
|
35092
35169
|
if (cached === void 0) {
|
|
@@ -35135,50 +35212,60 @@ const buildDiagnosticPipeline = (input) => {
|
|
|
35135
35212
|
if (hasRawTextWrappers && isInsideStringOnlyWrapper(lines, diagnostic.line, diagnostic.column, rawTextWrapperComponentNames)) return true;
|
|
35136
35213
|
return false;
|
|
35137
35214
|
};
|
|
35138
|
-
|
|
35139
|
-
if (
|
|
35140
|
-
|
|
35141
|
-
|
|
35142
|
-
|
|
35143
|
-
|
|
35144
|
-
|
|
35145
|
-
|
|
35146
|
-
|
|
35147
|
-
|
|
35148
|
-
|
|
35149
|
-
|
|
35150
|
-
if (
|
|
35151
|
-
|
|
35152
|
-
|
|
35153
|
-
|
|
35154
|
-
|
|
35155
|
-
|
|
35156
|
-
|
|
35157
|
-
|
|
35158
|
-
|
|
35159
|
-
if (isFileIgnoredByPatterns(current.filePath, rootDirectory, ignoredFilePatterns)) return null;
|
|
35160
|
-
if (isDiagnosticIgnoredByOverrides(current, rootDirectory, compiledOverrides)) return null;
|
|
35161
|
-
if (isRnRawTextSuppressedByConfig(current)) return null;
|
|
35162
|
-
}
|
|
35163
|
-
if (respectInlineDisables && current.line > 0) {
|
|
35164
|
-
const lines = getFileLines(current.filePath);
|
|
35165
|
-
if (lines) {
|
|
35166
|
-
const ruleIdentifier = `${current.plugin}/${current.rule}`;
|
|
35167
|
-
const evaluation = evaluateSuppression(lines, current.line - 1, ruleIdentifier);
|
|
35168
|
-
if (evaluation.isSuppressed) return null;
|
|
35169
|
-
if (evaluation.nearMissHint) current = {
|
|
35170
|
-
...current,
|
|
35171
|
-
suppressionHint: evaluation.nearMissHint
|
|
35172
|
-
};
|
|
35215
|
+
const isJsxNoUndefSuppressedByConfig = (diagnostic) => {
|
|
35216
|
+
if (!hasRuntimeGlobals) return false;
|
|
35217
|
+
if (diagnostic.rule !== "jsx-no-undef") return false;
|
|
35218
|
+
const identifierMatch = JSX_NO_UNDEF_IDENTIFIER_PATTERN.exec(diagnostic.message);
|
|
35219
|
+
return identifierMatch !== null && runtimeGlobalNames.has(identifierMatch[1]);
|
|
35220
|
+
};
|
|
35221
|
+
return {
|
|
35222
|
+
apply: (diagnostic) => {
|
|
35223
|
+
if (shouldAutoSuppress(diagnostic)) return null;
|
|
35224
|
+
let current = diagnostic;
|
|
35225
|
+
let explicitSeverityOverride;
|
|
35226
|
+
let explicitRuleOverride;
|
|
35227
|
+
if (severityControls) {
|
|
35228
|
+
const { ruleKey, category } = getDiagnosticRuleIdentity(current);
|
|
35229
|
+
explicitRuleOverride = resolveRuleSeverityOverride({ ruleKey }, severityControls);
|
|
35230
|
+
explicitSeverityOverride = resolveRuleSeverityOverride({
|
|
35231
|
+
ruleKey,
|
|
35232
|
+
category
|
|
35233
|
+
}, severityControls);
|
|
35234
|
+
if (explicitSeverityOverride === "off") return suppress(current, "config");
|
|
35235
|
+
if (explicitSeverityOverride !== void 0) current = restampSeverity(current, explicitSeverityOverride);
|
|
35173
35236
|
}
|
|
35174
|
-
|
|
35175
|
-
|
|
35176
|
-
|
|
35177
|
-
|
|
35178
|
-
|
|
35179
|
-
|
|
35180
|
-
|
|
35181
|
-
|
|
35237
|
+
if (explicitRuleOverride === void 0) {
|
|
35238
|
+
if (isAppOnlyRule(`${current.plugin}/${current.rule}`) && isLibraryFile(current.filePath)) return null;
|
|
35239
|
+
}
|
|
35240
|
+
if (!showWarnings && current.severity === "warning" && explicitSeverityOverride !== "warn") return null;
|
|
35241
|
+
if (userConfig) {
|
|
35242
|
+
if (isRuleIgnored(`${current.plugin}/${current.rule}`)) return suppress(current, "config");
|
|
35243
|
+
if (isFileIgnoredByPatterns(current.filePath, rootDirectory, ignoredFilePatterns)) return null;
|
|
35244
|
+
if (isDiagnosticIgnoredByOverrides(current, rootDirectory, compiledOverrides)) return suppress(current, "override");
|
|
35245
|
+
if (isRnRawTextSuppressedByConfig(current)) return null;
|
|
35246
|
+
if (isJsxNoUndefSuppressedByConfig(current)) return null;
|
|
35247
|
+
}
|
|
35248
|
+
if (respectInlineDisables && current.line > 0) {
|
|
35249
|
+
const lines = getFileLines(current.filePath);
|
|
35250
|
+
if (lines) {
|
|
35251
|
+
const ruleIdentifier = `${current.plugin}/${current.rule}`;
|
|
35252
|
+
const evaluation = evaluateSuppression(lines, current.line - 1, ruleIdentifier);
|
|
35253
|
+
if (evaluation.isSuppressed) return suppress(current, "inline");
|
|
35254
|
+
if (evaluation.nearMissHint) current = {
|
|
35255
|
+
...current,
|
|
35256
|
+
suppressionHint: evaluation.nearMissHint
|
|
35257
|
+
};
|
|
35258
|
+
}
|
|
35259
|
+
}
|
|
35260
|
+
const fileContext = getFileContext(current.filePath);
|
|
35261
|
+
if (fileContext !== "production") current = {
|
|
35262
|
+
...current,
|
|
35263
|
+
fileContext
|
|
35264
|
+
};
|
|
35265
|
+
return current;
|
|
35266
|
+
},
|
|
35267
|
+
summarizeSuppressions: () => [...suppressions.values()]
|
|
35268
|
+
};
|
|
35182
35269
|
};
|
|
35183
35270
|
const OxlintUnavailableKind = Literals(["binary-not-found", "native-binding-missing"]);
|
|
35184
35271
|
var OxlintUnavailable = class extends TaggedErrorClass()("OxlintUnavailable", {
|
|
@@ -35398,7 +35485,7 @@ const resolveScanConcurrency = (requested) => {
|
|
|
35398
35485
|
if (!Number.isFinite(requested) || requested < 1) return 1;
|
|
35399
35486
|
return Math.min(Math.floor(requested), 32);
|
|
35400
35487
|
};
|
|
35401
|
-
const readSystemFacts = () => ({
|
|
35488
|
+
const readSystemFacts$1 = () => ({
|
|
35402
35489
|
availableCores: os.availableParallelism(),
|
|
35403
35490
|
totalMemoryBytes: os.totalmem(),
|
|
35404
35491
|
cgroupMemoryLimitBytes: readCgroupMemoryLimitBytes()
|
|
@@ -35419,7 +35506,7 @@ const readSystemFacts = () => ({
|
|
|
35419
35506
|
* `facts` is injectable so tests exercise core-bound, memory-bound, cgroup-
|
|
35420
35507
|
* limited, and ceiling cases without mocking `os` or the filesystem.
|
|
35421
35508
|
*/
|
|
35422
|
-
const resolveAutoScanConcurrency = (facts = readSystemFacts()) => {
|
|
35509
|
+
const resolveAutoScanConcurrency = (facts = readSystemFacts$1()) => {
|
|
35423
35510
|
const availableMemoryBytes = Math.min(facts.totalMemoryBytes, facts.cgroupMemoryLimitBytes ?? Number.POSITIVE_INFINITY);
|
|
35424
35511
|
const memoryBoundedWorkers = Math.floor(availableMemoryBytes / PER_WORKER_MEM_BUDGET_BYTES);
|
|
35425
35512
|
return resolveScanConcurrency(Math.min(facts.availableCores, memoryBoundedWorkers));
|
|
@@ -35460,13 +35547,7 @@ var ScanDeadlineMs = class extends Reference("react-doctor/ScanDeadlineMs", { de
|
|
|
35460
35547
|
* without recompiling react-doctor. Tests override via
|
|
35461
35548
|
* `Layer.succeed(SupplyChainOverlapTimeoutMs, ...)`.
|
|
35462
35549
|
*/
|
|
35463
|
-
var SupplyChainOverlapTimeoutMs = class extends Reference("react-doctor/SupplyChainOverlapTimeoutMs", { defaultValue: () => {
|
|
35464
|
-
const raw = process.env["REACT_DOCTOR_SUPPLY_CHAIN_TIMEOUT_MS"];
|
|
35465
|
-
if (raw === void 0) return SUPPLY_CHAIN_OVERLAP_TIMEOUT_MS;
|
|
35466
|
-
const parsed = Number(raw);
|
|
35467
|
-
if (!Number.isFinite(parsed) || parsed <= 0) return SUPPLY_CHAIN_OVERLAP_TIMEOUT_MS;
|
|
35468
|
-
return parsed;
|
|
35469
|
-
} }) {};
|
|
35550
|
+
var SupplyChainOverlapTimeoutMs = class extends Reference("react-doctor/SupplyChainOverlapTimeoutMs", { defaultValue: () => readPositiveEnvMs("REACT_DOCTOR_SUPPLY_CHAIN_TIMEOUT_MS", SUPPLY_CHAIN_OVERLAP_TIMEOUT_MS) }) {};
|
|
35470
35551
|
/**
|
|
35471
35552
|
* Hard cap on combined stdout+stderr bytes per oxlint batch. The
|
|
35472
35553
|
* subprocess gets SIGKILL'd if it produces more; the recovery path
|
|
@@ -35610,6 +35691,13 @@ const BOOLEAN_FIELD_NAMES = [
|
|
|
35610
35691
|
"adoptExistingLintConfig"
|
|
35611
35692
|
];
|
|
35612
35693
|
const STRING_FIELD_NAMES = ["rootDir"];
|
|
35694
|
+
const STRING_ARRAY_FIELD_NAMES = [
|
|
35695
|
+
"projects",
|
|
35696
|
+
"textComponents",
|
|
35697
|
+
"rawTextWrapperComponents",
|
|
35698
|
+
"runtimeGlobals",
|
|
35699
|
+
"serverAuthFunctionNames"
|
|
35700
|
+
];
|
|
35613
35701
|
const SURFACE_CONTROL_FIELD_NAMES = [
|
|
35614
35702
|
"includeTags",
|
|
35615
35703
|
"excludeTags",
|
|
@@ -35711,6 +35799,7 @@ const validateConfigTypes = (config) => {
|
|
|
35711
35799
|
const validated = { ...config };
|
|
35712
35800
|
for (const fieldName of BOOLEAN_FIELD_NAMES) applyFieldValidator(config, validated, fieldName, (value) => coerceMaybeBooleanString(fieldName, value));
|
|
35713
35801
|
for (const fieldName of STRING_FIELD_NAMES) applyFieldValidator(config, validated, fieldName, (value) => validateString(fieldName, value));
|
|
35802
|
+
for (const fieldName of STRING_ARRAY_FIELD_NAMES) applyFieldValidator(config, validated, fieldName, (value) => validateStringArrayField(fieldName, value));
|
|
35714
35803
|
applyFieldValidator(config, validated, "surfaces", validateSurfacesField);
|
|
35715
35804
|
for (const fieldName of SEVERITY_FIELD_NAMES) applyFieldValidator(config, validated, fieldName, (value) => validateSeverityMap(fieldName, value, fieldName === "categories"));
|
|
35716
35805
|
applyFieldValidator(config, validated, "plugins", (value) => validateStringArrayField("plugins", value));
|
|
@@ -36695,14 +36784,17 @@ const checkReactServerComponentsAdvisory = (scanDirectory, project) => {
|
|
|
36695
36784
|
return diagnostics;
|
|
36696
36785
|
};
|
|
36697
36786
|
const REDUCED_MOTION_GREP_PATTERN = "prefers-reduced-motion|useReducedMotion|MotionConfig|reducedMotion";
|
|
36698
|
-
const
|
|
36699
|
-
|
|
36700
|
-
"
|
|
36701
|
-
"
|
|
36702
|
-
"
|
|
36703
|
-
"
|
|
36704
|
-
"
|
|
36705
|
-
|
|
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;
|
|
36706
36798
|
const MISSING_REDUCED_MOTION_DIAGNOSTIC = {
|
|
36707
36799
|
filePath: "package.json",
|
|
36708
36800
|
plugin: "react-doctor",
|
|
@@ -36714,6 +36806,19 @@ const MISSING_REDUCED_MOTION_DIAGNOSTIC = {
|
|
|
36714
36806
|
column: 0,
|
|
36715
36807
|
category: "Accessibility"
|
|
36716
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
|
+
};
|
|
36717
36822
|
const checkReducedMotion = (rootDirectory) => {
|
|
36718
36823
|
const packageJsonPath = Path.join(rootDirectory, "package.json");
|
|
36719
36824
|
if (!isFile(packageJsonPath)) return [];
|
|
@@ -36732,22 +36837,21 @@ const checkReducedMotion = (rootDirectory) => {
|
|
|
36732
36837
|
const result = spawnSync("git", [
|
|
36733
36838
|
"grep",
|
|
36734
36839
|
"--untracked",
|
|
36735
|
-
"-
|
|
36736
|
-
"-E",
|
|
36840
|
+
"-lE",
|
|
36737
36841
|
REDUCED_MOTION_GREP_PATTERN,
|
|
36738
36842
|
"--",
|
|
36739
36843
|
...REDUCED_MOTION_FILE_GLOBS
|
|
36740
36844
|
], {
|
|
36741
36845
|
cwd: rootDirectory,
|
|
36846
|
+
encoding: "utf-8",
|
|
36742
36847
|
stdio: [
|
|
36743
36848
|
"ignore",
|
|
36744
36849
|
"pipe",
|
|
36745
36850
|
"pipe"
|
|
36746
36851
|
]
|
|
36747
36852
|
});
|
|
36748
|
-
if (result.error) return [MISSING_REDUCED_MOTION_DIAGNOSTIC];
|
|
36749
|
-
|
|
36750
|
-
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];
|
|
36751
36855
|
};
|
|
36752
36856
|
const buildSecurityScanDiagnostic = (finding, entry, relativePath) => ({
|
|
36753
36857
|
filePath: relativePath,
|
|
@@ -36897,7 +37001,10 @@ const shouldEnableRule = (requires, tags, capabilities, ignoredTags, disabledBy)
|
|
|
36897
37001
|
}
|
|
36898
37002
|
return true;
|
|
36899
37003
|
};
|
|
36900
|
-
const
|
|
37004
|
+
const yieldToEventLoop = () => new Promise((resolve) => {
|
|
37005
|
+
setImmediate(resolve);
|
|
37006
|
+
});
|
|
37007
|
+
const createSecurityScanSession = (rootDirectory, options) => {
|
|
36901
37008
|
const capabilities = options.project ? buildCapabilities(options.project) : /* @__PURE__ */ new Set();
|
|
36902
37009
|
const ignoredTags = options.ignoredTags ?? /* @__PURE__ */ new Set();
|
|
36903
37010
|
const enabledScanRules = REACT_DOCTOR_RULES.flatMap((entry) => {
|
|
@@ -36912,7 +37019,7 @@ const checkSecurityScan = (rootDirectory, options = {}) => {
|
|
|
36912
37019
|
committedFilesOnly: rule.committedFilesOnly === true
|
|
36913
37020
|
}];
|
|
36914
37021
|
});
|
|
36915
|
-
if (enabledScanRules.length === 0) return
|
|
37022
|
+
if (enabledScanRules.length === 0) return null;
|
|
36916
37023
|
const diagnostics = [];
|
|
36917
37024
|
const seen = /* @__PURE__ */ new Set();
|
|
36918
37025
|
const gitIgnoredCache = /* @__PURE__ */ new Map();
|
|
@@ -36924,15 +37031,34 @@ const checkSecurityScan = (rootDirectory, options = {}) => {
|
|
|
36924
37031
|
}
|
|
36925
37032
|
return status === true;
|
|
36926
37033
|
};
|
|
36927
|
-
|
|
36928
|
-
|
|
36929
|
-
|
|
36930
|
-
|
|
36931
|
-
|
|
36932
|
-
|
|
36933
|
-
|
|
37034
|
+
const scanFile = (file) => {
|
|
37035
|
+
for (const { entry, scan, committedFilesOnly } of enabledScanRules) for (const finding of scan(file)) {
|
|
37036
|
+
if (committedFilesOnly && isFileGitIgnored(file)) continue;
|
|
37037
|
+
const diagnostic = buildSecurityScanDiagnostic(finding, entry, file.relativePath);
|
|
37038
|
+
const key = `${diagnostic.rule}:${diagnostic.filePath}:${diagnostic.line}:${diagnostic.column}:${diagnostic.message}`;
|
|
37039
|
+
if (seen.has(key)) continue;
|
|
37040
|
+
seen.add(key);
|
|
37041
|
+
diagnostics.push(diagnostic);
|
|
37042
|
+
}
|
|
37043
|
+
};
|
|
37044
|
+
return {
|
|
37045
|
+
scanFile,
|
|
37046
|
+
diagnostics
|
|
37047
|
+
};
|
|
37048
|
+
};
|
|
37049
|
+
const checkSecurityScanCooperative = async (rootDirectory, options = {}) => {
|
|
37050
|
+
const session = createSecurityScanSession(rootDirectory, options);
|
|
37051
|
+
if (session === null) return [];
|
|
37052
|
+
let filesSinceYield = 0;
|
|
37053
|
+
for (const file of collectSecurityScanFiles(rootDirectory)) {
|
|
37054
|
+
session.scanFile(file);
|
|
37055
|
+
filesSinceYield += 1;
|
|
37056
|
+
if (filesSinceYield >= 16) {
|
|
37057
|
+
filesSinceYield = 0;
|
|
37058
|
+
await yieldToEventLoop();
|
|
37059
|
+
}
|
|
36934
37060
|
}
|
|
36935
|
-
return diagnostics;
|
|
37061
|
+
return session.diagnostics;
|
|
36936
37062
|
};
|
|
36937
37063
|
var import_picocolors = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
36938
37064
|
let p = process || {}, argv = p.argv || [], env = p.env || {};
|
|
@@ -37149,6 +37275,74 @@ const collectDeadCodeIgnorePatterns = (rootDirectory) => {
|
|
|
37149
37275
|
return [...seen].filter((pattern) => pattern.length > 0);
|
|
37150
37276
|
};
|
|
37151
37277
|
const collectDeadCodeEntryPatterns = (rootDirectory) => [...new Set(collectKnipPatterns(rootDirectory, "entry"))].filter((pattern) => pattern.length > 0);
|
|
37278
|
+
const readSystemFacts = () => ({
|
|
37279
|
+
availableCores: os.availableParallelism(),
|
|
37280
|
+
totalMemoryBytes: os.totalmem(),
|
|
37281
|
+
cgroupMemoryLimitBytes: readCgroupMemoryLimitBytes()
|
|
37282
|
+
});
|
|
37283
|
+
/**
|
|
37284
|
+
* How many real deslop dead-code child processes may run at once, across the
|
|
37285
|
+
* concurrent per-project `runInspect` fibers of one CLI run. The cap is the
|
|
37286
|
+
* smaller of the core count and the number of `DEAD_CODE_WORKER_MEM_BUDGET_BYTES`
|
|
37287
|
+
* workers that fit in available memory, floored at 1.
|
|
37288
|
+
*
|
|
37289
|
+
* On a roomy dev box / CI runner this resolves high enough that every
|
|
37290
|
+
* concurrently-scanned project still spawns its own worker (no serialization vs
|
|
37291
|
+
* the prior uncapped behavior); on a memory-constrained runner it collapses
|
|
37292
|
+
* toward 1, so the `withDeadCodeWorkerSlot` semaphore serializes the spawns
|
|
37293
|
+
* instead of oversubscribing memory with N simultaneous children — the global
|
|
37294
|
+
* cap the per-project spawn path lacked.
|
|
37295
|
+
*
|
|
37296
|
+
* Mirrors `resolveAutoScanConcurrency` (lint), but budgets memory per the
|
|
37297
|
+
* heavier dead-code worker. `facts` is injectable for tests.
|
|
37298
|
+
*/
|
|
37299
|
+
const resolveDeadCodeConcurrency = (facts = readSystemFacts()) => {
|
|
37300
|
+
const availableMemoryBytes = Math.min(facts.totalMemoryBytes, facts.cgroupMemoryLimitBytes ?? Number.POSITIVE_INFINITY);
|
|
37301
|
+
const memoryBoundedWorkers = Math.floor(availableMemoryBytes / DEAD_CODE_WORKER_MEM_BUDGET_BYTES);
|
|
37302
|
+
return Math.max(1, Math.min(facts.availableCores, memoryBoundedWorkers));
|
|
37303
|
+
};
|
|
37304
|
+
let availableSlots = -1;
|
|
37305
|
+
const waiters = [];
|
|
37306
|
+
const releaseSlot = () => {
|
|
37307
|
+
const nextWaiter = waiters.shift();
|
|
37308
|
+
if (nextWaiter !== void 0) nextWaiter();
|
|
37309
|
+
else availableSlots += 1;
|
|
37310
|
+
};
|
|
37311
|
+
/**
|
|
37312
|
+
* Runs `task` once a dead-code worker slot is free, releasing the slot when the
|
|
37313
|
+
* task settles (success or failure). With a high cap (roomy machine) every
|
|
37314
|
+
* caller proceeds immediately; with a low cap (constrained runner) callers
|
|
37315
|
+
* queue and run as slots free.
|
|
37316
|
+
*
|
|
37317
|
+
* `abortSignal` short-circuits the WAIT: if it's already aborted, or fires while
|
|
37318
|
+
* this caller is queued, the call rejects without acquiring a slot or running
|
|
37319
|
+
* `task` — so a cancelled scan (e.g. lint failed) doesn't sit in the queue and
|
|
37320
|
+
* then spawn a child only to tear it down. A queued caller that aborts removes
|
|
37321
|
+
* its own waiter so a later release never hands a slot to a dead request.
|
|
37322
|
+
*/
|
|
37323
|
+
const withDeadCodeWorkerSlot = async (task, abortSignal) => {
|
|
37324
|
+
if (abortSignal?.aborted) throw new Error("Dead-code worker aborted.");
|
|
37325
|
+
if (availableSlots < 0) availableSlots = resolveDeadCodeConcurrency();
|
|
37326
|
+
if (availableSlots > 0) availableSlots -= 1;
|
|
37327
|
+
else await new Promise((resolve, reject) => {
|
|
37328
|
+
const waiter = () => {
|
|
37329
|
+
abortSignal?.removeEventListener("abort", onAbort);
|
|
37330
|
+
resolve();
|
|
37331
|
+
};
|
|
37332
|
+
const onAbort = () => {
|
|
37333
|
+
const queuedIndex = waiters.indexOf(waiter);
|
|
37334
|
+
if (queuedIndex !== -1) waiters.splice(queuedIndex, 1);
|
|
37335
|
+
reject(/* @__PURE__ */ new Error("Dead-code worker aborted."));
|
|
37336
|
+
};
|
|
37337
|
+
waiters.push(waiter);
|
|
37338
|
+
abortSignal?.addEventListener("abort", onAbort, { once: true });
|
|
37339
|
+
});
|
|
37340
|
+
try {
|
|
37341
|
+
return await task();
|
|
37342
|
+
} finally {
|
|
37343
|
+
releaseSlot();
|
|
37344
|
+
}
|
|
37345
|
+
};
|
|
37152
37346
|
/**
|
|
37153
37347
|
* Resolves a path to its canonical, symlink-free form, falling back to
|
|
37154
37348
|
* the input when it cannot be realpath'd (broken symlink, permission
|
|
@@ -37173,6 +37367,11 @@ const toCanonicalPath = (filePath) => {
|
|
|
37173
37367
|
};
|
|
37174
37368
|
const DEAD_CODE_PLUGIN = "deslop";
|
|
37175
37369
|
const DEAD_CODE_CATEGORY = "Maintainability";
|
|
37370
|
+
const REACT_DOCTOR_TOOLCHAIN_PACKAGES = new Set([
|
|
37371
|
+
"react-doctor",
|
|
37372
|
+
"eslint-plugin-react-doctor",
|
|
37373
|
+
"oxlint-plugin-react-doctor"
|
|
37374
|
+
]);
|
|
37176
37375
|
const DEAD_CODE_WORKER_SCRIPT = `
|
|
37177
37376
|
const inputChunks = [];
|
|
37178
37377
|
process.stdin.on("data", (chunk) => inputChunks.push(chunk));
|
|
@@ -37439,14 +37638,17 @@ const checkDeadCode = async (options) => {
|
|
|
37439
37638
|
if (!NFS.existsSync(Path.join(rootDirectory, "package.json"))) return [];
|
|
37440
37639
|
const entryPatterns = collectDeadCodeEntryPatterns(rootDirectory);
|
|
37441
37640
|
const ignorePatterns = collectDeadCodeIgnorePatterns(rootDirectory);
|
|
37442
|
-
const
|
|
37443
|
-
|
|
37444
|
-
|
|
37445
|
-
|
|
37446
|
-
|
|
37447
|
-
|
|
37448
|
-
|
|
37449
|
-
|
|
37641
|
+
const spawnAndRun = () => {
|
|
37642
|
+
return runDeadCodeWorkerWithTimeout((options.createWorker ?? createDeadCodeWorker)({
|
|
37643
|
+
rootDirectory,
|
|
37644
|
+
entryPatterns,
|
|
37645
|
+
tsConfigPath: resolveTsConfigPath(rootDirectory),
|
|
37646
|
+
ignorePatterns,
|
|
37647
|
+
deslopJsModuleSpecifier: options.deslopJsModuleSpecifier ?? import.meta.resolve("deslop-js"),
|
|
37648
|
+
parseConcurrency: options.parseConcurrency
|
|
37649
|
+
}), options.workerTimeoutMs ?? 12e4, options.abortSignal);
|
|
37650
|
+
};
|
|
37651
|
+
const result = parseDeadCodeWorkerResult(options.createWorker === void 0 ? await withDeadCodeWorkerSlot(spawnAndRun, options.abortSignal) : await spawnAndRun());
|
|
37450
37652
|
const toRelative = (filePath) => toRelativeFilePath(rootDirectory, filePath);
|
|
37451
37653
|
const diagnostics = [];
|
|
37452
37654
|
for (const unusedFile of result.unusedFiles) diagnostics.push({
|
|
@@ -37475,6 +37677,7 @@ const checkDeadCode = async (options) => {
|
|
|
37475
37677
|
});
|
|
37476
37678
|
}
|
|
37477
37679
|
for (const unusedDependency of result.unusedDependencies) {
|
|
37680
|
+
if (REACT_DOCTOR_TOOLCHAIN_PACKAGES.has(unusedDependency.name)) continue;
|
|
37478
37681
|
const label = unusedDependency.isDevDependency ? "devDependency" : "dependency";
|
|
37479
37682
|
diagnostics.push({
|
|
37480
37683
|
filePath: "package.json",
|
|
@@ -37544,6 +37747,7 @@ const isDiagnosticOnSurface = (diagnostic, surface, config) => {
|
|
|
37544
37747
|
return true;
|
|
37545
37748
|
};
|
|
37546
37749
|
const filterDiagnosticsForSurface = (diagnostics, surface, config) => diagnostics.filter((diagnostic) => isDiagnosticOnSurface(diagnostic, surface, config));
|
|
37750
|
+
const remainingDeadlineBudgetMs = (deadlineEpochMs) => Math.max(deadlineEpochMs - Date.now(), 0);
|
|
37547
37751
|
/**
|
|
37548
37752
|
* Budget for the dead-code phase, scaled to the work. deslop's graph build is
|
|
37549
37753
|
* CPU-bound and roughly linear in file count, so a fixed 120s cap is too tight
|
|
@@ -37562,53 +37766,6 @@ const resolveDeadCodeTimeout = (input) => {
|
|
|
37562
37766
|
phaseTimeoutMs: workerTimeoutMs + DEAD_CODE_PHASE_TIMEOUT_OVER_WORKER_MS
|
|
37563
37767
|
};
|
|
37564
37768
|
};
|
|
37565
|
-
const collectSizedSourceFiles = (rootDirectory, relativePaths) => {
|
|
37566
|
-
const entries = [];
|
|
37567
|
-
for (const relativePath of relativePaths) {
|
|
37568
|
-
const absolutePath = Path.resolve(rootDirectory, relativePath);
|
|
37569
|
-
const sizeBytes = statSourceFileSize(absolutePath);
|
|
37570
|
-
if (isLargeMinifiedFile(absolutePath, sizeBytes)) continue;
|
|
37571
|
-
entries.push({
|
|
37572
|
-
path: relativePath,
|
|
37573
|
-
sizeBytes: sizeBytes ?? 0
|
|
37574
|
-
});
|
|
37575
|
-
}
|
|
37576
|
-
return entries;
|
|
37577
|
-
};
|
|
37578
|
-
const listSourceFilesViaGit = (rootDirectory) => {
|
|
37579
|
-
const result = spawnSync("git", [
|
|
37580
|
-
"ls-files",
|
|
37581
|
-
"-z",
|
|
37582
|
-
"--cached",
|
|
37583
|
-
"--others",
|
|
37584
|
-
"--exclude-standard"
|
|
37585
|
-
], {
|
|
37586
|
-
cwd: rootDirectory,
|
|
37587
|
-
encoding: "utf-8",
|
|
37588
|
-
maxBuffer: GIT_LS_FILES_MAX_BUFFER_BYTES
|
|
37589
|
-
});
|
|
37590
|
-
if (result.error || result.status !== 0) return null;
|
|
37591
|
-
return result.stdout.split("\0").filter((filePath) => filePath.length > 0 && isLintableSourceFile(filePath));
|
|
37592
|
-
};
|
|
37593
|
-
const listSourceFilesViaFilesystem = (rootDirectory) => {
|
|
37594
|
-
const filePaths = [];
|
|
37595
|
-
const stack = [rootDirectory];
|
|
37596
|
-
while (stack.length > 0) {
|
|
37597
|
-
const currentDirectory = stack.pop();
|
|
37598
|
-
const entries = readDirectoryEntries(currentDirectory);
|
|
37599
|
-
for (const entry of entries) {
|
|
37600
|
-
const absolutePath = Path.join(currentDirectory, entry.name);
|
|
37601
|
-
if (entry.isDirectory()) {
|
|
37602
|
-
if (!entry.name.startsWith(".") && !IGNORED_DIRECTORIES.has(entry.name)) stack.push(absolutePath);
|
|
37603
|
-
continue;
|
|
37604
|
-
}
|
|
37605
|
-
if (entry.isFile() && isLintableSourceFile(entry.name)) filePaths.push(Path.relative(rootDirectory, absolutePath).replace(/\\/g, "/"));
|
|
37606
|
-
}
|
|
37607
|
-
}
|
|
37608
|
-
return filePaths;
|
|
37609
|
-
};
|
|
37610
|
-
const listSourceFilesWithSize = (rootDirectory) => collectSizedSourceFiles(rootDirectory, listSourceFilesViaGit(rootDirectory) ?? listSourceFilesViaFilesystem(rootDirectory));
|
|
37611
|
-
const listSourceFiles = (rootDirectory) => listSourceFilesWithSize(rootDirectory).map((entry) => entry.path);
|
|
37612
37769
|
const resolveLintIncludePaths = (rootDirectory, userConfig, project) => {
|
|
37613
37770
|
if (!Array.isArray(userConfig?.ignore?.files) || userConfig.ignore.files.length === 0) return;
|
|
37614
37771
|
const ignoredPatterns = compileIgnoredFilePatterns(userConfig);
|
|
@@ -37653,7 +37810,6 @@ var DeadCode = class DeadCode extends Service()("react-doctor/DeadCode") {
|
|
|
37653
37810
|
return yield* tryPromise({
|
|
37654
37811
|
try: (signal) => checkDeadCode({
|
|
37655
37812
|
rootDirectory: input.rootDirectory,
|
|
37656
|
-
userConfig: input.userConfig,
|
|
37657
37813
|
parseConcurrency: input.parseConcurrency,
|
|
37658
37814
|
workerTimeoutMs: input.workerTimeoutMs,
|
|
37659
37815
|
abortSignal: signal
|
|
@@ -37776,6 +37932,11 @@ const isSafeGitRevision = (candidate) => {
|
|
|
37776
37932
|
if (candidate.includes("..") || candidate.includes("@{")) return false;
|
|
37777
37933
|
return SAFE_GIT_REVISION_PATTERN.test(candidate);
|
|
37778
37934
|
};
|
|
37935
|
+
const resolveSpawnArgsLengthCap = () => {
|
|
37936
|
+
if (process.platform === "win32") return SPAWN_ARGS_MAX_LENGTH_CHARS;
|
|
37937
|
+
if (process.platform === "darwin") return SPAWN_ARGS_MAX_LENGTH_CHARS_DARWIN;
|
|
37938
|
+
return SPAWN_ARGS_MAX_LENGTH_CHARS_POSIX;
|
|
37939
|
+
};
|
|
37779
37940
|
/**
|
|
37780
37941
|
* Splits a git revision range into its endpoints: three-dot `A...B`
|
|
37781
37942
|
* (symmetric, merge-base) or two-dot `A..B` (direct). Returns `null`
|
|
@@ -37847,43 +38008,47 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
37847
38008
|
* reason: GitInvocationFailed })` so the rest of the codebase
|
|
37848
38009
|
* sees a single failure channel.
|
|
37849
38010
|
*/
|
|
37850
|
-
const runCommand = (input) =>
|
|
37851
|
-
const
|
|
37852
|
-
cwd: input.directory,
|
|
37853
|
-
env: input.env,
|
|
37854
|
-
extendEnv: true
|
|
37855
|
-
}));
|
|
37856
|
-
const maxStdoutBytes = input.maxStdoutBytes;
|
|
37857
|
-
const stdoutByteCount = yield* make$13(0);
|
|
37858
|
-
const [stdout, stderr, status] = yield* all([
|
|
37859
|
-
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({
|
|
37860
|
-
args: [...input.args],
|
|
37861
|
-
directory: input.directory,
|
|
37862
|
-
cause: /* @__PURE__ */ new Error(`git stdout exceeded ${maxStdoutBytes} bytes`)
|
|
37863
|
-
}) })) : void_)))))),
|
|
37864
|
-
mkString(decodeText(handle.stderr)),
|
|
37865
|
-
handle.exitCode
|
|
37866
|
-
], { concurrency: 3 });
|
|
37867
|
-
return {
|
|
37868
|
-
status,
|
|
37869
|
-
stdout,
|
|
37870
|
-
stderr
|
|
37871
|
-
};
|
|
37872
|
-
})).pipe(catchTag$1("PlatformError", (cause) => {
|
|
37873
|
-
if (input.command !== "git") return succeed$2({
|
|
38011
|
+
const runCommand = (input) => {
|
|
38012
|
+
const foldSpawnFailure = (cause) => input.command !== "git" ? succeed$2({
|
|
37874
38013
|
status: 127,
|
|
37875
38014
|
stdout: "",
|
|
37876
38015
|
stderr: String(cause)
|
|
37877
|
-
})
|
|
37878
|
-
return new ReactDoctorError({ reason: new GitInvocationFailed({
|
|
38016
|
+
}) : fail$4(new ReactDoctorError({ reason: new GitInvocationFailed({
|
|
37879
38017
|
args: [...input.args],
|
|
37880
38018
|
directory: input.directory,
|
|
37881
38019
|
cause
|
|
37882
|
-
}) });
|
|
37883
|
-
|
|
37884
|
-
|
|
37885
|
-
|
|
37886
|
-
|
|
38020
|
+
}) }));
|
|
38021
|
+
return scoped(gen(function* () {
|
|
38022
|
+
if (!isDirectory(input.directory)) return yield* foldSpawnFailure(`spawn ENOTDIR (cwd is not a directory: ${input.directory})`);
|
|
38023
|
+
const argvLengthChars = input.command.length + 1 + input.args.reduce((total, arg) => total + arg.length + 1, 0);
|
|
38024
|
+
const spawnArgsLengthCap = resolveSpawnArgsLengthCap();
|
|
38025
|
+
if (argvLengthChars > spawnArgsLengthCap) return yield* foldSpawnFailure(`spawn ENAMETOOLONG (${argvLengthChars} argv chars exceed ${spawnArgsLengthCap})`);
|
|
38026
|
+
const handle = yield* spawner.spawn(make$1(input.command, [...input.args], {
|
|
38027
|
+
cwd: input.directory,
|
|
38028
|
+
env: input.env,
|
|
38029
|
+
extendEnv: true
|
|
38030
|
+
}));
|
|
38031
|
+
const maxStdoutBytes = input.maxStdoutBytes;
|
|
38032
|
+
const stdoutByteCount = yield* make$13(0);
|
|
38033
|
+
const [stdout, stderr, status] = yield* all([
|
|
38034
|
+
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({
|
|
38035
|
+
args: [...input.args],
|
|
38036
|
+
directory: input.directory,
|
|
38037
|
+
cause: /* @__PURE__ */ new Error(`git stdout exceeded ${maxStdoutBytes} bytes`)
|
|
38038
|
+
}) })) : void_)))))),
|
|
38039
|
+
mkString(decodeText(handle.stderr)),
|
|
38040
|
+
handle.exitCode
|
|
38041
|
+
], { concurrency: 3 });
|
|
38042
|
+
return {
|
|
38043
|
+
status,
|
|
38044
|
+
stdout,
|
|
38045
|
+
stderr
|
|
38046
|
+
};
|
|
38047
|
+
})).pipe(catchTag$1("PlatformError", foldSpawnFailure), withSpan("git.exec", { attributes: {
|
|
38048
|
+
"git.command": input.command,
|
|
38049
|
+
"git.subcommand": input.args[0] ?? ""
|
|
38050
|
+
} }));
|
|
38051
|
+
};
|
|
37887
38052
|
const runGit = (directory, args) => runCommand({
|
|
37888
38053
|
command: "git",
|
|
37889
38054
|
args,
|
|
@@ -37916,7 +38081,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
37916
38081
|
"rev-parse",
|
|
37917
38082
|
"--verify",
|
|
37918
38083
|
branch
|
|
37919
|
-
]).pipe(map$3((result) => result.status === 0));
|
|
38084
|
+
]).pipe(map$3((result) => result.status === 0), catch_$1((error) => error.reason._tag === "GitInvocationFailed" ? succeed$2(false) : fail$4(error)));
|
|
37920
38085
|
const headSha = (directory) => runGit(directory, ["rev-parse", "HEAD"]).pipe(map$3((result) => result.status === 0 ? trimOrNull(result.stdout) : null));
|
|
37921
38086
|
const mergeBase = (input) => isSafeGitRevision(input.ref) ? runGit(input.directory, [
|
|
37922
38087
|
"merge-base",
|
|
@@ -38026,6 +38191,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
38026
38191
|
if (resolvedCurrentBranch === null && explicitBaseBranch === void 0) return null;
|
|
38027
38192
|
const baseBranch = explicitBaseBranch ?? (yield* defaultBranch(directory));
|
|
38028
38193
|
if (baseBranch === null) return null;
|
|
38194
|
+
if (!isSafeGitRevision(baseBranch)) return null;
|
|
38029
38195
|
if (explicitBaseBranch !== void 0) {
|
|
38030
38196
|
if (!(yield* branchExists(directory, explicitBaseBranch))) return yield* fail$4(new ReactDoctorError({ reason: new GitBaseBranchMissing({ branch: explicitBaseBranch }) }));
|
|
38031
38197
|
}
|
|
@@ -38130,7 +38296,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
38130
38296
|
]);
|
|
38131
38297
|
if (result.status !== 0) return null;
|
|
38132
38298
|
return parseChangedLineRanges(result.stdout);
|
|
38133
|
-
}).pipe(withSpan("Git.changedLineRanges"))
|
|
38299
|
+
}).pipe(catch_$1((error) => error.reason._tag === "GitInvocationFailed" ? succeed$2(null) : fail$4(error)), withSpan("Git.changedLineRanges"))
|
|
38134
38300
|
});
|
|
38135
38301
|
})).pipe(provide$2(layer$2.pipe(provide$2(mergeAll$1(layer$1, layer)))));
|
|
38136
38302
|
/**
|
|
@@ -38296,12 +38462,12 @@ const findFilesWithDisableDirectivesViaGit = async (rootDirectory, includePaths)
|
|
|
38296
38462
|
return null;
|
|
38297
38463
|
}
|
|
38298
38464
|
if (grepResult === null) return null;
|
|
38299
|
-
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));
|
|
38300
38466
|
};
|
|
38301
38467
|
const findFilesWithDisableDirectivesViaFilesystem = (rootDirectory, includePaths) => {
|
|
38302
38468
|
const matches = [];
|
|
38303
38469
|
const checkFile = (relativePath) => {
|
|
38304
|
-
if (!isLintableSourceFile(relativePath)) return;
|
|
38470
|
+
if (!isLintableSourceFile(relativePath) || hasIgnoredPathSegment(relativePath)) return;
|
|
38305
38471
|
const absolutePath = Path.join(rootDirectory, relativePath);
|
|
38306
38472
|
let content;
|
|
38307
38473
|
try {
|
|
@@ -38315,22 +38481,7 @@ const findFilesWithDisableDirectivesViaFilesystem = (rootDirectory, includePaths
|
|
|
38315
38481
|
for (const candidate of includePaths) checkFile(candidate);
|
|
38316
38482
|
return matches;
|
|
38317
38483
|
}
|
|
38318
|
-
const
|
|
38319
|
-
while (stack.length > 0) {
|
|
38320
|
-
const current = stack.pop();
|
|
38321
|
-
if (current === void 0) continue;
|
|
38322
|
-
const entries = readDirectoryEntries(current);
|
|
38323
|
-
for (const entry of entries) {
|
|
38324
|
-
if (entry.isDirectory()) {
|
|
38325
|
-
if (entry.name.startsWith(".") || IGNORED_DIRECTORIES.has(entry.name)) continue;
|
|
38326
|
-
stack.push(Path.join(current, entry.name));
|
|
38327
|
-
continue;
|
|
38328
|
-
}
|
|
38329
|
-
if (!entry.isFile()) continue;
|
|
38330
|
-
const absolute = Path.join(current, entry.name);
|
|
38331
|
-
checkFile(Path.relative(rootDirectory, absolute));
|
|
38332
|
-
}
|
|
38333
|
-
}
|
|
38484
|
+
for (const { absolutePath } of walkSourceTreeFiles(rootDirectory)) checkFile(Path.relative(rootDirectory, absolutePath));
|
|
38334
38485
|
return matches;
|
|
38335
38486
|
};
|
|
38336
38487
|
const findFilesWithDisableDirectives = async (rootDirectory, includePaths) => await findFilesWithDisableDirectivesViaGit(rootDirectory, includePaths) ?? findFilesWithDisableDirectivesViaFilesystem(rootDirectory, includePaths);
|
|
@@ -38376,7 +38527,7 @@ const normalizeConfigForHash = (config) => {
|
|
|
38376
38527
|
if (Array.isArray(clone?.jsPlugins)) clone.jsPlugins = clone.jsPlugins.map((_, index) => `<plugin:${index}>`);
|
|
38377
38528
|
return clone;
|
|
38378
38529
|
};
|
|
38379
|
-
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("
|
|
38530
|
+
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");
|
|
38380
38531
|
/**
|
|
38381
38532
|
* Loads a plugin module via the local require resolver and extracts
|
|
38382
38533
|
* `(name, ruleNames)` from either `module.exports.meta + rules` or
|
|
@@ -38547,12 +38698,12 @@ const createOxlintConfig = ({ pluginPath, project, customRulesOnly = false, exte
|
|
|
38547
38698
|
if (customRulesOnly && registryEntry.originallyExternal) continue;
|
|
38548
38699
|
if (rule.framework !== "global" && !rule.requires) continue;
|
|
38549
38700
|
if (!shouldEnableRule(rule.requires, rule.tags, capabilities, ignoredTags, rule.disabledBy)) continue;
|
|
38550
|
-
const
|
|
38701
|
+
const explicitRuleOverride = resolveRuleSeverityOverride({ ruleKey: registryEntry.key }, severityControls);
|
|
38702
|
+
if (rule.defaultEnabled === false && explicitRuleOverride === void 0) continue;
|
|
38703
|
+
const severity = resolveRuleSeverityOverride({
|
|
38551
38704
|
ruleKey: registryEntry.key,
|
|
38552
38705
|
category: rule.category
|
|
38553
|
-
}, severityControls);
|
|
38554
|
-
if (rule.defaultEnabled === false && explicitSeverity === void 0) continue;
|
|
38555
|
-
const severity = explicitSeverity ?? resolveCompilerCleanupBucketSeverity(registryEntry.key, severityControls) ?? rule.severity;
|
|
38706
|
+
}, severityControls) ?? resolveCompilerCleanupBucketSeverity(registryEntry.key, severityControls) ?? rule.severity;
|
|
38556
38707
|
if (severity === "off") continue;
|
|
38557
38708
|
enabledReactDoctorRules[registryEntry.key] = severity;
|
|
38558
38709
|
}
|
|
@@ -38654,24 +38805,38 @@ const createFileLintCache = (cacheDirectory, rulesetHash) => {
|
|
|
38654
38805
|
updatedAtMs: Date.now(),
|
|
38655
38806
|
files: Object.fromEntries(cappedEntries)
|
|
38656
38807
|
};
|
|
38657
|
-
const keptHashes = Object.entries(rulesets).sort(([, first], [, second]) => second.updatedAtMs - first.updatedAtMs).slice(0, 8).map(([hash]) => hash);
|
|
38658
|
-
const prunedRulesets = {};
|
|
38659
|
-
for (const hash of keptHashes) prunedRulesets[hash] = rulesets[hash];
|
|
38660
38808
|
atomicWriteJson(cacheFilePath, {
|
|
38661
38809
|
version: 1,
|
|
38662
|
-
rulesets:
|
|
38810
|
+
rulesets: Object.fromEntries(Object.entries(rulesets).filter(([, bucket]) => isRecord(bucket) && typeof bucket.updatedAtMs === "number").sort(([, first], [, second]) => second.updatedAtMs - first.updatedAtMs).slice(0, 8))
|
|
38663
38811
|
});
|
|
38664
38812
|
}
|
|
38665
38813
|
};
|
|
38666
38814
|
};
|
|
38667
38815
|
const bundledRequire = createRequire(import.meta.url);
|
|
38816
|
+
const childNodeVersionByBinaryPath = /* @__PURE__ */ new Map();
|
|
38817
|
+
const resolveChildNodeVersion = (nodeBinaryPath) => {
|
|
38818
|
+
if (nodeBinaryPath === process.execPath) return process.version;
|
|
38819
|
+
const cachedVersion = childNodeVersionByBinaryPath.get(nodeBinaryPath);
|
|
38820
|
+
if (cachedVersion !== void 0) return cachedVersion;
|
|
38821
|
+
let version;
|
|
38822
|
+
try {
|
|
38823
|
+
version = execFileSync(nodeBinaryPath, ["--version"], {
|
|
38824
|
+
encoding: "utf8",
|
|
38825
|
+
timeout: NODE_VERSION_PROBE_TIMEOUT_MS
|
|
38826
|
+
}).trim();
|
|
38827
|
+
} catch {
|
|
38828
|
+
version = `unknown:${nodeBinaryPath}`;
|
|
38829
|
+
}
|
|
38830
|
+
childNodeVersionByBinaryPath.set(nodeBinaryPath, version);
|
|
38831
|
+
return version;
|
|
38832
|
+
};
|
|
38668
38833
|
const TOOLCHAIN_PACKAGE_SPECIFIERS = [
|
|
38669
38834
|
"oxlint/package.json",
|
|
38670
38835
|
"oxlint-plugin-react-doctor/package.json",
|
|
38671
38836
|
"eslint-plugin-react-hooks/package.json"
|
|
38672
38837
|
];
|
|
38673
|
-
const resolveOxlintToolchainVersions = () => {
|
|
38674
|
-
const versions = [`node=${
|
|
38838
|
+
const resolveOxlintToolchainVersions = (nodeBinaryPath = process.execPath) => {
|
|
38839
|
+
const versions = [`node=${resolveChildNodeVersion(nodeBinaryPath)}`];
|
|
38675
38840
|
for (const specifier of TOOLCHAIN_PACKAGE_SPECIFIERS) try {
|
|
38676
38841
|
const packageJson = bundledRequire(specifier);
|
|
38677
38842
|
const version = typeof packageJson.version === "string" ? packageJson.version : "unknown";
|
|
@@ -39205,11 +39370,12 @@ const REACT_COMPILER_TITLE = "React Compiler can't optimize this";
|
|
|
39205
39370
|
const REACT_COMPILER_TODO_TITLE = "React Compiler doesn't support this syntax";
|
|
39206
39371
|
const REACT_COMPILER_IMPACT = "This component misses React Compiler's automatic memoization & re-renders more than it should";
|
|
39207
39372
|
const REACT_COMPILER_ACTION = "Rewrite the flagged code so the compiler can optimize it.";
|
|
39373
|
+
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`.";
|
|
39208
39374
|
const REACT_COMPILER_GENERIC_MESSAGE = `${REACT_COMPILER_IMPACT}. ${REACT_COMPILER_ACTION}`;
|
|
39209
|
-
const buildReactCompilerMessage = (reasonSummary) => {
|
|
39375
|
+
const buildReactCompilerMessage = (reasonSummary, action = REACT_COMPILER_ACTION) => {
|
|
39210
39376
|
const normalizedSummary = reasonSummary.replace(TRAILING_PERIOD_PATTERN, "");
|
|
39211
|
-
if (!normalizedSummary) return
|
|
39212
|
-
return `${REACT_COMPILER_IMPACT}: ${normalizedSummary}. ${
|
|
39377
|
+
if (!normalizedSummary) return `${REACT_COMPILER_IMPACT}. ${action}`;
|
|
39378
|
+
return `${REACT_COMPILER_IMPACT}: ${normalizedSummary}. ${action}`;
|
|
39213
39379
|
};
|
|
39214
39380
|
const PLUGIN_CATEGORY_MAP = {
|
|
39215
39381
|
react: "Bugs",
|
|
@@ -39258,7 +39424,7 @@ const resolveCleanedDiagnostic = (message, help, plugin, rule, project) => {
|
|
|
39258
39424
|
const [reasonSummary = "", ...reasonDetailLines] = bailoutReason.split("\n");
|
|
39259
39425
|
const reasonDetail = reasonDetailLines.join("\n").trim();
|
|
39260
39426
|
return {
|
|
39261
|
-
message: buildReactCompilerMessage(reasonSummary.trim()),
|
|
39427
|
+
message: buildReactCompilerMessage(reasonSummary.trim(), rule === "incompatible-library" ? REACT_COMPILER_INCOMPATIBLE_LIBRARY_ACTION : REACT_COMPILER_ACTION),
|
|
39262
39428
|
help: appendReanimatedSharedValueHint(reasonDetail || help, rule, project)
|
|
39263
39429
|
};
|
|
39264
39430
|
}
|
|
@@ -39519,28 +39685,46 @@ const isParallelismRelatedSpawnError = (error) => {
|
|
|
39519
39685
|
* loop with a slimmer config in that case.
|
|
39520
39686
|
*/
|
|
39521
39687
|
const spawnLintBatches = async (input) => {
|
|
39522
|
-
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;
|
|
39523
39689
|
const requestedConcurrency = resolveScanConcurrency(input.concurrency ?? 1);
|
|
39524
39690
|
const totalFileCount = fileBatches.reduce((sum, batch) => sum + batch.length, 0);
|
|
39525
39691
|
const runBatchPass = async (concurrency) => {
|
|
39526
39692
|
const allDiagnostics = [];
|
|
39527
39693
|
const droppedFiles = [];
|
|
39694
|
+
const deadlineSkippedFiles = [];
|
|
39695
|
+
const isPastDeadline = () => deadlineEpochMs !== void 0 && remainingDeadlineBudgetMs(deadlineEpochMs) === 0;
|
|
39528
39696
|
let firstDropReason = null;
|
|
39529
|
-
const
|
|
39530
|
-
|
|
39697
|
+
const spawnLintBatch = async (batch, depth, batchState) => {
|
|
39698
|
+
if (isPastDeadline()) {
|
|
39699
|
+
deadlineSkippedFiles.push(...batch);
|
|
39700
|
+
batchState.deadlineSkippedFileCount += batch.length;
|
|
39701
|
+
return [];
|
|
39702
|
+
}
|
|
39531
39703
|
const batchArgs = [...baseArgs, ...batch];
|
|
39532
39704
|
try {
|
|
39533
39705
|
return parseOxlintOutput(await spawnOxlint(batchArgs, rootDirectory, nodeBinaryPath, spawnTimeoutMs, outputMaxBytes, signal), project, rootDirectory);
|
|
39534
39706
|
} catch (error) {
|
|
39535
39707
|
if (!isSplittableReactDoctorError(error)) throw error;
|
|
39536
|
-
|
|
39537
|
-
|
|
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;
|
|
39715
|
+
const isDepthCapReached = depth >= splitMaxDepth;
|
|
39716
|
+
if (batch.length <= 1 || isBudgetElapsed || isDepthCapReached) {
|
|
39538
39717
|
droppedFiles.push(...batch);
|
|
39539
|
-
if (firstDropReason === null)
|
|
39718
|
+
if (firstDropReason === null) {
|
|
39719
|
+
let limitHint = "";
|
|
39720
|
+
if (isDepthCapReached) limitHint = ` (split depth cap of ${splitMaxDepth} levels reached)`;
|
|
39721
|
+
else if (isBudgetElapsed) limitHint = ` (split budget of ${splitTotalBudgetMs / MILLISECONDS_PER_SECOND}s exhausted at depth ${depth})`;
|
|
39722
|
+
firstDropReason = batch.length > 1 ? `${error.message}${limitHint}` : error.message;
|
|
39723
|
+
}
|
|
39540
39724
|
return [];
|
|
39541
39725
|
}
|
|
39542
39726
|
const splitIndex = Math.ceil(batch.length / 2);
|
|
39543
|
-
return [...await spawnLintBatch(batch.slice(0, splitIndex), depth + 1), ...await spawnLintBatch(batch.slice(splitIndex), depth + 1)];
|
|
39727
|
+
return [...await spawnLintBatch(batch.slice(0, splitIndex), depth + 1, batchState), ...await spawnLintBatch(batch.slice(splitIndex), depth + 1, batchState)];
|
|
39544
39728
|
}
|
|
39545
39729
|
};
|
|
39546
39730
|
let startedFileCount = 0;
|
|
@@ -39556,9 +39740,17 @@ const spawnLintBatches = async (input) => {
|
|
|
39556
39740
|
progressTimer?.unref?.();
|
|
39557
39741
|
try {
|
|
39558
39742
|
const batchResults = await mapWithConcurrency(fileBatches, concurrency, async (batch) => {
|
|
39743
|
+
if (isPastDeadline()) {
|
|
39744
|
+
deadlineSkippedFiles.push(...batch);
|
|
39745
|
+
return [];
|
|
39746
|
+
}
|
|
39559
39747
|
startedFileCount += batch.length;
|
|
39560
|
-
const
|
|
39561
|
-
|
|
39748
|
+
const batchState = {
|
|
39749
|
+
deadlineMs: null,
|
|
39750
|
+
deadlineSkippedFileCount: 0
|
|
39751
|
+
};
|
|
39752
|
+
const batchDiagnostics = await spawnLintBatch(batch, 0, batchState);
|
|
39753
|
+
scannedFileCount += batch.length - batchState.deadlineSkippedFileCount;
|
|
39562
39754
|
if (onFileProgress) {
|
|
39563
39755
|
displayedFileCount = Math.min(Math.max(displayedFileCount, scannedFileCount), totalFileCount);
|
|
39564
39756
|
onFileProgress(displayedFileCount, totalFileCount);
|
|
@@ -39569,12 +39761,13 @@ const spawnLintBatches = async (input) => {
|
|
|
39569
39761
|
} finally {
|
|
39570
39762
|
if (progressTimer !== null) clearInterval(progressTimer);
|
|
39571
39763
|
}
|
|
39572
|
-
|
|
39573
|
-
|
|
39574
|
-
|
|
39575
|
-
|
|
39576
|
-
|
|
39577
|
-
}
|
|
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})`);
|
|
39578
39771
|
return allDiagnostics;
|
|
39579
39772
|
};
|
|
39580
39773
|
let diagnostics;
|
|
@@ -39631,7 +39824,14 @@ const resolveReactDoctorCacheDir = (projectDirectory) => {
|
|
|
39631
39824
|
if (cacheDirOverride) return Path.join(cacheDirOverride, projectCacheSubdir(projectDirectory));
|
|
39632
39825
|
const nodeModulesDirectory = Path.join(projectDirectory, "node_modules");
|
|
39633
39826
|
if (NFS.existsSync(nodeModulesDirectory)) return Path.join(nodeModulesDirectory, ".cache", "react-doctor");
|
|
39634
|
-
return Path.join(os.tmpdir(),
|
|
39827
|
+
return Path.join(os.tmpdir(), `react-doctor-cache-${resolveUserCacheScope()}`, projectCacheSubdir(projectDirectory));
|
|
39828
|
+
};
|
|
39829
|
+
const resolveUserCacheScope = () => {
|
|
39830
|
+
try {
|
|
39831
|
+
return String(process.getuid?.() ?? os.userInfo().username);
|
|
39832
|
+
} catch {
|
|
39833
|
+
return "shared";
|
|
39834
|
+
}
|
|
39635
39835
|
};
|
|
39636
39836
|
const sortSourceFilesByCost = (entries) => [...entries].sort((left, right) => right.sizeBytes - left.sizeBytes).map((entry) => entry.path);
|
|
39637
39837
|
/**
|
|
@@ -39771,7 +39971,8 @@ const runOxlint = async (options) => {
|
|
|
39771
39971
|
spawnTimeoutMs,
|
|
39772
39972
|
outputMaxBytes,
|
|
39773
39973
|
concurrency: options.concurrency,
|
|
39774
|
-
signal: options.signal
|
|
39974
|
+
signal: options.signal,
|
|
39975
|
+
deadlineEpochMs: options.deadlineEpochMs
|
|
39775
39976
|
});
|
|
39776
39977
|
writeOxlintConfig(passConfigPath, buildConfigForPass({}));
|
|
39777
39978
|
try {
|
|
@@ -39799,7 +40000,7 @@ const runOxlint = async (options) => {
|
|
|
39799
40000
|
extendsPaths: [],
|
|
39800
40001
|
ruleSelection: "cacheable"
|
|
39801
40002
|
}),
|
|
39802
|
-
toolchainVersions: resolveOxlintToolchainVersions(),
|
|
40003
|
+
toolchainVersions: resolveOxlintToolchainVersions(nodeBinaryPath),
|
|
39803
40004
|
ignorePatterns: combinedPatterns,
|
|
39804
40005
|
tsconfigContent
|
|
39805
40006
|
});
|
|
@@ -39813,7 +40014,7 @@ const runOxlint = async (options) => {
|
|
|
39813
40014
|
missFiles.push(candidateFile);
|
|
39814
40015
|
continue;
|
|
39815
40016
|
}
|
|
39816
|
-
const cacheKey = `${candidateFile.replaceAll("\\", "/")}
|
|
40017
|
+
const cacheKey = `${candidateFile.replaceAll("\\", "/")}\u0000${contentHash}`;
|
|
39817
40018
|
cacheKeyByFile.set(candidateFile, cacheKey);
|
|
39818
40019
|
const cachedDiagnostics = cache.lookup(cacheKey);
|
|
39819
40020
|
if (cachedDiagnostics === null) missFiles.push(candidateFile);
|
|
@@ -39870,7 +40071,8 @@ const runOxlint = async (options) => {
|
|
|
39870
40071
|
spawnTimeoutMs,
|
|
39871
40072
|
outputMaxBytes,
|
|
39872
40073
|
concurrency: options.concurrency,
|
|
39873
|
-
signal: options.signal
|
|
40074
|
+
signal: options.signal,
|
|
40075
|
+
deadlineEpochMs: options.deadlineEpochMs
|
|
39874
40076
|
});
|
|
39875
40077
|
writeOxlintConfig(configPath, buildConfig({ extendsPaths }));
|
|
39876
40078
|
try {
|
|
@@ -39974,7 +40176,8 @@ var Linter = class Linter extends Service()("react-doctor/Linter") {
|
|
|
39974
40176
|
outputMaxBytes,
|
|
39975
40177
|
concurrency,
|
|
39976
40178
|
signal,
|
|
39977
|
-
lintBatchOrdering
|
|
40179
|
+
lintBatchOrdering,
|
|
40180
|
+
deadlineEpochMs: input.deadlineEpochMs
|
|
39978
40181
|
}),
|
|
39979
40182
|
catch: ensureReactDoctorError
|
|
39980
40183
|
});
|
|
@@ -40129,7 +40332,7 @@ const ScoreApiResponseSchema = Struct({
|
|
|
40129
40332
|
})))
|
|
40130
40333
|
});
|
|
40131
40334
|
const parseScoreResult = (value) => getOrNull(decodeUnknownOption(ScoreApiResponseSchema)(value));
|
|
40132
|
-
const stripLocalFileFields = (diagnostics) => diagnostics.map(({ filePath: _filePath, fileContext: _fileContext, ...rest }) => rest);
|
|
40335
|
+
const stripLocalFileFields = (diagnostics) => diagnostics.map(({ filePath: _filePath, fileContext: _fileContext, fixGroupId: _fixGroupId, ...rest }) => rest);
|
|
40133
40336
|
const isAbortError = (error) => error instanceof Error && (error.name === "AbortError" || error.name === "TimeoutError");
|
|
40134
40337
|
const describeFailure = (error) => {
|
|
40135
40338
|
if (isAbortError(error)) return `timed out after ${FETCH_TIMEOUT_MS / 1e3}s`;
|
|
@@ -40433,7 +40636,10 @@ const ALERT_SEVERITY_RANK = {
|
|
|
40433
40636
|
medium: 2,
|
|
40434
40637
|
low: 1
|
|
40435
40638
|
};
|
|
40436
|
-
const severityRank = (severity) =>
|
|
40639
|
+
const severityRank = (severity) => {
|
|
40640
|
+
const normalized = severity.toLowerCase();
|
|
40641
|
+
return Object.hasOwn(ALERT_SEVERITY_RANK, normalized) ? ALERT_SEVERITY_RANK[normalized] : 0;
|
|
40642
|
+
};
|
|
40437
40643
|
const displaySeverity = (severity) => {
|
|
40438
40644
|
const normalized = sanitizeTerminalText(severity.toLowerCase());
|
|
40439
40645
|
return normalized === "middle" ? "medium" : normalized;
|
|
@@ -40450,7 +40656,7 @@ const ALERT_TYPE_LABELS = {
|
|
|
40450
40656
|
typosquat: "possible typosquat"
|
|
40451
40657
|
};
|
|
40452
40658
|
const humanizeAlertType = (type) => type.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/[_-]+/g, " ").toLowerCase().trim();
|
|
40453
|
-
const friendlyAlertType = (type) => ALERT_TYPE_LABELS[type]
|
|
40659
|
+
const friendlyAlertType = (type) => Object.hasOwn(ALERT_TYPE_LABELS, type) ? ALERT_TYPE_LABELS[type] : sanitizeTerminalText(humanizeAlertType(type));
|
|
40454
40660
|
const summarizeAlertNote = (note) => {
|
|
40455
40661
|
const collapsed = sanitizeTerminalText(note.replace(/\s+/g, " ").trim());
|
|
40456
40662
|
const firstSentence = collapsed.split(/(?<=\.)\s/)[0] || collapsed;
|
|
@@ -40545,7 +40751,10 @@ const checkSupplyChain = (input) => gen(function* () {
|
|
|
40545
40751
|
* outage.
|
|
40546
40752
|
*/
|
|
40547
40753
|
var SupplyChain = class SupplyChain extends Service()("react-doctor/SupplyChain") {
|
|
40548
|
-
static layerNode = succeed$3(SupplyChain, SupplyChain.of({ run: (input) => unwrap(
|
|
40754
|
+
static layerNode = succeed$3(SupplyChain, SupplyChain.of({ run: (input) => unwrap(flatMap$2(SupplyChainOverlapTimeoutMs, (totalTimeoutMs) => checkSupplyChain({
|
|
40755
|
+
...input,
|
|
40756
|
+
totalTimeoutMs
|
|
40757
|
+
})).pipe(map$3((diagnostics) => fromIterable$1(diagnostics)), withSpan("SupplyChain.run"))) }));
|
|
40549
40758
|
static layerOf = (diagnostics) => succeed$3(SupplyChain, SupplyChain.of({ run: () => fromIterable$1(diagnostics) }));
|
|
40550
40759
|
};
|
|
40551
40760
|
const getObjectProperty = (value, propertyName) => {
|
|
@@ -40607,7 +40816,10 @@ const formatLintFailText = (reasonTag, nodeVersion) => {
|
|
|
40607
40816
|
* diagnostics).
|
|
40608
40817
|
* 2. beforeLint hook (e.g. CLI renders the project-detection block)
|
|
40609
40818
|
* 3. environment checks (reduced-motion + pnpm hardening +
|
|
40610
|
-
* expo/react-native
|
|
40819
|
+
* expo/react-native), collected synchronously. The heavier
|
|
40820
|
+
* content-regex security scan is forked instead (like supply-chain
|
|
40821
|
+
* below) and joined before the concat, so its CPU overlaps lint
|
|
40822
|
+
* rather than blocking the event loop before it.
|
|
40611
40823
|
* 4. The supply-chain check (Socket.dev) is forked onto a background
|
|
40612
40824
|
* fiber so its ~100% network-bound time overlaps the ~100%
|
|
40613
40825
|
* CPU/subprocess-bound lint pass below, collapsing two serial
|
|
@@ -40627,7 +40839,7 @@ const formatLintFailText = (reasonTag, nodeVersion) => {
|
|
|
40627
40839
|
* order, so terminal output is identical either way; supply-chain
|
|
40628
40840
|
* rides alongside without a spinner.
|
|
40629
40841
|
* 6. Join the supply-chain fiber, then assemble the diagnostics in a
|
|
40630
|
-
* FIXED order (env, supply-chain, lint, dead-code) so the output is
|
|
40842
|
+
* FIXED order (env, security-scan, supply-chain, lint, dead-code) so the output is
|
|
40631
40843
|
* byte-identical regardless of which fiber settled first. The
|
|
40632
40844
|
* viewer-permission fiber is joined later, during score-metadata
|
|
40633
40845
|
* assembly (it feeds score metadata, not diagnostics). The per-element
|
|
@@ -40688,12 +40900,13 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
40688
40900
|
...checkPnpmHardening(scanDirectory),
|
|
40689
40901
|
...checkReactServerComponentsAdvisory(scanDirectory, project),
|
|
40690
40902
|
...checkExpoProject(scanDirectory, project),
|
|
40691
|
-
...checkReactNativeProject(scanDirectory, project)
|
|
40692
|
-
...checkSecurityScan(scanDirectory, {
|
|
40693
|
-
project,
|
|
40694
|
-
ignoredTags: input.ignoredTags
|
|
40695
|
-
})
|
|
40903
|
+
...checkReactNativeProject(scanDirectory, project)
|
|
40696
40904
|
])));
|
|
40905
|
+
const securityScanFailedRef = yield* make$13(false);
|
|
40906
|
+
const securityScanFiber = yield* forkChild(runCollect(applyPerElementPipeline(isDiffMode ? empty$4 : unwrap(tryPromise(() => checkSecurityScanCooperative(scanDirectory, {
|
|
40907
|
+
project,
|
|
40908
|
+
ignoredTags: input.ignoredTags
|
|
40909
|
+
})).pipe(map$3((diagnostics) => fromIterable$1(diagnostics)), catch_$1(() => set(securityScanFailedRef, true).pipe(as(empty$4))))))).pipe(withSpan("SecurityScan.run")));
|
|
40697
40910
|
const shouldRunSupplyChain = !isDiffMode || (input.supplyChainManifestChanged ?? false);
|
|
40698
40911
|
const supplyChainOverlapTimeout = yield* SupplyChainOverlapTimeoutMs;
|
|
40699
40912
|
const supplyChainFiber = yield* forkChild(shouldRunSupplyChain ? runCollect(applyPerElementPipeline(supplyChainService.run({
|
|
@@ -40724,6 +40937,7 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
40724
40937
|
const deadCodePhaseTimeoutMs = yield* DeadCodePhaseTimeoutMs;
|
|
40725
40938
|
const resolveDeadCodePhaseTimeoutMs = (scaledPhaseTimeoutMs) => deadCodePhaseTimeoutMs === 15e4 ? scaledPhaseTimeoutMs : deadCodePhaseTimeoutMs;
|
|
40726
40939
|
const workerCountSuffix = scanConcurrency > 1 ? ` ${highlighter.dim(`[~${scanConcurrency} workers]`)}` : "";
|
|
40940
|
+
const capToDeadline = (phaseTimeoutMs) => input.deadlineEpochMs === void 0 ? phaseTimeoutMs : Math.min(phaseTimeoutMs, remainingDeadlineBudgetMs(input.deadlineEpochMs));
|
|
40727
40941
|
const shouldRunDeadCode = input.runDeadCode && !isDiffMode && (showWarnings || deadCodeMaySurfaceWhenWarningsHidden(resolvedConfig.config));
|
|
40728
40942
|
const deadCodeOverlapMode = yield* DeadCodeOverlap;
|
|
40729
40943
|
const shouldOverlapDeadCode = shouldRunDeadCode && deadCodeOverlapMode === "on";
|
|
@@ -40731,7 +40945,6 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
40731
40945
|
const lintConcurrency = deadCodeParseConcurrency === void 0 ? scanConcurrency : Math.max(1, scanConcurrency - deadCodeParseConcurrency);
|
|
40732
40946
|
const buildCollectDeadCode = (deadCodeTimeout) => runCollect(applyPerElementPipeline(deadCodeService.run({
|
|
40733
40947
|
rootDirectory: scanDirectory,
|
|
40734
|
-
userConfig: resolvedConfig.config,
|
|
40735
40948
|
parseConcurrency: deadCodeParseConcurrency,
|
|
40736
40949
|
workerTimeoutMs: deadCodeTimeout.workerTimeoutMs
|
|
40737
40950
|
}).pipe(catchTag("ReactDoctorError", (error) => unwrap(gen(function* () {
|
|
@@ -40754,7 +40967,7 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
40754
40967
|
});
|
|
40755
40968
|
const deadCodeFiber = shouldOverlapDeadCode ? yield* forkChild(buildCollectDeadCode({
|
|
40756
40969
|
workerTimeoutMs: overlapDeadCodeTimeout.workerTimeoutMs,
|
|
40757
|
-
phaseTimeoutMs: resolveDeadCodePhaseTimeoutMs(overlapDeadCodeTimeout.phaseTimeoutMs)
|
|
40970
|
+
phaseTimeoutMs: capToDeadline(resolveDeadCodePhaseTimeoutMs(overlapDeadCodeTimeout.phaseTimeoutMs))
|
|
40758
40971
|
})) : null;
|
|
40759
40972
|
const scanProgress = yield* progressService.start("Scanning...");
|
|
40760
40973
|
const scanStartTime = Date.now();
|
|
@@ -40779,7 +40992,8 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
40779
40992
|
onCacheStats: (cacheHitFileCount, totalConsideredFileCount) => {
|
|
40780
40993
|
lintCacheHitFileCount = cacheHitFileCount;
|
|
40781
40994
|
lintCacheTotalFileCount = totalConsideredFileCount;
|
|
40782
|
-
}
|
|
40995
|
+
},
|
|
40996
|
+
deadlineEpochMs: input.deadlineEpochMs
|
|
40783
40997
|
}).pipe(catchTag("ReactDoctorError", (error) => unwrap(gen(function* () {
|
|
40784
40998
|
yield* set(lintFailure, {
|
|
40785
40999
|
didFail: true,
|
|
@@ -40806,7 +41020,13 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
40806
41020
|
let deadCodeCollected = [];
|
|
40807
41021
|
if (lintFailureState.didFail) {
|
|
40808
41022
|
if (deadCodeFiber !== null) yield* interrupt(deadCodeFiber);
|
|
40809
|
-
} 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 {
|
|
40810
41030
|
yield* scanProgress.update(`Scanned ${scannedFilesLabel}, analyzing dead code...`);
|
|
40811
41031
|
const sequentialDeadCodeTimeout = resolveDeadCodeTimeout({
|
|
40812
41032
|
sourceFileCount: totalFileCount,
|
|
@@ -40815,7 +41035,7 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
40815
41035
|
});
|
|
40816
41036
|
deadCodeCollected = deadCodeFiber !== null ? yield* join(deadCodeFiber) : yield* buildCollectDeadCode({
|
|
40817
41037
|
workerTimeoutMs: sequentialDeadCodeTimeout.workerTimeoutMs,
|
|
40818
|
-
phaseTimeoutMs: resolveDeadCodePhaseTimeoutMs(sequentialDeadCodeTimeout.phaseTimeoutMs)
|
|
41038
|
+
phaseTimeoutMs: capToDeadline(resolveDeadCodePhaseTimeoutMs(sequentialDeadCodeTimeout.phaseTimeoutMs))
|
|
40819
41039
|
});
|
|
40820
41040
|
}
|
|
40821
41041
|
const deadCodeFailureState = lintFailureState.didFail ? {
|
|
@@ -40829,9 +41049,11 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
40829
41049
|
else yield* scanProgress.succeed(`Scanned ${scannedFilesLabel} in ${scanElapsedSeconds}s${workerCountSuffix}`);
|
|
40830
41050
|
const supplyChainResult = yield* join(supplyChainFiber);
|
|
40831
41051
|
const supplyChainCollected = supplyChainResult.diagnostics;
|
|
41052
|
+
const securityScanCollected = yield* join(securityScanFiber);
|
|
40832
41053
|
yield* reporterService.finalize;
|
|
40833
41054
|
const finalDiagnostics = sortDiagnosticsStable(assignFixGroups([
|
|
40834
41055
|
...envCollected,
|
|
41056
|
+
...securityScanCollected,
|
|
40835
41057
|
...supplyChainCollected,
|
|
40836
41058
|
...lintCollected,
|
|
40837
41059
|
...deadCodeCollected
|
|
@@ -40851,12 +41073,13 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
40851
41073
|
};
|
|
40852
41074
|
const scoreSurface = input.scoreSurface ?? "score";
|
|
40853
41075
|
const scoreDiagnostics = filterDiagnosticsForSurface([...finalDiagnostics], scoreSurface, resolvedConfig.config);
|
|
40854
|
-
const score = lintFailureState.didFail ? null : yield* scoreService.compute({
|
|
41076
|
+
const score = lintFailureState.didFail || deadCodeFailureState.didFail ? null : yield* scoreService.compute({
|
|
40855
41077
|
diagnostics: scoreDiagnostics,
|
|
40856
41078
|
isCi: input.isCi,
|
|
40857
41079
|
metadata: scoreMetadata
|
|
40858
41080
|
});
|
|
40859
41081
|
const lintPartialFailures = yield* get$2(partialFailuresRef);
|
|
41082
|
+
const securityScanFailed = yield* get$2(securityScanFailedRef);
|
|
40860
41083
|
return {
|
|
40861
41084
|
project,
|
|
40862
41085
|
userConfig: resolvedConfig.config,
|
|
@@ -40877,8 +41100,10 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
40877
41100
|
scanElapsedMilliseconds,
|
|
40878
41101
|
scanConcurrency,
|
|
40879
41102
|
supplyChainOverlapTimedOut: supplyChainResult.timedOut,
|
|
41103
|
+
securityScanFailed,
|
|
40880
41104
|
lintCacheHitFileCount,
|
|
40881
|
-
lintCacheTotalFileCount
|
|
41105
|
+
lintCacheTotalFileCount,
|
|
41106
|
+
suppressedRuleCounts: transform.summarizeSuppressions()
|
|
40882
41107
|
};
|
|
40883
41108
|
}).pipe(withSpan("runInspect", { attributes: {
|
|
40884
41109
|
"inspect.directory": input.directory,
|
|
@@ -42903,6 +43128,7 @@ const SENTRY_FLUSH_TIMEOUT_MS = 2e3;
|
|
|
42903
43128
|
const METRIC = {
|
|
42904
43129
|
cliInvoked: "cli.invoked",
|
|
42905
43130
|
cliError: "cli.error",
|
|
43131
|
+
cliEnvironmentError: "cli.env_error",
|
|
42906
43132
|
projectDetected: "project.detected",
|
|
42907
43133
|
projectPathSelected: "project.path_selected",
|
|
42908
43134
|
projectConfigSelected: "project.config_selected",
|
|
@@ -42915,6 +43141,8 @@ const METRIC = {
|
|
|
42915
43141
|
scanCheckSkipped: "scan.check_skipped",
|
|
42916
43142
|
baselineDegraded: "baseline.degraded",
|
|
42917
43143
|
ruleFired: "rule.fired",
|
|
43144
|
+
ruleDisabled: "rule.disabled",
|
|
43145
|
+
ruleSuppressed: "rule.suppressed",
|
|
42918
43146
|
lintFailed: "lint.failed",
|
|
42919
43147
|
deadCodeFailed: "deadcode.failed",
|
|
42920
43148
|
scoreUnavailable: "score.unavailable",
|
|
@@ -42927,12 +43155,17 @@ const METRIC = {
|
|
|
42927
43155
|
installWorkflow: "install.workflow",
|
|
42928
43156
|
installAgentHooks: "install.agent_hooks",
|
|
42929
43157
|
installDependency: "install.dependency",
|
|
43158
|
+
ciScaffolded: "ci.scaffolded",
|
|
43159
|
+
ciUpgraded: "ci.upgraded",
|
|
43160
|
+
ciConfigured: "ci.configured",
|
|
42930
43161
|
rulesChanged: "rules.changed",
|
|
42931
43162
|
rulesQueried: "rules.queried",
|
|
42932
43163
|
lspSessionStarted: "lsp.session.started",
|
|
42933
43164
|
lspScanCompleted: "lsp.scan.completed",
|
|
42934
43165
|
lspScanDuration: "lsp.scan.duration",
|
|
42935
|
-
lspScanDiagnostics: "lsp.scan.diagnostics"
|
|
43166
|
+
lspScanDiagnostics: "lsp.scan.diagnostics",
|
|
43167
|
+
aiTrainingWarningShown: "ai.training.warning_shown",
|
|
43168
|
+
jsonOutUsed: "json.out_used"
|
|
42936
43169
|
};
|
|
42937
43170
|
//#endregion
|
|
42938
43171
|
//#region src/cli/utils/to-category-key.ts
|
|
@@ -43249,5 +43482,5 @@ const startLanguageServer = () => {
|
|
|
43249
43482
|
};
|
|
43250
43483
|
//#endregion
|
|
43251
43484
|
export { startLanguageServer };
|
|
43252
|
-
!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]="
|
|
43253
|
-
//# 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
|