react-doctor 0.5.6 → 0.5.7-dev.350a6ed
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 +774 -412
- package/dist/index.d.ts +23 -3
- package/dist/index.js +324 -196
- package/dist/lsp.js +343 -220
- package/package.json +5 -5
package/dist/lsp.js
CHANGED
|
@@ -14,7 +14,7 @@ import * as NodeUrl from "node:url";
|
|
|
14
14
|
import { fileURLToPath } from "node:url";
|
|
15
15
|
import { createJiti } from "jiti";
|
|
16
16
|
import * as Crypto from "node:crypto";
|
|
17
|
-
import crypto from "node:crypto";
|
|
17
|
+
import crypto, { createHash } from "node:crypto";
|
|
18
18
|
import { gzipSync } from "node:zlib";
|
|
19
19
|
import { CodeActionKind, CodeActionTriggerKind, DidChangeWatchedFilesNotification, DocumentDiagnosticReportKind, FileChangeType, TextDocumentSyncKind, TextDocuments, createConnection } from "vscode-languageserver/node.js";
|
|
20
20
|
import { TextDocument } from "vscode-languageserver-textdocument";
|
|
@@ -19286,7 +19286,8 @@ var Diagnostic = class extends Class("Diagnostic")({
|
|
|
19286
19286
|
category: String$1,
|
|
19287
19287
|
fileContext: optional(Literals(["test", "story"])),
|
|
19288
19288
|
suppressionHint: optional(String$1),
|
|
19289
|
-
relatedLocations: optional(ArraySchema(DiagnosticRelatedLocation))
|
|
19289
|
+
relatedLocations: optional(ArraySchema(DiagnosticRelatedLocation)),
|
|
19290
|
+
fixGroupId: optional(String$1)
|
|
19290
19291
|
}) {};
|
|
19291
19292
|
/**
|
|
19292
19293
|
* Deterministic identity string for a diagnostic. Same diagnostic
|
|
@@ -19335,6 +19336,7 @@ var JsonReportProjectEntry = class extends Class("JsonReportProjectEntry")({
|
|
|
19335
19336
|
score: Unknown,
|
|
19336
19337
|
skippedChecks: ArraySchema(String$1),
|
|
19337
19338
|
skippedCheckReasons: optional(Record$1(String$1, String$1)),
|
|
19339
|
+
scannedFileCount: optional(Number$1),
|
|
19338
19340
|
elapsedMilliseconds: Number$1
|
|
19339
19341
|
}) {};
|
|
19340
19342
|
/**
|
|
@@ -32761,6 +32763,7 @@ const isLargeMinifiedFile = (absolutePath) => {
|
|
|
32761
32763
|
if (sizeBytes < 2e4) return false;
|
|
32762
32764
|
return isMinifiedSource(absolutePath);
|
|
32763
32765
|
};
|
|
32766
|
+
const isErrnoException = (error) => error instanceof Error && "code" in error;
|
|
32764
32767
|
const IGNORABLE_READDIR_ERROR_CODES = new Set([
|
|
32765
32768
|
"EACCES",
|
|
32766
32769
|
"EPERM",
|
|
@@ -32770,11 +32773,7 @@ const IGNORABLE_READDIR_ERROR_CODES = new Set([
|
|
|
32770
32773
|
"ELOOP",
|
|
32771
32774
|
"ENAMETOOLONG"
|
|
32772
32775
|
]);
|
|
32773
|
-
const isIgnorableReaddirError = (error) =>
|
|
32774
|
-
if (typeof error !== "object" || error === null) return false;
|
|
32775
|
-
const errorCode = error.code;
|
|
32776
|
-
return typeof errorCode === "string" && IGNORABLE_READDIR_ERROR_CODES.has(errorCode);
|
|
32777
|
-
};
|
|
32776
|
+
const isIgnorableReaddirError = (error) => isErrnoException(error) && typeof error.code === "string" && IGNORABLE_READDIR_ERROR_CODES.has(error.code);
|
|
32778
32777
|
const readDirectoryEntries = (directoryPath) => {
|
|
32779
32778
|
try {
|
|
32780
32779
|
return NFS.readdirSync(directoryPath, { withFileTypes: true });
|
|
@@ -32824,7 +32823,7 @@ const readPackageJsonUncached = (packageJsonPath) => {
|
|
|
32824
32823
|
return JSON.parse(NFS.readFileSync(packageJsonPath, "utf-8"));
|
|
32825
32824
|
} catch (error) {
|
|
32826
32825
|
if (error instanceof SyntaxError) return {};
|
|
32827
|
-
if (error
|
|
32826
|
+
if (isErrnoException(error)) {
|
|
32828
32827
|
const { code } = error;
|
|
32829
32828
|
if (code === "EISDIR" || code === "EACCES" || code === "EPERM" || code === "ENOENT") return {};
|
|
32830
32829
|
}
|
|
@@ -33549,17 +33548,13 @@ const isPackageJsonReactNativeAware = (packageJson) => {
|
|
|
33549
33548
|
return false;
|
|
33550
33549
|
};
|
|
33551
33550
|
const hasReactNativeWorkspaceAnywhere = (rootDirectory, rootPackageJson) => someWorkspacePackageJson(rootDirectory, rootPackageJson, isPackageJsonReactNativeAware);
|
|
33552
|
-
const
|
|
33553
|
-
const spec = packageJson.dependencies?.
|
|
33551
|
+
const getDependencySpec = (packageJson, packageName) => {
|
|
33552
|
+
const spec = packageJson.dependencies?.[packageName] ?? packageJson.devDependencies?.[packageName] ?? packageJson.peerDependencies?.[packageName] ?? packageJson.optionalDependencies?.[packageName];
|
|
33554
33553
|
return typeof spec === "string" ? spec : null;
|
|
33555
33554
|
};
|
|
33556
|
-
const findExpoVersion = (rootDirectory, rootPackageJson) => findInWorkspacePackageJsons(rootDirectory, rootPackageJson,
|
|
33555
|
+
const findExpoVersion = (rootDirectory, rootPackageJson) => findInWorkspacePackageJsons(rootDirectory, rootPackageJson, (packageJson) => getDependencySpec(packageJson, "expo"));
|
|
33557
33556
|
const SHOPIFY_FLASH_LIST_PACKAGE_NAME = "@shopify/flash-list";
|
|
33558
|
-
const
|
|
33559
|
-
const spec = packageJson.dependencies?.["@shopify/flash-list"] ?? packageJson.devDependencies?.["@shopify/flash-list"] ?? packageJson.peerDependencies?.["@shopify/flash-list"] ?? packageJson.optionalDependencies?.["@shopify/flash-list"];
|
|
33560
|
-
return typeof spec === "string" ? spec : null;
|
|
33561
|
-
};
|
|
33562
|
-
const findShopifyFlashListVersion = (rootDirectory, rootPackageJson) => findInWorkspacePackageJsons(rootDirectory, rootPackageJson, getShopifyFlashListDependencySpec);
|
|
33557
|
+
const findShopifyFlashListVersion = (rootDirectory, rootPackageJson) => findInWorkspacePackageJsons(rootDirectory, rootPackageJson, (packageJson) => getDependencySpec(packageJson, SHOPIFY_FLASH_LIST_PACKAGE_NAME));
|
|
33563
33558
|
const resolveCatalogBackedDependencyVersion = ({ rootDirectory, rootPackageJson, packageName, version }) => {
|
|
33564
33559
|
if (version === null || !isCatalogReference(version)) return version;
|
|
33565
33560
|
const catalogName = extractCatalogName(version);
|
|
@@ -33571,11 +33566,7 @@ const resolveCatalogBackedDependencyVersion = ({ rootDirectory, rootPackageJson,
|
|
|
33571
33566
|
if (!isFile(monorepoPackageJsonPath)) return version;
|
|
33572
33567
|
return resolveCatalogVersion(readPackageJson(monorepoPackageJsonPath), packageName, monorepoRoot, catalogName) ?? version;
|
|
33573
33568
|
};
|
|
33574
|
-
const
|
|
33575
|
-
const spec = packageJson.dependencies?.next ?? packageJson.devDependencies?.next ?? packageJson.peerDependencies?.next ?? packageJson.optionalDependencies?.next;
|
|
33576
|
-
return typeof spec === "string" ? spec : null;
|
|
33577
|
-
};
|
|
33578
|
-
const findNextjsVersion = (rootDirectory, rootPackageJson) => findInWorkspacePackageJsons(rootDirectory, rootPackageJson, getNextjsDependencySpec);
|
|
33569
|
+
const findNextjsVersion = (rootDirectory, rootPackageJson) => findInWorkspacePackageJsons(rootDirectory, rootPackageJson, (packageJson) => getDependencySpec(packageJson, "next"));
|
|
33579
33570
|
const getPreactVersion = (packageJson) => {
|
|
33580
33571
|
return {
|
|
33581
33572
|
...packageJson.peerDependencies,
|
|
@@ -33657,6 +33648,11 @@ const ES_TARGET_YEAR_BY_NAME = {
|
|
|
33657
33648
|
esnext: 9999
|
|
33658
33649
|
};
|
|
33659
33650
|
/**
|
|
33651
|
+
* tsconfig filenames probed when resolving a project's TypeScript
|
|
33652
|
+
* compiler options — the root config first, then a monorepo base config.
|
|
33653
|
+
*/
|
|
33654
|
+
const TSCONFIG_FILENAMES = ["tsconfig.json", "tsconfig.base.json"];
|
|
33655
|
+
/**
|
|
33660
33656
|
* Project-config files that `StagedFiles.materialize` copies into
|
|
33661
33657
|
* the temp directory alongside staged sources so oxlint resolves
|
|
33662
33658
|
* `tsconfig` / `package.json` / lint configs the same way it would
|
|
@@ -33726,6 +33722,13 @@ const APP_ONLY_RULE_KEYS = new Set([
|
|
|
33726
33722
|
]);
|
|
33727
33723
|
const COMPILER_CLEANUP_BUCKET = "compiler-cleanup";
|
|
33728
33724
|
const COMPILER_CLEANUP_RULE_KEYS = new Set(["react-doctor/react-compiler-no-manual-memoization"]);
|
|
33725
|
+
const ROOT_CAUSE_GROUPABLE_RULE_KEYS = new Set([
|
|
33726
|
+
"react-doctor/no-derived-state",
|
|
33727
|
+
"react-doctor/no-derived-state-effect",
|
|
33728
|
+
"react-doctor/no-derived-useState",
|
|
33729
|
+
"react-doctor/no-adjust-state-on-prop-change",
|
|
33730
|
+
"react-doctor/no-reset-all-state-on-prop-change"
|
|
33731
|
+
]);
|
|
33729
33732
|
const MAX_GLOB_PATTERN_LENGTH_CHARS = 1024;
|
|
33730
33733
|
const CONFIG_CACHE_TTL_MS = 300 * 1e3;
|
|
33731
33734
|
const SOCKET_FREE_PURL_API_BASE = "https://firewall-api.socket.dev/purl";
|
|
@@ -34178,6 +34181,7 @@ const isTailwindAtLeast = (detected, required) => {
|
|
|
34178
34181
|
if (detected.major !== required.major) return detected.major > required.major;
|
|
34179
34182
|
return detected.minor >= required.minor;
|
|
34180
34183
|
};
|
|
34184
|
+
const messageFromUnknown = (error) => error instanceof Error ? error.message : String(error);
|
|
34181
34185
|
var InvalidGlobPatternError = class extends Error {
|
|
34182
34186
|
pattern;
|
|
34183
34187
|
reason;
|
|
@@ -34206,7 +34210,7 @@ const compileGlobPattern = (rawPattern) => {
|
|
|
34206
34210
|
try {
|
|
34207
34211
|
return import_picomatch.default.makeRe(normalizeGlobPattern(rawPattern), PICOMATCH_OPTIONS);
|
|
34208
34212
|
} catch (caughtError) {
|
|
34209
|
-
throw new InvalidGlobPatternError(rawPattern,
|
|
34213
|
+
throw new InvalidGlobPatternError(rawPattern, messageFromUnknown(caughtError));
|
|
34210
34214
|
}
|
|
34211
34215
|
};
|
|
34212
34216
|
const compileGlobPatternsLenient = (patterns, onInvalid) => {
|
|
@@ -34302,115 +34306,6 @@ const buildRuleSeverityControls = (config) => {
|
|
|
34302
34306
|
...config.buckets !== void 0 ? { buckets: config.buckets } : {}
|
|
34303
34307
|
};
|
|
34304
34308
|
};
|
|
34305
|
-
const JSX_OPENER_TAG_PATTERN = /<[A-Za-z][\w.]*/g;
|
|
34306
|
-
const JSX_TAG_NAME_FOLLOW = /[A-Za-z]/;
|
|
34307
|
-
const isOpenerMatchInsideLineComment = (line, openerCharIndex) => {
|
|
34308
|
-
let stringDelimiter = null;
|
|
34309
|
-
for (let charIndex = 0; charIndex < openerCharIndex; charIndex++) {
|
|
34310
|
-
const character = line[charIndex];
|
|
34311
|
-
if (stringDelimiter !== null) {
|
|
34312
|
-
if (character === "\\") {
|
|
34313
|
-
charIndex++;
|
|
34314
|
-
continue;
|
|
34315
|
-
}
|
|
34316
|
-
if (character === stringDelimiter) stringDelimiter = null;
|
|
34317
|
-
continue;
|
|
34318
|
-
}
|
|
34319
|
-
if (character === "\"" || character === "'" || character === "`") {
|
|
34320
|
-
stringDelimiter = character;
|
|
34321
|
-
continue;
|
|
34322
|
-
}
|
|
34323
|
-
if (character === "/" && line[charIndex + 1] === "/") return true;
|
|
34324
|
-
}
|
|
34325
|
-
return false;
|
|
34326
|
-
};
|
|
34327
|
-
const findOpenerTagOnLine = (line) => {
|
|
34328
|
-
for (const match of line.matchAll(JSX_OPENER_TAG_PATTERN)) {
|
|
34329
|
-
if (match.index === void 0) continue;
|
|
34330
|
-
if (!isOpenerMatchInsideLineComment(line, match.index)) return { startCharIndex: match.index + match[0].length };
|
|
34331
|
-
}
|
|
34332
|
-
return null;
|
|
34333
|
-
};
|
|
34334
|
-
const findJsxOpenerSpan = (lines, openerLineIndex) => {
|
|
34335
|
-
const openerLine = lines[openerLineIndex];
|
|
34336
|
-
if (openerLine === void 0) return null;
|
|
34337
|
-
const opener = findOpenerTagOnLine(openerLine);
|
|
34338
|
-
if (!opener) return null;
|
|
34339
|
-
const lookaheadLimit = Math.min(lines.length, openerLineIndex + 32);
|
|
34340
|
-
let braceDepth = 0;
|
|
34341
|
-
let innerAngleDepth = 0;
|
|
34342
|
-
let stringDelimiter = null;
|
|
34343
|
-
for (let lineIndex = openerLineIndex; lineIndex < lookaheadLimit; lineIndex++) {
|
|
34344
|
-
const currentLine = lines[lineIndex];
|
|
34345
|
-
const startCharForLine = lineIndex === openerLineIndex ? opener.startCharIndex : 0;
|
|
34346
|
-
for (let charIndex = startCharForLine; charIndex < currentLine.length; charIndex++) {
|
|
34347
|
-
const character = currentLine[charIndex];
|
|
34348
|
-
if (stringDelimiter !== null) {
|
|
34349
|
-
if (character === "\\") {
|
|
34350
|
-
charIndex++;
|
|
34351
|
-
continue;
|
|
34352
|
-
}
|
|
34353
|
-
if (character === stringDelimiter) stringDelimiter = null;
|
|
34354
|
-
continue;
|
|
34355
|
-
}
|
|
34356
|
-
if (character === "\"" || character === "'" || character === "`") {
|
|
34357
|
-
stringDelimiter = character;
|
|
34358
|
-
continue;
|
|
34359
|
-
}
|
|
34360
|
-
if (character === "{") {
|
|
34361
|
-
braceDepth++;
|
|
34362
|
-
continue;
|
|
34363
|
-
}
|
|
34364
|
-
if (character === "}") {
|
|
34365
|
-
braceDepth--;
|
|
34366
|
-
continue;
|
|
34367
|
-
}
|
|
34368
|
-
if (braceDepth !== 0) continue;
|
|
34369
|
-
if (character === "<") {
|
|
34370
|
-
const followCharacter = currentLine[charIndex + 1];
|
|
34371
|
-
if (followCharacter !== void 0 && JSX_TAG_NAME_FOLLOW.test(followCharacter)) innerAngleDepth++;
|
|
34372
|
-
continue;
|
|
34373
|
-
}
|
|
34374
|
-
if (character !== ">") continue;
|
|
34375
|
-
const previousCharacter = currentLine[charIndex - 1];
|
|
34376
|
-
const nextCharacter = currentLine[charIndex + 1];
|
|
34377
|
-
if (previousCharacter === "=" || nextCharacter === "=") continue;
|
|
34378
|
-
if (innerAngleDepth > 0) {
|
|
34379
|
-
innerAngleDepth--;
|
|
34380
|
-
continue;
|
|
34381
|
-
}
|
|
34382
|
-
return lineIndex;
|
|
34383
|
-
}
|
|
34384
|
-
}
|
|
34385
|
-
return null;
|
|
34386
|
-
};
|
|
34387
|
-
const findEnclosingMultilineJsxOpenerStart = (lines, diagnosticLineIndex) => {
|
|
34388
|
-
for (let candidateIndex = diagnosticLineIndex - 1; candidateIndex >= 0 && diagnosticLineIndex - candidateIndex <= 32; candidateIndex--) {
|
|
34389
|
-
const openerCloseIndex = findJsxOpenerSpan(lines, candidateIndex);
|
|
34390
|
-
if (openerCloseIndex !== null && openerCloseIndex >= diagnosticLineIndex) return candidateIndex;
|
|
34391
|
-
}
|
|
34392
|
-
return null;
|
|
34393
|
-
};
|
|
34394
|
-
const DISABLE_NEXT_LINE_PATTERN = /(?:\/\/|\/\*)\s*react-doctor-disable-next-line\b(?:\s+([^\r\n]*?))?\s*(?:\*\/)?\s*\}?\s*$/;
|
|
34395
|
-
const findStackedDisableCommentsAbove = (lines, anchorIndex) => {
|
|
34396
|
-
const collected = [];
|
|
34397
|
-
let isStillInChain = true;
|
|
34398
|
-
for (let candidateIndex = anchorIndex - 1; candidateIndex >= 0 && anchorIndex - candidateIndex <= 10; candidateIndex--) {
|
|
34399
|
-
const candidateLine = lines[candidateIndex];
|
|
34400
|
-
if (candidateLine === void 0) break;
|
|
34401
|
-
const match = candidateLine.match(DISABLE_NEXT_LINE_PATTERN);
|
|
34402
|
-
if (match) {
|
|
34403
|
-
collected.push({
|
|
34404
|
-
commentLineIndex: candidateIndex,
|
|
34405
|
-
ruleList: match[1],
|
|
34406
|
-
isInChain: isStillInChain
|
|
34407
|
-
});
|
|
34408
|
-
continue;
|
|
34409
|
-
}
|
|
34410
|
-
isStillInChain = false;
|
|
34411
|
-
}
|
|
34412
|
-
return collected;
|
|
34413
|
-
};
|
|
34414
34309
|
const LEGACY_RULE_KEY_TO_NATIVE_RULE_KEY = {
|
|
34415
34310
|
"effect/no-adjust-state-on-prop-change": "react-doctor/no-adjust-state-on-prop-change",
|
|
34416
34311
|
"effect/no-chain-state-updates": "react-doctor/no-chain-state-updates",
|
|
@@ -34535,7 +34430,13 @@ for (const [legacyRuleKey, nativeRuleKey] of Object.entries(LEGACY_RULE_KEY_TO_N
|
|
|
34535
34430
|
}
|
|
34536
34431
|
const getLegacyRuleKeysForNative = (ruleKey) => NATIVE_RULE_KEY_TO_LEGACY_RULE_KEYS.get(ruleKey) ?? [];
|
|
34537
34432
|
const canonicalizeRuleKey = (ruleKey) => LEGACY_RULE_KEY_TO_NATIVE_RULE_KEY[ruleKey] ?? ruleKey;
|
|
34538
|
-
const
|
|
34433
|
+
const isReactDoctorShortIdOf = (bareRuleKey, qualifiedRuleKey) => !bareRuleKey.includes("/") && qualifiedRuleKey === `react-doctor/${bareRuleKey}`;
|
|
34434
|
+
const isSameRuleKey = (candidateRuleKey, targetRuleKey) => {
|
|
34435
|
+
const canonicalCandidate = canonicalizeRuleKey(candidateRuleKey);
|
|
34436
|
+
const canonicalTarget = canonicalizeRuleKey(targetRuleKey);
|
|
34437
|
+
if (canonicalCandidate === canonicalTarget) return true;
|
|
34438
|
+
return isReactDoctorShortIdOf(canonicalCandidate, canonicalTarget) || isReactDoctorShortIdOf(canonicalTarget, canonicalCandidate);
|
|
34439
|
+
};
|
|
34539
34440
|
const getEquivalentRuleKeys = (ruleKey) => {
|
|
34540
34441
|
const nativeRuleKey = canonicalizeRuleKey(ruleKey);
|
|
34541
34442
|
return [nativeRuleKey, ...getLegacyRuleKeysForNative(nativeRuleKey)];
|
|
@@ -34545,12 +34446,182 @@ const stripDescriptionTail = (ruleList) => {
|
|
|
34545
34446
|
if (!descriptionMatch || descriptionMatch.index === void 0) return ruleList;
|
|
34546
34447
|
return ruleList.slice(0, descriptionMatch.index);
|
|
34547
34448
|
};
|
|
34548
|
-
const
|
|
34449
|
+
const tokenizeRuleList = (ruleList) => {
|
|
34549
34450
|
const trimmed = ruleList?.trim();
|
|
34550
|
-
if (!trimmed) return
|
|
34451
|
+
if (!trimmed) return [];
|
|
34551
34452
|
const ruleSection = stripDescriptionTail(trimmed).trim();
|
|
34552
|
-
if (!ruleSection) return
|
|
34553
|
-
return ruleSection.split(/[,\s]+/).
|
|
34453
|
+
if (!ruleSection) return [];
|
|
34454
|
+
return ruleSection.split(/[,\s]+/).map((token) => token.trim()).filter(Boolean);
|
|
34455
|
+
};
|
|
34456
|
+
const FOREIGN_INLINE_DISABLE_PATTERN = /(?:\/\/|\/\*)[ \t]*(eslint|oxlint)-disable-(next-line|line)(?![\w-])([^\r\n]*)/;
|
|
34457
|
+
const FOREIGN_BLOCK_DISABLE_PATTERN = /\/\*[ \t]*(eslint|oxlint)-disable(?![\w-])([^*\r\n]*)/;
|
|
34458
|
+
const FOREIGN_BLOCK_ENABLE_PATTERN = /\/\*[ \t]*(?:eslint|oxlint)-enable(?![\w-])([^*\r\n]*)/;
|
|
34459
|
+
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}\` — change it to \`${ruleId}\`.`;
|
|
34460
|
+
const tokenMisnamesRule = (token, ruleId) => token !== ruleId && isSameRuleKey(token, ruleId);
|
|
34461
|
+
const detectInlineNearMiss = (lines, diagnosticLineIndex, ruleId) => {
|
|
34462
|
+
const candidates = [{
|
|
34463
|
+
line: lines[diagnosticLineIndex],
|
|
34464
|
+
requiredScope: "line"
|
|
34465
|
+
}, {
|
|
34466
|
+
line: lines[diagnosticLineIndex - 1],
|
|
34467
|
+
requiredScope: "next-line"
|
|
34468
|
+
}];
|
|
34469
|
+
for (const { line, requiredScope } of candidates) {
|
|
34470
|
+
const match = line?.match(FOREIGN_INLINE_DISABLE_PATTERN);
|
|
34471
|
+
if (!match) continue;
|
|
34472
|
+
const [, tool, scope, ruleList] = match;
|
|
34473
|
+
if (scope !== requiredScope) continue;
|
|
34474
|
+
const tokens = tokenizeRuleList(ruleList);
|
|
34475
|
+
if (tokens.includes(ruleId)) continue;
|
|
34476
|
+
for (const token of tokens) if (tokenMisnamesRule(token, ruleId)) return buildHint(tool, token, ruleId);
|
|
34477
|
+
}
|
|
34478
|
+
return null;
|
|
34479
|
+
};
|
|
34480
|
+
const detectBlockNearMiss = (lines, diagnosticLineIndex, ruleId) => {
|
|
34481
|
+
let openMisname = null;
|
|
34482
|
+
const lastLineIndex = Math.min(diagnosticLineIndex, lines.length - 1);
|
|
34483
|
+
for (let lineIndex = 0; lineIndex <= lastLineIndex; lineIndex++) {
|
|
34484
|
+
const line = lines[lineIndex];
|
|
34485
|
+
if (line === void 0 || !line.includes("-disable") && !line.includes("-enable")) continue;
|
|
34486
|
+
const disableMatch = line.match(FOREIGN_BLOCK_DISABLE_PATTERN);
|
|
34487
|
+
if (disableMatch) {
|
|
34488
|
+
const [, tool, ruleList] = disableMatch;
|
|
34489
|
+
const tokens = tokenizeRuleList(ruleList);
|
|
34490
|
+
if (tokens.includes(ruleId)) openMisname = null;
|
|
34491
|
+
else {
|
|
34492
|
+
const misnamed = tokens.find((token) => tokenMisnamesRule(token, ruleId));
|
|
34493
|
+
if (misnamed) openMisname = {
|
|
34494
|
+
tool,
|
|
34495
|
+
token: misnamed
|
|
34496
|
+
};
|
|
34497
|
+
}
|
|
34498
|
+
continue;
|
|
34499
|
+
}
|
|
34500
|
+
const enableMatch = line.match(FOREIGN_BLOCK_ENABLE_PATTERN);
|
|
34501
|
+
if (enableMatch) {
|
|
34502
|
+
const enabledRules = tokenizeRuleList(enableMatch[1]);
|
|
34503
|
+
if (enabledRules.length === 0 || enabledRules.some((rule) => isSameRuleKey(rule, ruleId))) openMisname = null;
|
|
34504
|
+
}
|
|
34505
|
+
}
|
|
34506
|
+
return openMisname ? buildHint(openMisname.tool, openMisname.token, ruleId) : null;
|
|
34507
|
+
};
|
|
34508
|
+
const detectForeignDisableNearMiss = (lines, diagnosticLineIndex, ruleId) => {
|
|
34509
|
+
if (!ruleId.startsWith("react-doctor/")) return null;
|
|
34510
|
+
return detectInlineNearMiss(lines, diagnosticLineIndex, ruleId) ?? detectBlockNearMiss(lines, diagnosticLineIndex, ruleId);
|
|
34511
|
+
};
|
|
34512
|
+
const JSX_OPENER_TAG_PATTERN = /<[A-Za-z][\w.]*/g;
|
|
34513
|
+
const JSX_TAG_NAME_FOLLOW = /[A-Za-z]/;
|
|
34514
|
+
const isOpenerMatchInsideLineComment = (line, openerCharIndex) => {
|
|
34515
|
+
let stringDelimiter = null;
|
|
34516
|
+
for (let charIndex = 0; charIndex < openerCharIndex; charIndex++) {
|
|
34517
|
+
const character = line[charIndex];
|
|
34518
|
+
if (stringDelimiter !== null) {
|
|
34519
|
+
if (character === "\\") {
|
|
34520
|
+
charIndex++;
|
|
34521
|
+
continue;
|
|
34522
|
+
}
|
|
34523
|
+
if (character === stringDelimiter) stringDelimiter = null;
|
|
34524
|
+
continue;
|
|
34525
|
+
}
|
|
34526
|
+
if (character === "\"" || character === "'" || character === "`") {
|
|
34527
|
+
stringDelimiter = character;
|
|
34528
|
+
continue;
|
|
34529
|
+
}
|
|
34530
|
+
if (character === "/" && line[charIndex + 1] === "/") return true;
|
|
34531
|
+
}
|
|
34532
|
+
return false;
|
|
34533
|
+
};
|
|
34534
|
+
const findOpenerTagOnLine = (line) => {
|
|
34535
|
+
for (const match of line.matchAll(JSX_OPENER_TAG_PATTERN)) {
|
|
34536
|
+
if (match.index === void 0) continue;
|
|
34537
|
+
if (!isOpenerMatchInsideLineComment(line, match.index)) return { startCharIndex: match.index + match[0].length };
|
|
34538
|
+
}
|
|
34539
|
+
return null;
|
|
34540
|
+
};
|
|
34541
|
+
const findJsxOpenerSpan = (lines, openerLineIndex) => {
|
|
34542
|
+
const openerLine = lines[openerLineIndex];
|
|
34543
|
+
if (openerLine === void 0) return null;
|
|
34544
|
+
const opener = findOpenerTagOnLine(openerLine);
|
|
34545
|
+
if (!opener) return null;
|
|
34546
|
+
const lookaheadLimit = Math.min(lines.length, openerLineIndex + 32);
|
|
34547
|
+
let braceDepth = 0;
|
|
34548
|
+
let innerAngleDepth = 0;
|
|
34549
|
+
let stringDelimiter = null;
|
|
34550
|
+
for (let lineIndex = openerLineIndex; lineIndex < lookaheadLimit; lineIndex++) {
|
|
34551
|
+
const currentLine = lines[lineIndex];
|
|
34552
|
+
const startCharForLine = lineIndex === openerLineIndex ? opener.startCharIndex : 0;
|
|
34553
|
+
for (let charIndex = startCharForLine; charIndex < currentLine.length; charIndex++) {
|
|
34554
|
+
const character = currentLine[charIndex];
|
|
34555
|
+
if (stringDelimiter !== null) {
|
|
34556
|
+
if (character === "\\") {
|
|
34557
|
+
charIndex++;
|
|
34558
|
+
continue;
|
|
34559
|
+
}
|
|
34560
|
+
if (character === stringDelimiter) stringDelimiter = null;
|
|
34561
|
+
continue;
|
|
34562
|
+
}
|
|
34563
|
+
if (character === "\"" || character === "'" || character === "`") {
|
|
34564
|
+
stringDelimiter = character;
|
|
34565
|
+
continue;
|
|
34566
|
+
}
|
|
34567
|
+
if (character === "{") {
|
|
34568
|
+
braceDepth++;
|
|
34569
|
+
continue;
|
|
34570
|
+
}
|
|
34571
|
+
if (character === "}") {
|
|
34572
|
+
braceDepth--;
|
|
34573
|
+
continue;
|
|
34574
|
+
}
|
|
34575
|
+
if (braceDepth !== 0) continue;
|
|
34576
|
+
if (character === "<") {
|
|
34577
|
+
const followCharacter = currentLine[charIndex + 1];
|
|
34578
|
+
if (followCharacter !== void 0 && JSX_TAG_NAME_FOLLOW.test(followCharacter)) innerAngleDepth++;
|
|
34579
|
+
continue;
|
|
34580
|
+
}
|
|
34581
|
+
if (character !== ">") continue;
|
|
34582
|
+
const previousCharacter = currentLine[charIndex - 1];
|
|
34583
|
+
const nextCharacter = currentLine[charIndex + 1];
|
|
34584
|
+
if (previousCharacter === "=" || nextCharacter === "=") continue;
|
|
34585
|
+
if (innerAngleDepth > 0) {
|
|
34586
|
+
innerAngleDepth--;
|
|
34587
|
+
continue;
|
|
34588
|
+
}
|
|
34589
|
+
return lineIndex;
|
|
34590
|
+
}
|
|
34591
|
+
}
|
|
34592
|
+
return null;
|
|
34593
|
+
};
|
|
34594
|
+
const findEnclosingMultilineJsxOpenerStart = (lines, diagnosticLineIndex) => {
|
|
34595
|
+
for (let candidateIndex = diagnosticLineIndex - 1; candidateIndex >= 0 && diagnosticLineIndex - candidateIndex <= 32; candidateIndex--) {
|
|
34596
|
+
const openerCloseIndex = findJsxOpenerSpan(lines, candidateIndex);
|
|
34597
|
+
if (openerCloseIndex !== null && openerCloseIndex >= diagnosticLineIndex) return candidateIndex;
|
|
34598
|
+
}
|
|
34599
|
+
return null;
|
|
34600
|
+
};
|
|
34601
|
+
const DISABLE_NEXT_LINE_PATTERN = /(?:\/\/|\/\*)\s*react-doctor-disable-next-line\b(?:\s+([^\r\n]*?))?\s*(?:\*\/)?\s*\}?\s*$/;
|
|
34602
|
+
const findStackedDisableCommentsAbove = (lines, anchorIndex) => {
|
|
34603
|
+
const collected = [];
|
|
34604
|
+
let isStillInChain = true;
|
|
34605
|
+
for (let candidateIndex = anchorIndex - 1; candidateIndex >= 0 && anchorIndex - candidateIndex <= 10; candidateIndex--) {
|
|
34606
|
+
const candidateLine = lines[candidateIndex];
|
|
34607
|
+
if (candidateLine === void 0) break;
|
|
34608
|
+
const match = candidateLine.match(DISABLE_NEXT_LINE_PATTERN);
|
|
34609
|
+
if (match) {
|
|
34610
|
+
collected.push({
|
|
34611
|
+
commentLineIndex: candidateIndex,
|
|
34612
|
+
ruleList: match[1],
|
|
34613
|
+
isInChain: isStillInChain
|
|
34614
|
+
});
|
|
34615
|
+
continue;
|
|
34616
|
+
}
|
|
34617
|
+
isStillInChain = false;
|
|
34618
|
+
}
|
|
34619
|
+
return collected;
|
|
34620
|
+
};
|
|
34621
|
+
const isRuleListedInComment = (ruleList, ruleId) => {
|
|
34622
|
+
const tokens = tokenizeRuleList(ruleList);
|
|
34623
|
+
if (tokens.length === 0) return true;
|
|
34624
|
+
return tokens.some((token) => isSameRuleKey(token, ruleId));
|
|
34554
34625
|
};
|
|
34555
34626
|
const DISABLE_LINE_PATTERN = /(?:\/\/|\/\*)\s*react-doctor-disable-line\b(?:\s+([^\r\n]*?))?\s*(?:\*\/)?\s*\}?\s*$/;
|
|
34556
34627
|
const formatLineGap = (gapLineCount) => `${gapLineCount} line${gapLineCount === 1 ? "" : "s"}`;
|
|
@@ -34594,7 +34665,7 @@ const evaluateSuppression = (lines, diagnosticLineIndex, ruleId) => {
|
|
|
34594
34665
|
};
|
|
34595
34666
|
return {
|
|
34596
34667
|
isSuppressed: false,
|
|
34597
|
-
nearMissHint: classifyFromComments([directComments, openerComments], diagnosticLineIndex, ruleId)
|
|
34668
|
+
nearMissHint: classifyFromComments([directComments, openerComments], diagnosticLineIndex, ruleId) ?? detectForeignDisableNearMiss(lines, diagnosticLineIndex, ruleId)
|
|
34598
34669
|
};
|
|
34599
34670
|
};
|
|
34600
34671
|
/**
|
|
@@ -35362,7 +35433,6 @@ const PACKAGE_JSON_FILENAME = "package.json";
|
|
|
35362
35433
|
const PACKAGE_JSON_CONFIG_KEY = "reactDoctor";
|
|
35363
35434
|
const LEGACY_CONFIG_FILENAME = "react-doctor.config.json";
|
|
35364
35435
|
const jiti = createJiti(import.meta.url);
|
|
35365
|
-
const formatError = (error) => error instanceof Error ? error.message : String(error);
|
|
35366
35436
|
const importDefaultExport = async (jitiInstance, filePath) => {
|
|
35367
35437
|
const imported = await jitiInstance.import(filePath);
|
|
35368
35438
|
return imported?.default ?? imported;
|
|
@@ -35394,7 +35464,7 @@ const loadModuleConfig = async (filePath) => {
|
|
|
35394
35464
|
try {
|
|
35395
35465
|
return await importDefaultExport(aliasJiti, filePath);
|
|
35396
35466
|
} catch (retryError) {
|
|
35397
|
-
throw new Error(`${
|
|
35467
|
+
throw new Error(`${messageFromUnknown(error)} (retry with ${SELF_PACKAGE_IMPORT_SPECIFIER} aliased to the running react-doctor package also failed: ${messageFromUnknown(retryError)})`, { cause: retryError });
|
|
35398
35468
|
}
|
|
35399
35469
|
}
|
|
35400
35470
|
};
|
|
@@ -35443,7 +35513,7 @@ const loadLegacyConfig = (directory) => {
|
|
|
35443
35513
|
}
|
|
35444
35514
|
warn(`${LEGACY_CONFIG_FILENAME} must contain an object, ignoring.`);
|
|
35445
35515
|
} catch (error) {
|
|
35446
|
-
warn(`Failed to load ${LEGACY_CONFIG_FILENAME}: ${
|
|
35516
|
+
warn(`Failed to load ${LEGACY_CONFIG_FILENAME}: ${messageFromUnknown(error)}`);
|
|
35447
35517
|
}
|
|
35448
35518
|
return {
|
|
35449
35519
|
status: "invalid",
|
|
@@ -35470,7 +35540,7 @@ const loadConfigFromDirectory = async (directory) => {
|
|
|
35470
35540
|
warn(`${CONFIG_BASENAME}.${extension} must export an object, ignoring.`);
|
|
35471
35541
|
sawBrokenConfigFile = true;
|
|
35472
35542
|
} catch (error) {
|
|
35473
|
-
warn(`Failed to load ${CONFIG_BASENAME}.${extension}: ${
|
|
35543
|
+
warn(`Failed to load ${CONFIG_BASENAME}.${extension}: ${messageFromUnknown(error)}`);
|
|
35474
35544
|
sawBrokenConfigFile = true;
|
|
35475
35545
|
}
|
|
35476
35546
|
}
|
|
@@ -35524,6 +35594,29 @@ const resolveConfigRootDir = (config, configSourceDirectory) => {
|
|
|
35524
35594
|
}
|
|
35525
35595
|
return resolvedRootDir;
|
|
35526
35596
|
};
|
|
35597
|
+
const buildFixGroupId = (diagnostic) => createHash("sha1").update(JSON.stringify([
|
|
35598
|
+
diagnostic.filePath,
|
|
35599
|
+
`${diagnostic.plugin}/${diagnostic.rule}`,
|
|
35600
|
+
diagnostic.message
|
|
35601
|
+
])).digest("hex").slice(0, 16);
|
|
35602
|
+
const isGroupableRule = (diagnostic) => ROOT_CAUSE_GROUPABLE_RULE_KEYS.has(`${diagnostic.plugin}/${diagnostic.rule}`);
|
|
35603
|
+
const assignFixGroups = (diagnostics) => {
|
|
35604
|
+
const siteCountByGroupId = /* @__PURE__ */ new Map();
|
|
35605
|
+
for (const diagnostic of diagnostics) {
|
|
35606
|
+
if (!isGroupableRule(diagnostic)) continue;
|
|
35607
|
+
const groupId = buildFixGroupId(diagnostic);
|
|
35608
|
+
siteCountByGroupId.set(groupId, (siteCountByGroupId.get(groupId) ?? 0) + 1);
|
|
35609
|
+
}
|
|
35610
|
+
return diagnostics.map((diagnostic) => {
|
|
35611
|
+
if (!isGroupableRule(diagnostic)) return diagnostic;
|
|
35612
|
+
const groupId = buildFixGroupId(diagnostic);
|
|
35613
|
+
if ((siteCountByGroupId.get(groupId) ?? 0) < 2) return diagnostic;
|
|
35614
|
+
return {
|
|
35615
|
+
...diagnostic,
|
|
35616
|
+
fixGroupId: groupId
|
|
35617
|
+
};
|
|
35618
|
+
});
|
|
35619
|
+
};
|
|
35527
35620
|
const getDirectDependencyNames = (packageJson) => new Set([...Object.keys(packageJson.dependencies ?? {}), ...Object.keys(packageJson.devDependencies ?? {})]);
|
|
35528
35621
|
const buildExpoCheckContext = (rootDirectory, expoVersion) => {
|
|
35529
35622
|
const packageJson = readPackageJson(Path.join(rootDirectory, "package.json"));
|
|
@@ -36030,10 +36123,15 @@ const buildHardeningDiagnostic = (input) => ({
|
|
|
36030
36123
|
column: input.column ?? 0,
|
|
36031
36124
|
category: "Security"
|
|
36032
36125
|
});
|
|
36033
|
-
const checkPnpmHardening = (
|
|
36034
|
-
if (!isPnpmManagedProject(
|
|
36035
|
-
const workspacePath = Path.join(
|
|
36036
|
-
const
|
|
36126
|
+
const checkPnpmHardening = (scanDirectory) => {
|
|
36127
|
+
if (!isPnpmManagedProject(scanDirectory)) return [];
|
|
36128
|
+
const workspacePath = Path.join(scanDirectory, PNPM_WORKSPACE_FILE);
|
|
36129
|
+
const hasWorkspaceFile = isFile(workspacePath);
|
|
36130
|
+
if (!hasWorkspaceFile) {
|
|
36131
|
+
const monorepoRoot = findMonorepoRoot(scanDirectory);
|
|
36132
|
+
if (monorepoRoot !== null && isFile(Path.join(monorepoRoot, PNPM_WORKSPACE_FILE))) return [];
|
|
36133
|
+
}
|
|
36134
|
+
const settings = parseHardeningSettings(hasWorkspaceFile ? NFS.readFileSync(workspacePath, "utf-8") : "");
|
|
36037
36135
|
const diagnostics = [];
|
|
36038
36136
|
if (settings.minimumReleaseAge === null) diagnostics.push(buildHardeningDiagnostic({
|
|
36039
36137
|
message: "pnpm-workspace.yaml is missing `minimumReleaseAge` — newly published versions can ship malware that gets caught and unpublished within hours",
|
|
@@ -36650,7 +36748,7 @@ const readIgnoreFile = (filePath) => {
|
|
|
36650
36748
|
try {
|
|
36651
36749
|
content = NFS.readFileSync(filePath, "utf-8");
|
|
36652
36750
|
} catch (error) {
|
|
36653
|
-
const errnoCode = error
|
|
36751
|
+
const errnoCode = isErrnoException(error) ? error.code : void 0;
|
|
36654
36752
|
if (errnoCode && errnoCode !== "ENOENT") runSync(warn$1(`Could not read ignore file ${filePath}: ${errnoCode}`));
|
|
36655
36753
|
return [];
|
|
36656
36754
|
}
|
|
@@ -36691,8 +36789,8 @@ const collectIgnorePatterns = (rootDirectory) => {
|
|
|
36691
36789
|
cachedPatternsByRoot.set(rootDirectory, patterns);
|
|
36692
36790
|
return patterns;
|
|
36693
36791
|
};
|
|
36792
|
+
const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
36694
36793
|
const KNIP_JSON_FILENAME = "knip.json";
|
|
36695
|
-
const isRecord$1 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
36696
36794
|
const readJsonFileSafe = (filePath) => {
|
|
36697
36795
|
let rawContents;
|
|
36698
36796
|
try {
|
|
@@ -36708,10 +36806,10 @@ const readJsonFileSafe = (filePath) => {
|
|
|
36708
36806
|
};
|
|
36709
36807
|
const readKnipConfig = (rootDirectory) => {
|
|
36710
36808
|
const knipJson = readJsonFileSafe(path.join(rootDirectory, KNIP_JSON_FILENAME));
|
|
36711
|
-
if (isRecord
|
|
36809
|
+
if (isRecord(knipJson)) return knipJson;
|
|
36712
36810
|
const packageJson = readJsonFileSafe(path.join(rootDirectory, "package.json"));
|
|
36713
|
-
const packageKnipConfig = isRecord
|
|
36714
|
-
return isRecord
|
|
36811
|
+
const packageKnipConfig = isRecord(packageJson) ? packageJson.knip : null;
|
|
36812
|
+
return isRecord(packageKnipConfig) ? packageKnipConfig : null;
|
|
36715
36813
|
};
|
|
36716
36814
|
const normalizePatternList = (value) => {
|
|
36717
36815
|
if (typeof value === "string" && value.length > 0) return [value];
|
|
@@ -36723,10 +36821,10 @@ const prefixWorkspacePatterns = (workspacePattern, patterns) => {
|
|
|
36723
36821
|
return patterns.map((pattern) => pattern.startsWith("!") ? `!${normalizedWorkspacePattern}/${pattern.slice(1)}` : `${normalizedWorkspacePattern}/${pattern}`);
|
|
36724
36822
|
};
|
|
36725
36823
|
const collectKnipWorkspacePatterns = (workspaces, settingName) => {
|
|
36726
|
-
if (!isRecord
|
|
36824
|
+
if (!isRecord(workspaces)) return [];
|
|
36727
36825
|
const patterns = [];
|
|
36728
36826
|
for (const [workspacePattern, workspaceConfig] of Object.entries(workspaces)) {
|
|
36729
|
-
if (!isRecord
|
|
36827
|
+
if (!isRecord(workspaceConfig)) continue;
|
|
36730
36828
|
patterns.push(...prefixWorkspacePatterns(workspacePattern, normalizePatternList(workspaceConfig[settingName])));
|
|
36731
36829
|
}
|
|
36732
36830
|
return patterns;
|
|
@@ -36736,12 +36834,11 @@ const collectKnipPatterns = (rootDirectory, settingName) => {
|
|
|
36736
36834
|
if (!config) return [];
|
|
36737
36835
|
return [...normalizePatternList(config[settingName]), ...collectKnipWorkspacePatterns(config.workspaces, settingName)];
|
|
36738
36836
|
};
|
|
36739
|
-
const collectDeadCodeIgnorePatterns = (rootDirectory
|
|
36837
|
+
const collectDeadCodeIgnorePatterns = (rootDirectory) => {
|
|
36740
36838
|
const seen = /* @__PURE__ */ new Set();
|
|
36741
36839
|
const sources = [
|
|
36742
36840
|
readIgnoreFile(path.join(rootDirectory, ".gitignore")),
|
|
36743
36841
|
collectIgnorePatterns(rootDirectory),
|
|
36744
|
-
userConfig?.ignore?.files ?? [],
|
|
36745
36842
|
collectKnipPatterns(rootDirectory, "ignore")
|
|
36746
36843
|
];
|
|
36747
36844
|
for (const source of sources) for (const pattern of source) seen.add(pattern);
|
|
@@ -36772,8 +36869,6 @@ const toCanonicalPath = (filePath) => {
|
|
|
36772
36869
|
};
|
|
36773
36870
|
const DEAD_CODE_PLUGIN = "deslop";
|
|
36774
36871
|
const DEAD_CODE_CATEGORY = "Maintainability";
|
|
36775
|
-
const TSCONFIG_FILENAMES$1 = ["tsconfig.json", "tsconfig.base.json"];
|
|
36776
|
-
const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
36777
36872
|
const DEAD_CODE_WORKER_SCRIPT = `
|
|
36778
36873
|
const inputChunks = [];
|
|
36779
36874
|
process.stdin.on("data", (chunk) => inputChunks.push(chunk));
|
|
@@ -36831,7 +36926,7 @@ process.stdin.on("end", () => {
|
|
|
36831
36926
|
});
|
|
36832
36927
|
`;
|
|
36833
36928
|
const resolveTsConfigPath = (rootDirectory) => {
|
|
36834
|
-
for (const filename of TSCONFIG_FILENAMES
|
|
36929
|
+
for (const filename of TSCONFIG_FILENAMES) {
|
|
36835
36930
|
const candidate = Path.join(rootDirectory, filename);
|
|
36836
36931
|
if (NFS.existsSync(candidate)) return candidate;
|
|
36837
36932
|
}
|
|
@@ -37019,11 +37114,10 @@ const runDeadCodeWorkerWithTimeout = (handle, timeoutMs) => new Promise((resolve
|
|
|
37019
37114
|
});
|
|
37020
37115
|
});
|
|
37021
37116
|
const checkDeadCode = async (options) => {
|
|
37022
|
-
const { userConfig } = options;
|
|
37023
37117
|
const rootDirectory = toCanonicalPath(options.rootDirectory);
|
|
37024
37118
|
if (!NFS.existsSync(Path.join(rootDirectory, "package.json"))) return [];
|
|
37025
37119
|
const entryPatterns = collectDeadCodeEntryPatterns(rootDirectory);
|
|
37026
|
-
const ignorePatterns = collectDeadCodeIgnorePatterns(rootDirectory
|
|
37120
|
+
const ignorePatterns = collectDeadCodeIgnorePatterns(rootDirectory);
|
|
37027
37121
|
const result = parseDeadCodeWorkerResult(await runDeadCodeWorkerWithTimeout((options.createWorker ?? createDeadCodeWorker)({
|
|
37028
37122
|
rootDirectory,
|
|
37029
37123
|
entryPatterns,
|
|
@@ -37213,15 +37307,13 @@ var DeadCode = class DeadCode extends Service()("react-doctor/DeadCode") {
|
|
|
37213
37307
|
})()) }));
|
|
37214
37308
|
static layerOf = (diagnostics) => succeed$3(DeadCode, DeadCode.of({ run: () => fromIterable$1(diagnostics) }));
|
|
37215
37309
|
};
|
|
37216
|
-
const createNodeReadFileLinesSync = (rootDirectory) => {
|
|
37217
|
-
|
|
37218
|
-
|
|
37219
|
-
|
|
37220
|
-
|
|
37221
|
-
|
|
37222
|
-
|
|
37223
|
-
}
|
|
37224
|
-
};
|
|
37310
|
+
const createNodeReadFileLinesSync = (rootDirectory) => (filePath) => {
|
|
37311
|
+
const absolutePath = Path.isAbsolute(filePath) ? filePath : Path.join(rootDirectory, filePath);
|
|
37312
|
+
try {
|
|
37313
|
+
return NFS.readFileSync(absolutePath, "utf-8").split("\n");
|
|
37314
|
+
} catch {
|
|
37315
|
+
return null;
|
|
37316
|
+
}
|
|
37225
37317
|
};
|
|
37226
37318
|
var Files = class Files extends Service()("react-doctor/Files") {
|
|
37227
37319
|
static layerNode = succeed$3(Files, Files.of({
|
|
@@ -37432,7 +37524,10 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
37432
37524
|
directory: input.directory,
|
|
37433
37525
|
cause
|
|
37434
37526
|
}) });
|
|
37435
|
-
})
|
|
37527
|
+
}), withSpan("git.exec", { attributes: {
|
|
37528
|
+
"git.command": input.command,
|
|
37529
|
+
"git.subcommand": input.args[0] ?? ""
|
|
37530
|
+
} }));
|
|
37436
37531
|
const runGit = (directory, args) => runCommand({
|
|
37437
37532
|
command: "git",
|
|
37438
37533
|
args,
|
|
@@ -37460,7 +37555,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
37460
37555
|
]);
|
|
37461
37556
|
if (candidates.status !== 0) return null;
|
|
37462
37557
|
return trimOrNull(candidates.stdout.split("\n")[0] ?? "");
|
|
37463
|
-
});
|
|
37558
|
+
}).pipe(withSpan("Git.defaultBranch"));
|
|
37464
37559
|
const branchExists = (directory, branch) => runGit(directory, [
|
|
37465
37560
|
"rev-parse",
|
|
37466
37561
|
"--verify",
|
|
@@ -37507,7 +37602,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
37507
37602
|
const result = resultOption.value;
|
|
37508
37603
|
if (result.status !== 0) return null;
|
|
37509
37604
|
return parseGithubViewerPermission(result.stdout);
|
|
37510
|
-
}).pipe(catch_$1(() => succeed$2(null)));
|
|
37605
|
+
}).pipe(catch_$1(() => succeed$2(null)), withSpan("Git.githubViewerPermission"));
|
|
37511
37606
|
/**
|
|
37512
37607
|
* Resolves a `--diff A..B` / `A...B` commit range into a changed-file
|
|
37513
37608
|
* selection. Each endpoint is validated with `isSafeGitRevision`
|
|
@@ -37621,7 +37716,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
37621
37716
|
changedFiles: splitNullSeparated(diff.stdout),
|
|
37622
37717
|
isCurrentChanges: false
|
|
37623
37718
|
};
|
|
37624
|
-
}),
|
|
37719
|
+
}).pipe(withSpan("Git.diffSelection")),
|
|
37625
37720
|
stagedFilePaths: (directory) => runGit(directory, [
|
|
37626
37721
|
"diff",
|
|
37627
37722
|
"--cached",
|
|
@@ -37663,7 +37758,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
37663
37758
|
status: result.status,
|
|
37664
37759
|
stdout: result.stdout
|
|
37665
37760
|
};
|
|
37666
|
-
}),
|
|
37761
|
+
}).pipe(withSpan("Git.grep")),
|
|
37667
37762
|
changedLineRanges: ({ directory, baseRef, cached, files }) => gen(function* () {
|
|
37668
37763
|
if (files.length === 0) return [];
|
|
37669
37764
|
if (baseRef !== void 0 && !isSafeGitRevision(baseRef)) return null;
|
|
@@ -37679,7 +37774,7 @@ var Git = class Git extends Service()("react-doctor/Git") {
|
|
|
37679
37774
|
]);
|
|
37680
37775
|
if (result.status !== 0) return null;
|
|
37681
37776
|
return parseChangedLineRanges(result.stdout);
|
|
37682
|
-
})
|
|
37777
|
+
}).pipe(withSpan("Git.changedLineRanges"))
|
|
37683
37778
|
});
|
|
37684
37779
|
})).pipe(provide$2(layer$2.pipe(provide$2(mergeAll$1(layer$1, layer)))));
|
|
37685
37780
|
/**
|
|
@@ -37894,7 +37989,7 @@ const neutralizeDisableDirectives = async (rootDirectory, includePaths) => {
|
|
|
37894
37989
|
for (const [absolutePath, originalContent] of originalContents) try {
|
|
37895
37990
|
NFS.writeFileSync(absolutePath, originalContent);
|
|
37896
37991
|
} catch (error) {
|
|
37897
|
-
process.stderr.write(`[react-doctor] Failed to restore inline disable directives in ${absolutePath}: ${
|
|
37992
|
+
process.stderr.write(`[react-doctor] Failed to restore inline disable directives in ${absolutePath}: ${messageFromUnknown(error)}\n[react-doctor] Run: git checkout -- ${absolutePath}\n`);
|
|
37898
37993
|
}
|
|
37899
37994
|
};
|
|
37900
37995
|
const onExit = () => restore();
|
|
@@ -38000,7 +38095,7 @@ const resolveUserPlugin = (spec, configSourceDirectory) => {
|
|
|
38000
38095
|
try {
|
|
38001
38096
|
resolvedSpecifier = isRelative ? Path.resolve(configSourceDirectory, spec) : candidateRequire.resolve(spec);
|
|
38002
38097
|
} catch (error) {
|
|
38003
|
-
warnConfigIssue(`config.plugins entry "${spec}" could not be resolved from ${configSourceDirectory}: ${
|
|
38098
|
+
warnConfigIssue(`config.plugins entry "${spec}" could not be resolved from ${configSourceDirectory}: ${messageFromUnknown(error)}`);
|
|
38004
38099
|
return null;
|
|
38005
38100
|
}
|
|
38006
38101
|
const { name, ruleNames } = readPluginShape(resolvedSpecifier, (target) => candidateRequire(target));
|
|
@@ -38072,8 +38167,8 @@ const buildUserPluginRules = (userPlugin, severityControls) => {
|
|
|
38072
38167
|
}
|
|
38073
38168
|
return enabled;
|
|
38074
38169
|
};
|
|
38075
|
-
const createOxlintConfig = ({ pluginPath, project, customRulesOnly = false, extendsPaths = [], ignoredTags = /* @__PURE__ */ new Set(), serverAuthFunctionNames, severityControls, userPlugins = [] }) => {
|
|
38076
|
-
const reactHooksJsPlugin = resolveReactHooksJsPlugin(project.hasReactCompiler, customRulesOnly);
|
|
38170
|
+
const createOxlintConfig = ({ pluginPath, project, customRulesOnly = false, extendsPaths = [], ignoredTags = /* @__PURE__ */ new Set(), serverAuthFunctionNames, severityControls, userPlugins = [], disableReactHooksJsPlugin = false }) => {
|
|
38171
|
+
const reactHooksJsPlugin = disableReactHooksJsPlugin ? null : resolveReactHooksJsPlugin(project.hasReactCompiler, customRulesOnly);
|
|
38077
38172
|
const reactCompilerRules = reactHooksJsPlugin ? applyRuleSeverityControls(filterRulesToAvailable(REACT_COMPILER_RULES, "react-hooks-js", reactHooksJsPlugin.availableRuleNames), severityControls) : {};
|
|
38078
38173
|
const jsPlugins = [];
|
|
38079
38174
|
if (reactHooksJsPlugin) jsPlugins.push(reactHooksJsPlugin.entry);
|
|
@@ -38133,7 +38228,6 @@ const resolveOxlintBinary = () => {
|
|
|
38133
38228
|
return Path.join(oxlintPackageDirectory, "bin", "oxlint");
|
|
38134
38229
|
};
|
|
38135
38230
|
const resolvePluginPath = () => esmRequire.resolve("oxlint-plugin-react-doctor");
|
|
38136
|
-
const TSCONFIG_FILENAMES = ["tsconfig.json", "tsconfig.base.json"];
|
|
38137
38231
|
const resolveTsConfigRelativePath = (rootDirectory) => {
|
|
38138
38232
|
for (const filename of TSCONFIG_FILENAMES) if (NFS.existsSync(Path.join(rootDirectory, filename))) return `./${filename}`;
|
|
38139
38233
|
return null;
|
|
@@ -38505,7 +38599,7 @@ const scopeContainsNonImportBinding = (node, scopeNode, identifierName) => {
|
|
|
38505
38599
|
const isIdentifierShadowedByLocalBinding = (identifier, sourceFile) => {
|
|
38506
38600
|
let currentNode = identifier.parent;
|
|
38507
38601
|
while (currentNode) {
|
|
38508
|
-
if (
|
|
38602
|
+
if (isScopeBoundary(currentNode)) {
|
|
38509
38603
|
if (scopeContainsNonImportBinding(currentNode, currentNode, identifier.text)) return true;
|
|
38510
38604
|
}
|
|
38511
38605
|
if (currentNode === sourceFile) return false;
|
|
@@ -38596,11 +38690,10 @@ const findResolutionInScope = (scopeNode, identifierName, reactImportBindings, s
|
|
|
38596
38690
|
});
|
|
38597
38691
|
return resolution;
|
|
38598
38692
|
};
|
|
38599
|
-
const isScopeNode = isScopeBoundary;
|
|
38600
38693
|
const resolveIdentifierBinding = (identifier, reactImportBindings, sourceFile, visitedDeclarations = /* @__PURE__ */ new Set()) => {
|
|
38601
38694
|
let currentNode = identifier.parent;
|
|
38602
38695
|
while (currentNode) {
|
|
38603
|
-
if (
|
|
38696
|
+
if (isScopeBoundary(currentNode)) {
|
|
38604
38697
|
const resolution = findResolutionInScope(currentNode, identifier.text, reactImportBindings, sourceFile, visitedDeclarations);
|
|
38605
38698
|
if (resolution) return resolution;
|
|
38606
38699
|
}
|
|
@@ -38770,9 +38863,9 @@ const parseOxlintOutput = (stdout, project, rootDirectory) => {
|
|
|
38770
38863
|
try {
|
|
38771
38864
|
parsed = JSON.parse(sanitizedStdout);
|
|
38772
38865
|
} catch {
|
|
38773
|
-
throw new ReactDoctorError({ reason: new OxlintOutputUnparseable({ preview: stdout.slice(0,
|
|
38866
|
+
throw new ReactDoctorError({ reason: new OxlintOutputUnparseable({ preview: stdout.slice(0, 600) }) });
|
|
38774
38867
|
}
|
|
38775
|
-
if (!isOxlintOutput(parsed)) throw new ReactDoctorError({ reason: new OxlintOutputUnparseable({ preview: stdout.slice(0,
|
|
38868
|
+
if (!isOxlintOutput(parsed)) throw new ReactDoctorError({ reason: new OxlintOutputUnparseable({ preview: stdout.slice(0, 600) }) });
|
|
38776
38869
|
const minifiedFileCache = /* @__PURE__ */ new Map();
|
|
38777
38870
|
const isMinifiedDiagnosticFile = (filename) => {
|
|
38778
38871
|
const absolutePath = Path.isAbsolute(filename) ? filename : Path.resolve(rootDirectory || ".", filename);
|
|
@@ -38848,7 +38941,7 @@ const spawnOxlint = (args, rootDirectory, nodeBinaryPath, spawnTimeoutMs = OXLIN
|
|
|
38848
38941
|
child.kill("SIGKILL");
|
|
38849
38942
|
reject(new ReactDoctorError({ reason: new OxlintBatchExceeded({
|
|
38850
38943
|
kind: "timeout",
|
|
38851
|
-
detail: `${spawnTimeoutMs /
|
|
38944
|
+
detail: `${spawnTimeoutMs / MILLISECONDS_PER_SECOND}s budget exceeded`
|
|
38852
38945
|
}) }));
|
|
38853
38946
|
}, spawnTimeoutMs);
|
|
38854
38947
|
timeoutHandle.unref?.();
|
|
@@ -39063,6 +39156,28 @@ const writeOxlintConfig = (configPath, configToWrite) => {
|
|
|
39063
39156
|
NFS.closeSync(fileHandle);
|
|
39064
39157
|
}
|
|
39065
39158
|
};
|
|
39159
|
+
const REACT_HOOKS_JS_DROP_PREFIX = "React Compiler rules (react-hooks-js/*) skipped — eslint-plugin-react-hooks failed to load in this environment";
|
|
39160
|
+
/**
|
|
39161
|
+
* Detects an oxlint config-load crash caused by the optional
|
|
39162
|
+
* `react-hooks-js` (eslint-plugin-react-hooks) React Compiler plugin and
|
|
39163
|
+
* builds the partial-failure note for it; returns `null` when the failure
|
|
39164
|
+
* was anything else.
|
|
39165
|
+
*
|
|
39166
|
+
* oxlint prints a framed error to stdout (not stderr) and exits non-zero
|
|
39167
|
+
* when a `jsPlugins` entry can't be imported; that non-JSON stdout
|
|
39168
|
+
* surfaces as `OxlintOutputUnparseable`. Because oxlint fails the WHOLE
|
|
39169
|
+
* config load on it, leaving the plugin in would drop every curated
|
|
39170
|
+
* react-doctor diagnostic too — so the caller retries with the plugin
|
|
39171
|
+
* stripped (issue #833). Both markers sit at the start of oxlint's
|
|
39172
|
+
* message, so they survive the `preview` slice even for deep pnpm paths.
|
|
39173
|
+
*/
|
|
39174
|
+
const reactHooksJsPluginDropNote = (error) => {
|
|
39175
|
+
if (!(error instanceof ReactDoctorError) || error.reason._tag !== "OxlintOutputUnparseable") return null;
|
|
39176
|
+
const { preview } = error.reason;
|
|
39177
|
+
if (!preview.includes("Failed to load JS plugin") || !preview.includes("eslint-plugin-react-hooks")) return null;
|
|
39178
|
+
const underlyingReason = preview.match(/Error:[^\n]*/)?.[0]?.trim();
|
|
39179
|
+
return `${REACT_HOOKS_JS_DROP_PREFIX}${underlyingReason ? `: ${underlyingReason}` : ""}. Other rules ran normally.`;
|
|
39180
|
+
};
|
|
39066
39181
|
/**
|
|
39067
39182
|
* The oxlint runner. Composed of three pieces in `runners/oxlint/`:
|
|
39068
39183
|
*
|
|
@@ -39090,15 +39205,16 @@ const runOxlint = async (options) => {
|
|
|
39090
39205
|
const pluginPath = resolvePluginPath();
|
|
39091
39206
|
const extendsPaths = (adoptExistingLintConfig && !customRulesOnly ? detectUserLintConfigPaths(rootDirectory) : []).filter(canOxlintExtendConfig);
|
|
39092
39207
|
const userPlugins = resolveUserPlugins(userConfig?.plugins, configSourceDirectory);
|
|
39093
|
-
const buildConfig = (
|
|
39208
|
+
const buildConfig = (overrides) => createOxlintConfig({
|
|
39094
39209
|
pluginPath,
|
|
39095
39210
|
project,
|
|
39096
39211
|
customRulesOnly,
|
|
39097
|
-
extendsPaths:
|
|
39212
|
+
extendsPaths: overrides.extendsPaths,
|
|
39098
39213
|
ignoredTags,
|
|
39099
39214
|
serverAuthFunctionNames,
|
|
39100
39215
|
severityControls,
|
|
39101
|
-
userPlugins
|
|
39216
|
+
userPlugins,
|
|
39217
|
+
disableReactHooksJsPlugin: overrides.disableReactHooksJsPlugin
|
|
39102
39218
|
});
|
|
39103
39219
|
const restoreDisableDirectives = respectInlineDisables ? () => {} : await neutralizeDisableDirectives(rootDirectory, includePaths);
|
|
39104
39220
|
const configDirectory = NFS.mkdtempSync(Path.join(os.tmpdir(), "react-doctor-oxlintrc-"));
|
|
@@ -39134,12 +39250,22 @@ const runOxlint = async (options) => {
|
|
|
39134
39250
|
outputMaxBytes,
|
|
39135
39251
|
concurrency: options.concurrency
|
|
39136
39252
|
});
|
|
39137
|
-
writeOxlintConfig(configPath, buildConfig(extendsPaths));
|
|
39253
|
+
writeOxlintConfig(configPath, buildConfig({ extendsPaths }));
|
|
39138
39254
|
try {
|
|
39139
39255
|
return await runBatches();
|
|
39140
39256
|
} catch (error) {
|
|
39257
|
+
const reactHooksJsDropNote = reactHooksJsPluginDropNote(error);
|
|
39258
|
+
if (reactHooksJsDropNote !== null) {
|
|
39259
|
+
writeOxlintConfig(configPath, buildConfig({
|
|
39260
|
+
extendsPaths,
|
|
39261
|
+
disableReactHooksJsPlugin: true
|
|
39262
|
+
}));
|
|
39263
|
+
const diagnostics = await runBatches();
|
|
39264
|
+
onPartialFailure?.(reactHooksJsDropNote);
|
|
39265
|
+
return diagnostics;
|
|
39266
|
+
}
|
|
39141
39267
|
if (extendsPaths.length === 0) throw error;
|
|
39142
|
-
writeOxlintConfig(configPath, buildConfig([]));
|
|
39268
|
+
writeOxlintConfig(configPath, buildConfig({ extendsPaths: [] }));
|
|
39143
39269
|
return await runBatches();
|
|
39144
39270
|
}
|
|
39145
39271
|
} finally {
|
|
@@ -39937,17 +40063,17 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
39937
40063
|
}))))))));
|
|
39938
40064
|
const deadCodeFailureState = yield* get$2(deadCodeFailure);
|
|
39939
40065
|
const scanElapsedMilliseconds = Date.now() - scanStartTime;
|
|
39940
|
-
const scanElapsedSeconds = (scanElapsedMilliseconds /
|
|
40066
|
+
const scanElapsedSeconds = (scanElapsedMilliseconds / MILLISECONDS_PER_SECOND).toFixed(1);
|
|
39941
40067
|
if (!lintFailureState.didFail) if (deadCodeFailureState.didFail) yield* scanProgress.fail(DEAD_CODE_FAIL_TEXT);
|
|
39942
40068
|
else if (input.suppressScanSummary) yield* scanProgress.stop();
|
|
39943
40069
|
else yield* scanProgress.succeed(`Scanned ${scannedFilesLabel} in ${scanElapsedSeconds}s${workerCountSuffix}`);
|
|
39944
40070
|
yield* reporterService.finalize;
|
|
39945
|
-
const finalDiagnostics = [
|
|
40071
|
+
const finalDiagnostics = assignFixGroups([
|
|
39946
40072
|
...envCollected,
|
|
39947
40073
|
...supplyChainCollected,
|
|
39948
40074
|
...lintCollected,
|
|
39949
40075
|
...deadCodeCollected
|
|
39950
|
-
];
|
|
40076
|
+
]);
|
|
39951
40077
|
const githubViewerPermission = yield* join(githubViewerPermissionFiber);
|
|
39952
40078
|
const scoreMetadata = {
|
|
39953
40079
|
...repo !== null ? { repo } : {},
|
|
@@ -40151,7 +40277,7 @@ const materializeSourceTree = (input) => gen(function* () {
|
|
|
40151
40277
|
static layerNode = effect(StagedFiles, gen(function* () {
|
|
40152
40278
|
const git = yield* Git;
|
|
40153
40279
|
return StagedFiles.of({
|
|
40154
|
-
discoverSourceFiles: (directory) => git.stagedFilePaths(directory).pipe(map$3((entries) => entries.filter(isLintableSourceFile))),
|
|
40280
|
+
discoverSourceFiles: (directory) => git.stagedFilePaths(directory).pipe(map$3((entries) => entries.filter(isLintableSourceFile)), withSpan("StagedFiles.discoverSourceFiles")),
|
|
40155
40281
|
materialize: ({ directory, stagedFiles, tempDirectory }) => materializeSourceTree({
|
|
40156
40282
|
directory,
|
|
40157
40283
|
files: stagedFiles,
|
|
@@ -40161,7 +40287,7 @@ const materializeSourceTree = (input) => gen(function* () {
|
|
|
40161
40287
|
tempDirectory: tree.tempDirectory,
|
|
40162
40288
|
stagedFiles: tree.materializedFiles,
|
|
40163
40289
|
cleanup: tree.cleanup
|
|
40164
|
-
})))
|
|
40290
|
+
})), withSpan("StagedFiles.materialize"))
|
|
40165
40291
|
});
|
|
40166
40292
|
}));
|
|
40167
40293
|
/**
|
|
@@ -40229,7 +40355,10 @@ const runEditorScan = async (input) => {
|
|
|
40229
40355
|
isCi: false,
|
|
40230
40356
|
resolveLocalGithubViewerPermission: false,
|
|
40231
40357
|
skipJsxIncludeFilter: true
|
|
40232
|
-
}).pipe(
|
|
40358
|
+
}).pipe(withSpan("runEditorScan", { attributes: {
|
|
40359
|
+
"editor.lint": lint,
|
|
40360
|
+
"editor.runDeadCode": runDeadCode
|
|
40361
|
+
} }), provide(layers), provide(layerOtlp)));
|
|
40233
40362
|
if (isSuccess(exit)) {
|
|
40234
40363
|
const output = exit.value;
|
|
40235
40364
|
return {
|
|
@@ -40259,7 +40388,7 @@ const runEditorScan = async (input) => {
|
|
|
40259
40388
|
didDeadCodeFail: false,
|
|
40260
40389
|
deadCodeFailureReason: null,
|
|
40261
40390
|
lintPartialFailures: [],
|
|
40262
|
-
error:
|
|
40391
|
+
error: messageFromUnknown(error)
|
|
40263
40392
|
};
|
|
40264
40393
|
};
|
|
40265
40394
|
/**
|
|
@@ -40311,7 +40440,7 @@ const computeConfigFingerprint = (projectDirectory, version) => {
|
|
|
40311
40440
|
/** Display name used in client-facing messages and progress titles. */
|
|
40312
40441
|
const SERVER_DISPLAY_NAME = "React Doctor";
|
|
40313
40442
|
/** Server version reported in `serverInfo`; injected at build, `dev` from source. */
|
|
40314
|
-
const SERVER_VERSION = "0.5.
|
|
40443
|
+
const SERVER_VERSION = "0.5.7";
|
|
40315
40444
|
/** `Diagnostic.source` shown next to every published diagnostic. */
|
|
40316
40445
|
const DIAGNOSTIC_SOURCE = "react-doctor";
|
|
40317
40446
|
/**
|
|
@@ -40541,7 +40670,6 @@ const toLspDiagnostic = (input) => {
|
|
|
40541
40670
|
data
|
|
40542
40671
|
};
|
|
40543
40672
|
};
|
|
40544
|
-
const toUri = (absoluteFilePath) => fsPathToUri(absoluteFilePath);
|
|
40545
40673
|
/**
|
|
40546
40674
|
* Owns the published-diagnostic state. Maps scan outcomes to LSP
|
|
40547
40675
|
* diagnostics, publishes complete per-URI replacement sets (so the
|
|
@@ -40571,7 +40699,7 @@ var DiagnosticsManager = class {
|
|
|
40571
40699
|
const isProtectedPath = (fsPath) => protectOpen && this.isOpen(fsPath);
|
|
40572
40700
|
for (const [fsPath, coreDiagnostics] of outcome.byFile) {
|
|
40573
40701
|
if (isProtectedPath(fsPath)) continue;
|
|
40574
|
-
const uri =
|
|
40702
|
+
const uri = fsPathToUri(fsPath);
|
|
40575
40703
|
const text = this.textProvider(fsPath);
|
|
40576
40704
|
const lspDiagnostics = coreDiagnostics.map((diagnostic) => toLspDiagnostic({
|
|
40577
40705
|
diagnostic,
|
|
@@ -40593,7 +40721,7 @@ var DiagnosticsManager = class {
|
|
|
40593
40721
|
for (const fsPath of outcome.requestedPaths) {
|
|
40594
40722
|
if (isProtectedPath(fsPath)) continue;
|
|
40595
40723
|
if (outcome.byFile.has(fsPath)) continue;
|
|
40596
|
-
const uri =
|
|
40724
|
+
const uri = fsPathToUri(fsPath);
|
|
40597
40725
|
if (this.byUri.has(uri)) this.byUri.delete(uri);
|
|
40598
40726
|
this.publish(uri, []);
|
|
40599
40727
|
}
|
|
@@ -40618,7 +40746,7 @@ var DiagnosticsManager = class {
|
|
|
40618
40746
|
const set = this.projectUris.get(project) ?? /* @__PURE__ */ new Set();
|
|
40619
40747
|
for (const uri of liveUris) set.add(uri);
|
|
40620
40748
|
for (const fsPath of outcome.requestedPaths) {
|
|
40621
|
-
const uri =
|
|
40749
|
+
const uri = fsPathToUri(fsPath);
|
|
40622
40750
|
if (!liveUris.has(uri)) set.delete(uri);
|
|
40623
40751
|
}
|
|
40624
40752
|
this.projectUris.set(project, set);
|
|
@@ -40646,7 +40774,7 @@ var DiagnosticsManager = class {
|
|
|
40646
40774
|
const tracked = this.projectUris.get(project);
|
|
40647
40775
|
if (!tracked) return;
|
|
40648
40776
|
const liveUris = /* @__PURE__ */ new Set();
|
|
40649
|
-
for (const fsPath of liveFsPaths) liveUris.add(
|
|
40777
|
+
for (const fsPath of liveFsPaths) liveUris.add(fsPathToUri(fsPath));
|
|
40650
40778
|
for (const uri of [...tracked]) {
|
|
40651
40779
|
if (liveUris.has(uri)) continue;
|
|
40652
40780
|
this.byUri.delete(uri);
|
|
@@ -40943,7 +41071,7 @@ const createProjectGraph = (options) => {
|
|
|
40943
41071
|
});
|
|
40944
41072
|
}
|
|
40945
41073
|
} catch (error) {
|
|
40946
|
-
logger.warn(`Project discovery failed for ${root}: ${
|
|
41074
|
+
logger.warn(`Project discovery failed for ${root}: ${messageFromUnknown(error)}`);
|
|
40947
41075
|
}
|
|
40948
41076
|
return [...seen.values()].sort((first, second) => second.directory.length - first.directory.length);
|
|
40949
41077
|
};
|
|
@@ -40971,6 +41099,11 @@ const createProjectGraph = (options) => {
|
|
|
40971
41099
|
}
|
|
40972
41100
|
};
|
|
40973
41101
|
};
|
|
41102
|
+
const toProjectRelative = (projectDirectory, filePath) => {
|
|
41103
|
+
const relative = Path.relative(projectDirectory, filePath).replace(/\\/g, "/");
|
|
41104
|
+
if (relative.length === 0 || relative.startsWith("../") || Path.isAbsolute(relative)) return null;
|
|
41105
|
+
return relative;
|
|
41106
|
+
};
|
|
40974
41107
|
const resolveCacheFilePath = (projectDirectory) => {
|
|
40975
41108
|
const nodeModules = path.join(projectDirectory, "node_modules");
|
|
40976
41109
|
if (fs.existsSync(nodeModules)) return path.join(nodeModules, ".cache", "react-doctor", "lint-cache.json");
|
|
@@ -41005,7 +41138,7 @@ const createLintCache = (input) => {
|
|
|
41005
41138
|
fs.writeFileSync(tempPath, JSON.stringify(payload));
|
|
41006
41139
|
fs.renameSync(tempPath, cacheFilePath);
|
|
41007
41140
|
} catch (error) {
|
|
41008
|
-
logger.warn(`Failed to persist lint cache: ${
|
|
41141
|
+
logger.warn(`Failed to persist lint cache: ${messageFromUnknown(error)}`);
|
|
41009
41142
|
}
|
|
41010
41143
|
};
|
|
41011
41144
|
return {
|
|
@@ -41031,11 +41164,6 @@ const createLintCache = (input) => {
|
|
|
41031
41164
|
};
|
|
41032
41165
|
const OVERLAY_TEMP_PREFIX = "react-doctor-lsp-";
|
|
41033
41166
|
const OVERLAY_CONFIG_FILENAMES = [...new Set([...STAGED_FILES_PROJECT_CONFIG_FILENAMES, ...ADOPTABLE_LINT_CONFIG_FILENAMES])];
|
|
41034
|
-
const toProjectRelative$1 = (projectDirectory, filePath) => {
|
|
41035
|
-
const relative = path.relative(projectDirectory, filePath).replace(/\\/g, "/");
|
|
41036
|
-
if (relative.length === 0 || relative.startsWith("../") || path.isAbsolute(relative)) return null;
|
|
41037
|
-
return relative;
|
|
41038
|
-
};
|
|
41039
41167
|
/**
|
|
41040
41168
|
* Writes the live (possibly unsaved) content of the target files into a
|
|
41041
41169
|
* throwaway temp tree that mirrors the project, alongside the well-known
|
|
@@ -41049,7 +41177,7 @@ const materializeOverlay = (input) => {
|
|
|
41049
41177
|
const relativePaths = [];
|
|
41050
41178
|
try {
|
|
41051
41179
|
for (const filePath of input.files) {
|
|
41052
|
-
const relative = toProjectRelative
|
|
41180
|
+
const relative = toProjectRelative(input.projectDirectory, filePath);
|
|
41053
41181
|
if (relative === null) continue;
|
|
41054
41182
|
const content = input.readText(filePath);
|
|
41055
41183
|
if (content === null) continue;
|
|
@@ -41097,11 +41225,6 @@ const materializeOverlay = (input) => {
|
|
|
41097
41225
|
throw error;
|
|
41098
41226
|
}
|
|
41099
41227
|
};
|
|
41100
|
-
const toProjectRelative = (projectDirectory, filePath) => {
|
|
41101
|
-
const relative = path.relative(projectDirectory, filePath).replace(/\\/g, "/");
|
|
41102
|
-
if (relative.length === 0 || relative.startsWith("../") || path.isAbsolute(relative)) return null;
|
|
41103
|
-
return relative;
|
|
41104
|
-
};
|
|
41105
41228
|
/**
|
|
41106
41229
|
* Resolves a diagnostic's (possibly relative, possibly overlay-temp)
|
|
41107
41230
|
* file path back to the canonical absolute path inside the real project.
|
|
@@ -41323,7 +41446,7 @@ const createScheduler = (options) => {
|
|
|
41323
41446
|
if (outcome && !token.isCancelled) options.onResult(outcome);
|
|
41324
41447
|
}).catch((error) => {
|
|
41325
41448
|
if (options.onError) options.onError(error, request);
|
|
41326
|
-
else logger.error(`Scan failed: ${
|
|
41449
|
+
else logger.error(`Scan failed: ${messageFromUnknown(error)}`);
|
|
41327
41450
|
}).finally(() => {
|
|
41328
41451
|
running -= 1;
|
|
41329
41452
|
if (isBackground) runningBackground -= 1;
|
|
@@ -41710,7 +41833,7 @@ const createServer = (connection, options = {}) => {
|
|
|
41710
41833
|
maybeWarnLintUnavailable(outcome);
|
|
41711
41834
|
if (outcome.request.priority === "background") scanTelemetry.accumulate(outcome);
|
|
41712
41835
|
},
|
|
41713
|
-
onError: (error, request) => logger.error(`Scan of ${request.projectDirectory} threw: ${
|
|
41836
|
+
onError: (error, request) => logger.error(`Scan of ${request.projectDirectory} threw: ${messageFromUnknown(error)}`),
|
|
41714
41837
|
onIdleChange: (idle) => {
|
|
41715
41838
|
setBusy(!idle);
|
|
41716
41839
|
if (idle) scanTelemetry.finish();
|
|
@@ -42358,5 +42481,5 @@ const startLanguageServer = () => {
|
|
|
42358
42481
|
};
|
|
42359
42482
|
//#endregion
|
|
42360
42483
|
export { startLanguageServer };
|
|
42361
|
-
!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]="
|
|
42362
|
-
//# debugId=
|
|
42484
|
+
!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]="d139c7ae-128c-5b80-90a7-3ae52c407bf7")}catch(e){}}();
|
|
42485
|
+
//# debugId=d139c7ae-128c-5b80-90a7-3ae52c407bf7
|