react-doctor 0.5.8-dev.c2ce298 → 0.5.8-dev.d241e51
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 +9689 -1592
- package/dist/index.d.ts +11 -2
- package/dist/index.js +393 -145
- package/dist/lsp.js +339 -146
- package/package.json +7 -5
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="
|
|
2
|
+
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="20ae6ec9-b785-55cb-96d6-9c8b9fe3a35b")}catch(e){}}();
|
|
3
3
|
import { r as __toESM$1, t as __commonJSMin$1 } from "./chunk-N93fKeF6.js";
|
|
4
4
|
import { createRequire } from "node:module";
|
|
5
5
|
import * as NFS from "node:fs";
|
|
@@ -7,7 +7,7 @@ import fs from "node:fs";
|
|
|
7
7
|
import * as Path from "node:path";
|
|
8
8
|
import path from "node:path";
|
|
9
9
|
import * as NodeChildProcess from "node:child_process";
|
|
10
|
-
import { spawn, spawnSync } from "node:child_process";
|
|
10
|
+
import { execFileSync, spawn, spawnSync } from "node:child_process";
|
|
11
11
|
import * as ts from "typescript";
|
|
12
12
|
import reactDoctorPlugin, { ALL_REACT_DOCTOR_RULE_KEYS, CROSS_FILE_RULE_IDS, FRAMEWORK_SPECIFIC_RULE_KEYS, MOTION_LIBRARY_PACKAGES, REACT_COMPILER_RULES, REACT_DOCTOR_RULES, classifySecurityScanFile, shouldReadSecurityScanContent } from "oxlint-plugin-react-doctor";
|
|
13
13
|
import * as OS from "node:os";
|
|
@@ -5996,7 +5996,7 @@ const composePassthrough = /* @__PURE__ */ dual(2, (left, right) => (input) => {
|
|
|
5996
5996
|
* @since 2.0.0
|
|
5997
5997
|
*/
|
|
5998
5998
|
const Scheduler = /* @__PURE__ */ Reference("effect/Scheduler", { defaultValue: () => new MixedScheduler() });
|
|
5999
|
-
const setImmediate = "setImmediate" in globalThis ? (f) => {
|
|
5999
|
+
const setImmediate$1 = "setImmediate" in globalThis ? (f) => {
|
|
6000
6000
|
const timer = globalThis.setImmediate(f);
|
|
6001
6001
|
return () => globalThis.clearImmediate(timer);
|
|
6002
6002
|
} : (f) => {
|
|
@@ -6040,7 +6040,7 @@ var PriorityBuckets = class {
|
|
|
6040
6040
|
var MixedScheduler = class {
|
|
6041
6041
|
executionMode;
|
|
6042
6042
|
setImmediate;
|
|
6043
|
-
constructor(executionMode = "async", setImmediateFn = setImmediate) {
|
|
6043
|
+
constructor(executionMode = "async", setImmediateFn = setImmediate$1) {
|
|
6044
6044
|
this.executionMode = executionMode;
|
|
6045
6045
|
this.setImmediate = setImmediateFn;
|
|
6046
6046
|
}
|
|
@@ -6065,7 +6065,7 @@ var MixedSchedulerDispatcher = class {
|
|
|
6065
6065
|
tasks = /* @__PURE__ */ new PriorityBuckets();
|
|
6066
6066
|
running = void 0;
|
|
6067
6067
|
setImmediate;
|
|
6068
|
-
constructor(setImmediateFn = setImmediate) {
|
|
6068
|
+
constructor(setImmediateFn = setImmediate$1) {
|
|
6069
6069
|
this.setImmediate = setImmediateFn;
|
|
6070
6070
|
}
|
|
6071
6071
|
/**
|
|
@@ -33694,6 +33694,10 @@ const MILLISECONDS_PER_SECOND = 1e3;
|
|
|
33694
33694
|
const SCORE_API_URL = "https://www.react.doctor/api/score";
|
|
33695
33695
|
const FETCH_TIMEOUT_MS = 1e4;
|
|
33696
33696
|
const GITHUB_VIEWER_PERMISSION_TIMEOUT_MS = 2e3;
|
|
33697
|
+
const SPAWN_ARGS_MAX_LENGTH_CHARS = 24e3;
|
|
33698
|
+
const SPAWN_ARGS_MAX_LENGTH_CHARS_POSIX = 15e5;
|
|
33699
|
+
const SPAWN_ARGS_MAX_LENGTH_CHARS_DARWIN = 8e5;
|
|
33700
|
+
const NODE_VERSION_PROBE_TIMEOUT_MS = 5e3;
|
|
33697
33701
|
const PER_WORKER_MEM_BUDGET_BYTES = 1024 * 1024 * 1024;
|
|
33698
33702
|
const DEFAULT_BRANCH_CANDIDATES = ["main", "master"];
|
|
33699
33703
|
const ADOPTABLE_LINT_CONFIG_FILENAMES = [".oxlintrc.json", ".eslintrc.json"];
|
|
@@ -33751,6 +33755,7 @@ const DEAD_CODE_PHASE_TIMEOUT_MS = 15e4;
|
|
|
33751
33755
|
const LINT_PHASE_TIMEOUT_MS = 3e5;
|
|
33752
33756
|
const SCAN_TOTAL_DEADLINE_MS = 9e5;
|
|
33753
33757
|
const DEAD_CODE_WORKER_MAX_OLD_SPACE_MB = 8192;
|
|
33758
|
+
const DEAD_CODE_WORKER_MEM_BUDGET_BYTES = 2 * 1024 * 1024 * 1024;
|
|
33754
33759
|
const DEAD_CODE_TIMEOUT_CEILING_MS = 6e5;
|
|
33755
33760
|
const DEAD_CODE_PHASE_TIMEOUT_OVER_WORKER_MS = 3e4;
|
|
33756
33761
|
const DEAD_CODE_OVERLAP_PARSE_SHARE = .4;
|
|
@@ -34517,7 +34522,7 @@ const tokenizeRuleList = (ruleList) => {
|
|
|
34517
34522
|
const FOREIGN_INLINE_DISABLE_PATTERN = /(?:\/\/|\/\*)[ \t]*(eslint|oxlint)-disable-(next-line|line)(?![\w-])([^\r\n]*)/;
|
|
34518
34523
|
const FOREIGN_BLOCK_DISABLE_PATTERN = /\/\*[ \t]*(eslint|oxlint)-disable(?![\w-])([^*\r\n]*)/;
|
|
34519
34524
|
const FOREIGN_BLOCK_ENABLE_PATTERN = /\/\*[ \t]*(?:eslint|oxlint)-enable(?![\w-])([^*\r\n]*)/;
|
|
34520
|
-
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}\` —
|
|
34525
|
+
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}\`).`;
|
|
34521
34526
|
const tokenMisnamesRule = (token, ruleId) => token !== ruleId && isSameRuleKey(token, ruleId);
|
|
34522
34527
|
const detectInlineNearMiss = (lines, diagnosticLineIndex, ruleId) => {
|
|
34523
34528
|
const candidates = [{
|
|
@@ -34997,6 +35002,7 @@ const collectStringSet = (values) => {
|
|
|
34997
35002
|
if (!Array.isArray(values)) return /* @__PURE__ */ new Set();
|
|
34998
35003
|
return new Set(values.filter((value) => typeof value === "string"));
|
|
34999
35004
|
};
|
|
35005
|
+
const JSX_NO_UNDEF_IDENTIFIER_PATTERN = /^`([^`]+)`/;
|
|
35000
35006
|
/**
|
|
35001
35007
|
* Pre-compiles every stateful filter and returns a single
|
|
35002
35008
|
* `apply(diagnostic)` closure that runs (in order):
|
|
@@ -35030,11 +35036,28 @@ const buildDiagnosticPipeline = (input) => {
|
|
|
35030
35036
|
const compiledOverrides = compileIgnoreOverrides(userConfig);
|
|
35031
35037
|
const textComponentNames = collectStringSet(userConfig?.textComponents);
|
|
35032
35038
|
const rawTextWrapperComponentNames = collectStringSet(userConfig?.rawTextWrapperComponents);
|
|
35039
|
+
const runtimeGlobalNames = collectStringSet(userConfig?.runtimeGlobals);
|
|
35033
35040
|
const hasTextComponents = textComponentNames.size > 0;
|
|
35034
35041
|
const hasRawTextWrappers = rawTextWrapperComponentNames.size > 0;
|
|
35042
|
+
const hasRuntimeGlobals = runtimeGlobalNames.size > 0;
|
|
35035
35043
|
const fileLinesCache = /* @__PURE__ */ new Map();
|
|
35036
35044
|
const fileContextCache = /* @__PURE__ */ new Map();
|
|
35037
35045
|
const libraryFileCache = /* @__PURE__ */ new Map();
|
|
35046
|
+
const suppressions = /* @__PURE__ */ new Map();
|
|
35047
|
+
const suppress = (diagnostic, source) => {
|
|
35048
|
+
const { ruleKey } = getDiagnosticRuleIdentity(diagnostic);
|
|
35049
|
+
const suppressionKey = `${ruleKey}\u0000${source}`;
|
|
35050
|
+
const existing = suppressions.get(suppressionKey);
|
|
35051
|
+
suppressions.set(suppressionKey, existing ? {
|
|
35052
|
+
...existing,
|
|
35053
|
+
count: existing.count + 1
|
|
35054
|
+
} : {
|
|
35055
|
+
rule: ruleKey,
|
|
35056
|
+
source,
|
|
35057
|
+
count: 1
|
|
35058
|
+
});
|
|
35059
|
+
return null;
|
|
35060
|
+
};
|
|
35038
35061
|
const isLibraryFile = (filePath) => {
|
|
35039
35062
|
let cached = libraryFileCache.get(filePath);
|
|
35040
35063
|
if (cached === void 0) {
|
|
@@ -35083,50 +35106,60 @@ const buildDiagnosticPipeline = (input) => {
|
|
|
35083
35106
|
if (hasRawTextWrappers && isInsideStringOnlyWrapper(lines, diagnostic.line, diagnostic.column, rawTextWrapperComponentNames)) return true;
|
|
35084
35107
|
return false;
|
|
35085
35108
|
};
|
|
35086
|
-
|
|
35087
|
-
if (
|
|
35088
|
-
|
|
35089
|
-
|
|
35090
|
-
|
|
35091
|
-
|
|
35092
|
-
|
|
35093
|
-
|
|
35094
|
-
|
|
35095
|
-
|
|
35096
|
-
|
|
35097
|
-
|
|
35098
|
-
if (
|
|
35099
|
-
|
|
35100
|
-
|
|
35101
|
-
|
|
35102
|
-
|
|
35103
|
-
|
|
35104
|
-
|
|
35105
|
-
|
|
35106
|
-
|
|
35107
|
-
if (isFileIgnoredByPatterns(current.filePath, rootDirectory, ignoredFilePatterns)) return null;
|
|
35108
|
-
if (isDiagnosticIgnoredByOverrides(current, rootDirectory, compiledOverrides)) return null;
|
|
35109
|
-
if (isRnRawTextSuppressedByConfig(current)) return null;
|
|
35110
|
-
}
|
|
35111
|
-
if (respectInlineDisables && current.line > 0) {
|
|
35112
|
-
const lines = getFileLines(current.filePath);
|
|
35113
|
-
if (lines) {
|
|
35114
|
-
const ruleIdentifier = `${current.plugin}/${current.rule}`;
|
|
35115
|
-
const evaluation = evaluateSuppression(lines, current.line - 1, ruleIdentifier);
|
|
35116
|
-
if (evaluation.isSuppressed) return null;
|
|
35117
|
-
if (evaluation.nearMissHint) current = {
|
|
35118
|
-
...current,
|
|
35119
|
-
suppressionHint: evaluation.nearMissHint
|
|
35120
|
-
};
|
|
35109
|
+
const isJsxNoUndefSuppressedByConfig = (diagnostic) => {
|
|
35110
|
+
if (!hasRuntimeGlobals) return false;
|
|
35111
|
+
if (diagnostic.rule !== "jsx-no-undef") return false;
|
|
35112
|
+
const identifierMatch = JSX_NO_UNDEF_IDENTIFIER_PATTERN.exec(diagnostic.message);
|
|
35113
|
+
return identifierMatch !== null && runtimeGlobalNames.has(identifierMatch[1]);
|
|
35114
|
+
};
|
|
35115
|
+
return {
|
|
35116
|
+
apply: (diagnostic) => {
|
|
35117
|
+
if (shouldAutoSuppress(diagnostic)) return null;
|
|
35118
|
+
let current = diagnostic;
|
|
35119
|
+
let explicitSeverityOverride;
|
|
35120
|
+
let explicitRuleOverride;
|
|
35121
|
+
if (severityControls) {
|
|
35122
|
+
const { ruleKey, category } = getDiagnosticRuleIdentity(current);
|
|
35123
|
+
explicitRuleOverride = resolveRuleSeverityOverride({ ruleKey }, severityControls);
|
|
35124
|
+
explicitSeverityOverride = resolveRuleSeverityOverride({
|
|
35125
|
+
ruleKey,
|
|
35126
|
+
category
|
|
35127
|
+
}, severityControls);
|
|
35128
|
+
if (explicitSeverityOverride === "off") return suppress(current, "config");
|
|
35129
|
+
if (explicitSeverityOverride !== void 0) current = restampSeverity(current, explicitSeverityOverride);
|
|
35121
35130
|
}
|
|
35122
|
-
|
|
35123
|
-
|
|
35124
|
-
|
|
35125
|
-
|
|
35126
|
-
|
|
35127
|
-
|
|
35128
|
-
|
|
35129
|
-
|
|
35131
|
+
if (explicitRuleOverride === void 0) {
|
|
35132
|
+
if (isAppOnlyRule(`${current.plugin}/${current.rule}`) && isLibraryFile(current.filePath)) return null;
|
|
35133
|
+
}
|
|
35134
|
+
if (!showWarnings && current.severity === "warning" && explicitSeverityOverride !== "warn") return null;
|
|
35135
|
+
if (userConfig) {
|
|
35136
|
+
if (isRuleIgnored(`${current.plugin}/${current.rule}`)) return suppress(current, "config");
|
|
35137
|
+
if (isFileIgnoredByPatterns(current.filePath, rootDirectory, ignoredFilePatterns)) return null;
|
|
35138
|
+
if (isDiagnosticIgnoredByOverrides(current, rootDirectory, compiledOverrides)) return suppress(current, "override");
|
|
35139
|
+
if (isRnRawTextSuppressedByConfig(current)) return null;
|
|
35140
|
+
if (isJsxNoUndefSuppressedByConfig(current)) return null;
|
|
35141
|
+
}
|
|
35142
|
+
if (respectInlineDisables && current.line > 0) {
|
|
35143
|
+
const lines = getFileLines(current.filePath);
|
|
35144
|
+
if (lines) {
|
|
35145
|
+
const ruleIdentifier = `${current.plugin}/${current.rule}`;
|
|
35146
|
+
const evaluation = evaluateSuppression(lines, current.line - 1, ruleIdentifier);
|
|
35147
|
+
if (evaluation.isSuppressed) return suppress(current, "inline");
|
|
35148
|
+
if (evaluation.nearMissHint) current = {
|
|
35149
|
+
...current,
|
|
35150
|
+
suppressionHint: evaluation.nearMissHint
|
|
35151
|
+
};
|
|
35152
|
+
}
|
|
35153
|
+
}
|
|
35154
|
+
const fileContext = getFileContext(current.filePath);
|
|
35155
|
+
if (fileContext !== "production") current = {
|
|
35156
|
+
...current,
|
|
35157
|
+
fileContext
|
|
35158
|
+
};
|
|
35159
|
+
return current;
|
|
35160
|
+
},
|
|
35161
|
+
summarizeSuppressions: () => [...suppressions.values()]
|
|
35162
|
+
};
|
|
35130
35163
|
};
|
|
35131
35164
|
const OxlintUnavailableKind = Literals(["binary-not-found", "native-binding-missing"]);
|
|
35132
35165
|
var OxlintUnavailable = class extends TaggedErrorClass()("OxlintUnavailable", {
|
|
@@ -35368,7 +35401,7 @@ const resolveScanConcurrency = (requested) => {
|
|
|
35368
35401
|
if (!Number.isFinite(requested) || requested < 1) return 1;
|
|
35369
35402
|
return Math.min(Math.floor(requested), 32);
|
|
35370
35403
|
};
|
|
35371
|
-
const readSystemFacts = () => ({
|
|
35404
|
+
const readSystemFacts$1 = () => ({
|
|
35372
35405
|
availableCores: os.availableParallelism(),
|
|
35373
35406
|
totalMemoryBytes: os.totalmem(),
|
|
35374
35407
|
cgroupMemoryLimitBytes: readCgroupMemoryLimitBytes()
|
|
@@ -35389,7 +35422,7 @@ const readSystemFacts = () => ({
|
|
|
35389
35422
|
* `facts` is injectable so tests exercise core-bound, memory-bound, cgroup-
|
|
35390
35423
|
* limited, and ceiling cases without mocking `os` or the filesystem.
|
|
35391
35424
|
*/
|
|
35392
|
-
const resolveAutoScanConcurrency = (facts = readSystemFacts()) => {
|
|
35425
|
+
const resolveAutoScanConcurrency = (facts = readSystemFacts$1()) => {
|
|
35393
35426
|
const availableMemoryBytes = Math.min(facts.totalMemoryBytes, facts.cgroupMemoryLimitBytes ?? Number.POSITIVE_INFINITY);
|
|
35394
35427
|
const memoryBoundedWorkers = Math.floor(availableMemoryBytes / PER_WORKER_MEM_BUDGET_BYTES);
|
|
35395
35428
|
return resolveScanConcurrency(Math.min(facts.availableCores, memoryBoundedWorkers));
|
|
@@ -35430,13 +35463,7 @@ var ScanDeadlineMs = class extends Reference("react-doctor/ScanDeadlineMs", { de
|
|
|
35430
35463
|
* without recompiling react-doctor. Tests override via
|
|
35431
35464
|
* `Layer.succeed(SupplyChainOverlapTimeoutMs, ...)`.
|
|
35432
35465
|
*/
|
|
35433
|
-
var SupplyChainOverlapTimeoutMs = class extends Reference("react-doctor/SupplyChainOverlapTimeoutMs", { defaultValue: () => {
|
|
35434
|
-
const raw = process.env["REACT_DOCTOR_SUPPLY_CHAIN_TIMEOUT_MS"];
|
|
35435
|
-
if (raw === void 0) return SUPPLY_CHAIN_OVERLAP_TIMEOUT_MS;
|
|
35436
|
-
const parsed = Number(raw);
|
|
35437
|
-
if (!Number.isFinite(parsed) || parsed <= 0) return SUPPLY_CHAIN_OVERLAP_TIMEOUT_MS;
|
|
35438
|
-
return parsed;
|
|
35439
|
-
} }) {};
|
|
35466
|
+
var SupplyChainOverlapTimeoutMs = class extends Reference("react-doctor/SupplyChainOverlapTimeoutMs", { defaultValue: () => readPositiveEnvMs("REACT_DOCTOR_SUPPLY_CHAIN_TIMEOUT_MS", SUPPLY_CHAIN_OVERLAP_TIMEOUT_MS) }) {};
|
|
35440
35467
|
/**
|
|
35441
35468
|
* Hard cap on combined stdout+stderr bytes per oxlint batch. The
|
|
35442
35469
|
* subprocess gets SIGKILL'd if it produces more; the recovery path
|
|
@@ -35580,6 +35607,13 @@ const BOOLEAN_FIELD_NAMES = [
|
|
|
35580
35607
|
"adoptExistingLintConfig"
|
|
35581
35608
|
];
|
|
35582
35609
|
const STRING_FIELD_NAMES = ["rootDir"];
|
|
35610
|
+
const STRING_ARRAY_FIELD_NAMES = [
|
|
35611
|
+
"projects",
|
|
35612
|
+
"textComponents",
|
|
35613
|
+
"rawTextWrapperComponents",
|
|
35614
|
+
"runtimeGlobals",
|
|
35615
|
+
"serverAuthFunctionNames"
|
|
35616
|
+
];
|
|
35583
35617
|
const SURFACE_CONTROL_FIELD_NAMES = [
|
|
35584
35618
|
"includeTags",
|
|
35585
35619
|
"excludeTags",
|
|
@@ -35681,6 +35715,7 @@ const validateConfigTypes = (config) => {
|
|
|
35681
35715
|
const validated = { ...config };
|
|
35682
35716
|
for (const fieldName of BOOLEAN_FIELD_NAMES) applyFieldValidator(config, validated, fieldName, (value) => coerceMaybeBooleanString(fieldName, value));
|
|
35683
35717
|
for (const fieldName of STRING_FIELD_NAMES) applyFieldValidator(config, validated, fieldName, (value) => validateString(fieldName, value));
|
|
35718
|
+
for (const fieldName of STRING_ARRAY_FIELD_NAMES) applyFieldValidator(config, validated, fieldName, (value) => validateStringArrayField(fieldName, value));
|
|
35684
35719
|
applyFieldValidator(config, validated, "surfaces", validateSurfacesField);
|
|
35685
35720
|
for (const fieldName of SEVERITY_FIELD_NAMES) applyFieldValidator(config, validated, fieldName, (value) => validateSeverityMap(fieldName, value, fieldName === "categories"));
|
|
35686
35721
|
applyFieldValidator(config, validated, "plugins", (value) => validateStringArrayField("plugins", value));
|
|
@@ -36915,7 +36950,10 @@ const shouldEnableRule = (requires, tags, capabilities, ignoredTags, disabledBy)
|
|
|
36915
36950
|
}
|
|
36916
36951
|
return true;
|
|
36917
36952
|
};
|
|
36918
|
-
const
|
|
36953
|
+
const yieldToEventLoop = () => new Promise((resolve) => {
|
|
36954
|
+
setImmediate(resolve);
|
|
36955
|
+
});
|
|
36956
|
+
const createSecurityScanSession = (rootDirectory, options) => {
|
|
36919
36957
|
const capabilities = options.project ? buildCapabilities(options.project) : /* @__PURE__ */ new Set();
|
|
36920
36958
|
const ignoredTags = options.ignoredTags ?? /* @__PURE__ */ new Set();
|
|
36921
36959
|
const enabledScanRules = REACT_DOCTOR_RULES.flatMap((entry) => {
|
|
@@ -36930,7 +36968,7 @@ const checkSecurityScan = (rootDirectory, options = {}) => {
|
|
|
36930
36968
|
committedFilesOnly: rule.committedFilesOnly === true
|
|
36931
36969
|
}];
|
|
36932
36970
|
});
|
|
36933
|
-
if (enabledScanRules.length === 0) return
|
|
36971
|
+
if (enabledScanRules.length === 0) return null;
|
|
36934
36972
|
const diagnostics = [];
|
|
36935
36973
|
const seen = /* @__PURE__ */ new Set();
|
|
36936
36974
|
const gitIgnoredCache = /* @__PURE__ */ new Map();
|
|
@@ -36942,15 +36980,34 @@ const checkSecurityScan = (rootDirectory, options = {}) => {
|
|
|
36942
36980
|
}
|
|
36943
36981
|
return status === true;
|
|
36944
36982
|
};
|
|
36945
|
-
|
|
36946
|
-
|
|
36947
|
-
|
|
36948
|
-
|
|
36949
|
-
|
|
36950
|
-
|
|
36951
|
-
|
|
36983
|
+
const scanFile = (file) => {
|
|
36984
|
+
for (const { entry, scan, committedFilesOnly } of enabledScanRules) for (const finding of scan(file)) {
|
|
36985
|
+
if (committedFilesOnly && isFileGitIgnored(file)) continue;
|
|
36986
|
+
const diagnostic = buildSecurityScanDiagnostic(finding, entry, file.relativePath);
|
|
36987
|
+
const key = `${diagnostic.rule}:${diagnostic.filePath}:${diagnostic.line}:${diagnostic.column}:${diagnostic.message}`;
|
|
36988
|
+
if (seen.has(key)) continue;
|
|
36989
|
+
seen.add(key);
|
|
36990
|
+
diagnostics.push(diagnostic);
|
|
36991
|
+
}
|
|
36992
|
+
};
|
|
36993
|
+
return {
|
|
36994
|
+
scanFile,
|
|
36995
|
+
diagnostics
|
|
36996
|
+
};
|
|
36997
|
+
};
|
|
36998
|
+
const checkSecurityScanCooperative = async (rootDirectory, options = {}) => {
|
|
36999
|
+
const session = createSecurityScanSession(rootDirectory, options);
|
|
37000
|
+
if (session === null) return [];
|
|
37001
|
+
let filesSinceYield = 0;
|
|
37002
|
+
for (const file of collectSecurityScanFiles(rootDirectory)) {
|
|
37003
|
+
session.scanFile(file);
|
|
37004
|
+
filesSinceYield += 1;
|
|
37005
|
+
if (filesSinceYield >= 16) {
|
|
37006
|
+
filesSinceYield = 0;
|
|
37007
|
+
await yieldToEventLoop();
|
|
37008
|
+
}
|
|
36952
37009
|
}
|
|
36953
|
-
return diagnostics;
|
|
37010
|
+
return session.diagnostics;
|
|
36954
37011
|
};
|
|
36955
37012
|
var import_picocolors = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
36956
37013
|
let p = process || {}, argv = p.argv || [], env = p.env || {};
|
|
@@ -37167,6 +37224,74 @@ const collectDeadCodeIgnorePatterns = (rootDirectory) => {
|
|
|
37167
37224
|
return [...seen].filter((pattern) => pattern.length > 0);
|
|
37168
37225
|
};
|
|
37169
37226
|
const collectDeadCodeEntryPatterns = (rootDirectory) => [...new Set(collectKnipPatterns(rootDirectory, "entry"))].filter((pattern) => pattern.length > 0);
|
|
37227
|
+
const readSystemFacts = () => ({
|
|
37228
|
+
availableCores: os.availableParallelism(),
|
|
37229
|
+
totalMemoryBytes: os.totalmem(),
|
|
37230
|
+
cgroupMemoryLimitBytes: readCgroupMemoryLimitBytes()
|
|
37231
|
+
});
|
|
37232
|
+
/**
|
|
37233
|
+
* How many real deslop dead-code child processes may run at once, across the
|
|
37234
|
+
* concurrent per-project `runInspect` fibers of one CLI run. The cap is the
|
|
37235
|
+
* smaller of the core count and the number of `DEAD_CODE_WORKER_MEM_BUDGET_BYTES`
|
|
37236
|
+
* workers that fit in available memory, floored at 1.
|
|
37237
|
+
*
|
|
37238
|
+
* On a roomy dev box / CI runner this resolves high enough that every
|
|
37239
|
+
* concurrently-scanned project still spawns its own worker (no serialization vs
|
|
37240
|
+
* the prior uncapped behavior); on a memory-constrained runner it collapses
|
|
37241
|
+
* toward 1, so the `withDeadCodeWorkerSlot` semaphore serializes the spawns
|
|
37242
|
+
* instead of oversubscribing memory with N simultaneous children — the global
|
|
37243
|
+
* cap the per-project spawn path lacked.
|
|
37244
|
+
*
|
|
37245
|
+
* Mirrors `resolveAutoScanConcurrency` (lint), but budgets memory per the
|
|
37246
|
+
* heavier dead-code worker. `facts` is injectable for tests.
|
|
37247
|
+
*/
|
|
37248
|
+
const resolveDeadCodeConcurrency = (facts = readSystemFacts()) => {
|
|
37249
|
+
const availableMemoryBytes = Math.min(facts.totalMemoryBytes, facts.cgroupMemoryLimitBytes ?? Number.POSITIVE_INFINITY);
|
|
37250
|
+
const memoryBoundedWorkers = Math.floor(availableMemoryBytes / DEAD_CODE_WORKER_MEM_BUDGET_BYTES);
|
|
37251
|
+
return Math.max(1, Math.min(facts.availableCores, memoryBoundedWorkers));
|
|
37252
|
+
};
|
|
37253
|
+
let availableSlots = -1;
|
|
37254
|
+
const waiters = [];
|
|
37255
|
+
const releaseSlot = () => {
|
|
37256
|
+
const nextWaiter = waiters.shift();
|
|
37257
|
+
if (nextWaiter !== void 0) nextWaiter();
|
|
37258
|
+
else availableSlots += 1;
|
|
37259
|
+
};
|
|
37260
|
+
/**
|
|
37261
|
+
* Runs `task` once a dead-code worker slot is free, releasing the slot when the
|
|
37262
|
+
* task settles (success or failure). With a high cap (roomy machine) every
|
|
37263
|
+
* caller proceeds immediately; with a low cap (constrained runner) callers
|
|
37264
|
+
* queue and run as slots free.
|
|
37265
|
+
*
|
|
37266
|
+
* `abortSignal` short-circuits the WAIT: if it's already aborted, or fires while
|
|
37267
|
+
* this caller is queued, the call rejects without acquiring a slot or running
|
|
37268
|
+
* `task` — so a cancelled scan (e.g. lint failed) doesn't sit in the queue and
|
|
37269
|
+
* then spawn a child only to tear it down. A queued caller that aborts removes
|
|
37270
|
+
* its own waiter so a later release never hands a slot to a dead request.
|
|
37271
|
+
*/
|
|
37272
|
+
const withDeadCodeWorkerSlot = async (task, abortSignal) => {
|
|
37273
|
+
if (abortSignal?.aborted) throw new Error("Dead-code worker aborted.");
|
|
37274
|
+
if (availableSlots < 0) availableSlots = resolveDeadCodeConcurrency();
|
|
37275
|
+
if (availableSlots > 0) availableSlots -= 1;
|
|
37276
|
+
else await new Promise((resolve, reject) => {
|
|
37277
|
+
const waiter = () => {
|
|
37278
|
+
abortSignal?.removeEventListener("abort", onAbort);
|
|
37279
|
+
resolve();
|
|
37280
|
+
};
|
|
37281
|
+
const onAbort = () => {
|
|
37282
|
+
const queuedIndex = waiters.indexOf(waiter);
|
|
37283
|
+
if (queuedIndex !== -1) waiters.splice(queuedIndex, 1);
|
|
37284
|
+
reject(/* @__PURE__ */ new Error("Dead-code worker aborted."));
|
|
37285
|
+
};
|
|
37286
|
+
waiters.push(waiter);
|
|
37287
|
+
abortSignal?.addEventListener("abort", onAbort, { once: true });
|
|
37288
|
+
});
|
|
37289
|
+
try {
|
|
37290
|
+
return await task();
|
|
37291
|
+
} finally {
|
|
37292
|
+
releaseSlot();
|
|
37293
|
+
}
|
|
37294
|
+
};
|
|
37170
37295
|
/**
|
|
37171
37296
|
* Resolves a path to its canonical, symlink-free form, falling back to
|
|
37172
37297
|
* the input when it cannot be realpath'd (broken symlink, permission
|
|
@@ -37191,6 +37316,11 @@ const toCanonicalPath = (filePath) => {
|
|
|
37191
37316
|
};
|
|
37192
37317
|
const DEAD_CODE_PLUGIN = "deslop";
|
|
37193
37318
|
const DEAD_CODE_CATEGORY = "Maintainability";
|
|
37319
|
+
const REACT_DOCTOR_TOOLCHAIN_PACKAGES = new Set([
|
|
37320
|
+
"react-doctor",
|
|
37321
|
+
"eslint-plugin-react-doctor",
|
|
37322
|
+
"oxlint-plugin-react-doctor"
|
|
37323
|
+
]);
|
|
37194
37324
|
const DEAD_CODE_WORKER_SCRIPT = `
|
|
37195
37325
|
const inputChunks = [];
|
|
37196
37326
|
process.stdin.on("data", (chunk) => inputChunks.push(chunk));
|
|
@@ -37457,14 +37587,17 @@ const checkDeadCode = async (options) => {
|
|
|
37457
37587
|
if (!NFS.existsSync(Path.join(rootDirectory, "package.json"))) return [];
|
|
37458
37588
|
const entryPatterns = collectDeadCodeEntryPatterns(rootDirectory);
|
|
37459
37589
|
const ignorePatterns = collectDeadCodeIgnorePatterns(rootDirectory);
|
|
37460
|
-
const
|
|
37461
|
-
|
|
37462
|
-
|
|
37463
|
-
|
|
37464
|
-
|
|
37465
|
-
|
|
37466
|
-
|
|
37467
|
-
|
|
37590
|
+
const spawnAndRun = () => {
|
|
37591
|
+
return runDeadCodeWorkerWithTimeout((options.createWorker ?? createDeadCodeWorker)({
|
|
37592
|
+
rootDirectory,
|
|
37593
|
+
entryPatterns,
|
|
37594
|
+
tsConfigPath: resolveTsConfigPath(rootDirectory),
|
|
37595
|
+
ignorePatterns,
|
|
37596
|
+
deslopJsModuleSpecifier: options.deslopJsModuleSpecifier ?? import.meta.resolve("deslop-js"),
|
|
37597
|
+
parseConcurrency: options.parseConcurrency
|
|
37598
|
+
}), options.workerTimeoutMs ?? 12e4, options.abortSignal);
|
|
37599
|
+
};
|
|
37600
|
+
const result = parseDeadCodeWorkerResult(options.createWorker === void 0 ? await withDeadCodeWorkerSlot(spawnAndRun, options.abortSignal) : await spawnAndRun());
|
|
37468
37601
|
const toRelative = (filePath) => toRelativeFilePath(rootDirectory, filePath);
|
|
37469
37602
|
const diagnostics = [];
|
|
37470
37603
|
for (const unusedFile of result.unusedFiles) diagnostics.push({
|
|
@@ -37493,6 +37626,7 @@ const checkDeadCode = async (options) => {
|
|
|
37493
37626
|
});
|
|
37494
37627
|
}
|
|
37495
37628
|
for (const unusedDependency of result.unusedDependencies) {
|
|
37629
|
+
if (REACT_DOCTOR_TOOLCHAIN_PACKAGES.has(unusedDependency.name)) continue;
|
|
37496
37630
|
const label = unusedDependency.isDevDependency ? "devDependency" : "dependency";
|
|
37497
37631
|
diagnostics.push({
|
|
37498
37632
|
filePath: "package.json",
|
|
@@ -37671,7 +37805,6 @@ var DeadCode = class DeadCode extends Service()("react-doctor/DeadCode") {
|
|
|
37671
37805
|
return yield* tryPromise({
|
|
37672
37806
|
try: (signal) => checkDeadCode({
|
|
37673
37807
|
rootDirectory: input.rootDirectory,
|
|
37674
|
-
userConfig: input.userConfig,
|
|
37675
37808
|
parseConcurrency: input.parseConcurrency,
|
|
37676
37809
|
workerTimeoutMs: input.workerTimeoutMs,
|
|
37677
37810
|
abortSignal: signal
|
|
@@ -37794,6 +37927,11 @@ const isSafeGitRevision = (candidate) => {
|
|
|
37794
37927
|
if (candidate.includes("..") || candidate.includes("@{")) return false;
|
|
37795
37928
|
return SAFE_GIT_REVISION_PATTERN.test(candidate);
|
|
37796
37929
|
};
|
|
37930
|
+
const resolveSpawnArgsLengthCap = () => {
|
|
37931
|
+
if (process.platform === "win32") return SPAWN_ARGS_MAX_LENGTH_CHARS;
|
|
37932
|
+
if (process.platform === "darwin") return SPAWN_ARGS_MAX_LENGTH_CHARS_DARWIN;
|
|
37933
|
+
return SPAWN_ARGS_MAX_LENGTH_CHARS_POSIX;
|
|
37934
|
+
};
|
|
37797
37935
|
/**
|
|
37798
37936
|
* Splits a git revision range into its endpoints: three-dot `A...B`
|
|
37799
37937
|
* (symmetric, merge-base) or two-dot `A..B` (direct). Returns `null`
|
|
@@ -37865,43 +38003,47 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
37865
38003
|
* reason: GitInvocationFailed })` so the rest of the codebase
|
|
37866
38004
|
* sees a single failure channel.
|
|
37867
38005
|
*/
|
|
37868
|
-
const runCommand = (input) =>
|
|
37869
|
-
const
|
|
37870
|
-
cwd: input.directory,
|
|
37871
|
-
env: input.env,
|
|
37872
|
-
extendEnv: true
|
|
37873
|
-
}));
|
|
37874
|
-
const maxStdoutBytes = input.maxStdoutBytes;
|
|
37875
|
-
const stdoutByteCount = yield* make$13(0);
|
|
37876
|
-
const [stdout, stderr, status] = yield* all([
|
|
37877
|
-
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({
|
|
37878
|
-
args: [...input.args],
|
|
37879
|
-
directory: input.directory,
|
|
37880
|
-
cause: /* @__PURE__ */ new Error(`git stdout exceeded ${maxStdoutBytes} bytes`)
|
|
37881
|
-
}) })) : void_)))))),
|
|
37882
|
-
mkString(decodeText(handle.stderr)),
|
|
37883
|
-
handle.exitCode
|
|
37884
|
-
], { concurrency: 3 });
|
|
37885
|
-
return {
|
|
37886
|
-
status,
|
|
37887
|
-
stdout,
|
|
37888
|
-
stderr
|
|
37889
|
-
};
|
|
37890
|
-
})).pipe(catchTag$1("PlatformError", (cause) => {
|
|
37891
|
-
if (input.command !== "git") return succeed$2({
|
|
38006
|
+
const runCommand = (input) => {
|
|
38007
|
+
const foldSpawnFailure = (cause) => input.command !== "git" ? succeed$2({
|
|
37892
38008
|
status: 127,
|
|
37893
38009
|
stdout: "",
|
|
37894
38010
|
stderr: String(cause)
|
|
37895
|
-
})
|
|
37896
|
-
return new ReactDoctorError({ reason: new GitInvocationFailed({
|
|
38011
|
+
}) : fail$4(new ReactDoctorError({ reason: new GitInvocationFailed({
|
|
37897
38012
|
args: [...input.args],
|
|
37898
38013
|
directory: input.directory,
|
|
37899
38014
|
cause
|
|
37900
|
-
}) });
|
|
37901
|
-
|
|
37902
|
-
|
|
37903
|
-
|
|
37904
|
-
|
|
38015
|
+
}) }));
|
|
38016
|
+
return scoped(gen(function* () {
|
|
38017
|
+
if (!isDirectory(input.directory)) return yield* foldSpawnFailure(`spawn ENOTDIR (cwd is not a directory: ${input.directory})`);
|
|
38018
|
+
const argvLengthChars = input.command.length + 1 + input.args.reduce((total, arg) => total + arg.length + 1, 0);
|
|
38019
|
+
const spawnArgsLengthCap = resolveSpawnArgsLengthCap();
|
|
38020
|
+
if (argvLengthChars > spawnArgsLengthCap) return yield* foldSpawnFailure(`spawn ENAMETOOLONG (${argvLengthChars} argv chars exceed ${spawnArgsLengthCap})`);
|
|
38021
|
+
const handle = yield* spawner.spawn(make$1(input.command, [...input.args], {
|
|
38022
|
+
cwd: input.directory,
|
|
38023
|
+
env: input.env,
|
|
38024
|
+
extendEnv: true
|
|
38025
|
+
}));
|
|
38026
|
+
const maxStdoutBytes = input.maxStdoutBytes;
|
|
38027
|
+
const stdoutByteCount = yield* make$13(0);
|
|
38028
|
+
const [stdout, stderr, status] = yield* all([
|
|
38029
|
+
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({
|
|
38030
|
+
args: [...input.args],
|
|
38031
|
+
directory: input.directory,
|
|
38032
|
+
cause: /* @__PURE__ */ new Error(`git stdout exceeded ${maxStdoutBytes} bytes`)
|
|
38033
|
+
}) })) : void_)))))),
|
|
38034
|
+
mkString(decodeText(handle.stderr)),
|
|
38035
|
+
handle.exitCode
|
|
38036
|
+
], { concurrency: 3 });
|
|
38037
|
+
return {
|
|
38038
|
+
status,
|
|
38039
|
+
stdout,
|
|
38040
|
+
stderr
|
|
38041
|
+
};
|
|
38042
|
+
})).pipe(catchTag$1("PlatformError", foldSpawnFailure), withSpan("git.exec", { attributes: {
|
|
38043
|
+
"git.command": input.command,
|
|
38044
|
+
"git.subcommand": input.args[0] ?? ""
|
|
38045
|
+
} }));
|
|
38046
|
+
};
|
|
37905
38047
|
const runGit = (directory, args) => runCommand({
|
|
37906
38048
|
command: "git",
|
|
37907
38049
|
args,
|
|
@@ -37934,7 +38076,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
37934
38076
|
"rev-parse",
|
|
37935
38077
|
"--verify",
|
|
37936
38078
|
branch
|
|
37937
|
-
]).pipe(map$3((result) => result.status === 0));
|
|
38079
|
+
]).pipe(map$3((result) => result.status === 0), catch_$1((error) => error.reason._tag === "GitInvocationFailed" ? succeed$2(false) : fail$4(error)));
|
|
37938
38080
|
const headSha = (directory) => runGit(directory, ["rev-parse", "HEAD"]).pipe(map$3((result) => result.status === 0 ? trimOrNull(result.stdout) : null));
|
|
37939
38081
|
const mergeBase = (input) => isSafeGitRevision(input.ref) ? runGit(input.directory, [
|
|
37940
38082
|
"merge-base",
|
|
@@ -38044,6 +38186,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
38044
38186
|
if (resolvedCurrentBranch === null && explicitBaseBranch === void 0) return null;
|
|
38045
38187
|
const baseBranch = explicitBaseBranch ?? (yield* defaultBranch(directory));
|
|
38046
38188
|
if (baseBranch === null) return null;
|
|
38189
|
+
if (!isSafeGitRevision(baseBranch)) return null;
|
|
38047
38190
|
if (explicitBaseBranch !== void 0) {
|
|
38048
38191
|
if (!(yield* branchExists(directory, explicitBaseBranch))) return yield* fail$4(new ReactDoctorError({ reason: new GitBaseBranchMissing({ branch: explicitBaseBranch }) }));
|
|
38049
38192
|
}
|
|
@@ -38148,7 +38291,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
38148
38291
|
]);
|
|
38149
38292
|
if (result.status !== 0) return null;
|
|
38150
38293
|
return parseChangedLineRanges(result.stdout);
|
|
38151
|
-
}).pipe(withSpan("Git.changedLineRanges"))
|
|
38294
|
+
}).pipe(catch_$1((error) => error.reason._tag === "GitInvocationFailed" ? succeed$2(null) : fail$4(error)), withSpan("Git.changedLineRanges"))
|
|
38152
38295
|
});
|
|
38153
38296
|
})).pipe(provide$2(layer$2.pipe(provide$2(mergeAll$1(layer$1, layer)))));
|
|
38154
38297
|
/**
|
|
@@ -38394,7 +38537,7 @@ const normalizeConfigForHash = (config) => {
|
|
|
38394
38537
|
if (Array.isArray(clone?.jsPlugins)) clone.jsPlugins = clone.jsPlugins.map((_, index) => `<plugin:${index}>`);
|
|
38395
38538
|
return clone;
|
|
38396
38539
|
};
|
|
38397
|
-
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("
|
|
38540
|
+
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");
|
|
38398
38541
|
/**
|
|
38399
38542
|
* Loads a plugin module via the local require resolver and extracts
|
|
38400
38543
|
* `(name, ruleNames)` from either `module.exports.meta + rules` or
|
|
@@ -38672,24 +38815,38 @@ const createFileLintCache = (cacheDirectory, rulesetHash) => {
|
|
|
38672
38815
|
updatedAtMs: Date.now(),
|
|
38673
38816
|
files: Object.fromEntries(cappedEntries)
|
|
38674
38817
|
};
|
|
38675
|
-
const keptHashes = Object.entries(rulesets).sort(([, first], [, second]) => second.updatedAtMs - first.updatedAtMs).slice(0, 8).map(([hash]) => hash);
|
|
38676
|
-
const prunedRulesets = {};
|
|
38677
|
-
for (const hash of keptHashes) prunedRulesets[hash] = rulesets[hash];
|
|
38678
38818
|
atomicWriteJson(cacheFilePath, {
|
|
38679
38819
|
version: 1,
|
|
38680
|
-
rulesets:
|
|
38820
|
+
rulesets: Object.fromEntries(Object.entries(rulesets).filter(([, bucket]) => isRecord(bucket) && typeof bucket.updatedAtMs === "number").sort(([, first], [, second]) => second.updatedAtMs - first.updatedAtMs).slice(0, 8))
|
|
38681
38821
|
});
|
|
38682
38822
|
}
|
|
38683
38823
|
};
|
|
38684
38824
|
};
|
|
38685
38825
|
const bundledRequire = createRequire(import.meta.url);
|
|
38826
|
+
const childNodeVersionByBinaryPath = /* @__PURE__ */ new Map();
|
|
38827
|
+
const resolveChildNodeVersion = (nodeBinaryPath) => {
|
|
38828
|
+
if (nodeBinaryPath === process.execPath) return process.version;
|
|
38829
|
+
const cachedVersion = childNodeVersionByBinaryPath.get(nodeBinaryPath);
|
|
38830
|
+
if (cachedVersion !== void 0) return cachedVersion;
|
|
38831
|
+
let version;
|
|
38832
|
+
try {
|
|
38833
|
+
version = execFileSync(nodeBinaryPath, ["--version"], {
|
|
38834
|
+
encoding: "utf8",
|
|
38835
|
+
timeout: NODE_VERSION_PROBE_TIMEOUT_MS
|
|
38836
|
+
}).trim();
|
|
38837
|
+
} catch {
|
|
38838
|
+
version = `unknown:${nodeBinaryPath}`;
|
|
38839
|
+
}
|
|
38840
|
+
childNodeVersionByBinaryPath.set(nodeBinaryPath, version);
|
|
38841
|
+
return version;
|
|
38842
|
+
};
|
|
38686
38843
|
const TOOLCHAIN_PACKAGE_SPECIFIERS = [
|
|
38687
38844
|
"oxlint/package.json",
|
|
38688
38845
|
"oxlint-plugin-react-doctor/package.json",
|
|
38689
38846
|
"eslint-plugin-react-hooks/package.json"
|
|
38690
38847
|
];
|
|
38691
|
-
const resolveOxlintToolchainVersions = () => {
|
|
38692
|
-
const versions = [`node=${
|
|
38848
|
+
const resolveOxlintToolchainVersions = (nodeBinaryPath = process.execPath) => {
|
|
38849
|
+
const versions = [`node=${resolveChildNodeVersion(nodeBinaryPath)}`];
|
|
38693
38850
|
for (const specifier of TOOLCHAIN_PACKAGE_SPECIFIERS) try {
|
|
38694
38851
|
const packageJson = bundledRequire(specifier);
|
|
38695
38852
|
const version = typeof packageJson.version === "string" ? packageJson.version : "unknown";
|
|
@@ -39223,11 +39380,12 @@ const REACT_COMPILER_TITLE = "React Compiler can't optimize this";
|
|
|
39223
39380
|
const REACT_COMPILER_TODO_TITLE = "React Compiler doesn't support this syntax";
|
|
39224
39381
|
const REACT_COMPILER_IMPACT = "This component misses React Compiler's automatic memoization & re-renders more than it should";
|
|
39225
39382
|
const REACT_COMPILER_ACTION = "Rewrite the flagged code so the compiler can optimize it.";
|
|
39383
|
+
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`.";
|
|
39226
39384
|
const REACT_COMPILER_GENERIC_MESSAGE = `${REACT_COMPILER_IMPACT}. ${REACT_COMPILER_ACTION}`;
|
|
39227
|
-
const buildReactCompilerMessage = (reasonSummary) => {
|
|
39385
|
+
const buildReactCompilerMessage = (reasonSummary, action = REACT_COMPILER_ACTION) => {
|
|
39228
39386
|
const normalizedSummary = reasonSummary.replace(TRAILING_PERIOD_PATTERN, "");
|
|
39229
|
-
if (!normalizedSummary) return
|
|
39230
|
-
return `${REACT_COMPILER_IMPACT}: ${normalizedSummary}. ${
|
|
39387
|
+
if (!normalizedSummary) return `${REACT_COMPILER_IMPACT}. ${action}`;
|
|
39388
|
+
return `${REACT_COMPILER_IMPACT}: ${normalizedSummary}. ${action}`;
|
|
39231
39389
|
};
|
|
39232
39390
|
const PLUGIN_CATEGORY_MAP = {
|
|
39233
39391
|
react: "Bugs",
|
|
@@ -39276,7 +39434,7 @@ const resolveCleanedDiagnostic = (message, help, plugin, rule, project) => {
|
|
|
39276
39434
|
const [reasonSummary = "", ...reasonDetailLines] = bailoutReason.split("\n");
|
|
39277
39435
|
const reasonDetail = reasonDetailLines.join("\n").trim();
|
|
39278
39436
|
return {
|
|
39279
|
-
message: buildReactCompilerMessage(reasonSummary.trim()),
|
|
39437
|
+
message: buildReactCompilerMessage(reasonSummary.trim(), rule === "incompatible-library" ? REACT_COMPILER_INCOMPATIBLE_LIBRARY_ACTION : REACT_COMPILER_ACTION),
|
|
39280
39438
|
help: appendReanimatedSharedValueHint(reasonDetail || help, rule, project)
|
|
39281
39439
|
};
|
|
39282
39440
|
}
|
|
@@ -39544,21 +39702,27 @@ const spawnLintBatches = async (input) => {
|
|
|
39544
39702
|
const allDiagnostics = [];
|
|
39545
39703
|
const droppedFiles = [];
|
|
39546
39704
|
let firstDropReason = null;
|
|
39547
|
-
const
|
|
39548
|
-
const spawnLintBatch = async (batch, depth) => {
|
|
39705
|
+
const spawnLintBatch = async (batch, depth, splitBudget) => {
|
|
39549
39706
|
const batchArgs = [...baseArgs, ...batch];
|
|
39550
39707
|
try {
|
|
39551
39708
|
return parseOxlintOutput(await spawnOxlint(batchArgs, rootDirectory, nodeBinaryPath, spawnTimeoutMs, outputMaxBytes, signal), project, rootDirectory);
|
|
39552
39709
|
} catch (error) {
|
|
39553
39710
|
if (!isSplittableReactDoctorError(error)) throw error;
|
|
39554
|
-
|
|
39555
|
-
|
|
39711
|
+
splitBudget.deadlineMs ??= Date.now() + splitTotalBudgetMs;
|
|
39712
|
+
const isBudgetElapsed = Date.now() >= splitBudget.deadlineMs;
|
|
39713
|
+
const isDepthCapReached = depth >= splitMaxDepth;
|
|
39714
|
+
if (batch.length <= 1 || isBudgetElapsed || isDepthCapReached) {
|
|
39556
39715
|
droppedFiles.push(...batch);
|
|
39557
|
-
if (firstDropReason === null)
|
|
39716
|
+
if (firstDropReason === null) {
|
|
39717
|
+
let limitHint = "";
|
|
39718
|
+
if (isDepthCapReached) limitHint = ` (split depth cap of ${splitMaxDepth} levels reached)`;
|
|
39719
|
+
else if (isBudgetElapsed) limitHint = ` (split budget of ${splitTotalBudgetMs / MILLISECONDS_PER_SECOND}s exhausted at depth ${depth})`;
|
|
39720
|
+
firstDropReason = batch.length > 1 ? `${error.message}${limitHint}` : error.message;
|
|
39721
|
+
}
|
|
39558
39722
|
return [];
|
|
39559
39723
|
}
|
|
39560
39724
|
const splitIndex = Math.ceil(batch.length / 2);
|
|
39561
|
-
return [...await spawnLintBatch(batch.slice(0, splitIndex), depth + 1), ...await spawnLintBatch(batch.slice(splitIndex), depth + 1)];
|
|
39725
|
+
return [...await spawnLintBatch(batch.slice(0, splitIndex), depth + 1, splitBudget), ...await spawnLintBatch(batch.slice(splitIndex), depth + 1, splitBudget)];
|
|
39562
39726
|
}
|
|
39563
39727
|
};
|
|
39564
39728
|
let startedFileCount = 0;
|
|
@@ -39575,7 +39739,7 @@ const spawnLintBatches = async (input) => {
|
|
|
39575
39739
|
try {
|
|
39576
39740
|
const batchResults = await mapWithConcurrency(fileBatches, concurrency, async (batch) => {
|
|
39577
39741
|
startedFileCount += batch.length;
|
|
39578
|
-
const batchDiagnostics = await spawnLintBatch(batch, 0);
|
|
39742
|
+
const batchDiagnostics = await spawnLintBatch(batch, 0, { deadlineMs: null });
|
|
39579
39743
|
scannedFileCount += batch.length;
|
|
39580
39744
|
if (onFileProgress) {
|
|
39581
39745
|
displayedFileCount = Math.min(Math.max(displayedFileCount, scannedFileCount), totalFileCount);
|
|
@@ -39649,7 +39813,14 @@ const resolveReactDoctorCacheDir = (projectDirectory) => {
|
|
|
39649
39813
|
if (cacheDirOverride) return Path.join(cacheDirOverride, projectCacheSubdir(projectDirectory));
|
|
39650
39814
|
const nodeModulesDirectory = Path.join(projectDirectory, "node_modules");
|
|
39651
39815
|
if (NFS.existsSync(nodeModulesDirectory)) return Path.join(nodeModulesDirectory, ".cache", "react-doctor");
|
|
39652
|
-
return Path.join(os.tmpdir(),
|
|
39816
|
+
return Path.join(os.tmpdir(), `react-doctor-cache-${resolveUserCacheScope()}`, projectCacheSubdir(projectDirectory));
|
|
39817
|
+
};
|
|
39818
|
+
const resolveUserCacheScope = () => {
|
|
39819
|
+
try {
|
|
39820
|
+
return String(process.getuid?.() ?? os.userInfo().username);
|
|
39821
|
+
} catch {
|
|
39822
|
+
return "shared";
|
|
39823
|
+
}
|
|
39653
39824
|
};
|
|
39654
39825
|
const sortSourceFilesByCost = (entries) => [...entries].sort((left, right) => right.sizeBytes - left.sizeBytes).map((entry) => entry.path);
|
|
39655
39826
|
/**
|
|
@@ -39817,7 +39988,7 @@ const runOxlint = async (options) => {
|
|
|
39817
39988
|
extendsPaths: [],
|
|
39818
39989
|
ruleSelection: "cacheable"
|
|
39819
39990
|
}),
|
|
39820
|
-
toolchainVersions: resolveOxlintToolchainVersions(),
|
|
39991
|
+
toolchainVersions: resolveOxlintToolchainVersions(nodeBinaryPath),
|
|
39821
39992
|
ignorePatterns: combinedPatterns,
|
|
39822
39993
|
tsconfigContent
|
|
39823
39994
|
});
|
|
@@ -39831,7 +40002,7 @@ const runOxlint = async (options) => {
|
|
|
39831
40002
|
missFiles.push(candidateFile);
|
|
39832
40003
|
continue;
|
|
39833
40004
|
}
|
|
39834
|
-
const cacheKey = `${candidateFile.replaceAll("\\", "/")}
|
|
40005
|
+
const cacheKey = `${candidateFile.replaceAll("\\", "/")}\u0000${contentHash}`;
|
|
39835
40006
|
cacheKeyByFile.set(candidateFile, cacheKey);
|
|
39836
40007
|
const cachedDiagnostics = cache.lookup(cacheKey);
|
|
39837
40008
|
if (cachedDiagnostics === null) missFiles.push(candidateFile);
|
|
@@ -40147,7 +40318,7 @@ const ScoreApiResponseSchema = Struct({
|
|
|
40147
40318
|
})))
|
|
40148
40319
|
});
|
|
40149
40320
|
const parseScoreResult = (value) => getOrNull(decodeUnknownOption(ScoreApiResponseSchema)(value));
|
|
40150
|
-
const stripLocalFileFields = (diagnostics) => diagnostics.map(({ filePath: _filePath, fileContext: _fileContext, ...rest }) => rest);
|
|
40321
|
+
const stripLocalFileFields = (diagnostics) => diagnostics.map(({ filePath: _filePath, fileContext: _fileContext, fixGroupId: _fixGroupId, ...rest }) => rest);
|
|
40151
40322
|
const isAbortError = (error) => error instanceof Error && (error.name === "AbortError" || error.name === "TimeoutError");
|
|
40152
40323
|
const describeFailure = (error) => {
|
|
40153
40324
|
if (isAbortError(error)) return `timed out after ${FETCH_TIMEOUT_MS / 1e3}s`;
|
|
@@ -40451,7 +40622,10 @@ const ALERT_SEVERITY_RANK = {
|
|
|
40451
40622
|
medium: 2,
|
|
40452
40623
|
low: 1
|
|
40453
40624
|
};
|
|
40454
|
-
const severityRank = (severity) =>
|
|
40625
|
+
const severityRank = (severity) => {
|
|
40626
|
+
const normalized = severity.toLowerCase();
|
|
40627
|
+
return Object.hasOwn(ALERT_SEVERITY_RANK, normalized) ? ALERT_SEVERITY_RANK[normalized] : 0;
|
|
40628
|
+
};
|
|
40455
40629
|
const displaySeverity = (severity) => {
|
|
40456
40630
|
const normalized = sanitizeTerminalText(severity.toLowerCase());
|
|
40457
40631
|
return normalized === "middle" ? "medium" : normalized;
|
|
@@ -40468,7 +40642,7 @@ const ALERT_TYPE_LABELS = {
|
|
|
40468
40642
|
typosquat: "possible typosquat"
|
|
40469
40643
|
};
|
|
40470
40644
|
const humanizeAlertType = (type) => type.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/[_-]+/g, " ").toLowerCase().trim();
|
|
40471
|
-
const friendlyAlertType = (type) => ALERT_TYPE_LABELS[type]
|
|
40645
|
+
const friendlyAlertType = (type) => Object.hasOwn(ALERT_TYPE_LABELS, type) ? ALERT_TYPE_LABELS[type] : sanitizeTerminalText(humanizeAlertType(type));
|
|
40472
40646
|
const summarizeAlertNote = (note) => {
|
|
40473
40647
|
const collapsed = sanitizeTerminalText(note.replace(/\s+/g, " ").trim());
|
|
40474
40648
|
const firstSentence = collapsed.split(/(?<=\.)\s/)[0] || collapsed;
|
|
@@ -40563,7 +40737,10 @@ const checkSupplyChain = (input) => gen(function* () {
|
|
|
40563
40737
|
* outage.
|
|
40564
40738
|
*/
|
|
40565
40739
|
var SupplyChain = class SupplyChain extends Service()("react-doctor/SupplyChain") {
|
|
40566
|
-
static layerNode = succeed$3(SupplyChain, SupplyChain.of({ run: (input) => unwrap(
|
|
40740
|
+
static layerNode = succeed$3(SupplyChain, SupplyChain.of({ run: (input) => unwrap(flatMap$2(SupplyChainOverlapTimeoutMs, (totalTimeoutMs) => checkSupplyChain({
|
|
40741
|
+
...input,
|
|
40742
|
+
totalTimeoutMs
|
|
40743
|
+
})).pipe(map$3((diagnostics) => fromIterable$1(diagnostics)), withSpan("SupplyChain.run"))) }));
|
|
40567
40744
|
static layerOf = (diagnostics) => succeed$3(SupplyChain, SupplyChain.of({ run: () => fromIterable$1(diagnostics) }));
|
|
40568
40745
|
};
|
|
40569
40746
|
const getObjectProperty = (value, propertyName) => {
|
|
@@ -40625,7 +40802,10 @@ const formatLintFailText = (reasonTag, nodeVersion) => {
|
|
|
40625
40802
|
* diagnostics).
|
|
40626
40803
|
* 2. beforeLint hook (e.g. CLI renders the project-detection block)
|
|
40627
40804
|
* 3. environment checks (reduced-motion + pnpm hardening +
|
|
40628
|
-
* expo/react-native
|
|
40805
|
+
* expo/react-native), collected synchronously. The heavier
|
|
40806
|
+
* content-regex security scan is forked instead (like supply-chain
|
|
40807
|
+
* below) and joined before the concat, so its CPU overlaps lint
|
|
40808
|
+
* rather than blocking the event loop before it.
|
|
40629
40809
|
* 4. The supply-chain check (Socket.dev) is forked onto a background
|
|
40630
40810
|
* fiber so its ~100% network-bound time overlaps the ~100%
|
|
40631
40811
|
* CPU/subprocess-bound lint pass below, collapsing two serial
|
|
@@ -40645,7 +40825,7 @@ const formatLintFailText = (reasonTag, nodeVersion) => {
|
|
|
40645
40825
|
* order, so terminal output is identical either way; supply-chain
|
|
40646
40826
|
* rides alongside without a spinner.
|
|
40647
40827
|
* 6. Join the supply-chain fiber, then assemble the diagnostics in a
|
|
40648
|
-
* FIXED order (env, supply-chain, lint, dead-code) so the output is
|
|
40828
|
+
* FIXED order (env, security-scan, supply-chain, lint, dead-code) so the output is
|
|
40649
40829
|
* byte-identical regardless of which fiber settled first. The
|
|
40650
40830
|
* viewer-permission fiber is joined later, during score-metadata
|
|
40651
40831
|
* assembly (it feeds score metadata, not diagnostics). The per-element
|
|
@@ -40706,12 +40886,13 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
40706
40886
|
...checkPnpmHardening(scanDirectory),
|
|
40707
40887
|
...checkReactServerComponentsAdvisory(scanDirectory, project),
|
|
40708
40888
|
...checkExpoProject(scanDirectory, project),
|
|
40709
|
-
...checkReactNativeProject(scanDirectory, project)
|
|
40710
|
-
...checkSecurityScan(scanDirectory, {
|
|
40711
|
-
project,
|
|
40712
|
-
ignoredTags: input.ignoredTags
|
|
40713
|
-
})
|
|
40889
|
+
...checkReactNativeProject(scanDirectory, project)
|
|
40714
40890
|
])));
|
|
40891
|
+
const securityScanFailedRef = yield* make$13(false);
|
|
40892
|
+
const securityScanFiber = yield* forkChild(runCollect(applyPerElementPipeline(isDiffMode ? empty$4 : unwrap(tryPromise(() => checkSecurityScanCooperative(scanDirectory, {
|
|
40893
|
+
project,
|
|
40894
|
+
ignoredTags: input.ignoredTags
|
|
40895
|
+
})).pipe(map$3((diagnostics) => fromIterable$1(diagnostics)), catch_$1(() => set(securityScanFailedRef, true).pipe(as(empty$4))))))).pipe(withSpan("SecurityScan.run")));
|
|
40715
40896
|
const shouldRunSupplyChain = !isDiffMode || (input.supplyChainManifestChanged ?? false);
|
|
40716
40897
|
const supplyChainOverlapTimeout = yield* SupplyChainOverlapTimeoutMs;
|
|
40717
40898
|
const supplyChainFiber = yield* forkChild(shouldRunSupplyChain ? runCollect(applyPerElementPipeline(supplyChainService.run({
|
|
@@ -40749,7 +40930,6 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
40749
40930
|
const lintConcurrency = deadCodeParseConcurrency === void 0 ? scanConcurrency : Math.max(1, scanConcurrency - deadCodeParseConcurrency);
|
|
40750
40931
|
const buildCollectDeadCode = (deadCodeTimeout) => runCollect(applyPerElementPipeline(deadCodeService.run({
|
|
40751
40932
|
rootDirectory: scanDirectory,
|
|
40752
|
-
userConfig: resolvedConfig.config,
|
|
40753
40933
|
parseConcurrency: deadCodeParseConcurrency,
|
|
40754
40934
|
workerTimeoutMs: deadCodeTimeout.workerTimeoutMs
|
|
40755
40935
|
}).pipe(catchTag("ReactDoctorError", (error) => unwrap(gen(function* () {
|
|
@@ -40847,9 +41027,11 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
40847
41027
|
else yield* scanProgress.succeed(`Scanned ${scannedFilesLabel} in ${scanElapsedSeconds}s${workerCountSuffix}`);
|
|
40848
41028
|
const supplyChainResult = yield* join(supplyChainFiber);
|
|
40849
41029
|
const supplyChainCollected = supplyChainResult.diagnostics;
|
|
41030
|
+
const securityScanCollected = yield* join(securityScanFiber);
|
|
40850
41031
|
yield* reporterService.finalize;
|
|
40851
41032
|
const finalDiagnostics = sortDiagnosticsStable(assignFixGroups([
|
|
40852
41033
|
...envCollected,
|
|
41034
|
+
...securityScanCollected,
|
|
40853
41035
|
...supplyChainCollected,
|
|
40854
41036
|
...lintCollected,
|
|
40855
41037
|
...deadCodeCollected
|
|
@@ -40875,6 +41057,7 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
40875
41057
|
metadata: scoreMetadata
|
|
40876
41058
|
});
|
|
40877
41059
|
const lintPartialFailures = yield* get$2(partialFailuresRef);
|
|
41060
|
+
const securityScanFailed = yield* get$2(securityScanFailedRef);
|
|
40878
41061
|
return {
|
|
40879
41062
|
project,
|
|
40880
41063
|
userConfig: resolvedConfig.config,
|
|
@@ -40895,8 +41078,10 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
40895
41078
|
scanElapsedMilliseconds,
|
|
40896
41079
|
scanConcurrency,
|
|
40897
41080
|
supplyChainOverlapTimedOut: supplyChainResult.timedOut,
|
|
41081
|
+
securityScanFailed,
|
|
40898
41082
|
lintCacheHitFileCount,
|
|
40899
|
-
lintCacheTotalFileCount
|
|
41083
|
+
lintCacheTotalFileCount,
|
|
41084
|
+
suppressedRuleCounts: transform.summarizeSuppressions()
|
|
40900
41085
|
};
|
|
40901
41086
|
}).pipe(withSpan("runInspect", { attributes: {
|
|
40902
41087
|
"inspect.directory": input.directory,
|
|
@@ -41301,6 +41486,61 @@ const clearAutoSuppressionCaches = () => {};
|
|
|
41301
41486
|
* untouched so the loader sees the same plain object either way.
|
|
41302
41487
|
*/
|
|
41303
41488
|
const defineConfig = (config) => config;
|
|
41489
|
+
const AI_TRAINING_ENV_VARS = [
|
|
41490
|
+
["HF_DATASETS_CACHE", "huggingface"],
|
|
41491
|
+
["HF_HOME", "huggingface"],
|
|
41492
|
+
["HUGGINGFACE_HUB_CACHE", "huggingface"],
|
|
41493
|
+
["SPACE_ID", "huggingface-spaces"],
|
|
41494
|
+
["CUDA_VISIBLE_DEVICES", "cuda"],
|
|
41495
|
+
["NVIDIA_VISIBLE_DEVICES", "nvidia"],
|
|
41496
|
+
["ROCR_VISIBLE_DEVICES", "rocm"],
|
|
41497
|
+
["TPU_NAME", "google-tpu"],
|
|
41498
|
+
["WANDB_RUN_ID", "wandb"],
|
|
41499
|
+
["WANDB_SWEEP_ID", "wandb"],
|
|
41500
|
+
["MLFLOW_RUN_ID", "mlflow"],
|
|
41501
|
+
["MLFLOW_TRACKING_URI", "mlflow"],
|
|
41502
|
+
["COMET_EXPERIMENT_KEY", "comet"],
|
|
41503
|
+
["NEPTUNE_RUN_ID", "neptune"],
|
|
41504
|
+
["CLEARML_TASK_ID", "clearml"],
|
|
41505
|
+
["DVC_STAGE", "dvc"],
|
|
41506
|
+
["RAY_WORKER_PROCESS", "ray"],
|
|
41507
|
+
["RAY_ADDRESS", "ray"],
|
|
41508
|
+
["MUJOCO_GL", "mujoco"],
|
|
41509
|
+
["MUJOCO_PATH", "mujoco"],
|
|
41510
|
+
["GYM_DISABLE_ENV_CHECKER", "gymnasium"],
|
|
41511
|
+
["SM_TRAINING_ENV", "sagemaker"],
|
|
41512
|
+
["TRAINING_JOB_ARN", "sagemaker"],
|
|
41513
|
+
["SAGEMAKER_BASE_DIR", "sagemaker"],
|
|
41514
|
+
["AZUREML_RUN_ID", "azure-ml"],
|
|
41515
|
+
["AZURE_ML_MODEL_DIR", "azure-ml"],
|
|
41516
|
+
["CLOUD_ML_PROJECT_ID", "vertex-ai"],
|
|
41517
|
+
["VERTEX_AI_LOG_LEVEL", "vertex-ai"],
|
|
41518
|
+
["DET_MASTER", "determined-ai"],
|
|
41519
|
+
["LIGHTNING_USER_ID", "lightning-ai"],
|
|
41520
|
+
["FLYTE_INTERNAL_EXECUTION_ID", "flyte"],
|
|
41521
|
+
["ARGO_WORKFLOW_NAME", "argo-workflows"],
|
|
41522
|
+
["KFP_POD_NAME", "kubeflow-pipelines"],
|
|
41523
|
+
["KAGGLE_KERNEL_RUN_TYPE", "kaggle"],
|
|
41524
|
+
["COLAB_BACKEND_VERSION", "google-colab"],
|
|
41525
|
+
["DATABRICKS_RUNTIME_VERSION", "databricks"],
|
|
41526
|
+
["DAYTONA_WS_ID", "daytona"],
|
|
41527
|
+
["DAYTONA_WS_NAME", "daytona"],
|
|
41528
|
+
["E2B_SANDBOX_ID", "e2b"],
|
|
41529
|
+
["MODAL_FUNCTION_ID", "modal"],
|
|
41530
|
+
["MODAL_TASK_ID", "modal"],
|
|
41531
|
+
["RUNPOD_POD_ID", "runpod"],
|
|
41532
|
+
["REPLICATE_USERNAME", "replicate"],
|
|
41533
|
+
["VAST_CONTAINERLABEL", "vast-ai"],
|
|
41534
|
+
["HARBOR_URL", "harbor"],
|
|
41535
|
+
["HARBOR_HOSTNAME", "harbor"],
|
|
41536
|
+
["SWE_BENCH_TASK", "swe-bench"],
|
|
41537
|
+
["SWEBENCH_TASK", "swe-bench"],
|
|
41538
|
+
["SWE_AGENT_MODEL", "swe-agent"]
|
|
41539
|
+
];
|
|
41540
|
+
const detectAiTrainingEnvironment = () => {
|
|
41541
|
+
for (const [envVar, label] of AI_TRAINING_ENV_VARS) if (process.env[envVar]) return label;
|
|
41542
|
+
return null;
|
|
41543
|
+
};
|
|
41304
41544
|
const unionValues = (baseValues, overrideValues) => [...new Set([...baseValues, ...overrideValues])];
|
|
41305
41545
|
const mergeIgnores = (baseIgnore, overrideIgnore) => {
|
|
41306
41546
|
const mergedIgnore = {
|
|
@@ -41345,6 +41585,12 @@ const mergeReactDoctorConfigs = (baseConfig, overrideConfig) => {
|
|
|
41345
41585
|
};
|
|
41346
41586
|
//#endregion
|
|
41347
41587
|
//#region ../api/dist/index.js
|
|
41588
|
+
let didWarnAiTraining = false;
|
|
41589
|
+
const warnIfAiTrainingEnvironment = () => {
|
|
41590
|
+
if (didWarnAiTraining || detectAiTrainingEnvironment() === null) return;
|
|
41591
|
+
didWarnAiTraining = true;
|
|
41592
|
+
console.warn("[react-doctor] Use in an AI or ML pipeline requires written permission under the react-doctor license. Contact founders@million.dev to request access.");
|
|
41593
|
+
};
|
|
41348
41594
|
const buildDiagnoseLayer = (config, configOverrideTarget) => {
|
|
41349
41595
|
const configLayer = configOverrideTarget === void 0 ? Config.layerNode : Config.layerOf({
|
|
41350
41596
|
config,
|
|
@@ -41382,6 +41628,7 @@ const outputToDiagnoseResult = (output, elapsedMilliseconds) => {
|
|
|
41382
41628
|
};
|
|
41383
41629
|
};
|
|
41384
41630
|
const diagnoseDirectory = async (directory, options) => {
|
|
41631
|
+
warnIfAiTrainingEnvironment();
|
|
41385
41632
|
const startTime = globalThis.performance.now();
|
|
41386
41633
|
const scanTarget = await resolveScanTarget(directory);
|
|
41387
41634
|
return outputToDiagnoseResult(await runPromise(restoreLegacyThrow(buildInspectProgram(scanTarget, options).pipe(provide(buildDiagnoseLayer(scanTarget.userConfig)), provide(layerOtlp)))), globalThis.performance.now() - startTime);
|
|
@@ -41428,6 +41675,7 @@ const diagnoseProject = async (projectDefinition, baseOptions, batchConfig) => {
|
|
|
41428
41675
|
}
|
|
41429
41676
|
};
|
|
41430
41677
|
const diagnoseProjectBatch = async (input) => {
|
|
41678
|
+
warnIfAiTrainingEnvironment();
|
|
41431
41679
|
const startTime = globalThis.performance.now();
|
|
41432
41680
|
const { projects, concurrency, config: batchConfig, ...baseOptions } = input;
|
|
41433
41681
|
const projectResults = await mapWithConcurrency(projects, concurrency ?? 4, (projectDefinition) => diagnoseProject(projectDefinition, baseOptions, batchConfig));
|
|
@@ -41472,4 +41720,4 @@ const toJsonReport = (result, options) => buildJsonReport({
|
|
|
41472
41720
|
export { AmbiguousProjectError, NoReactDependencyError, NotADirectoryError, PackageJsonNotFoundError, ProjectNotFoundError, ReactDoctorError, buildJsonReport, buildJsonReportError, clearCaches, defineConfig, diagnose, filterSourceFiles, getDiffInfo, isProjectDiscoveryError, isReactDoctorError, summarizeDiagnostics, toJsonReport };
|
|
41473
41721
|
|
|
41474
41722
|
//# sourceMappingURL=index.js.map
|
|
41475
|
-
//# debugId=
|
|
41723
|
+
//# debugId=20ae6ec9-b785-55cb-96d6-9c8b9fe3a35b
|