react-doctor 0.5.8-dev.c72b560 → 0.5.8-dev.d241e51
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 +9311 -1515
- package/dist/index.d.ts +9 -0
- package/dist/index.js +160 -85
- package/dist/lsp.js +167 -86
- package/package.json +6 -5
package/dist/index.d.ts
CHANGED
|
@@ -9271,6 +9271,15 @@ interface ReactDoctorConfig {
|
|
|
9271
9271
|
* suppresses regardless of sibling content.
|
|
9272
9272
|
*/
|
|
9273
9273
|
rawTextWrapperComponents?: string[];
|
|
9274
|
+
/**
|
|
9275
|
+
* Capitalized identifiers that exist at runtime but aren't imported in
|
|
9276
|
+
* the file that uses them — react-live's `<LiveProvider scope={...}>`,
|
|
9277
|
+
* Storybook globals, MDX live blocks, or an ambient `declare global` in a
|
|
9278
|
+
* separate `.d.ts`. The single-file `jsx-no-undef` rule can't see those
|
|
9279
|
+
* bindings and would report `<DatePicker />` as an undefined JSX component.
|
|
9280
|
+
* List the injected names here and `jsx-no-undef` treats them as known.
|
|
9281
|
+
*/
|
|
9282
|
+
runtimeGlobals?: string[];
|
|
9274
9283
|
/**
|
|
9275
9284
|
* Project-level allowlist of function names that the
|
|
9276
9285
|
* `server-auth-actions` rule treats as an auth check at the top of
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="
|
|
2
|
+
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="20ae6ec9-b785-55cb-96d6-9c8b9fe3a35b")}catch(e){}}();
|
|
3
3
|
import { r as __toESM$1, t as __commonJSMin$1 } from "./chunk-N93fKeF6.js";
|
|
4
4
|
import { createRequire } from "node:module";
|
|
5
5
|
import * as NFS from "node:fs";
|
|
@@ -7,7 +7,7 @@ import fs from "node:fs";
|
|
|
7
7
|
import * as Path from "node:path";
|
|
8
8
|
import path from "node:path";
|
|
9
9
|
import * as NodeChildProcess from "node:child_process";
|
|
10
|
-
import { spawn, spawnSync } from "node:child_process";
|
|
10
|
+
import { execFileSync, spawn, spawnSync } from "node:child_process";
|
|
11
11
|
import * as ts from "typescript";
|
|
12
12
|
import reactDoctorPlugin, { ALL_REACT_DOCTOR_RULE_KEYS, CROSS_FILE_RULE_IDS, FRAMEWORK_SPECIFIC_RULE_KEYS, MOTION_LIBRARY_PACKAGES, REACT_COMPILER_RULES, REACT_DOCTOR_RULES, classifySecurityScanFile, shouldReadSecurityScanContent } from "oxlint-plugin-react-doctor";
|
|
13
13
|
import * as OS from "node:os";
|
|
@@ -33695,6 +33695,9 @@ const SCORE_API_URL = "https://www.react.doctor/api/score";
|
|
|
33695
33695
|
const FETCH_TIMEOUT_MS = 1e4;
|
|
33696
33696
|
const GITHUB_VIEWER_PERMISSION_TIMEOUT_MS = 2e3;
|
|
33697
33697
|
const SPAWN_ARGS_MAX_LENGTH_CHARS = 24e3;
|
|
33698
|
+
const SPAWN_ARGS_MAX_LENGTH_CHARS_POSIX = 15e5;
|
|
33699
|
+
const SPAWN_ARGS_MAX_LENGTH_CHARS_DARWIN = 8e5;
|
|
33700
|
+
const NODE_VERSION_PROBE_TIMEOUT_MS = 5e3;
|
|
33698
33701
|
const PER_WORKER_MEM_BUDGET_BYTES = 1024 * 1024 * 1024;
|
|
33699
33702
|
const DEFAULT_BRANCH_CANDIDATES = ["main", "master"];
|
|
33700
33703
|
const ADOPTABLE_LINT_CONFIG_FILENAMES = [".oxlintrc.json", ".eslintrc.json"];
|
|
@@ -34519,7 +34522,7 @@ const tokenizeRuleList = (ruleList) => {
|
|
|
34519
34522
|
const FOREIGN_INLINE_DISABLE_PATTERN = /(?:\/\/|\/\*)[ \t]*(eslint|oxlint)-disable-(next-line|line)(?![\w-])([^\r\n]*)/;
|
|
34520
34523
|
const FOREIGN_BLOCK_DISABLE_PATTERN = /\/\*[ \t]*(eslint|oxlint)-disable(?![\w-])([^*\r\n]*)/;
|
|
34521
34524
|
const FOREIGN_BLOCK_ENABLE_PATTERN = /\/\*[ \t]*(?:eslint|oxlint)-enable(?![\w-])([^*\r\n]*)/;
|
|
34522
|
-
const buildHint = (tool, token, ruleId) => `oxlint matches plugin rules only by their full name, so \`${token}\` in your ${tool}-disable comment does not silence \`${ruleId}\` —
|
|
34525
|
+
const buildHint = (tool, token, ruleId) => `oxlint matches plugin rules only by their full name, so \`${token}\` in your ${tool}-disable comment does not silence \`${ruleId}\` — add \`${ruleId}\` to the comment (e.g. \`${token}, ${ruleId}\`).`;
|
|
34523
34526
|
const tokenMisnamesRule = (token, ruleId) => token !== ruleId && isSameRuleKey(token, ruleId);
|
|
34524
34527
|
const detectInlineNearMiss = (lines, diagnosticLineIndex, ruleId) => {
|
|
34525
34528
|
const candidates = [{
|
|
@@ -34999,6 +35002,7 @@ const collectStringSet = (values) => {
|
|
|
34999
35002
|
if (!Array.isArray(values)) return /* @__PURE__ */ new Set();
|
|
35000
35003
|
return new Set(values.filter((value) => typeof value === "string"));
|
|
35001
35004
|
};
|
|
35005
|
+
const JSX_NO_UNDEF_IDENTIFIER_PATTERN = /^`([^`]+)`/;
|
|
35002
35006
|
/**
|
|
35003
35007
|
* Pre-compiles every stateful filter and returns a single
|
|
35004
35008
|
* `apply(diagnostic)` closure that runs (in order):
|
|
@@ -35032,11 +35036,28 @@ const buildDiagnosticPipeline = (input) => {
|
|
|
35032
35036
|
const compiledOverrides = compileIgnoreOverrides(userConfig);
|
|
35033
35037
|
const textComponentNames = collectStringSet(userConfig?.textComponents);
|
|
35034
35038
|
const rawTextWrapperComponentNames = collectStringSet(userConfig?.rawTextWrapperComponents);
|
|
35039
|
+
const runtimeGlobalNames = collectStringSet(userConfig?.runtimeGlobals);
|
|
35035
35040
|
const hasTextComponents = textComponentNames.size > 0;
|
|
35036
35041
|
const hasRawTextWrappers = rawTextWrapperComponentNames.size > 0;
|
|
35042
|
+
const hasRuntimeGlobals = runtimeGlobalNames.size > 0;
|
|
35037
35043
|
const fileLinesCache = /* @__PURE__ */ new Map();
|
|
35038
35044
|
const fileContextCache = /* @__PURE__ */ new Map();
|
|
35039
35045
|
const libraryFileCache = /* @__PURE__ */ new Map();
|
|
35046
|
+
const suppressions = /* @__PURE__ */ new Map();
|
|
35047
|
+
const suppress = (diagnostic, source) => {
|
|
35048
|
+
const { ruleKey } = getDiagnosticRuleIdentity(diagnostic);
|
|
35049
|
+
const suppressionKey = `${ruleKey}\u0000${source}`;
|
|
35050
|
+
const existing = suppressions.get(suppressionKey);
|
|
35051
|
+
suppressions.set(suppressionKey, existing ? {
|
|
35052
|
+
...existing,
|
|
35053
|
+
count: existing.count + 1
|
|
35054
|
+
} : {
|
|
35055
|
+
rule: ruleKey,
|
|
35056
|
+
source,
|
|
35057
|
+
count: 1
|
|
35058
|
+
});
|
|
35059
|
+
return null;
|
|
35060
|
+
};
|
|
35040
35061
|
const isLibraryFile = (filePath) => {
|
|
35041
35062
|
let cached = libraryFileCache.get(filePath);
|
|
35042
35063
|
if (cached === void 0) {
|
|
@@ -35085,50 +35106,60 @@ const buildDiagnosticPipeline = (input) => {
|
|
|
35085
35106
|
if (hasRawTextWrappers && isInsideStringOnlyWrapper(lines, diagnostic.line, diagnostic.column, rawTextWrapperComponentNames)) return true;
|
|
35086
35107
|
return false;
|
|
35087
35108
|
};
|
|
35088
|
-
|
|
35089
|
-
if (
|
|
35090
|
-
|
|
35091
|
-
|
|
35092
|
-
|
|
35093
|
-
|
|
35094
|
-
|
|
35095
|
-
|
|
35096
|
-
|
|
35097
|
-
|
|
35098
|
-
|
|
35099
|
-
|
|
35100
|
-
if (
|
|
35101
|
-
|
|
35102
|
-
|
|
35103
|
-
|
|
35104
|
-
|
|
35105
|
-
|
|
35106
|
-
|
|
35107
|
-
|
|
35108
|
-
|
|
35109
|
-
if (isFileIgnoredByPatterns(current.filePath, rootDirectory, ignoredFilePatterns)) return null;
|
|
35110
|
-
if (isDiagnosticIgnoredByOverrides(current, rootDirectory, compiledOverrides)) return null;
|
|
35111
|
-
if (isRnRawTextSuppressedByConfig(current)) return null;
|
|
35112
|
-
}
|
|
35113
|
-
if (respectInlineDisables && current.line > 0) {
|
|
35114
|
-
const lines = getFileLines(current.filePath);
|
|
35115
|
-
if (lines) {
|
|
35116
|
-
const ruleIdentifier = `${current.plugin}/${current.rule}`;
|
|
35117
|
-
const evaluation = evaluateSuppression(lines, current.line - 1, ruleIdentifier);
|
|
35118
|
-
if (evaluation.isSuppressed) return null;
|
|
35119
|
-
if (evaluation.nearMissHint) current = {
|
|
35120
|
-
...current,
|
|
35121
|
-
suppressionHint: evaluation.nearMissHint
|
|
35122
|
-
};
|
|
35109
|
+
const isJsxNoUndefSuppressedByConfig = (diagnostic) => {
|
|
35110
|
+
if (!hasRuntimeGlobals) return false;
|
|
35111
|
+
if (diagnostic.rule !== "jsx-no-undef") return false;
|
|
35112
|
+
const identifierMatch = JSX_NO_UNDEF_IDENTIFIER_PATTERN.exec(diagnostic.message);
|
|
35113
|
+
return identifierMatch !== null && runtimeGlobalNames.has(identifierMatch[1]);
|
|
35114
|
+
};
|
|
35115
|
+
return {
|
|
35116
|
+
apply: (diagnostic) => {
|
|
35117
|
+
if (shouldAutoSuppress(diagnostic)) return null;
|
|
35118
|
+
let current = diagnostic;
|
|
35119
|
+
let explicitSeverityOverride;
|
|
35120
|
+
let explicitRuleOverride;
|
|
35121
|
+
if (severityControls) {
|
|
35122
|
+
const { ruleKey, category } = getDiagnosticRuleIdentity(current);
|
|
35123
|
+
explicitRuleOverride = resolveRuleSeverityOverride({ ruleKey }, severityControls);
|
|
35124
|
+
explicitSeverityOverride = resolveRuleSeverityOverride({
|
|
35125
|
+
ruleKey,
|
|
35126
|
+
category
|
|
35127
|
+
}, severityControls);
|
|
35128
|
+
if (explicitSeverityOverride === "off") return suppress(current, "config");
|
|
35129
|
+
if (explicitSeverityOverride !== void 0) current = restampSeverity(current, explicitSeverityOverride);
|
|
35123
35130
|
}
|
|
35124
|
-
|
|
35125
|
-
|
|
35126
|
-
|
|
35127
|
-
|
|
35128
|
-
|
|
35129
|
-
|
|
35130
|
-
|
|
35131
|
-
|
|
35131
|
+
if (explicitRuleOverride === void 0) {
|
|
35132
|
+
if (isAppOnlyRule(`${current.plugin}/${current.rule}`) && isLibraryFile(current.filePath)) return null;
|
|
35133
|
+
}
|
|
35134
|
+
if (!showWarnings && current.severity === "warning" && explicitSeverityOverride !== "warn") return null;
|
|
35135
|
+
if (userConfig) {
|
|
35136
|
+
if (isRuleIgnored(`${current.plugin}/${current.rule}`)) return suppress(current, "config");
|
|
35137
|
+
if (isFileIgnoredByPatterns(current.filePath, rootDirectory, ignoredFilePatterns)) return null;
|
|
35138
|
+
if (isDiagnosticIgnoredByOverrides(current, rootDirectory, compiledOverrides)) return suppress(current, "override");
|
|
35139
|
+
if (isRnRawTextSuppressedByConfig(current)) return null;
|
|
35140
|
+
if (isJsxNoUndefSuppressedByConfig(current)) return null;
|
|
35141
|
+
}
|
|
35142
|
+
if (respectInlineDisables && current.line > 0) {
|
|
35143
|
+
const lines = getFileLines(current.filePath);
|
|
35144
|
+
if (lines) {
|
|
35145
|
+
const ruleIdentifier = `${current.plugin}/${current.rule}`;
|
|
35146
|
+
const evaluation = evaluateSuppression(lines, current.line - 1, ruleIdentifier);
|
|
35147
|
+
if (evaluation.isSuppressed) return suppress(current, "inline");
|
|
35148
|
+
if (evaluation.nearMissHint) current = {
|
|
35149
|
+
...current,
|
|
35150
|
+
suppressionHint: evaluation.nearMissHint
|
|
35151
|
+
};
|
|
35152
|
+
}
|
|
35153
|
+
}
|
|
35154
|
+
const fileContext = getFileContext(current.filePath);
|
|
35155
|
+
if (fileContext !== "production") current = {
|
|
35156
|
+
...current,
|
|
35157
|
+
fileContext
|
|
35158
|
+
};
|
|
35159
|
+
return current;
|
|
35160
|
+
},
|
|
35161
|
+
summarizeSuppressions: () => [...suppressions.values()]
|
|
35162
|
+
};
|
|
35132
35163
|
};
|
|
35133
35164
|
const OxlintUnavailableKind = Literals(["binary-not-found", "native-binding-missing"]);
|
|
35134
35165
|
var OxlintUnavailable = class extends TaggedErrorClass()("OxlintUnavailable", {
|
|
@@ -35432,13 +35463,7 @@ var ScanDeadlineMs = class extends Reference("react-doctor/ScanDeadlineMs", { de
|
|
|
35432
35463
|
* without recompiling react-doctor. Tests override via
|
|
35433
35464
|
* `Layer.succeed(SupplyChainOverlapTimeoutMs, ...)`.
|
|
35434
35465
|
*/
|
|
35435
|
-
var SupplyChainOverlapTimeoutMs = class extends Reference("react-doctor/SupplyChainOverlapTimeoutMs", { defaultValue: () => {
|
|
35436
|
-
const raw = process.env["REACT_DOCTOR_SUPPLY_CHAIN_TIMEOUT_MS"];
|
|
35437
|
-
if (raw === void 0) return SUPPLY_CHAIN_OVERLAP_TIMEOUT_MS;
|
|
35438
|
-
const parsed = Number(raw);
|
|
35439
|
-
if (!Number.isFinite(parsed) || parsed <= 0) return SUPPLY_CHAIN_OVERLAP_TIMEOUT_MS;
|
|
35440
|
-
return parsed;
|
|
35441
|
-
} }) {};
|
|
35466
|
+
var SupplyChainOverlapTimeoutMs = class extends Reference("react-doctor/SupplyChainOverlapTimeoutMs", { defaultValue: () => readPositiveEnvMs("REACT_DOCTOR_SUPPLY_CHAIN_TIMEOUT_MS", SUPPLY_CHAIN_OVERLAP_TIMEOUT_MS) }) {};
|
|
35442
35467
|
/**
|
|
35443
35468
|
* Hard cap on combined stdout+stderr bytes per oxlint batch. The
|
|
35444
35469
|
* subprocess gets SIGKILL'd if it produces more; the recovery path
|
|
@@ -35586,6 +35611,7 @@ const STRING_ARRAY_FIELD_NAMES = [
|
|
|
35586
35611
|
"projects",
|
|
35587
35612
|
"textComponents",
|
|
35588
35613
|
"rawTextWrapperComponents",
|
|
35614
|
+
"runtimeGlobals",
|
|
35589
35615
|
"serverAuthFunctionNames"
|
|
35590
35616
|
];
|
|
35591
35617
|
const SURFACE_CONTROL_FIELD_NAMES = [
|
|
@@ -37290,6 +37316,11 @@ const toCanonicalPath = (filePath) => {
|
|
|
37290
37316
|
};
|
|
37291
37317
|
const DEAD_CODE_PLUGIN = "deslop";
|
|
37292
37318
|
const DEAD_CODE_CATEGORY = "Maintainability";
|
|
37319
|
+
const REACT_DOCTOR_TOOLCHAIN_PACKAGES = new Set([
|
|
37320
|
+
"react-doctor",
|
|
37321
|
+
"eslint-plugin-react-doctor",
|
|
37322
|
+
"oxlint-plugin-react-doctor"
|
|
37323
|
+
]);
|
|
37293
37324
|
const DEAD_CODE_WORKER_SCRIPT = `
|
|
37294
37325
|
const inputChunks = [];
|
|
37295
37326
|
process.stdin.on("data", (chunk) => inputChunks.push(chunk));
|
|
@@ -37595,6 +37626,7 @@ const checkDeadCode = async (options) => {
|
|
|
37595
37626
|
});
|
|
37596
37627
|
}
|
|
37597
37628
|
for (const unusedDependency of result.unusedDependencies) {
|
|
37629
|
+
if (REACT_DOCTOR_TOOLCHAIN_PACKAGES.has(unusedDependency.name)) continue;
|
|
37598
37630
|
const label = unusedDependency.isDevDependency ? "devDependency" : "dependency";
|
|
37599
37631
|
diagnostics.push({
|
|
37600
37632
|
filePath: "package.json",
|
|
@@ -37773,7 +37805,6 @@ var DeadCode = class DeadCode extends Service()("react-doctor/DeadCode") {
|
|
|
37773
37805
|
return yield* tryPromise({
|
|
37774
37806
|
try: (signal) => checkDeadCode({
|
|
37775
37807
|
rootDirectory: input.rootDirectory,
|
|
37776
|
-
userConfig: input.userConfig,
|
|
37777
37808
|
parseConcurrency: input.parseConcurrency,
|
|
37778
37809
|
workerTimeoutMs: input.workerTimeoutMs,
|
|
37779
37810
|
abortSignal: signal
|
|
@@ -37896,6 +37927,11 @@ const isSafeGitRevision = (candidate) => {
|
|
|
37896
37927
|
if (candidate.includes("..") || candidate.includes("@{")) return false;
|
|
37897
37928
|
return SAFE_GIT_REVISION_PATTERN.test(candidate);
|
|
37898
37929
|
};
|
|
37930
|
+
const resolveSpawnArgsLengthCap = () => {
|
|
37931
|
+
if (process.platform === "win32") return SPAWN_ARGS_MAX_LENGTH_CHARS;
|
|
37932
|
+
if (process.platform === "darwin") return SPAWN_ARGS_MAX_LENGTH_CHARS_DARWIN;
|
|
37933
|
+
return SPAWN_ARGS_MAX_LENGTH_CHARS_POSIX;
|
|
37934
|
+
};
|
|
37899
37935
|
/**
|
|
37900
37936
|
* Splits a git revision range into its endpoints: three-dot `A...B`
|
|
37901
37937
|
* (symmetric, merge-base) or two-dot `A..B` (direct). Returns `null`
|
|
@@ -37980,7 +38016,8 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
37980
38016
|
return scoped(gen(function* () {
|
|
37981
38017
|
if (!isDirectory(input.directory)) return yield* foldSpawnFailure(`spawn ENOTDIR (cwd is not a directory: ${input.directory})`);
|
|
37982
38018
|
const argvLengthChars = input.command.length + 1 + input.args.reduce((total, arg) => total + arg.length + 1, 0);
|
|
37983
|
-
|
|
38019
|
+
const spawnArgsLengthCap = resolveSpawnArgsLengthCap();
|
|
38020
|
+
if (argvLengthChars > spawnArgsLengthCap) return yield* foldSpawnFailure(`spawn ENAMETOOLONG (${argvLengthChars} argv chars exceed ${spawnArgsLengthCap})`);
|
|
37984
38021
|
const handle = yield* spawner.spawn(make$1(input.command, [...input.args], {
|
|
37985
38022
|
cwd: input.directory,
|
|
37986
38023
|
env: input.env,
|
|
@@ -38149,6 +38186,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
38149
38186
|
if (resolvedCurrentBranch === null && explicitBaseBranch === void 0) return null;
|
|
38150
38187
|
const baseBranch = explicitBaseBranch ?? (yield* defaultBranch(directory));
|
|
38151
38188
|
if (baseBranch === null) return null;
|
|
38189
|
+
if (!isSafeGitRevision(baseBranch)) return null;
|
|
38152
38190
|
if (explicitBaseBranch !== void 0) {
|
|
38153
38191
|
if (!(yield* branchExists(directory, explicitBaseBranch))) return yield* fail$4(new ReactDoctorError({ reason: new GitBaseBranchMissing({ branch: explicitBaseBranch }) }));
|
|
38154
38192
|
}
|
|
@@ -38499,7 +38537,7 @@ const normalizeConfigForHash = (config) => {
|
|
|
38499
38537
|
if (Array.isArray(clone?.jsPlugins)) clone.jsPlugins = clone.jsPlugins.map((_, index) => `<plugin:${index}>`);
|
|
38500
38538
|
return clone;
|
|
38501
38539
|
};
|
|
38502
|
-
const computeRulesetHash = (input) => crypto.createHash("sha1").update(JSON.stringify(normalizeConfigForHash(input.config))).update("\0").update([...input.toolchainVersions].join("\0")).update("\0").update([...input.ignorePatterns].join("\n")).update("
|
|
38540
|
+
const computeRulesetHash = (input) => crypto.createHash("sha1").update(JSON.stringify(normalizeConfigForHash(input.config))).update("\0").update([...input.toolchainVersions].join("\0")).update("\0").update([...input.ignorePatterns].join("\n")).update("\0").update(input.tsconfigContent ?? "").digest("hex");
|
|
38503
38541
|
/**
|
|
38504
38542
|
* Loads a plugin module via the local require resolver and extracts
|
|
38505
38543
|
* `(name, ruleNames)` from either `module.exports.meta + rules` or
|
|
@@ -38777,24 +38815,38 @@ const createFileLintCache = (cacheDirectory, rulesetHash) => {
|
|
|
38777
38815
|
updatedAtMs: Date.now(),
|
|
38778
38816
|
files: Object.fromEntries(cappedEntries)
|
|
38779
38817
|
};
|
|
38780
|
-
const keptHashes = Object.entries(rulesets).sort(([, first], [, second]) => second.updatedAtMs - first.updatedAtMs).slice(0, 8).map(([hash]) => hash);
|
|
38781
|
-
const prunedRulesets = {};
|
|
38782
|
-
for (const hash of keptHashes) prunedRulesets[hash] = rulesets[hash];
|
|
38783
38818
|
atomicWriteJson(cacheFilePath, {
|
|
38784
38819
|
version: 1,
|
|
38785
|
-
rulesets:
|
|
38820
|
+
rulesets: Object.fromEntries(Object.entries(rulesets).filter(([, bucket]) => isRecord(bucket) && typeof bucket.updatedAtMs === "number").sort(([, first], [, second]) => second.updatedAtMs - first.updatedAtMs).slice(0, 8))
|
|
38786
38821
|
});
|
|
38787
38822
|
}
|
|
38788
38823
|
};
|
|
38789
38824
|
};
|
|
38790
38825
|
const bundledRequire = createRequire(import.meta.url);
|
|
38826
|
+
const childNodeVersionByBinaryPath = /* @__PURE__ */ new Map();
|
|
38827
|
+
const resolveChildNodeVersion = (nodeBinaryPath) => {
|
|
38828
|
+
if (nodeBinaryPath === process.execPath) return process.version;
|
|
38829
|
+
const cachedVersion = childNodeVersionByBinaryPath.get(nodeBinaryPath);
|
|
38830
|
+
if (cachedVersion !== void 0) return cachedVersion;
|
|
38831
|
+
let version;
|
|
38832
|
+
try {
|
|
38833
|
+
version = execFileSync(nodeBinaryPath, ["--version"], {
|
|
38834
|
+
encoding: "utf8",
|
|
38835
|
+
timeout: NODE_VERSION_PROBE_TIMEOUT_MS
|
|
38836
|
+
}).trim();
|
|
38837
|
+
} catch {
|
|
38838
|
+
version = `unknown:${nodeBinaryPath}`;
|
|
38839
|
+
}
|
|
38840
|
+
childNodeVersionByBinaryPath.set(nodeBinaryPath, version);
|
|
38841
|
+
return version;
|
|
38842
|
+
};
|
|
38791
38843
|
const TOOLCHAIN_PACKAGE_SPECIFIERS = [
|
|
38792
38844
|
"oxlint/package.json",
|
|
38793
38845
|
"oxlint-plugin-react-doctor/package.json",
|
|
38794
38846
|
"eslint-plugin-react-hooks/package.json"
|
|
38795
38847
|
];
|
|
38796
|
-
const resolveOxlintToolchainVersions = () => {
|
|
38797
|
-
const versions = [`node=${
|
|
38848
|
+
const resolveOxlintToolchainVersions = (nodeBinaryPath = process.execPath) => {
|
|
38849
|
+
const versions = [`node=${resolveChildNodeVersion(nodeBinaryPath)}`];
|
|
38798
38850
|
for (const specifier of TOOLCHAIN_PACKAGE_SPECIFIERS) try {
|
|
38799
38851
|
const packageJson = bundledRequire(specifier);
|
|
38800
38852
|
const version = typeof packageJson.version === "string" ? packageJson.version : "unknown";
|
|
@@ -39328,11 +39380,12 @@ const REACT_COMPILER_TITLE = "React Compiler can't optimize this";
|
|
|
39328
39380
|
const REACT_COMPILER_TODO_TITLE = "React Compiler doesn't support this syntax";
|
|
39329
39381
|
const REACT_COMPILER_IMPACT = "This component misses React Compiler's automatic memoization & re-renders more than it should";
|
|
39330
39382
|
const REACT_COMPILER_ACTION = "Rewrite the flagged code so the compiler can optimize it.";
|
|
39383
|
+
const REACT_COMPILER_INCOMPATIBLE_LIBRARY_ACTION = "It's how the library works, not a bug in your code. Memoize values you pass from it into other memoized components, or suppress it with `// react-doctor-disable-next-line react-hooks-js/incompatible-library`.";
|
|
39331
39384
|
const REACT_COMPILER_GENERIC_MESSAGE = `${REACT_COMPILER_IMPACT}. ${REACT_COMPILER_ACTION}`;
|
|
39332
|
-
const buildReactCompilerMessage = (reasonSummary) => {
|
|
39385
|
+
const buildReactCompilerMessage = (reasonSummary, action = REACT_COMPILER_ACTION) => {
|
|
39333
39386
|
const normalizedSummary = reasonSummary.replace(TRAILING_PERIOD_PATTERN, "");
|
|
39334
|
-
if (!normalizedSummary) return
|
|
39335
|
-
return `${REACT_COMPILER_IMPACT}: ${normalizedSummary}. ${
|
|
39387
|
+
if (!normalizedSummary) return `${REACT_COMPILER_IMPACT}. ${action}`;
|
|
39388
|
+
return `${REACT_COMPILER_IMPACT}: ${normalizedSummary}. ${action}`;
|
|
39336
39389
|
};
|
|
39337
39390
|
const PLUGIN_CATEGORY_MAP = {
|
|
39338
39391
|
react: "Bugs",
|
|
@@ -39381,7 +39434,7 @@ const resolveCleanedDiagnostic = (message, help, plugin, rule, project) => {
|
|
|
39381
39434
|
const [reasonSummary = "", ...reasonDetailLines] = bailoutReason.split("\n");
|
|
39382
39435
|
const reasonDetail = reasonDetailLines.join("\n").trim();
|
|
39383
39436
|
return {
|
|
39384
|
-
message: buildReactCompilerMessage(reasonSummary.trim()),
|
|
39437
|
+
message: buildReactCompilerMessage(reasonSummary.trim(), rule === "incompatible-library" ? REACT_COMPILER_INCOMPATIBLE_LIBRARY_ACTION : REACT_COMPILER_ACTION),
|
|
39385
39438
|
help: appendReanimatedSharedValueHint(reasonDetail || help, rule, project)
|
|
39386
39439
|
};
|
|
39387
39440
|
}
|
|
@@ -39649,21 +39702,27 @@ const spawnLintBatches = async (input) => {
|
|
|
39649
39702
|
const allDiagnostics = [];
|
|
39650
39703
|
const droppedFiles = [];
|
|
39651
39704
|
let firstDropReason = null;
|
|
39652
|
-
const
|
|
39653
|
-
const spawnLintBatch = async (batch, depth) => {
|
|
39705
|
+
const spawnLintBatch = async (batch, depth, splitBudget) => {
|
|
39654
39706
|
const batchArgs = [...baseArgs, ...batch];
|
|
39655
39707
|
try {
|
|
39656
39708
|
return parseOxlintOutput(await spawnOxlint(batchArgs, rootDirectory, nodeBinaryPath, spawnTimeoutMs, outputMaxBytes, signal), project, rootDirectory);
|
|
39657
39709
|
} catch (error) {
|
|
39658
39710
|
if (!isSplittableReactDoctorError(error)) throw error;
|
|
39659
|
-
|
|
39660
|
-
|
|
39711
|
+
splitBudget.deadlineMs ??= Date.now() + splitTotalBudgetMs;
|
|
39712
|
+
const isBudgetElapsed = Date.now() >= splitBudget.deadlineMs;
|
|
39713
|
+
const isDepthCapReached = depth >= splitMaxDepth;
|
|
39714
|
+
if (batch.length <= 1 || isBudgetElapsed || isDepthCapReached) {
|
|
39661
39715
|
droppedFiles.push(...batch);
|
|
39662
|
-
if (firstDropReason === null)
|
|
39716
|
+
if (firstDropReason === null) {
|
|
39717
|
+
let limitHint = "";
|
|
39718
|
+
if (isDepthCapReached) limitHint = ` (split depth cap of ${splitMaxDepth} levels reached)`;
|
|
39719
|
+
else if (isBudgetElapsed) limitHint = ` (split budget of ${splitTotalBudgetMs / MILLISECONDS_PER_SECOND}s exhausted at depth ${depth})`;
|
|
39720
|
+
firstDropReason = batch.length > 1 ? `${error.message}${limitHint}` : error.message;
|
|
39721
|
+
}
|
|
39663
39722
|
return [];
|
|
39664
39723
|
}
|
|
39665
39724
|
const splitIndex = Math.ceil(batch.length / 2);
|
|
39666
|
-
return [...await spawnLintBatch(batch.slice(0, splitIndex), depth + 1), ...await spawnLintBatch(batch.slice(splitIndex), depth + 1)];
|
|
39725
|
+
return [...await spawnLintBatch(batch.slice(0, splitIndex), depth + 1, splitBudget), ...await spawnLintBatch(batch.slice(splitIndex), depth + 1, splitBudget)];
|
|
39667
39726
|
}
|
|
39668
39727
|
};
|
|
39669
39728
|
let startedFileCount = 0;
|
|
@@ -39680,7 +39739,7 @@ const spawnLintBatches = async (input) => {
|
|
|
39680
39739
|
try {
|
|
39681
39740
|
const batchResults = await mapWithConcurrency(fileBatches, concurrency, async (batch) => {
|
|
39682
39741
|
startedFileCount += batch.length;
|
|
39683
|
-
const batchDiagnostics = await spawnLintBatch(batch, 0);
|
|
39742
|
+
const batchDiagnostics = await spawnLintBatch(batch, 0, { deadlineMs: null });
|
|
39684
39743
|
scannedFileCount += batch.length;
|
|
39685
39744
|
if (onFileProgress) {
|
|
39686
39745
|
displayedFileCount = Math.min(Math.max(displayedFileCount, scannedFileCount), totalFileCount);
|
|
@@ -39754,7 +39813,14 @@ const resolveReactDoctorCacheDir = (projectDirectory) => {
|
|
|
39754
39813
|
if (cacheDirOverride) return Path.join(cacheDirOverride, projectCacheSubdir(projectDirectory));
|
|
39755
39814
|
const nodeModulesDirectory = Path.join(projectDirectory, "node_modules");
|
|
39756
39815
|
if (NFS.existsSync(nodeModulesDirectory)) return Path.join(nodeModulesDirectory, ".cache", "react-doctor");
|
|
39757
|
-
return Path.join(os.tmpdir(),
|
|
39816
|
+
return Path.join(os.tmpdir(), `react-doctor-cache-${resolveUserCacheScope()}`, projectCacheSubdir(projectDirectory));
|
|
39817
|
+
};
|
|
39818
|
+
const resolveUserCacheScope = () => {
|
|
39819
|
+
try {
|
|
39820
|
+
return String(process.getuid?.() ?? os.userInfo().username);
|
|
39821
|
+
} catch {
|
|
39822
|
+
return "shared";
|
|
39823
|
+
}
|
|
39758
39824
|
};
|
|
39759
39825
|
const sortSourceFilesByCost = (entries) => [...entries].sort((left, right) => right.sizeBytes - left.sizeBytes).map((entry) => entry.path);
|
|
39760
39826
|
/**
|
|
@@ -39922,7 +39988,7 @@ const runOxlint = async (options) => {
|
|
|
39922
39988
|
extendsPaths: [],
|
|
39923
39989
|
ruleSelection: "cacheable"
|
|
39924
39990
|
}),
|
|
39925
|
-
toolchainVersions: resolveOxlintToolchainVersions(),
|
|
39991
|
+
toolchainVersions: resolveOxlintToolchainVersions(nodeBinaryPath),
|
|
39926
39992
|
ignorePatterns: combinedPatterns,
|
|
39927
39993
|
tsconfigContent
|
|
39928
39994
|
});
|
|
@@ -39936,7 +40002,7 @@ const runOxlint = async (options) => {
|
|
|
39936
40002
|
missFiles.push(candidateFile);
|
|
39937
40003
|
continue;
|
|
39938
40004
|
}
|
|
39939
|
-
const cacheKey = `${candidateFile.replaceAll("\\", "/")}
|
|
40005
|
+
const cacheKey = `${candidateFile.replaceAll("\\", "/")}\u0000${contentHash}`;
|
|
39940
40006
|
cacheKeyByFile.set(candidateFile, cacheKey);
|
|
39941
40007
|
const cachedDiagnostics = cache.lookup(cacheKey);
|
|
39942
40008
|
if (cachedDiagnostics === null) missFiles.push(candidateFile);
|
|
@@ -40252,7 +40318,7 @@ const ScoreApiResponseSchema = Struct({
|
|
|
40252
40318
|
})))
|
|
40253
40319
|
});
|
|
40254
40320
|
const parseScoreResult = (value) => getOrNull(decodeUnknownOption(ScoreApiResponseSchema)(value));
|
|
40255
|
-
const stripLocalFileFields = (diagnostics) => diagnostics.map(({ filePath: _filePath, fileContext: _fileContext, ...rest }) => rest);
|
|
40321
|
+
const stripLocalFileFields = (diagnostics) => diagnostics.map(({ filePath: _filePath, fileContext: _fileContext, fixGroupId: _fixGroupId, ...rest }) => rest);
|
|
40256
40322
|
const isAbortError = (error) => error instanceof Error && (error.name === "AbortError" || error.name === "TimeoutError");
|
|
40257
40323
|
const describeFailure = (error) => {
|
|
40258
40324
|
if (isAbortError(error)) return `timed out after ${FETCH_TIMEOUT_MS / 1e3}s`;
|
|
@@ -40556,7 +40622,10 @@ const ALERT_SEVERITY_RANK = {
|
|
|
40556
40622
|
medium: 2,
|
|
40557
40623
|
low: 1
|
|
40558
40624
|
};
|
|
40559
|
-
const severityRank = (severity) =>
|
|
40625
|
+
const severityRank = (severity) => {
|
|
40626
|
+
const normalized = severity.toLowerCase();
|
|
40627
|
+
return Object.hasOwn(ALERT_SEVERITY_RANK, normalized) ? ALERT_SEVERITY_RANK[normalized] : 0;
|
|
40628
|
+
};
|
|
40560
40629
|
const displaySeverity = (severity) => {
|
|
40561
40630
|
const normalized = sanitizeTerminalText(severity.toLowerCase());
|
|
40562
40631
|
return normalized === "middle" ? "medium" : normalized;
|
|
@@ -40573,7 +40642,7 @@ const ALERT_TYPE_LABELS = {
|
|
|
40573
40642
|
typosquat: "possible typosquat"
|
|
40574
40643
|
};
|
|
40575
40644
|
const humanizeAlertType = (type) => type.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/[_-]+/g, " ").toLowerCase().trim();
|
|
40576
|
-
const friendlyAlertType = (type) => ALERT_TYPE_LABELS[type]
|
|
40645
|
+
const friendlyAlertType = (type) => Object.hasOwn(ALERT_TYPE_LABELS, type) ? ALERT_TYPE_LABELS[type] : sanitizeTerminalText(humanizeAlertType(type));
|
|
40577
40646
|
const summarizeAlertNote = (note) => {
|
|
40578
40647
|
const collapsed = sanitizeTerminalText(note.replace(/\s+/g, " ").trim());
|
|
40579
40648
|
const firstSentence = collapsed.split(/(?<=\.)\s/)[0] || collapsed;
|
|
@@ -40668,7 +40737,10 @@ const checkSupplyChain = (input) => gen(function* () {
|
|
|
40668
40737
|
* outage.
|
|
40669
40738
|
*/
|
|
40670
40739
|
var SupplyChain = class SupplyChain extends Service()("react-doctor/SupplyChain") {
|
|
40671
|
-
static layerNode = succeed$3(SupplyChain, SupplyChain.of({ run: (input) => unwrap(
|
|
40740
|
+
static layerNode = succeed$3(SupplyChain, SupplyChain.of({ run: (input) => unwrap(flatMap$2(SupplyChainOverlapTimeoutMs, (totalTimeoutMs) => checkSupplyChain({
|
|
40741
|
+
...input,
|
|
40742
|
+
totalTimeoutMs
|
|
40743
|
+
})).pipe(map$3((diagnostics) => fromIterable$1(diagnostics)), withSpan("SupplyChain.run"))) }));
|
|
40672
40744
|
static layerOf = (diagnostics) => succeed$3(SupplyChain, SupplyChain.of({ run: () => fromIterable$1(diagnostics) }));
|
|
40673
40745
|
};
|
|
40674
40746
|
const getObjectProperty = (value, propertyName) => {
|
|
@@ -40816,10 +40888,11 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
40816
40888
|
...checkExpoProject(scanDirectory, project),
|
|
40817
40889
|
...checkReactNativeProject(scanDirectory, project)
|
|
40818
40890
|
])));
|
|
40819
|
-
const
|
|
40891
|
+
const securityScanFailedRef = yield* make$13(false);
|
|
40892
|
+
const securityScanFiber = yield* forkChild(runCollect(applyPerElementPipeline(isDiffMode ? empty$4 : unwrap(tryPromise(() => checkSecurityScanCooperative(scanDirectory, {
|
|
40820
40893
|
project,
|
|
40821
40894
|
ignoredTags: input.ignoredTags
|
|
40822
|
-
})).pipe(map$3((diagnostics) => fromIterable$1(diagnostics)))))).pipe(withSpan("SecurityScan.run")));
|
|
40895
|
+
})).pipe(map$3((diagnostics) => fromIterable$1(diagnostics)), catch_$1(() => set(securityScanFailedRef, true).pipe(as(empty$4))))))).pipe(withSpan("SecurityScan.run")));
|
|
40823
40896
|
const shouldRunSupplyChain = !isDiffMode || (input.supplyChainManifestChanged ?? false);
|
|
40824
40897
|
const supplyChainOverlapTimeout = yield* SupplyChainOverlapTimeoutMs;
|
|
40825
40898
|
const supplyChainFiber = yield* forkChild(shouldRunSupplyChain ? runCollect(applyPerElementPipeline(supplyChainService.run({
|
|
@@ -40857,7 +40930,6 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
40857
40930
|
const lintConcurrency = deadCodeParseConcurrency === void 0 ? scanConcurrency : Math.max(1, scanConcurrency - deadCodeParseConcurrency);
|
|
40858
40931
|
const buildCollectDeadCode = (deadCodeTimeout) => runCollect(applyPerElementPipeline(deadCodeService.run({
|
|
40859
40932
|
rootDirectory: scanDirectory,
|
|
40860
|
-
userConfig: resolvedConfig.config,
|
|
40861
40933
|
parseConcurrency: deadCodeParseConcurrency,
|
|
40862
40934
|
workerTimeoutMs: deadCodeTimeout.workerTimeoutMs
|
|
40863
40935
|
}).pipe(catchTag("ReactDoctorError", (error) => unwrap(gen(function* () {
|
|
@@ -40985,6 +41057,7 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
40985
41057
|
metadata: scoreMetadata
|
|
40986
41058
|
});
|
|
40987
41059
|
const lintPartialFailures = yield* get$2(partialFailuresRef);
|
|
41060
|
+
const securityScanFailed = yield* get$2(securityScanFailedRef);
|
|
40988
41061
|
return {
|
|
40989
41062
|
project,
|
|
40990
41063
|
userConfig: resolvedConfig.config,
|
|
@@ -41005,8 +41078,10 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
41005
41078
|
scanElapsedMilliseconds,
|
|
41006
41079
|
scanConcurrency,
|
|
41007
41080
|
supplyChainOverlapTimedOut: supplyChainResult.timedOut,
|
|
41081
|
+
securityScanFailed,
|
|
41008
41082
|
lintCacheHitFileCount,
|
|
41009
|
-
lintCacheTotalFileCount
|
|
41083
|
+
lintCacheTotalFileCount,
|
|
41084
|
+
suppressedRuleCounts: transform.summarizeSuppressions()
|
|
41010
41085
|
};
|
|
41011
41086
|
}).pipe(withSpan("runInspect", { attributes: {
|
|
41012
41087
|
"inspect.directory": input.directory,
|
|
@@ -41645,4 +41720,4 @@ const toJsonReport = (result, options) => buildJsonReport({
|
|
|
41645
41720
|
export { AmbiguousProjectError, NoReactDependencyError, NotADirectoryError, PackageJsonNotFoundError, ProjectNotFoundError, ReactDoctorError, buildJsonReport, buildJsonReportError, clearCaches, defineConfig, diagnose, filterSourceFiles, getDiffInfo, isProjectDiscoveryError, isReactDoctorError, summarizeDiagnostics, toJsonReport };
|
|
41646
41721
|
|
|
41647
41722
|
//# sourceMappingURL=index.js.map
|
|
41648
|
-
//# debugId=
|
|
41723
|
+
//# debugId=20ae6ec9-b785-55cb-96d6-9c8b9fe3a35b
|