react-doctor 0.5.8-dev.f853efd → 0.5.8-dev.fff9466
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/dist/cli.js +513 -220
- package/dist/index.d.ts +2 -2
- package/dist/index.js +236 -64
- package/dist/lsp.js +176 -65
- package/package.json +6 -5
package/dist/index.d.ts
CHANGED
|
@@ -9726,7 +9726,7 @@ interface InspectResult {
|
|
|
9726
9726
|
* Per-file lint cache outcome: files served from cache, and total files
|
|
9727
9727
|
* considered. Both absent when the cache was off or bypassed (audit mode,
|
|
9728
9728
|
* adopted `extends`, user plugins). The CLI projects these onto the Sentry
|
|
9729
|
-
* wide event as `
|
|
9729
|
+
* wide event as `lint.cacheHitRatio`.
|
|
9730
9730
|
*/
|
|
9731
9731
|
lintCacheHitFileCount?: number | null;
|
|
9732
9732
|
lintCacheTotalFileCount?: number | null;
|
|
@@ -10123,7 +10123,7 @@ declare const summarizeDiagnostics: (diagnostics: Diagnostic[], worstScore?: num
|
|
|
10123
10123
|
* untouched so the loader sees the same plain object either way.
|
|
10124
10124
|
*/
|
|
10125
10125
|
declare const defineConfig: (config: ReactDoctorConfig) => ReactDoctorConfig; //#endregion
|
|
10126
|
-
//#region src/utils/
|
|
10126
|
+
//#region src/utils/detect-ai-training-environment.d.ts
|
|
10127
10127
|
//#endregion
|
|
10128
10128
|
//#region ../api/dist/index.d.ts
|
|
10129
10129
|
//#region src/diagnose.d.ts
|
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]="faa2d232-c423-58f8-97f4-ba8d41a199d7")}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";
|
|
@@ -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,7 @@ 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;
|
|
33697
33698
|
const PER_WORKER_MEM_BUDGET_BYTES = 1024 * 1024 * 1024;
|
|
33698
33699
|
const DEFAULT_BRANCH_CANDIDATES = ["main", "master"];
|
|
33699
33700
|
const ADOPTABLE_LINT_CONFIG_FILENAMES = [".oxlintrc.json", ".eslintrc.json"];
|
|
@@ -33751,6 +33752,7 @@ const DEAD_CODE_PHASE_TIMEOUT_MS = 15e4;
|
|
|
33751
33752
|
const LINT_PHASE_TIMEOUT_MS = 3e5;
|
|
33752
33753
|
const SCAN_TOTAL_DEADLINE_MS = 9e5;
|
|
33753
33754
|
const DEAD_CODE_WORKER_MAX_OLD_SPACE_MB = 8192;
|
|
33755
|
+
const DEAD_CODE_WORKER_MEM_BUDGET_BYTES = 2 * 1024 * 1024 * 1024;
|
|
33754
33756
|
const DEAD_CODE_TIMEOUT_CEILING_MS = 6e5;
|
|
33755
33757
|
const DEAD_CODE_PHASE_TIMEOUT_OVER_WORKER_MS = 3e4;
|
|
33756
33758
|
const DEAD_CODE_OVERLAP_PARSE_SHARE = .4;
|
|
@@ -35368,7 +35370,7 @@ const resolveScanConcurrency = (requested) => {
|
|
|
35368
35370
|
if (!Number.isFinite(requested) || requested < 1) return 1;
|
|
35369
35371
|
return Math.min(Math.floor(requested), 32);
|
|
35370
35372
|
};
|
|
35371
|
-
const readSystemFacts = () => ({
|
|
35373
|
+
const readSystemFacts$1 = () => ({
|
|
35372
35374
|
availableCores: os.availableParallelism(),
|
|
35373
35375
|
totalMemoryBytes: os.totalmem(),
|
|
35374
35376
|
cgroupMemoryLimitBytes: readCgroupMemoryLimitBytes()
|
|
@@ -35389,7 +35391,7 @@ const readSystemFacts = () => ({
|
|
|
35389
35391
|
* `facts` is injectable so tests exercise core-bound, memory-bound, cgroup-
|
|
35390
35392
|
* limited, and ceiling cases without mocking `os` or the filesystem.
|
|
35391
35393
|
*/
|
|
35392
|
-
const resolveAutoScanConcurrency = (facts = readSystemFacts()) => {
|
|
35394
|
+
const resolveAutoScanConcurrency = (facts = readSystemFacts$1()) => {
|
|
35393
35395
|
const availableMemoryBytes = Math.min(facts.totalMemoryBytes, facts.cgroupMemoryLimitBytes ?? Number.POSITIVE_INFINITY);
|
|
35394
35396
|
const memoryBoundedWorkers = Math.floor(availableMemoryBytes / PER_WORKER_MEM_BUDGET_BYTES);
|
|
35395
35397
|
return resolveScanConcurrency(Math.min(facts.availableCores, memoryBoundedWorkers));
|
|
@@ -36922,7 +36924,10 @@ const shouldEnableRule = (requires, tags, capabilities, ignoredTags, disabledBy)
|
|
|
36922
36924
|
}
|
|
36923
36925
|
return true;
|
|
36924
36926
|
};
|
|
36925
|
-
const
|
|
36927
|
+
const yieldToEventLoop = () => new Promise((resolve) => {
|
|
36928
|
+
setImmediate(resolve);
|
|
36929
|
+
});
|
|
36930
|
+
const createSecurityScanSession = (rootDirectory, options) => {
|
|
36926
36931
|
const capabilities = options.project ? buildCapabilities(options.project) : /* @__PURE__ */ new Set();
|
|
36927
36932
|
const ignoredTags = options.ignoredTags ?? /* @__PURE__ */ new Set();
|
|
36928
36933
|
const enabledScanRules = REACT_DOCTOR_RULES.flatMap((entry) => {
|
|
@@ -36937,7 +36942,7 @@ const checkSecurityScan = (rootDirectory, options = {}) => {
|
|
|
36937
36942
|
committedFilesOnly: rule.committedFilesOnly === true
|
|
36938
36943
|
}];
|
|
36939
36944
|
});
|
|
36940
|
-
if (enabledScanRules.length === 0) return
|
|
36945
|
+
if (enabledScanRules.length === 0) return null;
|
|
36941
36946
|
const diagnostics = [];
|
|
36942
36947
|
const seen = /* @__PURE__ */ new Set();
|
|
36943
36948
|
const gitIgnoredCache = /* @__PURE__ */ new Map();
|
|
@@ -36949,15 +36954,34 @@ const checkSecurityScan = (rootDirectory, options = {}) => {
|
|
|
36949
36954
|
}
|
|
36950
36955
|
return status === true;
|
|
36951
36956
|
};
|
|
36952
|
-
|
|
36953
|
-
|
|
36954
|
-
|
|
36955
|
-
|
|
36956
|
-
|
|
36957
|
-
|
|
36958
|
-
|
|
36957
|
+
const scanFile = (file) => {
|
|
36958
|
+
for (const { entry, scan, committedFilesOnly } of enabledScanRules) for (const finding of scan(file)) {
|
|
36959
|
+
if (committedFilesOnly && isFileGitIgnored(file)) continue;
|
|
36960
|
+
const diagnostic = buildSecurityScanDiagnostic(finding, entry, file.relativePath);
|
|
36961
|
+
const key = `${diagnostic.rule}:${diagnostic.filePath}:${diagnostic.line}:${diagnostic.column}:${diagnostic.message}`;
|
|
36962
|
+
if (seen.has(key)) continue;
|
|
36963
|
+
seen.add(key);
|
|
36964
|
+
diagnostics.push(diagnostic);
|
|
36965
|
+
}
|
|
36966
|
+
};
|
|
36967
|
+
return {
|
|
36968
|
+
scanFile,
|
|
36969
|
+
diagnostics
|
|
36970
|
+
};
|
|
36971
|
+
};
|
|
36972
|
+
const checkSecurityScanCooperative = async (rootDirectory, options = {}) => {
|
|
36973
|
+
const session = createSecurityScanSession(rootDirectory, options);
|
|
36974
|
+
if (session === null) return [];
|
|
36975
|
+
let filesSinceYield = 0;
|
|
36976
|
+
for (const file of collectSecurityScanFiles(rootDirectory)) {
|
|
36977
|
+
session.scanFile(file);
|
|
36978
|
+
filesSinceYield += 1;
|
|
36979
|
+
if (filesSinceYield >= 16) {
|
|
36980
|
+
filesSinceYield = 0;
|
|
36981
|
+
await yieldToEventLoop();
|
|
36982
|
+
}
|
|
36959
36983
|
}
|
|
36960
|
-
return diagnostics;
|
|
36984
|
+
return session.diagnostics;
|
|
36961
36985
|
};
|
|
36962
36986
|
var import_picocolors = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
36963
36987
|
let p = process || {}, argv = p.argv || [], env = p.env || {};
|
|
@@ -37174,6 +37198,74 @@ const collectDeadCodeIgnorePatterns = (rootDirectory) => {
|
|
|
37174
37198
|
return [...seen].filter((pattern) => pattern.length > 0);
|
|
37175
37199
|
};
|
|
37176
37200
|
const collectDeadCodeEntryPatterns = (rootDirectory) => [...new Set(collectKnipPatterns(rootDirectory, "entry"))].filter((pattern) => pattern.length > 0);
|
|
37201
|
+
const readSystemFacts = () => ({
|
|
37202
|
+
availableCores: os.availableParallelism(),
|
|
37203
|
+
totalMemoryBytes: os.totalmem(),
|
|
37204
|
+
cgroupMemoryLimitBytes: readCgroupMemoryLimitBytes()
|
|
37205
|
+
});
|
|
37206
|
+
/**
|
|
37207
|
+
* How many real deslop dead-code child processes may run at once, across the
|
|
37208
|
+
* concurrent per-project `runInspect` fibers of one CLI run. The cap is the
|
|
37209
|
+
* smaller of the core count and the number of `DEAD_CODE_WORKER_MEM_BUDGET_BYTES`
|
|
37210
|
+
* workers that fit in available memory, floored at 1.
|
|
37211
|
+
*
|
|
37212
|
+
* On a roomy dev box / CI runner this resolves high enough that every
|
|
37213
|
+
* concurrently-scanned project still spawns its own worker (no serialization vs
|
|
37214
|
+
* the prior uncapped behavior); on a memory-constrained runner it collapses
|
|
37215
|
+
* toward 1, so the `withDeadCodeWorkerSlot` semaphore serializes the spawns
|
|
37216
|
+
* instead of oversubscribing memory with N simultaneous children — the global
|
|
37217
|
+
* cap the per-project spawn path lacked.
|
|
37218
|
+
*
|
|
37219
|
+
* Mirrors `resolveAutoScanConcurrency` (lint), but budgets memory per the
|
|
37220
|
+
* heavier dead-code worker. `facts` is injectable for tests.
|
|
37221
|
+
*/
|
|
37222
|
+
const resolveDeadCodeConcurrency = (facts = readSystemFacts()) => {
|
|
37223
|
+
const availableMemoryBytes = Math.min(facts.totalMemoryBytes, facts.cgroupMemoryLimitBytes ?? Number.POSITIVE_INFINITY);
|
|
37224
|
+
const memoryBoundedWorkers = Math.floor(availableMemoryBytes / DEAD_CODE_WORKER_MEM_BUDGET_BYTES);
|
|
37225
|
+
return Math.max(1, Math.min(facts.availableCores, memoryBoundedWorkers));
|
|
37226
|
+
};
|
|
37227
|
+
let availableSlots = -1;
|
|
37228
|
+
const waiters = [];
|
|
37229
|
+
const releaseSlot = () => {
|
|
37230
|
+
const nextWaiter = waiters.shift();
|
|
37231
|
+
if (nextWaiter !== void 0) nextWaiter();
|
|
37232
|
+
else availableSlots += 1;
|
|
37233
|
+
};
|
|
37234
|
+
/**
|
|
37235
|
+
* Runs `task` once a dead-code worker slot is free, releasing the slot when the
|
|
37236
|
+
* task settles (success or failure). With a high cap (roomy machine) every
|
|
37237
|
+
* caller proceeds immediately; with a low cap (constrained runner) callers
|
|
37238
|
+
* queue and run as slots free.
|
|
37239
|
+
*
|
|
37240
|
+
* `abortSignal` short-circuits the WAIT: if it's already aborted, or fires while
|
|
37241
|
+
* this caller is queued, the call rejects without acquiring a slot or running
|
|
37242
|
+
* `task` — so a cancelled scan (e.g. lint failed) doesn't sit in the queue and
|
|
37243
|
+
* then spawn a child only to tear it down. A queued caller that aborts removes
|
|
37244
|
+
* its own waiter so a later release never hands a slot to a dead request.
|
|
37245
|
+
*/
|
|
37246
|
+
const withDeadCodeWorkerSlot = async (task, abortSignal) => {
|
|
37247
|
+
if (abortSignal?.aborted) throw new Error("Dead-code worker aborted.");
|
|
37248
|
+
if (availableSlots < 0) availableSlots = resolveDeadCodeConcurrency();
|
|
37249
|
+
if (availableSlots > 0) availableSlots -= 1;
|
|
37250
|
+
else await new Promise((resolve, reject) => {
|
|
37251
|
+
const waiter = () => {
|
|
37252
|
+
abortSignal?.removeEventListener("abort", onAbort);
|
|
37253
|
+
resolve();
|
|
37254
|
+
};
|
|
37255
|
+
const onAbort = () => {
|
|
37256
|
+
const queuedIndex = waiters.indexOf(waiter);
|
|
37257
|
+
if (queuedIndex !== -1) waiters.splice(queuedIndex, 1);
|
|
37258
|
+
reject(/* @__PURE__ */ new Error("Dead-code worker aborted."));
|
|
37259
|
+
};
|
|
37260
|
+
waiters.push(waiter);
|
|
37261
|
+
abortSignal?.addEventListener("abort", onAbort, { once: true });
|
|
37262
|
+
});
|
|
37263
|
+
try {
|
|
37264
|
+
return await task();
|
|
37265
|
+
} finally {
|
|
37266
|
+
releaseSlot();
|
|
37267
|
+
}
|
|
37268
|
+
};
|
|
37177
37269
|
/**
|
|
37178
37270
|
* Resolves a path to its canonical, symlink-free form, falling back to
|
|
37179
37271
|
* the input when it cannot be realpath'd (broken symlink, permission
|
|
@@ -37198,6 +37290,11 @@ const toCanonicalPath = (filePath) => {
|
|
|
37198
37290
|
};
|
|
37199
37291
|
const DEAD_CODE_PLUGIN = "deslop";
|
|
37200
37292
|
const DEAD_CODE_CATEGORY = "Maintainability";
|
|
37293
|
+
const REACT_DOCTOR_TOOLCHAIN_PACKAGES = new Set([
|
|
37294
|
+
"react-doctor",
|
|
37295
|
+
"eslint-plugin-react-doctor",
|
|
37296
|
+
"oxlint-plugin-react-doctor"
|
|
37297
|
+
]);
|
|
37201
37298
|
const DEAD_CODE_WORKER_SCRIPT = `
|
|
37202
37299
|
const inputChunks = [];
|
|
37203
37300
|
process.stdin.on("data", (chunk) => inputChunks.push(chunk));
|
|
@@ -37464,14 +37561,17 @@ const checkDeadCode = async (options) => {
|
|
|
37464
37561
|
if (!NFS.existsSync(Path.join(rootDirectory, "package.json"))) return [];
|
|
37465
37562
|
const entryPatterns = collectDeadCodeEntryPatterns(rootDirectory);
|
|
37466
37563
|
const ignorePatterns = collectDeadCodeIgnorePatterns(rootDirectory);
|
|
37467
|
-
const
|
|
37468
|
-
|
|
37469
|
-
|
|
37470
|
-
|
|
37471
|
-
|
|
37472
|
-
|
|
37473
|
-
|
|
37474
|
-
|
|
37564
|
+
const spawnAndRun = () => {
|
|
37565
|
+
return runDeadCodeWorkerWithTimeout((options.createWorker ?? createDeadCodeWorker)({
|
|
37566
|
+
rootDirectory,
|
|
37567
|
+
entryPatterns,
|
|
37568
|
+
tsConfigPath: resolveTsConfigPath(rootDirectory),
|
|
37569
|
+
ignorePatterns,
|
|
37570
|
+
deslopJsModuleSpecifier: options.deslopJsModuleSpecifier ?? import.meta.resolve("deslop-js"),
|
|
37571
|
+
parseConcurrency: options.parseConcurrency
|
|
37572
|
+
}), options.workerTimeoutMs ?? 12e4, options.abortSignal);
|
|
37573
|
+
};
|
|
37574
|
+
const result = parseDeadCodeWorkerResult(options.createWorker === void 0 ? await withDeadCodeWorkerSlot(spawnAndRun, options.abortSignal) : await spawnAndRun());
|
|
37475
37575
|
const toRelative = (filePath) => toRelativeFilePath(rootDirectory, filePath);
|
|
37476
37576
|
const diagnostics = [];
|
|
37477
37577
|
for (const unusedFile of result.unusedFiles) diagnostics.push({
|
|
@@ -37500,6 +37600,7 @@ const checkDeadCode = async (options) => {
|
|
|
37500
37600
|
});
|
|
37501
37601
|
}
|
|
37502
37602
|
for (const unusedDependency of result.unusedDependencies) {
|
|
37603
|
+
if (REACT_DOCTOR_TOOLCHAIN_PACKAGES.has(unusedDependency.name)) continue;
|
|
37503
37604
|
const label = unusedDependency.isDevDependency ? "devDependency" : "dependency";
|
|
37504
37605
|
diagnostics.push({
|
|
37505
37606
|
filePath: "package.json",
|
|
@@ -37872,43 +37973,46 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
37872
37973
|
* reason: GitInvocationFailed })` so the rest of the codebase
|
|
37873
37974
|
* sees a single failure channel.
|
|
37874
37975
|
*/
|
|
37875
|
-
const runCommand = (input) =>
|
|
37876
|
-
const
|
|
37877
|
-
cwd: input.directory,
|
|
37878
|
-
env: input.env,
|
|
37879
|
-
extendEnv: true
|
|
37880
|
-
}));
|
|
37881
|
-
const maxStdoutBytes = input.maxStdoutBytes;
|
|
37882
|
-
const stdoutByteCount = yield* make$13(0);
|
|
37883
|
-
const [stdout, stderr, status] = yield* all([
|
|
37884
|
-
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({
|
|
37885
|
-
args: [...input.args],
|
|
37886
|
-
directory: input.directory,
|
|
37887
|
-
cause: /* @__PURE__ */ new Error(`git stdout exceeded ${maxStdoutBytes} bytes`)
|
|
37888
|
-
}) })) : void_)))))),
|
|
37889
|
-
mkString(decodeText(handle.stderr)),
|
|
37890
|
-
handle.exitCode
|
|
37891
|
-
], { concurrency: 3 });
|
|
37892
|
-
return {
|
|
37893
|
-
status,
|
|
37894
|
-
stdout,
|
|
37895
|
-
stderr
|
|
37896
|
-
};
|
|
37897
|
-
})).pipe(catchTag$1("PlatformError", (cause) => {
|
|
37898
|
-
if (input.command !== "git") return succeed$2({
|
|
37976
|
+
const runCommand = (input) => {
|
|
37977
|
+
const foldSpawnFailure = (cause) => input.command !== "git" ? succeed$2({
|
|
37899
37978
|
status: 127,
|
|
37900
37979
|
stdout: "",
|
|
37901
37980
|
stderr: String(cause)
|
|
37902
|
-
})
|
|
37903
|
-
return new ReactDoctorError({ reason: new GitInvocationFailed({
|
|
37981
|
+
}) : fail$4(new ReactDoctorError({ reason: new GitInvocationFailed({
|
|
37904
37982
|
args: [...input.args],
|
|
37905
37983
|
directory: input.directory,
|
|
37906
37984
|
cause
|
|
37907
|
-
}) });
|
|
37908
|
-
|
|
37909
|
-
|
|
37910
|
-
|
|
37911
|
-
|
|
37985
|
+
}) }));
|
|
37986
|
+
return scoped(gen(function* () {
|
|
37987
|
+
if (!isDirectory(input.directory)) return yield* foldSpawnFailure(`spawn ENOTDIR (cwd is not a directory: ${input.directory})`);
|
|
37988
|
+
const argvLengthChars = input.command.length + 1 + input.args.reduce((total, arg) => total + arg.length + 1, 0);
|
|
37989
|
+
if (argvLengthChars > 24e3) return yield* foldSpawnFailure(`spawn ENAMETOOLONG (${argvLengthChars} argv chars exceed ${SPAWN_ARGS_MAX_LENGTH_CHARS})`);
|
|
37990
|
+
const handle = yield* spawner.spawn(make$1(input.command, [...input.args], {
|
|
37991
|
+
cwd: input.directory,
|
|
37992
|
+
env: input.env,
|
|
37993
|
+
extendEnv: true
|
|
37994
|
+
}));
|
|
37995
|
+
const maxStdoutBytes = input.maxStdoutBytes;
|
|
37996
|
+
const stdoutByteCount = yield* make$13(0);
|
|
37997
|
+
const [stdout, stderr, status] = yield* all([
|
|
37998
|
+
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({
|
|
37999
|
+
args: [...input.args],
|
|
38000
|
+
directory: input.directory,
|
|
38001
|
+
cause: /* @__PURE__ */ new Error(`git stdout exceeded ${maxStdoutBytes} bytes`)
|
|
38002
|
+
}) })) : void_)))))),
|
|
38003
|
+
mkString(decodeText(handle.stderr)),
|
|
38004
|
+
handle.exitCode
|
|
38005
|
+
], { concurrency: 3 });
|
|
38006
|
+
return {
|
|
38007
|
+
status,
|
|
38008
|
+
stdout,
|
|
38009
|
+
stderr
|
|
38010
|
+
};
|
|
38011
|
+
})).pipe(catchTag$1("PlatformError", foldSpawnFailure), withSpan("git.exec", { attributes: {
|
|
38012
|
+
"git.command": input.command,
|
|
38013
|
+
"git.subcommand": input.args[0] ?? ""
|
|
38014
|
+
} }));
|
|
38015
|
+
};
|
|
37912
38016
|
const runGit = (directory, args) => runCommand({
|
|
37913
38017
|
command: "git",
|
|
37914
38018
|
args,
|
|
@@ -38155,7 +38259,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
38155
38259
|
]);
|
|
38156
38260
|
if (result.status !== 0) return null;
|
|
38157
38261
|
return parseChangedLineRanges(result.stdout);
|
|
38158
|
-
}).pipe(withSpan("Git.changedLineRanges"))
|
|
38262
|
+
}).pipe(catch_$1((error) => error.reason._tag === "GitInvocationFailed" ? succeed$2(null) : fail$4(error)), withSpan("Git.changedLineRanges"))
|
|
38159
38263
|
});
|
|
38160
38264
|
})).pipe(provide$2(layer$2.pipe(provide$2(mergeAll$1(layer$1, layer)))));
|
|
38161
38265
|
/**
|
|
@@ -40632,7 +40736,10 @@ const formatLintFailText = (reasonTag, nodeVersion) => {
|
|
|
40632
40736
|
* diagnostics).
|
|
40633
40737
|
* 2. beforeLint hook (e.g. CLI renders the project-detection block)
|
|
40634
40738
|
* 3. environment checks (reduced-motion + pnpm hardening +
|
|
40635
|
-
* expo/react-native
|
|
40739
|
+
* expo/react-native), collected synchronously. The heavier
|
|
40740
|
+
* content-regex security scan is forked instead (like supply-chain
|
|
40741
|
+
* below) and joined before the concat, so its CPU overlaps lint
|
|
40742
|
+
* rather than blocking the event loop before it.
|
|
40636
40743
|
* 4. The supply-chain check (Socket.dev) is forked onto a background
|
|
40637
40744
|
* fiber so its ~100% network-bound time overlaps the ~100%
|
|
40638
40745
|
* CPU/subprocess-bound lint pass below, collapsing two serial
|
|
@@ -40652,7 +40759,7 @@ const formatLintFailText = (reasonTag, nodeVersion) => {
|
|
|
40652
40759
|
* order, so terminal output is identical either way; supply-chain
|
|
40653
40760
|
* rides alongside without a spinner.
|
|
40654
40761
|
* 6. Join the supply-chain fiber, then assemble the diagnostics in a
|
|
40655
|
-
* FIXED order (env, supply-chain, lint, dead-code) so the output is
|
|
40762
|
+
* FIXED order (env, security-scan, supply-chain, lint, dead-code) so the output is
|
|
40656
40763
|
* byte-identical regardless of which fiber settled first. The
|
|
40657
40764
|
* viewer-permission fiber is joined later, during score-metadata
|
|
40658
40765
|
* assembly (it feeds score metadata, not diagnostics). The per-element
|
|
@@ -40713,12 +40820,12 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
40713
40820
|
...checkPnpmHardening(scanDirectory),
|
|
40714
40821
|
...checkReactServerComponentsAdvisory(scanDirectory, project),
|
|
40715
40822
|
...checkExpoProject(scanDirectory, project),
|
|
40716
|
-
...checkReactNativeProject(scanDirectory, project)
|
|
40717
|
-
...checkSecurityScan(scanDirectory, {
|
|
40718
|
-
project,
|
|
40719
|
-
ignoredTags: input.ignoredTags
|
|
40720
|
-
})
|
|
40823
|
+
...checkReactNativeProject(scanDirectory, project)
|
|
40721
40824
|
])));
|
|
40825
|
+
const securityScanFiber = yield* forkChild(runCollect(applyPerElementPipeline(isDiffMode ? empty$4 : unwrap(promise(() => checkSecurityScanCooperative(scanDirectory, {
|
|
40826
|
+
project,
|
|
40827
|
+
ignoredTags: input.ignoredTags
|
|
40828
|
+
})).pipe(map$3((diagnostics) => fromIterable$1(diagnostics)))))).pipe(withSpan("SecurityScan.run")));
|
|
40722
40829
|
const shouldRunSupplyChain = !isDiffMode || (input.supplyChainManifestChanged ?? false);
|
|
40723
40830
|
const supplyChainOverlapTimeout = yield* SupplyChainOverlapTimeoutMs;
|
|
40724
40831
|
const supplyChainFiber = yield* forkChild(shouldRunSupplyChain ? runCollect(applyPerElementPipeline(supplyChainService.run({
|
|
@@ -40854,9 +40961,11 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
40854
40961
|
else yield* scanProgress.succeed(`Scanned ${scannedFilesLabel} in ${scanElapsedSeconds}s${workerCountSuffix}`);
|
|
40855
40962
|
const supplyChainResult = yield* join(supplyChainFiber);
|
|
40856
40963
|
const supplyChainCollected = supplyChainResult.diagnostics;
|
|
40964
|
+
const securityScanCollected = yield* join(securityScanFiber);
|
|
40857
40965
|
yield* reporterService.finalize;
|
|
40858
40966
|
const finalDiagnostics = sortDiagnosticsStable(assignFixGroups([
|
|
40859
40967
|
...envCollected,
|
|
40968
|
+
...securityScanCollected,
|
|
40860
40969
|
...supplyChainCollected,
|
|
40861
40970
|
...lintCollected,
|
|
40862
40971
|
...deadCodeCollected
|
|
@@ -41308,6 +41417,61 @@ const clearAutoSuppressionCaches = () => {};
|
|
|
41308
41417
|
* untouched so the loader sees the same plain object either way.
|
|
41309
41418
|
*/
|
|
41310
41419
|
const defineConfig = (config) => config;
|
|
41420
|
+
const AI_TRAINING_ENV_VARS = [
|
|
41421
|
+
["HF_DATASETS_CACHE", "huggingface"],
|
|
41422
|
+
["HF_HOME", "huggingface"],
|
|
41423
|
+
["HUGGINGFACE_HUB_CACHE", "huggingface"],
|
|
41424
|
+
["SPACE_ID", "huggingface-spaces"],
|
|
41425
|
+
["CUDA_VISIBLE_DEVICES", "cuda"],
|
|
41426
|
+
["NVIDIA_VISIBLE_DEVICES", "nvidia"],
|
|
41427
|
+
["ROCR_VISIBLE_DEVICES", "rocm"],
|
|
41428
|
+
["TPU_NAME", "google-tpu"],
|
|
41429
|
+
["WANDB_RUN_ID", "wandb"],
|
|
41430
|
+
["WANDB_SWEEP_ID", "wandb"],
|
|
41431
|
+
["MLFLOW_RUN_ID", "mlflow"],
|
|
41432
|
+
["MLFLOW_TRACKING_URI", "mlflow"],
|
|
41433
|
+
["COMET_EXPERIMENT_KEY", "comet"],
|
|
41434
|
+
["NEPTUNE_RUN_ID", "neptune"],
|
|
41435
|
+
["CLEARML_TASK_ID", "clearml"],
|
|
41436
|
+
["DVC_STAGE", "dvc"],
|
|
41437
|
+
["RAY_WORKER_PROCESS", "ray"],
|
|
41438
|
+
["RAY_ADDRESS", "ray"],
|
|
41439
|
+
["MUJOCO_GL", "mujoco"],
|
|
41440
|
+
["MUJOCO_PATH", "mujoco"],
|
|
41441
|
+
["GYM_DISABLE_ENV_CHECKER", "gymnasium"],
|
|
41442
|
+
["SM_TRAINING_ENV", "sagemaker"],
|
|
41443
|
+
["TRAINING_JOB_ARN", "sagemaker"],
|
|
41444
|
+
["SAGEMAKER_BASE_DIR", "sagemaker"],
|
|
41445
|
+
["AZUREML_RUN_ID", "azure-ml"],
|
|
41446
|
+
["AZURE_ML_MODEL_DIR", "azure-ml"],
|
|
41447
|
+
["CLOUD_ML_PROJECT_ID", "vertex-ai"],
|
|
41448
|
+
["VERTEX_AI_LOG_LEVEL", "vertex-ai"],
|
|
41449
|
+
["DET_MASTER", "determined-ai"],
|
|
41450
|
+
["LIGHTNING_USER_ID", "lightning-ai"],
|
|
41451
|
+
["FLYTE_INTERNAL_EXECUTION_ID", "flyte"],
|
|
41452
|
+
["ARGO_WORKFLOW_NAME", "argo-workflows"],
|
|
41453
|
+
["KFP_POD_NAME", "kubeflow-pipelines"],
|
|
41454
|
+
["KAGGLE_KERNEL_RUN_TYPE", "kaggle"],
|
|
41455
|
+
["COLAB_BACKEND_VERSION", "google-colab"],
|
|
41456
|
+
["DATABRICKS_RUNTIME_VERSION", "databricks"],
|
|
41457
|
+
["DAYTONA_WS_ID", "daytona"],
|
|
41458
|
+
["DAYTONA_WS_NAME", "daytona"],
|
|
41459
|
+
["E2B_SANDBOX_ID", "e2b"],
|
|
41460
|
+
["MODAL_FUNCTION_ID", "modal"],
|
|
41461
|
+
["MODAL_TASK_ID", "modal"],
|
|
41462
|
+
["RUNPOD_POD_ID", "runpod"],
|
|
41463
|
+
["REPLICATE_USERNAME", "replicate"],
|
|
41464
|
+
["VAST_CONTAINERLABEL", "vast-ai"],
|
|
41465
|
+
["HARBOR_URL", "harbor"],
|
|
41466
|
+
["HARBOR_HOSTNAME", "harbor"],
|
|
41467
|
+
["SWE_BENCH_TASK", "swe-bench"],
|
|
41468
|
+
["SWEBENCH_TASK", "swe-bench"],
|
|
41469
|
+
["SWE_AGENT_MODEL", "swe-agent"]
|
|
41470
|
+
];
|
|
41471
|
+
const detectAiTrainingEnvironment = () => {
|
|
41472
|
+
for (const [envVar, label] of AI_TRAINING_ENV_VARS) if (process.env[envVar]) return label;
|
|
41473
|
+
return null;
|
|
41474
|
+
};
|
|
41311
41475
|
const unionValues = (baseValues, overrideValues) => [...new Set([...baseValues, ...overrideValues])];
|
|
41312
41476
|
const mergeIgnores = (baseIgnore, overrideIgnore) => {
|
|
41313
41477
|
const mergedIgnore = {
|
|
@@ -41352,6 +41516,12 @@ const mergeReactDoctorConfigs = (baseConfig, overrideConfig) => {
|
|
|
41352
41516
|
};
|
|
41353
41517
|
//#endregion
|
|
41354
41518
|
//#region ../api/dist/index.js
|
|
41519
|
+
let didWarnAiTraining = false;
|
|
41520
|
+
const warnIfAiTrainingEnvironment = () => {
|
|
41521
|
+
if (didWarnAiTraining || detectAiTrainingEnvironment() === null) return;
|
|
41522
|
+
didWarnAiTraining = true;
|
|
41523
|
+
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.");
|
|
41524
|
+
};
|
|
41355
41525
|
const buildDiagnoseLayer = (config, configOverrideTarget) => {
|
|
41356
41526
|
const configLayer = configOverrideTarget === void 0 ? Config.layerNode : Config.layerOf({
|
|
41357
41527
|
config,
|
|
@@ -41389,6 +41559,7 @@ const outputToDiagnoseResult = (output, elapsedMilliseconds) => {
|
|
|
41389
41559
|
};
|
|
41390
41560
|
};
|
|
41391
41561
|
const diagnoseDirectory = async (directory, options) => {
|
|
41562
|
+
warnIfAiTrainingEnvironment();
|
|
41392
41563
|
const startTime = globalThis.performance.now();
|
|
41393
41564
|
const scanTarget = await resolveScanTarget(directory);
|
|
41394
41565
|
return outputToDiagnoseResult(await runPromise(restoreLegacyThrow(buildInspectProgram(scanTarget, options).pipe(provide(buildDiagnoseLayer(scanTarget.userConfig)), provide(layerOtlp)))), globalThis.performance.now() - startTime);
|
|
@@ -41435,6 +41606,7 @@ const diagnoseProject = async (projectDefinition, baseOptions, batchConfig) => {
|
|
|
41435
41606
|
}
|
|
41436
41607
|
};
|
|
41437
41608
|
const diagnoseProjectBatch = async (input) => {
|
|
41609
|
+
warnIfAiTrainingEnvironment();
|
|
41438
41610
|
const startTime = globalThis.performance.now();
|
|
41439
41611
|
const { projects, concurrency, config: batchConfig, ...baseOptions } = input;
|
|
41440
41612
|
const projectResults = await mapWithConcurrency(projects, concurrency ?? 4, (projectDefinition) => diagnoseProject(projectDefinition, baseOptions, batchConfig));
|
|
@@ -41479,4 +41651,4 @@ const toJsonReport = (result, options) => buildJsonReport({
|
|
|
41479
41651
|
export { AmbiguousProjectError, NoReactDependencyError, NotADirectoryError, PackageJsonNotFoundError, ProjectNotFoundError, ReactDoctorError, buildJsonReport, buildJsonReportError, clearCaches, defineConfig, diagnose, filterSourceFiles, getDiffInfo, isProjectDiscoveryError, isReactDoctorError, summarizeDiagnostics, toJsonReport };
|
|
41480
41652
|
|
|
41481
41653
|
//# sourceMappingURL=index.js.map
|
|
41482
|
-
//# debugId=
|
|
41654
|
+
//# debugId=faa2d232-c423-58f8-97f4-ba8d41a199d7
|