react-doctor 0.9.2-dev.9512488 → 0.9.2-dev.992205a
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +123 -75
- package/dist/index.d.ts +1 -1
- package/dist/index.js +109 -65
- package/dist/lsp.js +90 -61
- package/dist/{run-scan-app-C9MJnVwv.js → run-scan-app-DHRvqfkX.js} +11 -10
- package/package.json +5 -5
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]="3bff4f9b-31f8-5abe-b7a2-a0ab9f6a85b4")}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";
|
|
@@ -8,7 +8,7 @@ import path from "node:path";
|
|
|
8
8
|
import * as NFS from "node:fs";
|
|
9
9
|
import fs, { mkdtempSync, rmSync } from "node:fs";
|
|
10
10
|
import process$1 from "node:process";
|
|
11
|
-
import
|
|
11
|
+
import { ALL_REACT_DOCTOR_RULE_KEYS, CROSS_FILE_DEPENDENCY_COLLECTORS, CROSS_FILE_RULE_IDS, FRAMEWORK_SPECIFIC_RULE_KEYS, FRAMEWORK_TOKENS, MOTION_LIBRARY_PACKAGES, REACT_COMPILER_RULES, REACT_DOCTOR_RULES, REACT_DOCTOR_RULE_REGISTRY, REACT_DOCTOR_SCAN_RULES, classifySecurityScanFile, collectCrossFileDependencyProbes, resetManifestCaches, shouldReadSecurityScanContent } from "oxlint-plugin-react-doctor/core";
|
|
12
12
|
import * as Crypto from "node:crypto";
|
|
13
13
|
import crypto, { createHash, randomUUID } from "node:crypto";
|
|
14
14
|
import { ResolverFactory } from "oxc-resolver";
|
|
@@ -68874,7 +68874,7 @@ const evaluateSuppression = (lines, diagnosticLineIndex, ruleId) => {
|
|
|
68874
68874
|
const getDiagnosticRuleIdentity = (diagnostic) => ({
|
|
68875
68875
|
ruleKey: `${diagnostic.plugin}/${diagnostic.rule}`,
|
|
68876
68876
|
category: diagnostic.category,
|
|
68877
|
-
tags: diagnostic.plugin === "react-doctor" ?
|
|
68877
|
+
tags: diagnostic.plugin === "react-doctor" ? REACT_DOCTOR_RULE_REGISTRY[diagnostic.rule]?.tags ?? [] : []
|
|
68878
68878
|
});
|
|
68879
68879
|
const compileIgnoredFilePatterns = (userConfig) => {
|
|
68880
68880
|
const files = userConfig?.ignore?.files;
|
|
@@ -69222,7 +69222,7 @@ const buildDiagnosticPipeline = (input) => {
|
|
|
69222
69222
|
};
|
|
69223
69223
|
const shouldAutoSuppress = (diagnostic) => {
|
|
69224
69224
|
if (diagnostic.plugin !== "react-doctor") return false;
|
|
69225
|
-
const rule =
|
|
69225
|
+
const rule = REACT_DOCTOR_RULE_REGISTRY[diagnostic.rule];
|
|
69226
69226
|
if (!rule?.tags?.includes("test-noise")) return false;
|
|
69227
69227
|
if (rule.tags.includes("migration-hint")) return false;
|
|
69228
69228
|
return getFileContext(diagnostic.filePath) !== "production";
|
|
@@ -69654,6 +69654,7 @@ var OxlintConcurrency = class extends Reference("react-doctor/OxlintConcurrency"
|
|
|
69654
69654
|
if (Number.isInteger(parsed) && parsed > 0) return resolveScanConcurrency(parsed);
|
|
69655
69655
|
return resolveAutoScanConcurrency();
|
|
69656
69656
|
} }) {};
|
|
69657
|
+
var OxlintSpawnSlots = class extends Reference("react-doctor/OxlintSpawnSlots", { defaultValue: () => null }) {};
|
|
69657
69658
|
/**
|
|
69658
69659
|
* Three-state control for overlapping the dead-code pass with the lint pass —
|
|
69659
69660
|
* forking dead-code as a child fiber that runs DURING lint instead of strictly
|
|
@@ -72004,8 +72005,8 @@ const getCapabilities = (project) => {
|
|
|
72004
72005
|
const cached = capabilitiesByProject.get(project);
|
|
72005
72006
|
if (cached !== void 0) return cached;
|
|
72006
72007
|
const capabilities = new Set(buildCapabilities(project));
|
|
72007
|
-
const
|
|
72008
|
-
const targetBlankOpenerProtection = detectTargetBlankOpenerProtection(
|
|
72008
|
+
const capabilityRootDirectory = isFile(Path.join(project.rootDirectory, "package.json")) ? project.rootDirectory : findNearestAncestorPackageJson(project.rootDirectory) ?? project.rootDirectory;
|
|
72009
|
+
const targetBlankOpenerProtection = detectTargetBlankOpenerProtection(capabilityRootDirectory, readPackageJson$1(Path.join(capabilityRootDirectory, "package.json")));
|
|
72009
72010
|
if (targetBlankOpenerProtection !== void 0) capabilities.add("target-blank-needs-explicit-protection");
|
|
72010
72011
|
if (targetBlankOpenerProtection === "noreferrer") capabilities.add("target-blank-needs-noreferrer");
|
|
72011
72012
|
capabilitiesByProject.set(project, capabilities);
|
|
@@ -72035,7 +72036,7 @@ const createSecurityScanSession = (rootDirectory, options) => {
|
|
|
72035
72036
|
const capabilities = options.project ? getCapabilities(options.project) : /* @__PURE__ */ new Set();
|
|
72036
72037
|
const ignoredTags = options.ignoredTags ?? /* @__PURE__ */ new Set();
|
|
72037
72038
|
const includedTags = options.includedTags ?? /* @__PURE__ */ new Set();
|
|
72038
|
-
const enabledScanRules =
|
|
72039
|
+
const enabledScanRules = REACT_DOCTOR_SCAN_RULES.flatMap((entry) => {
|
|
72039
72040
|
const rule = entry.rule;
|
|
72040
72041
|
const scan = rule.scan;
|
|
72041
72042
|
if (typeof scan !== "function") return [];
|
|
@@ -72496,47 +72497,55 @@ const resolveDeadCodeConcurrency = (facts = readSystemFacts()) => {
|
|
|
72496
72497
|
const memoryBoundedWorkers = Math.floor(availableMemoryBytes / DEAD_CODE_WORKER_MEM_BUDGET_BYTES);
|
|
72497
72498
|
return Math.max(1, Math.min(facts.availableCores, memoryBoundedWorkers));
|
|
72498
72499
|
};
|
|
72499
|
-
|
|
72500
|
-
|
|
72501
|
-
const
|
|
72502
|
-
const
|
|
72503
|
-
|
|
72504
|
-
|
|
72500
|
+
const createWorkerSlots = (input) => {
|
|
72501
|
+
let availableSlotCount = input.slotCount;
|
|
72502
|
+
const waiters = [];
|
|
72503
|
+
const releaseSlot = () => {
|
|
72504
|
+
const nextWaiter = waiters.shift();
|
|
72505
|
+
if (nextWaiter === void 0) {
|
|
72506
|
+
availableSlotCount += 1;
|
|
72507
|
+
return;
|
|
72508
|
+
}
|
|
72509
|
+
nextWaiter.abortSignal?.removeEventListener("abort", nextWaiter.onAbort);
|
|
72510
|
+
nextWaiter.resolve();
|
|
72511
|
+
};
|
|
72512
|
+
const acquireSlot = async (abortSignal) => {
|
|
72513
|
+
if (abortSignal?.aborted) throw input.createAbortError();
|
|
72514
|
+
if (availableSlotCount > 0) {
|
|
72515
|
+
availableSlotCount -= 1;
|
|
72516
|
+
return;
|
|
72517
|
+
}
|
|
72518
|
+
await new Promise((resolve, reject) => {
|
|
72519
|
+
const onAbort = () => {
|
|
72520
|
+
const waiterIndex = waiters.indexOf(waiter);
|
|
72521
|
+
if (waiterIndex !== -1) waiters.splice(waiterIndex, 1);
|
|
72522
|
+
reject(input.createAbortError());
|
|
72523
|
+
};
|
|
72524
|
+
const waiter = {
|
|
72525
|
+
resolve,
|
|
72526
|
+
abortSignal,
|
|
72527
|
+
onAbort
|
|
72528
|
+
};
|
|
72529
|
+
waiters.push(waiter);
|
|
72530
|
+
abortSignal?.addEventListener("abort", onAbort, { once: true });
|
|
72531
|
+
});
|
|
72532
|
+
};
|
|
72533
|
+
return { run: async (task, abortSignal) => {
|
|
72534
|
+
await acquireSlot(abortSignal);
|
|
72535
|
+
try {
|
|
72536
|
+
return await task();
|
|
72537
|
+
} finally {
|
|
72538
|
+
releaseSlot();
|
|
72539
|
+
}
|
|
72540
|
+
} };
|
|
72505
72541
|
};
|
|
72506
|
-
|
|
72507
|
-
* Runs `task` once a dead-code worker slot is free, releasing the slot when the
|
|
72508
|
-
* task settles (success or failure). With a high cap (roomy machine) every
|
|
72509
|
-
* caller proceeds immediately; with a low cap (constrained runner) callers
|
|
72510
|
-
* queue and run as slots free.
|
|
72511
|
-
*
|
|
72512
|
-
* `abortSignal` short-circuits the WAIT: if it's already aborted, or fires while
|
|
72513
|
-
* this caller is queued, the call rejects without acquiring a slot or running
|
|
72514
|
-
* `task` — so a cancelled scan (e.g. lint failed) doesn't sit in the queue and
|
|
72515
|
-
* then spawn a child only to tear it down. A queued caller that aborts removes
|
|
72516
|
-
* its own waiter so a later release never hands a slot to a dead request.
|
|
72517
|
-
*/
|
|
72542
|
+
let deadCodeWorkerSlots = null;
|
|
72518
72543
|
const withDeadCodeWorkerSlot = async (task, abortSignal) => {
|
|
72519
|
-
|
|
72520
|
-
|
|
72521
|
-
|
|
72522
|
-
else await new Promise((resolve, reject) => {
|
|
72523
|
-
const waiter = () => {
|
|
72524
|
-
abortSignal?.removeEventListener("abort", onAbort);
|
|
72525
|
-
resolve();
|
|
72526
|
-
};
|
|
72527
|
-
const onAbort = () => {
|
|
72528
|
-
const queuedIndex = waiters.indexOf(waiter);
|
|
72529
|
-
if (queuedIndex !== -1) waiters.splice(queuedIndex, 1);
|
|
72530
|
-
reject(/* @__PURE__ */ new Error("Dead-code worker aborted."));
|
|
72531
|
-
};
|
|
72532
|
-
waiters.push(waiter);
|
|
72533
|
-
abortSignal?.addEventListener("abort", onAbort, { once: true });
|
|
72544
|
+
deadCodeWorkerSlots ??= createWorkerSlots({
|
|
72545
|
+
slotCount: resolveDeadCodeConcurrency(),
|
|
72546
|
+
createAbortError: () => /* @__PURE__ */ new Error("Dead-code worker aborted.")
|
|
72534
72547
|
});
|
|
72535
|
-
|
|
72536
|
-
return await task();
|
|
72537
|
-
} finally {
|
|
72538
|
-
releaseSlot();
|
|
72539
|
-
}
|
|
72548
|
+
return deadCodeWorkerSlots.run(task, abortSignal);
|
|
72540
72549
|
};
|
|
72541
72550
|
const projectCacheSubdir = (projectDirectory) => crypto.createHash("sha256").update(projectDirectory).digest("hex").slice(0, 16);
|
|
72542
72551
|
const resolveReactDoctorCacheDir = (projectDirectory) => {
|
|
@@ -74063,12 +74072,12 @@ const createOxlintConfig = ({ pluginPath, project, customRulesOnly = false, exte
|
|
|
74063
74072
|
const settingsRootDirectory = resolveSettingsRootDirectory(project.rootDirectory);
|
|
74064
74073
|
const enabledReactDoctorRules = {};
|
|
74065
74074
|
for (const registryEntry of REACT_DOCTOR_RULES) {
|
|
74066
|
-
const rule =
|
|
74075
|
+
const rule = REACT_DOCTOR_RULE_REGISTRY[registryEntry.id];
|
|
74067
74076
|
if (!rule) continue;
|
|
74068
74077
|
if (ruleSelection === "cacheable" && CROSS_FILE_RULE_IDS.has(registryEntry.id)) continue;
|
|
74069
74078
|
if (ruleSelection === "sidecar" && !CROSS_FILE_RULE_IDS.has(registryEntry.id)) continue;
|
|
74070
74079
|
if (ruleSelection === "sidecar" && sidecarRuleIdFilter !== void 0 && !sidecarRuleIdFilter.has(registryEntry.id)) continue;
|
|
74071
|
-
if (rule.
|
|
74080
|
+
if (rule.isScanRule) continue;
|
|
74072
74081
|
if (customRulesOnly && registryEntry.originallyExternal) continue;
|
|
74073
74082
|
if (rule.framework !== "global" && !rule.requires) continue;
|
|
74074
74083
|
if (!shouldEnableRule(rule.requires, rule.tags, capabilities, ignoredTags, rule.disabledWhen, includedTags)) continue;
|
|
@@ -75379,7 +75388,7 @@ const PLUGIN_CATEGORY_MAP = {
|
|
|
75379
75388
|
};
|
|
75380
75389
|
const lookupOwnString = (record, key) => Object.hasOwn(record, key) ? record[key] : void 0;
|
|
75381
75390
|
const getRuleRecommendation = (ruleName, project) => {
|
|
75382
|
-
const rule =
|
|
75391
|
+
const rule = REACT_DOCTOR_RULE_REGISTRY[ruleName];
|
|
75383
75392
|
if (!rule) return void 0;
|
|
75384
75393
|
if (rule.recommendationFor) {
|
|
75385
75394
|
const capabilities = getCapabilities(project);
|
|
@@ -75388,8 +75397,8 @@ const getRuleRecommendation = (ruleName, project) => {
|
|
|
75388
75397
|
}
|
|
75389
75398
|
return rule.recommendation;
|
|
75390
75399
|
};
|
|
75391
|
-
const getRuleCategory = (ruleName) =>
|
|
75392
|
-
const getRuleTitle = (ruleName) =>
|
|
75400
|
+
const getRuleCategory = (ruleName) => REACT_DOCTOR_RULE_REGISTRY[ruleName]?.category;
|
|
75401
|
+
const getRuleTitle = (ruleName) => REACT_DOCTOR_RULE_REGISTRY[ruleName]?.title;
|
|
75393
75402
|
const resolveDiagnosticTitle = (plugin, rule) => {
|
|
75394
75403
|
if (plugin !== "react-hooks-js") return getRuleTitle(rule);
|
|
75395
75404
|
if (rule === "error-boundaries") return REACT_ERROR_BOUNDARY_TITLE;
|
|
@@ -75444,7 +75453,7 @@ const resolveDiagnosticCategory = (plugin, rule) => {
|
|
|
75444
75453
|
if (plugin === "react-hooks-js" && rule === "error-boundaries") return "Bugs";
|
|
75445
75454
|
return getRuleCategory(rule) ?? lookupOwnString(PLUGIN_CATEGORY_MAP, plugin) ?? "Bugs";
|
|
75446
75455
|
};
|
|
75447
|
-
const resolveMatchByOccurrence = (rule, category) => OCCURRENCE_MATCHED_CATEGORIES.has(category) || Boolean(
|
|
75456
|
+
const resolveMatchByOccurrence = (rule, category) => OCCURRENCE_MATCHED_CATEGORIES.has(category) || Boolean(REACT_DOCTOR_RULE_REGISTRY[rule]?.matchByOccurrence);
|
|
75448
75457
|
/**
|
|
75449
75458
|
* Maps oxlint's non-primary labels (`labels[1..]`) into related source
|
|
75450
75459
|
* locations. Editors surface these as a diagnostic's
|
|
@@ -75535,7 +75544,7 @@ const parseOxlintOutput = (stdout, project, rootDirectory, sourcePathByLintPath,
|
|
|
75535
75544
|
const preparedSourceMap = resolvePreparedSourceMap(diagnostic.filename);
|
|
75536
75545
|
if (preparedSourceMap === void 0) return [diagnostic];
|
|
75537
75546
|
const { plugin, rule } = parseRuleCode(diagnostic.code);
|
|
75538
|
-
if (plugin !== "react-doctor" || !
|
|
75547
|
+
if (plugin !== "react-doctor" || !REACT_DOCTOR_RULE_REGISTRY[rule]?.tags?.includes("design")) return [];
|
|
75539
75548
|
const labels = mapPreparedSourceLabels(diagnostic.labels, preparedSourceMap);
|
|
75540
75549
|
const sourceFilename = resolveMappedSourceFilename(diagnostic.filename);
|
|
75541
75550
|
return labels === null || sourceFilename === void 0 ? [] : [{
|
|
@@ -75621,11 +75630,12 @@ const SANITIZED_ENV = buildOxlintChildEnv(process.env);
|
|
|
75621
75630
|
* The first three are splittable (the caller's binary-split retry
|
|
75622
75631
|
* shrinks the batch and re-spawns); the fourth isn't.
|
|
75623
75632
|
*/
|
|
75624
|
-
const spawnOxlint = (args, rootDirectory, nodeBinaryPath, spawnTimeoutMs = OXLINT_SPAWN_TIMEOUT_MS, outputMaxBytes = OXLINT_OUTPUT_MAX_BYTES, abortSignal) => new Promise((resolve, reject) => {
|
|
75633
|
+
const spawnOxlint = (args, rootDirectory, nodeBinaryPath, spawnTimeoutMs = OXLINT_SPAWN_TIMEOUT_MS, outputMaxBytes = OXLINT_OUTPUT_MAX_BYTES, abortSignal, onSpawn) => new Promise((resolve, reject) => {
|
|
75625
75634
|
if (abortSignal?.aborted) {
|
|
75626
75635
|
reject(new ReactDoctorError({ reason: new OxlintSpawnFailed({ cause: "lint phase aborted" }) }));
|
|
75627
75636
|
return;
|
|
75628
75637
|
}
|
|
75638
|
+
onSpawn?.();
|
|
75629
75639
|
const child = spawn(nodeBinaryPath, buildProfiledNodeArguments({
|
|
75630
75640
|
argumentsList: args,
|
|
75631
75641
|
cpuProfileDirectory: SANITIZED_ENV.REACT_DOCTOR_CPU_PROFILE_DIR,
|
|
@@ -75785,7 +75795,21 @@ const spawnLintBatches = async (input) => {
|
|
|
75785
75795
|
}
|
|
75786
75796
|
const batchArgs = [...baseArgs, ...batch];
|
|
75787
75797
|
try {
|
|
75788
|
-
|
|
75798
|
+
const spawnBatch = () => {
|
|
75799
|
+
if (isPastDeadline()) {
|
|
75800
|
+
deadlineSkippedFiles.push(...batch);
|
|
75801
|
+
batchState.deadlineSkippedFileCount += batch.length;
|
|
75802
|
+
return Promise.resolve(null);
|
|
75803
|
+
}
|
|
75804
|
+
return spawnOxlint(batchArgs, rootDirectory, nodeBinaryPath, spawnTimeoutMs, outputMaxBytes, signal, () => {
|
|
75805
|
+
if (batchState.didStart) return;
|
|
75806
|
+
batchState.didStart = true;
|
|
75807
|
+
startedFileCount += batchState.initialFileCount;
|
|
75808
|
+
});
|
|
75809
|
+
};
|
|
75810
|
+
const stdout = input.spawnSlots === void 0 ? await spawnBatch() : await input.spawnSlots.run(spawnBatch, signal);
|
|
75811
|
+
if (stdout === null) return [];
|
|
75812
|
+
return parseOxlintOutput(stdout, project, rootDirectory, sourcePathByLintPath, sourceMapByLintPath);
|
|
75789
75813
|
} catch (error) {
|
|
75790
75814
|
if (!isSplittableReactDoctorError(error)) throw error;
|
|
75791
75815
|
if (isPastDeadline()) {
|
|
@@ -75829,14 +75853,15 @@ const spawnLintBatches = async (input) => {
|
|
|
75829
75853
|
deadlineSkippedFiles.push(...batch);
|
|
75830
75854
|
return [];
|
|
75831
75855
|
}
|
|
75832
|
-
startedFileCount += batch.length;
|
|
75833
75856
|
const batchState = {
|
|
75834
75857
|
deadlineMs: null,
|
|
75835
|
-
deadlineSkippedFileCount: 0
|
|
75858
|
+
deadlineSkippedFileCount: 0,
|
|
75859
|
+
didStart: false,
|
|
75860
|
+
initialFileCount: batch.length
|
|
75836
75861
|
};
|
|
75837
75862
|
const batchDiagnostics = await spawnLintBatch(batch, 0, batchState);
|
|
75838
75863
|
scannedFileCount += batch.length - batchState.deadlineSkippedFileCount;
|
|
75839
|
-
if (passOnFileProgress) {
|
|
75864
|
+
if (passOnFileProgress && batchState.didStart) {
|
|
75840
75865
|
displayedFileCount = Math.min(Math.max(displayedFileCount, scannedFileCount), totalFileCount);
|
|
75841
75866
|
passOnFileProgress(displayedFileCount, totalFileCount);
|
|
75842
75867
|
}
|
|
@@ -75920,8 +75945,8 @@ const validateRuleRegistration = () => {
|
|
|
75920
75945
|
for (const fullKey of ALL_REACT_DOCTOR_RULE_KEYS) {
|
|
75921
75946
|
const ruleName = fullKey.replace(/^react-doctor\//, "");
|
|
75922
75947
|
if (!getRuleCategory(ruleName)) missingCategory.push(fullKey);
|
|
75923
|
-
if (!
|
|
75924
|
-
if (FRAMEWORK_SPECIFIC_RULE_KEYS.has(fullKey) && !
|
|
75948
|
+
if (!REACT_DOCTOR_RULE_REGISTRY[ruleName]?.recommendation) missingHelp.push(fullKey);
|
|
75949
|
+
if (FRAMEWORK_SPECIFIC_RULE_KEYS.has(fullKey) && !REACT_DOCTOR_RULE_REGISTRY[ruleName]?.requires) missingMetadata.push(fullKey);
|
|
75925
75950
|
}
|
|
75926
75951
|
if (missingCategory.length === 0 && missingHelp.length === 0 && missingMetadata.length === 0) return;
|
|
75927
75952
|
const detail = [
|
|
@@ -76562,6 +76587,7 @@ const runOxlint = async (options) => {
|
|
|
76562
76587
|
spawnTimeoutMs,
|
|
76563
76588
|
outputMaxBytes,
|
|
76564
76589
|
concurrency: options.concurrency,
|
|
76590
|
+
spawnSlots: options.spawnSlots,
|
|
76565
76591
|
signal: options.signal,
|
|
76566
76592
|
deadlineEpochMs: options.deadlineEpochMs
|
|
76567
76593
|
});
|
|
@@ -76764,6 +76790,7 @@ const runOxlint = async (options) => {
|
|
|
76764
76790
|
spawnTimeoutMs,
|
|
76765
76791
|
outputMaxBytes,
|
|
76766
76792
|
concurrency: options.concurrency,
|
|
76793
|
+
spawnSlots: options.spawnSlots,
|
|
76767
76794
|
signal: options.signal,
|
|
76768
76795
|
deadlineEpochMs: options.deadlineEpochMs
|
|
76769
76796
|
});
|
|
@@ -76858,6 +76885,7 @@ var Linter = class Linter extends Service()("react-doctor/Linter") {
|
|
|
76858
76885
|
const spawnTimeoutMs = yield* OxlintSpawnTimeoutMs;
|
|
76859
76886
|
const outputMaxBytes = yield* OxlintOutputMaxBytes;
|
|
76860
76887
|
const concurrency = yield* OxlintConcurrency;
|
|
76888
|
+
const spawnSlots = yield* OxlintSpawnSlots;
|
|
76861
76889
|
const lintBatchOrdering = yield* LintBatchOrdering;
|
|
76862
76890
|
const perFileLintCacheEnabled = yield* PerFileLintCacheEnabled;
|
|
76863
76891
|
const sidecarLintCacheEnabled = yield* SidecarLintCacheEnabled;
|
|
@@ -76887,6 +76915,7 @@ var Linter = class Linter extends Service()("react-doctor/Linter") {
|
|
|
76887
76915
|
onSidecarStats: input.onSidecarStats,
|
|
76888
76916
|
spawnTimeoutMs,
|
|
76889
76917
|
outputMaxBytes,
|
|
76918
|
+
spawnSlots: spawnSlots ?? void 0,
|
|
76890
76919
|
concurrency,
|
|
76891
76920
|
signal,
|
|
76892
76921
|
lintBatchOrdering,
|
|
@@ -78549,6 +78578,11 @@ const computeConfigFingerprint = (projectDirectory, version) => {
|
|
|
78549
78578
|
}
|
|
78550
78579
|
return crypto.createHash("sha1").update(parts.join("|")).digest("hex");
|
|
78551
78580
|
};
|
|
78581
|
+
const createLintPhaseAbortError = () => new ReactDoctorError({ reason: new OxlintSpawnFailed({ cause: "lint phase aborted" }) });
|
|
78582
|
+
const createOxlintSpawnSlots = (concurrency) => createWorkerSlots({
|
|
78583
|
+
slotCount: resolveScanConcurrency(concurrency),
|
|
78584
|
+
createAbortError: createLintPhaseAbortError
|
|
78585
|
+
});
|
|
78552
78586
|
const AI_TRAINING_ENV_VARS = [
|
|
78553
78587
|
["HF_DATASETS_CACHE", "huggingface"],
|
|
78554
78588
|
["HF_HOME", "huggingface"],
|
|
@@ -78628,7 +78662,7 @@ const groupBy = (items, keyFn) => {
|
|
|
78628
78662
|
* recipe, so advertising "fetch the fix recipe" for them sends agents to
|
|
78629
78663
|
* a 404. Gate the directive on this predicate.
|
|
78630
78664
|
*/
|
|
78631
|
-
const hasPublishedFixRecipe = (diagnostic) => diagnostic.plugin === "react-doctor" && Object.hasOwn(
|
|
78665
|
+
const hasPublishedFixRecipe = (diagnostic) => diagnostic.plugin === "react-doctor" && Object.hasOwn(REACT_DOCTOR_RULE_REGISTRY, diagnostic.rule);
|
|
78632
78666
|
const unionValues = (baseValues, overrideValues) => [...new Set([...baseValues, ...overrideValues])];
|
|
78633
78667
|
const mergeIgnores = (baseIgnore, overrideIgnore) => {
|
|
78634
78668
|
const mergedIgnore = {
|
|
@@ -78976,7 +79010,7 @@ const makeNoopConsole = () => ({
|
|
|
78976
79010
|
});
|
|
78977
79011
|
//#endregion
|
|
78978
79012
|
//#region src/cli/utils/version.ts
|
|
78979
|
-
const VERSION = "0.9.2-dev.
|
|
79013
|
+
const VERSION = "0.9.2-dev.992205a";
|
|
78980
79014
|
//#endregion
|
|
78981
79015
|
//#region src/cli/utils/json-mode.ts
|
|
78982
79016
|
let context = null;
|
|
@@ -79334,13 +79368,13 @@ const isDevVersion = (version) => version === "0.0.0" || version.includes("-");
|
|
|
79334
79368
|
* uploads source-map artifacts under, so stack frames symbolicate. Honors the
|
|
79335
79369
|
* standard `SENTRY_RELEASE` override.
|
|
79336
79370
|
*/
|
|
79337
|
-
const resolveSentryRelease = () => process.env.SENTRY_RELEASE || `react-doctor@0.9.2-dev.
|
|
79371
|
+
const resolveSentryRelease = () => process.env.SENTRY_RELEASE || `react-doctor@0.9.2-dev.992205a`;
|
|
79338
79372
|
/**
|
|
79339
79373
|
* Deployment environment shown in Sentry's environment filter. Defaults to
|
|
79340
79374
|
* `production` for tagged releases and `development` for dev/unbuilt versions,
|
|
79341
79375
|
* overridable via the standard `SENTRY_ENVIRONMENT` env var.
|
|
79342
79376
|
*/
|
|
79343
|
-
const resolveSentryEnvironment = () => process.env.SENTRY_ENVIRONMENT || (isDevVersion("0.9.2-dev.
|
|
79377
|
+
const resolveSentryEnvironment = () => process.env.SENTRY_ENVIRONMENT || (isDevVersion("0.9.2-dev.992205a") ? "development" : "production");
|
|
79344
79378
|
/**
|
|
79345
79379
|
* Performance-tracing sample rate in `[0, 1]`. Reads `SENTRY_TRACES_SAMPLE_RATE`
|
|
79346
79380
|
* (set to `0` to disable tracing) and falls back to
|
|
@@ -91194,7 +91228,8 @@ const buildRuntimeLayers = (input) => {
|
|
|
91194
91228
|
resolvedDirectory: input.directory,
|
|
91195
91229
|
configSourceDirectory: input.configSourceDirectory
|
|
91196
91230
|
}) : Config.layerNode, Files.layerNode, Git.layerNode, linterLayer, LintPartialFailures.layerLive, deadCodeLayer, progressLayer, reporterLayer, scoreLayer, supplyChainLayer);
|
|
91197
|
-
|
|
91231
|
+
const layersWithConcurrency = input.oxlintConcurrency === void 0 ? baseLayers : mergeAll$1(baseLayers, succeed$3(OxlintConcurrency, input.oxlintConcurrency));
|
|
91232
|
+
return input.oxlintSpawnSlots === void 0 ? layersWithConcurrency : mergeAll$1(layersWithConcurrency, succeed$3(OxlintSpawnSlots, input.oxlintSpawnSlots));
|
|
91198
91233
|
};
|
|
91199
91234
|
//#endregion
|
|
91200
91235
|
//#region src/cli/utils/with-sentry-run-span.ts
|
|
@@ -93698,7 +93733,7 @@ const buildRunEventConfig = (options, userConfig, hasCustomConfig, resolvedWorke
|
|
|
93698
93733
|
userConfig
|
|
93699
93734
|
};
|
|
93700
93735
|
};
|
|
93701
|
-
const
|
|
93736
|
+
const inspectWithOxlintRuntime = async (directory, inputOptions, oxlintRuntime) => {
|
|
93702
93737
|
const startTime = performance$1.now();
|
|
93703
93738
|
const deadlineEpochMs = inputOptions.deadlineEpochMs ?? (inputOptions.maxDurationMs != null ? Date.now() + inputOptions.maxDurationMs : null);
|
|
93704
93739
|
const isConcurrentScan = inputOptions.concurrentScan === true;
|
|
@@ -93724,7 +93759,7 @@ const inspect = async (directory, inputOptions = {}) => {
|
|
|
93724
93759
|
try {
|
|
93725
93760
|
const result = await withSentryRunSpan(async (rootSentrySpan) => {
|
|
93726
93761
|
try {
|
|
93727
|
-
return await runInspectWithRuntime(scanDirectory, options, userConfig, hasConfigOverride, configSourceDirectory, startTime, deadlineEpochMs, rootSentrySpan);
|
|
93762
|
+
return await runInspectWithRuntime(scanDirectory, options, userConfig, hasConfigOverride, configSourceDirectory, startTime, deadlineEpochMs, rootSentrySpan, oxlintRuntime);
|
|
93728
93763
|
} catch (error) {
|
|
93729
93764
|
recordRunEvent(rootSentrySpan, {
|
|
93730
93765
|
...buildRunEventConfig(options, userConfig, userConfig !== null),
|
|
@@ -93740,6 +93775,15 @@ const inspect = async (directory, inputOptions = {}) => {
|
|
|
93740
93775
|
if (ownsSpinnerSilence) setSpinnerSilent(wasSpinnerSilent);
|
|
93741
93776
|
}
|
|
93742
93777
|
};
|
|
93778
|
+
const createInvocationInspect = (requestedOxlintConcurrency) => {
|
|
93779
|
+
const concurrency = resolveScanConcurrency(requestedOxlintConcurrency ?? runSync(OxlintConcurrency));
|
|
93780
|
+
const oxlintRuntime = {
|
|
93781
|
+
concurrency,
|
|
93782
|
+
spawnSlots: createOxlintSpawnSlots(concurrency)
|
|
93783
|
+
};
|
|
93784
|
+
return (directory, inputOptions = {}) => inspectWithOxlintRuntime(directory, inputOptions, oxlintRuntime);
|
|
93785
|
+
};
|
|
93786
|
+
const inspect = async (directory, inputOptions = {}) => createInvocationInspect(inputOptions.concurrency)(directory, inputOptions);
|
|
93743
93787
|
const countIncompleteLintFiles = (lintPartialFailures) => countDroppedLintFiles(lintPartialFailures) + countDeadlineSkippedFiles(lintPartialFailures);
|
|
93744
93788
|
/**
|
|
93745
93789
|
* Runs a second, lint-only scan over the changed files as they existed at the
|
|
@@ -93793,7 +93837,8 @@ const runBaselineComparison = async (params) => {
|
|
|
93793
93837
|
shouldRunSupplyChain: params.options.supplyChain,
|
|
93794
93838
|
shouldComputeScore: false,
|
|
93795
93839
|
shouldShowProgressSpinners: false,
|
|
93796
|
-
oxlintConcurrency: params.
|
|
93840
|
+
oxlintConcurrency: params.oxlintRuntime.concurrency,
|
|
93841
|
+
oxlintSpawnSlots: params.oxlintRuntime.spawnSlots
|
|
93797
93842
|
});
|
|
93798
93843
|
const baseOutput = await runPromise(restoreLegacyThrow(runInspect({
|
|
93799
93844
|
directory: snapshot.tempDirectory,
|
|
@@ -93838,7 +93883,7 @@ const runBaselineComparison = async (params) => {
|
|
|
93838
93883
|
snapshot.cleanup();
|
|
93839
93884
|
}
|
|
93840
93885
|
};
|
|
93841
|
-
const runInspectWithRuntime = async (directory, options, userConfig, hasConfigOverride, configSourceDirectory, startTime, deadlineEpochMs, rootSentrySpan) => {
|
|
93886
|
+
const runInspectWithRuntime = async (directory, options, userConfig, hasConfigOverride, configSourceDirectory, startTime, deadlineEpochMs, rootSentrySpan, oxlintRuntime) => {
|
|
93842
93887
|
const isDiffMode = options.includePaths.length > 0;
|
|
93843
93888
|
const resolvedNodeBinaryPath = await resolveOxlintNode(options.lint, options.scoreOnly || options.silent);
|
|
93844
93889
|
const lintBindingMissing = options.lint && !resolvedNodeBinaryPath;
|
|
@@ -93888,7 +93933,8 @@ const runInspectWithRuntime = async (directory, options, userConfig, hasConfigOv
|
|
|
93888
93933
|
shouldRunSupplyChain: options.supplyChain,
|
|
93889
93934
|
shouldComputeScore: !options.noScore,
|
|
93890
93935
|
shouldShowProgressSpinners,
|
|
93891
|
-
oxlintConcurrency:
|
|
93936
|
+
oxlintConcurrency: oxlintRuntime.concurrency,
|
|
93937
|
+
oxlintSpawnSlots: oxlintRuntime.spawnSlots,
|
|
93892
93938
|
reporterLayer: options.uiLayers?.reporter,
|
|
93893
93939
|
progressLayer: options.uiLayers?.progress
|
|
93894
93940
|
});
|
|
@@ -93945,7 +93991,8 @@ const runInspectWithRuntime = async (directory, options, userConfig, hasConfigOv
|
|
|
93945
93991
|
baseFiles: options.baseline.baseFiles,
|
|
93946
93992
|
headFiles: options.baseline.headFiles,
|
|
93947
93993
|
headAnalyzedFiles: output.analyzedFiles,
|
|
93948
|
-
deadlineEpochMs
|
|
93994
|
+
deadlineEpochMs,
|
|
93995
|
+
oxlintRuntime
|
|
93949
93996
|
});
|
|
93950
93997
|
if (comparison) {
|
|
93951
93998
|
inspectDiagnostics = comparison.displayDiagnostics;
|
|
@@ -96778,6 +96825,7 @@ const inspectAction = async (directory, flags, invocationCommand = "inspect") =>
|
|
|
96778
96825
|
if (!isQuiet) if (!flags.verbose && canAnimateOnboarding(process.stdout)) await runPromise(playWelcomeScene({ speedMultiplier: !isOnboardingForced() && hasCompletedOnboarding() ? 2 : 1 }));
|
|
96779
96826
|
else runSync(printBrandedHeader);
|
|
96780
96827
|
const scanOptions = resolveCliInspectOptions(flags, userConfig);
|
|
96828
|
+
const inspectProject = createInvocationInspect(scanOptions.concurrency);
|
|
96781
96829
|
const scanDeadlineEpochMs = scanOptions.maxDurationMs !== void 0 ? Date.now() + scanOptions.maxDurationMs : void 0;
|
|
96782
96830
|
const categoryFilters = new Set(scanOptions.categoryFilters ?? []);
|
|
96783
96831
|
const skipPrompts = shouldSkipPrompts({
|
|
@@ -96923,7 +96971,7 @@ const inspectAction = async (directory, flags, invocationCommand = "inspect") =>
|
|
|
96923
96971
|
const scanStagedProject = async (projectRun) => {
|
|
96924
96972
|
const { projectScan, includePaths } = projectRun;
|
|
96925
96973
|
const projectTempDirectory = Path.join(snapshot.tempDirectory, projectScan.treeRelativeDirectory);
|
|
96926
|
-
const scanResult = await
|
|
96974
|
+
const scanResult = await inspectProject(projectTempDirectory, {
|
|
96927
96975
|
...scanOptions,
|
|
96928
96976
|
deadlineEpochMs: scanDeadlineEpochMs,
|
|
96929
96977
|
includePaths: [...includePaths],
|
|
@@ -97082,7 +97130,7 @@ const inspectAction = async (directory, flags, invocationCommand = "inspect") =>
|
|
|
97082
97130
|
if (supplyChainManifestChanged) includePaths.push("package.json");
|
|
97083
97131
|
}
|
|
97084
97132
|
if (!isQuiet && !isMultiProject) cliLogger.dim(" ");
|
|
97085
|
-
const scanResult = await
|
|
97133
|
+
const scanResult = await inspectProject(scanDirectory, {
|
|
97086
97134
|
...scanOptions,
|
|
97087
97135
|
deadlineEpochMs: scanDeadlineEpochMs,
|
|
97088
97136
|
includePaths,
|
|
@@ -98307,7 +98355,7 @@ program.command("experimental-tui [directory]", { hidden: true }).description("[
|
|
|
98307
98355
|
return;
|
|
98308
98356
|
}
|
|
98309
98357
|
recordCount(METRIC.cliInvoked, 1, { command: "experimental-tui" });
|
|
98310
|
-
const { runScanApp } = await import("./run-scan-app-
|
|
98358
|
+
const { runScanApp } = await import("./run-scan-app-DHRvqfkX.js");
|
|
98311
98359
|
const { shouldFail } = await runScanApp({
|
|
98312
98360
|
directory,
|
|
98313
98361
|
options: {
|
|
@@ -98347,7 +98395,7 @@ Promise.resolve().then(() => assertNoRemovedFlags(process.argv)).then(() => prog
|
|
|
98347
98395
|
handleError(error, { sentryEventId });
|
|
98348
98396
|
});
|
|
98349
98397
|
//#endregion
|
|
98350
|
-
export { toForwardSlashes as A, getCategoryImpact as B, buildRulePriorityMap as C, resolveBlockingLevel as D, formatLearnMoreLine as E, TUI_REPORT_DETAIL_WIDTH_FRACTION as F, runPromise as G, mapWithConcurrency as H, isCiEnvironment as I, succeed$3 as J, sync as K, DIAGNOSTIC_CATEGORY_BUCKETS as L, isUnicodeSupported as M, METRIC as N, isReactDoctorWorkflowInstalled as O, TUI_REPORT_COMPACT_MAX_ROWS as P, Progress as R, hasLintHardFailure as S, formatFixRecipeLine as T, mergeReactDoctorConfigs as U, highlighter as V, resolveScanTarget as W, canAnimateOnboarding as _, resolveCliInspectOptions as a, buildCodeFrame as b, detectLaunchableAgents as c, launchCliAgent as d, isShareOptedOut as f, doctorFace as g, easeOutCubic as h, selectProjects as i, findNearestPackageDirectory as j, recordCount as k, CLI_AGENT_BINARIES as l, printFooter as m, shouldFailScanGate as n, countUniqueScannedFiles as o,
|
|
98398
|
+
export { toForwardSlashes as A, getCategoryImpact as B, buildRulePriorityMap as C, resolveBlockingLevel as D, formatLearnMoreLine as E, TUI_REPORT_DETAIL_WIDTH_FRACTION as F, runPromise as G, mapWithConcurrency as H, isCiEnvironment as I, succeed$3 as J, sync as K, DIAGNOSTIC_CATEGORY_BUCKETS as L, isUnicodeSupported as M, METRIC as N, isReactDoctorWorkflowInstalled as O, TUI_REPORT_COMPACT_MAX_ROWS as P, Progress as R, hasLintHardFailure as S, formatFixRecipeLine as T, mergeReactDoctorConfigs as U, highlighter as V, resolveScanTarget as W, canAnimateOnboarding as _, resolveCliInspectOptions as a, buildCodeFrame as b, detectLaunchableAgents as c, launchCliAgent as d, isShareOptedOut as f, doctorFace as g, easeOutCubic as h, selectProjects as i, findNearestPackageDirectory as j, recordCount as k, CLI_AGENT_BINARIES as l, printFooter as m, shouldFailScanGate as n, countUniqueScannedFiles as o, createInvocationInspect as p, void_ as q, discoverWorkspacePackages as r, setUpGitHubActions as s, exitGracefully as t, copyToClipboard as u, computeProjectedScore as v, buildSortedRuleGroups as w, buildNoScoreMessage as x, formatElapsedTime as y, Reporter as z };
|
|
98351
98399
|
|
|
98352
98400
|
//# sourceMappingURL=cli.js.map
|
|
98353
|
-
//# debugId=
|
|
98401
|
+
//# debugId=3bff4f9b-31f8-5abe-b7a2-a0ab9f6a85b4
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
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]="61d9689f-8ba5-5ca3-a0c2-09557d3f7028")}catch(e){}}();
|
|
3
3
|
import { i as __toESM$1, r as __require, t as __commonJSMin$1 } from "./chunk-ChKA3wi6.js";
|
|
4
4
|
import { createRequire } from "node:module";
|
|
5
|
-
import
|
|
5
|
+
import { ALL_REACT_DOCTOR_RULE_KEYS, CROSS_FILE_DEPENDENCY_COLLECTORS, CROSS_FILE_RULE_IDS, FRAMEWORK_SPECIFIC_RULE_KEYS, FRAMEWORK_TOKENS, MOTION_LIBRARY_PACKAGES, REACT_COMPILER_RULES, REACT_DOCTOR_RULES, REACT_DOCTOR_RULE_REGISTRY, REACT_DOCTOR_SCAN_RULES, classifySecurityScanFile, collectCrossFileDependencyProbes, resetManifestCaches, shouldReadSecurityScanContent } from "oxlint-plugin-react-doctor/core";
|
|
6
6
|
import * as Crypto from "node:crypto";
|
|
7
7
|
import crypto, { createHash } from "node:crypto";
|
|
8
8
|
import * as NFS from "node:fs";
|
|
@@ -65673,7 +65673,7 @@ const evaluateSuppression = (lines, diagnosticLineIndex, ruleId) => {
|
|
|
65673
65673
|
const getDiagnosticRuleIdentity = (diagnostic) => ({
|
|
65674
65674
|
ruleKey: `${diagnostic.plugin}/${diagnostic.rule}`,
|
|
65675
65675
|
category: diagnostic.category,
|
|
65676
|
-
tags: diagnostic.plugin === "react-doctor" ?
|
|
65676
|
+
tags: diagnostic.plugin === "react-doctor" ? REACT_DOCTOR_RULE_REGISTRY[diagnostic.rule]?.tags ?? [] : []
|
|
65677
65677
|
});
|
|
65678
65678
|
const compileIgnoredFilePatterns = (userConfig) => {
|
|
65679
65679
|
const files = userConfig?.ignore?.files;
|
|
@@ -66025,7 +66025,7 @@ const buildDiagnosticPipeline = (input) => {
|
|
|
66025
66025
|
};
|
|
66026
66026
|
const shouldAutoSuppress = (diagnostic) => {
|
|
66027
66027
|
if (diagnostic.plugin !== "react-doctor") return false;
|
|
66028
|
-
const rule =
|
|
66028
|
+
const rule = REACT_DOCTOR_RULE_REGISTRY[diagnostic.rule];
|
|
66029
66029
|
if (!rule?.tags?.includes("test-noise")) return false;
|
|
66030
66030
|
if (rule.tags.includes("migration-hint")) return false;
|
|
66031
66031
|
return getFileContext(diagnostic.filePath) !== "production";
|
|
@@ -66457,6 +66457,7 @@ var OxlintConcurrency = class extends Reference("react-doctor/OxlintConcurrency"
|
|
|
66457
66457
|
if (Number.isInteger(parsed) && parsed > 0) return resolveScanConcurrency(parsed);
|
|
66458
66458
|
return resolveAutoScanConcurrency();
|
|
66459
66459
|
} }) {};
|
|
66460
|
+
var OxlintSpawnSlots = class extends Reference("react-doctor/OxlintSpawnSlots", { defaultValue: () => null }) {};
|
|
66460
66461
|
/**
|
|
66461
66462
|
* Three-state control for overlapping the dead-code pass with the lint pass —
|
|
66462
66463
|
* forking dead-code as a child fiber that runs DURING lint instead of strictly
|
|
@@ -68779,8 +68780,8 @@ const getCapabilities = (project) => {
|
|
|
68779
68780
|
const cached = capabilitiesByProject.get(project);
|
|
68780
68781
|
if (cached !== void 0) return cached;
|
|
68781
68782
|
const capabilities = new Set(buildCapabilities(project));
|
|
68782
|
-
const
|
|
68783
|
-
const targetBlankOpenerProtection = detectTargetBlankOpenerProtection(
|
|
68783
|
+
const capabilityRootDirectory = isFile(Path.join(project.rootDirectory, "package.json")) ? project.rootDirectory : findNearestAncestorPackageJson(project.rootDirectory) ?? project.rootDirectory;
|
|
68784
|
+
const targetBlankOpenerProtection = detectTargetBlankOpenerProtection(capabilityRootDirectory, readPackageJson(Path.join(capabilityRootDirectory, "package.json")));
|
|
68784
68785
|
if (targetBlankOpenerProtection !== void 0) capabilities.add("target-blank-needs-explicit-protection");
|
|
68785
68786
|
if (targetBlankOpenerProtection === "noreferrer") capabilities.add("target-blank-needs-noreferrer");
|
|
68786
68787
|
capabilitiesByProject.set(project, capabilities);
|
|
@@ -68810,7 +68811,7 @@ const createSecurityScanSession = (rootDirectory, options) => {
|
|
|
68810
68811
|
const capabilities = options.project ? getCapabilities(options.project) : /* @__PURE__ */ new Set();
|
|
68811
68812
|
const ignoredTags = options.ignoredTags ?? /* @__PURE__ */ new Set();
|
|
68812
68813
|
const includedTags = options.includedTags ?? /* @__PURE__ */ new Set();
|
|
68813
|
-
const enabledScanRules =
|
|
68814
|
+
const enabledScanRules = REACT_DOCTOR_SCAN_RULES.flatMap((entry) => {
|
|
68814
68815
|
const rule = entry.rule;
|
|
68815
68816
|
const scan = rule.scan;
|
|
68816
68817
|
if (typeof scan !== "function") return [];
|
|
@@ -69255,47 +69256,55 @@ const resolveDeadCodeConcurrency = (facts = readSystemFacts()) => {
|
|
|
69255
69256
|
const memoryBoundedWorkers = Math.floor(availableMemoryBytes / DEAD_CODE_WORKER_MEM_BUDGET_BYTES);
|
|
69256
69257
|
return Math.max(1, Math.min(facts.availableCores, memoryBoundedWorkers));
|
|
69257
69258
|
};
|
|
69258
|
-
|
|
69259
|
-
|
|
69260
|
-
const
|
|
69261
|
-
const
|
|
69262
|
-
|
|
69263
|
-
|
|
69259
|
+
const createWorkerSlots = (input) => {
|
|
69260
|
+
let availableSlotCount = input.slotCount;
|
|
69261
|
+
const waiters = [];
|
|
69262
|
+
const releaseSlot = () => {
|
|
69263
|
+
const nextWaiter = waiters.shift();
|
|
69264
|
+
if (nextWaiter === void 0) {
|
|
69265
|
+
availableSlotCount += 1;
|
|
69266
|
+
return;
|
|
69267
|
+
}
|
|
69268
|
+
nextWaiter.abortSignal?.removeEventListener("abort", nextWaiter.onAbort);
|
|
69269
|
+
nextWaiter.resolve();
|
|
69270
|
+
};
|
|
69271
|
+
const acquireSlot = async (abortSignal) => {
|
|
69272
|
+
if (abortSignal?.aborted) throw input.createAbortError();
|
|
69273
|
+
if (availableSlotCount > 0) {
|
|
69274
|
+
availableSlotCount -= 1;
|
|
69275
|
+
return;
|
|
69276
|
+
}
|
|
69277
|
+
await new Promise((resolve, reject) => {
|
|
69278
|
+
const onAbort = () => {
|
|
69279
|
+
const waiterIndex = waiters.indexOf(waiter);
|
|
69280
|
+
if (waiterIndex !== -1) waiters.splice(waiterIndex, 1);
|
|
69281
|
+
reject(input.createAbortError());
|
|
69282
|
+
};
|
|
69283
|
+
const waiter = {
|
|
69284
|
+
resolve,
|
|
69285
|
+
abortSignal,
|
|
69286
|
+
onAbort
|
|
69287
|
+
};
|
|
69288
|
+
waiters.push(waiter);
|
|
69289
|
+
abortSignal?.addEventListener("abort", onAbort, { once: true });
|
|
69290
|
+
});
|
|
69291
|
+
};
|
|
69292
|
+
return { run: async (task, abortSignal) => {
|
|
69293
|
+
await acquireSlot(abortSignal);
|
|
69294
|
+
try {
|
|
69295
|
+
return await task();
|
|
69296
|
+
} finally {
|
|
69297
|
+
releaseSlot();
|
|
69298
|
+
}
|
|
69299
|
+
} };
|
|
69264
69300
|
};
|
|
69265
|
-
|
|
69266
|
-
* Runs `task` once a dead-code worker slot is free, releasing the slot when the
|
|
69267
|
-
* task settles (success or failure). With a high cap (roomy machine) every
|
|
69268
|
-
* caller proceeds immediately; with a low cap (constrained runner) callers
|
|
69269
|
-
* queue and run as slots free.
|
|
69270
|
-
*
|
|
69271
|
-
* `abortSignal` short-circuits the WAIT: if it's already aborted, or fires while
|
|
69272
|
-
* this caller is queued, the call rejects without acquiring a slot or running
|
|
69273
|
-
* `task` — so a cancelled scan (e.g. lint failed) doesn't sit in the queue and
|
|
69274
|
-
* then spawn a child only to tear it down. A queued caller that aborts removes
|
|
69275
|
-
* its own waiter so a later release never hands a slot to a dead request.
|
|
69276
|
-
*/
|
|
69301
|
+
let deadCodeWorkerSlots = null;
|
|
69277
69302
|
const withDeadCodeWorkerSlot = async (task, abortSignal) => {
|
|
69278
|
-
|
|
69279
|
-
|
|
69280
|
-
|
|
69281
|
-
else await new Promise((resolve, reject) => {
|
|
69282
|
-
const waiter = () => {
|
|
69283
|
-
abortSignal?.removeEventListener("abort", onAbort);
|
|
69284
|
-
resolve();
|
|
69285
|
-
};
|
|
69286
|
-
const onAbort = () => {
|
|
69287
|
-
const queuedIndex = waiters.indexOf(waiter);
|
|
69288
|
-
if (queuedIndex !== -1) waiters.splice(queuedIndex, 1);
|
|
69289
|
-
reject(/* @__PURE__ */ new Error("Dead-code worker aborted."));
|
|
69290
|
-
};
|
|
69291
|
-
waiters.push(waiter);
|
|
69292
|
-
abortSignal?.addEventListener("abort", onAbort, { once: true });
|
|
69303
|
+
deadCodeWorkerSlots ??= createWorkerSlots({
|
|
69304
|
+
slotCount: resolveDeadCodeConcurrency(),
|
|
69305
|
+
createAbortError: () => /* @__PURE__ */ new Error("Dead-code worker aborted.")
|
|
69293
69306
|
});
|
|
69294
|
-
|
|
69295
|
-
return await task();
|
|
69296
|
-
} finally {
|
|
69297
|
-
releaseSlot();
|
|
69298
|
-
}
|
|
69307
|
+
return deadCodeWorkerSlots.run(task, abortSignal);
|
|
69299
69308
|
};
|
|
69300
69309
|
const projectCacheSubdir = (projectDirectory) => crypto.createHash("sha256").update(projectDirectory).digest("hex").slice(0, 16);
|
|
69301
69310
|
const resolveReactDoctorCacheDir = (projectDirectory) => {
|
|
@@ -70822,12 +70831,12 @@ const createOxlintConfig = ({ pluginPath, project, customRulesOnly = false, exte
|
|
|
70822
70831
|
const settingsRootDirectory = resolveSettingsRootDirectory(project.rootDirectory);
|
|
70823
70832
|
const enabledReactDoctorRules = {};
|
|
70824
70833
|
for (const registryEntry of REACT_DOCTOR_RULES) {
|
|
70825
|
-
const rule =
|
|
70834
|
+
const rule = REACT_DOCTOR_RULE_REGISTRY[registryEntry.id];
|
|
70826
70835
|
if (!rule) continue;
|
|
70827
70836
|
if (ruleSelection === "cacheable" && CROSS_FILE_RULE_IDS.has(registryEntry.id)) continue;
|
|
70828
70837
|
if (ruleSelection === "sidecar" && !CROSS_FILE_RULE_IDS.has(registryEntry.id)) continue;
|
|
70829
70838
|
if (ruleSelection === "sidecar" && sidecarRuleIdFilter !== void 0 && !sidecarRuleIdFilter.has(registryEntry.id)) continue;
|
|
70830
|
-
if (rule.
|
|
70839
|
+
if (rule.isScanRule) continue;
|
|
70831
70840
|
if (customRulesOnly && registryEntry.originallyExternal) continue;
|
|
70832
70841
|
if (rule.framework !== "global" && !rule.requires) continue;
|
|
70833
70842
|
if (!shouldEnableRule(rule.requires, rule.tags, capabilities, ignoredTags, rule.disabledWhen, includedTags)) continue;
|
|
@@ -72138,7 +72147,7 @@ const PLUGIN_CATEGORY_MAP = {
|
|
|
72138
72147
|
};
|
|
72139
72148
|
const lookupOwnString = (record, key) => Object.hasOwn(record, key) ? record[key] : void 0;
|
|
72140
72149
|
const getRuleRecommendation = (ruleName, project) => {
|
|
72141
|
-
const rule =
|
|
72150
|
+
const rule = REACT_DOCTOR_RULE_REGISTRY[ruleName];
|
|
72142
72151
|
if (!rule) return void 0;
|
|
72143
72152
|
if (rule.recommendationFor) {
|
|
72144
72153
|
const capabilities = getCapabilities(project);
|
|
@@ -72147,8 +72156,8 @@ const getRuleRecommendation = (ruleName, project) => {
|
|
|
72147
72156
|
}
|
|
72148
72157
|
return rule.recommendation;
|
|
72149
72158
|
};
|
|
72150
|
-
const getRuleCategory = (ruleName) =>
|
|
72151
|
-
const getRuleTitle = (ruleName) =>
|
|
72159
|
+
const getRuleCategory = (ruleName) => REACT_DOCTOR_RULE_REGISTRY[ruleName]?.category;
|
|
72160
|
+
const getRuleTitle = (ruleName) => REACT_DOCTOR_RULE_REGISTRY[ruleName]?.title;
|
|
72152
72161
|
const resolveDiagnosticTitle = (plugin, rule) => {
|
|
72153
72162
|
if (plugin !== "react-hooks-js") return getRuleTitle(rule);
|
|
72154
72163
|
if (rule === "error-boundaries") return REACT_ERROR_BOUNDARY_TITLE;
|
|
@@ -72203,7 +72212,7 @@ const resolveDiagnosticCategory = (plugin, rule) => {
|
|
|
72203
72212
|
if (plugin === "react-hooks-js" && rule === "error-boundaries") return "Bugs";
|
|
72204
72213
|
return getRuleCategory(rule) ?? lookupOwnString(PLUGIN_CATEGORY_MAP, plugin) ?? "Bugs";
|
|
72205
72214
|
};
|
|
72206
|
-
const resolveMatchByOccurrence = (rule, category) => OCCURRENCE_MATCHED_CATEGORIES.has(category) || Boolean(
|
|
72215
|
+
const resolveMatchByOccurrence = (rule, category) => OCCURRENCE_MATCHED_CATEGORIES.has(category) || Boolean(REACT_DOCTOR_RULE_REGISTRY[rule]?.matchByOccurrence);
|
|
72207
72216
|
/**
|
|
72208
72217
|
* Maps oxlint's non-primary labels (`labels[1..]`) into related source
|
|
72209
72218
|
* locations. Editors surface these as a diagnostic's
|
|
@@ -72294,7 +72303,7 @@ const parseOxlintOutput = (stdout, project, rootDirectory, sourcePathByLintPath,
|
|
|
72294
72303
|
const preparedSourceMap = resolvePreparedSourceMap(diagnostic.filename);
|
|
72295
72304
|
if (preparedSourceMap === void 0) return [diagnostic];
|
|
72296
72305
|
const { plugin, rule } = parseRuleCode(diagnostic.code);
|
|
72297
|
-
if (plugin !== "react-doctor" || !
|
|
72306
|
+
if (plugin !== "react-doctor" || !REACT_DOCTOR_RULE_REGISTRY[rule]?.tags?.includes("design")) return [];
|
|
72298
72307
|
const labels = mapPreparedSourceLabels(diagnostic.labels, preparedSourceMap);
|
|
72299
72308
|
const sourceFilename = resolveMappedSourceFilename(diagnostic.filename);
|
|
72300
72309
|
return labels === null || sourceFilename === void 0 ? [] : [{
|
|
@@ -72380,11 +72389,12 @@ const SANITIZED_ENV = buildOxlintChildEnv(process.env);
|
|
|
72380
72389
|
* The first three are splittable (the caller's binary-split retry
|
|
72381
72390
|
* shrinks the batch and re-spawns); the fourth isn't.
|
|
72382
72391
|
*/
|
|
72383
|
-
const spawnOxlint = (args, rootDirectory, nodeBinaryPath, spawnTimeoutMs = OXLINT_SPAWN_TIMEOUT_MS, outputMaxBytes = OXLINT_OUTPUT_MAX_BYTES, abortSignal) => new Promise((resolve, reject) => {
|
|
72392
|
+
const spawnOxlint = (args, rootDirectory, nodeBinaryPath, spawnTimeoutMs = OXLINT_SPAWN_TIMEOUT_MS, outputMaxBytes = OXLINT_OUTPUT_MAX_BYTES, abortSignal, onSpawn) => new Promise((resolve, reject) => {
|
|
72384
72393
|
if (abortSignal?.aborted) {
|
|
72385
72394
|
reject(new ReactDoctorError({ reason: new OxlintSpawnFailed({ cause: "lint phase aborted" }) }));
|
|
72386
72395
|
return;
|
|
72387
72396
|
}
|
|
72397
|
+
onSpawn?.();
|
|
72388
72398
|
const child = spawn(nodeBinaryPath, buildProfiledNodeArguments({
|
|
72389
72399
|
argumentsList: args,
|
|
72390
72400
|
cpuProfileDirectory: SANITIZED_ENV.REACT_DOCTOR_CPU_PROFILE_DIR,
|
|
@@ -72544,7 +72554,21 @@ const spawnLintBatches = async (input) => {
|
|
|
72544
72554
|
}
|
|
72545
72555
|
const batchArgs = [...baseArgs, ...batch];
|
|
72546
72556
|
try {
|
|
72547
|
-
|
|
72557
|
+
const spawnBatch = () => {
|
|
72558
|
+
if (isPastDeadline()) {
|
|
72559
|
+
deadlineSkippedFiles.push(...batch);
|
|
72560
|
+
batchState.deadlineSkippedFileCount += batch.length;
|
|
72561
|
+
return Promise.resolve(null);
|
|
72562
|
+
}
|
|
72563
|
+
return spawnOxlint(batchArgs, rootDirectory, nodeBinaryPath, spawnTimeoutMs, outputMaxBytes, signal, () => {
|
|
72564
|
+
if (batchState.didStart) return;
|
|
72565
|
+
batchState.didStart = true;
|
|
72566
|
+
startedFileCount += batchState.initialFileCount;
|
|
72567
|
+
});
|
|
72568
|
+
};
|
|
72569
|
+
const stdout = input.spawnSlots === void 0 ? await spawnBatch() : await input.spawnSlots.run(spawnBatch, signal);
|
|
72570
|
+
if (stdout === null) return [];
|
|
72571
|
+
return parseOxlintOutput(stdout, project, rootDirectory, sourcePathByLintPath, sourceMapByLintPath);
|
|
72548
72572
|
} catch (error) {
|
|
72549
72573
|
if (!isSplittableReactDoctorError(error)) throw error;
|
|
72550
72574
|
if (isPastDeadline()) {
|
|
@@ -72588,14 +72612,15 @@ const spawnLintBatches = async (input) => {
|
|
|
72588
72612
|
deadlineSkippedFiles.push(...batch);
|
|
72589
72613
|
return [];
|
|
72590
72614
|
}
|
|
72591
|
-
startedFileCount += batch.length;
|
|
72592
72615
|
const batchState = {
|
|
72593
72616
|
deadlineMs: null,
|
|
72594
|
-
deadlineSkippedFileCount: 0
|
|
72617
|
+
deadlineSkippedFileCount: 0,
|
|
72618
|
+
didStart: false,
|
|
72619
|
+
initialFileCount: batch.length
|
|
72595
72620
|
};
|
|
72596
72621
|
const batchDiagnostics = await spawnLintBatch(batch, 0, batchState);
|
|
72597
72622
|
scannedFileCount += batch.length - batchState.deadlineSkippedFileCount;
|
|
72598
|
-
if (passOnFileProgress) {
|
|
72623
|
+
if (passOnFileProgress && batchState.didStart) {
|
|
72599
72624
|
displayedFileCount = Math.min(Math.max(displayedFileCount, scannedFileCount), totalFileCount);
|
|
72600
72625
|
passOnFileProgress(displayedFileCount, totalFileCount);
|
|
72601
72626
|
}
|
|
@@ -72679,8 +72704,8 @@ const validateRuleRegistration = () => {
|
|
|
72679
72704
|
for (const fullKey of ALL_REACT_DOCTOR_RULE_KEYS) {
|
|
72680
72705
|
const ruleName = fullKey.replace(/^react-doctor\//, "");
|
|
72681
72706
|
if (!getRuleCategory(ruleName)) missingCategory.push(fullKey);
|
|
72682
|
-
if (!
|
|
72683
|
-
if (FRAMEWORK_SPECIFIC_RULE_KEYS.has(fullKey) && !
|
|
72707
|
+
if (!REACT_DOCTOR_RULE_REGISTRY[ruleName]?.recommendation) missingHelp.push(fullKey);
|
|
72708
|
+
if (FRAMEWORK_SPECIFIC_RULE_KEYS.has(fullKey) && !REACT_DOCTOR_RULE_REGISTRY[ruleName]?.requires) missingMetadata.push(fullKey);
|
|
72684
72709
|
}
|
|
72685
72710
|
if (missingCategory.length === 0 && missingHelp.length === 0 && missingMetadata.length === 0) return;
|
|
72686
72711
|
const detail = [
|
|
@@ -73321,6 +73346,7 @@ const runOxlint = async (options) => {
|
|
|
73321
73346
|
spawnTimeoutMs,
|
|
73322
73347
|
outputMaxBytes,
|
|
73323
73348
|
concurrency: options.concurrency,
|
|
73349
|
+
spawnSlots: options.spawnSlots,
|
|
73324
73350
|
signal: options.signal,
|
|
73325
73351
|
deadlineEpochMs: options.deadlineEpochMs
|
|
73326
73352
|
});
|
|
@@ -73523,6 +73549,7 @@ const runOxlint = async (options) => {
|
|
|
73523
73549
|
spawnTimeoutMs,
|
|
73524
73550
|
outputMaxBytes,
|
|
73525
73551
|
concurrency: options.concurrency,
|
|
73552
|
+
spawnSlots: options.spawnSlots,
|
|
73526
73553
|
signal: options.signal,
|
|
73527
73554
|
deadlineEpochMs: options.deadlineEpochMs
|
|
73528
73555
|
});
|
|
@@ -73617,6 +73644,7 @@ var Linter = class Linter extends Service()("react-doctor/Linter") {
|
|
|
73617
73644
|
const spawnTimeoutMs = yield* OxlintSpawnTimeoutMs;
|
|
73618
73645
|
const outputMaxBytes = yield* OxlintOutputMaxBytes;
|
|
73619
73646
|
const concurrency = yield* OxlintConcurrency;
|
|
73647
|
+
const spawnSlots = yield* OxlintSpawnSlots;
|
|
73620
73648
|
const lintBatchOrdering = yield* LintBatchOrdering;
|
|
73621
73649
|
const perFileLintCacheEnabled = yield* PerFileLintCacheEnabled;
|
|
73622
73650
|
const sidecarLintCacheEnabled = yield* SidecarLintCacheEnabled;
|
|
@@ -73646,6 +73674,7 @@ var Linter = class Linter extends Service()("react-doctor/Linter") {
|
|
|
73646
73674
|
onSidecarStats: input.onSidecarStats,
|
|
73647
73675
|
spawnTimeoutMs,
|
|
73648
73676
|
outputMaxBytes,
|
|
73677
|
+
spawnSlots: spawnSlots ?? void 0,
|
|
73649
73678
|
concurrency,
|
|
73650
73679
|
signal,
|
|
73651
73680
|
lintBatchOrdering,
|
|
@@ -75173,6 +75202,11 @@ const filterSourceFiles = (filePaths) => filePaths.filter(isLintableSourceFile);
|
|
|
75173
75202
|
* other `clear*` helpers.
|
|
75174
75203
|
*/
|
|
75175
75204
|
const clearAutoSuppressionCaches = () => {};
|
|
75205
|
+
const createLintPhaseAbortError = () => new ReactDoctorError({ reason: new OxlintSpawnFailed({ cause: "lint phase aborted" }) });
|
|
75206
|
+
const createOxlintSpawnSlots = (concurrency) => createWorkerSlots({
|
|
75207
|
+
slotCount: resolveScanConcurrency(concurrency),
|
|
75208
|
+
createAbortError: createLintPhaseAbortError
|
|
75209
|
+
});
|
|
75176
75210
|
/**
|
|
75177
75211
|
* Identity helper for authoring a typed `doctor.config.{ts,js,mjs,cjs}`.
|
|
75178
75212
|
* Gives editor autocomplete and type-checking for the config object without
|
|
@@ -75293,7 +75327,7 @@ const buildDiagnoseLayer = (input) => {
|
|
|
75293
75327
|
resolvedDirectory: input.configOverrideTarget.resolvedDirectory,
|
|
75294
75328
|
configSourceDirectory: input.configOverrideTarget.configSourceDirectory
|
|
75295
75329
|
});
|
|
75296
|
-
return mergeAll$1(Project.layerNode, configLayer, input.shouldRunDeadCode ? DeadCode.layerNode : DeadCode.layerOf([]), Files.layerNode, Git.layerNode, input.shouldRunLint ? Linter.layerOxlint : Linter.layerOf([]), LintPartialFailures.layerLive, Progress.layerNoop, Reporter.layerNoop, Score.layerHttp, input.config?.supplyChain?.enabled !== false ? SupplyChain.layerNode : SupplyChain.layerOf([]));
|
|
75330
|
+
return mergeAll$1(Project.layerNode, configLayer, input.shouldRunDeadCode ? DeadCode.layerNode : DeadCode.layerOf([]), Files.layerNode, Git.layerNode, input.shouldRunLint ? Linter.layerOxlint : Linter.layerOf([]), LintPartialFailures.layerLive, succeed$3(OxlintConcurrency, input.oxlintConcurrency), succeed$3(OxlintSpawnSlots, input.oxlintSpawnSlots), Progress.layerNoop, Reporter.layerNoop, Score.layerHttp, input.config?.supplyChain?.enabled !== false ? SupplyChain.layerNode : SupplyChain.layerOf([]));
|
|
75297
75331
|
};
|
|
75298
75332
|
const buildInspectProgram = (scanTarget, options, configOverride) => {
|
|
75299
75333
|
const effectiveConfig = configOverride ?? scanTarget.userConfig;
|
|
@@ -75334,10 +75368,14 @@ const diagnoseDirectory = async (directory, options) => {
|
|
|
75334
75368
|
const program = buildInspectProgram(scanTarget, options);
|
|
75335
75369
|
const shouldRunLint = resolveShouldRunLint(options, scanTarget.userConfig);
|
|
75336
75370
|
const shouldRunDeadCode = resolveShouldRunDeadCode(options, scanTarget.userConfig);
|
|
75371
|
+
const oxlintConcurrency = runSync(OxlintConcurrency);
|
|
75372
|
+
const oxlintSpawnSlots = createOxlintSpawnSlots(oxlintConcurrency);
|
|
75337
75373
|
return outputToDiagnoseResult(await runPromise(restoreLegacyThrow(program.pipe(provide(buildDiagnoseLayer({
|
|
75338
75374
|
config: scanTarget.userConfig,
|
|
75339
75375
|
shouldRunLint,
|
|
75340
|
-
shouldRunDeadCode
|
|
75376
|
+
shouldRunDeadCode,
|
|
75377
|
+
oxlintConcurrency,
|
|
75378
|
+
oxlintSpawnSlots
|
|
75341
75379
|
})), provide(layerOtlp)))), globalThis.performance.now() - startTime);
|
|
75342
75380
|
};
|
|
75343
75381
|
const findWorstScore = (projectResults) => {
|
|
@@ -75352,7 +75390,7 @@ const findWorstScore = (projectResults) => {
|
|
|
75352
75390
|
}
|
|
75353
75391
|
return worstResult;
|
|
75354
75392
|
};
|
|
75355
|
-
const diagnoseProject = async (projectDefinition, baseOptions, batchConfig) => {
|
|
75393
|
+
const diagnoseProject = async (projectDefinition, baseOptions, batchConfig, oxlintConcurrency, oxlintSpawnSlots) => {
|
|
75356
75394
|
const startTime = globalThis.performance.now();
|
|
75357
75395
|
try {
|
|
75358
75396
|
const scanTarget = await resolveScanTarget(projectDefinition.directory);
|
|
@@ -75371,6 +75409,8 @@ const diagnoseProject = async (projectDefinition, baseOptions, batchConfig) => {
|
|
|
75371
75409
|
config: effectiveConfig,
|
|
75372
75410
|
shouldRunLint,
|
|
75373
75411
|
shouldRunDeadCode,
|
|
75412
|
+
oxlintConcurrency,
|
|
75413
|
+
oxlintSpawnSlots,
|
|
75374
75414
|
configOverrideTarget: {
|
|
75375
75415
|
resolvedDirectory: scanTarget.resolvedDirectory,
|
|
75376
75416
|
configSourceDirectory: didOverridePlugins ? null : scanTarget.configSourceDirectory
|
|
@@ -75378,7 +75418,9 @@ const diagnoseProject = async (projectDefinition, baseOptions, batchConfig) => {
|
|
|
75378
75418
|
} : {
|
|
75379
75419
|
config: effectiveConfig,
|
|
75380
75420
|
shouldRunLint,
|
|
75381
|
-
shouldRunDeadCode
|
|
75421
|
+
shouldRunDeadCode,
|
|
75422
|
+
oxlintConcurrency,
|
|
75423
|
+
oxlintSpawnSlots
|
|
75382
75424
|
});
|
|
75383
75425
|
return {
|
|
75384
75426
|
ok: true,
|
|
@@ -75397,7 +75439,9 @@ const diagnoseProjectBatch = async (input) => {
|
|
|
75397
75439
|
warnIfAiTrainingEnvironment();
|
|
75398
75440
|
const startTime = globalThis.performance.now();
|
|
75399
75441
|
const { projects, concurrency, config: batchConfig, ...baseOptions } = input;
|
|
75400
|
-
const
|
|
75442
|
+
const oxlintConcurrency = runSync(OxlintConcurrency);
|
|
75443
|
+
const oxlintSpawnSlots = createOxlintSpawnSlots(oxlintConcurrency);
|
|
75444
|
+
const projectResults = await mapWithConcurrency(projects, concurrency ?? 4, (projectDefinition) => diagnoseProject(projectDefinition, baseOptions, batchConfig, oxlintConcurrency, oxlintSpawnSlots));
|
|
75401
75445
|
const succeededProjects = projectResults.filter((projectResult) => projectResult.ok);
|
|
75402
75446
|
return {
|
|
75403
75447
|
projects: projectResults,
|
|
@@ -75443,4 +75487,4 @@ const toJsonReport = (result, options) => buildJsonReport({
|
|
|
75443
75487
|
export { AmbiguousProjectError, NoReactDependencyError, NotADirectoryError, PackageJsonNotFoundError, ProjectNotFoundError, ReactDoctorError, buildJsonReport, buildJsonReportError, clearCaches, defineConfig, diagnose, filterSourceFiles, getDiffInfo, hasReactRuntime, isProjectDiscoveryError, isReactDoctorError, summarizeDiagnostics, toJsonReport };
|
|
75444
75488
|
|
|
75445
75489
|
//# sourceMappingURL=index.js.map
|
|
75446
|
-
//# debugId=
|
|
75490
|
+
//# debugId=61d9689f-8ba5-5ca3-a0c2-09557d3f7028
|
package/dist/lsp.js
CHANGED
|
@@ -5,7 +5,7 @@ import * as OS from "node:os";
|
|
|
5
5
|
import os from "node:os";
|
|
6
6
|
import * as Path from "node:path";
|
|
7
7
|
import path from "node:path";
|
|
8
|
-
import
|
|
8
|
+
import { ALL_REACT_DOCTOR_RULE_KEYS, CROSS_FILE_DEPENDENCY_COLLECTORS, CROSS_FILE_RULE_IDS, FRAMEWORK_SPECIFIC_RULE_KEYS, FRAMEWORK_TOKENS, MOTION_LIBRARY_PACKAGES, REACT_COMPILER_RULES, REACT_DOCTOR_RULES, REACT_DOCTOR_RULE_REGISTRY, REACT_DOCTOR_SCAN_RULES, classifySecurityScanFile, collectCrossFileDependencyProbes, resetManifestCaches, shouldReadSecurityScanContent } from "oxlint-plugin-react-doctor/core";
|
|
9
9
|
import * as Crypto from "node:crypto";
|
|
10
10
|
import crypto, { createHash } from "node:crypto";
|
|
11
11
|
import * as NodeChildProcess from "node:child_process";
|
|
@@ -65726,7 +65726,7 @@ const evaluateSuppression = (lines, diagnosticLineIndex, ruleId) => {
|
|
|
65726
65726
|
const getDiagnosticRuleIdentity = (diagnostic) => ({
|
|
65727
65727
|
ruleKey: `${diagnostic.plugin}/${diagnostic.rule}`,
|
|
65728
65728
|
category: diagnostic.category,
|
|
65729
|
-
tags: diagnostic.plugin === "react-doctor" ?
|
|
65729
|
+
tags: diagnostic.plugin === "react-doctor" ? REACT_DOCTOR_RULE_REGISTRY[diagnostic.rule]?.tags ?? [] : []
|
|
65730
65730
|
});
|
|
65731
65731
|
const compileIgnoredFilePatterns = (userConfig) => {
|
|
65732
65732
|
const files = userConfig?.ignore?.files;
|
|
@@ -66074,7 +66074,7 @@ const buildDiagnosticPipeline = (input) => {
|
|
|
66074
66074
|
};
|
|
66075
66075
|
const shouldAutoSuppress = (diagnostic) => {
|
|
66076
66076
|
if (diagnostic.plugin !== "react-doctor") return false;
|
|
66077
|
-
const rule =
|
|
66077
|
+
const rule = REACT_DOCTOR_RULE_REGISTRY[diagnostic.rule];
|
|
66078
66078
|
if (!rule?.tags?.includes("test-noise")) return false;
|
|
66079
66079
|
if (rule.tags.includes("migration-hint")) return false;
|
|
66080
66080
|
return getFileContext(diagnostic.filePath) !== "production";
|
|
@@ -66484,6 +66484,7 @@ var OxlintConcurrency = class extends Reference("react-doctor/OxlintConcurrency"
|
|
|
66484
66484
|
if (Number.isInteger(parsed) && parsed > 0) return resolveScanConcurrency(parsed);
|
|
66485
66485
|
return resolveAutoScanConcurrency();
|
|
66486
66486
|
} }) {};
|
|
66487
|
+
var OxlintSpawnSlots = class extends Reference("react-doctor/OxlintSpawnSlots", { defaultValue: () => null }) {};
|
|
66487
66488
|
/**
|
|
66488
66489
|
* Three-state control for overlapping the dead-code pass with the lint pass —
|
|
66489
66490
|
* forking dead-code as a child fiber that runs DURING lint instead of strictly
|
|
@@ -68758,8 +68759,8 @@ const getCapabilities = (project) => {
|
|
|
68758
68759
|
const cached = capabilitiesByProject.get(project);
|
|
68759
68760
|
if (cached !== void 0) return cached;
|
|
68760
68761
|
const capabilities = new Set(buildCapabilities(project));
|
|
68761
|
-
const
|
|
68762
|
-
const targetBlankOpenerProtection = detectTargetBlankOpenerProtection(
|
|
68762
|
+
const capabilityRootDirectory = isFile(Path.join(project.rootDirectory, "package.json")) ? project.rootDirectory : findNearestAncestorPackageJson(project.rootDirectory) ?? project.rootDirectory;
|
|
68763
|
+
const targetBlankOpenerProtection = detectTargetBlankOpenerProtection(capabilityRootDirectory, readPackageJson(Path.join(capabilityRootDirectory, "package.json")));
|
|
68763
68764
|
if (targetBlankOpenerProtection !== void 0) capabilities.add("target-blank-needs-explicit-protection");
|
|
68764
68765
|
if (targetBlankOpenerProtection === "noreferrer") capabilities.add("target-blank-needs-noreferrer");
|
|
68765
68766
|
capabilitiesByProject.set(project, capabilities);
|
|
@@ -68789,7 +68790,7 @@ const createSecurityScanSession = (rootDirectory, options) => {
|
|
|
68789
68790
|
const capabilities = options.project ? getCapabilities(options.project) : /* @__PURE__ */ new Set();
|
|
68790
68791
|
const ignoredTags = options.ignoredTags ?? /* @__PURE__ */ new Set();
|
|
68791
68792
|
const includedTags = options.includedTags ?? /* @__PURE__ */ new Set();
|
|
68792
|
-
const enabledScanRules =
|
|
68793
|
+
const enabledScanRules = REACT_DOCTOR_SCAN_RULES.flatMap((entry) => {
|
|
68793
68794
|
const rule = entry.rule;
|
|
68794
68795
|
const scan = rule.scan;
|
|
68795
68796
|
if (typeof scan !== "function") return [];
|
|
@@ -69234,47 +69235,55 @@ const resolveDeadCodeConcurrency = (facts = readSystemFacts()) => {
|
|
|
69234
69235
|
const memoryBoundedWorkers = Math.floor(availableMemoryBytes / DEAD_CODE_WORKER_MEM_BUDGET_BYTES);
|
|
69235
69236
|
return Math.max(1, Math.min(facts.availableCores, memoryBoundedWorkers));
|
|
69236
69237
|
};
|
|
69237
|
-
|
|
69238
|
-
|
|
69239
|
-
const
|
|
69240
|
-
const
|
|
69241
|
-
|
|
69242
|
-
|
|
69238
|
+
const createWorkerSlots = (input) => {
|
|
69239
|
+
let availableSlotCount = input.slotCount;
|
|
69240
|
+
const waiters = [];
|
|
69241
|
+
const releaseSlot = () => {
|
|
69242
|
+
const nextWaiter = waiters.shift();
|
|
69243
|
+
if (nextWaiter === void 0) {
|
|
69244
|
+
availableSlotCount += 1;
|
|
69245
|
+
return;
|
|
69246
|
+
}
|
|
69247
|
+
nextWaiter.abortSignal?.removeEventListener("abort", nextWaiter.onAbort);
|
|
69248
|
+
nextWaiter.resolve();
|
|
69249
|
+
};
|
|
69250
|
+
const acquireSlot = async (abortSignal) => {
|
|
69251
|
+
if (abortSignal?.aborted) throw input.createAbortError();
|
|
69252
|
+
if (availableSlotCount > 0) {
|
|
69253
|
+
availableSlotCount -= 1;
|
|
69254
|
+
return;
|
|
69255
|
+
}
|
|
69256
|
+
await new Promise((resolve, reject) => {
|
|
69257
|
+
const onAbort = () => {
|
|
69258
|
+
const waiterIndex = waiters.indexOf(waiter);
|
|
69259
|
+
if (waiterIndex !== -1) waiters.splice(waiterIndex, 1);
|
|
69260
|
+
reject(input.createAbortError());
|
|
69261
|
+
};
|
|
69262
|
+
const waiter = {
|
|
69263
|
+
resolve,
|
|
69264
|
+
abortSignal,
|
|
69265
|
+
onAbort
|
|
69266
|
+
};
|
|
69267
|
+
waiters.push(waiter);
|
|
69268
|
+
abortSignal?.addEventListener("abort", onAbort, { once: true });
|
|
69269
|
+
});
|
|
69270
|
+
};
|
|
69271
|
+
return { run: async (task, abortSignal) => {
|
|
69272
|
+
await acquireSlot(abortSignal);
|
|
69273
|
+
try {
|
|
69274
|
+
return await task();
|
|
69275
|
+
} finally {
|
|
69276
|
+
releaseSlot();
|
|
69277
|
+
}
|
|
69278
|
+
} };
|
|
69243
69279
|
};
|
|
69244
|
-
|
|
69245
|
-
* Runs `task` once a dead-code worker slot is free, releasing the slot when the
|
|
69246
|
-
* task settles (success or failure). With a high cap (roomy machine) every
|
|
69247
|
-
* caller proceeds immediately; with a low cap (constrained runner) callers
|
|
69248
|
-
* queue and run as slots free.
|
|
69249
|
-
*
|
|
69250
|
-
* `abortSignal` short-circuits the WAIT: if it's already aborted, or fires while
|
|
69251
|
-
* this caller is queued, the call rejects without acquiring a slot or running
|
|
69252
|
-
* `task` — so a cancelled scan (e.g. lint failed) doesn't sit in the queue and
|
|
69253
|
-
* then spawn a child only to tear it down. A queued caller that aborts removes
|
|
69254
|
-
* its own waiter so a later release never hands a slot to a dead request.
|
|
69255
|
-
*/
|
|
69280
|
+
let deadCodeWorkerSlots = null;
|
|
69256
69281
|
const withDeadCodeWorkerSlot = async (task, abortSignal) => {
|
|
69257
|
-
|
|
69258
|
-
|
|
69259
|
-
|
|
69260
|
-
else await new Promise((resolve, reject) => {
|
|
69261
|
-
const waiter = () => {
|
|
69262
|
-
abortSignal?.removeEventListener("abort", onAbort);
|
|
69263
|
-
resolve();
|
|
69264
|
-
};
|
|
69265
|
-
const onAbort = () => {
|
|
69266
|
-
const queuedIndex = waiters.indexOf(waiter);
|
|
69267
|
-
if (queuedIndex !== -1) waiters.splice(queuedIndex, 1);
|
|
69268
|
-
reject(/* @__PURE__ */ new Error("Dead-code worker aborted."));
|
|
69269
|
-
};
|
|
69270
|
-
waiters.push(waiter);
|
|
69271
|
-
abortSignal?.addEventListener("abort", onAbort, { once: true });
|
|
69282
|
+
deadCodeWorkerSlots ??= createWorkerSlots({
|
|
69283
|
+
slotCount: resolveDeadCodeConcurrency(),
|
|
69284
|
+
createAbortError: () => /* @__PURE__ */ new Error("Dead-code worker aborted.")
|
|
69272
69285
|
});
|
|
69273
|
-
|
|
69274
|
-
return await task();
|
|
69275
|
-
} finally {
|
|
69276
|
-
releaseSlot();
|
|
69277
|
-
}
|
|
69286
|
+
return deadCodeWorkerSlots.run(task, abortSignal);
|
|
69278
69287
|
};
|
|
69279
69288
|
const projectCacheSubdir = (projectDirectory) => crypto.createHash("sha256").update(projectDirectory).digest("hex").slice(0, 16);
|
|
69280
69289
|
const resolveReactDoctorCacheDir = (projectDirectory) => {
|
|
@@ -70801,12 +70810,12 @@ const createOxlintConfig = ({ pluginPath, project, customRulesOnly = false, exte
|
|
|
70801
70810
|
const settingsRootDirectory = resolveSettingsRootDirectory(project.rootDirectory);
|
|
70802
70811
|
const enabledReactDoctorRules = {};
|
|
70803
70812
|
for (const registryEntry of REACT_DOCTOR_RULES) {
|
|
70804
|
-
const rule =
|
|
70813
|
+
const rule = REACT_DOCTOR_RULE_REGISTRY[registryEntry.id];
|
|
70805
70814
|
if (!rule) continue;
|
|
70806
70815
|
if (ruleSelection === "cacheable" && CROSS_FILE_RULE_IDS.has(registryEntry.id)) continue;
|
|
70807
70816
|
if (ruleSelection === "sidecar" && !CROSS_FILE_RULE_IDS.has(registryEntry.id)) continue;
|
|
70808
70817
|
if (ruleSelection === "sidecar" && sidecarRuleIdFilter !== void 0 && !sidecarRuleIdFilter.has(registryEntry.id)) continue;
|
|
70809
|
-
if (rule.
|
|
70818
|
+
if (rule.isScanRule) continue;
|
|
70810
70819
|
if (customRulesOnly && registryEntry.originallyExternal) continue;
|
|
70811
70820
|
if (rule.framework !== "global" && !rule.requires) continue;
|
|
70812
70821
|
if (!shouldEnableRule(rule.requires, rule.tags, capabilities, ignoredTags, rule.disabledWhen, includedTags)) continue;
|
|
@@ -72117,7 +72126,7 @@ const PLUGIN_CATEGORY_MAP = {
|
|
|
72117
72126
|
};
|
|
72118
72127
|
const lookupOwnString = (record, key) => Object.hasOwn(record, key) ? record[key] : void 0;
|
|
72119
72128
|
const getRuleRecommendation = (ruleName, project) => {
|
|
72120
|
-
const rule =
|
|
72129
|
+
const rule = REACT_DOCTOR_RULE_REGISTRY[ruleName];
|
|
72121
72130
|
if (!rule) return void 0;
|
|
72122
72131
|
if (rule.recommendationFor) {
|
|
72123
72132
|
const capabilities = getCapabilities(project);
|
|
@@ -72126,8 +72135,8 @@ const getRuleRecommendation = (ruleName, project) => {
|
|
|
72126
72135
|
}
|
|
72127
72136
|
return rule.recommendation;
|
|
72128
72137
|
};
|
|
72129
|
-
const getRuleCategory = (ruleName) =>
|
|
72130
|
-
const getRuleTitle = (ruleName) =>
|
|
72138
|
+
const getRuleCategory = (ruleName) => REACT_DOCTOR_RULE_REGISTRY[ruleName]?.category;
|
|
72139
|
+
const getRuleTitle = (ruleName) => REACT_DOCTOR_RULE_REGISTRY[ruleName]?.title;
|
|
72131
72140
|
const resolveDiagnosticTitle = (plugin, rule) => {
|
|
72132
72141
|
if (plugin !== "react-hooks-js") return getRuleTitle(rule);
|
|
72133
72142
|
if (rule === "error-boundaries") return REACT_ERROR_BOUNDARY_TITLE;
|
|
@@ -72182,7 +72191,7 @@ const resolveDiagnosticCategory = (plugin, rule) => {
|
|
|
72182
72191
|
if (plugin === "react-hooks-js" && rule === "error-boundaries") return "Bugs";
|
|
72183
72192
|
return getRuleCategory(rule) ?? lookupOwnString(PLUGIN_CATEGORY_MAP, plugin) ?? "Bugs";
|
|
72184
72193
|
};
|
|
72185
|
-
const resolveMatchByOccurrence = (rule, category) => OCCURRENCE_MATCHED_CATEGORIES.has(category) || Boolean(
|
|
72194
|
+
const resolveMatchByOccurrence = (rule, category) => OCCURRENCE_MATCHED_CATEGORIES.has(category) || Boolean(REACT_DOCTOR_RULE_REGISTRY[rule]?.matchByOccurrence);
|
|
72186
72195
|
/**
|
|
72187
72196
|
* Maps oxlint's non-primary labels (`labels[1..]`) into related source
|
|
72188
72197
|
* locations. Editors surface these as a diagnostic's
|
|
@@ -72273,7 +72282,7 @@ const parseOxlintOutput = (stdout, project, rootDirectory, sourcePathByLintPath,
|
|
|
72273
72282
|
const preparedSourceMap = resolvePreparedSourceMap(diagnostic.filename);
|
|
72274
72283
|
if (preparedSourceMap === void 0) return [diagnostic];
|
|
72275
72284
|
const { plugin, rule } = parseRuleCode(diagnostic.code);
|
|
72276
|
-
if (plugin !== "react-doctor" || !
|
|
72285
|
+
if (plugin !== "react-doctor" || !REACT_DOCTOR_RULE_REGISTRY[rule]?.tags?.includes("design")) return [];
|
|
72277
72286
|
const labels = mapPreparedSourceLabels(diagnostic.labels, preparedSourceMap);
|
|
72278
72287
|
const sourceFilename = resolveMappedSourceFilename(diagnostic.filename);
|
|
72279
72288
|
return labels === null || sourceFilename === void 0 ? [] : [{
|
|
@@ -72359,11 +72368,12 @@ const SANITIZED_ENV = buildOxlintChildEnv(process.env);
|
|
|
72359
72368
|
* The first three are splittable (the caller's binary-split retry
|
|
72360
72369
|
* shrinks the batch and re-spawns); the fourth isn't.
|
|
72361
72370
|
*/
|
|
72362
|
-
const spawnOxlint = (args, rootDirectory, nodeBinaryPath, spawnTimeoutMs = OXLINT_SPAWN_TIMEOUT_MS, outputMaxBytes = OXLINT_OUTPUT_MAX_BYTES, abortSignal) => new Promise((resolve, reject) => {
|
|
72371
|
+
const spawnOxlint = (args, rootDirectory, nodeBinaryPath, spawnTimeoutMs = OXLINT_SPAWN_TIMEOUT_MS, outputMaxBytes = OXLINT_OUTPUT_MAX_BYTES, abortSignal, onSpawn) => new Promise((resolve, reject) => {
|
|
72363
72372
|
if (abortSignal?.aborted) {
|
|
72364
72373
|
reject(new ReactDoctorError({ reason: new OxlintSpawnFailed({ cause: "lint phase aborted" }) }));
|
|
72365
72374
|
return;
|
|
72366
72375
|
}
|
|
72376
|
+
onSpawn?.();
|
|
72367
72377
|
const child = spawn(nodeBinaryPath, buildProfiledNodeArguments({
|
|
72368
72378
|
argumentsList: args,
|
|
72369
72379
|
cpuProfileDirectory: SANITIZED_ENV.REACT_DOCTOR_CPU_PROFILE_DIR,
|
|
@@ -72523,7 +72533,21 @@ const spawnLintBatches = async (input) => {
|
|
|
72523
72533
|
}
|
|
72524
72534
|
const batchArgs = [...baseArgs, ...batch];
|
|
72525
72535
|
try {
|
|
72526
|
-
|
|
72536
|
+
const spawnBatch = () => {
|
|
72537
|
+
if (isPastDeadline()) {
|
|
72538
|
+
deadlineSkippedFiles.push(...batch);
|
|
72539
|
+
batchState.deadlineSkippedFileCount += batch.length;
|
|
72540
|
+
return Promise.resolve(null);
|
|
72541
|
+
}
|
|
72542
|
+
return spawnOxlint(batchArgs, rootDirectory, nodeBinaryPath, spawnTimeoutMs, outputMaxBytes, signal, () => {
|
|
72543
|
+
if (batchState.didStart) return;
|
|
72544
|
+
batchState.didStart = true;
|
|
72545
|
+
startedFileCount += batchState.initialFileCount;
|
|
72546
|
+
});
|
|
72547
|
+
};
|
|
72548
|
+
const stdout = input.spawnSlots === void 0 ? await spawnBatch() : await input.spawnSlots.run(spawnBatch, signal);
|
|
72549
|
+
if (stdout === null) return [];
|
|
72550
|
+
return parseOxlintOutput(stdout, project, rootDirectory, sourcePathByLintPath, sourceMapByLintPath);
|
|
72527
72551
|
} catch (error) {
|
|
72528
72552
|
if (!isSplittableReactDoctorError(error)) throw error;
|
|
72529
72553
|
if (isPastDeadline()) {
|
|
@@ -72567,14 +72591,15 @@ const spawnLintBatches = async (input) => {
|
|
|
72567
72591
|
deadlineSkippedFiles.push(...batch);
|
|
72568
72592
|
return [];
|
|
72569
72593
|
}
|
|
72570
|
-
startedFileCount += batch.length;
|
|
72571
72594
|
const batchState = {
|
|
72572
72595
|
deadlineMs: null,
|
|
72573
|
-
deadlineSkippedFileCount: 0
|
|
72596
|
+
deadlineSkippedFileCount: 0,
|
|
72597
|
+
didStart: false,
|
|
72598
|
+
initialFileCount: batch.length
|
|
72574
72599
|
};
|
|
72575
72600
|
const batchDiagnostics = await spawnLintBatch(batch, 0, batchState);
|
|
72576
72601
|
scannedFileCount += batch.length - batchState.deadlineSkippedFileCount;
|
|
72577
|
-
if (passOnFileProgress) {
|
|
72602
|
+
if (passOnFileProgress && batchState.didStart) {
|
|
72578
72603
|
displayedFileCount = Math.min(Math.max(displayedFileCount, scannedFileCount), totalFileCount);
|
|
72579
72604
|
passOnFileProgress(displayedFileCount, totalFileCount);
|
|
72580
72605
|
}
|
|
@@ -72658,8 +72683,8 @@ const validateRuleRegistration = () => {
|
|
|
72658
72683
|
for (const fullKey of ALL_REACT_DOCTOR_RULE_KEYS) {
|
|
72659
72684
|
const ruleName = fullKey.replace(/^react-doctor\//, "");
|
|
72660
72685
|
if (!getRuleCategory(ruleName)) missingCategory.push(fullKey);
|
|
72661
|
-
if (!
|
|
72662
|
-
if (FRAMEWORK_SPECIFIC_RULE_KEYS.has(fullKey) && !
|
|
72686
|
+
if (!REACT_DOCTOR_RULE_REGISTRY[ruleName]?.recommendation) missingHelp.push(fullKey);
|
|
72687
|
+
if (FRAMEWORK_SPECIFIC_RULE_KEYS.has(fullKey) && !REACT_DOCTOR_RULE_REGISTRY[ruleName]?.requires) missingMetadata.push(fullKey);
|
|
72663
72688
|
}
|
|
72664
72689
|
if (missingCategory.length === 0 && missingHelp.length === 0 && missingMetadata.length === 0) return;
|
|
72665
72690
|
const detail = [
|
|
@@ -73300,6 +73325,7 @@ const runOxlint = async (options) => {
|
|
|
73300
73325
|
spawnTimeoutMs,
|
|
73301
73326
|
outputMaxBytes,
|
|
73302
73327
|
concurrency: options.concurrency,
|
|
73328
|
+
spawnSlots: options.spawnSlots,
|
|
73303
73329
|
signal: options.signal,
|
|
73304
73330
|
deadlineEpochMs: options.deadlineEpochMs
|
|
73305
73331
|
});
|
|
@@ -73502,6 +73528,7 @@ const runOxlint = async (options) => {
|
|
|
73502
73528
|
spawnTimeoutMs,
|
|
73503
73529
|
outputMaxBytes,
|
|
73504
73530
|
concurrency: options.concurrency,
|
|
73531
|
+
spawnSlots: options.spawnSlots,
|
|
73505
73532
|
signal: options.signal,
|
|
73506
73533
|
deadlineEpochMs: options.deadlineEpochMs
|
|
73507
73534
|
});
|
|
@@ -73596,6 +73623,7 @@ var Linter = class Linter extends Service()("react-doctor/Linter") {
|
|
|
73596
73623
|
const spawnTimeoutMs = yield* OxlintSpawnTimeoutMs;
|
|
73597
73624
|
const outputMaxBytes = yield* OxlintOutputMaxBytes;
|
|
73598
73625
|
const concurrency = yield* OxlintConcurrency;
|
|
73626
|
+
const spawnSlots = yield* OxlintSpawnSlots;
|
|
73599
73627
|
const lintBatchOrdering = yield* LintBatchOrdering;
|
|
73600
73628
|
const perFileLintCacheEnabled = yield* PerFileLintCacheEnabled;
|
|
73601
73629
|
const sidecarLintCacheEnabled = yield* SidecarLintCacheEnabled;
|
|
@@ -73625,6 +73653,7 @@ var Linter = class Linter extends Service()("react-doctor/Linter") {
|
|
|
73625
73653
|
onSidecarStats: input.onSidecarStats,
|
|
73626
73654
|
spawnTimeoutMs,
|
|
73627
73655
|
outputMaxBytes,
|
|
73656
|
+
spawnSlots: spawnSlots ?? void 0,
|
|
73628
73657
|
concurrency,
|
|
73629
73658
|
signal,
|
|
73630
73659
|
lintBatchOrdering,
|
|
@@ -74995,7 +75024,7 @@ const runEditorScan = async (input) => {
|
|
|
74995
75024
|
* other `clear*` helpers.
|
|
74996
75025
|
*/
|
|
74997
75026
|
const clearAutoSuppressionCaches = () => {};
|
|
74998
|
-
const lookupOwnRule = (rule) => Object.hasOwn(
|
|
75027
|
+
const lookupOwnRule = (rule) => Object.hasOwn(REACT_DOCTOR_RULE_REGISTRY, rule) ? REACT_DOCTOR_RULE_REGISTRY[rule] : void 0;
|
|
74999
75028
|
/**
|
|
75000
75029
|
* Returns presentation metadata for `<plugin>/<rule>`, or `null` when the
|
|
75001
75030
|
* rule is not part of the bundled react-doctor plugin (e.g. an adopted
|
|
@@ -77071,5 +77100,5 @@ const startLanguageServer = () => {
|
|
|
77071
77100
|
};
|
|
77072
77101
|
//#endregion
|
|
77073
77102
|
export { startLanguageServer };
|
|
77074
|
-
!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]="
|
|
77075
|
-
//# debugId=
|
|
77103
|
+
!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]="ec49e114-f0f3-593d-b3c1-8b1a04d065a6")}catch(e){}}();
|
|
77104
|
+
//# debugId=ec49e114-f0f3-593d-b3c1-8b1a04d065a6
|
|
@@ -1,6 +1,6 @@
|
|
|
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]="
|
|
3
|
-
import { A as toForwardSlashes, B as getCategoryImpact, C as buildRulePriorityMap, D as resolveBlockingLevel, E as formatLearnMoreLine, F as TUI_REPORT_DETAIL_WIDTH_FRACTION, G as runPromise, H as mapWithConcurrency, I as isCiEnvironment, J as succeed, K as sync, L as DIAGNOSTIC_CATEGORY_BUCKETS, M as isUnicodeSupported, N as METRIC, O as isReactDoctorWorkflowInstalled, P as TUI_REPORT_COMPACT_MAX_ROWS, R as Progress, S as hasLintHardFailure, T as formatFixRecipeLine, U as mergeReactDoctorConfigs, V as highlighter, W as resolveScanTarget, _ as canAnimateOnboarding, a as resolveCliInspectOptions, b as buildCodeFrame, c as detectLaunchableAgents, d as launchCliAgent, f as isShareOptedOut, g as doctorFace, h as easeOutCubic, i as selectProjects, j as findNearestPackageDirectory, k as recordCount, l as CLI_AGENT_BINARIES, m as printFooter, n as shouldFailScanGate, o as countUniqueScannedFiles, p as
|
|
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]="898bdb3e-afed-5fca-92a2-f14d1e8ca6fb")}catch(e){}}();
|
|
3
|
+
import { A as toForwardSlashes, B as getCategoryImpact, C as buildRulePriorityMap, D as resolveBlockingLevel, E as formatLearnMoreLine, F as TUI_REPORT_DETAIL_WIDTH_FRACTION, G as runPromise, H as mapWithConcurrency, I as isCiEnvironment, J as succeed, K as sync, L as DIAGNOSTIC_CATEGORY_BUCKETS, M as isUnicodeSupported, N as METRIC, O as isReactDoctorWorkflowInstalled, P as TUI_REPORT_COMPACT_MAX_ROWS, R as Progress, S as hasLintHardFailure, T as formatFixRecipeLine, U as mergeReactDoctorConfigs, V as highlighter, W as resolveScanTarget, _ as canAnimateOnboarding, a as resolveCliInspectOptions, b as buildCodeFrame, c as detectLaunchableAgents, d as launchCliAgent, f as isShareOptedOut, g as doctorFace, h as easeOutCubic, i as selectProjects, j as findNearestPackageDirectory, k as recordCount, l as CLI_AGENT_BINARIES, m as printFooter, n as shouldFailScanGate, o as countUniqueScannedFiles, p as createInvocationInspect, q as void_, r as discoverWorkspacePackages, s as setUpGitHubActions, t as exitGracefully, u as copyToClipboard, v as computeProjectedScore, w as buildSortedRuleGroups, x as buildNoScoreMessage, y as formatElapsedTime, z as Reporter } from "./cli.js";
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import { performance } from "node:perf_hooks";
|
|
6
6
|
import { getSkillAgentConfig } from "agent-install";
|
|
@@ -1610,11 +1610,11 @@ const runMountedScan = async (rootDirectory, presentation, blockingLevel, execut
|
|
|
1610
1610
|
throw error;
|
|
1611
1611
|
}
|
|
1612
1612
|
};
|
|
1613
|
-
const runSingleProjectScan = async (rootScanTarget, projectDirectory, input, blockingLevel) => {
|
|
1613
|
+
const runSingleProjectScan = async (rootScanTarget, projectDirectory, input, blockingLevel, inspectProject) => {
|
|
1614
1614
|
const projectScan = await resolveProjectScan(rootScanTarget, projectDirectory);
|
|
1615
1615
|
const presentation = resolveScanPresentation(input, [projectScan]);
|
|
1616
1616
|
return runMountedScan(projectScan.directory, presentation, blockingLevel, async (context) => {
|
|
1617
|
-
const result = await
|
|
1617
|
+
const result = await inspectProject(projectScan.directory, {
|
|
1618
1618
|
...resolveTuiInspectOptions(input, projectScan.config),
|
|
1619
1619
|
isCi: isCiEnvironment(),
|
|
1620
1620
|
configOverride: projectScan.config,
|
|
@@ -1645,7 +1645,7 @@ const runSingleProjectScan = async (rootScanTarget, projectDirectory, input, blo
|
|
|
1645
1645
|
};
|
|
1646
1646
|
});
|
|
1647
1647
|
};
|
|
1648
|
-
const runMultiProjectScan = async (rootScanTarget, directories, input, blockingLevel) => {
|
|
1648
|
+
const runMultiProjectScan = async (rootScanTarget, directories, input, blockingLevel, inspectProject) => {
|
|
1649
1649
|
const rootDirectory = rootScanTarget.resolvedDirectory;
|
|
1650
1650
|
const projectScans = await mapWithConcurrency([...directories], 4, (projectDirectory) => resolveProjectScan(rootScanTarget, projectDirectory));
|
|
1651
1651
|
return runMountedScan(rootDirectory, resolveScanPresentation(input, projectScans), blockingLevel, async (context) => {
|
|
@@ -1653,7 +1653,7 @@ const runMultiProjectScan = async (rootScanTarget, directories, input, blockingL
|
|
|
1653
1653
|
let finishedCount = 0;
|
|
1654
1654
|
context.store.setProgress(`Scanning ${directories.length} projects…`);
|
|
1655
1655
|
const results = await mapWithConcurrency(projectScans, 4, async (projectScan) => {
|
|
1656
|
-
const result = await
|
|
1656
|
+
const result = await inspectProject(projectScan.directory, {
|
|
1657
1657
|
...resolveTuiInspectOptions(input, projectScan.config),
|
|
1658
1658
|
isCi: isCiEnvironment(),
|
|
1659
1659
|
configOverride: projectScan.config,
|
|
@@ -1720,13 +1720,14 @@ const runScanApp = async (input) => {
|
|
|
1720
1720
|
share: input.share ?? scanTarget.userConfig?.share ?? true
|
|
1721
1721
|
};
|
|
1722
1722
|
const selectedDirectories = await resolveSelectedDirectories(rootDirectory, resolvedInput);
|
|
1723
|
+
const inspectProject = createInvocationInspect(input.options?.concurrency);
|
|
1723
1724
|
const blockingLevel = resolveBlockingLevel({ blocking: resolvedInput.blocking }, scanTarget.userConfig);
|
|
1724
1725
|
if (selectedDirectories.length === 0) return { shouldFail: false };
|
|
1725
|
-
if (selectedDirectories.length === 1) return runSingleProjectScan(scanTarget, selectedDirectories[0], resolvedInput, blockingLevel);
|
|
1726
|
-
return runMultiProjectScan(scanTarget, selectedDirectories, resolvedInput, blockingLevel);
|
|
1726
|
+
if (selectedDirectories.length === 1) return runSingleProjectScan(scanTarget, selectedDirectories[0], resolvedInput, blockingLevel, inspectProject);
|
|
1727
|
+
return runMultiProjectScan(scanTarget, selectedDirectories, resolvedInput, blockingLevel, inspectProject);
|
|
1727
1728
|
};
|
|
1728
1729
|
//#endregion
|
|
1729
1730
|
export { runScanApp };
|
|
1730
1731
|
|
|
1731
|
-
//# sourceMappingURL=run-scan-app-
|
|
1732
|
-
//# debugId=
|
|
1732
|
+
//# sourceMappingURL=run-scan-app-DHRvqfkX.js.map
|
|
1733
|
+
//# debugId=898bdb3e-afed-5fca-92a2-f14d1e8ca6fb
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-doctor",
|
|
3
|
-
"version": "0.9.2-dev.
|
|
3
|
+
"version": "0.9.2-dev.992205a",
|
|
4
4
|
"description": "Your agent writes bad React. This catches it",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"accessibility",
|
|
@@ -71,8 +71,8 @@
|
|
|
71
71
|
"vscode-languageserver-textdocument": "^1.0.12",
|
|
72
72
|
"vscode-uri": "^3.1.0",
|
|
73
73
|
"yaml": "^2.9.0",
|
|
74
|
-
"
|
|
75
|
-
"
|
|
74
|
+
"deslop-js": "0.9.2",
|
|
75
|
+
"oxlint-plugin-react-doctor": "0.9.2-dev.992205a"
|
|
76
76
|
},
|
|
77
77
|
"devDependencies": {
|
|
78
78
|
"@types/babel__code-frame": "^7.27.0",
|
|
@@ -82,9 +82,9 @@
|
|
|
82
82
|
"commander": "^14.0.3",
|
|
83
83
|
"ink-testing-library": "^4.0.0",
|
|
84
84
|
"ora": "^9.4.0",
|
|
85
|
-
"@react-doctor/
|
|
85
|
+
"@react-doctor/language-server": "0.9.2",
|
|
86
86
|
"@react-doctor/core": "0.9.2",
|
|
87
|
-
"@react-doctor/
|
|
87
|
+
"@react-doctor/api": "0.9.2"
|
|
88
88
|
},
|
|
89
89
|
"engines": {
|
|
90
90
|
"node": "^20.19.0 || >=22.13.0"
|