react-doctor 0.5.8-dev.ea00b1b → 0.5.8-dev.f69f216
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 +372 -107
- package/dist/index.d.ts +2 -2
- package/dist/index.js +194 -32
- package/dist/lsp.js +134 -33
- package/package.json +8 -7
package/dist/cli.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="
|
|
2
|
+
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="fabe3e34-b40f-514e-a5af-fa68a36365ae")}catch(e){}}();
|
|
3
3
|
import { createRequire } from "node:module";
|
|
4
4
|
import * as NodeChildProcess from "node:child_process";
|
|
5
5
|
import { execFile, execFileSync, spawn, spawnSync } from "node:child_process";
|
|
@@ -9041,7 +9041,7 @@ const composePassthrough = /* @__PURE__ */ dual(2, (left, right) => (input) => {
|
|
|
9041
9041
|
* @since 2.0.0
|
|
9042
9042
|
*/
|
|
9043
9043
|
const Scheduler = /* @__PURE__ */ Reference("effect/Scheduler", { defaultValue: () => new MixedScheduler() });
|
|
9044
|
-
const setImmediate = "setImmediate" in globalThis ? (f) => {
|
|
9044
|
+
const setImmediate$1 = "setImmediate" in globalThis ? (f) => {
|
|
9045
9045
|
const timer = globalThis.setImmediate(f);
|
|
9046
9046
|
return () => globalThis.clearImmediate(timer);
|
|
9047
9047
|
} : (f) => {
|
|
@@ -9085,7 +9085,7 @@ var PriorityBuckets = class {
|
|
|
9085
9085
|
var MixedScheduler = class {
|
|
9086
9086
|
executionMode;
|
|
9087
9087
|
setImmediate;
|
|
9088
|
-
constructor(executionMode = "async", setImmediateFn = setImmediate) {
|
|
9088
|
+
constructor(executionMode = "async", setImmediateFn = setImmediate$1) {
|
|
9089
9089
|
this.executionMode = executionMode;
|
|
9090
9090
|
this.setImmediate = setImmediateFn;
|
|
9091
9091
|
}
|
|
@@ -9110,7 +9110,7 @@ var MixedSchedulerDispatcher = class {
|
|
|
9110
9110
|
tasks = /* @__PURE__ */ new PriorityBuckets();
|
|
9111
9111
|
running = void 0;
|
|
9112
9112
|
setImmediate;
|
|
9113
|
-
constructor(setImmediateFn = setImmediate) {
|
|
9113
|
+
constructor(setImmediateFn = setImmediate$1) {
|
|
9114
9114
|
this.setImmediate = setImmediateFn;
|
|
9115
9115
|
}
|
|
9116
9116
|
/**
|
|
@@ -36947,6 +36947,7 @@ const DEAD_CODE_PHASE_TIMEOUT_MS = 15e4;
|
|
|
36947
36947
|
const LINT_PHASE_TIMEOUT_MS = 3e5;
|
|
36948
36948
|
const SCAN_TOTAL_DEADLINE_MS = 9e5;
|
|
36949
36949
|
const DEAD_CODE_WORKER_MAX_OLD_SPACE_MB = 8192;
|
|
36950
|
+
const DEAD_CODE_WORKER_MEM_BUDGET_BYTES = 2 * 1024 * 1024 * 1024;
|
|
36950
36951
|
const DEAD_CODE_TIMEOUT_CEILING_MS = 6e5;
|
|
36951
36952
|
const DEAD_CODE_PHASE_TIMEOUT_OVER_WORKER_MS = 3e4;
|
|
36952
36953
|
const DEAD_CODE_OVERLAP_PARSE_SHARE = .4;
|
|
@@ -38557,7 +38558,7 @@ const resolveScanConcurrency = (requested) => {
|
|
|
38557
38558
|
if (!Number.isFinite(requested) || requested < 1) return 1;
|
|
38558
38559
|
return Math.min(Math.floor(requested), 32);
|
|
38559
38560
|
};
|
|
38560
|
-
const readSystemFacts = () => ({
|
|
38561
|
+
const readSystemFacts$1 = () => ({
|
|
38561
38562
|
availableCores: os.availableParallelism(),
|
|
38562
38563
|
totalMemoryBytes: os.totalmem(),
|
|
38563
38564
|
cgroupMemoryLimitBytes: readCgroupMemoryLimitBytes()
|
|
@@ -38578,7 +38579,7 @@ const readSystemFacts = () => ({
|
|
|
38578
38579
|
* `facts` is injectable so tests exercise core-bound, memory-bound, cgroup-
|
|
38579
38580
|
* limited, and ceiling cases without mocking `os` or the filesystem.
|
|
38580
38581
|
*/
|
|
38581
|
-
const resolveAutoScanConcurrency = (facts = readSystemFacts()) => {
|
|
38582
|
+
const resolveAutoScanConcurrency = (facts = readSystemFacts$1()) => {
|
|
38582
38583
|
const availableMemoryBytes = Math.min(facts.totalMemoryBytes, facts.cgroupMemoryLimitBytes ?? Number.POSITIVE_INFINITY);
|
|
38583
38584
|
const memoryBoundedWorkers = Math.floor(availableMemoryBytes / PER_WORKER_MEM_BUDGET_BYTES);
|
|
38584
38585
|
return resolveScanConcurrency(Math.min(facts.availableCores, memoryBoundedWorkers));
|
|
@@ -40139,7 +40140,10 @@ const shouldEnableRule = (requires, tags, capabilities, ignoredTags, disabledBy)
|
|
|
40139
40140
|
}
|
|
40140
40141
|
return true;
|
|
40141
40142
|
};
|
|
40142
|
-
const
|
|
40143
|
+
const yieldToEventLoop = () => new Promise((resolve) => {
|
|
40144
|
+
setImmediate(resolve);
|
|
40145
|
+
});
|
|
40146
|
+
const createSecurityScanSession = (rootDirectory, options) => {
|
|
40143
40147
|
const capabilities = options.project ? buildCapabilities(options.project) : /* @__PURE__ */ new Set();
|
|
40144
40148
|
const ignoredTags = options.ignoredTags ?? /* @__PURE__ */ new Set();
|
|
40145
40149
|
const enabledScanRules = REACT_DOCTOR_RULES.flatMap((entry) => {
|
|
@@ -40154,7 +40158,7 @@ const checkSecurityScan = (rootDirectory, options = {}) => {
|
|
|
40154
40158
|
committedFilesOnly: rule.committedFilesOnly === true
|
|
40155
40159
|
}];
|
|
40156
40160
|
});
|
|
40157
|
-
if (enabledScanRules.length === 0) return
|
|
40161
|
+
if (enabledScanRules.length === 0) return null;
|
|
40158
40162
|
const diagnostics = [];
|
|
40159
40163
|
const seen = /* @__PURE__ */ new Set();
|
|
40160
40164
|
const gitIgnoredCache = /* @__PURE__ */ new Map();
|
|
@@ -40166,15 +40170,34 @@ const checkSecurityScan = (rootDirectory, options = {}) => {
|
|
|
40166
40170
|
}
|
|
40167
40171
|
return status === true;
|
|
40168
40172
|
};
|
|
40169
|
-
|
|
40170
|
-
|
|
40171
|
-
|
|
40172
|
-
|
|
40173
|
-
|
|
40174
|
-
|
|
40175
|
-
|
|
40173
|
+
const scanFile = (file) => {
|
|
40174
|
+
for (const { entry, scan, committedFilesOnly } of enabledScanRules) for (const finding of scan(file)) {
|
|
40175
|
+
if (committedFilesOnly && isFileGitIgnored(file)) continue;
|
|
40176
|
+
const diagnostic = buildSecurityScanDiagnostic(finding, entry, file.relativePath);
|
|
40177
|
+
const key = `${diagnostic.rule}:${diagnostic.filePath}:${diagnostic.line}:${diagnostic.column}:${diagnostic.message}`;
|
|
40178
|
+
if (seen.has(key)) continue;
|
|
40179
|
+
seen.add(key);
|
|
40180
|
+
diagnostics.push(diagnostic);
|
|
40181
|
+
}
|
|
40182
|
+
};
|
|
40183
|
+
return {
|
|
40184
|
+
scanFile,
|
|
40185
|
+
diagnostics
|
|
40186
|
+
};
|
|
40187
|
+
};
|
|
40188
|
+
const checkSecurityScanCooperative = async (rootDirectory, options = {}) => {
|
|
40189
|
+
const session = createSecurityScanSession(rootDirectory, options);
|
|
40190
|
+
if (session === null) return [];
|
|
40191
|
+
let filesSinceYield = 0;
|
|
40192
|
+
for (const file of collectSecurityScanFiles(rootDirectory)) {
|
|
40193
|
+
session.scanFile(file);
|
|
40194
|
+
filesSinceYield += 1;
|
|
40195
|
+
if (filesSinceYield >= 16) {
|
|
40196
|
+
filesSinceYield = 0;
|
|
40197
|
+
await yieldToEventLoop();
|
|
40198
|
+
}
|
|
40176
40199
|
}
|
|
40177
|
-
return diagnostics;
|
|
40200
|
+
return session.diagnostics;
|
|
40178
40201
|
};
|
|
40179
40202
|
var import_picocolors = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
40180
40203
|
let p = process || {}, argv = p.argv || [], env = p.env || {};
|
|
@@ -40407,6 +40430,74 @@ const collectDeadCodeIgnorePatterns = (rootDirectory) => {
|
|
|
40407
40430
|
return [...seen].filter((pattern) => pattern.length > 0);
|
|
40408
40431
|
};
|
|
40409
40432
|
const collectDeadCodeEntryPatterns = (rootDirectory) => [...new Set(collectKnipPatterns(rootDirectory, "entry"))].filter((pattern) => pattern.length > 0);
|
|
40433
|
+
const readSystemFacts = () => ({
|
|
40434
|
+
availableCores: os.availableParallelism(),
|
|
40435
|
+
totalMemoryBytes: os.totalmem(),
|
|
40436
|
+
cgroupMemoryLimitBytes: readCgroupMemoryLimitBytes()
|
|
40437
|
+
});
|
|
40438
|
+
/**
|
|
40439
|
+
* How many real deslop dead-code child processes may run at once, across the
|
|
40440
|
+
* concurrent per-project `runInspect` fibers of one CLI run. The cap is the
|
|
40441
|
+
* smaller of the core count and the number of `DEAD_CODE_WORKER_MEM_BUDGET_BYTES`
|
|
40442
|
+
* workers that fit in available memory, floored at 1.
|
|
40443
|
+
*
|
|
40444
|
+
* On a roomy dev box / CI runner this resolves high enough that every
|
|
40445
|
+
* concurrently-scanned project still spawns its own worker (no serialization vs
|
|
40446
|
+
* the prior uncapped behavior); on a memory-constrained runner it collapses
|
|
40447
|
+
* toward 1, so the `withDeadCodeWorkerSlot` semaphore serializes the spawns
|
|
40448
|
+
* instead of oversubscribing memory with N simultaneous children — the global
|
|
40449
|
+
* cap the per-project spawn path lacked.
|
|
40450
|
+
*
|
|
40451
|
+
* Mirrors `resolveAutoScanConcurrency` (lint), but budgets memory per the
|
|
40452
|
+
* heavier dead-code worker. `facts` is injectable for tests.
|
|
40453
|
+
*/
|
|
40454
|
+
const resolveDeadCodeConcurrency = (facts = readSystemFacts()) => {
|
|
40455
|
+
const availableMemoryBytes = Math.min(facts.totalMemoryBytes, facts.cgroupMemoryLimitBytes ?? Number.POSITIVE_INFINITY);
|
|
40456
|
+
const memoryBoundedWorkers = Math.floor(availableMemoryBytes / DEAD_CODE_WORKER_MEM_BUDGET_BYTES);
|
|
40457
|
+
return Math.max(1, Math.min(facts.availableCores, memoryBoundedWorkers));
|
|
40458
|
+
};
|
|
40459
|
+
let availableSlots = -1;
|
|
40460
|
+
const waiters = [];
|
|
40461
|
+
const releaseSlot = () => {
|
|
40462
|
+
const nextWaiter = waiters.shift();
|
|
40463
|
+
if (nextWaiter !== void 0) nextWaiter();
|
|
40464
|
+
else availableSlots += 1;
|
|
40465
|
+
};
|
|
40466
|
+
/**
|
|
40467
|
+
* Runs `task` once a dead-code worker slot is free, releasing the slot when the
|
|
40468
|
+
* task settles (success or failure). With a high cap (roomy machine) every
|
|
40469
|
+
* caller proceeds immediately; with a low cap (constrained runner) callers
|
|
40470
|
+
* queue and run as slots free.
|
|
40471
|
+
*
|
|
40472
|
+
* `abortSignal` short-circuits the WAIT: if it's already aborted, or fires while
|
|
40473
|
+
* this caller is queued, the call rejects without acquiring a slot or running
|
|
40474
|
+
* `task` — so a cancelled scan (e.g. lint failed) doesn't sit in the queue and
|
|
40475
|
+
* then spawn a child only to tear it down. A queued caller that aborts removes
|
|
40476
|
+
* its own waiter so a later release never hands a slot to a dead request.
|
|
40477
|
+
*/
|
|
40478
|
+
const withDeadCodeWorkerSlot = async (task, abortSignal) => {
|
|
40479
|
+
if (abortSignal?.aborted) throw new Error("Dead-code worker aborted.");
|
|
40480
|
+
if (availableSlots < 0) availableSlots = resolveDeadCodeConcurrency();
|
|
40481
|
+
if (availableSlots > 0) availableSlots -= 1;
|
|
40482
|
+
else await new Promise((resolve, reject) => {
|
|
40483
|
+
const waiter = () => {
|
|
40484
|
+
abortSignal?.removeEventListener("abort", onAbort);
|
|
40485
|
+
resolve();
|
|
40486
|
+
};
|
|
40487
|
+
const onAbort = () => {
|
|
40488
|
+
const queuedIndex = waiters.indexOf(waiter);
|
|
40489
|
+
if (queuedIndex !== -1) waiters.splice(queuedIndex, 1);
|
|
40490
|
+
reject(/* @__PURE__ */ new Error("Dead-code worker aborted."));
|
|
40491
|
+
};
|
|
40492
|
+
waiters.push(waiter);
|
|
40493
|
+
abortSignal?.addEventListener("abort", onAbort, { once: true });
|
|
40494
|
+
});
|
|
40495
|
+
try {
|
|
40496
|
+
return await task();
|
|
40497
|
+
} finally {
|
|
40498
|
+
releaseSlot();
|
|
40499
|
+
}
|
|
40500
|
+
};
|
|
40410
40501
|
/**
|
|
40411
40502
|
* Resolves a path to its canonical, symlink-free form, falling back to
|
|
40412
40503
|
* the input when it cannot be realpath'd (broken symlink, permission
|
|
@@ -40697,14 +40788,17 @@ const checkDeadCode = async (options) => {
|
|
|
40697
40788
|
if (!NFS.existsSync(Path.join(rootDirectory, "package.json"))) return [];
|
|
40698
40789
|
const entryPatterns = collectDeadCodeEntryPatterns(rootDirectory);
|
|
40699
40790
|
const ignorePatterns = collectDeadCodeIgnorePatterns(rootDirectory);
|
|
40700
|
-
const
|
|
40701
|
-
|
|
40702
|
-
|
|
40703
|
-
|
|
40704
|
-
|
|
40705
|
-
|
|
40706
|
-
|
|
40707
|
-
|
|
40791
|
+
const spawnAndRun = () => {
|
|
40792
|
+
return runDeadCodeWorkerWithTimeout((options.createWorker ?? createDeadCodeWorker)({
|
|
40793
|
+
rootDirectory,
|
|
40794
|
+
entryPatterns,
|
|
40795
|
+
tsConfigPath: resolveTsConfigPath(rootDirectory),
|
|
40796
|
+
ignorePatterns,
|
|
40797
|
+
deslopJsModuleSpecifier: options.deslopJsModuleSpecifier ?? import.meta.resolve("deslop-js"),
|
|
40798
|
+
parseConcurrency: options.parseConcurrency
|
|
40799
|
+
}), options.workerTimeoutMs ?? 12e4, options.abortSignal);
|
|
40800
|
+
};
|
|
40801
|
+
const result = parseDeadCodeWorkerResult(options.createWorker === void 0 ? await withDeadCodeWorkerSlot(spawnAndRun, options.abortSignal) : await spawnAndRun());
|
|
40708
40802
|
const toRelative = (filePath) => toRelativeFilePath(rootDirectory, filePath);
|
|
40709
40803
|
const diagnostics = [];
|
|
40710
40804
|
for (const unusedFile of result.unusedFiles) diagnostics.push({
|
|
@@ -43868,7 +43962,10 @@ const formatLintFailText = (reasonTag, nodeVersion) => {
|
|
|
43868
43962
|
* diagnostics).
|
|
43869
43963
|
* 2. beforeLint hook (e.g. CLI renders the project-detection block)
|
|
43870
43964
|
* 3. environment checks (reduced-motion + pnpm hardening +
|
|
43871
|
-
* expo/react-native
|
|
43965
|
+
* expo/react-native), collected synchronously. The heavier
|
|
43966
|
+
* content-regex security scan is forked instead (like supply-chain
|
|
43967
|
+
* below) and joined before the concat, so its CPU overlaps lint
|
|
43968
|
+
* rather than blocking the event loop before it.
|
|
43872
43969
|
* 4. The supply-chain check (Socket.dev) is forked onto a background
|
|
43873
43970
|
* fiber so its ~100% network-bound time overlaps the ~100%
|
|
43874
43971
|
* CPU/subprocess-bound lint pass below, collapsing two serial
|
|
@@ -43888,7 +43985,7 @@ const formatLintFailText = (reasonTag, nodeVersion) => {
|
|
|
43888
43985
|
* order, so terminal output is identical either way; supply-chain
|
|
43889
43986
|
* rides alongside without a spinner.
|
|
43890
43987
|
* 6. Join the supply-chain fiber, then assemble the diagnostics in a
|
|
43891
|
-
* FIXED order (env, supply-chain, lint, dead-code) so the output is
|
|
43988
|
+
* FIXED order (env, security-scan, supply-chain, lint, dead-code) so the output is
|
|
43892
43989
|
* byte-identical regardless of which fiber settled first. The
|
|
43893
43990
|
* viewer-permission fiber is joined later, during score-metadata
|
|
43894
43991
|
* assembly (it feeds score metadata, not diagnostics). The per-element
|
|
@@ -43949,12 +44046,12 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
43949
44046
|
...checkPnpmHardening(scanDirectory),
|
|
43950
44047
|
...checkReactServerComponentsAdvisory(scanDirectory, project),
|
|
43951
44048
|
...checkExpoProject(scanDirectory, project),
|
|
43952
|
-
...checkReactNativeProject(scanDirectory, project)
|
|
43953
|
-
...checkSecurityScan(scanDirectory, {
|
|
43954
|
-
project,
|
|
43955
|
-
ignoredTags: input.ignoredTags
|
|
43956
|
-
})
|
|
44049
|
+
...checkReactNativeProject(scanDirectory, project)
|
|
43957
44050
|
])));
|
|
44051
|
+
const securityScanFiber = yield* forkChild(runCollect(applyPerElementPipeline(isDiffMode ? empty$4 : unwrap(promise(() => checkSecurityScanCooperative(scanDirectory, {
|
|
44052
|
+
project,
|
|
44053
|
+
ignoredTags: input.ignoredTags
|
|
44054
|
+
})).pipe(map$3((diagnostics) => fromIterable$1(diagnostics)))))).pipe(withSpan("SecurityScan.run")));
|
|
43958
44055
|
const shouldRunSupplyChain = !isDiffMode || (input.supplyChainManifestChanged ?? false);
|
|
43959
44056
|
const supplyChainOverlapTimeout = yield* SupplyChainOverlapTimeoutMs;
|
|
43960
44057
|
const supplyChainFiber = yield* forkChild(shouldRunSupplyChain ? runCollect(applyPerElementPipeline(supplyChainService.run({
|
|
@@ -44090,9 +44187,11 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
44090
44187
|
else yield* scanProgress.succeed(`Scanned ${scannedFilesLabel} in ${scanElapsedSeconds}s${workerCountSuffix}`);
|
|
44091
44188
|
const supplyChainResult = yield* join(supplyChainFiber);
|
|
44092
44189
|
const supplyChainCollected = supplyChainResult.diagnostics;
|
|
44190
|
+
const securityScanCollected = yield* join(securityScanFiber);
|
|
44093
44191
|
yield* reporterService.finalize;
|
|
44094
44192
|
const finalDiagnostics = sortDiagnosticsStable(assignFixGroups([
|
|
44095
44193
|
...envCollected,
|
|
44194
|
+
...securityScanCollected,
|
|
44096
44195
|
...supplyChainCollected,
|
|
44097
44196
|
...lintCollected,
|
|
44098
44197
|
...deadCodeCollected
|
|
@@ -44631,6 +44730,61 @@ const computeConfigFingerprint = (projectDirectory, version) => {
|
|
|
44631
44730
|
}
|
|
44632
44731
|
return crypto.createHash("sha1").update(parts.join("|")).digest("hex");
|
|
44633
44732
|
};
|
|
44733
|
+
const AI_TRAINING_ENV_VARS = [
|
|
44734
|
+
["HF_DATASETS_CACHE", "huggingface"],
|
|
44735
|
+
["HF_HOME", "huggingface"],
|
|
44736
|
+
["HUGGINGFACE_HUB_CACHE", "huggingface"],
|
|
44737
|
+
["SPACE_ID", "huggingface-spaces"],
|
|
44738
|
+
["CUDA_VISIBLE_DEVICES", "cuda"],
|
|
44739
|
+
["NVIDIA_VISIBLE_DEVICES", "nvidia"],
|
|
44740
|
+
["ROCR_VISIBLE_DEVICES", "rocm"],
|
|
44741
|
+
["TPU_NAME", "google-tpu"],
|
|
44742
|
+
["WANDB_RUN_ID", "wandb"],
|
|
44743
|
+
["WANDB_SWEEP_ID", "wandb"],
|
|
44744
|
+
["MLFLOW_RUN_ID", "mlflow"],
|
|
44745
|
+
["MLFLOW_TRACKING_URI", "mlflow"],
|
|
44746
|
+
["COMET_EXPERIMENT_KEY", "comet"],
|
|
44747
|
+
["NEPTUNE_RUN_ID", "neptune"],
|
|
44748
|
+
["CLEARML_TASK_ID", "clearml"],
|
|
44749
|
+
["DVC_STAGE", "dvc"],
|
|
44750
|
+
["RAY_WORKER_PROCESS", "ray"],
|
|
44751
|
+
["RAY_ADDRESS", "ray"],
|
|
44752
|
+
["MUJOCO_GL", "mujoco"],
|
|
44753
|
+
["MUJOCO_PATH", "mujoco"],
|
|
44754
|
+
["GYM_DISABLE_ENV_CHECKER", "gymnasium"],
|
|
44755
|
+
["SM_TRAINING_ENV", "sagemaker"],
|
|
44756
|
+
["TRAINING_JOB_ARN", "sagemaker"],
|
|
44757
|
+
["SAGEMAKER_BASE_DIR", "sagemaker"],
|
|
44758
|
+
["AZUREML_RUN_ID", "azure-ml"],
|
|
44759
|
+
["AZURE_ML_MODEL_DIR", "azure-ml"],
|
|
44760
|
+
["CLOUD_ML_PROJECT_ID", "vertex-ai"],
|
|
44761
|
+
["VERTEX_AI_LOG_LEVEL", "vertex-ai"],
|
|
44762
|
+
["DET_MASTER", "determined-ai"],
|
|
44763
|
+
["LIGHTNING_USER_ID", "lightning-ai"],
|
|
44764
|
+
["FLYTE_INTERNAL_EXECUTION_ID", "flyte"],
|
|
44765
|
+
["ARGO_WORKFLOW_NAME", "argo-workflows"],
|
|
44766
|
+
["KFP_POD_NAME", "kubeflow-pipelines"],
|
|
44767
|
+
["KAGGLE_KERNEL_RUN_TYPE", "kaggle"],
|
|
44768
|
+
["COLAB_BACKEND_VERSION", "google-colab"],
|
|
44769
|
+
["DATABRICKS_RUNTIME_VERSION", "databricks"],
|
|
44770
|
+
["DAYTONA_WS_ID", "daytona"],
|
|
44771
|
+
["DAYTONA_WS_NAME", "daytona"],
|
|
44772
|
+
["E2B_SANDBOX_ID", "e2b"],
|
|
44773
|
+
["MODAL_FUNCTION_ID", "modal"],
|
|
44774
|
+
["MODAL_TASK_ID", "modal"],
|
|
44775
|
+
["RUNPOD_POD_ID", "runpod"],
|
|
44776
|
+
["REPLICATE_USERNAME", "replicate"],
|
|
44777
|
+
["VAST_CONTAINERLABEL", "vast-ai"],
|
|
44778
|
+
["HARBOR_URL", "harbor"],
|
|
44779
|
+
["HARBOR_HOSTNAME", "harbor"],
|
|
44780
|
+
["SWE_BENCH_TASK", "swe-bench"],
|
|
44781
|
+
["SWEBENCH_TASK", "swe-bench"],
|
|
44782
|
+
["SWE_AGENT_MODEL", "swe-agent"]
|
|
44783
|
+
];
|
|
44784
|
+
const detectAiTrainingEnvironment = () => {
|
|
44785
|
+
for (const [envVar, label] of AI_TRAINING_ENV_VARS) if (process.env[envVar]) return label;
|
|
44786
|
+
return null;
|
|
44787
|
+
};
|
|
44634
44788
|
const groupBy = (items, keyFn) => {
|
|
44635
44789
|
const groups = /* @__PURE__ */ new Map();
|
|
44636
44790
|
for (const item of items) {
|
|
@@ -44716,7 +44870,25 @@ const CI_PROVIDER_BY_ENVIRONMENT_VARIABLE = [
|
|
|
44716
44870
|
["TEAMCITY_VERSION", "teamcity"],
|
|
44717
44871
|
["BITBUCKET_BUILD_NUMBER", "bitbucket"],
|
|
44718
44872
|
["TRAVIS", "travis"],
|
|
44719
|
-
["DRONE", "drone"]
|
|
44873
|
+
["DRONE", "drone"],
|
|
44874
|
+
["SEMAPHORE", "semaphore"],
|
|
44875
|
+
["APPVEYOR", "appveyor"],
|
|
44876
|
+
["HARNESS_BUILD_ID", "harness"],
|
|
44877
|
+
["BUDDY", "buddy"],
|
|
44878
|
+
["CF_BUILD_ID", "codefresh"],
|
|
44879
|
+
["NETLIFY", "netlify"],
|
|
44880
|
+
["RAILWAY_SERVICE_ID", "railway"],
|
|
44881
|
+
["VERCEL", "vercel"],
|
|
44882
|
+
["CM_BUILD_ID", "codemagic"],
|
|
44883
|
+
["PROW_JOB_ID", "prow"],
|
|
44884
|
+
["AGOLA_GIT_REF", "agola"],
|
|
44885
|
+
["CIRRUS_CI", "cirrus-ci"],
|
|
44886
|
+
["BLACKSMITH_STICKYDISK_TOKEN", "blacksmith"],
|
|
44887
|
+
["WARPBUILD_RUNNER_VERIFICATION_TOKEN", "warpbuild"],
|
|
44888
|
+
["NSC_CACHE_PATH", "namespace"],
|
|
44889
|
+
["UBICLOUD_CACHE_URL", "ubicloud"],
|
|
44890
|
+
["RENDER", "render"],
|
|
44891
|
+
["FLY_APP_NAME", "fly-io"]
|
|
44720
44892
|
];
|
|
44721
44893
|
const GITHUB_ACTION_MARKER_ENVIRONMENT_VARIABLE = "REACT_DOCTOR_GITHUB_ACTION";
|
|
44722
44894
|
const ACTION_INPUT_ENVIRONMENT_VARIABLES = {
|
|
@@ -44734,7 +44906,10 @@ const CODING_AGENT_BY_ENVIRONMENT_VARIABLE = [
|
|
|
44734
44906
|
["CODEX_SANDBOX_NETWORK_DISABLED", "codex"],
|
|
44735
44907
|
["OPENCODE", "opencode"],
|
|
44736
44908
|
["GOOSE_TERMINAL", "goose"],
|
|
44737
|
-
["AMP_THREAD_ID", "amp"]
|
|
44909
|
+
["AMP_THREAD_ID", "amp"],
|
|
44910
|
+
["CLINE_ACTIVE", "cline"],
|
|
44911
|
+
["AUGMENT_AGENT", "augment"],
|
|
44912
|
+
["TRAE_AI_SHELL_ID", "trae-ai"]
|
|
44738
44913
|
];
|
|
44739
44914
|
const GENERIC_CODING_AGENT_ENVIRONMENT_VARIABLES = ["AGENT_SESSION_ID", "AGENT_THREAD_ID"];
|
|
44740
44915
|
const CODING_AGENT_ENVIRONMENT_VALUE_VARIABLES = ["AGENT"];
|
|
@@ -44883,7 +45058,9 @@ const METRIC = {
|
|
|
44883
45058
|
lspSessionStarted: "lsp.session.started",
|
|
44884
45059
|
lspScanCompleted: "lsp.scan.completed",
|
|
44885
45060
|
lspScanDuration: "lsp.scan.duration",
|
|
44886
|
-
lspScanDiagnostics: "lsp.scan.diagnostics"
|
|
45061
|
+
lspScanDiagnostics: "lsp.scan.diagnostics",
|
|
45062
|
+
aiTrainingWarningShown: "ai.training.warning_shown",
|
|
45063
|
+
jsonOutUsed: "json.out_used"
|
|
44887
45064
|
};
|
|
44888
45065
|
//#endregion
|
|
44889
45066
|
//#region src/cli/utils/noop-console.ts
|
|
@@ -44926,7 +45103,7 @@ const makeNoopConsole = () => ({
|
|
|
44926
45103
|
});
|
|
44927
45104
|
//#endregion
|
|
44928
45105
|
//#region src/cli/utils/version.ts
|
|
44929
|
-
const VERSION = "0.5.8-dev.
|
|
45106
|
+
const VERSION = "0.5.8-dev.f69f216";
|
|
44930
45107
|
//#endregion
|
|
44931
45108
|
//#region src/cli/utils/json-mode.ts
|
|
44932
45109
|
let context = null;
|
|
@@ -44964,12 +45141,13 @@ const installSilentConsole = () => {
|
|
|
44964
45141
|
"trace"
|
|
44965
45142
|
]) target[key] = source[key];
|
|
44966
45143
|
};
|
|
44967
|
-
const enableJsonMode = ({ compact, directory }) => {
|
|
45144
|
+
const enableJsonMode = ({ compact, directory, outputFile }) => {
|
|
44968
45145
|
context = {
|
|
44969
45146
|
compact,
|
|
44970
45147
|
directory,
|
|
44971
45148
|
startTime: performance$1.now(),
|
|
44972
|
-
mode: "full"
|
|
45149
|
+
mode: "full",
|
|
45150
|
+
outputFile: outputFile ?? null
|
|
44973
45151
|
};
|
|
44974
45152
|
installSilentConsole();
|
|
44975
45153
|
};
|
|
@@ -44982,7 +45160,11 @@ const setJsonReportMode = (mode) => {
|
|
|
44982
45160
|
};
|
|
44983
45161
|
const writeJsonReport = (report) => {
|
|
44984
45162
|
const serialized = context?.compact ? JSON.stringify(report) : JSON.stringify(report, null, 2);
|
|
44985
|
-
|
|
45163
|
+
if (context?.outputFile) {
|
|
45164
|
+
const resolvedPath = Path.resolve(context.outputFile);
|
|
45165
|
+
NFS.mkdirSync(Path.dirname(resolvedPath), { recursive: true });
|
|
45166
|
+
NFS.writeFileSync(resolvedPath, `${serialized}\n`);
|
|
45167
|
+
} else process.stdout.write(`${serialized}\n`);
|
|
44986
45168
|
};
|
|
44987
45169
|
const writeJsonErrorReport = (error, sentryEventId) => {
|
|
44988
45170
|
if (!context) return;
|
|
@@ -45290,13 +45472,13 @@ const isDevVersion = (version) => version === "0.0.0" || version.includes("-");
|
|
|
45290
45472
|
* uploads source-map artifacts under, so stack frames symbolicate. Honors the
|
|
45291
45473
|
* standard `SENTRY_RELEASE` override.
|
|
45292
45474
|
*/
|
|
45293
|
-
const resolveSentryRelease = () => process.env.SENTRY_RELEASE || `react-doctor@0.5.8-dev.
|
|
45475
|
+
const resolveSentryRelease = () => process.env.SENTRY_RELEASE || `react-doctor@0.5.8-dev.f69f216`;
|
|
45294
45476
|
/**
|
|
45295
45477
|
* Deployment environment shown in Sentry's environment filter. Defaults to
|
|
45296
45478
|
* `production` for tagged releases and `development` for dev/unbuilt versions,
|
|
45297
45479
|
* overridable via the standard `SENTRY_ENVIRONMENT` env var.
|
|
45298
45480
|
*/
|
|
45299
|
-
const resolveSentryEnvironment = () => process.env.SENTRY_ENVIRONMENT || (isDevVersion("0.5.8-dev.
|
|
45481
|
+
const resolveSentryEnvironment = () => process.env.SENTRY_ENVIRONMENT || (isDevVersion("0.5.8-dev.f69f216") ? "development" : "production");
|
|
45300
45482
|
/**
|
|
45301
45483
|
* Performance-tracing sample rate in `[0, 1]`. Reads `SENTRY_TRACES_SAMPLE_RATE`
|
|
45302
45484
|
* (set to `0` to disable tracing) and falls back to
|
|
@@ -48892,6 +49074,25 @@ const shouldBlockCi = (diagnostics, blockingLevel) => {
|
|
|
48892
49074
|
*/
|
|
48893
49075
|
const toCategoryKey = (category) => category.toLowerCase().replace(/[^a-z0-9]+/g, "_");
|
|
48894
49076
|
//#endregion
|
|
49077
|
+
//#region src/cli/utils/with-namespace.ts
|
|
49078
|
+
/**
|
|
49079
|
+
* Prefixes every key in a flat attribute group with `namespace.`, so a logical
|
|
49080
|
+
* group (scan config, diagnostics rollup, score, …) carries one dotted
|
|
49081
|
+
* namespace applied in a single place rather than hand-spelled into each key —
|
|
49082
|
+
* which is how the wide event drifted into a flat, half-namespaced soup. The
|
|
49083
|
+
* dotted prefix is what makes the attributes tree up in Sentry's attribute
|
|
49084
|
+
* browser and stay group-/filter-/aggregate-able in the Spans dataset.
|
|
49085
|
+
*
|
|
49086
|
+
* Value types are preserved verbatim (numbers stay numbers so Sentry infers a
|
|
49087
|
+
* numeric attribute and `p75(...)`/`avg(...)` work; `null` is kept so
|
|
49088
|
+
* `toSpanAttributes` can drop absent signals rather than coerce them).
|
|
49089
|
+
*/
|
|
49090
|
+
const withNamespace = (namespace, attributes) => {
|
|
49091
|
+
const namespaced = {};
|
|
49092
|
+
for (const [key, value] of Object.entries(attributes)) namespaced[`${namespace}.${key}`] = value;
|
|
49093
|
+
return namespaced;
|
|
49094
|
+
};
|
|
49095
|
+
//#endregion
|
|
48895
49096
|
//#region src/cli/utils/build-run-event.ts
|
|
48896
49097
|
const readEnvBoolean = (name) => {
|
|
48897
49098
|
const value = process.env[name];
|
|
@@ -48913,12 +49114,12 @@ const buildOutcomeAttributes = (input) => {
|
|
|
48913
49114
|
if (input.result === void 0) {
|
|
48914
49115
|
const error = input.error;
|
|
48915
49116
|
const known = isReactDoctorError(error);
|
|
48916
|
-
return {
|
|
48917
|
-
|
|
49117
|
+
return withNamespace("outcome", {
|
|
49118
|
+
status: "error",
|
|
48918
49119
|
exitCode: 1,
|
|
48919
49120
|
knownError: known,
|
|
48920
49121
|
errorTag: known ? error.reason._tag : error instanceof Error ? error.name : null
|
|
48921
|
-
};
|
|
49122
|
+
});
|
|
48922
49123
|
}
|
|
48923
49124
|
const result = input.result;
|
|
48924
49125
|
const summary = summarizeDiagnostics(result.diagnostics);
|
|
@@ -48952,66 +49153,82 @@ const buildOutcomeAttributes = (input) => {
|
|
|
48952
49153
|
}
|
|
48953
49154
|
let fixGroupedFindings = 0;
|
|
48954
49155
|
for (const count of findingsPerFixGroup.values()) fixGroupedFindings += count;
|
|
49156
|
+
const categoryRollup = {};
|
|
49157
|
+
for (const [category, count] of countByCategory) categoryRollup[`category.${toCategoryKey(category)}`] = count;
|
|
48955
49158
|
const attributes = {
|
|
48956
|
-
outcome,
|
|
48957
|
-
|
|
48958
|
-
|
|
48959
|
-
|
|
48960
|
-
|
|
48961
|
-
|
|
48962
|
-
|
|
48963
|
-
|
|
48964
|
-
|
|
48965
|
-
|
|
48966
|
-
|
|
48967
|
-
|
|
48968
|
-
|
|
48969
|
-
|
|
48970
|
-
|
|
48971
|
-
|
|
48972
|
-
|
|
48973
|
-
|
|
48974
|
-
|
|
48975
|
-
|
|
48976
|
-
|
|
48977
|
-
|
|
48978
|
-
|
|
48979
|
-
|
|
48980
|
-
|
|
48981
|
-
|
|
48982
|
-
|
|
48983
|
-
|
|
48984
|
-
|
|
48985
|
-
|
|
48986
|
-
|
|
48987
|
-
|
|
48988
|
-
|
|
48989
|
-
|
|
48990
|
-
|
|
48991
|
-
|
|
48992
|
-
|
|
48993
|
-
|
|
48994
|
-
|
|
48995
|
-
|
|
48996
|
-
|
|
48997
|
-
|
|
48998
|
-
|
|
49159
|
+
...withNamespace("outcome", {
|
|
49160
|
+
status: outcome,
|
|
49161
|
+
exitCode: wouldBlock ? 1 : 0,
|
|
49162
|
+
wouldBlock,
|
|
49163
|
+
blocking: blockingLevel,
|
|
49164
|
+
clean: isClean,
|
|
49165
|
+
skippedChecks: result.skippedChecks.length
|
|
49166
|
+
}),
|
|
49167
|
+
...withNamespace("diag", {
|
|
49168
|
+
total: summary.totalDiagnosticCount,
|
|
49169
|
+
errors: summary.errorCount,
|
|
49170
|
+
warnings: summary.warningCount,
|
|
49171
|
+
affectedFiles: summary.affectedFileCount,
|
|
49172
|
+
inTestFiles: diagnosticsInTestFiles,
|
|
49173
|
+
inStoryFiles: diagnosticsInStoryFiles,
|
|
49174
|
+
distinctRules: countByRule.size,
|
|
49175
|
+
topRule,
|
|
49176
|
+
fixGroups: findingsPerFixGroup.size,
|
|
49177
|
+
fixGroupedFindings,
|
|
49178
|
+
...categoryRollup
|
|
49179
|
+
}),
|
|
49180
|
+
...withNamespace("score", {
|
|
49181
|
+
value: result.score ? result.score.score : null,
|
|
49182
|
+
label: result.score ? result.score.label : null,
|
|
49183
|
+
available: result.score !== null
|
|
49184
|
+
}),
|
|
49185
|
+
...withNamespace("lint", {
|
|
49186
|
+
failed: input.didLintFail ?? null,
|
|
49187
|
+
failureReasonKind: input.lintFailureReasonKind ?? null,
|
|
49188
|
+
partialFailureCount: input.lintPartialFailureCount ?? null,
|
|
49189
|
+
droppedFileCount: input.lintDroppedFileCount ?? null,
|
|
49190
|
+
cacheHitFiles: result.lintCacheHitFileCount ?? null,
|
|
49191
|
+
cacheTotalFiles: result.lintCacheTotalFileCount ?? null,
|
|
49192
|
+
cacheHitRatio: result.lintCacheTotalFileCount != null && result.lintCacheTotalFileCount > 0 ? (result.lintCacheHitFileCount ?? 0) / result.lintCacheTotalFileCount : null
|
|
49193
|
+
}),
|
|
49194
|
+
...withNamespace("deadCode", {
|
|
49195
|
+
failed: input.didDeadCodeFail ?? null,
|
|
49196
|
+
overlapped: input.deadCodeOverlapped ?? null
|
|
49197
|
+
}),
|
|
49198
|
+
...withNamespace("supplyChain", { overlapTimedOut: input.supplyChainOverlapTimedOut ?? null }),
|
|
49199
|
+
...withNamespace("timing", {
|
|
49200
|
+
elapsedMs: result.elapsedMilliseconds,
|
|
49201
|
+
scanMs: result.scanElapsedMilliseconds ?? null
|
|
49202
|
+
}),
|
|
49203
|
+
...withNamespace("migration", {
|
|
49204
|
+
largestRuleBucketFiles: largestRuleBucket ? largestRuleBucket.fileCount : null,
|
|
49205
|
+
largestRuleBucketSites: largestRuleBucket ? largestRuleBucket.siteCount : null,
|
|
49206
|
+
largestRuleBucketRule: largestRuleBucket ? largestRuleBucket.ruleKey : null
|
|
49207
|
+
})
|
|
49208
|
+
};
|
|
49209
|
+
if (result.baselineDelta) Object.assign(attributes, withNamespace("baseline", {
|
|
49210
|
+
new: summary.totalDiagnosticCount,
|
|
49211
|
+
fixed: result.baselineDelta.fixedCount,
|
|
49212
|
+
baseTotal: result.baselineDelta.baseTotalCount,
|
|
49213
|
+
degraded: false
|
|
49214
|
+
}));
|
|
49215
|
+
else if (input.gateExempt) attributes["baseline.degraded"] = true;
|
|
48999
49216
|
return attributes;
|
|
49000
49217
|
};
|
|
49001
|
-
const
|
|
49218
|
+
const buildActionAttributes = () => {
|
|
49002
49219
|
const { githubActorAssociation } = resolveGithubActionsScoreMetadata();
|
|
49003
|
-
return {
|
|
49220
|
+
return withNamespace("action", {
|
|
49004
49221
|
actorAssociation: githubActorAssociation ?? null,
|
|
49005
49222
|
runnerOs: detectRunnerOs(),
|
|
49006
49223
|
comment: readEnvBoolean(ACTION_INPUT_ENVIRONMENT_VARIABLES.comment),
|
|
49007
49224
|
reviewComments: readEnvBoolean(ACTION_INPUT_ENVIRONMENT_VARIABLES.reviewComments),
|
|
49008
49225
|
versionPin: resolveVersionPin(process.env[ACTION_INPUT_ENVIRONMENT_VARIABLES.version])
|
|
49009
|
-
};
|
|
49226
|
+
});
|
|
49010
49227
|
};
|
|
49011
|
-
const
|
|
49228
|
+
const buildScanAttributes = (input) => {
|
|
49012
49229
|
const ruleOverrides = input.userConfig?.rules ?? {};
|
|
49013
49230
|
const ruleKeys = Object.keys(ruleOverrides);
|
|
49014
|
-
return {
|
|
49231
|
+
return withNamespace("scan", {
|
|
49015
49232
|
mode: input.mode,
|
|
49016
49233
|
scope: input.scope,
|
|
49017
49234
|
parallel: input.parallel,
|
|
@@ -49026,12 +49243,18 @@ const buildConfigAttributes = (input) => {
|
|
|
49026
49243
|
ignoredTagCount: input.ignoredTagCount,
|
|
49027
49244
|
hasCustomConfig: input.hasCustomConfig,
|
|
49028
49245
|
rulesConfigured: ruleKeys.length,
|
|
49029
|
-
rulesDisabled: ruleKeys.filter((key) => ruleOverrides[key] === "off").length
|
|
49030
|
-
|
|
49246
|
+
rulesDisabled: ruleKeys.filter((key) => ruleOverrides[key] === "off").length,
|
|
49247
|
+
fileCount: input.result?.scannedFileCount ?? null
|
|
49248
|
+
});
|
|
49031
49249
|
};
|
|
49032
49250
|
/**
|
|
49033
|
-
* Projects a scan into the
|
|
49034
|
-
* per-scan "wide event".
|
|
49251
|
+
* Projects a scan into the namespaced attribute set for its root span — the
|
|
49252
|
+
* canonical per-scan "wide event". Every attribute carries a dotted namespace
|
|
49253
|
+
* that groups it by concept (`scan.*` config, `action.*` CI knobs, `outcome.*`
|
|
49254
|
+
* verdict, `diag.*` findings, `score.*`, `lint.*`, `deadCode.*`,
|
|
49255
|
+
* `supplyChain.*`, `timing.*`, `migration.*`, `baseline.*`) so the attributes
|
|
49256
|
+
* tree up in Sentry's attribute browser and stay filter-/group-/aggregate-able
|
|
49257
|
+
* in the Spans dataset. Pure and exported so the projection (outcome
|
|
49035
49258
|
* precedence, rule/category rollups, CI knobs, config shape) is unit-testable
|
|
49036
49259
|
* without a live Sentry client. `null` values are dropped so absent signals
|
|
49037
49260
|
* never become misleading `"null"` attributes. The run + project base context
|
|
@@ -49040,8 +49263,8 @@ const buildConfigAttributes = (input) => {
|
|
|
49040
49263
|
* those don't carry.
|
|
49041
49264
|
*/
|
|
49042
49265
|
const buildRunEventAttributes = (input) => toSpanAttributes({
|
|
49043
|
-
...
|
|
49044
|
-
...
|
|
49266
|
+
...buildScanAttributes(input),
|
|
49267
|
+
...buildActionAttributes(),
|
|
49045
49268
|
...buildOutcomeAttributes(input)
|
|
49046
49269
|
});
|
|
49047
49270
|
/**
|
|
@@ -51089,7 +51312,8 @@ const getStagedSourceFiles = async (directory) => {
|
|
|
51089
51312
|
return [...await runPromise(gen(function* () {
|
|
51090
51313
|
return yield* (yield* StagedFiles).discoverSourceFiles(directory);
|
|
51091
51314
|
}).pipe(provide(stagedFilesLayer)))];
|
|
51092
|
-
} catch {
|
|
51315
|
+
} catch (error) {
|
|
51316
|
+
cliLogger.warn(`Failed to discover staged files: ${error instanceof Error ? error.message : String(error)}`);
|
|
51093
51317
|
return [];
|
|
51094
51318
|
}
|
|
51095
51319
|
};
|
|
@@ -53034,6 +53258,26 @@ const CLI_AGENT_AUTO_FLAGS = {
|
|
|
53034
53258
|
codex: ["--yolo"],
|
|
53035
53259
|
cursor: ["--force"]
|
|
53036
53260
|
};
|
|
53261
|
+
const resolveCursorBundledNode = () => {
|
|
53262
|
+
if (!isWindows) return null;
|
|
53263
|
+
const localAppData = process.env.LOCALAPPDATA;
|
|
53264
|
+
if (!localAppData) return null;
|
|
53265
|
+
const cursorAgentRoot = Path.join(localAppData, "cursor-agent", "versions");
|
|
53266
|
+
try {
|
|
53267
|
+
const versions = NFS.readdirSync(cursorAgentRoot);
|
|
53268
|
+
if (versions.length === 0) return null;
|
|
53269
|
+
const sortedVersions = versions.sort((a, b) => b.localeCompare(a, void 0, { numeric: true }));
|
|
53270
|
+
for (const version of sortedVersions) try {
|
|
53271
|
+
const bundledNodePath = Path.join(cursorAgentRoot, version, "node.exe");
|
|
53272
|
+
const entryScriptPath = Path.join(cursorAgentRoot, version, "index.js");
|
|
53273
|
+
if (NFS.statSync(bundledNodePath).isFile() && NFS.statSync(entryScriptPath).isFile()) return {
|
|
53274
|
+
command: bundledNodePath,
|
|
53275
|
+
args: [entryScriptPath]
|
|
53276
|
+
};
|
|
53277
|
+
} catch {}
|
|
53278
|
+
} catch {}
|
|
53279
|
+
return null;
|
|
53280
|
+
};
|
|
53037
53281
|
const resolveWindowsCmdEntryScript = (command) => {
|
|
53038
53282
|
if (!isWindows) return null;
|
|
53039
53283
|
const pathDirectories = (process.env.PATH ?? "").split(Path.delimiter).filter(Boolean);
|
|
@@ -53053,11 +53297,10 @@ const resolveWindowsCmdEntryScript = (command) => {
|
|
|
53053
53297
|
}
|
|
53054
53298
|
return null;
|
|
53055
53299
|
};
|
|
53056
|
-
const spawnAgent = (command, args, cwd
|
|
53300
|
+
const spawnAgent = (command, args, cwd) => new Promise((resolve, reject) => {
|
|
53057
53301
|
const child = spawn(command, [...args], {
|
|
53058
53302
|
cwd,
|
|
53059
|
-
stdio: "inherit"
|
|
53060
|
-
shell
|
|
53303
|
+
stdio: "inherit"
|
|
53061
53304
|
});
|
|
53062
53305
|
child.on("error", reject);
|
|
53063
53306
|
child.on("close", (code) => resolve(code ?? 0));
|
|
@@ -53066,6 +53309,10 @@ const launchCliAgent = async (agentId, prompt, cwd) => {
|
|
|
53066
53309
|
const binary = CLI_AGENT_BINARIES[agentId];
|
|
53067
53310
|
const agentArgs = [...CLI_AGENT_AUTO_FLAGS[agentId], prompt];
|
|
53068
53311
|
if (isWindows) {
|
|
53312
|
+
if (agentId === "cursor") {
|
|
53313
|
+
const cursorResolution = resolveCursorBundledNode();
|
|
53314
|
+
if (cursorResolution) return spawnAgent(cursorResolution.command, [...cursorResolution.args, ...agentArgs], cwd);
|
|
53315
|
+
}
|
|
53069
53316
|
const entryScript = resolveWindowsCmdEntryScript(binary);
|
|
53070
53317
|
if (entryScript) return spawnAgent(process.execPath, [entryScript, ...agentArgs], cwd);
|
|
53071
53318
|
}
|
|
@@ -53976,6 +54223,7 @@ const resolveRequestedProjects = (requestedNames, workspacePackages, rootDirecto
|
|
|
53976
54223
|
return requestedNames.map((requestedName) => {
|
|
53977
54224
|
const matched = workspacePackages.find((workspacePackage) => workspacePackage.name === requestedName || Path.basename(workspacePackage.directory) === requestedName);
|
|
53978
54225
|
if (matched) return matched.directory;
|
|
54226
|
+
if (Path.basename(rootDirectory) === requestedName) return rootDirectory;
|
|
53979
54227
|
const candidateDirectory = Path.resolve(rootDirectory, requestedName);
|
|
53980
54228
|
if (isDirectory(candidateDirectory)) {
|
|
53981
54229
|
recordCount(METRIC.projectPathSelected);
|
|
@@ -54083,6 +54331,17 @@ const warnDeprecatedFailOn = (flags, userConfig) => {
|
|
|
54083
54331
|
cliLogger.warn(`${source} is deprecated; rename it to ${replacement}.`);
|
|
54084
54332
|
};
|
|
54085
54333
|
//#endregion
|
|
54334
|
+
//#region src/cli/utils/warn-ai-training-environment.ts
|
|
54335
|
+
let didWarnAiTraining = false;
|
|
54336
|
+
const warnIfAiTrainingEnvironment = () => {
|
|
54337
|
+
if (didWarnAiTraining) return;
|
|
54338
|
+
const detected = detectAiTrainingEnvironment();
|
|
54339
|
+
if (detected === null) return;
|
|
54340
|
+
didWarnAiTraining = true;
|
|
54341
|
+
process.stderr.write(`${highlighter.warn("react-doctor detected use in an AI or ML pipeline. This use requires written permission under the react-doctor license — contact founders@million.dev to request access.")}\n`);
|
|
54342
|
+
recordCount(METRIC.aiTrainingWarningShown, 1, { environment: detected });
|
|
54343
|
+
};
|
|
54344
|
+
//#endregion
|
|
54086
54345
|
//#region src/cli/utils/validate-mode-flags.ts
|
|
54087
54346
|
const usedDiffAlias = (flags) => flags.diff !== void 0 && flags.diff !== false && flags.diff !== "false" && flags.diff !== "";
|
|
54088
54347
|
const usedScope = (flags) => typeof flags.scope === "string" && flags.scope.length > 0;
|
|
@@ -54166,10 +54425,14 @@ const inspectAction = async (directory, flags) => {
|
|
|
54166
54425
|
const isQuiet = isScoreOnly || isJsonMode;
|
|
54167
54426
|
const requestedDirectory = Path.resolve(directory);
|
|
54168
54427
|
const startTime = performance$1.now();
|
|
54169
|
-
if (isJsonMode)
|
|
54170
|
-
|
|
54171
|
-
|
|
54172
|
-
|
|
54428
|
+
if (isJsonMode) {
|
|
54429
|
+
enableJsonMode({
|
|
54430
|
+
compact: Boolean(flags.jsonCompact),
|
|
54431
|
+
directory: requestedDirectory,
|
|
54432
|
+
outputFile: flags.jsonOut
|
|
54433
|
+
});
|
|
54434
|
+
if (flags.jsonOut) recordCount(METRIC.jsonOutUsed, 1);
|
|
54435
|
+
}
|
|
54173
54436
|
recordCount(METRIC.cliInvoked, 1, { command: "inspect" });
|
|
54174
54437
|
try {
|
|
54175
54438
|
validateModeFlags(flags);
|
|
@@ -54183,6 +54446,7 @@ const inspectAction = async (directory, flags) => {
|
|
|
54183
54446
|
setJsonReportDirectory(resolvedDirectory);
|
|
54184
54447
|
warnDeprecatedFailOn(flags, userConfig);
|
|
54185
54448
|
warnDeprecatedDiff(flags, userConfig);
|
|
54449
|
+
warnIfAiTrainingEnvironment();
|
|
54186
54450
|
if (scanTarget.didRedirectViaRootDir && !isQuiet) {
|
|
54187
54451
|
cliLogger.dim(`Redirected to ${highlighter.info(toRelativePath(resolvedDirectory, requestedDirectory))} via react-doctor config "rootDir".`);
|
|
54188
54452
|
cliLogger.break();
|
|
@@ -55248,6 +55512,7 @@ const ROOT_FLAG_SPEC = {
|
|
|
55248
55512
|
"--category",
|
|
55249
55513
|
"--changed-files-from",
|
|
55250
55514
|
"--blocking",
|
|
55515
|
+
"--json-out",
|
|
55251
55516
|
"--output-dir",
|
|
55252
55517
|
"--fail-on",
|
|
55253
55518
|
"--project",
|
|
@@ -55438,7 +55703,7 @@ ${highlighter.dim("Learn more:")}
|
|
|
55438
55703
|
${highlighter.info(CANONICAL_GITHUB_URL)}
|
|
55439
55704
|
`;
|
|
55440
55705
|
const collectCategoryOption = (value, previousValues) => [...previousValues ?? [], value];
|
|
55441
|
-
const program = new Command().name("react-doctor").description("Diagnose React codebase health").version(VERSION, "-v, --version", "display the version number").argument("[directory]", "project directory to scan", ".").option("--lint", "enable linting").option("--no-lint", "skip linting").option("--dead-code", "enable dead-code analysis (default)").option("--no-dead-code", "skip dead-code analysis (unused files / exports / dependencies, circular imports)").option("--verbose", "show every rule and per-file details (default shows top 3 rules)").option("--debug", "force a Sentry trace and print its id at the end (paste it into a bug report)").option("--output-dir <dir>", "directory for the full diagnostics dump (default: a temp folder)").option("--score", "output only the score").option("--json", "output a single structured JSON report (suppresses other output)").option("--json-compact", "with --json, emit compact JSON (no indentation)").option("-y, --yes", "skip prompts, scan all workspace projects").option("--no-parallel", "lint serially with one worker (default: parallel across CPU cores; set the worker count with REACT_DOCTOR_PARALLEL)").option("--project <name>", "select projects: workspace names or directory paths (comma-separated for multiple); overrides the `projects` config field").option("--scope <value>", "how much to scan/report: full (default), files, changed (only new issues vs base), or lines (only changed lines)").option("--base <ref>", "base git ref for files/changed/lines scope (auto-detected when omitted)").addOption(new Option("--diff [base]", "[deprecated] alias for --scope changed (pass `false` to force a full scan)").hideHelp()).addOption(new Option("--changed-files-from <file>", "scan source files listed in a newline-delimited changed-files file").hideHelp()).option("--no-score", "skip the score API, the share URL, and crash reporting").addOption(new Option("--category <category>", "only show diagnostics in a category (repeatable; e.g. Security)").argParser(collectCategoryOption)).option("--no-telemetry", "alias for --no-score (skip the score API, share URL, and crash reporting)").option("--staged", "scan only staged (git index) files for pre-commit hooks").option("--blocking <level>", "severity that fails CI: error (default), warning, or none (advisory)").addOption(new Option("--fail-on <level>", "[deprecated] alias for --blocking <level>").hideHelp()).option("--no-respect-inline-disables", "audit mode: neutralize inline lint suppressions before scanning").option("--warnings", "show warning-severity diagnostics (default)").option("--no-warnings", "hide warning-severity diagnostics (errors only)").option("--color", "force colored output").option("--no-color", "disable colored output (also honors NO_COLOR)").addHelpText("after", renderRootHelpEpilog);
|
|
55706
|
+
const program = new Command().name("react-doctor").description("Diagnose React codebase health").version(VERSION, "-v, --version", "display the version number").argument("[directory]", "project directory to scan", ".").option("--lint", "enable linting").option("--no-lint", "skip linting").option("--dead-code", "enable dead-code analysis (default)").option("--no-dead-code", "skip dead-code analysis (unused files / exports / dependencies, circular imports)").option("--verbose", "show every rule and per-file details (default shows top 3 rules)").option("--debug", "force a Sentry trace and print its id at the end (paste it into a bug report)").option("--output-dir <dir>", "directory for the full diagnostics dump (default: a temp folder)").option("--score", "output only the score").option("--json", "output a single structured JSON report (suppresses other output)").option("--json-compact", "with --json, emit compact JSON (no indentation)").option("--json-out <path>", "with --json, write the report to a file instead of stdout").option("-y, --yes", "skip prompts, scan all workspace projects").option("--no-parallel", "lint serially with one worker (default: parallel across CPU cores; set the worker count with REACT_DOCTOR_PARALLEL)").option("--project <name>", "select projects: workspace names or directory paths (comma-separated for multiple); overrides the `projects` config field").option("--scope <value>", "how much to scan/report: full (default), files, changed (only new issues vs base), or lines (only changed lines)").option("--base <ref>", "base git ref for files/changed/lines scope (auto-detected when omitted)").addOption(new Option("--diff [base]", "[deprecated] alias for --scope changed (pass `false` to force a full scan)").hideHelp()).addOption(new Option("--changed-files-from <file>", "scan source files listed in a newline-delimited changed-files file").hideHelp()).option("--no-score", "skip the score API, the share URL, and crash reporting").addOption(new Option("--category <category>", "only show diagnostics in a category (repeatable; e.g. Security)").argParser(collectCategoryOption)).option("--no-telemetry", "alias for --no-score (skip the score API, share URL, and crash reporting)").option("--staged", "scan only staged (git index) files for pre-commit hooks").option("--blocking <level>", "severity that fails CI: error (default), warning, or none (advisory)").addOption(new Option("--fail-on <level>", "[deprecated] alias for --blocking <level>").hideHelp()).option("--no-respect-inline-disables", "audit mode: neutralize inline lint suppressions before scanning").option("--warnings", "show warning-severity diagnostics (default)").option("--no-warnings", "hide warning-severity diagnostics (errors only)").option("--color", "force colored output").option("--no-color", "disable colored output (also honors NO_COLOR)").addHelpText("after", renderRootHelpEpilog);
|
|
55442
55707
|
program.action(inspectAction);
|
|
55443
55708
|
program.command("why <location>").description("Explain why a rule fired (or why a suppression didn't apply) at a file:line").option("--project <name>", "select projects: workspace names or directory paths (comma-separated for multiple)").option("-c, --cwd <cwd>", "working directory", process.cwd()).option("--color", "force colored output").option("--no-color", "disable colored output (also honors NO_COLOR)").action((location, options) => whyAction(location, options));
|
|
55444
55709
|
program.command("install").alias("setup").description("Install the react-doctor skill into your coding agents and optional git hook").option("-y, --yes", "skip prompts, install for all detected agents").option("--dry-run", "show what would be installed without writing files").option("--agent-hooks", "install native non-blocking agent hooks for Claude Code and Cursor").option("-c, --cwd <cwd>", "working directory", process.cwd()).option("--color", "force colored output").option("--no-color", "disable colored output (also honors NO_COLOR)").addHelpText("after", renderInstallHelpEpilog).action(installAction);
|
|
@@ -55481,4 +55746,4 @@ Promise.resolve().then(() => assertNoRemovedFlags(process.argv)).then(() => prog
|
|
|
55481
55746
|
export {};
|
|
55482
55747
|
|
|
55483
55748
|
//# sourceMappingURL=cli.js.map
|
|
55484
|
-
//# debugId=
|
|
55749
|
+
//# debugId=fabe3e34-b40f-514e-a5af-fa68a36365ae
|