react-doctor 0.7.5 → 0.7.6-dev.0150fc9
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 +116 -38
- package/dist/index.d.ts +7 -6
- package/dist/index.js +100 -31
- package/dist/lsp.js +120 -58
- package/package.json +6 -6
package/dist/cli.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="
|
|
2
|
+
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="9a5c1a3c-7706-5a23-bca4-ddddd2f085af")}catch(e){}}();
|
|
3
3
|
import { createRequire } from "node:module";
|
|
4
4
|
import * as NodeChildProcess from "node:child_process";
|
|
5
5
|
import { execFile, execFileSync, spawn, spawnSync } from "node:child_process";
|
|
@@ -36241,7 +36241,7 @@ const SOCKET_FREE_USER_AGENT = "react-doctor-supply-chain";
|
|
|
36241
36241
|
const FILE_LINT_CACHE_FILENAME = "file-lint-cache.json";
|
|
36242
36242
|
const FILE_LINT_CACHE_MAX_FILE_COUNT = 5e4;
|
|
36243
36243
|
const SIDECAR_LINT_CACHE_FILENAME = "sidecar-lint-cache.json";
|
|
36244
|
-
const CORE_PACKAGE_VERSION = "0.7.
|
|
36244
|
+
const CORE_PACKAGE_VERSION = "0.7.6";
|
|
36245
36245
|
const DEAD_CODE_CACHE_FILENAME = "dead-code-cache.json";
|
|
36246
36246
|
const DEAD_CODE_SUMMARY_CACHE_FILENAME = "dead-code-summaries.json";
|
|
36247
36247
|
const SUPPLY_CHAIN_PLUGIN = "socket";
|
|
@@ -36341,10 +36341,12 @@ const listSourceFilesViaFilesystem = (rootDirectory) => {
|
|
|
36341
36341
|
const listSourceFilesWithSize = (rootDirectory) => collectSizedSourceFiles(rootDirectory, (listSourceFilesViaGit(rootDirectory) ?? listSourceFilesViaFilesystem(rootDirectory)).sort());
|
|
36342
36342
|
const listSourceFiles = (rootDirectory) => listSourceFilesWithSize(rootDirectory).map((entry) => entry.path);
|
|
36343
36343
|
const countSourceFiles = (rootDirectory) => listSourceFilesWithSize(rootDirectory).length;
|
|
36344
|
+
const UTF8_BOM_CHARACTER = "";
|
|
36345
|
+
const stripUtf8Bom = (content) => content.startsWith(UTF8_BOM_CHARACTER) ? content.slice(1) : content;
|
|
36344
36346
|
const cachedPackageJsons = /* @__PURE__ */ new Map();
|
|
36345
36347
|
const readPackageJsonUncached = (packageJsonPath) => {
|
|
36346
36348
|
try {
|
|
36347
|
-
return JSON.parse(NFS.readFileSync(packageJsonPath, "utf-8"));
|
|
36349
|
+
return JSON.parse(stripUtf8Bom(NFS.readFileSync(packageJsonPath, "utf-8")));
|
|
36348
36350
|
} catch (error) {
|
|
36349
36351
|
if (error instanceof SyntaxError) return {};
|
|
36350
36352
|
if (isErrnoException(error)) {
|
|
@@ -40017,7 +40019,6 @@ const PNPM_WORKSPACE_FILE = "pnpm-workspace.yaml";
|
|
|
40017
40019
|
const PNPM_LOCKFILE = "pnpm-lock.yaml";
|
|
40018
40020
|
const PACKAGE_JSON_FILE = "package.json";
|
|
40019
40021
|
const PNPM_HARDENING_RULE_KEY = "require-pnpm-hardening";
|
|
40020
|
-
const UTF8_BOM_CHAR = "";
|
|
40021
40022
|
const HARDENING_SETTING_KEYS = new Set([
|
|
40022
40023
|
"minimumReleaseAge",
|
|
40023
40024
|
"blockExoticSubdeps",
|
|
@@ -40042,12 +40043,11 @@ const stripInlineComment = (rawValue) => {
|
|
|
40042
40043
|
return rawValue;
|
|
40043
40044
|
};
|
|
40044
40045
|
const unquote = (rawValue) => rawValue.replace(/^["']|["']$/g, "");
|
|
40045
|
-
const stripBom = (rawContent) => rawContent.startsWith(UTF8_BOM_CHAR) ? rawContent.slice(1) : rawContent;
|
|
40046
40046
|
const parseHardeningSettings = (content) => {
|
|
40047
40047
|
let minimumReleaseAge = null;
|
|
40048
40048
|
let blockExoticSubdeps = null;
|
|
40049
40049
|
let trustPolicy = null;
|
|
40050
|
-
const lines =
|
|
40050
|
+
const lines = stripUtf8Bom(content).split(/\r?\n/);
|
|
40051
40051
|
for (let lineIndex = 0; lineIndex < lines.length; lineIndex += 1) {
|
|
40052
40052
|
const lineText = lines[lineIndex];
|
|
40053
40053
|
if (lineText === void 0) continue;
|
|
@@ -40081,7 +40081,7 @@ const isPnpmManagedProject = (rootDirectory) => {
|
|
|
40081
40081
|
const packageJsonPath = Path.join(rootDirectory, PACKAGE_JSON_FILE);
|
|
40082
40082
|
if (!isFile(packageJsonPath)) return false;
|
|
40083
40083
|
try {
|
|
40084
|
-
const packageJsonRaw = NFS.readFileSync(packageJsonPath, "utf-8");
|
|
40084
|
+
const packageJsonRaw = stripUtf8Bom(NFS.readFileSync(packageJsonPath, "utf-8"));
|
|
40085
40085
|
const packageJson = JSON.parse(packageJsonRaw);
|
|
40086
40086
|
if (packageJson !== null && typeof packageJson === "object" && "packageManager" in packageJson && typeof packageJson.packageManager === "string" && packageJson.packageManager.startsWith("pnpm@")) return true;
|
|
40087
40087
|
} catch {
|
|
@@ -40762,9 +40762,7 @@ const setColorEnabled = (enabled) => {
|
|
|
40762
40762
|
highlighter.gray = colors.gray;
|
|
40763
40763
|
highlighter.bold = colors.bold;
|
|
40764
40764
|
};
|
|
40765
|
-
const
|
|
40766
|
-
const shouldIncludeExplicitLintPath = (filePath, project) => JSX_FILE_PATTERN.test(filePath) || project?.framework === "nextjs" && NEXT_ENTRY_FILE_PATTERN.test(filePath);
|
|
40767
|
-
const computeExplicitLintIncludePaths = (includePaths, project) => includePaths.length > 0 ? includePaths.filter((filePath) => shouldIncludeExplicitLintPath(filePath, project)) : void 0;
|
|
40765
|
+
const computeExplicitLintIncludePaths = (includePaths) => includePaths.length > 0 ? includePaths.filter(isLintableSourceFile) : void 0;
|
|
40768
40766
|
const LINGUIST_ATTRIBUTE_PATTERN = /^linguist-(?:vendored|generated)(?:=([a-zA-Z0-9]+))?$/i;
|
|
40769
40767
|
const FALSY_VALUES = new Set([
|
|
40770
40768
|
"false",
|
|
@@ -41603,11 +41601,11 @@ const resolveDeadCodeTimeout = (input) => {
|
|
|
41603
41601
|
phaseTimeoutMs: workerTimeoutMs + DEAD_CODE_PHASE_TIMEOUT_OVER_WORKER_MS
|
|
41604
41602
|
};
|
|
41605
41603
|
};
|
|
41606
|
-
const resolveLintIncludePaths = (rootDirectory, userConfig
|
|
41604
|
+
const resolveLintIncludePaths = (rootDirectory, userConfig) => {
|
|
41607
41605
|
if (!Array.isArray(userConfig?.ignore?.files) || userConfig.ignore.files.length === 0) return;
|
|
41608
41606
|
const ignoredPatterns = compileIgnoredFilePatterns(userConfig);
|
|
41609
41607
|
return listSourceFiles(rootDirectory).filter((filePath) => {
|
|
41610
|
-
if (!
|
|
41608
|
+
if (!isLintableSourceFile(filePath)) return false;
|
|
41611
41609
|
return !isFileIgnoredByPatterns(filePath, rootDirectory, ignoredPatterns);
|
|
41612
41610
|
});
|
|
41613
41611
|
};
|
|
@@ -42368,7 +42366,7 @@ const normalizeConfigForHash = (config) => {
|
|
|
42368
42366
|
if (Array.isArray(clone?.jsPlugins)) clone.jsPlugins = clone.jsPlugins.map((_, index) => `<plugin:${index}>`);
|
|
42369
42367
|
return clone;
|
|
42370
42368
|
};
|
|
42371
|
-
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");
|
|
42369
|
+
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 ?? "").update("\0").update(input.respectInlineDisables ? "respect-inline-disables" : "audit-mode").digest("hex");
|
|
42372
42370
|
/**
|
|
42373
42371
|
* Loads a plugin module via the local require resolver and extracts
|
|
42374
42372
|
* `(name, ruleNames)` from either `module.exports.meta + rules` or
|
|
@@ -42404,7 +42402,7 @@ const resolveReactHooksJsPlugin = (hasReactCompiler, customRulesOnly) => {
|
|
|
42404
42402
|
} catch {
|
|
42405
42403
|
return null;
|
|
42406
42404
|
}
|
|
42407
|
-
const { ruleNames } = readPluginShape(pluginSpecifier, (
|
|
42405
|
+
const { ruleNames } = readPluginShape(pluginSpecifier, (specifier) => bundledRequire$1(specifier));
|
|
42408
42406
|
return {
|
|
42409
42407
|
entry: {
|
|
42410
42408
|
name: "react-hooks-js",
|
|
@@ -42812,6 +42810,23 @@ const resolveTsConfigRelativePath = (rootDirectory) => {
|
|
|
42812
42810
|
for (const filename of TSCONFIG_FILENAMES) if (NFS.existsSync(Path.join(rootDirectory, filename))) return `./${filename}`;
|
|
42813
42811
|
return null;
|
|
42814
42812
|
};
|
|
42813
|
+
const DERIVED_STATE_RULE_PRIORITY = new Map([
|
|
42814
|
+
["no-initialize-state", 0],
|
|
42815
|
+
["no-adjust-state-on-prop-change", 1],
|
|
42816
|
+
["no-derived-state", 2],
|
|
42817
|
+
["no-derived-state-effect", 3]
|
|
42818
|
+
]);
|
|
42819
|
+
const doDiagnosticSpansOverlap = (first, second) => {
|
|
42820
|
+
if (first.filePath !== second.filePath || first.plugin !== second.plugin) return false;
|
|
42821
|
+
if (first.offset !== void 0 && second.offset !== void 0) {
|
|
42822
|
+
const firstEnd = first.offset + Math.max(first.length ?? 1, 1);
|
|
42823
|
+
const secondEnd = second.offset + Math.max(second.length ?? 1, 1);
|
|
42824
|
+
return first.offset < secondEnd && second.offset < firstEnd;
|
|
42825
|
+
}
|
|
42826
|
+
const firstEndLine = first.endLine ?? first.line;
|
|
42827
|
+
const secondEndLine = second.endLine ?? second.line;
|
|
42828
|
+
return first.line <= secondEndLine && second.line <= firstEndLine;
|
|
42829
|
+
};
|
|
42815
42830
|
const dedupeDiagnostics = (diagnostics) => {
|
|
42816
42831
|
const seenKeys = /* @__PURE__ */ new Set();
|
|
42817
42832
|
const uniqueDiagnostics = [];
|
|
@@ -42819,6 +42834,16 @@ const dedupeDiagnostics = (diagnostics) => {
|
|
|
42819
42834
|
const key = `${diagnostic.filePath}\u0000${diagnostic.line}\u0000${diagnostic.column}\u0000${diagnostic.plugin}\u0000${diagnostic.rule}\u0000${diagnostic.severity}\u0000${diagnostic.message}`;
|
|
42820
42835
|
if (seenKeys.has(key)) continue;
|
|
42821
42836
|
seenKeys.add(key);
|
|
42837
|
+
const priority = DERIVED_STATE_RULE_PRIORITY.get(diagnostic.rule);
|
|
42838
|
+
if (priority !== void 0) {
|
|
42839
|
+
const overlappingDiagnosticIndex = uniqueDiagnostics.findIndex((candidate) => candidate.rule !== diagnostic.rule && candidate.severity === diagnostic.severity && DERIVED_STATE_RULE_PRIORITY.has(candidate.rule) && doDiagnosticSpansOverlap(candidate, diagnostic));
|
|
42840
|
+
if (overlappingDiagnosticIndex >= 0) {
|
|
42841
|
+
const existingDiagnostic = uniqueDiagnostics[overlappingDiagnosticIndex];
|
|
42842
|
+
const existingPriority = existingDiagnostic ? DERIVED_STATE_RULE_PRIORITY.get(existingDiagnostic.rule) : void 0;
|
|
42843
|
+
if (existingPriority !== void 0 && priority < existingPriority) uniqueDiagnostics[overlappingDiagnosticIndex] = diagnostic;
|
|
42844
|
+
continue;
|
|
42845
|
+
}
|
|
42846
|
+
}
|
|
42822
42847
|
uniqueDiagnostics.push(diagnostic);
|
|
42823
42848
|
}
|
|
42824
42849
|
return uniqueDiagnostics;
|
|
@@ -42861,6 +42886,12 @@ const mapWithConcurrency = async (items, concurrency, task) => {
|
|
|
42861
42886
|
if (errors.length > 0) throw errors[0];
|
|
42862
42887
|
return results;
|
|
42863
42888
|
};
|
|
42889
|
+
const lineOfUtf8Offset = (sourceBuffer, utf8Offset) => {
|
|
42890
|
+
let lineNumber = 1;
|
|
42891
|
+
const scanEnd = Math.min(utf8Offset, sourceBuffer.length);
|
|
42892
|
+
for (let byteIndex = 0; byteIndex < scanEnd; byteIndex++) if (sourceBuffer[byteIndex] === 10) lineNumber++;
|
|
42893
|
+
return lineNumber;
|
|
42894
|
+
};
|
|
42864
42895
|
const REANIMATED_SHARED_VALUE_HINT = "If this is a Reanimated shared value, prefer its React Compiler-compatible `.get()` / `.set()` accessors over `.value` — https://docs.swmansion.com/react-native-reanimated/docs/core/useSharedValue/#react-compiler-support";
|
|
42865
42896
|
const ACCESSORS_MIN_VERSION = "3.15.0";
|
|
42866
42897
|
const supportsAccessors = (reanimatedVersion) => {
|
|
@@ -43432,13 +43463,6 @@ const findOutermostEnclosingFunctionRange = (sourceFile, targetLine) => {
|
|
|
43432
43463
|
visit(sourceFile);
|
|
43433
43464
|
return foundRange;
|
|
43434
43465
|
};
|
|
43435
|
-
const NEWLINE_BYTE = 10;
|
|
43436
|
-
const lineOfUtf8Offset = (sourceBuffer, utf8Offset) => {
|
|
43437
|
-
let lineNumber = 1;
|
|
43438
|
-
const scanEnd = Math.min(utf8Offset, sourceBuffer.length);
|
|
43439
|
-
for (let byteIndex = 0; byteIndex < scanEnd; byteIndex++) if (sourceBuffer[byteIndex] === NEWLINE_BYTE) lineNumber++;
|
|
43440
|
-
return lineNumber;
|
|
43441
|
-
};
|
|
43442
43466
|
/**
|
|
43443
43467
|
* Drops `react-doctor/react-compiler-no-manual-memoization` diagnostics
|
|
43444
43468
|
* whose enclosing function also carries a `react-hooks-js` (React
|
|
@@ -43643,8 +43667,33 @@ const parseOxlintOutput = (stdout, project, rootDirectory) => {
|
|
|
43643
43667
|
}
|
|
43644
43668
|
if (!isOxlintOutput(parsed)) throw new ReactDoctorError({ reason: new OxlintOutputUnparseable({ preview: stdout.slice(0, 600) }) });
|
|
43645
43669
|
const minifiedFileCache = /* @__PURE__ */ new Map();
|
|
43670
|
+
const sourceBufferCache = /* @__PURE__ */ new Map();
|
|
43671
|
+
const sourceLinesCache = /* @__PURE__ */ new Map();
|
|
43672
|
+
const resolveAbsolutePath = (filename) => Path.isAbsolute(filename) ? filename : Path.resolve(rootDirectory || ".", filename);
|
|
43673
|
+
const readSourceBuffer = (filename) => {
|
|
43674
|
+
const absolutePath = resolveAbsolutePath(filename);
|
|
43675
|
+
const cached = sourceBufferCache.get(absolutePath);
|
|
43676
|
+
if (cached !== void 0) return cached;
|
|
43677
|
+
let sourceBuffer;
|
|
43678
|
+
try {
|
|
43679
|
+
sourceBuffer = NFS.readFileSync(absolutePath);
|
|
43680
|
+
} catch {
|
|
43681
|
+
sourceBuffer = null;
|
|
43682
|
+
}
|
|
43683
|
+
sourceBufferCache.set(absolutePath, sourceBuffer);
|
|
43684
|
+
return sourceBuffer;
|
|
43685
|
+
};
|
|
43686
|
+
const readSourceLines = (filename) => {
|
|
43687
|
+
const absolutePath = resolveAbsolutePath(filename);
|
|
43688
|
+
const cached = sourceLinesCache.get(absolutePath);
|
|
43689
|
+
if (cached !== void 0) return cached;
|
|
43690
|
+
const sourceBuffer = readSourceBuffer(filename);
|
|
43691
|
+
const sourceLines = sourceBuffer ? sourceBuffer.toString("utf8").split("\n") : null;
|
|
43692
|
+
sourceLinesCache.set(absolutePath, sourceLines);
|
|
43693
|
+
return sourceLines;
|
|
43694
|
+
};
|
|
43646
43695
|
const isMinifiedDiagnosticFile = (filename) => {
|
|
43647
|
-
const absolutePath =
|
|
43696
|
+
const absolutePath = resolveAbsolutePath(filename);
|
|
43648
43697
|
const cached = minifiedFileCache.get(absolutePath);
|
|
43649
43698
|
if (cached !== void 0) return cached;
|
|
43650
43699
|
const minified = isMinifiedSource(absolutePath);
|
|
@@ -43657,8 +43706,17 @@ const parseOxlintOutput = (stdout, project, rootDirectory) => {
|
|
|
43657
43706
|
const cleaned = cleanDiagnosticMessage(diagnostic.message, diagnostic.help, plugin, rule, project);
|
|
43658
43707
|
const normalizedFilePath = diagnostic.filename.replaceAll("\\", "/");
|
|
43659
43708
|
const primarySpan = primaryLabel?.span;
|
|
43709
|
+
const sourceBuffer = primarySpan ? readSourceBuffer(diagnostic.filename) : null;
|
|
43660
43710
|
const relatedLocations = buildRelatedLocations(diagnostic.labels, normalizedFilePath);
|
|
43661
43711
|
const category = resolveDiagnosticCategory(plugin, rule);
|
|
43712
|
+
const matchByOccurrence = resolveMatchByOccurrence(rule, category);
|
|
43713
|
+
const sourceLines = primarySpan ? readSourceLines(diagnostic.filename) : null;
|
|
43714
|
+
const primaryLineIndex = primarySpan ? primarySpan.line - 1 : -1;
|
|
43715
|
+
const primaryLine = sourceLines?.[primaryLineIndex];
|
|
43716
|
+
const primaryColumnIndex = primarySpan ? primarySpan.column - 1 : -1;
|
|
43717
|
+
const isJsxTagLabel = primaryLine !== void 0 && primaryColumnIndex >= 0 && (primaryLine[primaryColumnIndex] === "<" || primaryLine[primaryColumnIndex - 1] === "<");
|
|
43718
|
+
const jsxOpenerEndLineIndex = sourceLines && isJsxTagLabel ? findJsxOpenerSpan(sourceLines, primaryLineIndex) : null;
|
|
43719
|
+
const primarySpanEndLine = jsxOpenerEndLineIndex !== null ? jsxOpenerEndLineIndex + 1 : primarySpan && sourceBuffer ? lineOfUtf8Offset(sourceBuffer, primarySpan.offset + Math.max(primarySpan.length - 1, 0)) : void 0;
|
|
43662
43720
|
return {
|
|
43663
43721
|
filePath: normalizedFilePath,
|
|
43664
43722
|
plugin,
|
|
@@ -43672,10 +43730,11 @@ const parseOxlintOutput = (stdout, project, rootDirectory) => {
|
|
|
43672
43730
|
column: primarySpan?.column ?? 0,
|
|
43673
43731
|
...primarySpan ? {
|
|
43674
43732
|
offset: primarySpan.offset,
|
|
43675
|
-
length: primarySpan.length
|
|
43733
|
+
length: primarySpan.length,
|
|
43734
|
+
...primarySpanEndLine !== void 0 ? { endLine: primarySpanEndLine } : {}
|
|
43676
43735
|
} : {},
|
|
43677
43736
|
category,
|
|
43678
|
-
...
|
|
43737
|
+
...matchByOccurrence ? { matchByOccurrence: true } : {},
|
|
43679
43738
|
...relatedLocations.length > 0 ? { relatedLocations } : {}
|
|
43680
43739
|
};
|
|
43681
43740
|
}), rootDirectory);
|
|
@@ -43692,6 +43751,11 @@ const buildOxlintChildEnv = (sourceEnv) => {
|
|
|
43692
43751
|
if (!isCompileCacheDisabled && !isCompileCacheAlreadySet) childEnv.NODE_COMPILE_CACHE = Path.join(os.tmpdir(), NODE_COMPILE_CACHE_DIR_NAME);
|
|
43693
43752
|
return childEnv;
|
|
43694
43753
|
};
|
|
43754
|
+
const buildProfiledNodeArguments = (input) => [
|
|
43755
|
+
...input.cpuProfileDirectory ? ["--cpu-prof", `--cpu-prof-dir=${input.cpuProfileDirectory}`] : [],
|
|
43756
|
+
...input.heapProfileDirectory ? ["--heap-prof", `--heap-prof-dir=${input.heapProfileDirectory}`] : [],
|
|
43757
|
+
...input.argumentsList
|
|
43758
|
+
];
|
|
43695
43759
|
const SANITIZED_ENV = buildOxlintChildEnv(process.env);
|
|
43696
43760
|
/**
|
|
43697
43761
|
* Spawn one oxlint subprocess with hard ceilings on wall time and
|
|
@@ -43716,7 +43780,11 @@ const spawnOxlint = (args, rootDirectory, nodeBinaryPath, spawnTimeoutMs = OXLIN
|
|
|
43716
43780
|
reject(new ReactDoctorError({ reason: new OxlintSpawnFailed({ cause: "lint phase aborted" }) }));
|
|
43717
43781
|
return;
|
|
43718
43782
|
}
|
|
43719
|
-
const child = spawn(nodeBinaryPath,
|
|
43783
|
+
const child = spawn(nodeBinaryPath, buildProfiledNodeArguments({
|
|
43784
|
+
argumentsList: args,
|
|
43785
|
+
cpuProfileDirectory: SANITIZED_ENV.REACT_DOCTOR_CPU_PROFILE_DIR,
|
|
43786
|
+
heapProfileDirectory: SANITIZED_ENV.REACT_DOCTOR_HEAP_PROFILE_DIR
|
|
43787
|
+
}), {
|
|
43720
43788
|
cwd: rootDirectory,
|
|
43721
43789
|
env: SANITIZED_ENV,
|
|
43722
43790
|
stdio: [
|
|
@@ -44360,7 +44428,7 @@ const runOxlint = async (options) => {
|
|
|
44360
44428
|
};
|
|
44361
44429
|
}
|
|
44362
44430
|
};
|
|
44363
|
-
if (perFileLintCacheEnabled &&
|
|
44431
|
+
if (perFileLintCacheEnabled && !project.hasReactCompiler && extendsPaths.length === 0 && userPlugins.length === 0) {
|
|
44364
44432
|
const rulesetHash = computeRulesetHash({
|
|
44365
44433
|
config: buildConfig({
|
|
44366
44434
|
extendsPaths: [],
|
|
@@ -44368,7 +44436,8 @@ const runOxlint = async (options) => {
|
|
|
44368
44436
|
}),
|
|
44369
44437
|
toolchainVersions: resolveOxlintToolchainVersions(nodeBinaryPath),
|
|
44370
44438
|
ignorePatterns: combinedPatterns,
|
|
44371
|
-
tsconfigContent
|
|
44439
|
+
tsconfigContent,
|
|
44440
|
+
respectInlineDisables
|
|
44372
44441
|
});
|
|
44373
44442
|
const cache = createFileLintCache(resolveReactDoctorCacheDir(rootDirectory), rulesetHash);
|
|
44374
44443
|
const cacheKeyByFile = /* @__PURE__ */ new Map();
|
|
@@ -44411,7 +44480,8 @@ const runOxlint = async (options) => {
|
|
|
44411
44480
|
}),
|
|
44412
44481
|
toolchainVersions: resolveOxlintToolchainVersions(nodeBinaryPath),
|
|
44413
44482
|
ignorePatterns: combinedPatterns,
|
|
44414
|
-
tsconfigContent
|
|
44483
|
+
tsconfigContent,
|
|
44484
|
+
respectInlineDisables
|
|
44415
44485
|
})) : null;
|
|
44416
44486
|
const probeAnswers = createSidecarProbeAnswerResolver({
|
|
44417
44487
|
rootDirectory,
|
|
@@ -45451,7 +45521,7 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
45451
45521
|
directory: scanDirectory,
|
|
45452
45522
|
repo
|
|
45453
45523
|
}).pipe(orElseSucceed(() => null)) : succeed$2(null));
|
|
45454
|
-
const lintIncludePaths = (input.
|
|
45524
|
+
const lintIncludePaths = (input.skipExplicitIncludePathFilter ? input.includePaths.length > 0 ? [...input.includePaths] : void 0 : computeExplicitLintIncludePaths([...input.includePaths])) ?? resolveLintIncludePaths(scanDirectory, resolvedConfig.config);
|
|
45455
45525
|
const fallbackScannedFilePaths = input.suppressScanSummary ? (lintIncludePaths ?? (yield* filesService.listSourceFiles(scanDirectory))).map((relativePath) => Path.resolve(scanDirectory, relativePath)) : [];
|
|
45456
45526
|
const beforeLint = hooks.beforeLint ?? NO_HOOKS.beforeLint;
|
|
45457
45527
|
const afterLint = hooks.afterLint ?? NO_HOOKS.afterLint;
|
|
@@ -46629,7 +46699,7 @@ const makeNoopConsole = () => ({
|
|
|
46629
46699
|
});
|
|
46630
46700
|
//#endregion
|
|
46631
46701
|
//#region src/cli/utils/version.ts
|
|
46632
|
-
const VERSION = "0.7.
|
|
46702
|
+
const VERSION = "0.7.6-dev.0150fc9";
|
|
46633
46703
|
//#endregion
|
|
46634
46704
|
//#region src/cli/utils/json-mode.ts
|
|
46635
46705
|
let context = null;
|
|
@@ -46998,13 +47068,13 @@ const isDevVersion = (version) => version === "0.0.0" || version.includes("-");
|
|
|
46998
47068
|
* uploads source-map artifacts under, so stack frames symbolicate. Honors the
|
|
46999
47069
|
* standard `SENTRY_RELEASE` override.
|
|
47000
47070
|
*/
|
|
47001
|
-
const resolveSentryRelease = () => process.env.SENTRY_RELEASE || `react-doctor@0.7.
|
|
47071
|
+
const resolveSentryRelease = () => process.env.SENTRY_RELEASE || `react-doctor@0.7.6-dev.0150fc9`;
|
|
47002
47072
|
/**
|
|
47003
47073
|
* Deployment environment shown in Sentry's environment filter. Defaults to
|
|
47004
47074
|
* `production` for tagged releases and `development` for dev/unbuilt versions,
|
|
47005
47075
|
* overridable via the standard `SENTRY_ENVIRONMENT` env var.
|
|
47006
47076
|
*/
|
|
47007
|
-
const resolveSentryEnvironment = () => process.env.SENTRY_ENVIRONMENT || (isDevVersion("0.7.
|
|
47077
|
+
const resolveSentryEnvironment = () => process.env.SENTRY_ENVIRONMENT || (isDevVersion("0.7.6-dev.0150fc9") ? "development" : "production");
|
|
47008
47078
|
/**
|
|
47009
47079
|
* Performance-tracing sample rate in `[0, 1]`. Reads `SENTRY_TRACES_SAMPLE_RATE`
|
|
47010
47080
|
* (set to `0` to disable tracing) and falls back to
|
|
@@ -50769,7 +50839,7 @@ const BLOCKING_SUMMARY = {
|
|
|
50769
50839
|
const SCOPE_SUMMARY = {
|
|
50770
50840
|
full: "Scan the whole project on every run",
|
|
50771
50841
|
files: "Report every issue in changed files",
|
|
50772
|
-
lines: "Report issues
|
|
50842
|
+
lines: "Report issues whose source spans touch changed lines",
|
|
50773
50843
|
changed: "Report only the issues a change introduces"
|
|
50774
50844
|
};
|
|
50775
50845
|
const gatesEqual = (left, right) => left.blocking === right.blocking && left.scope === right.scope && left.comment === right.comment && left.reviewComments === right.reviewComments && left.commitStatus === right.commitStatus;
|
|
@@ -59164,7 +59234,7 @@ const buildDirtyPathContentFingerprint = (absolutePath, statusCode) => {
|
|
|
59164
59234
|
return null;
|
|
59165
59235
|
}
|
|
59166
59236
|
if (!stats.isFile()) return null;
|
|
59167
|
-
if (stats.size > 10485760) return
|
|
59237
|
+
if (stats.size > 10485760) return null;
|
|
59168
59238
|
return hashFileContents(absolutePath);
|
|
59169
59239
|
};
|
|
59170
59240
|
const buildWorktreeFingerprint = (projectDirectory) => {
|
|
@@ -59638,7 +59708,9 @@ const buildScanAttributes = (input) => {
|
|
|
59638
59708
|
hasCustomConfig: input.hasCustomConfig,
|
|
59639
59709
|
rulesConfigured: ruleKeys.length,
|
|
59640
59710
|
rulesDisabled: ruleKeys.filter((key) => ruleOverrides[key] === "off").length,
|
|
59641
|
-
fileCount: input.result?.scannedFileCount ?? null
|
|
59711
|
+
fileCount: input.result?.scannedFileCount ?? null,
|
|
59712
|
+
nonJsxFileCount: input.result?.analyzedFiles?.filter((filePath) => !JSX_FILE_PATTERN.test(filePath)).length ?? null,
|
|
59713
|
+
multilineDiagnosticCount: input.result?.diagnostics.filter((diagnostic) => (diagnostic.endLine ?? diagnostic.line) > diagnostic.line).length ?? null
|
|
59642
59714
|
});
|
|
59643
59715
|
};
|
|
59644
59716
|
/**
|
|
@@ -59707,6 +59779,12 @@ const countDeadlineSkippedFiles = (lintPartialFailures) => sumFileCountsMatching
|
|
|
59707
59779
|
const DROPPED_FILES_MESSAGE_PATTERN = /^(\d+) file\(s\) failed to lint and were skipped/;
|
|
59708
59780
|
const countDroppedLintFiles = (lintPartialFailures) => sumFileCountsMatching(lintPartialFailures, DROPPED_FILES_MESSAGE_PATTERN);
|
|
59709
59781
|
//#endregion
|
|
59782
|
+
//#region src/cli/utils/diagnostic-intersects-line-ranges.ts
|
|
59783
|
+
const diagnosticIntersectsLineRanges = (diagnostic, lineRanges) => {
|
|
59784
|
+
const diagnosticEndLine = Math.max(diagnostic.line, diagnostic.endLine ?? diagnostic.line);
|
|
59785
|
+
return lineRanges.some(([rangeStartLine, rangeEndLine]) => diagnostic.line <= rangeEndLine && diagnosticEndLine >= rangeStartLine);
|
|
59786
|
+
};
|
|
59787
|
+
//#endregion
|
|
59710
59788
|
//#region src/cli/utils/materialize-baseline-files.ts
|
|
59711
59789
|
/**
|
|
59712
59790
|
* Materializes the `ref` version of `files` into `tempDirectory` (via
|
|
@@ -60892,7 +60970,7 @@ const buildChangedLineMatcher = (directory, changedLineRanges) => {
|
|
|
60892
60970
|
const relativePath = toForwardSlashes(path.isAbsolute(diagnostic.filePath) ? path.relative(directory, diagnostic.filePath) : diagnostic.filePath);
|
|
60893
60971
|
const ranges = rangesByFile.get(relativePath);
|
|
60894
60972
|
if (ranges === void 0) return false;
|
|
60895
|
-
return
|
|
60973
|
+
return diagnosticIntersectsLineRanges(diagnostic, ranges);
|
|
60896
60974
|
};
|
|
60897
60975
|
};
|
|
60898
60976
|
const buildIgnoredTags = (userConfig) => {
|
|
@@ -65143,7 +65221,7 @@ ${highlighter.dim("Learn more:")}
|
|
|
65143
65221
|
${highlighter.info(CI_URL)}
|
|
65144
65222
|
`;
|
|
65145
65223
|
const collectCategoryOption = (value, previousValues) => [...previousValues ?? [], value];
|
|
65146
|
-
const program = new Command().name("react-doctor").description("Diagnose React codebase health").version(VERSION, "-v, --version", "display the version number").argument("[directory]", "project directory to scan", ".").option("--lint", "enable linting").option("--no-lint", "skip linting").option("--dead-code", "enable dead-code analysis (default)").option("--no-dead-code", "skip dead-code analysis (unused files / exports / dependencies, circular imports)").option("--supply-chain", "enable the dependency supply-chain scan (default)").option("--no-supply-chain", "skip the dependency supply-chain scan (Socket.dev dependency health checks)").option("--verbose", "show every rule and per-file details (default shows top 3 rules)").option("--debug", "force a Sentry trace and print its id at the end (paste it into a bug report)").option("--output-dir <dir>", "directory for the full diagnostics dump (default: a temp folder)").option("--score", "output only the score").option("--json", "output a single structured JSON report (suppresses other output)").option("--json-compact", "with --json, emit compact JSON (no indentation)").option("--json-out <path>", "with --json, write the report to a file instead of stdout").option("-y, --yes", "skip prompts, scan all workspace projects").option("--no-parallel", "lint serially with one worker (default: parallel across CPU cores; set the worker count with REACT_DOCTOR_PARALLEL)").option("--project <name>", "select projects: workspace names or directory paths (comma-separated for multiple); overrides the `projects` config field").option("--scope <value>", "how much to scan/report: full (default), files, changed (only new issues vs base), or lines (
|
|
65224
|
+
const program = new Command().name("react-doctor").description("Diagnose React codebase health").version(VERSION, "-v, --version", "display the version number").argument("[directory]", "project directory to scan", ".").option("--lint", "enable linting").option("--no-lint", "skip linting").option("--dead-code", "enable dead-code analysis (default)").option("--no-dead-code", "skip dead-code analysis (unused files / exports / dependencies, circular imports)").option("--supply-chain", "enable the dependency supply-chain scan (default)").option("--no-supply-chain", "skip the dependency supply-chain scan (Socket.dev dependency health checks)").option("--verbose", "show every rule and per-file details (default shows top 3 rules)").option("--debug", "force a Sentry trace and print its id at the end (paste it into a bug report)").option("--output-dir <dir>", "directory for the full diagnostics dump (default: a temp folder)").option("--score", "output only the score").option("--json", "output a single structured JSON report (suppresses other output)").option("--json-compact", "with --json, emit compact JSON (no indentation)").option("--json-out <path>", "with --json, write the report to a file instead of stdout").option("-y, --yes", "skip prompts, scan all workspace projects").option("--no-parallel", "lint serially with one worker (default: parallel across CPU cores; set the worker count with REACT_DOCTOR_PARALLEL)").option("--project <name>", "select projects: workspace names or directory paths (comma-separated for multiple); overrides the `projects` config field").option("--scope <value>", "how much supported JS/TS source to scan/report: full (default), files, changed (only new issues vs base), or lines (issues whose source spans touch changed lines)").option("--base <ref>", "base git ref for files/changed/lines scope (auto-detected when omitted)").addOption(new Option("--diff [base]", "[deprecated] alias for --scope changed (pass `false` to force a full scan)").hideHelp()).addOption(new Option("--changed-files-from <file>", "scan source files listed in a newline-delimited changed-files file").hideHelp()).option("--no-score", "skip the score API, the share URL, and crash reporting").addOption(new Option("--category <category>", "only show diagnostics in a category (repeatable; e.g. Security)").argParser(collectCategoryOption)).option("--no-telemetry", "alias for --no-score (skip the score API, share URL, and crash reporting)").option("--staged", "scan only staged (git index) files for pre-commit hooks").option("--max-duration <seconds>", "scan time budget for the whole run, shared across workspace projects: past it, remaining lint batches and dead-code are skipped and partial results are reported (skipped files are listed in the JSON report)").option("--blocking <level>", "severity that fails CI: error (default), warning, or none (advisory)").addOption(new Option("--fail-on <level>", "[deprecated] alias for --blocking <level>").hideHelp()).option("--no-respect-inline-disables", "audit mode: neutralize inline lint suppressions before scanning").option("--warnings", "show warning-severity diagnostics (default)").option("--no-warnings", "hide warning-severity diagnostics (errors only)").option("--color", "force colored output").option("--no-color", "disable colored output (also honors NO_COLOR)").addHelpText("after", renderRootHelpEpilog);
|
|
65147
65225
|
program.action(inspectAction);
|
|
65148
65226
|
program.command("why <location>").description("Explain why a rule fired (or why a suppression didn't apply) at a file:line").option("--project <name>", "select projects: workspace names or directory paths (comma-separated for multiple)").option("-c, --cwd <cwd>", "working directory", process.cwd()).option("--color", "force colored output").option("--no-color", "disable colored output (also honors NO_COLOR)").action((location, options) => whyAction(location, options));
|
|
65149
65227
|
program.command("install").alias("setup").description("Install the react-doctor skill into your coding agents and optional git hook").option("-y, --yes", "skip prompts, install for all detected agents").option("--dry-run", "show what would be installed without writing files").option("--agent-hooks", "install native non-blocking agent hooks for Claude Code and Cursor").option("-c, --cwd <cwd>", "working directory", process.cwd()).option("--color", "force colored output").option("--no-color", "disable colored output (also honors NO_COLOR)").addHelpText("after", renderInstallHelpEpilog).action(installAction);
|
|
@@ -65193,4 +65271,4 @@ Promise.resolve().then(() => assertNoRemovedFlags(process.argv)).then(() => prog
|
|
|
65193
65271
|
export {};
|
|
65194
65272
|
|
|
65195
65273
|
//# sourceMappingURL=cli.js.map
|
|
65196
|
-
//# debugId=
|
|
65274
|
+
//# debugId=9a5c1a3c-7706-5a23-bca4-ddddd2f085af
|
package/dist/index.d.ts
CHANGED
|
@@ -9654,6 +9654,7 @@ interface DiagnoseOptions {
|
|
|
9654
9654
|
/** See `ReactDoctorConfig.deadCode`. Ignored in diff mode. */
|
|
9655
9655
|
deadCode?: boolean;
|
|
9656
9656
|
verbose?: boolean;
|
|
9657
|
+
/** Restrict linting to these supported JS/TS source files. */
|
|
9657
9658
|
includePaths?: string[];
|
|
9658
9659
|
/**
|
|
9659
9660
|
* Per-call override for `ReactDoctorConfig.respectInlineDisables`.
|
|
@@ -9800,9 +9801,9 @@ interface InspectResult {
|
|
|
9800
9801
|
scanElapsedMilliseconds?: number;
|
|
9801
9802
|
/**
|
|
9802
9803
|
* Per-file lint cache outcome: files served from cache, and total files
|
|
9803
|
-
* considered. Both absent when the cache was off or bypassed (
|
|
9804
|
-
*
|
|
9805
|
-
* wide event as `lint.cacheHitRatio`.
|
|
9804
|
+
* considered. Both absent when the cache was off or bypassed (adopted
|
|
9805
|
+
* `extends`, user plugins, React Compiler). The CLI projects these onto the
|
|
9806
|
+
* Sentry wide event as `lint.cacheHitRatio`.
|
|
9806
9807
|
*/
|
|
9807
9808
|
lintCacheHitFileCount?: number | null;
|
|
9808
9809
|
lintCacheTotalFileCount?: number | null;
|
|
@@ -9915,9 +9916,9 @@ interface JsonReportProjectEntry {
|
|
|
9915
9916
|
skippedCheckReasons?: Record<string, string>;
|
|
9916
9917
|
/**
|
|
9917
9918
|
* Number of source files this scan's linter examined. In diff / changed
|
|
9918
|
-
* mode it's the count of changed
|
|
9919
|
-
*
|
|
9920
|
-
*
|
|
9919
|
+
* mode it's the count of changed supported JS/TS source files; in a full
|
|
9920
|
+
* scan it's the whole source tree. `0` in a diff scan means the changed
|
|
9921
|
+
* files held nothing React Doctor lints —
|
|
9921
9922
|
* the GitHub Action reads that as "nothing to report" (skips the PR comment;
|
|
9922
9923
|
* the commit status says "skipped"). Optional: absent on reports from
|
|
9923
9924
|
* constructors that don't track it (e.g. `toJsonReport`).
|
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]="11364a34-6c8b-5bd3-b9e8-aaa7c0ea264c")}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 reactDoctorPlugin, { ALL_REACT_DOCTOR_RULE_KEYS, CROSS_FILE_DEPENDENCY_COLLECTORS, CROSS_FILE_RULE_IDS, FRAMEWORK_SPECIFIC_RULE_KEYS, FRAMEWORK_TOKENS, MOTION_LIBRARY_PACKAGES, REACT_COMPILER_RULES, REACT_DOCTOR_RULES, classifySecurityScanFile, collectCrossFileDependencyProbes, resetManifestCaches, shouldReadSecurityScanContent } from "oxlint-plugin-react-doctor";
|
|
@@ -32930,7 +32930,6 @@ const IGNORED_DIRECTORIES = new Set([
|
|
|
32930
32930
|
"out",
|
|
32931
32931
|
"storybook-static"
|
|
32932
32932
|
]);
|
|
32933
|
-
const JSX_FILE_PATTERN = /\.(tsx|jsx)$/;
|
|
32934
32933
|
const MILLISECONDS_PER_SECOND = 1e3;
|
|
32935
32934
|
const SCORE_API_URL = "https://www.react.doctor/api/score";
|
|
32936
32935
|
const FETCH_TIMEOUT_MS = 1e4;
|
|
@@ -33040,7 +33039,7 @@ const SOCKET_FREE_USER_AGENT = "react-doctor-supply-chain";
|
|
|
33040
33039
|
const FILE_LINT_CACHE_FILENAME = "file-lint-cache.json";
|
|
33041
33040
|
const FILE_LINT_CACHE_MAX_FILE_COUNT = 5e4;
|
|
33042
33041
|
const SIDECAR_LINT_CACHE_FILENAME = "sidecar-lint-cache.json";
|
|
33043
|
-
const CORE_PACKAGE_VERSION = "0.7.
|
|
33042
|
+
const CORE_PACKAGE_VERSION = "0.7.6";
|
|
33044
33043
|
const DEAD_CODE_CACHE_FILENAME = "dead-code-cache.json";
|
|
33045
33044
|
const DEAD_CODE_SUMMARY_CACHE_FILENAME = "dead-code-summaries.json";
|
|
33046
33045
|
const SUPPLY_CHAIN_PLUGIN = "socket";
|
|
@@ -33143,13 +33142,15 @@ const listSourceFilesViaFilesystem = (rootDirectory) => {
|
|
|
33143
33142
|
const listSourceFilesWithSize = (rootDirectory) => collectSizedSourceFiles(rootDirectory, (listSourceFilesViaGit(rootDirectory) ?? listSourceFilesViaFilesystem(rootDirectory)).sort());
|
|
33144
33143
|
const listSourceFiles = (rootDirectory) => listSourceFilesWithSize(rootDirectory).map((entry) => entry.path);
|
|
33145
33144
|
const countSourceFiles = (rootDirectory) => listSourceFilesWithSize(rootDirectory).length;
|
|
33145
|
+
const UTF8_BOM_CHARACTER = "";
|
|
33146
|
+
const stripUtf8Bom = (content) => content.startsWith(UTF8_BOM_CHARACTER) ? content.slice(1) : content;
|
|
33146
33147
|
const cachedPackageJsons = /* @__PURE__ */ new Map();
|
|
33147
33148
|
const clearPackageJsonCache = () => {
|
|
33148
33149
|
cachedPackageJsons.clear();
|
|
33149
33150
|
};
|
|
33150
33151
|
const readPackageJsonUncached = (packageJsonPath) => {
|
|
33151
33152
|
try {
|
|
33152
|
-
return JSON.parse(NFS.readFileSync(packageJsonPath, "utf-8"));
|
|
33153
|
+
return JSON.parse(stripUtf8Bom(NFS.readFileSync(packageJsonPath, "utf-8")));
|
|
33153
33154
|
} catch (error) {
|
|
33154
33155
|
if (error instanceof SyntaxError) return {};
|
|
33155
33156
|
if (isErrnoException(error)) {
|
|
@@ -36790,7 +36791,6 @@ const PNPM_WORKSPACE_FILE = "pnpm-workspace.yaml";
|
|
|
36790
36791
|
const PNPM_LOCKFILE = "pnpm-lock.yaml";
|
|
36791
36792
|
const PACKAGE_JSON_FILE = "package.json";
|
|
36792
36793
|
const PNPM_HARDENING_RULE_KEY = "require-pnpm-hardening";
|
|
36793
|
-
const UTF8_BOM_CHAR = "";
|
|
36794
36794
|
const HARDENING_SETTING_KEYS = new Set([
|
|
36795
36795
|
"minimumReleaseAge",
|
|
36796
36796
|
"blockExoticSubdeps",
|
|
@@ -36815,12 +36815,11 @@ const stripInlineComment = (rawValue) => {
|
|
|
36815
36815
|
return rawValue;
|
|
36816
36816
|
};
|
|
36817
36817
|
const unquote = (rawValue) => rawValue.replace(/^["']|["']$/g, "");
|
|
36818
|
-
const stripBom = (rawContent) => rawContent.startsWith(UTF8_BOM_CHAR) ? rawContent.slice(1) : rawContent;
|
|
36819
36818
|
const parseHardeningSettings = (content) => {
|
|
36820
36819
|
let minimumReleaseAge = null;
|
|
36821
36820
|
let blockExoticSubdeps = null;
|
|
36822
36821
|
let trustPolicy = null;
|
|
36823
|
-
const lines =
|
|
36822
|
+
const lines = stripUtf8Bom(content).split(/\r?\n/);
|
|
36824
36823
|
for (let lineIndex = 0; lineIndex < lines.length; lineIndex += 1) {
|
|
36825
36824
|
const lineText = lines[lineIndex];
|
|
36826
36825
|
if (lineText === void 0) continue;
|
|
@@ -36854,7 +36853,7 @@ const isPnpmManagedProject = (rootDirectory) => {
|
|
|
36854
36853
|
const packageJsonPath = Path.join(rootDirectory, PACKAGE_JSON_FILE);
|
|
36855
36854
|
if (!isFile(packageJsonPath)) return false;
|
|
36856
36855
|
try {
|
|
36857
|
-
const packageJsonRaw = NFS.readFileSync(packageJsonPath, "utf-8");
|
|
36856
|
+
const packageJsonRaw = stripUtf8Bom(NFS.readFileSync(packageJsonPath, "utf-8"));
|
|
36858
36857
|
const packageJson = JSON.parse(packageJsonRaw);
|
|
36859
36858
|
if (packageJson !== null && typeof packageJson === "object" && "packageManager" in packageJson && typeof packageJson.packageManager === "string" && packageJson.packageManager.startsWith("pnpm@")) return true;
|
|
36860
36859
|
} catch {
|
|
@@ -37516,9 +37515,7 @@ const highlighter = {
|
|
|
37516
37515
|
gray: import_picocolors.default.gray,
|
|
37517
37516
|
bold: import_picocolors.default.bold
|
|
37518
37517
|
};
|
|
37519
|
-
const
|
|
37520
|
-
const shouldIncludeExplicitLintPath = (filePath, project) => JSX_FILE_PATTERN.test(filePath) || project?.framework === "nextjs" && NEXT_ENTRY_FILE_PATTERN.test(filePath);
|
|
37521
|
-
const computeExplicitLintIncludePaths = (includePaths, project) => includePaths.length > 0 ? includePaths.filter((filePath) => shouldIncludeExplicitLintPath(filePath, project)) : void 0;
|
|
37518
|
+
const computeExplicitLintIncludePaths = (includePaths) => includePaths.length > 0 ? includePaths.filter(isLintableSourceFile) : void 0;
|
|
37522
37519
|
const LINGUIST_ATTRIBUTE_PATTERN = /^linguist-(?:vendored|generated)(?:=([a-zA-Z0-9]+))?$/i;
|
|
37523
37520
|
const FALSY_VALUES = new Set([
|
|
37524
37521
|
"false",
|
|
@@ -38360,11 +38357,11 @@ const resolveDeadCodeTimeout = (input) => {
|
|
|
38360
38357
|
phaseTimeoutMs: workerTimeoutMs + DEAD_CODE_PHASE_TIMEOUT_OVER_WORKER_MS
|
|
38361
38358
|
};
|
|
38362
38359
|
};
|
|
38363
|
-
const resolveLintIncludePaths = (rootDirectory, userConfig
|
|
38360
|
+
const resolveLintIncludePaths = (rootDirectory, userConfig) => {
|
|
38364
38361
|
if (!Array.isArray(userConfig?.ignore?.files) || userConfig.ignore.files.length === 0) return;
|
|
38365
38362
|
const ignoredPatterns = compileIgnoredFilePatterns(userConfig);
|
|
38366
38363
|
return listSourceFiles(rootDirectory).filter((filePath) => {
|
|
38367
|
-
if (!
|
|
38364
|
+
if (!isLintableSourceFile(filePath)) return false;
|
|
38368
38365
|
return !isFileIgnoredByPatterns(filePath, rootDirectory, ignoredPatterns);
|
|
38369
38366
|
});
|
|
38370
38367
|
};
|
|
@@ -39125,7 +39122,7 @@ const normalizeConfigForHash = (config) => {
|
|
|
39125
39122
|
if (Array.isArray(clone?.jsPlugins)) clone.jsPlugins = clone.jsPlugins.map((_, index) => `<plugin:${index}>`);
|
|
39126
39123
|
return clone;
|
|
39127
39124
|
};
|
|
39128
|
-
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");
|
|
39125
|
+
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 ?? "").update("\0").update(input.respectInlineDisables ? "respect-inline-disables" : "audit-mode").digest("hex");
|
|
39129
39126
|
/**
|
|
39130
39127
|
* Loads a plugin module via the local require resolver and extracts
|
|
39131
39128
|
* `(name, ruleNames)` from either `module.exports.meta + rules` or
|
|
@@ -39161,7 +39158,7 @@ const resolveReactHooksJsPlugin = (hasReactCompiler, customRulesOnly) => {
|
|
|
39161
39158
|
} catch {
|
|
39162
39159
|
return null;
|
|
39163
39160
|
}
|
|
39164
|
-
const { ruleNames } = readPluginShape(pluginSpecifier, (
|
|
39161
|
+
const { ruleNames } = readPluginShape(pluginSpecifier, (specifier) => bundledRequire$1(specifier));
|
|
39165
39162
|
return {
|
|
39166
39163
|
entry: {
|
|
39167
39164
|
name: "react-hooks-js",
|
|
@@ -39569,6 +39566,23 @@ const resolveTsConfigRelativePath = (rootDirectory) => {
|
|
|
39569
39566
|
for (const filename of TSCONFIG_FILENAMES) if (NFS.existsSync(Path.join(rootDirectory, filename))) return `./${filename}`;
|
|
39570
39567
|
return null;
|
|
39571
39568
|
};
|
|
39569
|
+
const DERIVED_STATE_RULE_PRIORITY = new Map([
|
|
39570
|
+
["no-initialize-state", 0],
|
|
39571
|
+
["no-adjust-state-on-prop-change", 1],
|
|
39572
|
+
["no-derived-state", 2],
|
|
39573
|
+
["no-derived-state-effect", 3]
|
|
39574
|
+
]);
|
|
39575
|
+
const doDiagnosticSpansOverlap = (first, second) => {
|
|
39576
|
+
if (first.filePath !== second.filePath || first.plugin !== second.plugin) return false;
|
|
39577
|
+
if (first.offset !== void 0 && second.offset !== void 0) {
|
|
39578
|
+
const firstEnd = first.offset + Math.max(first.length ?? 1, 1);
|
|
39579
|
+
const secondEnd = second.offset + Math.max(second.length ?? 1, 1);
|
|
39580
|
+
return first.offset < secondEnd && second.offset < firstEnd;
|
|
39581
|
+
}
|
|
39582
|
+
const firstEndLine = first.endLine ?? first.line;
|
|
39583
|
+
const secondEndLine = second.endLine ?? second.line;
|
|
39584
|
+
return first.line <= secondEndLine && second.line <= firstEndLine;
|
|
39585
|
+
};
|
|
39572
39586
|
const dedupeDiagnostics = (diagnostics) => {
|
|
39573
39587
|
const seenKeys = /* @__PURE__ */ new Set();
|
|
39574
39588
|
const uniqueDiagnostics = [];
|
|
@@ -39576,6 +39590,16 @@ const dedupeDiagnostics = (diagnostics) => {
|
|
|
39576
39590
|
const key = `${diagnostic.filePath}\u0000${diagnostic.line}\u0000${diagnostic.column}\u0000${diagnostic.plugin}\u0000${diagnostic.rule}\u0000${diagnostic.severity}\u0000${diagnostic.message}`;
|
|
39577
39591
|
if (seenKeys.has(key)) continue;
|
|
39578
39592
|
seenKeys.add(key);
|
|
39593
|
+
const priority = DERIVED_STATE_RULE_PRIORITY.get(diagnostic.rule);
|
|
39594
|
+
if (priority !== void 0) {
|
|
39595
|
+
const overlappingDiagnosticIndex = uniqueDiagnostics.findIndex((candidate) => candidate.rule !== diagnostic.rule && candidate.severity === diagnostic.severity && DERIVED_STATE_RULE_PRIORITY.has(candidate.rule) && doDiagnosticSpansOverlap(candidate, diagnostic));
|
|
39596
|
+
if (overlappingDiagnosticIndex >= 0) {
|
|
39597
|
+
const existingDiagnostic = uniqueDiagnostics[overlappingDiagnosticIndex];
|
|
39598
|
+
const existingPriority = existingDiagnostic ? DERIVED_STATE_RULE_PRIORITY.get(existingDiagnostic.rule) : void 0;
|
|
39599
|
+
if (existingPriority !== void 0 && priority < existingPriority) uniqueDiagnostics[overlappingDiagnosticIndex] = diagnostic;
|
|
39600
|
+
continue;
|
|
39601
|
+
}
|
|
39602
|
+
}
|
|
39579
39603
|
uniqueDiagnostics.push(diagnostic);
|
|
39580
39604
|
}
|
|
39581
39605
|
return uniqueDiagnostics;
|
|
@@ -39618,6 +39642,12 @@ const mapWithConcurrency = async (items, concurrency, task) => {
|
|
|
39618
39642
|
if (errors.length > 0) throw errors[0];
|
|
39619
39643
|
return results;
|
|
39620
39644
|
};
|
|
39645
|
+
const lineOfUtf8Offset = (sourceBuffer, utf8Offset) => {
|
|
39646
|
+
let lineNumber = 1;
|
|
39647
|
+
const scanEnd = Math.min(utf8Offset, sourceBuffer.length);
|
|
39648
|
+
for (let byteIndex = 0; byteIndex < scanEnd; byteIndex++) if (sourceBuffer[byteIndex] === 10) lineNumber++;
|
|
39649
|
+
return lineNumber;
|
|
39650
|
+
};
|
|
39621
39651
|
const REANIMATED_SHARED_VALUE_HINT = "If this is a Reanimated shared value, prefer its React Compiler-compatible `.get()` / `.set()` accessors over `.value` — https://docs.swmansion.com/react-native-reanimated/docs/core/useSharedValue/#react-compiler-support";
|
|
39622
39652
|
const ACCESSORS_MIN_VERSION = "3.15.0";
|
|
39623
39653
|
const supportsAccessors = (reanimatedVersion) => {
|
|
@@ -40189,13 +40219,6 @@ const findOutermostEnclosingFunctionRange = (sourceFile, targetLine) => {
|
|
|
40189
40219
|
visit(sourceFile);
|
|
40190
40220
|
return foundRange;
|
|
40191
40221
|
};
|
|
40192
|
-
const NEWLINE_BYTE = 10;
|
|
40193
|
-
const lineOfUtf8Offset = (sourceBuffer, utf8Offset) => {
|
|
40194
|
-
let lineNumber = 1;
|
|
40195
|
-
const scanEnd = Math.min(utf8Offset, sourceBuffer.length);
|
|
40196
|
-
for (let byteIndex = 0; byteIndex < scanEnd; byteIndex++) if (sourceBuffer[byteIndex] === NEWLINE_BYTE) lineNumber++;
|
|
40197
|
-
return lineNumber;
|
|
40198
|
-
};
|
|
40199
40222
|
/**
|
|
40200
40223
|
* Drops `react-doctor/react-compiler-no-manual-memoization` diagnostics
|
|
40201
40224
|
* whose enclosing function also carries a `react-hooks-js` (React
|
|
@@ -40400,8 +40423,33 @@ const parseOxlintOutput = (stdout, project, rootDirectory) => {
|
|
|
40400
40423
|
}
|
|
40401
40424
|
if (!isOxlintOutput(parsed)) throw new ReactDoctorError({ reason: new OxlintOutputUnparseable({ preview: stdout.slice(0, 600) }) });
|
|
40402
40425
|
const minifiedFileCache = /* @__PURE__ */ new Map();
|
|
40426
|
+
const sourceBufferCache = /* @__PURE__ */ new Map();
|
|
40427
|
+
const sourceLinesCache = /* @__PURE__ */ new Map();
|
|
40428
|
+
const resolveAbsolutePath = (filename) => Path.isAbsolute(filename) ? filename : Path.resolve(rootDirectory || ".", filename);
|
|
40429
|
+
const readSourceBuffer = (filename) => {
|
|
40430
|
+
const absolutePath = resolveAbsolutePath(filename);
|
|
40431
|
+
const cached = sourceBufferCache.get(absolutePath);
|
|
40432
|
+
if (cached !== void 0) return cached;
|
|
40433
|
+
let sourceBuffer;
|
|
40434
|
+
try {
|
|
40435
|
+
sourceBuffer = NFS.readFileSync(absolutePath);
|
|
40436
|
+
} catch {
|
|
40437
|
+
sourceBuffer = null;
|
|
40438
|
+
}
|
|
40439
|
+
sourceBufferCache.set(absolutePath, sourceBuffer);
|
|
40440
|
+
return sourceBuffer;
|
|
40441
|
+
};
|
|
40442
|
+
const readSourceLines = (filename) => {
|
|
40443
|
+
const absolutePath = resolveAbsolutePath(filename);
|
|
40444
|
+
const cached = sourceLinesCache.get(absolutePath);
|
|
40445
|
+
if (cached !== void 0) return cached;
|
|
40446
|
+
const sourceBuffer = readSourceBuffer(filename);
|
|
40447
|
+
const sourceLines = sourceBuffer ? sourceBuffer.toString("utf8").split("\n") : null;
|
|
40448
|
+
sourceLinesCache.set(absolutePath, sourceLines);
|
|
40449
|
+
return sourceLines;
|
|
40450
|
+
};
|
|
40403
40451
|
const isMinifiedDiagnosticFile = (filename) => {
|
|
40404
|
-
const absolutePath =
|
|
40452
|
+
const absolutePath = resolveAbsolutePath(filename);
|
|
40405
40453
|
const cached = minifiedFileCache.get(absolutePath);
|
|
40406
40454
|
if (cached !== void 0) return cached;
|
|
40407
40455
|
const minified = isMinifiedSource(absolutePath);
|
|
@@ -40414,8 +40462,17 @@ const parseOxlintOutput = (stdout, project, rootDirectory) => {
|
|
|
40414
40462
|
const cleaned = cleanDiagnosticMessage(diagnostic.message, diagnostic.help, plugin, rule, project);
|
|
40415
40463
|
const normalizedFilePath = diagnostic.filename.replaceAll("\\", "/");
|
|
40416
40464
|
const primarySpan = primaryLabel?.span;
|
|
40465
|
+
const sourceBuffer = primarySpan ? readSourceBuffer(diagnostic.filename) : null;
|
|
40417
40466
|
const relatedLocations = buildRelatedLocations(diagnostic.labels, normalizedFilePath);
|
|
40418
40467
|
const category = resolveDiagnosticCategory(plugin, rule);
|
|
40468
|
+
const matchByOccurrence = resolveMatchByOccurrence(rule, category);
|
|
40469
|
+
const sourceLines = primarySpan ? readSourceLines(diagnostic.filename) : null;
|
|
40470
|
+
const primaryLineIndex = primarySpan ? primarySpan.line - 1 : -1;
|
|
40471
|
+
const primaryLine = sourceLines?.[primaryLineIndex];
|
|
40472
|
+
const primaryColumnIndex = primarySpan ? primarySpan.column - 1 : -1;
|
|
40473
|
+
const isJsxTagLabel = primaryLine !== void 0 && primaryColumnIndex >= 0 && (primaryLine[primaryColumnIndex] === "<" || primaryLine[primaryColumnIndex - 1] === "<");
|
|
40474
|
+
const jsxOpenerEndLineIndex = sourceLines && isJsxTagLabel ? findJsxOpenerSpan(sourceLines, primaryLineIndex) : null;
|
|
40475
|
+
const primarySpanEndLine = jsxOpenerEndLineIndex !== null ? jsxOpenerEndLineIndex + 1 : primarySpan && sourceBuffer ? lineOfUtf8Offset(sourceBuffer, primarySpan.offset + Math.max(primarySpan.length - 1, 0)) : void 0;
|
|
40419
40476
|
return {
|
|
40420
40477
|
filePath: normalizedFilePath,
|
|
40421
40478
|
plugin,
|
|
@@ -40429,10 +40486,11 @@ const parseOxlintOutput = (stdout, project, rootDirectory) => {
|
|
|
40429
40486
|
column: primarySpan?.column ?? 0,
|
|
40430
40487
|
...primarySpan ? {
|
|
40431
40488
|
offset: primarySpan.offset,
|
|
40432
|
-
length: primarySpan.length
|
|
40489
|
+
length: primarySpan.length,
|
|
40490
|
+
...primarySpanEndLine !== void 0 ? { endLine: primarySpanEndLine } : {}
|
|
40433
40491
|
} : {},
|
|
40434
40492
|
category,
|
|
40435
|
-
...
|
|
40493
|
+
...matchByOccurrence ? { matchByOccurrence: true } : {},
|
|
40436
40494
|
...relatedLocations.length > 0 ? { relatedLocations } : {}
|
|
40437
40495
|
};
|
|
40438
40496
|
}), rootDirectory);
|
|
@@ -40449,6 +40507,11 @@ const buildOxlintChildEnv = (sourceEnv) => {
|
|
|
40449
40507
|
if (!isCompileCacheDisabled && !isCompileCacheAlreadySet) childEnv.NODE_COMPILE_CACHE = Path.join(os.tmpdir(), NODE_COMPILE_CACHE_DIR_NAME);
|
|
40450
40508
|
return childEnv;
|
|
40451
40509
|
};
|
|
40510
|
+
const buildProfiledNodeArguments = (input) => [
|
|
40511
|
+
...input.cpuProfileDirectory ? ["--cpu-prof", `--cpu-prof-dir=${input.cpuProfileDirectory}`] : [],
|
|
40512
|
+
...input.heapProfileDirectory ? ["--heap-prof", `--heap-prof-dir=${input.heapProfileDirectory}`] : [],
|
|
40513
|
+
...input.argumentsList
|
|
40514
|
+
];
|
|
40452
40515
|
const SANITIZED_ENV = buildOxlintChildEnv(process.env);
|
|
40453
40516
|
/**
|
|
40454
40517
|
* Spawn one oxlint subprocess with hard ceilings on wall time and
|
|
@@ -40473,7 +40536,11 @@ const spawnOxlint = (args, rootDirectory, nodeBinaryPath, spawnTimeoutMs = OXLIN
|
|
|
40473
40536
|
reject(new ReactDoctorError({ reason: new OxlintSpawnFailed({ cause: "lint phase aborted" }) }));
|
|
40474
40537
|
return;
|
|
40475
40538
|
}
|
|
40476
|
-
const child = spawn(nodeBinaryPath,
|
|
40539
|
+
const child = spawn(nodeBinaryPath, buildProfiledNodeArguments({
|
|
40540
|
+
argumentsList: args,
|
|
40541
|
+
cpuProfileDirectory: SANITIZED_ENV.REACT_DOCTOR_CPU_PROFILE_DIR,
|
|
40542
|
+
heapProfileDirectory: SANITIZED_ENV.REACT_DOCTOR_HEAP_PROFILE_DIR
|
|
40543
|
+
}), {
|
|
40477
40544
|
cwd: rootDirectory,
|
|
40478
40545
|
env: SANITIZED_ENV,
|
|
40479
40546
|
stdio: [
|
|
@@ -41117,7 +41184,7 @@ const runOxlint = async (options) => {
|
|
|
41117
41184
|
};
|
|
41118
41185
|
}
|
|
41119
41186
|
};
|
|
41120
|
-
if (perFileLintCacheEnabled &&
|
|
41187
|
+
if (perFileLintCacheEnabled && !project.hasReactCompiler && extendsPaths.length === 0 && userPlugins.length === 0) {
|
|
41121
41188
|
const rulesetHash = computeRulesetHash({
|
|
41122
41189
|
config: buildConfig({
|
|
41123
41190
|
extendsPaths: [],
|
|
@@ -41125,7 +41192,8 @@ const runOxlint = async (options) => {
|
|
|
41125
41192
|
}),
|
|
41126
41193
|
toolchainVersions: resolveOxlintToolchainVersions(nodeBinaryPath),
|
|
41127
41194
|
ignorePatterns: combinedPatterns,
|
|
41128
|
-
tsconfigContent
|
|
41195
|
+
tsconfigContent,
|
|
41196
|
+
respectInlineDisables
|
|
41129
41197
|
});
|
|
41130
41198
|
const cache = createFileLintCache(resolveReactDoctorCacheDir(rootDirectory), rulesetHash);
|
|
41131
41199
|
const cacheKeyByFile = /* @__PURE__ */ new Map();
|
|
@@ -41168,7 +41236,8 @@ const runOxlint = async (options) => {
|
|
|
41168
41236
|
}),
|
|
41169
41237
|
toolchainVersions: resolveOxlintToolchainVersions(nodeBinaryPath),
|
|
41170
41238
|
ignorePatterns: combinedPatterns,
|
|
41171
|
-
tsconfigContent
|
|
41239
|
+
tsconfigContent,
|
|
41240
|
+
respectInlineDisables
|
|
41172
41241
|
})) : null;
|
|
41173
41242
|
const probeAnswers = createSidecarProbeAnswerResolver({
|
|
41174
41243
|
rootDirectory,
|
|
@@ -42208,7 +42277,7 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
42208
42277
|
directory: scanDirectory,
|
|
42209
42278
|
repo
|
|
42210
42279
|
}).pipe(orElseSucceed(() => null)) : succeed$2(null));
|
|
42211
|
-
const lintIncludePaths = (input.
|
|
42280
|
+
const lintIncludePaths = (input.skipExplicitIncludePathFilter ? input.includePaths.length > 0 ? [...input.includePaths] : void 0 : computeExplicitLintIncludePaths([...input.includePaths])) ?? resolveLintIncludePaths(scanDirectory, resolvedConfig.config);
|
|
42212
42281
|
const fallbackScannedFilePaths = input.suppressScanSummary ? (lintIncludePaths ?? (yield* filesService.listSourceFiles(scanDirectory))).map((relativePath) => Path.resolve(scanDirectory, relativePath)) : [];
|
|
42213
42282
|
const beforeLint = hooks.beforeLint ?? NO_HOOKS.beforeLint;
|
|
42214
42283
|
const afterLint = hooks.afterLint ?? NO_HOOKS.afterLint;
|
|
@@ -43172,4 +43241,4 @@ const toJsonReport = (result, options) => buildJsonReport({
|
|
|
43172
43241
|
export { AmbiguousProjectError, NoReactDependencyError, NotADirectoryError, PackageJsonNotFoundError, ProjectNotFoundError, ReactDoctorError, buildJsonReport, buildJsonReportError, clearCaches, defineConfig, diagnose, filterSourceFiles, getDiffInfo, hasReactRuntime, isProjectDiscoveryError, isReactDoctorError, summarizeDiagnostics, toJsonReport };
|
|
43173
43242
|
|
|
43174
43243
|
//# sourceMappingURL=index.js.map
|
|
43175
|
-
//# debugId=
|
|
43244
|
+
//# debugId=11364a34-6c8b-5bd3-b9e8-aaa7c0ea264c
|
package/dist/lsp.js
CHANGED
|
@@ -32959,7 +32959,6 @@ const IGNORED_DIRECTORIES = new Set([
|
|
|
32959
32959
|
"out",
|
|
32960
32960
|
"storybook-static"
|
|
32961
32961
|
]);
|
|
32962
|
-
const JSX_FILE_PATTERN = /\.(tsx|jsx)$/;
|
|
32963
32962
|
const MILLISECONDS_PER_SECOND = 1e3;
|
|
32964
32963
|
const SCORE_API_URL = "https://www.react.doctor/api/score";
|
|
32965
32964
|
const FETCH_TIMEOUT_MS = 1e4;
|
|
@@ -33092,7 +33091,7 @@ const SOCKET_FREE_USER_AGENT = "react-doctor-supply-chain";
|
|
|
33092
33091
|
const FILE_LINT_CACHE_FILENAME = "file-lint-cache.json";
|
|
33093
33092
|
const FILE_LINT_CACHE_MAX_FILE_COUNT = 5e4;
|
|
33094
33093
|
const SIDECAR_LINT_CACHE_FILENAME = "sidecar-lint-cache.json";
|
|
33095
|
-
const CORE_PACKAGE_VERSION = "0.7.
|
|
33094
|
+
const CORE_PACKAGE_VERSION = "0.7.6";
|
|
33096
33095
|
const DEAD_CODE_CACHE_FILENAME = "dead-code-cache.json";
|
|
33097
33096
|
const DEAD_CODE_SUMMARY_CACHE_FILENAME = "dead-code-summaries.json";
|
|
33098
33097
|
const SUPPLY_CHAIN_PLUGIN = "socket";
|
|
@@ -33195,13 +33194,15 @@ const listSourceFilesViaFilesystem = (rootDirectory) => {
|
|
|
33195
33194
|
const listSourceFilesWithSize = (rootDirectory) => collectSizedSourceFiles(rootDirectory, (listSourceFilesViaGit(rootDirectory) ?? listSourceFilesViaFilesystem(rootDirectory)).sort());
|
|
33196
33195
|
const listSourceFiles = (rootDirectory) => listSourceFilesWithSize(rootDirectory).map((entry) => entry.path);
|
|
33197
33196
|
const countSourceFiles = (rootDirectory) => listSourceFilesWithSize(rootDirectory).length;
|
|
33197
|
+
const UTF8_BOM_CHARACTER = "";
|
|
33198
|
+
const stripUtf8Bom = (content) => content.startsWith(UTF8_BOM_CHARACTER) ? content.slice(1) : content;
|
|
33198
33199
|
const cachedPackageJsons = /* @__PURE__ */ new Map();
|
|
33199
33200
|
const clearPackageJsonCache = () => {
|
|
33200
33201
|
cachedPackageJsons.clear();
|
|
33201
33202
|
};
|
|
33202
33203
|
const readPackageJsonUncached = (packageJsonPath) => {
|
|
33203
33204
|
try {
|
|
33204
|
-
return JSON.parse(NFS.readFileSync(packageJsonPath, "utf-8"));
|
|
33205
|
+
return JSON.parse(stripUtf8Bom(NFS.readFileSync(packageJsonPath, "utf-8")));
|
|
33205
33206
|
} catch (error) {
|
|
33206
33207
|
if (error instanceof SyntaxError) return {};
|
|
33207
33208
|
if (isErrnoException(error)) {
|
|
@@ -36768,7 +36769,6 @@ const PNPM_WORKSPACE_FILE = "pnpm-workspace.yaml";
|
|
|
36768
36769
|
const PNPM_LOCKFILE = "pnpm-lock.yaml";
|
|
36769
36770
|
const PACKAGE_JSON_FILE = "package.json";
|
|
36770
36771
|
const PNPM_HARDENING_RULE_KEY = "require-pnpm-hardening";
|
|
36771
|
-
const UTF8_BOM_CHAR = "";
|
|
36772
36772
|
const HARDENING_SETTING_KEYS = new Set([
|
|
36773
36773
|
"minimumReleaseAge",
|
|
36774
36774
|
"blockExoticSubdeps",
|
|
@@ -36793,12 +36793,11 @@ const stripInlineComment = (rawValue) => {
|
|
|
36793
36793
|
return rawValue;
|
|
36794
36794
|
};
|
|
36795
36795
|
const unquote = (rawValue) => rawValue.replace(/^["']|["']$/g, "");
|
|
36796
|
-
const stripBom = (rawContent) => rawContent.startsWith(UTF8_BOM_CHAR) ? rawContent.slice(1) : rawContent;
|
|
36797
36796
|
const parseHardeningSettings = (content) => {
|
|
36798
36797
|
let minimumReleaseAge = null;
|
|
36799
36798
|
let blockExoticSubdeps = null;
|
|
36800
36799
|
let trustPolicy = null;
|
|
36801
|
-
const lines =
|
|
36800
|
+
const lines = stripUtf8Bom(content).split(/\r?\n/);
|
|
36802
36801
|
for (let lineIndex = 0; lineIndex < lines.length; lineIndex += 1) {
|
|
36803
36802
|
const lineText = lines[lineIndex];
|
|
36804
36803
|
if (lineText === void 0) continue;
|
|
@@ -36832,7 +36831,7 @@ const isPnpmManagedProject = (rootDirectory) => {
|
|
|
36832
36831
|
const packageJsonPath = Path.join(rootDirectory, PACKAGE_JSON_FILE);
|
|
36833
36832
|
if (!isFile(packageJsonPath)) return false;
|
|
36834
36833
|
try {
|
|
36835
|
-
const packageJsonRaw = NFS.readFileSync(packageJsonPath, "utf-8");
|
|
36834
|
+
const packageJsonRaw = stripUtf8Bom(NFS.readFileSync(packageJsonPath, "utf-8"));
|
|
36836
36835
|
const packageJson = JSON.parse(packageJsonRaw);
|
|
36837
36836
|
if (packageJson !== null && typeof packageJson === "object" && "packageManager" in packageJson && typeof packageJson.packageManager === "string" && packageJson.packageManager.startsWith("pnpm@")) return true;
|
|
36838
36837
|
} catch {
|
|
@@ -37494,9 +37493,7 @@ const highlighter = {
|
|
|
37494
37493
|
gray: import_picocolors.default.gray,
|
|
37495
37494
|
bold: import_picocolors.default.bold
|
|
37496
37495
|
};
|
|
37497
|
-
const
|
|
37498
|
-
const shouldIncludeExplicitLintPath = (filePath, project) => JSX_FILE_PATTERN.test(filePath) || project?.framework === "nextjs" && NEXT_ENTRY_FILE_PATTERN.test(filePath);
|
|
37499
|
-
const computeExplicitLintIncludePaths = (includePaths, project) => includePaths.length > 0 ? includePaths.filter((filePath) => shouldIncludeExplicitLintPath(filePath, project)) : void 0;
|
|
37496
|
+
const computeExplicitLintIncludePaths = (includePaths) => includePaths.length > 0 ? includePaths.filter(isLintableSourceFile) : void 0;
|
|
37500
37497
|
const LINGUIST_ATTRIBUTE_PATTERN = /^linguist-(?:vendored|generated)(?:=([a-zA-Z0-9]+))?$/i;
|
|
37501
37498
|
const FALSY_VALUES = new Set([
|
|
37502
37499
|
"false",
|
|
@@ -38338,11 +38335,11 @@ const resolveDeadCodeTimeout = (input) => {
|
|
|
38338
38335
|
phaseTimeoutMs: workerTimeoutMs + DEAD_CODE_PHASE_TIMEOUT_OVER_WORKER_MS
|
|
38339
38336
|
};
|
|
38340
38337
|
};
|
|
38341
|
-
const resolveLintIncludePaths = (rootDirectory, userConfig
|
|
38338
|
+
const resolveLintIncludePaths = (rootDirectory, userConfig) => {
|
|
38342
38339
|
if (!Array.isArray(userConfig?.ignore?.files) || userConfig.ignore.files.length === 0) return;
|
|
38343
38340
|
const ignoredPatterns = compileIgnoredFilePatterns(userConfig);
|
|
38344
38341
|
return listSourceFiles(rootDirectory).filter((filePath) => {
|
|
38345
|
-
if (!
|
|
38342
|
+
if (!isLintableSourceFile(filePath)) return false;
|
|
38346
38343
|
return !isFileIgnoredByPatterns(filePath, rootDirectory, ignoredPatterns);
|
|
38347
38344
|
});
|
|
38348
38345
|
};
|
|
@@ -39103,7 +39100,7 @@ const normalizeConfigForHash = (config) => {
|
|
|
39103
39100
|
if (Array.isArray(clone?.jsPlugins)) clone.jsPlugins = clone.jsPlugins.map((_, index) => `<plugin:${index}>`);
|
|
39104
39101
|
return clone;
|
|
39105
39102
|
};
|
|
39106
|
-
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");
|
|
39103
|
+
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 ?? "").update("\0").update(input.respectInlineDisables ? "respect-inline-disables" : "audit-mode").digest("hex");
|
|
39107
39104
|
/**
|
|
39108
39105
|
* Loads a plugin module via the local require resolver and extracts
|
|
39109
39106
|
* `(name, ruleNames)` from either `module.exports.meta + rules` or
|
|
@@ -39139,7 +39136,7 @@ const resolveReactHooksJsPlugin = (hasReactCompiler, customRulesOnly) => {
|
|
|
39139
39136
|
} catch {
|
|
39140
39137
|
return null;
|
|
39141
39138
|
}
|
|
39142
|
-
const { ruleNames } = readPluginShape(pluginSpecifier, (
|
|
39139
|
+
const { ruleNames } = readPluginShape(pluginSpecifier, (specifier) => bundledRequire$1(specifier));
|
|
39143
39140
|
return {
|
|
39144
39141
|
entry: {
|
|
39145
39142
|
name: "react-hooks-js",
|
|
@@ -39547,6 +39544,23 @@ const resolveTsConfigRelativePath = (rootDirectory) => {
|
|
|
39547
39544
|
for (const filename of TSCONFIG_FILENAMES) if (NFS.existsSync(Path.join(rootDirectory, filename))) return `./${filename}`;
|
|
39548
39545
|
return null;
|
|
39549
39546
|
};
|
|
39547
|
+
const DERIVED_STATE_RULE_PRIORITY = new Map([
|
|
39548
|
+
["no-initialize-state", 0],
|
|
39549
|
+
["no-adjust-state-on-prop-change", 1],
|
|
39550
|
+
["no-derived-state", 2],
|
|
39551
|
+
["no-derived-state-effect", 3]
|
|
39552
|
+
]);
|
|
39553
|
+
const doDiagnosticSpansOverlap = (first, second) => {
|
|
39554
|
+
if (first.filePath !== second.filePath || first.plugin !== second.plugin) return false;
|
|
39555
|
+
if (first.offset !== void 0 && second.offset !== void 0) {
|
|
39556
|
+
const firstEnd = first.offset + Math.max(first.length ?? 1, 1);
|
|
39557
|
+
const secondEnd = second.offset + Math.max(second.length ?? 1, 1);
|
|
39558
|
+
return first.offset < secondEnd && second.offset < firstEnd;
|
|
39559
|
+
}
|
|
39560
|
+
const firstEndLine = first.endLine ?? first.line;
|
|
39561
|
+
const secondEndLine = second.endLine ?? second.line;
|
|
39562
|
+
return first.line <= secondEndLine && second.line <= firstEndLine;
|
|
39563
|
+
};
|
|
39550
39564
|
const dedupeDiagnostics = (diagnostics) => {
|
|
39551
39565
|
const seenKeys = /* @__PURE__ */ new Set();
|
|
39552
39566
|
const uniqueDiagnostics = [];
|
|
@@ -39554,6 +39568,16 @@ const dedupeDiagnostics = (diagnostics) => {
|
|
|
39554
39568
|
const key = `${diagnostic.filePath}\u0000${diagnostic.line}\u0000${diagnostic.column}\u0000${diagnostic.plugin}\u0000${diagnostic.rule}\u0000${diagnostic.severity}\u0000${diagnostic.message}`;
|
|
39555
39569
|
if (seenKeys.has(key)) continue;
|
|
39556
39570
|
seenKeys.add(key);
|
|
39571
|
+
const priority = DERIVED_STATE_RULE_PRIORITY.get(diagnostic.rule);
|
|
39572
|
+
if (priority !== void 0) {
|
|
39573
|
+
const overlappingDiagnosticIndex = uniqueDiagnostics.findIndex((candidate) => candidate.rule !== diagnostic.rule && candidate.severity === diagnostic.severity && DERIVED_STATE_RULE_PRIORITY.has(candidate.rule) && doDiagnosticSpansOverlap(candidate, diagnostic));
|
|
39574
|
+
if (overlappingDiagnosticIndex >= 0) {
|
|
39575
|
+
const existingDiagnostic = uniqueDiagnostics[overlappingDiagnosticIndex];
|
|
39576
|
+
const existingPriority = existingDiagnostic ? DERIVED_STATE_RULE_PRIORITY.get(existingDiagnostic.rule) : void 0;
|
|
39577
|
+
if (existingPriority !== void 0 && priority < existingPriority) uniqueDiagnostics[overlappingDiagnosticIndex] = diagnostic;
|
|
39578
|
+
continue;
|
|
39579
|
+
}
|
|
39580
|
+
}
|
|
39557
39581
|
uniqueDiagnostics.push(diagnostic);
|
|
39558
39582
|
}
|
|
39559
39583
|
return uniqueDiagnostics;
|
|
@@ -39596,6 +39620,12 @@ const mapWithConcurrency = async (items, concurrency, task) => {
|
|
|
39596
39620
|
if (errors.length > 0) throw errors[0];
|
|
39597
39621
|
return results;
|
|
39598
39622
|
};
|
|
39623
|
+
const lineOfUtf8Offset = (sourceBuffer, utf8Offset) => {
|
|
39624
|
+
let lineNumber = 1;
|
|
39625
|
+
const scanEnd = Math.min(utf8Offset, sourceBuffer.length);
|
|
39626
|
+
for (let byteIndex = 0; byteIndex < scanEnd; byteIndex++) if (sourceBuffer[byteIndex] === 10) lineNumber++;
|
|
39627
|
+
return lineNumber;
|
|
39628
|
+
};
|
|
39599
39629
|
const REANIMATED_SHARED_VALUE_HINT = "If this is a Reanimated shared value, prefer its React Compiler-compatible `.get()` / `.set()` accessors over `.value` — https://docs.swmansion.com/react-native-reanimated/docs/core/useSharedValue/#react-compiler-support";
|
|
39600
39630
|
const ACCESSORS_MIN_VERSION = "3.15.0";
|
|
39601
39631
|
const supportsAccessors = (reanimatedVersion) => {
|
|
@@ -40167,13 +40197,6 @@ const findOutermostEnclosingFunctionRange = (sourceFile, targetLine) => {
|
|
|
40167
40197
|
visit(sourceFile);
|
|
40168
40198
|
return foundRange;
|
|
40169
40199
|
};
|
|
40170
|
-
const NEWLINE_BYTE = 10;
|
|
40171
|
-
const lineOfUtf8Offset = (sourceBuffer, utf8Offset) => {
|
|
40172
|
-
let lineNumber = 1;
|
|
40173
|
-
const scanEnd = Math.min(utf8Offset, sourceBuffer.length);
|
|
40174
|
-
for (let byteIndex = 0; byteIndex < scanEnd; byteIndex++) if (sourceBuffer[byteIndex] === NEWLINE_BYTE) lineNumber++;
|
|
40175
|
-
return lineNumber;
|
|
40176
|
-
};
|
|
40177
40200
|
/**
|
|
40178
40201
|
* Drops `react-doctor/react-compiler-no-manual-memoization` diagnostics
|
|
40179
40202
|
* whose enclosing function also carries a `react-hooks-js` (React
|
|
@@ -40378,8 +40401,33 @@ const parseOxlintOutput = (stdout, project, rootDirectory) => {
|
|
|
40378
40401
|
}
|
|
40379
40402
|
if (!isOxlintOutput(parsed)) throw new ReactDoctorError({ reason: new OxlintOutputUnparseable({ preview: stdout.slice(0, 600) }) });
|
|
40380
40403
|
const minifiedFileCache = /* @__PURE__ */ new Map();
|
|
40404
|
+
const sourceBufferCache = /* @__PURE__ */ new Map();
|
|
40405
|
+
const sourceLinesCache = /* @__PURE__ */ new Map();
|
|
40406
|
+
const resolveAbsolutePath = (filename) => Path.isAbsolute(filename) ? filename : Path.resolve(rootDirectory || ".", filename);
|
|
40407
|
+
const readSourceBuffer = (filename) => {
|
|
40408
|
+
const absolutePath = resolveAbsolutePath(filename);
|
|
40409
|
+
const cached = sourceBufferCache.get(absolutePath);
|
|
40410
|
+
if (cached !== void 0) return cached;
|
|
40411
|
+
let sourceBuffer;
|
|
40412
|
+
try {
|
|
40413
|
+
sourceBuffer = NFS.readFileSync(absolutePath);
|
|
40414
|
+
} catch {
|
|
40415
|
+
sourceBuffer = null;
|
|
40416
|
+
}
|
|
40417
|
+
sourceBufferCache.set(absolutePath, sourceBuffer);
|
|
40418
|
+
return sourceBuffer;
|
|
40419
|
+
};
|
|
40420
|
+
const readSourceLines = (filename) => {
|
|
40421
|
+
const absolutePath = resolveAbsolutePath(filename);
|
|
40422
|
+
const cached = sourceLinesCache.get(absolutePath);
|
|
40423
|
+
if (cached !== void 0) return cached;
|
|
40424
|
+
const sourceBuffer = readSourceBuffer(filename);
|
|
40425
|
+
const sourceLines = sourceBuffer ? sourceBuffer.toString("utf8").split("\n") : null;
|
|
40426
|
+
sourceLinesCache.set(absolutePath, sourceLines);
|
|
40427
|
+
return sourceLines;
|
|
40428
|
+
};
|
|
40381
40429
|
const isMinifiedDiagnosticFile = (filename) => {
|
|
40382
|
-
const absolutePath =
|
|
40430
|
+
const absolutePath = resolveAbsolutePath(filename);
|
|
40383
40431
|
const cached = minifiedFileCache.get(absolutePath);
|
|
40384
40432
|
if (cached !== void 0) return cached;
|
|
40385
40433
|
const minified = isMinifiedSource(absolutePath);
|
|
@@ -40392,8 +40440,17 @@ const parseOxlintOutput = (stdout, project, rootDirectory) => {
|
|
|
40392
40440
|
const cleaned = cleanDiagnosticMessage(diagnostic.message, diagnostic.help, plugin, rule, project);
|
|
40393
40441
|
const normalizedFilePath = diagnostic.filename.replaceAll("\\", "/");
|
|
40394
40442
|
const primarySpan = primaryLabel?.span;
|
|
40443
|
+
const sourceBuffer = primarySpan ? readSourceBuffer(diagnostic.filename) : null;
|
|
40395
40444
|
const relatedLocations = buildRelatedLocations(diagnostic.labels, normalizedFilePath);
|
|
40396
40445
|
const category = resolveDiagnosticCategory(plugin, rule);
|
|
40446
|
+
const matchByOccurrence = resolveMatchByOccurrence(rule, category);
|
|
40447
|
+
const sourceLines = primarySpan ? readSourceLines(diagnostic.filename) : null;
|
|
40448
|
+
const primaryLineIndex = primarySpan ? primarySpan.line - 1 : -1;
|
|
40449
|
+
const primaryLine = sourceLines?.[primaryLineIndex];
|
|
40450
|
+
const primaryColumnIndex = primarySpan ? primarySpan.column - 1 : -1;
|
|
40451
|
+
const isJsxTagLabel = primaryLine !== void 0 && primaryColumnIndex >= 0 && (primaryLine[primaryColumnIndex] === "<" || primaryLine[primaryColumnIndex - 1] === "<");
|
|
40452
|
+
const jsxOpenerEndLineIndex = sourceLines && isJsxTagLabel ? findJsxOpenerSpan(sourceLines, primaryLineIndex) : null;
|
|
40453
|
+
const primarySpanEndLine = jsxOpenerEndLineIndex !== null ? jsxOpenerEndLineIndex + 1 : primarySpan && sourceBuffer ? lineOfUtf8Offset(sourceBuffer, primarySpan.offset + Math.max(primarySpan.length - 1, 0)) : void 0;
|
|
40397
40454
|
return {
|
|
40398
40455
|
filePath: normalizedFilePath,
|
|
40399
40456
|
plugin,
|
|
@@ -40407,10 +40464,11 @@ const parseOxlintOutput = (stdout, project, rootDirectory) => {
|
|
|
40407
40464
|
column: primarySpan?.column ?? 0,
|
|
40408
40465
|
...primarySpan ? {
|
|
40409
40466
|
offset: primarySpan.offset,
|
|
40410
|
-
length: primarySpan.length
|
|
40467
|
+
length: primarySpan.length,
|
|
40468
|
+
...primarySpanEndLine !== void 0 ? { endLine: primarySpanEndLine } : {}
|
|
40411
40469
|
} : {},
|
|
40412
40470
|
category,
|
|
40413
|
-
...
|
|
40471
|
+
...matchByOccurrence ? { matchByOccurrence: true } : {},
|
|
40414
40472
|
...relatedLocations.length > 0 ? { relatedLocations } : {}
|
|
40415
40473
|
};
|
|
40416
40474
|
}), rootDirectory);
|
|
@@ -40427,6 +40485,11 @@ const buildOxlintChildEnv = (sourceEnv) => {
|
|
|
40427
40485
|
if (!isCompileCacheDisabled && !isCompileCacheAlreadySet) childEnv.NODE_COMPILE_CACHE = Path.join(os.tmpdir(), NODE_COMPILE_CACHE_DIR_NAME);
|
|
40428
40486
|
return childEnv;
|
|
40429
40487
|
};
|
|
40488
|
+
const buildProfiledNodeArguments = (input) => [
|
|
40489
|
+
...input.cpuProfileDirectory ? ["--cpu-prof", `--cpu-prof-dir=${input.cpuProfileDirectory}`] : [],
|
|
40490
|
+
...input.heapProfileDirectory ? ["--heap-prof", `--heap-prof-dir=${input.heapProfileDirectory}`] : [],
|
|
40491
|
+
...input.argumentsList
|
|
40492
|
+
];
|
|
40430
40493
|
const SANITIZED_ENV = buildOxlintChildEnv(process.env);
|
|
40431
40494
|
/**
|
|
40432
40495
|
* Spawn one oxlint subprocess with hard ceilings on wall time and
|
|
@@ -40451,7 +40514,11 @@ const spawnOxlint = (args, rootDirectory, nodeBinaryPath, spawnTimeoutMs = OXLIN
|
|
|
40451
40514
|
reject(new ReactDoctorError({ reason: new OxlintSpawnFailed({ cause: "lint phase aborted" }) }));
|
|
40452
40515
|
return;
|
|
40453
40516
|
}
|
|
40454
|
-
const child = spawn(nodeBinaryPath,
|
|
40517
|
+
const child = spawn(nodeBinaryPath, buildProfiledNodeArguments({
|
|
40518
|
+
argumentsList: args,
|
|
40519
|
+
cpuProfileDirectory: SANITIZED_ENV.REACT_DOCTOR_CPU_PROFILE_DIR,
|
|
40520
|
+
heapProfileDirectory: SANITIZED_ENV.REACT_DOCTOR_HEAP_PROFILE_DIR
|
|
40521
|
+
}), {
|
|
40455
40522
|
cwd: rootDirectory,
|
|
40456
40523
|
env: SANITIZED_ENV,
|
|
40457
40524
|
stdio: [
|
|
@@ -41095,7 +41162,7 @@ const runOxlint = async (options) => {
|
|
|
41095
41162
|
};
|
|
41096
41163
|
}
|
|
41097
41164
|
};
|
|
41098
|
-
if (perFileLintCacheEnabled &&
|
|
41165
|
+
if (perFileLintCacheEnabled && !project.hasReactCompiler && extendsPaths.length === 0 && userPlugins.length === 0) {
|
|
41099
41166
|
const rulesetHash = computeRulesetHash({
|
|
41100
41167
|
config: buildConfig({
|
|
41101
41168
|
extendsPaths: [],
|
|
@@ -41103,7 +41170,8 @@ const runOxlint = async (options) => {
|
|
|
41103
41170
|
}),
|
|
41104
41171
|
toolchainVersions: resolveOxlintToolchainVersions(nodeBinaryPath),
|
|
41105
41172
|
ignorePatterns: combinedPatterns,
|
|
41106
|
-
tsconfigContent
|
|
41173
|
+
tsconfigContent,
|
|
41174
|
+
respectInlineDisables
|
|
41107
41175
|
});
|
|
41108
41176
|
const cache = createFileLintCache(resolveReactDoctorCacheDir(rootDirectory), rulesetHash);
|
|
41109
41177
|
const cacheKeyByFile = /* @__PURE__ */ new Map();
|
|
@@ -41146,7 +41214,8 @@ const runOxlint = async (options) => {
|
|
|
41146
41214
|
}),
|
|
41147
41215
|
toolchainVersions: resolveOxlintToolchainVersions(nodeBinaryPath),
|
|
41148
41216
|
ignorePatterns: combinedPatterns,
|
|
41149
|
-
tsconfigContent
|
|
41217
|
+
tsconfigContent,
|
|
41218
|
+
respectInlineDisables
|
|
41150
41219
|
})) : null;
|
|
41151
41220
|
const probeAnswers = createSidecarProbeAnswerResolver({
|
|
41152
41221
|
rootDirectory,
|
|
@@ -42186,7 +42255,7 @@ const runInspect = (input, hooks = {}) => gen(function* () {
|
|
|
42186
42255
|
directory: scanDirectory,
|
|
42187
42256
|
repo
|
|
42188
42257
|
}).pipe(orElseSucceed(() => null)) : succeed$2(null));
|
|
42189
|
-
const lintIncludePaths = (input.
|
|
42258
|
+
const lintIncludePaths = (input.skipExplicitIncludePathFilter ? input.includePaths.length > 0 ? [...input.includePaths] : void 0 : computeExplicitLintIncludePaths([...input.includePaths])) ?? resolveLintIncludePaths(scanDirectory, resolvedConfig.config);
|
|
42190
42259
|
const fallbackScannedFilePaths = input.suppressScanSummary ? (lintIncludePaths ?? (yield* filesService.listSourceFiles(scanDirectory))).map((relativePath) => Path.resolve(scanDirectory, relativePath)) : [];
|
|
42191
42260
|
const beforeLint = hooks.beforeLint ?? NO_HOOKS.beforeLint;
|
|
42192
42261
|
const afterLint = hooks.afterLint ?? NO_HOOKS.afterLint;
|
|
@@ -42681,7 +42750,7 @@ const runEditorScan = async (input) => {
|
|
|
42681
42750
|
warnings,
|
|
42682
42751
|
isCi: false,
|
|
42683
42752
|
resolveLocalGithubViewerPermission: false,
|
|
42684
|
-
|
|
42753
|
+
skipExplicitIncludePathFilter: true
|
|
42685
42754
|
}).pipe(withSpan("runEditorScan", { attributes: {
|
|
42686
42755
|
"editor.lint": lint,
|
|
42687
42756
|
"editor.runDeadCode": runDeadCode
|
|
@@ -42765,7 +42834,7 @@ const computeConfigFingerprint = (projectDirectory, version) => {
|
|
|
42765
42834
|
/** Display name used in client-facing messages and progress titles. */
|
|
42766
42835
|
const SERVER_DISPLAY_NAME = "React Doctor";
|
|
42767
42836
|
/** Server version reported in `serverInfo`; injected at build, `dev` from source. */
|
|
42768
|
-
const SERVER_VERSION = "0.7.
|
|
42837
|
+
const SERVER_VERSION = "0.7.6";
|
|
42769
42838
|
/** `Diagnostic.source` shown next to every published diagnostic. */
|
|
42770
42839
|
const DIAGNOSTIC_SOURCE = "react-doctor";
|
|
42771
42840
|
/**
|
|
@@ -43444,7 +43513,7 @@ const createLintCache = (input) => {
|
|
|
43444
43513
|
let persistTimer = null;
|
|
43445
43514
|
try {
|
|
43446
43515
|
const parsed = JSON.parse(fs.readFileSync(cacheFilePath, "utf8"));
|
|
43447
|
-
if (parsed.version ===
|
|
43516
|
+
if (parsed.version === 2 && parsed.fingerprint === input.fingerprint) for (const [fsPath, entry] of Object.entries(parsed.entries)) entries.set(fsPath, entry);
|
|
43448
43517
|
} catch {}
|
|
43449
43518
|
const persist = () => {
|
|
43450
43519
|
if (persistTimer) {
|
|
@@ -43455,7 +43524,7 @@ const createLintCache = (input) => {
|
|
|
43455
43524
|
dirty = false;
|
|
43456
43525
|
try {
|
|
43457
43526
|
const payload = {
|
|
43458
|
-
version:
|
|
43527
|
+
version: 2,
|
|
43459
43528
|
fingerprint: input.fingerprint,
|
|
43460
43529
|
entries: Object.fromEntries(entries)
|
|
43461
43530
|
};
|
|
@@ -43468,14 +43537,14 @@ const createLintCache = (input) => {
|
|
|
43468
43537
|
}
|
|
43469
43538
|
};
|
|
43470
43539
|
return {
|
|
43471
|
-
lookup: (fsPath,
|
|
43540
|
+
lookup: (fsPath, identity) => {
|
|
43472
43541
|
const entry = entries.get(fsPath);
|
|
43473
|
-
if (entry !== void 0 && entry.
|
|
43542
|
+
if (entry !== void 0 && entry.contentHash === identity.contentHash) return entry.diagnostics;
|
|
43474
43543
|
return null;
|
|
43475
43544
|
},
|
|
43476
|
-
store: (fsPath,
|
|
43545
|
+
store: (fsPath, identity, diagnostics) => {
|
|
43477
43546
|
entries.set(fsPath, {
|
|
43478
|
-
...
|
|
43547
|
+
...identity,
|
|
43479
43548
|
diagnostics
|
|
43480
43549
|
});
|
|
43481
43550
|
dirty = true;
|
|
@@ -43564,16 +43633,9 @@ const resolveDiagnosticFsPath = (rawFilePath, scanDirectory, projectDirectory, o
|
|
|
43564
43633
|
}
|
|
43565
43634
|
return normalizeFsPath(absolute);
|
|
43566
43635
|
};
|
|
43567
|
-
const
|
|
43568
|
-
|
|
43569
|
-
|
|
43570
|
-
return {
|
|
43571
|
-
mtimeMs: stat.mtimeMs,
|
|
43572
|
-
size: stat.size
|
|
43573
|
-
};
|
|
43574
|
-
} catch {
|
|
43575
|
-
return null;
|
|
43576
|
-
}
|
|
43636
|
+
const readFileIdentity = (fsPath) => {
|
|
43637
|
+
const contentHash = hashFileContents(fsPath);
|
|
43638
|
+
return contentHash === null ? null : { contentHash };
|
|
43577
43639
|
};
|
|
43578
43640
|
/**
|
|
43579
43641
|
* Outcome for a per-file request served without running oxlint (every
|
|
@@ -43599,7 +43661,7 @@ const outcomeWithoutScan = (request, byFile, requestedPaths) => ({
|
|
|
43599
43661
|
* overlay tree (unsaved buffers) or disk, groups diagnostics by canonical
|
|
43600
43662
|
* absolute path, and reports stale-detection metadata.
|
|
43601
43663
|
*
|
|
43602
|
-
* A persistent per-file lint cache (keyed by
|
|
43664
|
+
* A persistent per-file lint cache (keyed by content, namespaced by a
|
|
43603
43665
|
* config fingerprint) short-circuits unchanged files so a re-opened editor
|
|
43604
43666
|
* or repeated workspace scan skips the oxlint subprocess for everything it
|
|
43605
43667
|
* hasn't edited. The cache applies only to disk-based, lint-only, per-file
|
|
@@ -43629,15 +43691,15 @@ const createScanRunner = (options) => {
|
|
|
43629
43691
|
const requestedPaths = request.priority === "background" && !request.useOverlay && options.isOpen ? allRequested.filter((fsPath) => !options.isOpen?.(fsPath)) : allRequested;
|
|
43630
43692
|
const cache = cacheEnabled && !isWholeProject && !request.useOverlay && !request.runDeadCode ? getCache(projectDirectory) : null;
|
|
43631
43693
|
const byFile = /* @__PURE__ */ new Map();
|
|
43632
|
-
const
|
|
43694
|
+
const identityByPath = /* @__PURE__ */ new Map();
|
|
43633
43695
|
let filesToScan = requestedPaths;
|
|
43634
43696
|
if (cache) {
|
|
43635
43697
|
const uncached = [];
|
|
43636
43698
|
for (const fsPath of requestedPaths) {
|
|
43637
|
-
const
|
|
43638
|
-
if (
|
|
43639
|
-
|
|
43640
|
-
const hit = cache.lookup(fsPath,
|
|
43699
|
+
const identity = readFileIdentity(fsPath);
|
|
43700
|
+
if (identity) {
|
|
43701
|
+
identityByPath.set(fsPath, identity);
|
|
43702
|
+
const hit = cache.lookup(fsPath, identity);
|
|
43641
43703
|
if (hit !== null) {
|
|
43642
43704
|
if (hit.length > 0) byFile.set(fsPath, hit);
|
|
43643
43705
|
continue;
|
|
@@ -43680,9 +43742,9 @@ const createScanRunner = (options) => {
|
|
|
43680
43742
|
}
|
|
43681
43743
|
if (cache && result.ok && !result.skipped && !result.didLintFail && result.lintPartialFailures.length === 0) {
|
|
43682
43744
|
for (const fsPath of filesToScan) {
|
|
43683
|
-
const
|
|
43684
|
-
if (!
|
|
43685
|
-
cache.store(fsPath,
|
|
43745
|
+
const identity = identityByPath.get(fsPath);
|
|
43746
|
+
if (!identity) continue;
|
|
43747
|
+
cache.store(fsPath, identity, byFile.get(fsPath) ?? []);
|
|
43686
43748
|
}
|
|
43687
43749
|
cache.schedulePersist();
|
|
43688
43750
|
}
|
|
@@ -44816,5 +44878,5 @@ const startLanguageServer = () => {
|
|
|
44816
44878
|
};
|
|
44817
44879
|
//#endregion
|
|
44818
44880
|
export { startLanguageServer };
|
|
44819
|
-
!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]="
|
|
44820
|
-
//# debugId=
|
|
44881
|
+
!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]="f9337714-b38f-5f28-ac00-c9d7d79ebaea")}catch(e){}}();
|
|
44882
|
+
//# debugId=f9337714-b38f-5f28-ac00-c9d7d79ebaea
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-doctor",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.6-dev.0150fc9",
|
|
4
4
|
"description": "Your agent writes bad React. This catches it",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"accessibility",
|
|
@@ -65,8 +65,8 @@
|
|
|
65
65
|
"vscode-languageserver-textdocument": "^1.0.12",
|
|
66
66
|
"vscode-uri": "^3.1.0",
|
|
67
67
|
"yaml": "^2.9.0",
|
|
68
|
-
"deslop-js": "0.7.
|
|
69
|
-
"oxlint-plugin-react-doctor": "0.7.
|
|
68
|
+
"deslop-js": "0.7.6",
|
|
69
|
+
"oxlint-plugin-react-doctor": "0.7.6-dev.0150fc9"
|
|
70
70
|
},
|
|
71
71
|
"devDependencies": {
|
|
72
72
|
"@types/babel__code-frame": "^7.27.0",
|
|
@@ -74,9 +74,9 @@
|
|
|
74
74
|
"@xterm/headless": "^6.0.0",
|
|
75
75
|
"commander": "^14.0.3",
|
|
76
76
|
"ora": "^9.4.0",
|
|
77
|
-
"@react-doctor/api": "0.7.
|
|
78
|
-
"@react-doctor/
|
|
79
|
-
"@react-doctor/
|
|
77
|
+
"@react-doctor/api": "0.7.6",
|
|
78
|
+
"@react-doctor/language-server": "0.7.6",
|
|
79
|
+
"@react-doctor/core": "0.7.6"
|
|
80
80
|
},
|
|
81
81
|
"engines": {
|
|
82
82
|
"node": "^20.19.0 || >=22.13.0"
|