react-doctor 0.5.8-dev.7f9e7f4 → 0.5.8-dev.80e3093
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 +9571 -1520
- package/dist/index.d.ts +11 -2
- package/dist/index.js +397 -146
- package/dist/lsp.js +343 -147
- package/package.json +10 -8
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]="83529d8f-2ca3-50ba-a3b9-44622f913549")}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;
|
|
@@ -34487,7 +34492,10 @@ for (const [legacyRuleKey, nativeRuleKey] of Object.entries(LEGACY_RULE_KEY_TO_N
|
|
|
34487
34492
|
NATIVE_RULE_KEY_TO_LEGACY_RULE_KEYS.set(nativeRuleKey, aliases);
|
|
34488
34493
|
}
|
|
34489
34494
|
const getLegacyRuleKeysForNative = (ruleKey) => NATIVE_RULE_KEY_TO_LEGACY_RULE_KEYS.get(ruleKey) ?? [];
|
|
34490
|
-
const canonicalizeRuleKey = (ruleKey) =>
|
|
34495
|
+
const canonicalizeRuleKey = (ruleKey) => {
|
|
34496
|
+
const nativeRuleKey = LEGACY_RULE_KEY_TO_NATIVE_RULE_KEY[ruleKey];
|
|
34497
|
+
return typeof nativeRuleKey === "string" ? nativeRuleKey : ruleKey;
|
|
34498
|
+
};
|
|
34491
34499
|
const isReactDoctorShortIdOf = (bareRuleKey, qualifiedRuleKey) => !bareRuleKey.includes("/") && qualifiedRuleKey === `react-doctor/${bareRuleKey}`;
|
|
34492
34500
|
const isSameRuleKey = (candidateRuleKey, targetRuleKey) => {
|
|
34493
34501
|
const canonicalCandidate = canonicalizeRuleKey(candidateRuleKey);
|
|
@@ -34514,7 +34522,7 @@ const tokenizeRuleList = (ruleList) => {
|
|
|
34514
34522
|
const FOREIGN_INLINE_DISABLE_PATTERN = /(?:\/\/|\/\*)[ \t]*(eslint|oxlint)-disable-(next-line|line)(?![\w-])([^\r\n]*)/;
|
|
34515
34523
|
const FOREIGN_BLOCK_DISABLE_PATTERN = /\/\*[ \t]*(eslint|oxlint)-disable(?![\w-])([^*\r\n]*)/;
|
|
34516
34524
|
const FOREIGN_BLOCK_ENABLE_PATTERN = /\/\*[ \t]*(?:eslint|oxlint)-enable(?![\w-])([^*\r\n]*)/;
|
|
34517
|
-
const buildHint = (tool, token, ruleId) => `oxlint matches plugin rules only by their full name, so \`${token}\` in your ${tool}-disable comment does not silence \`${ruleId}\` —
|
|
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}\`).`;
|
|
34518
34526
|
const tokenMisnamesRule = (token, ruleId) => token !== ruleId && isSameRuleKey(token, ruleId);
|
|
34519
34527
|
const detectInlineNearMiss = (lines, diagnosticLineIndex, ruleId) => {
|
|
34520
34528
|
const candidates = [{
|
|
@@ -34994,6 +35002,7 @@ const collectStringSet = (values) => {
|
|
|
34994
35002
|
if (!Array.isArray(values)) return /* @__PURE__ */ new Set();
|
|
34995
35003
|
return new Set(values.filter((value) => typeof value === "string"));
|
|
34996
35004
|
};
|
|
35005
|
+
const JSX_NO_UNDEF_IDENTIFIER_PATTERN = /^`([^`]+)`/;
|
|
34997
35006
|
/**
|
|
34998
35007
|
* Pre-compiles every stateful filter and returns a single
|
|
34999
35008
|
* `apply(diagnostic)` closure that runs (in order):
|
|
@@ -35027,11 +35036,28 @@ const buildDiagnosticPipeline = (input) => {
|
|
|
35027
35036
|
const compiledOverrides = compileIgnoreOverrides(userConfig);
|
|
35028
35037
|
const textComponentNames = collectStringSet(userConfig?.textComponents);
|
|
35029
35038
|
const rawTextWrapperComponentNames = collectStringSet(userConfig?.rawTextWrapperComponents);
|
|
35039
|
+
const runtimeGlobalNames = collectStringSet(userConfig?.runtimeGlobals);
|
|
35030
35040
|
const hasTextComponents = textComponentNames.size > 0;
|
|
35031
35041
|
const hasRawTextWrappers = rawTextWrapperComponentNames.size > 0;
|
|
35042
|
+
const hasRuntimeGlobals = runtimeGlobalNames.size > 0;
|
|
35032
35043
|
const fileLinesCache = /* @__PURE__ */ new Map();
|
|
35033
35044
|
const fileContextCache = /* @__PURE__ */ new Map();
|
|
35034
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
|
+
};
|
|
35035
35061
|
const isLibraryFile = (filePath) => {
|
|
35036
35062
|
let cached = libraryFileCache.get(filePath);
|
|
35037
35063
|
if (cached === void 0) {
|
|
@@ -35080,50 +35106,60 @@ const buildDiagnosticPipeline = (input) => {
|
|
|
35080
35106
|
if (hasRawTextWrappers && isInsideStringOnlyWrapper(lines, diagnostic.line, diagnostic.column, rawTextWrapperComponentNames)) return true;
|
|
35081
35107
|
return false;
|
|
35082
35108
|
};
|
|
35083
|
-
|
|
35084
|
-
if (
|
|
35085
|
-
|
|
35086
|
-
|
|
35087
|
-
|
|
35088
|
-
|
|
35089
|
-
|
|
35090
|
-
|
|
35091
|
-
|
|
35092
|
-
|
|
35093
|
-
|
|
35094
|
-
|
|
35095
|
-
if (
|
|
35096
|
-
|
|
35097
|
-
|
|
35098
|
-
|
|
35099
|
-
|
|
35100
|
-
|
|
35101
|
-
|
|
35102
|
-
|
|
35103
|
-
|
|
35104
|
-
if (isFileIgnoredByPatterns(current.filePath, rootDirectory, ignoredFilePatterns)) return null;
|
|
35105
|
-
if (isDiagnosticIgnoredByOverrides(current, rootDirectory, compiledOverrides)) return null;
|
|
35106
|
-
if (isRnRawTextSuppressedByConfig(current)) return null;
|
|
35107
|
-
}
|
|
35108
|
-
if (respectInlineDisables && current.line > 0) {
|
|
35109
|
-
const lines = getFileLines(current.filePath);
|
|
35110
|
-
if (lines) {
|
|
35111
|
-
const ruleIdentifier = `${current.plugin}/${current.rule}`;
|
|
35112
|
-
const evaluation = evaluateSuppression(lines, current.line - 1, ruleIdentifier);
|
|
35113
|
-
if (evaluation.isSuppressed) return null;
|
|
35114
|
-
if (evaluation.nearMissHint) current = {
|
|
35115
|
-
...current,
|
|
35116
|
-
suppressionHint: evaluation.nearMissHint
|
|
35117
|
-
};
|
|
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);
|
|
35118
35130
|
}
|
|
35119
|
-
|
|
35120
|
-
|
|
35121
|
-
|
|
35122
|
-
|
|
35123
|
-
|
|
35124
|
-
|
|
35125
|
-
|
|
35126
|
-
|
|
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
|
+
};
|
|
35127
35163
|
};
|
|
35128
35164
|
const OxlintUnavailableKind = Literals(["binary-not-found", "native-binding-missing"]);
|
|
35129
35165
|
var OxlintUnavailable = class extends TaggedErrorClass()("OxlintUnavailable", {
|
|
@@ -35365,7 +35401,7 @@ const resolveScanConcurrency = (requested) => {
|
|
|
35365
35401
|
if (!Number.isFinite(requested) || requested < 1) return 1;
|
|
35366
35402
|
return Math.min(Math.floor(requested), 32);
|
|
35367
35403
|
};
|
|
35368
|
-
const readSystemFacts = () => ({
|
|
35404
|
+
const readSystemFacts$1 = () => ({
|
|
35369
35405
|
availableCores: os.availableParallelism(),
|
|
35370
35406
|
totalMemoryBytes: os.totalmem(),
|
|
35371
35407
|
cgroupMemoryLimitBytes: readCgroupMemoryLimitBytes()
|
|
@@ -35386,7 +35422,7 @@ const readSystemFacts = () => ({
|
|
|
35386
35422
|
* `facts` is injectable so tests exercise core-bound, memory-bound, cgroup-
|
|
35387
35423
|
* limited, and ceiling cases without mocking `os` or the filesystem.
|
|
35388
35424
|
*/
|
|
35389
|
-
const resolveAutoScanConcurrency = (facts = readSystemFacts()) => {
|
|
35425
|
+
const resolveAutoScanConcurrency = (facts = readSystemFacts$1()) => {
|
|
35390
35426
|
const availableMemoryBytes = Math.min(facts.totalMemoryBytes, facts.cgroupMemoryLimitBytes ?? Number.POSITIVE_INFINITY);
|
|
35391
35427
|
const memoryBoundedWorkers = Math.floor(availableMemoryBytes / PER_WORKER_MEM_BUDGET_BYTES);
|
|
35392
35428
|
return resolveScanConcurrency(Math.min(facts.availableCores, memoryBoundedWorkers));
|
|
@@ -35427,13 +35463,7 @@ var ScanDeadlineMs = class extends Reference("react-doctor/ScanDeadlineMs", { de
|
|
|
35427
35463
|
* without recompiling react-doctor. Tests override via
|
|
35428
35464
|
* `Layer.succeed(SupplyChainOverlapTimeoutMs, ...)`.
|
|
35429
35465
|
*/
|
|
35430
|
-
var SupplyChainOverlapTimeoutMs = class extends Reference("react-doctor/SupplyChainOverlapTimeoutMs", { defaultValue: () => {
|
|
35431
|
-
const raw = process.env["REACT_DOCTOR_SUPPLY_CHAIN_TIMEOUT_MS"];
|
|
35432
|
-
if (raw === void 0) return SUPPLY_CHAIN_OVERLAP_TIMEOUT_MS;
|
|
35433
|
-
const parsed = Number(raw);
|
|
35434
|
-
if (!Number.isFinite(parsed) || parsed <= 0) return SUPPLY_CHAIN_OVERLAP_TIMEOUT_MS;
|
|
35435
|
-
return parsed;
|
|
35436
|
-
} }) {};
|
|
35466
|
+
var SupplyChainOverlapTimeoutMs = class extends Reference("react-doctor/SupplyChainOverlapTimeoutMs", { defaultValue: () => readPositiveEnvMs("REACT_DOCTOR_SUPPLY_CHAIN_TIMEOUT_MS", SUPPLY_CHAIN_OVERLAP_TIMEOUT_MS) }) {};
|
|
35437
35467
|
/**
|
|
35438
35468
|
* Hard cap on combined stdout+stderr bytes per oxlint batch. The
|
|
35439
35469
|
* subprocess gets SIGKILL'd if it produces more; the recovery path
|
|
@@ -35577,6 +35607,13 @@ const BOOLEAN_FIELD_NAMES = [
|
|
|
35577
35607
|
"adoptExistingLintConfig"
|
|
35578
35608
|
];
|
|
35579
35609
|
const STRING_FIELD_NAMES = ["rootDir"];
|
|
35610
|
+
const STRING_ARRAY_FIELD_NAMES = [
|
|
35611
|
+
"projects",
|
|
35612
|
+
"textComponents",
|
|
35613
|
+
"rawTextWrapperComponents",
|
|
35614
|
+
"runtimeGlobals",
|
|
35615
|
+
"serverAuthFunctionNames"
|
|
35616
|
+
];
|
|
35580
35617
|
const SURFACE_CONTROL_FIELD_NAMES = [
|
|
35581
35618
|
"includeTags",
|
|
35582
35619
|
"excludeTags",
|
|
@@ -35678,6 +35715,7 @@ const validateConfigTypes = (config) => {
|
|
|
35678
35715
|
const validated = { ...config };
|
|
35679
35716
|
for (const fieldName of BOOLEAN_FIELD_NAMES) applyFieldValidator(config, validated, fieldName, (value) => coerceMaybeBooleanString(fieldName, value));
|
|
35680
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));
|
|
35681
35719
|
applyFieldValidator(config, validated, "surfaces", validateSurfacesField);
|
|
35682
35720
|
for (const fieldName of SEVERITY_FIELD_NAMES) applyFieldValidator(config, validated, fieldName, (value) => validateSeverityMap(fieldName, value, fieldName === "categories"));
|
|
35683
35721
|
applyFieldValidator(config, validated, "plugins", (value) => validateStringArrayField("plugins", value));
|
|
@@ -36912,7 +36950,10 @@ const shouldEnableRule = (requires, tags, capabilities, ignoredTags, disabledBy)
|
|
|
36912
36950
|
}
|
|
36913
36951
|
return true;
|
|
36914
36952
|
};
|
|
36915
|
-
const
|
|
36953
|
+
const yieldToEventLoop = () => new Promise((resolve) => {
|
|
36954
|
+
setImmediate(resolve);
|
|
36955
|
+
});
|
|
36956
|
+
const createSecurityScanSession = (rootDirectory, options) => {
|
|
36916
36957
|
const capabilities = options.project ? buildCapabilities(options.project) : /* @__PURE__ */ new Set();
|
|
36917
36958
|
const ignoredTags = options.ignoredTags ?? /* @__PURE__ */ new Set();
|
|
36918
36959
|
const enabledScanRules = REACT_DOCTOR_RULES.flatMap((entry) => {
|
|
@@ -36927,7 +36968,7 @@ const checkSecurityScan = (rootDirectory, options = {}) => {
|
|
|
36927
36968
|
committedFilesOnly: rule.committedFilesOnly === true
|
|
36928
36969
|
}];
|
|
36929
36970
|
});
|
|
36930
|
-
if (enabledScanRules.length === 0) return
|
|
36971
|
+
if (enabledScanRules.length === 0) return null;
|
|
36931
36972
|
const diagnostics = [];
|
|
36932
36973
|
const seen = /* @__PURE__ */ new Set();
|
|
36933
36974
|
const gitIgnoredCache = /* @__PURE__ */ new Map();
|
|
@@ -36939,15 +36980,34 @@ const checkSecurityScan = (rootDirectory, options = {}) => {
|
|
|
36939
36980
|
}
|
|
36940
36981
|
return status === true;
|
|
36941
36982
|
};
|
|
36942
|
-
|
|
36943
|
-
|
|
36944
|
-
|
|
36945
|
-
|
|
36946
|
-
|
|
36947
|
-
|
|
36948
|
-
|
|
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
|
+
}
|
|
36949
37009
|
}
|
|
36950
|
-
return diagnostics;
|
|
37010
|
+
return session.diagnostics;
|
|
36951
37011
|
};
|
|
36952
37012
|
var import_picocolors = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
36953
37013
|
let p = process || {}, argv = p.argv || [], env = p.env || {};
|
|
@@ -37164,6 +37224,74 @@ const collectDeadCodeIgnorePatterns = (rootDirectory) => {
|
|
|
37164
37224
|
return [...seen].filter((pattern) => pattern.length > 0);
|
|
37165
37225
|
};
|
|
37166
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
|
+
};
|
|
37167
37295
|
/**
|
|
37168
37296
|
* Resolves a path to its canonical, symlink-free form, falling back to
|
|
37169
37297
|
* the input when it cannot be realpath'd (broken symlink, permission
|
|
@@ -37188,6 +37316,11 @@ const toCanonicalPath = (filePath) => {
|
|
|
37188
37316
|
};
|
|
37189
37317
|
const DEAD_CODE_PLUGIN = "deslop";
|
|
37190
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
|
+
]);
|
|
37191
37324
|
const DEAD_CODE_WORKER_SCRIPT = `
|
|
37192
37325
|
const inputChunks = [];
|
|
37193
37326
|
process.stdin.on("data", (chunk) => inputChunks.push(chunk));
|
|
@@ -37454,14 +37587,17 @@ const checkDeadCode = async (options) => {
|
|
|
37454
37587
|
if (!NFS.existsSync(Path.join(rootDirectory, "package.json"))) return [];
|
|
37455
37588
|
const entryPatterns = collectDeadCodeEntryPatterns(rootDirectory);
|
|
37456
37589
|
const ignorePatterns = collectDeadCodeIgnorePatterns(rootDirectory);
|
|
37457
|
-
const
|
|
37458
|
-
|
|
37459
|
-
|
|
37460
|
-
|
|
37461
|
-
|
|
37462
|
-
|
|
37463
|
-
|
|
37464
|
-
|
|
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());
|
|
37465
37601
|
const toRelative = (filePath) => toRelativeFilePath(rootDirectory, filePath);
|
|
37466
37602
|
const diagnostics = [];
|
|
37467
37603
|
for (const unusedFile of result.unusedFiles) diagnostics.push({
|
|
@@ -37490,6 +37626,7 @@ const checkDeadCode = async (options) => {
|
|
|
37490
37626
|
});
|
|
37491
37627
|
}
|
|
37492
37628
|
for (const unusedDependency of result.unusedDependencies) {
|
|
37629
|
+
if (REACT_DOCTOR_TOOLCHAIN_PACKAGES.has(unusedDependency.name)) continue;
|
|
37493
37630
|
const label = unusedDependency.isDevDependency ? "devDependency" : "dependency";
|
|
37494
37631
|
diagnostics.push({
|
|
37495
37632
|
filePath: "package.json",
|
|
@@ -37668,7 +37805,6 @@ var DeadCode = class DeadCode extends Service()("react-doctor/DeadCode") {
|
|
|
37668
37805
|
return yield* tryPromise({
|
|
37669
37806
|
try: (signal) => checkDeadCode({
|
|
37670
37807
|
rootDirectory: input.rootDirectory,
|
|
37671
|
-
userConfig: input.userConfig,
|
|
37672
37808
|
parseConcurrency: input.parseConcurrency,
|
|
37673
37809
|
workerTimeoutMs: input.workerTimeoutMs,
|
|
37674
37810
|
abortSignal: signal
|
|
@@ -37791,6 +37927,11 @@ const isSafeGitRevision = (candidate) => {
|
|
|
37791
37927
|
if (candidate.includes("..") || candidate.includes("@{")) return false;
|
|
37792
37928
|
return SAFE_GIT_REVISION_PATTERN.test(candidate);
|
|
37793
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
|
+
};
|
|
37794
37935
|
/**
|
|
37795
37936
|
* Splits a git revision range into its endpoints: three-dot `A...B`
|
|
37796
37937
|
* (symmetric, merge-base) or two-dot `A..B` (direct). Returns `null`
|
|
@@ -37862,43 +38003,47 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
37862
38003
|
* reason: GitInvocationFailed })` so the rest of the codebase
|
|
37863
38004
|
* sees a single failure channel.
|
|
37864
38005
|
*/
|
|
37865
|
-
const runCommand = (input) =>
|
|
37866
|
-
const
|
|
37867
|
-
cwd: input.directory,
|
|
37868
|
-
env: input.env,
|
|
37869
|
-
extendEnv: true
|
|
37870
|
-
}));
|
|
37871
|
-
const maxStdoutBytes = input.maxStdoutBytes;
|
|
37872
|
-
const stdoutByteCount = yield* make$13(0);
|
|
37873
|
-
const [stdout, stderr, status] = yield* all([
|
|
37874
|
-
mkString(decodeText(maxStdoutBytes === void 0 ? handle.stdout : handle.stdout.pipe(tap((chunk) => updateAndGet(stdoutByteCount, (total) => total + chunk.length).pipe(flatMap$2((total) => total > maxStdoutBytes ? fail$4(new ReactDoctorError({ reason: new GitInvocationFailed({
|
|
37875
|
-
args: [...input.args],
|
|
37876
|
-
directory: input.directory,
|
|
37877
|
-
cause: /* @__PURE__ */ new Error(`git stdout exceeded ${maxStdoutBytes} bytes`)
|
|
37878
|
-
}) })) : void_)))))),
|
|
37879
|
-
mkString(decodeText(handle.stderr)),
|
|
37880
|
-
handle.exitCode
|
|
37881
|
-
], { concurrency: 3 });
|
|
37882
|
-
return {
|
|
37883
|
-
status,
|
|
37884
|
-
stdout,
|
|
37885
|
-
stderr
|
|
37886
|
-
};
|
|
37887
|
-
})).pipe(catchTag$1("PlatformError", (cause) => {
|
|
37888
|
-
if (input.command !== "git") return succeed$2({
|
|
38006
|
+
const runCommand = (input) => {
|
|
38007
|
+
const foldSpawnFailure = (cause) => input.command !== "git" ? succeed$2({
|
|
37889
38008
|
status: 127,
|
|
37890
38009
|
stdout: "",
|
|
37891
38010
|
stderr: String(cause)
|
|
37892
|
-
})
|
|
37893
|
-
return new ReactDoctorError({ reason: new GitInvocationFailed({
|
|
38011
|
+
}) : fail$4(new ReactDoctorError({ reason: new GitInvocationFailed({
|
|
37894
38012
|
args: [...input.args],
|
|
37895
38013
|
directory: input.directory,
|
|
37896
38014
|
cause
|
|
37897
|
-
}) });
|
|
37898
|
-
|
|
37899
|
-
|
|
37900
|
-
|
|
37901
|
-
|
|
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
|
+
};
|
|
37902
38047
|
const runGit = (directory, args) => runCommand({
|
|
37903
38048
|
command: "git",
|
|
37904
38049
|
args,
|
|
@@ -37931,7 +38076,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
37931
38076
|
"rev-parse",
|
|
37932
38077
|
"--verify",
|
|
37933
38078
|
branch
|
|
37934
|
-
]).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)));
|
|
37935
38080
|
const headSha = (directory) => runGit(directory, ["rev-parse", "HEAD"]).pipe(map$3((result) => result.status === 0 ? trimOrNull(result.stdout) : null));
|
|
37936
38081
|
const mergeBase = (input) => isSafeGitRevision(input.ref) ? runGit(input.directory, [
|
|
37937
38082
|
"merge-base",
|
|
@@ -38041,6 +38186,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
38041
38186
|
if (resolvedCurrentBranch === null && explicitBaseBranch === void 0) return null;
|
|
38042
38187
|
const baseBranch = explicitBaseBranch ?? (yield* defaultBranch(directory));
|
|
38043
38188
|
if (baseBranch === null) return null;
|
|
38189
|
+
if (!isSafeGitRevision(baseBranch)) return null;
|
|
38044
38190
|
if (explicitBaseBranch !== void 0) {
|
|
38045
38191
|
if (!(yield* branchExists(directory, explicitBaseBranch))) return yield* fail$4(new ReactDoctorError({ reason: new GitBaseBranchMissing({ branch: explicitBaseBranch }) }));
|
|
38046
38192
|
}
|
|
@@ -38145,7 +38291,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
38145
38291
|
]);
|
|
38146
38292
|
if (result.status !== 0) return null;
|
|
38147
38293
|
return parseChangedLineRanges(result.stdout);
|
|
38148
|
-
}).pipe(withSpan("Git.changedLineRanges"))
|
|
38294
|
+
}).pipe(catch_$1((error) => error.reason._tag === "GitInvocationFailed" ? succeed$2(null) : fail$4(error)), withSpan("Git.changedLineRanges"))
|
|
38149
38295
|
});
|
|
38150
38296
|
})).pipe(provide$2(layer$2.pipe(provide$2(mergeAll$1(layer$1, layer)))));
|
|
38151
38297
|
/**
|
|
@@ -38391,7 +38537,7 @@ const normalizeConfigForHash = (config) => {
|
|
|
38391
38537
|
if (Array.isArray(clone?.jsPlugins)) clone.jsPlugins = clone.jsPlugins.map((_, index) => `<plugin:${index}>`);
|
|
38392
38538
|
return clone;
|
|
38393
38539
|
};
|
|
38394
|
-
const computeRulesetHash = (input) => crypto.createHash("sha1").update(JSON.stringify(normalizeConfigForHash(input.config))).update("\0").update([...input.toolchainVersions].join("\0")).update("\0").update([...input.ignorePatterns].join("\n")).update("
|
|
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");
|
|
38395
38541
|
/**
|
|
38396
38542
|
* Loads a plugin module via the local require resolver and extracts
|
|
38397
38543
|
* `(name, ruleNames)` from either `module.exports.meta + rules` or
|
|
@@ -38669,24 +38815,38 @@ const createFileLintCache = (cacheDirectory, rulesetHash) => {
|
|
|
38669
38815
|
updatedAtMs: Date.now(),
|
|
38670
38816
|
files: Object.fromEntries(cappedEntries)
|
|
38671
38817
|
};
|
|
38672
|
-
const keptHashes = Object.entries(rulesets).sort(([, first], [, second]) => second.updatedAtMs - first.updatedAtMs).slice(0, 8).map(([hash]) => hash);
|
|
38673
|
-
const prunedRulesets = {};
|
|
38674
|
-
for (const hash of keptHashes) prunedRulesets[hash] = rulesets[hash];
|
|
38675
38818
|
atomicWriteJson(cacheFilePath, {
|
|
38676
38819
|
version: 1,
|
|
38677
|
-
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))
|
|
38678
38821
|
});
|
|
38679
38822
|
}
|
|
38680
38823
|
};
|
|
38681
38824
|
};
|
|
38682
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
|
+
};
|
|
38683
38843
|
const TOOLCHAIN_PACKAGE_SPECIFIERS = [
|
|
38684
38844
|
"oxlint/package.json",
|
|
38685
38845
|
"oxlint-plugin-react-doctor/package.json",
|
|
38686
38846
|
"eslint-plugin-react-hooks/package.json"
|
|
38687
38847
|
];
|
|
38688
|
-
const resolveOxlintToolchainVersions = () => {
|
|
38689
|
-
const versions = [`node=${
|
|
38848
|
+
const resolveOxlintToolchainVersions = (nodeBinaryPath = process.execPath) => {
|
|
38849
|
+
const versions = [`node=${resolveChildNodeVersion(nodeBinaryPath)}`];
|
|
38690
38850
|
for (const specifier of TOOLCHAIN_PACKAGE_SPECIFIERS) try {
|
|
38691
38851
|
const packageJson = bundledRequire(specifier);
|
|
38692
38852
|
const version = typeof packageJson.version === "string" ? packageJson.version : "unknown";
|
|
@@ -39220,11 +39380,12 @@ const REACT_COMPILER_TITLE = "React Compiler can't optimize this";
|
|
|
39220
39380
|
const REACT_COMPILER_TODO_TITLE = "React Compiler doesn't support this syntax";
|
|
39221
39381
|
const REACT_COMPILER_IMPACT = "This component misses React Compiler's automatic memoization & re-renders more than it should";
|
|
39222
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`.";
|
|
39223
39384
|
const REACT_COMPILER_GENERIC_MESSAGE = `${REACT_COMPILER_IMPACT}. ${REACT_COMPILER_ACTION}`;
|
|
39224
|
-
const buildReactCompilerMessage = (reasonSummary) => {
|
|
39385
|
+
const buildReactCompilerMessage = (reasonSummary, action = REACT_COMPILER_ACTION) => {
|
|
39225
39386
|
const normalizedSummary = reasonSummary.replace(TRAILING_PERIOD_PATTERN, "");
|
|
39226
|
-
if (!normalizedSummary) return
|
|
39227
|
-
return `${REACT_COMPILER_IMPACT}: ${normalizedSummary}. ${
|
|
39387
|
+
if (!normalizedSummary) return `${REACT_COMPILER_IMPACT}. ${action}`;
|
|
39388
|
+
return `${REACT_COMPILER_IMPACT}: ${normalizedSummary}. ${action}`;
|
|
39228
39389
|
};
|
|
39229
39390
|
const PLUGIN_CATEGORY_MAP = {
|
|
39230
39391
|
react: "Bugs",
|
|
@@ -39273,7 +39434,7 @@ const resolveCleanedDiagnostic = (message, help, plugin, rule, project) => {
|
|
|
39273
39434
|
const [reasonSummary = "", ...reasonDetailLines] = bailoutReason.split("\n");
|
|
39274
39435
|
const reasonDetail = reasonDetailLines.join("\n").trim();
|
|
39275
39436
|
return {
|
|
39276
|
-
message: buildReactCompilerMessage(reasonSummary.trim()),
|
|
39437
|
+
message: buildReactCompilerMessage(reasonSummary.trim(), rule === "incompatible-library" ? REACT_COMPILER_INCOMPATIBLE_LIBRARY_ACTION : REACT_COMPILER_ACTION),
|
|
39277
39438
|
help: appendReanimatedSharedValueHint(reasonDetail || help, rule, project)
|
|
39278
39439
|
};
|
|
39279
39440
|
}
|
|
@@ -39541,21 +39702,27 @@ const spawnLintBatches = async (input) => {
|
|
|
39541
39702
|
const allDiagnostics = [];
|
|
39542
39703
|
const droppedFiles = [];
|
|
39543
39704
|
let firstDropReason = null;
|
|
39544
|
-
const
|
|
39545
|
-
const spawnLintBatch = async (batch, depth) => {
|
|
39705
|
+
const spawnLintBatch = async (batch, depth, splitBudget) => {
|
|
39546
39706
|
const batchArgs = [...baseArgs, ...batch];
|
|
39547
39707
|
try {
|
|
39548
39708
|
return parseOxlintOutput(await spawnOxlint(batchArgs, rootDirectory, nodeBinaryPath, spawnTimeoutMs, outputMaxBytes, signal), project, rootDirectory);
|
|
39549
39709
|
} catch (error) {
|
|
39550
39710
|
if (!isSplittableReactDoctorError(error)) throw error;
|
|
39551
|
-
|
|
39552
|
-
|
|
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) {
|
|
39553
39715
|
droppedFiles.push(...batch);
|
|
39554
|
-
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
|
+
}
|
|
39555
39722
|
return [];
|
|
39556
39723
|
}
|
|
39557
39724
|
const splitIndex = Math.ceil(batch.length / 2);
|
|
39558
|
-
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)];
|
|
39559
39726
|
}
|
|
39560
39727
|
};
|
|
39561
39728
|
let startedFileCount = 0;
|
|
@@ -39572,7 +39739,7 @@ const spawnLintBatches = async (input) => {
|
|
|
39572
39739
|
try {
|
|
39573
39740
|
const batchResults = await mapWithConcurrency(fileBatches, concurrency, async (batch) => {
|
|
39574
39741
|
startedFileCount += batch.length;
|
|
39575
|
-
const batchDiagnostics = await spawnLintBatch(batch, 0);
|
|
39742
|
+
const batchDiagnostics = await spawnLintBatch(batch, 0, { deadlineMs: null });
|
|
39576
39743
|
scannedFileCount += batch.length;
|
|
39577
39744
|
if (onFileProgress) {
|
|
39578
39745
|
displayedFileCount = Math.min(Math.max(displayedFileCount, scannedFileCount), totalFileCount);
|
|
@@ -39646,7 +39813,14 @@ const resolveReactDoctorCacheDir = (projectDirectory) => {
|
|
|
39646
39813
|
if (cacheDirOverride) return Path.join(cacheDirOverride, projectCacheSubdir(projectDirectory));
|
|
39647
39814
|
const nodeModulesDirectory = Path.join(projectDirectory, "node_modules");
|
|
39648
39815
|
if (NFS.existsSync(nodeModulesDirectory)) return Path.join(nodeModulesDirectory, ".cache", "react-doctor");
|
|
39649
|
-
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
|
+
}
|
|
39650
39824
|
};
|
|
39651
39825
|
const sortSourceFilesByCost = (entries) => [...entries].sort((left, right) => right.sizeBytes - left.sizeBytes).map((entry) => entry.path);
|
|
39652
39826
|
/**
|
|
@@ -39814,7 +39988,7 @@ const runOxlint = async (options) => {
|
|
|
39814
39988
|
extendsPaths: [],
|
|
39815
39989
|
ruleSelection: "cacheable"
|
|
39816
39990
|
}),
|
|
39817
|
-
toolchainVersions: resolveOxlintToolchainVersions(),
|
|
39991
|
+
toolchainVersions: resolveOxlintToolchainVersions(nodeBinaryPath),
|
|
39818
39992
|
ignorePatterns: combinedPatterns,
|
|
39819
39993
|
tsconfigContent
|
|
39820
39994
|
});
|
|
@@ -39828,7 +40002,7 @@ const runOxlint = async (options) => {
|
|
|
39828
40002
|
missFiles.push(candidateFile);
|
|
39829
40003
|
continue;
|
|
39830
40004
|
}
|
|
39831
|
-
const cacheKey = `${candidateFile.replaceAll("\\", "/")}
|
|
40005
|
+
const cacheKey = `${candidateFile.replaceAll("\\", "/")}\u0000${contentHash}`;
|
|
39832
40006
|
cacheKeyByFile.set(candidateFile, cacheKey);
|
|
39833
40007
|
const cachedDiagnostics = cache.lookup(cacheKey);
|
|
39834
40008
|
if (cachedDiagnostics === null) missFiles.push(candidateFile);
|
|
@@ -40144,7 +40318,7 @@ const ScoreApiResponseSchema = Struct({
|
|
|
40144
40318
|
})))
|
|
40145
40319
|
});
|
|
40146
40320
|
const parseScoreResult = (value) => getOrNull(decodeUnknownOption(ScoreApiResponseSchema)(value));
|
|
40147
|
-
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);
|
|
40148
40322
|
const isAbortError = (error) => error instanceof Error && (error.name === "AbortError" || error.name === "TimeoutError");
|
|
40149
40323
|
const describeFailure = (error) => {
|
|
40150
40324
|
if (isAbortError(error)) return `timed out after ${FETCH_TIMEOUT_MS / 1e3}s`;
|
|
@@ -40448,7 +40622,10 @@ const ALERT_SEVERITY_RANK = {
|
|
|
40448
40622
|
medium: 2,
|
|
40449
40623
|
low: 1
|
|
40450
40624
|
};
|
|
40451
|
-
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
|
+
};
|
|
40452
40629
|
const displaySeverity = (severity) => {
|
|
40453
40630
|
const normalized = sanitizeTerminalText(severity.toLowerCase());
|
|
40454
40631
|
return normalized === "middle" ? "medium" : normalized;
|
|
@@ -40465,7 +40642,7 @@ const ALERT_TYPE_LABELS = {
|
|
|
40465
40642
|
typosquat: "possible typosquat"
|
|
40466
40643
|
};
|
|
40467
40644
|
const humanizeAlertType = (type) => type.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/[_-]+/g, " ").toLowerCase().trim();
|
|
40468
|
-
const friendlyAlertType = (type) => ALERT_TYPE_LABELS[type]
|
|
40645
|
+
const friendlyAlertType = (type) => Object.hasOwn(ALERT_TYPE_LABELS, type) ? ALERT_TYPE_LABELS[type] : sanitizeTerminalText(humanizeAlertType(type));
|
|
40469
40646
|
const summarizeAlertNote = (note) => {
|
|
40470
40647
|
const collapsed = sanitizeTerminalText(note.replace(/\s+/g, " ").trim());
|
|
40471
40648
|
const firstSentence = collapsed.split(/(?<=\.)\s/)[0] || collapsed;
|
|
@@ -40560,7 +40737,10 @@ const checkSupplyChain = (input) => gen(function* () {
|
|
|
40560
40737
|
* outage.
|
|
40561
40738
|
*/
|
|
40562
40739
|
var SupplyChain = class SupplyChain extends Service()("react-doctor/SupplyChain") {
|
|
40563
|
-
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"))) }));
|
|
40564
40744
|
static layerOf = (diagnostics) => succeed$3(SupplyChain, SupplyChain.of({ run: () => fromIterable$1(diagnostics) }));
|
|
40565
40745
|
};
|
|
40566
40746
|
const getObjectProperty = (value, propertyName) => {
|
|
@@ -40622,7 +40802,10 @@ const formatLintFailText = (reasonTag, nodeVersion) => {
|
|
|
40622
40802
|
* diagnostics).
|
|
40623
40803
|
* 2. beforeLint hook (e.g. CLI renders the project-detection block)
|
|
40624
40804
|
* 3. environment checks (reduced-motion + pnpm hardening +
|
|
40625
|
-
* 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.
|
|
40626
40809
|
* 4. The supply-chain check (Socket.dev) is forked onto a background
|
|
40627
40810
|
* fiber so its ~100% network-bound time overlaps the ~100%
|
|
40628
40811
|
* CPU/subprocess-bound lint pass below, collapsing two serial
|
|
@@ -40642,7 +40825,7 @@ const formatLintFailText = (reasonTag, nodeVersion) => {
|
|
|
40642
40825
|
* order, so terminal output is identical either way; supply-chain
|
|
40643
40826
|
* rides alongside without a spinner.
|
|
40644
40827
|
* 6. Join the supply-chain fiber, then assemble the diagnostics in a
|
|
40645
|
-
* FIXED order (env, supply-chain, lint, dead-code) so the output is
|
|
40828
|
+
* FIXED order (env, security-scan, supply-chain, lint, dead-code) so the output is
|
|
40646
40829
|
* byte-identical regardless of which fiber settled first. The
|
|
40647
40830
|
* viewer-permission fiber is joined later, during score-metadata
|
|
40648
40831
|
* assembly (it feeds score metadata, not diagnostics). The per-element
|
|
@@ -40703,12 +40886,13 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
40703
40886
|
...checkPnpmHardening(scanDirectory),
|
|
40704
40887
|
...checkReactServerComponentsAdvisory(scanDirectory, project),
|
|
40705
40888
|
...checkExpoProject(scanDirectory, project),
|
|
40706
|
-
...checkReactNativeProject(scanDirectory, project)
|
|
40707
|
-
...checkSecurityScan(scanDirectory, {
|
|
40708
|
-
project,
|
|
40709
|
-
ignoredTags: input.ignoredTags
|
|
40710
|
-
})
|
|
40889
|
+
...checkReactNativeProject(scanDirectory, project)
|
|
40711
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")));
|
|
40712
40896
|
const shouldRunSupplyChain = !isDiffMode || (input.supplyChainManifestChanged ?? false);
|
|
40713
40897
|
const supplyChainOverlapTimeout = yield* SupplyChainOverlapTimeoutMs;
|
|
40714
40898
|
const supplyChainFiber = yield* forkChild(shouldRunSupplyChain ? runCollect(applyPerElementPipeline(supplyChainService.run({
|
|
@@ -40746,7 +40930,6 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
40746
40930
|
const lintConcurrency = deadCodeParseConcurrency === void 0 ? scanConcurrency : Math.max(1, scanConcurrency - deadCodeParseConcurrency);
|
|
40747
40931
|
const buildCollectDeadCode = (deadCodeTimeout) => runCollect(applyPerElementPipeline(deadCodeService.run({
|
|
40748
40932
|
rootDirectory: scanDirectory,
|
|
40749
|
-
userConfig: resolvedConfig.config,
|
|
40750
40933
|
parseConcurrency: deadCodeParseConcurrency,
|
|
40751
40934
|
workerTimeoutMs: deadCodeTimeout.workerTimeoutMs
|
|
40752
40935
|
}).pipe(catchTag("ReactDoctorError", (error) => unwrap(gen(function* () {
|
|
@@ -40844,9 +41027,11 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
40844
41027
|
else yield* scanProgress.succeed(`Scanned ${scannedFilesLabel} in ${scanElapsedSeconds}s${workerCountSuffix}`);
|
|
40845
41028
|
const supplyChainResult = yield* join(supplyChainFiber);
|
|
40846
41029
|
const supplyChainCollected = supplyChainResult.diagnostics;
|
|
41030
|
+
const securityScanCollected = yield* join(securityScanFiber);
|
|
40847
41031
|
yield* reporterService.finalize;
|
|
40848
41032
|
const finalDiagnostics = sortDiagnosticsStable(assignFixGroups([
|
|
40849
41033
|
...envCollected,
|
|
41034
|
+
...securityScanCollected,
|
|
40850
41035
|
...supplyChainCollected,
|
|
40851
41036
|
...lintCollected,
|
|
40852
41037
|
...deadCodeCollected
|
|
@@ -40872,6 +41057,7 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
40872
41057
|
metadata: scoreMetadata
|
|
40873
41058
|
});
|
|
40874
41059
|
const lintPartialFailures = yield* get$2(partialFailuresRef);
|
|
41060
|
+
const securityScanFailed = yield* get$2(securityScanFailedRef);
|
|
40875
41061
|
return {
|
|
40876
41062
|
project,
|
|
40877
41063
|
userConfig: resolvedConfig.config,
|
|
@@ -40892,8 +41078,10 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
40892
41078
|
scanElapsedMilliseconds,
|
|
40893
41079
|
scanConcurrency,
|
|
40894
41080
|
supplyChainOverlapTimedOut: supplyChainResult.timedOut,
|
|
41081
|
+
securityScanFailed,
|
|
40895
41082
|
lintCacheHitFileCount,
|
|
40896
|
-
lintCacheTotalFileCount
|
|
41083
|
+
lintCacheTotalFileCount,
|
|
41084
|
+
suppressedRuleCounts: transform.summarizeSuppressions()
|
|
40897
41085
|
};
|
|
40898
41086
|
}).pipe(withSpan("runInspect", { attributes: {
|
|
40899
41087
|
"inspect.directory": input.directory,
|
|
@@ -41298,6 +41486,61 @@ const clearAutoSuppressionCaches = () => {};
|
|
|
41298
41486
|
* untouched so the loader sees the same plain object either way.
|
|
41299
41487
|
*/
|
|
41300
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
|
+
};
|
|
41301
41544
|
const unionValues = (baseValues, overrideValues) => [...new Set([...baseValues, ...overrideValues])];
|
|
41302
41545
|
const mergeIgnores = (baseIgnore, overrideIgnore) => {
|
|
41303
41546
|
const mergedIgnore = {
|
|
@@ -41342,6 +41585,12 @@ const mergeReactDoctorConfigs = (baseConfig, overrideConfig) => {
|
|
|
41342
41585
|
};
|
|
41343
41586
|
//#endregion
|
|
41344
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
|
+
};
|
|
41345
41594
|
const buildDiagnoseLayer = (config, configOverrideTarget) => {
|
|
41346
41595
|
const configLayer = configOverrideTarget === void 0 ? Config.layerNode : Config.layerOf({
|
|
41347
41596
|
config,
|
|
@@ -41379,6 +41628,7 @@ const outputToDiagnoseResult = (output, elapsedMilliseconds) => {
|
|
|
41379
41628
|
};
|
|
41380
41629
|
};
|
|
41381
41630
|
const diagnoseDirectory = async (directory, options) => {
|
|
41631
|
+
warnIfAiTrainingEnvironment();
|
|
41382
41632
|
const startTime = globalThis.performance.now();
|
|
41383
41633
|
const scanTarget = await resolveScanTarget(directory);
|
|
41384
41634
|
return outputToDiagnoseResult(await runPromise(restoreLegacyThrow(buildInspectProgram(scanTarget, options).pipe(provide(buildDiagnoseLayer(scanTarget.userConfig)), provide(layerOtlp)))), globalThis.performance.now() - startTime);
|
|
@@ -41425,6 +41675,7 @@ const diagnoseProject = async (projectDefinition, baseOptions, batchConfig) => {
|
|
|
41425
41675
|
}
|
|
41426
41676
|
};
|
|
41427
41677
|
const diagnoseProjectBatch = async (input) => {
|
|
41678
|
+
warnIfAiTrainingEnvironment();
|
|
41428
41679
|
const startTime = globalThis.performance.now();
|
|
41429
41680
|
const { projects, concurrency, config: batchConfig, ...baseOptions } = input;
|
|
41430
41681
|
const projectResults = await mapWithConcurrency(projects, concurrency ?? 4, (projectDefinition) => diagnoseProject(projectDefinition, baseOptions, batchConfig));
|
|
@@ -41469,4 +41720,4 @@ const toJsonReport = (result, options) => buildJsonReport({
|
|
|
41469
41720
|
export { AmbiguousProjectError, NoReactDependencyError, NotADirectoryError, PackageJsonNotFoundError, ProjectNotFoundError, ReactDoctorError, buildJsonReport, buildJsonReportError, clearCaches, defineConfig, diagnose, filterSourceFiles, getDiffInfo, isProjectDiscoveryError, isReactDoctorError, summarizeDiagnostics, toJsonReport };
|
|
41470
41721
|
|
|
41471
41722
|
//# sourceMappingURL=index.js.map
|
|
41472
|
-
//# debugId=
|
|
41723
|
+
//# debugId=83529d8f-2ca3-50ba-a3b9-44622f913549
|