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