react-doctor 0.5.8-dev.c16e8ea → 0.5.8-dev.c2af308
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/README.md +9 -3
- package/dist/cli.js +8966 -1325
- package/dist/index.js +139 -82
- package/dist/lsp.js +144 -82
- package/package.json +6 -5
package/dist/lsp.js
CHANGED
|
@@ -6,7 +6,7 @@ import os from "node:os";
|
|
|
6
6
|
import * as Path from "node:path";
|
|
7
7
|
import path from "node:path";
|
|
8
8
|
import * as NodeChildProcess from "node:child_process";
|
|
9
|
-
import { spawn, spawnSync } from "node:child_process";
|
|
9
|
+
import { execFileSync, spawn, spawnSync } from "node:child_process";
|
|
10
10
|
import * as ts from "typescript";
|
|
11
11
|
import reactDoctorPlugin, { ALL_REACT_DOCTOR_RULE_KEYS, CROSS_FILE_RULE_IDS, FRAMEWORK_SPECIFIC_RULE_KEYS, MOTION_LIBRARY_PACKAGES, REACT_COMPILER_RULES, REACT_DOCTOR_RULES, classifySecurityScanFile, shouldReadSecurityScanContent } from "oxlint-plugin-react-doctor";
|
|
12
12
|
import { parseJSON5 } from "confbox";
|
|
@@ -33731,6 +33731,9 @@ const SCORE_API_URL = "https://www.react.doctor/api/score";
|
|
|
33731
33731
|
const FETCH_TIMEOUT_MS = 1e4;
|
|
33732
33732
|
const GITHUB_VIEWER_PERMISSION_TIMEOUT_MS = 2e3;
|
|
33733
33733
|
const SPAWN_ARGS_MAX_LENGTH_CHARS = 24e3;
|
|
33734
|
+
const SPAWN_ARGS_MAX_LENGTH_CHARS_POSIX = 15e5;
|
|
33735
|
+
const SPAWN_ARGS_MAX_LENGTH_CHARS_DARWIN = 8e5;
|
|
33736
|
+
const NODE_VERSION_PROBE_TIMEOUT_MS = 5e3;
|
|
33734
33737
|
const PER_WORKER_MEM_BUDGET_BYTES = 1024 * 1024 * 1024;
|
|
33735
33738
|
const DEFAULT_BRANCH_CANDIDATES = ["main", "master"];
|
|
33736
33739
|
const ADOPTABLE_LINT_CONFIG_FILENAMES = [".oxlintrc.json", ".eslintrc.json"];
|
|
@@ -34578,7 +34581,7 @@ const tokenizeRuleList = (ruleList) => {
|
|
|
34578
34581
|
const FOREIGN_INLINE_DISABLE_PATTERN = /(?:\/\/|\/\*)[ \t]*(eslint|oxlint)-disable-(next-line|line)(?![\w-])([^\r\n]*)/;
|
|
34579
34582
|
const FOREIGN_BLOCK_DISABLE_PATTERN = /\/\*[ \t]*(eslint|oxlint)-disable(?![\w-])([^*\r\n]*)/;
|
|
34580
34583
|
const FOREIGN_BLOCK_ENABLE_PATTERN = /\/\*[ \t]*(?:eslint|oxlint)-enable(?![\w-])([^*\r\n]*)/;
|
|
34581
|
-
const buildHint = (tool, token, ruleId) => `oxlint matches plugin rules only by their full name, so \`${token}\` in your ${tool}-disable comment does not silence \`${ruleId}\` —
|
|
34584
|
+
const buildHint = (tool, token, ruleId) => `oxlint matches plugin rules only by their full name, so \`${token}\` in your ${tool}-disable comment does not silence \`${ruleId}\` — add \`${ruleId}\` to the comment (e.g. \`${token}, ${ruleId}\`).`;
|
|
34582
34585
|
const tokenMisnamesRule = (token, ruleId) => token !== ruleId && isSameRuleKey(token, ruleId);
|
|
34583
34586
|
const detectInlineNearMiss = (lines, diagnosticLineIndex, ruleId) => {
|
|
34584
34587
|
const candidates = [{
|
|
@@ -35095,6 +35098,21 @@ const buildDiagnosticPipeline = (input) => {
|
|
|
35095
35098
|
const fileLinesCache = /* @__PURE__ */ new Map();
|
|
35096
35099
|
const fileContextCache = /* @__PURE__ */ new Map();
|
|
35097
35100
|
const libraryFileCache = /* @__PURE__ */ new Map();
|
|
35101
|
+
const suppressions = /* @__PURE__ */ new Map();
|
|
35102
|
+
const suppress = (diagnostic, source) => {
|
|
35103
|
+
const { ruleKey } = getDiagnosticRuleIdentity(diagnostic);
|
|
35104
|
+
const suppressionKey = `${ruleKey}\u0000${source}`;
|
|
35105
|
+
const existing = suppressions.get(suppressionKey);
|
|
35106
|
+
suppressions.set(suppressionKey, existing ? {
|
|
35107
|
+
...existing,
|
|
35108
|
+
count: existing.count + 1
|
|
35109
|
+
} : {
|
|
35110
|
+
rule: ruleKey,
|
|
35111
|
+
source,
|
|
35112
|
+
count: 1
|
|
35113
|
+
});
|
|
35114
|
+
return null;
|
|
35115
|
+
};
|
|
35098
35116
|
const isLibraryFile = (filePath) => {
|
|
35099
35117
|
let cached = libraryFileCache.get(filePath);
|
|
35100
35118
|
if (cached === void 0) {
|
|
@@ -35149,51 +35167,54 @@ const buildDiagnosticPipeline = (input) => {
|
|
|
35149
35167
|
const identifierMatch = JSX_NO_UNDEF_IDENTIFIER_PATTERN.exec(diagnostic.message);
|
|
35150
35168
|
return identifierMatch !== null && runtimeGlobalNames.has(identifierMatch[1]);
|
|
35151
35169
|
};
|
|
35152
|
-
return {
|
|
35153
|
-
|
|
35154
|
-
|
|
35155
|
-
|
|
35156
|
-
|
|
35157
|
-
|
|
35158
|
-
|
|
35159
|
-
|
|
35160
|
-
|
|
35161
|
-
|
|
35162
|
-
|
|
35163
|
-
|
|
35164
|
-
|
|
35165
|
-
|
|
35166
|
-
|
|
35167
|
-
if (explicitRuleOverride === void 0) {
|
|
35168
|
-
if (isAppOnlyRule(`${current.plugin}/${current.rule}`) && isLibraryFile(current.filePath)) return null;
|
|
35169
|
-
}
|
|
35170
|
-
if (!showWarnings && current.severity === "warning" && explicitSeverityOverride !== "warn") return null;
|
|
35171
|
-
if (userConfig) {
|
|
35172
|
-
if (isRuleIgnored(`${current.plugin}/${current.rule}`)) return null;
|
|
35173
|
-
if (isFileIgnoredByPatterns(current.filePath, rootDirectory, ignoredFilePatterns)) return null;
|
|
35174
|
-
if (isDiagnosticIgnoredByOverrides(current, rootDirectory, compiledOverrides)) return null;
|
|
35175
|
-
if (isRnRawTextSuppressedByConfig(current)) return null;
|
|
35176
|
-
if (isJsxNoUndefSuppressedByConfig(current)) return null;
|
|
35177
|
-
}
|
|
35178
|
-
if (respectInlineDisables && current.line > 0) {
|
|
35179
|
-
const lines = getFileLines(current.filePath);
|
|
35180
|
-
if (lines) {
|
|
35181
|
-
const ruleIdentifier = `${current.plugin}/${current.rule}`;
|
|
35182
|
-
const evaluation = evaluateSuppression(lines, current.line - 1, ruleIdentifier);
|
|
35183
|
-
if (evaluation.isSuppressed) return null;
|
|
35184
|
-
if (evaluation.nearMissHint) current = {
|
|
35185
|
-
...current,
|
|
35186
|
-
suppressionHint: evaluation.nearMissHint
|
|
35187
|
-
};
|
|
35170
|
+
return {
|
|
35171
|
+
apply: (diagnostic) => {
|
|
35172
|
+
if (shouldAutoSuppress(diagnostic)) return null;
|
|
35173
|
+
let current = diagnostic;
|
|
35174
|
+
let explicitSeverityOverride;
|
|
35175
|
+
let explicitRuleOverride;
|
|
35176
|
+
if (severityControls) {
|
|
35177
|
+
const { ruleKey, category } = getDiagnosticRuleIdentity(current);
|
|
35178
|
+
explicitRuleOverride = resolveRuleSeverityOverride({ ruleKey }, severityControls);
|
|
35179
|
+
explicitSeverityOverride = resolveRuleSeverityOverride({
|
|
35180
|
+
ruleKey,
|
|
35181
|
+
category
|
|
35182
|
+
}, severityControls);
|
|
35183
|
+
if (explicitSeverityOverride === "off") return suppress(current, "config");
|
|
35184
|
+
if (explicitSeverityOverride !== void 0) current = restampSeverity(current, explicitSeverityOverride);
|
|
35188
35185
|
}
|
|
35189
|
-
|
|
35190
|
-
|
|
35191
|
-
|
|
35192
|
-
|
|
35193
|
-
|
|
35194
|
-
|
|
35195
|
-
|
|
35196
|
-
|
|
35186
|
+
if (explicitRuleOverride === void 0) {
|
|
35187
|
+
if (isAppOnlyRule(`${current.plugin}/${current.rule}`) && isLibraryFile(current.filePath)) return null;
|
|
35188
|
+
}
|
|
35189
|
+
if (!showWarnings && current.severity === "warning" && explicitSeverityOverride !== "warn") return null;
|
|
35190
|
+
if (userConfig) {
|
|
35191
|
+
if (isRuleIgnored(`${current.plugin}/${current.rule}`)) return suppress(current, "config");
|
|
35192
|
+
if (isFileIgnoredByPatterns(current.filePath, rootDirectory, ignoredFilePatterns)) return null;
|
|
35193
|
+
if (isDiagnosticIgnoredByOverrides(current, rootDirectory, compiledOverrides)) return suppress(current, "override");
|
|
35194
|
+
if (isRnRawTextSuppressedByConfig(current)) return null;
|
|
35195
|
+
if (isJsxNoUndefSuppressedByConfig(current)) return null;
|
|
35196
|
+
}
|
|
35197
|
+
if (respectInlineDisables && current.line > 0) {
|
|
35198
|
+
const lines = getFileLines(current.filePath);
|
|
35199
|
+
if (lines) {
|
|
35200
|
+
const ruleIdentifier = `${current.plugin}/${current.rule}`;
|
|
35201
|
+
const evaluation = evaluateSuppression(lines, current.line - 1, ruleIdentifier);
|
|
35202
|
+
if (evaluation.isSuppressed) return suppress(current, "inline");
|
|
35203
|
+
if (evaluation.nearMissHint) current = {
|
|
35204
|
+
...current,
|
|
35205
|
+
suppressionHint: evaluation.nearMissHint
|
|
35206
|
+
};
|
|
35207
|
+
}
|
|
35208
|
+
}
|
|
35209
|
+
const fileContext = getFileContext(current.filePath);
|
|
35210
|
+
if (fileContext !== "production") current = {
|
|
35211
|
+
...current,
|
|
35212
|
+
fileContext
|
|
35213
|
+
};
|
|
35214
|
+
return current;
|
|
35215
|
+
},
|
|
35216
|
+
summarizeSuppressions: () => [...suppressions.values()]
|
|
35217
|
+
};
|
|
35197
35218
|
};
|
|
35198
35219
|
const OxlintUnavailableKind = Literals(["binary-not-found", "native-binding-missing"]);
|
|
35199
35220
|
var OxlintUnavailable = class extends TaggedErrorClass()("OxlintUnavailable", {
|
|
@@ -35475,13 +35496,7 @@ var ScanDeadlineMs = class extends Reference("react-doctor/ScanDeadlineMs", { de
|
|
|
35475
35496
|
* without recompiling react-doctor. Tests override via
|
|
35476
35497
|
* `Layer.succeed(SupplyChainOverlapTimeoutMs, ...)`.
|
|
35477
35498
|
*/
|
|
35478
|
-
var SupplyChainOverlapTimeoutMs = class extends Reference("react-doctor/SupplyChainOverlapTimeoutMs", { defaultValue: () => {
|
|
35479
|
-
const raw = process.env["REACT_DOCTOR_SUPPLY_CHAIN_TIMEOUT_MS"];
|
|
35480
|
-
if (raw === void 0) return SUPPLY_CHAIN_OVERLAP_TIMEOUT_MS;
|
|
35481
|
-
const parsed = Number(raw);
|
|
35482
|
-
if (!Number.isFinite(parsed) || parsed <= 0) return SUPPLY_CHAIN_OVERLAP_TIMEOUT_MS;
|
|
35483
|
-
return parsed;
|
|
35484
|
-
} }) {};
|
|
35499
|
+
var SupplyChainOverlapTimeoutMs = class extends Reference("react-doctor/SupplyChainOverlapTimeoutMs", { defaultValue: () => readPositiveEnvMs("REACT_DOCTOR_SUPPLY_CHAIN_TIMEOUT_MS", SUPPLY_CHAIN_OVERLAP_TIMEOUT_MS) }) {};
|
|
35485
35500
|
/**
|
|
35486
35501
|
* Hard cap on combined stdout+stderr bytes per oxlint batch. The
|
|
35487
35502
|
* subprocess gets SIGKILL'd if it produces more; the recovery path
|
|
@@ -37775,7 +37790,6 @@ var DeadCode = class DeadCode extends Service()("react-doctor/DeadCode") {
|
|
|
37775
37790
|
return yield* tryPromise({
|
|
37776
37791
|
try: (signal) => checkDeadCode({
|
|
37777
37792
|
rootDirectory: input.rootDirectory,
|
|
37778
|
-
userConfig: input.userConfig,
|
|
37779
37793
|
parseConcurrency: input.parseConcurrency,
|
|
37780
37794
|
workerTimeoutMs: input.workerTimeoutMs,
|
|
37781
37795
|
abortSignal: signal
|
|
@@ -37898,6 +37912,11 @@ const isSafeGitRevision = (candidate) => {
|
|
|
37898
37912
|
if (candidate.includes("..") || candidate.includes("@{")) return false;
|
|
37899
37913
|
return SAFE_GIT_REVISION_PATTERN.test(candidate);
|
|
37900
37914
|
};
|
|
37915
|
+
const resolveSpawnArgsLengthCap = () => {
|
|
37916
|
+
if (process.platform === "win32") return SPAWN_ARGS_MAX_LENGTH_CHARS;
|
|
37917
|
+
if (process.platform === "darwin") return SPAWN_ARGS_MAX_LENGTH_CHARS_DARWIN;
|
|
37918
|
+
return SPAWN_ARGS_MAX_LENGTH_CHARS_POSIX;
|
|
37919
|
+
};
|
|
37901
37920
|
/**
|
|
37902
37921
|
* Splits a git revision range into its endpoints: three-dot `A...B`
|
|
37903
37922
|
* (symmetric, merge-base) or two-dot `A..B` (direct). Returns `null`
|
|
@@ -37982,7 +38001,8 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
37982
38001
|
return scoped(gen(function* () {
|
|
37983
38002
|
if (!isDirectory(input.directory)) return yield* foldSpawnFailure(`spawn ENOTDIR (cwd is not a directory: ${input.directory})`);
|
|
37984
38003
|
const argvLengthChars = input.command.length + 1 + input.args.reduce((total, arg) => total + arg.length + 1, 0);
|
|
37985
|
-
|
|
38004
|
+
const spawnArgsLengthCap = resolveSpawnArgsLengthCap();
|
|
38005
|
+
if (argvLengthChars > spawnArgsLengthCap) return yield* foldSpawnFailure(`spawn ENAMETOOLONG (${argvLengthChars} argv chars exceed ${spawnArgsLengthCap})`);
|
|
37986
38006
|
const handle = yield* spawner.spawn(make$1(input.command, [...input.args], {
|
|
37987
38007
|
cwd: input.directory,
|
|
37988
38008
|
env: input.env,
|
|
@@ -38151,6 +38171,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
38151
38171
|
if (resolvedCurrentBranch === null && explicitBaseBranch === void 0) return null;
|
|
38152
38172
|
const baseBranch = explicitBaseBranch ?? (yield* defaultBranch(directory));
|
|
38153
38173
|
if (baseBranch === null) return null;
|
|
38174
|
+
if (!isSafeGitRevision(baseBranch)) return null;
|
|
38154
38175
|
if (explicitBaseBranch !== void 0) {
|
|
38155
38176
|
if (!(yield* branchExists(directory, explicitBaseBranch))) return yield* fail$4(new ReactDoctorError({ reason: new GitBaseBranchMissing({ branch: explicitBaseBranch }) }));
|
|
38156
38177
|
}
|
|
@@ -38501,7 +38522,7 @@ const normalizeConfigForHash = (config) => {
|
|
|
38501
38522
|
if (Array.isArray(clone?.jsPlugins)) clone.jsPlugins = clone.jsPlugins.map((_, index) => `<plugin:${index}>`);
|
|
38502
38523
|
return clone;
|
|
38503
38524
|
};
|
|
38504
|
-
const computeRulesetHash = (input) => crypto.createHash("sha1").update(JSON.stringify(normalizeConfigForHash(input.config))).update("\0").update([...input.toolchainVersions].join("\0")).update("\0").update([...input.ignorePatterns].join("\n")).update("
|
|
38525
|
+
const computeRulesetHash = (input) => crypto.createHash("sha1").update(JSON.stringify(normalizeConfigForHash(input.config))).update("\0").update([...input.toolchainVersions].join("\0")).update("\0").update([...input.ignorePatterns].join("\n")).update("\0").update(input.tsconfigContent ?? "").digest("hex");
|
|
38505
38526
|
/**
|
|
38506
38527
|
* Loads a plugin module via the local require resolver and extracts
|
|
38507
38528
|
* `(name, ruleNames)` from either `module.exports.meta + rules` or
|
|
@@ -38779,24 +38800,38 @@ const createFileLintCache = (cacheDirectory, rulesetHash) => {
|
|
|
38779
38800
|
updatedAtMs: Date.now(),
|
|
38780
38801
|
files: Object.fromEntries(cappedEntries)
|
|
38781
38802
|
};
|
|
38782
|
-
const keptHashes = Object.entries(rulesets).sort(([, first], [, second]) => second.updatedAtMs - first.updatedAtMs).slice(0, 8).map(([hash]) => hash);
|
|
38783
|
-
const prunedRulesets = {};
|
|
38784
|
-
for (const hash of keptHashes) prunedRulesets[hash] = rulesets[hash];
|
|
38785
38803
|
atomicWriteJson(cacheFilePath, {
|
|
38786
38804
|
version: 1,
|
|
38787
|
-
rulesets:
|
|
38805
|
+
rulesets: Object.fromEntries(Object.entries(rulesets).filter(([, bucket]) => isRecord(bucket) && typeof bucket.updatedAtMs === "number").sort(([, first], [, second]) => second.updatedAtMs - first.updatedAtMs).slice(0, 8))
|
|
38788
38806
|
});
|
|
38789
38807
|
}
|
|
38790
38808
|
};
|
|
38791
38809
|
};
|
|
38792
38810
|
const bundledRequire = createRequire(import.meta.url);
|
|
38811
|
+
const childNodeVersionByBinaryPath = /* @__PURE__ */ new Map();
|
|
38812
|
+
const resolveChildNodeVersion = (nodeBinaryPath) => {
|
|
38813
|
+
if (nodeBinaryPath === process.execPath) return process.version;
|
|
38814
|
+
const cachedVersion = childNodeVersionByBinaryPath.get(nodeBinaryPath);
|
|
38815
|
+
if (cachedVersion !== void 0) return cachedVersion;
|
|
38816
|
+
let version;
|
|
38817
|
+
try {
|
|
38818
|
+
version = execFileSync(nodeBinaryPath, ["--version"], {
|
|
38819
|
+
encoding: "utf8",
|
|
38820
|
+
timeout: NODE_VERSION_PROBE_TIMEOUT_MS
|
|
38821
|
+
}).trim();
|
|
38822
|
+
} catch {
|
|
38823
|
+
version = `unknown:${nodeBinaryPath}`;
|
|
38824
|
+
}
|
|
38825
|
+
childNodeVersionByBinaryPath.set(nodeBinaryPath, version);
|
|
38826
|
+
return version;
|
|
38827
|
+
};
|
|
38793
38828
|
const TOOLCHAIN_PACKAGE_SPECIFIERS = [
|
|
38794
38829
|
"oxlint/package.json",
|
|
38795
38830
|
"oxlint-plugin-react-doctor/package.json",
|
|
38796
38831
|
"eslint-plugin-react-hooks/package.json"
|
|
38797
38832
|
];
|
|
38798
|
-
const resolveOxlintToolchainVersions = () => {
|
|
38799
|
-
const versions = [`node=${
|
|
38833
|
+
const resolveOxlintToolchainVersions = (nodeBinaryPath = process.execPath) => {
|
|
38834
|
+
const versions = [`node=${resolveChildNodeVersion(nodeBinaryPath)}`];
|
|
38800
38835
|
for (const specifier of TOOLCHAIN_PACKAGE_SPECIFIERS) try {
|
|
38801
38836
|
const packageJson = bundledRequire(specifier);
|
|
38802
38837
|
const version = typeof packageJson.version === "string" ? packageJson.version : "unknown";
|
|
@@ -39652,21 +39687,27 @@ const spawnLintBatches = async (input) => {
|
|
|
39652
39687
|
const allDiagnostics = [];
|
|
39653
39688
|
const droppedFiles = [];
|
|
39654
39689
|
let firstDropReason = null;
|
|
39655
|
-
const
|
|
39656
|
-
const spawnLintBatch = async (batch, depth) => {
|
|
39690
|
+
const spawnLintBatch = async (batch, depth, splitBudget) => {
|
|
39657
39691
|
const batchArgs = [...baseArgs, ...batch];
|
|
39658
39692
|
try {
|
|
39659
39693
|
return parseOxlintOutput(await spawnOxlint(batchArgs, rootDirectory, nodeBinaryPath, spawnTimeoutMs, outputMaxBytes, signal), project, rootDirectory);
|
|
39660
39694
|
} catch (error) {
|
|
39661
39695
|
if (!isSplittableReactDoctorError(error)) throw error;
|
|
39662
|
-
|
|
39663
|
-
|
|
39696
|
+
splitBudget.deadlineMs ??= Date.now() + splitTotalBudgetMs;
|
|
39697
|
+
const isBudgetElapsed = Date.now() >= splitBudget.deadlineMs;
|
|
39698
|
+
const isDepthCapReached = depth >= splitMaxDepth;
|
|
39699
|
+
if (batch.length <= 1 || isBudgetElapsed || isDepthCapReached) {
|
|
39664
39700
|
droppedFiles.push(...batch);
|
|
39665
|
-
if (firstDropReason === null)
|
|
39701
|
+
if (firstDropReason === null) {
|
|
39702
|
+
let limitHint = "";
|
|
39703
|
+
if (isDepthCapReached) limitHint = ` (split depth cap of ${splitMaxDepth} levels reached)`;
|
|
39704
|
+
else if (isBudgetElapsed) limitHint = ` (split budget of ${splitTotalBudgetMs / MILLISECONDS_PER_SECOND}s exhausted at depth ${depth})`;
|
|
39705
|
+
firstDropReason = batch.length > 1 ? `${error.message}${limitHint}` : error.message;
|
|
39706
|
+
}
|
|
39666
39707
|
return [];
|
|
39667
39708
|
}
|
|
39668
39709
|
const splitIndex = Math.ceil(batch.length / 2);
|
|
39669
|
-
return [...await spawnLintBatch(batch.slice(0, splitIndex), depth + 1), ...await spawnLintBatch(batch.slice(splitIndex), depth + 1)];
|
|
39710
|
+
return [...await spawnLintBatch(batch.slice(0, splitIndex), depth + 1, splitBudget), ...await spawnLintBatch(batch.slice(splitIndex), depth + 1, splitBudget)];
|
|
39670
39711
|
}
|
|
39671
39712
|
};
|
|
39672
39713
|
let startedFileCount = 0;
|
|
@@ -39683,7 +39724,7 @@ const spawnLintBatches = async (input) => {
|
|
|
39683
39724
|
try {
|
|
39684
39725
|
const batchResults = await mapWithConcurrency(fileBatches, concurrency, async (batch) => {
|
|
39685
39726
|
startedFileCount += batch.length;
|
|
39686
|
-
const batchDiagnostics = await spawnLintBatch(batch, 0);
|
|
39727
|
+
const batchDiagnostics = await spawnLintBatch(batch, 0, { deadlineMs: null });
|
|
39687
39728
|
scannedFileCount += batch.length;
|
|
39688
39729
|
if (onFileProgress) {
|
|
39689
39730
|
displayedFileCount = Math.min(Math.max(displayedFileCount, scannedFileCount), totalFileCount);
|
|
@@ -39757,7 +39798,14 @@ const resolveReactDoctorCacheDir = (projectDirectory) => {
|
|
|
39757
39798
|
if (cacheDirOverride) return Path.join(cacheDirOverride, projectCacheSubdir(projectDirectory));
|
|
39758
39799
|
const nodeModulesDirectory = Path.join(projectDirectory, "node_modules");
|
|
39759
39800
|
if (NFS.existsSync(nodeModulesDirectory)) return Path.join(nodeModulesDirectory, ".cache", "react-doctor");
|
|
39760
|
-
return Path.join(os.tmpdir(),
|
|
39801
|
+
return Path.join(os.tmpdir(), `react-doctor-cache-${resolveUserCacheScope()}`, projectCacheSubdir(projectDirectory));
|
|
39802
|
+
};
|
|
39803
|
+
const resolveUserCacheScope = () => {
|
|
39804
|
+
try {
|
|
39805
|
+
return String(process.getuid?.() ?? os.userInfo().username);
|
|
39806
|
+
} catch {
|
|
39807
|
+
return "shared";
|
|
39808
|
+
}
|
|
39761
39809
|
};
|
|
39762
39810
|
const sortSourceFilesByCost = (entries) => [...entries].sort((left, right) => right.sizeBytes - left.sizeBytes).map((entry) => entry.path);
|
|
39763
39811
|
/**
|
|
@@ -39925,7 +39973,7 @@ const runOxlint = async (options) => {
|
|
|
39925
39973
|
extendsPaths: [],
|
|
39926
39974
|
ruleSelection: "cacheable"
|
|
39927
39975
|
}),
|
|
39928
|
-
toolchainVersions: resolveOxlintToolchainVersions(),
|
|
39976
|
+
toolchainVersions: resolveOxlintToolchainVersions(nodeBinaryPath),
|
|
39929
39977
|
ignorePatterns: combinedPatterns,
|
|
39930
39978
|
tsconfigContent
|
|
39931
39979
|
});
|
|
@@ -39939,7 +39987,7 @@ const runOxlint = async (options) => {
|
|
|
39939
39987
|
missFiles.push(candidateFile);
|
|
39940
39988
|
continue;
|
|
39941
39989
|
}
|
|
39942
|
-
const cacheKey = `${candidateFile.replaceAll("\\", "/")}
|
|
39990
|
+
const cacheKey = `${candidateFile.replaceAll("\\", "/")}\u0000${contentHash}`;
|
|
39943
39991
|
cacheKeyByFile.set(candidateFile, cacheKey);
|
|
39944
39992
|
const cachedDiagnostics = cache.lookup(cacheKey);
|
|
39945
39993
|
if (cachedDiagnostics === null) missFiles.push(candidateFile);
|
|
@@ -40255,7 +40303,7 @@ const ScoreApiResponseSchema = Struct({
|
|
|
40255
40303
|
})))
|
|
40256
40304
|
});
|
|
40257
40305
|
const parseScoreResult = (value) => getOrNull(decodeUnknownOption(ScoreApiResponseSchema)(value));
|
|
40258
|
-
const stripLocalFileFields = (diagnostics) => diagnostics.map(({ filePath: _filePath, fileContext: _fileContext, ...rest }) => rest);
|
|
40306
|
+
const stripLocalFileFields = (diagnostics) => diagnostics.map(({ filePath: _filePath, fileContext: _fileContext, fixGroupId: _fixGroupId, ...rest }) => rest);
|
|
40259
40307
|
const isAbortError = (error) => error instanceof Error && (error.name === "AbortError" || error.name === "TimeoutError");
|
|
40260
40308
|
const describeFailure = (error) => {
|
|
40261
40309
|
if (isAbortError(error)) return `timed out after ${FETCH_TIMEOUT_MS / 1e3}s`;
|
|
@@ -40559,7 +40607,10 @@ const ALERT_SEVERITY_RANK = {
|
|
|
40559
40607
|
medium: 2,
|
|
40560
40608
|
low: 1
|
|
40561
40609
|
};
|
|
40562
|
-
const severityRank = (severity) =>
|
|
40610
|
+
const severityRank = (severity) => {
|
|
40611
|
+
const normalized = severity.toLowerCase();
|
|
40612
|
+
return Object.hasOwn(ALERT_SEVERITY_RANK, normalized) ? ALERT_SEVERITY_RANK[normalized] : 0;
|
|
40613
|
+
};
|
|
40563
40614
|
const displaySeverity = (severity) => {
|
|
40564
40615
|
const normalized = sanitizeTerminalText(severity.toLowerCase());
|
|
40565
40616
|
return normalized === "middle" ? "medium" : normalized;
|
|
@@ -40576,7 +40627,7 @@ const ALERT_TYPE_LABELS = {
|
|
|
40576
40627
|
typosquat: "possible typosquat"
|
|
40577
40628
|
};
|
|
40578
40629
|
const humanizeAlertType = (type) => type.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/[_-]+/g, " ").toLowerCase().trim();
|
|
40579
|
-
const friendlyAlertType = (type) => ALERT_TYPE_LABELS[type]
|
|
40630
|
+
const friendlyAlertType = (type) => Object.hasOwn(ALERT_TYPE_LABELS, type) ? ALERT_TYPE_LABELS[type] : sanitizeTerminalText(humanizeAlertType(type));
|
|
40580
40631
|
const summarizeAlertNote = (note) => {
|
|
40581
40632
|
const collapsed = sanitizeTerminalText(note.replace(/\s+/g, " ").trim());
|
|
40582
40633
|
const firstSentence = collapsed.split(/(?<=\.)\s/)[0] || collapsed;
|
|
@@ -40671,7 +40722,10 @@ const checkSupplyChain = (input) => gen(function* () {
|
|
|
40671
40722
|
* outage.
|
|
40672
40723
|
*/
|
|
40673
40724
|
var SupplyChain = class SupplyChain extends Service()("react-doctor/SupplyChain") {
|
|
40674
|
-
static layerNode = succeed$3(SupplyChain, SupplyChain.of({ run: (input) => unwrap(
|
|
40725
|
+
static layerNode = succeed$3(SupplyChain, SupplyChain.of({ run: (input) => unwrap(flatMap$2(SupplyChainOverlapTimeoutMs, (totalTimeoutMs) => checkSupplyChain({
|
|
40726
|
+
...input,
|
|
40727
|
+
totalTimeoutMs
|
|
40728
|
+
})).pipe(map$3((diagnostics) => fromIterable$1(diagnostics)), withSpan("SupplyChain.run"))) }));
|
|
40675
40729
|
static layerOf = (diagnostics) => succeed$3(SupplyChain, SupplyChain.of({ run: () => fromIterable$1(diagnostics) }));
|
|
40676
40730
|
};
|
|
40677
40731
|
const getObjectProperty = (value, propertyName) => {
|
|
@@ -40819,10 +40873,11 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
40819
40873
|
...checkExpoProject(scanDirectory, project),
|
|
40820
40874
|
...checkReactNativeProject(scanDirectory, project)
|
|
40821
40875
|
])));
|
|
40822
|
-
const
|
|
40876
|
+
const securityScanFailedRef = yield* make$13(false);
|
|
40877
|
+
const securityScanFiber = yield* forkChild(runCollect(applyPerElementPipeline(isDiffMode ? empty$4 : unwrap(tryPromise(() => checkSecurityScanCooperative(scanDirectory, {
|
|
40823
40878
|
project,
|
|
40824
40879
|
ignoredTags: input.ignoredTags
|
|
40825
|
-
})).pipe(map$3((diagnostics) => fromIterable$1(diagnostics)))))).pipe(withSpan("SecurityScan.run")));
|
|
40880
|
+
})).pipe(map$3((diagnostics) => fromIterable$1(diagnostics)), catch_$1(() => set(securityScanFailedRef, true).pipe(as(empty$4))))))).pipe(withSpan("SecurityScan.run")));
|
|
40826
40881
|
const shouldRunSupplyChain = !isDiffMode || (input.supplyChainManifestChanged ?? false);
|
|
40827
40882
|
const supplyChainOverlapTimeout = yield* SupplyChainOverlapTimeoutMs;
|
|
40828
40883
|
const supplyChainFiber = yield* forkChild(shouldRunSupplyChain ? runCollect(applyPerElementPipeline(supplyChainService.run({
|
|
@@ -40860,7 +40915,6 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
40860
40915
|
const lintConcurrency = deadCodeParseConcurrency === void 0 ? scanConcurrency : Math.max(1, scanConcurrency - deadCodeParseConcurrency);
|
|
40861
40916
|
const buildCollectDeadCode = (deadCodeTimeout) => runCollect(applyPerElementPipeline(deadCodeService.run({
|
|
40862
40917
|
rootDirectory: scanDirectory,
|
|
40863
|
-
userConfig: resolvedConfig.config,
|
|
40864
40918
|
parseConcurrency: deadCodeParseConcurrency,
|
|
40865
40919
|
workerTimeoutMs: deadCodeTimeout.workerTimeoutMs
|
|
40866
40920
|
}).pipe(catchTag("ReactDoctorError", (error) => unwrap(gen(function* () {
|
|
@@ -40988,6 +41042,7 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
40988
41042
|
metadata: scoreMetadata
|
|
40989
41043
|
});
|
|
40990
41044
|
const lintPartialFailures = yield* get$2(partialFailuresRef);
|
|
41045
|
+
const securityScanFailed = yield* get$2(securityScanFailedRef);
|
|
40991
41046
|
return {
|
|
40992
41047
|
project,
|
|
40993
41048
|
userConfig: resolvedConfig.config,
|
|
@@ -41008,8 +41063,10 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
41008
41063
|
scanElapsedMilliseconds,
|
|
41009
41064
|
scanConcurrency,
|
|
41010
41065
|
supplyChainOverlapTimedOut: supplyChainResult.timedOut,
|
|
41066
|
+
securityScanFailed,
|
|
41011
41067
|
lintCacheHitFileCount,
|
|
41012
|
-
lintCacheTotalFileCount
|
|
41068
|
+
lintCacheTotalFileCount,
|
|
41069
|
+
suppressedRuleCounts: transform.summarizeSuppressions()
|
|
41013
41070
|
};
|
|
41014
41071
|
}).pipe(withSpan("runInspect", { attributes: {
|
|
41015
41072
|
"inspect.directory": input.directory,
|
|
@@ -43047,6 +43104,8 @@ const METRIC = {
|
|
|
43047
43104
|
scanCheckSkipped: "scan.check_skipped",
|
|
43048
43105
|
baselineDegraded: "baseline.degraded",
|
|
43049
43106
|
ruleFired: "rule.fired",
|
|
43107
|
+
ruleDisabled: "rule.disabled",
|
|
43108
|
+
ruleSuppressed: "rule.suppressed",
|
|
43050
43109
|
lintFailed: "lint.failed",
|
|
43051
43110
|
deadCodeFailed: "deadcode.failed",
|
|
43052
43111
|
scoreUnavailable: "score.unavailable",
|
|
@@ -43059,6 +43118,9 @@ const METRIC = {
|
|
|
43059
43118
|
installWorkflow: "install.workflow",
|
|
43060
43119
|
installAgentHooks: "install.agent_hooks",
|
|
43061
43120
|
installDependency: "install.dependency",
|
|
43121
|
+
ciScaffolded: "ci.scaffolded",
|
|
43122
|
+
ciUpgraded: "ci.upgraded",
|
|
43123
|
+
ciConfigured: "ci.configured",
|
|
43062
43124
|
rulesChanged: "rules.changed",
|
|
43063
43125
|
rulesQueried: "rules.queried",
|
|
43064
43126
|
lspSessionStarted: "lsp.session.started",
|
|
@@ -43383,5 +43445,5 @@ const startLanguageServer = () => {
|
|
|
43383
43445
|
};
|
|
43384
43446
|
//#endregion
|
|
43385
43447
|
export { startLanguageServer };
|
|
43386
|
-
!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]="
|
|
43387
|
-
//# debugId=
|
|
43448
|
+
!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]="349b2e37-65e9-5f86-8168-70334a95aeec")}catch(e){}}();
|
|
43449
|
+
//# debugId=349b2e37-65e9-5f86-8168-70334a95aeec
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-doctor",
|
|
3
|
-
"version": "0.5.8-dev.
|
|
3
|
+
"version": "0.5.8-dev.c2af308",
|
|
4
4
|
"description": "Your agent writes bad React. This catches it",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"accessibility",
|
|
@@ -64,8 +64,9 @@
|
|
|
64
64
|
"vscode-languageserver": "^9.0.1",
|
|
65
65
|
"vscode-languageserver-textdocument": "^1.0.12",
|
|
66
66
|
"vscode-uri": "^3.1.0",
|
|
67
|
-
"
|
|
68
|
-
"oxlint-plugin-react-doctor": "0.5.8-dev.
|
|
67
|
+
"yaml": "^2.9.0",
|
|
68
|
+
"oxlint-plugin-react-doctor": "0.5.8-dev.c2af308",
|
|
69
|
+
"deslop-js": "0.5.8"
|
|
69
70
|
},
|
|
70
71
|
"devDependencies": {
|
|
71
72
|
"@types/babel__code-frame": "^7.27.0",
|
|
@@ -73,9 +74,9 @@
|
|
|
73
74
|
"@xterm/headless": "^6.0.0",
|
|
74
75
|
"commander": "^14.0.3",
|
|
75
76
|
"ora": "^9.4.0",
|
|
76
|
-
"@react-doctor/api": "0.5.8",
|
|
77
77
|
"@react-doctor/core": "0.5.8",
|
|
78
|
-
"@react-doctor/language-server": "0.5.8"
|
|
78
|
+
"@react-doctor/language-server": "0.5.8",
|
|
79
|
+
"@react-doctor/api": "0.5.8"
|
|
79
80
|
},
|
|
80
81
|
"engines": {
|
|
81
82
|
"node": "^20.19.0 || >=22.13.0"
|