react-doctor 0.5.6-dev.8908f98 → 0.5.6-dev.93b796d
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 +370 -208
- package/dist/index.d.ts +4 -3
- package/dist/index.js +60 -28
- package/dist/lsp.js +66 -39
- package/package.json +4 -4
package/dist/index.d.ts
CHANGED
|
@@ -9280,9 +9280,10 @@ interface ReactDoctorConfig {
|
|
|
9280
9280
|
* list, user-provided names are treated as distinctive and never
|
|
9281
9281
|
* subject to receiver-object disambiguation.
|
|
9282
9282
|
*
|
|
9283
|
-
*
|
|
9284
|
-
*
|
|
9285
|
-
*
|
|
9283
|
+
* Common guard conventions are already recognized automatically
|
|
9284
|
+
* (`requireAdmin`, `ensureSignedIn`, `getCurrentUser`, `hasRole`, …),
|
|
9285
|
+
* so this is only needed for domain-specific guards whose names carry
|
|
9286
|
+
* no auth noun — e.g. a project-local `requireWorkspaceMember`.
|
|
9286
9287
|
*/
|
|
9287
9288
|
serverAuthFunctionNames?: string[];
|
|
9288
9289
|
/**
|
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]="36898527-7b70-58ed-8b5b-19ca48903764")}catch(e){}}();
|
|
3
3
|
import { r as __toESM$1, t as __commonJSMin$1 } from "./chunk-N93fKeF6.js";
|
|
4
4
|
import { createRequire } from "node:module";
|
|
5
5
|
import * as NFS from "node:fs";
|
|
@@ -33620,6 +33620,11 @@ const ES_TARGET_YEAR_BY_NAME = {
|
|
|
33620
33620
|
esnext: 9999
|
|
33621
33621
|
};
|
|
33622
33622
|
/**
|
|
33623
|
+
* tsconfig filenames probed when resolving a project's TypeScript
|
|
33624
|
+
* compiler options — the root config first, then a monorepo base config.
|
|
33625
|
+
*/
|
|
33626
|
+
const TSCONFIG_FILENAMES = ["tsconfig.json", "tsconfig.base.json"];
|
|
33627
|
+
/**
|
|
33623
33628
|
* Project-config files that `StagedFiles.materialize` copies into
|
|
33624
33629
|
* the temp directory alongside staged sources so oxlint resolves
|
|
33625
33630
|
* `tsconfig` / `package.json` / lint configs the same way it would
|
|
@@ -36705,8 +36710,8 @@ const collectIgnorePatterns = (rootDirectory) => {
|
|
|
36705
36710
|
cachedPatternsByRoot.set(rootDirectory, patterns);
|
|
36706
36711
|
return patterns;
|
|
36707
36712
|
};
|
|
36713
|
+
const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
36708
36714
|
const KNIP_JSON_FILENAME = "knip.json";
|
|
36709
|
-
const isRecord$1 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
36710
36715
|
const readJsonFileSafe = (filePath) => {
|
|
36711
36716
|
let rawContents;
|
|
36712
36717
|
try {
|
|
@@ -36722,10 +36727,10 @@ const readJsonFileSafe = (filePath) => {
|
|
|
36722
36727
|
};
|
|
36723
36728
|
const readKnipConfig = (rootDirectory) => {
|
|
36724
36729
|
const knipJson = readJsonFileSafe(path.join(rootDirectory, KNIP_JSON_FILENAME));
|
|
36725
|
-
if (isRecord
|
|
36730
|
+
if (isRecord(knipJson)) return knipJson;
|
|
36726
36731
|
const packageJson = readJsonFileSafe(path.join(rootDirectory, "package.json"));
|
|
36727
|
-
const packageKnipConfig = isRecord
|
|
36728
|
-
return isRecord
|
|
36732
|
+
const packageKnipConfig = isRecord(packageJson) ? packageJson.knip : null;
|
|
36733
|
+
return isRecord(packageKnipConfig) ? packageKnipConfig : null;
|
|
36729
36734
|
};
|
|
36730
36735
|
const normalizePatternList = (value) => {
|
|
36731
36736
|
if (typeof value === "string" && value.length > 0) return [value];
|
|
@@ -36737,10 +36742,10 @@ const prefixWorkspacePatterns = (workspacePattern, patterns) => {
|
|
|
36737
36742
|
return patterns.map((pattern) => pattern.startsWith("!") ? `!${normalizedWorkspacePattern}/${pattern.slice(1)}` : `${normalizedWorkspacePattern}/${pattern}`);
|
|
36738
36743
|
};
|
|
36739
36744
|
const collectKnipWorkspacePatterns = (workspaces, settingName) => {
|
|
36740
|
-
if (!isRecord
|
|
36745
|
+
if (!isRecord(workspaces)) return [];
|
|
36741
36746
|
const patterns = [];
|
|
36742
36747
|
for (const [workspacePattern, workspaceConfig] of Object.entries(workspaces)) {
|
|
36743
|
-
if (!isRecord
|
|
36748
|
+
if (!isRecord(workspaceConfig)) continue;
|
|
36744
36749
|
patterns.push(...prefixWorkspacePatterns(workspacePattern, normalizePatternList(workspaceConfig[settingName])));
|
|
36745
36750
|
}
|
|
36746
36751
|
return patterns;
|
|
@@ -36750,12 +36755,11 @@ const collectKnipPatterns = (rootDirectory, settingName) => {
|
|
|
36750
36755
|
if (!config) return [];
|
|
36751
36756
|
return [...normalizePatternList(config[settingName]), ...collectKnipWorkspacePatterns(config.workspaces, settingName)];
|
|
36752
36757
|
};
|
|
36753
|
-
const collectDeadCodeIgnorePatterns = (rootDirectory
|
|
36758
|
+
const collectDeadCodeIgnorePatterns = (rootDirectory) => {
|
|
36754
36759
|
const seen = /* @__PURE__ */ new Set();
|
|
36755
36760
|
const sources = [
|
|
36756
36761
|
readIgnoreFile(path.join(rootDirectory, ".gitignore")),
|
|
36757
36762
|
collectIgnorePatterns(rootDirectory),
|
|
36758
|
-
userConfig?.ignore?.files ?? [],
|
|
36759
36763
|
collectKnipPatterns(rootDirectory, "ignore")
|
|
36760
36764
|
];
|
|
36761
36765
|
for (const source of sources) for (const pattern of source) seen.add(pattern);
|
|
@@ -36786,8 +36790,6 @@ const toCanonicalPath = (filePath) => {
|
|
|
36786
36790
|
};
|
|
36787
36791
|
const DEAD_CODE_PLUGIN = "deslop";
|
|
36788
36792
|
const DEAD_CODE_CATEGORY = "Maintainability";
|
|
36789
|
-
const TSCONFIG_FILENAMES$1 = ["tsconfig.json", "tsconfig.base.json"];
|
|
36790
|
-
const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
36791
36793
|
const DEAD_CODE_WORKER_SCRIPT = `
|
|
36792
36794
|
const inputChunks = [];
|
|
36793
36795
|
process.stdin.on("data", (chunk) => inputChunks.push(chunk));
|
|
@@ -36845,7 +36847,7 @@ process.stdin.on("end", () => {
|
|
|
36845
36847
|
});
|
|
36846
36848
|
`;
|
|
36847
36849
|
const resolveTsConfigPath = (rootDirectory) => {
|
|
36848
|
-
for (const filename of TSCONFIG_FILENAMES
|
|
36850
|
+
for (const filename of TSCONFIG_FILENAMES) {
|
|
36849
36851
|
const candidate = Path.join(rootDirectory, filename);
|
|
36850
36852
|
if (NFS.existsSync(candidate)) return candidate;
|
|
36851
36853
|
}
|
|
@@ -37033,11 +37035,10 @@ const runDeadCodeWorkerWithTimeout = (handle, timeoutMs) => new Promise((resolve
|
|
|
37033
37035
|
});
|
|
37034
37036
|
});
|
|
37035
37037
|
const checkDeadCode = async (options) => {
|
|
37036
|
-
const { userConfig } = options;
|
|
37037
37038
|
const rootDirectory = toCanonicalPath(options.rootDirectory);
|
|
37038
37039
|
if (!NFS.existsSync(Path.join(rootDirectory, "package.json"))) return [];
|
|
37039
37040
|
const entryPatterns = collectDeadCodeEntryPatterns(rootDirectory);
|
|
37040
|
-
const ignorePatterns = collectDeadCodeIgnorePatterns(rootDirectory
|
|
37041
|
+
const ignorePatterns = collectDeadCodeIgnorePatterns(rootDirectory);
|
|
37041
37042
|
const result = parseDeadCodeWorkerResult(await runDeadCodeWorkerWithTimeout((options.createWorker ?? createDeadCodeWorker)({
|
|
37042
37043
|
rootDirectory,
|
|
37043
37044
|
entryPatterns,
|
|
@@ -38086,8 +38087,8 @@ const buildUserPluginRules = (userPlugin, severityControls) => {
|
|
|
38086
38087
|
}
|
|
38087
38088
|
return enabled;
|
|
38088
38089
|
};
|
|
38089
|
-
const createOxlintConfig = ({ pluginPath, project, customRulesOnly = false, extendsPaths = [], ignoredTags = /* @__PURE__ */ new Set(), serverAuthFunctionNames, severityControls, userPlugins = [] }) => {
|
|
38090
|
-
const reactHooksJsPlugin = resolveReactHooksJsPlugin(project.hasReactCompiler, customRulesOnly);
|
|
38090
|
+
const createOxlintConfig = ({ pluginPath, project, customRulesOnly = false, extendsPaths = [], ignoredTags = /* @__PURE__ */ new Set(), serverAuthFunctionNames, severityControls, userPlugins = [], disableReactHooksJsPlugin = false }) => {
|
|
38091
|
+
const reactHooksJsPlugin = disableReactHooksJsPlugin ? null : resolveReactHooksJsPlugin(project.hasReactCompiler, customRulesOnly);
|
|
38091
38092
|
const reactCompilerRules = reactHooksJsPlugin ? applyRuleSeverityControls(filterRulesToAvailable(REACT_COMPILER_RULES, "react-hooks-js", reactHooksJsPlugin.availableRuleNames), severityControls) : {};
|
|
38092
38093
|
const jsPlugins = [];
|
|
38093
38094
|
if (reactHooksJsPlugin) jsPlugins.push(reactHooksJsPlugin.entry);
|
|
@@ -38147,7 +38148,6 @@ const resolveOxlintBinary = () => {
|
|
|
38147
38148
|
return Path.join(oxlintPackageDirectory, "bin", "oxlint");
|
|
38148
38149
|
};
|
|
38149
38150
|
const resolvePluginPath = () => esmRequire.resolve("oxlint-plugin-react-doctor");
|
|
38150
|
-
const TSCONFIG_FILENAMES = ["tsconfig.json", "tsconfig.base.json"];
|
|
38151
38151
|
const resolveTsConfigRelativePath = (rootDirectory) => {
|
|
38152
38152
|
for (const filename of TSCONFIG_FILENAMES) if (NFS.existsSync(Path.join(rootDirectory, filename))) return `./${filename}`;
|
|
38153
38153
|
return null;
|
|
@@ -38519,7 +38519,7 @@ const scopeContainsNonImportBinding = (node, scopeNode, identifierName) => {
|
|
|
38519
38519
|
const isIdentifierShadowedByLocalBinding = (identifier, sourceFile) => {
|
|
38520
38520
|
let currentNode = identifier.parent;
|
|
38521
38521
|
while (currentNode) {
|
|
38522
|
-
if (
|
|
38522
|
+
if (isScopeBoundary(currentNode)) {
|
|
38523
38523
|
if (scopeContainsNonImportBinding(currentNode, currentNode, identifier.text)) return true;
|
|
38524
38524
|
}
|
|
38525
38525
|
if (currentNode === sourceFile) return false;
|
|
@@ -38610,11 +38610,10 @@ const findResolutionInScope = (scopeNode, identifierName, reactImportBindings, s
|
|
|
38610
38610
|
});
|
|
38611
38611
|
return resolution;
|
|
38612
38612
|
};
|
|
38613
|
-
const isScopeNode = isScopeBoundary;
|
|
38614
38613
|
const resolveIdentifierBinding = (identifier, reactImportBindings, sourceFile, visitedDeclarations = /* @__PURE__ */ new Set()) => {
|
|
38615
38614
|
let currentNode = identifier.parent;
|
|
38616
38615
|
while (currentNode) {
|
|
38617
|
-
if (
|
|
38616
|
+
if (isScopeBoundary(currentNode)) {
|
|
38618
38617
|
const resolution = findResolutionInScope(currentNode, identifier.text, reactImportBindings, sourceFile, visitedDeclarations);
|
|
38619
38618
|
if (resolution) return resolution;
|
|
38620
38619
|
}
|
|
@@ -38784,9 +38783,9 @@ const parseOxlintOutput = (stdout, project, rootDirectory) => {
|
|
|
38784
38783
|
try {
|
|
38785
38784
|
parsed = JSON.parse(sanitizedStdout);
|
|
38786
38785
|
} catch {
|
|
38787
|
-
throw new ReactDoctorError({ reason: new OxlintOutputUnparseable({ preview: stdout.slice(0,
|
|
38786
|
+
throw new ReactDoctorError({ reason: new OxlintOutputUnparseable({ preview: stdout.slice(0, 600) }) });
|
|
38788
38787
|
}
|
|
38789
|
-
if (!isOxlintOutput(parsed)) throw new ReactDoctorError({ reason: new OxlintOutputUnparseable({ preview: stdout.slice(0,
|
|
38788
|
+
if (!isOxlintOutput(parsed)) throw new ReactDoctorError({ reason: new OxlintOutputUnparseable({ preview: stdout.slice(0, 600) }) });
|
|
38790
38789
|
const minifiedFileCache = /* @__PURE__ */ new Map();
|
|
38791
38790
|
const isMinifiedDiagnosticFile = (filename) => {
|
|
38792
38791
|
const absolutePath = Path.isAbsolute(filename) ? filename : Path.resolve(rootDirectory || ".", filename);
|
|
@@ -39077,6 +39076,28 @@ const writeOxlintConfig = (configPath, configToWrite) => {
|
|
|
39077
39076
|
NFS.closeSync(fileHandle);
|
|
39078
39077
|
}
|
|
39079
39078
|
};
|
|
39079
|
+
const REACT_HOOKS_JS_DROP_PREFIX = "React Compiler rules (react-hooks-js/*) skipped — eslint-plugin-react-hooks failed to load in this environment";
|
|
39080
|
+
/**
|
|
39081
|
+
* Detects an oxlint config-load crash caused by the optional
|
|
39082
|
+
* `react-hooks-js` (eslint-plugin-react-hooks) React Compiler plugin and
|
|
39083
|
+
* builds the partial-failure note for it; returns `null` when the failure
|
|
39084
|
+
* was anything else.
|
|
39085
|
+
*
|
|
39086
|
+
* oxlint prints a framed error to stdout (not stderr) and exits non-zero
|
|
39087
|
+
* when a `jsPlugins` entry can't be imported; that non-JSON stdout
|
|
39088
|
+
* surfaces as `OxlintOutputUnparseable`. Because oxlint fails the WHOLE
|
|
39089
|
+
* config load on it, leaving the plugin in would drop every curated
|
|
39090
|
+
* react-doctor diagnostic too — so the caller retries with the plugin
|
|
39091
|
+
* stripped (issue #833). Both markers sit at the start of oxlint's
|
|
39092
|
+
* message, so they survive the `preview` slice even for deep pnpm paths.
|
|
39093
|
+
*/
|
|
39094
|
+
const reactHooksJsPluginDropNote = (error) => {
|
|
39095
|
+
if (!(error instanceof ReactDoctorError) || error.reason._tag !== "OxlintOutputUnparseable") return null;
|
|
39096
|
+
const { preview } = error.reason;
|
|
39097
|
+
if (!preview.includes("Failed to load JS plugin") || !preview.includes("eslint-plugin-react-hooks")) return null;
|
|
39098
|
+
const underlyingReason = preview.match(/Error:[^\n]*/)?.[0]?.trim();
|
|
39099
|
+
return `${REACT_HOOKS_JS_DROP_PREFIX}${underlyingReason ? `: ${underlyingReason}` : ""}. Other rules ran normally.`;
|
|
39100
|
+
};
|
|
39080
39101
|
/**
|
|
39081
39102
|
* The oxlint runner. Composed of three pieces in `runners/oxlint/`:
|
|
39082
39103
|
*
|
|
@@ -39104,15 +39125,16 @@ const runOxlint = async (options) => {
|
|
|
39104
39125
|
const pluginPath = resolvePluginPath();
|
|
39105
39126
|
const extendsPaths = (adoptExistingLintConfig && !customRulesOnly ? detectUserLintConfigPaths(rootDirectory) : []).filter(canOxlintExtendConfig);
|
|
39106
39127
|
const userPlugins = resolveUserPlugins(userConfig?.plugins, configSourceDirectory);
|
|
39107
|
-
const buildConfig = (
|
|
39128
|
+
const buildConfig = (overrides) => createOxlintConfig({
|
|
39108
39129
|
pluginPath,
|
|
39109
39130
|
project,
|
|
39110
39131
|
customRulesOnly,
|
|
39111
|
-
extendsPaths:
|
|
39132
|
+
extendsPaths: overrides.extendsPaths,
|
|
39112
39133
|
ignoredTags,
|
|
39113
39134
|
serverAuthFunctionNames,
|
|
39114
39135
|
severityControls,
|
|
39115
|
-
userPlugins
|
|
39136
|
+
userPlugins,
|
|
39137
|
+
disableReactHooksJsPlugin: overrides.disableReactHooksJsPlugin
|
|
39116
39138
|
});
|
|
39117
39139
|
const restoreDisableDirectives = respectInlineDisables ? () => {} : await neutralizeDisableDirectives(rootDirectory, includePaths);
|
|
39118
39140
|
const configDirectory = NFS.mkdtempSync(Path.join(os.tmpdir(), "react-doctor-oxlintrc-"));
|
|
@@ -39148,12 +39170,22 @@ const runOxlint = async (options) => {
|
|
|
39148
39170
|
outputMaxBytes,
|
|
39149
39171
|
concurrency: options.concurrency
|
|
39150
39172
|
});
|
|
39151
|
-
writeOxlintConfig(configPath, buildConfig(extendsPaths));
|
|
39173
|
+
writeOxlintConfig(configPath, buildConfig({ extendsPaths }));
|
|
39152
39174
|
try {
|
|
39153
39175
|
return await runBatches();
|
|
39154
39176
|
} catch (error) {
|
|
39177
|
+
const reactHooksJsDropNote = reactHooksJsPluginDropNote(error);
|
|
39178
|
+
if (reactHooksJsDropNote !== null) {
|
|
39179
|
+
writeOxlintConfig(configPath, buildConfig({
|
|
39180
|
+
extendsPaths,
|
|
39181
|
+
disableReactHooksJsPlugin: true
|
|
39182
|
+
}));
|
|
39183
|
+
const diagnostics = await runBatches();
|
|
39184
|
+
onPartialFailure?.(reactHooksJsDropNote);
|
|
39185
|
+
return diagnostics;
|
|
39186
|
+
}
|
|
39155
39187
|
if (extendsPaths.length === 0) throw error;
|
|
39156
|
-
writeOxlintConfig(configPath, buildConfig([]));
|
|
39188
|
+
writeOxlintConfig(configPath, buildConfig({ extendsPaths: [] }));
|
|
39157
39189
|
return await runBatches();
|
|
39158
39190
|
}
|
|
39159
39191
|
} finally {
|
|
@@ -40573,4 +40605,4 @@ const toJsonReport = (result, options) => buildJsonReport({
|
|
|
40573
40605
|
export { AmbiguousProjectError, NoReactDependencyError, NotADirectoryError, PackageJsonNotFoundError, ProjectNotFoundError, ReactDoctorError, buildJsonReport, buildJsonReportError, clearCaches, defineConfig, diagnose, filterSourceFiles, getDiffInfo, isProjectDiscoveryError, isReactDoctorError, summarizeDiagnostics, toJsonReport };
|
|
40574
40606
|
|
|
40575
40607
|
//# sourceMappingURL=index.js.map
|
|
40576
|
-
//# debugId=
|
|
40608
|
+
//# debugId=36898527-7b70-58ed-8b5b-19ca48903764
|
package/dist/lsp.js
CHANGED
|
@@ -33657,6 +33657,11 @@ const ES_TARGET_YEAR_BY_NAME = {
|
|
|
33657
33657
|
esnext: 9999
|
|
33658
33658
|
};
|
|
33659
33659
|
/**
|
|
33660
|
+
* tsconfig filenames probed when resolving a project's TypeScript
|
|
33661
|
+
* compiler options — the root config first, then a monorepo base config.
|
|
33662
|
+
*/
|
|
33663
|
+
const TSCONFIG_FILENAMES = ["tsconfig.json", "tsconfig.base.json"];
|
|
33664
|
+
/**
|
|
33660
33665
|
* Project-config files that `StagedFiles.materialize` copies into
|
|
33661
33666
|
* the temp directory alongside staged sources so oxlint resolves
|
|
33662
33667
|
* `tsconfig` / `package.json` / lint configs the same way it would
|
|
@@ -36691,8 +36696,8 @@ const collectIgnorePatterns = (rootDirectory) => {
|
|
|
36691
36696
|
cachedPatternsByRoot.set(rootDirectory, patterns);
|
|
36692
36697
|
return patterns;
|
|
36693
36698
|
};
|
|
36699
|
+
const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
36694
36700
|
const KNIP_JSON_FILENAME = "knip.json";
|
|
36695
|
-
const isRecord$1 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
36696
36701
|
const readJsonFileSafe = (filePath) => {
|
|
36697
36702
|
let rawContents;
|
|
36698
36703
|
try {
|
|
@@ -36708,10 +36713,10 @@ const readJsonFileSafe = (filePath) => {
|
|
|
36708
36713
|
};
|
|
36709
36714
|
const readKnipConfig = (rootDirectory) => {
|
|
36710
36715
|
const knipJson = readJsonFileSafe(path.join(rootDirectory, KNIP_JSON_FILENAME));
|
|
36711
|
-
if (isRecord
|
|
36716
|
+
if (isRecord(knipJson)) return knipJson;
|
|
36712
36717
|
const packageJson = readJsonFileSafe(path.join(rootDirectory, "package.json"));
|
|
36713
|
-
const packageKnipConfig = isRecord
|
|
36714
|
-
return isRecord
|
|
36718
|
+
const packageKnipConfig = isRecord(packageJson) ? packageJson.knip : null;
|
|
36719
|
+
return isRecord(packageKnipConfig) ? packageKnipConfig : null;
|
|
36715
36720
|
};
|
|
36716
36721
|
const normalizePatternList = (value) => {
|
|
36717
36722
|
if (typeof value === "string" && value.length > 0) return [value];
|
|
@@ -36723,10 +36728,10 @@ const prefixWorkspacePatterns = (workspacePattern, patterns) => {
|
|
|
36723
36728
|
return patterns.map((pattern) => pattern.startsWith("!") ? `!${normalizedWorkspacePattern}/${pattern.slice(1)}` : `${normalizedWorkspacePattern}/${pattern}`);
|
|
36724
36729
|
};
|
|
36725
36730
|
const collectKnipWorkspacePatterns = (workspaces, settingName) => {
|
|
36726
|
-
if (!isRecord
|
|
36731
|
+
if (!isRecord(workspaces)) return [];
|
|
36727
36732
|
const patterns = [];
|
|
36728
36733
|
for (const [workspacePattern, workspaceConfig] of Object.entries(workspaces)) {
|
|
36729
|
-
if (!isRecord
|
|
36734
|
+
if (!isRecord(workspaceConfig)) continue;
|
|
36730
36735
|
patterns.push(...prefixWorkspacePatterns(workspacePattern, normalizePatternList(workspaceConfig[settingName])));
|
|
36731
36736
|
}
|
|
36732
36737
|
return patterns;
|
|
@@ -36736,12 +36741,11 @@ const collectKnipPatterns = (rootDirectory, settingName) => {
|
|
|
36736
36741
|
if (!config) return [];
|
|
36737
36742
|
return [...normalizePatternList(config[settingName]), ...collectKnipWorkspacePatterns(config.workspaces, settingName)];
|
|
36738
36743
|
};
|
|
36739
|
-
const collectDeadCodeIgnorePatterns = (rootDirectory
|
|
36744
|
+
const collectDeadCodeIgnorePatterns = (rootDirectory) => {
|
|
36740
36745
|
const seen = /* @__PURE__ */ new Set();
|
|
36741
36746
|
const sources = [
|
|
36742
36747
|
readIgnoreFile(path.join(rootDirectory, ".gitignore")),
|
|
36743
36748
|
collectIgnorePatterns(rootDirectory),
|
|
36744
|
-
userConfig?.ignore?.files ?? [],
|
|
36745
36749
|
collectKnipPatterns(rootDirectory, "ignore")
|
|
36746
36750
|
];
|
|
36747
36751
|
for (const source of sources) for (const pattern of source) seen.add(pattern);
|
|
@@ -36772,8 +36776,6 @@ const toCanonicalPath = (filePath) => {
|
|
|
36772
36776
|
};
|
|
36773
36777
|
const DEAD_CODE_PLUGIN = "deslop";
|
|
36774
36778
|
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
36779
|
const DEAD_CODE_WORKER_SCRIPT = `
|
|
36778
36780
|
const inputChunks = [];
|
|
36779
36781
|
process.stdin.on("data", (chunk) => inputChunks.push(chunk));
|
|
@@ -36831,7 +36833,7 @@ process.stdin.on("end", () => {
|
|
|
36831
36833
|
});
|
|
36832
36834
|
`;
|
|
36833
36835
|
const resolveTsConfigPath = (rootDirectory) => {
|
|
36834
|
-
for (const filename of TSCONFIG_FILENAMES
|
|
36836
|
+
for (const filename of TSCONFIG_FILENAMES) {
|
|
36835
36837
|
const candidate = Path.join(rootDirectory, filename);
|
|
36836
36838
|
if (NFS.existsSync(candidate)) return candidate;
|
|
36837
36839
|
}
|
|
@@ -37019,11 +37021,10 @@ const runDeadCodeWorkerWithTimeout = (handle, timeoutMs) => new Promise((resolve
|
|
|
37019
37021
|
});
|
|
37020
37022
|
});
|
|
37021
37023
|
const checkDeadCode = async (options) => {
|
|
37022
|
-
const { userConfig } = options;
|
|
37023
37024
|
const rootDirectory = toCanonicalPath(options.rootDirectory);
|
|
37024
37025
|
if (!NFS.existsSync(Path.join(rootDirectory, "package.json"))) return [];
|
|
37025
37026
|
const entryPatterns = collectDeadCodeEntryPatterns(rootDirectory);
|
|
37026
|
-
const ignorePatterns = collectDeadCodeIgnorePatterns(rootDirectory
|
|
37027
|
+
const ignorePatterns = collectDeadCodeIgnorePatterns(rootDirectory);
|
|
37027
37028
|
const result = parseDeadCodeWorkerResult(await runDeadCodeWorkerWithTimeout((options.createWorker ?? createDeadCodeWorker)({
|
|
37028
37029
|
rootDirectory,
|
|
37029
37030
|
entryPatterns,
|
|
@@ -38072,8 +38073,8 @@ const buildUserPluginRules = (userPlugin, severityControls) => {
|
|
|
38072
38073
|
}
|
|
38073
38074
|
return enabled;
|
|
38074
38075
|
};
|
|
38075
|
-
const createOxlintConfig = ({ pluginPath, project, customRulesOnly = false, extendsPaths = [], ignoredTags = /* @__PURE__ */ new Set(), serverAuthFunctionNames, severityControls, userPlugins = [] }) => {
|
|
38076
|
-
const reactHooksJsPlugin = resolveReactHooksJsPlugin(project.hasReactCompiler, customRulesOnly);
|
|
38076
|
+
const createOxlintConfig = ({ pluginPath, project, customRulesOnly = false, extendsPaths = [], ignoredTags = /* @__PURE__ */ new Set(), serverAuthFunctionNames, severityControls, userPlugins = [], disableReactHooksJsPlugin = false }) => {
|
|
38077
|
+
const reactHooksJsPlugin = disableReactHooksJsPlugin ? null : resolveReactHooksJsPlugin(project.hasReactCompiler, customRulesOnly);
|
|
38077
38078
|
const reactCompilerRules = reactHooksJsPlugin ? applyRuleSeverityControls(filterRulesToAvailable(REACT_COMPILER_RULES, "react-hooks-js", reactHooksJsPlugin.availableRuleNames), severityControls) : {};
|
|
38078
38079
|
const jsPlugins = [];
|
|
38079
38080
|
if (reactHooksJsPlugin) jsPlugins.push(reactHooksJsPlugin.entry);
|
|
@@ -38133,7 +38134,6 @@ const resolveOxlintBinary = () => {
|
|
|
38133
38134
|
return Path.join(oxlintPackageDirectory, "bin", "oxlint");
|
|
38134
38135
|
};
|
|
38135
38136
|
const resolvePluginPath = () => esmRequire.resolve("oxlint-plugin-react-doctor");
|
|
38136
|
-
const TSCONFIG_FILENAMES = ["tsconfig.json", "tsconfig.base.json"];
|
|
38137
38137
|
const resolveTsConfigRelativePath = (rootDirectory) => {
|
|
38138
38138
|
for (const filename of TSCONFIG_FILENAMES) if (NFS.existsSync(Path.join(rootDirectory, filename))) return `./${filename}`;
|
|
38139
38139
|
return null;
|
|
@@ -38505,7 +38505,7 @@ const scopeContainsNonImportBinding = (node, scopeNode, identifierName) => {
|
|
|
38505
38505
|
const isIdentifierShadowedByLocalBinding = (identifier, sourceFile) => {
|
|
38506
38506
|
let currentNode = identifier.parent;
|
|
38507
38507
|
while (currentNode) {
|
|
38508
|
-
if (
|
|
38508
|
+
if (isScopeBoundary(currentNode)) {
|
|
38509
38509
|
if (scopeContainsNonImportBinding(currentNode, currentNode, identifier.text)) return true;
|
|
38510
38510
|
}
|
|
38511
38511
|
if (currentNode === sourceFile) return false;
|
|
@@ -38596,11 +38596,10 @@ const findResolutionInScope = (scopeNode, identifierName, reactImportBindings, s
|
|
|
38596
38596
|
});
|
|
38597
38597
|
return resolution;
|
|
38598
38598
|
};
|
|
38599
|
-
const isScopeNode = isScopeBoundary;
|
|
38600
38599
|
const resolveIdentifierBinding = (identifier, reactImportBindings, sourceFile, visitedDeclarations = /* @__PURE__ */ new Set()) => {
|
|
38601
38600
|
let currentNode = identifier.parent;
|
|
38602
38601
|
while (currentNode) {
|
|
38603
|
-
if (
|
|
38602
|
+
if (isScopeBoundary(currentNode)) {
|
|
38604
38603
|
const resolution = findResolutionInScope(currentNode, identifier.text, reactImportBindings, sourceFile, visitedDeclarations);
|
|
38605
38604
|
if (resolution) return resolution;
|
|
38606
38605
|
}
|
|
@@ -38770,9 +38769,9 @@ const parseOxlintOutput = (stdout, project, rootDirectory) => {
|
|
|
38770
38769
|
try {
|
|
38771
38770
|
parsed = JSON.parse(sanitizedStdout);
|
|
38772
38771
|
} catch {
|
|
38773
|
-
throw new ReactDoctorError({ reason: new OxlintOutputUnparseable({ preview: stdout.slice(0,
|
|
38772
|
+
throw new ReactDoctorError({ reason: new OxlintOutputUnparseable({ preview: stdout.slice(0, 600) }) });
|
|
38774
38773
|
}
|
|
38775
|
-
if (!isOxlintOutput(parsed)) throw new ReactDoctorError({ reason: new OxlintOutputUnparseable({ preview: stdout.slice(0,
|
|
38774
|
+
if (!isOxlintOutput(parsed)) throw new ReactDoctorError({ reason: new OxlintOutputUnparseable({ preview: stdout.slice(0, 600) }) });
|
|
38776
38775
|
const minifiedFileCache = /* @__PURE__ */ new Map();
|
|
38777
38776
|
const isMinifiedDiagnosticFile = (filename) => {
|
|
38778
38777
|
const absolutePath = Path.isAbsolute(filename) ? filename : Path.resolve(rootDirectory || ".", filename);
|
|
@@ -39063,6 +39062,28 @@ const writeOxlintConfig = (configPath, configToWrite) => {
|
|
|
39063
39062
|
NFS.closeSync(fileHandle);
|
|
39064
39063
|
}
|
|
39065
39064
|
};
|
|
39065
|
+
const REACT_HOOKS_JS_DROP_PREFIX = "React Compiler rules (react-hooks-js/*) skipped — eslint-plugin-react-hooks failed to load in this environment";
|
|
39066
|
+
/**
|
|
39067
|
+
* Detects an oxlint config-load crash caused by the optional
|
|
39068
|
+
* `react-hooks-js` (eslint-plugin-react-hooks) React Compiler plugin and
|
|
39069
|
+
* builds the partial-failure note for it; returns `null` when the failure
|
|
39070
|
+
* was anything else.
|
|
39071
|
+
*
|
|
39072
|
+
* oxlint prints a framed error to stdout (not stderr) and exits non-zero
|
|
39073
|
+
* when a `jsPlugins` entry can't be imported; that non-JSON stdout
|
|
39074
|
+
* surfaces as `OxlintOutputUnparseable`. Because oxlint fails the WHOLE
|
|
39075
|
+
* config load on it, leaving the plugin in would drop every curated
|
|
39076
|
+
* react-doctor diagnostic too — so the caller retries with the plugin
|
|
39077
|
+
* stripped (issue #833). Both markers sit at the start of oxlint's
|
|
39078
|
+
* message, so they survive the `preview` slice even for deep pnpm paths.
|
|
39079
|
+
*/
|
|
39080
|
+
const reactHooksJsPluginDropNote = (error) => {
|
|
39081
|
+
if (!(error instanceof ReactDoctorError) || error.reason._tag !== "OxlintOutputUnparseable") return null;
|
|
39082
|
+
const { preview } = error.reason;
|
|
39083
|
+
if (!preview.includes("Failed to load JS plugin") || !preview.includes("eslint-plugin-react-hooks")) return null;
|
|
39084
|
+
const underlyingReason = preview.match(/Error:[^\n]*/)?.[0]?.trim();
|
|
39085
|
+
return `${REACT_HOOKS_JS_DROP_PREFIX}${underlyingReason ? `: ${underlyingReason}` : ""}. Other rules ran normally.`;
|
|
39086
|
+
};
|
|
39066
39087
|
/**
|
|
39067
39088
|
* The oxlint runner. Composed of three pieces in `runners/oxlint/`:
|
|
39068
39089
|
*
|
|
@@ -39090,15 +39111,16 @@ const runOxlint = async (options) => {
|
|
|
39090
39111
|
const pluginPath = resolvePluginPath();
|
|
39091
39112
|
const extendsPaths = (adoptExistingLintConfig && !customRulesOnly ? detectUserLintConfigPaths(rootDirectory) : []).filter(canOxlintExtendConfig);
|
|
39092
39113
|
const userPlugins = resolveUserPlugins(userConfig?.plugins, configSourceDirectory);
|
|
39093
|
-
const buildConfig = (
|
|
39114
|
+
const buildConfig = (overrides) => createOxlintConfig({
|
|
39094
39115
|
pluginPath,
|
|
39095
39116
|
project,
|
|
39096
39117
|
customRulesOnly,
|
|
39097
|
-
extendsPaths:
|
|
39118
|
+
extendsPaths: overrides.extendsPaths,
|
|
39098
39119
|
ignoredTags,
|
|
39099
39120
|
serverAuthFunctionNames,
|
|
39100
39121
|
severityControls,
|
|
39101
|
-
userPlugins
|
|
39122
|
+
userPlugins,
|
|
39123
|
+
disableReactHooksJsPlugin: overrides.disableReactHooksJsPlugin
|
|
39102
39124
|
});
|
|
39103
39125
|
const restoreDisableDirectives = respectInlineDisables ? () => {} : await neutralizeDisableDirectives(rootDirectory, includePaths);
|
|
39104
39126
|
const configDirectory = NFS.mkdtempSync(Path.join(os.tmpdir(), "react-doctor-oxlintrc-"));
|
|
@@ -39134,12 +39156,22 @@ const runOxlint = async (options) => {
|
|
|
39134
39156
|
outputMaxBytes,
|
|
39135
39157
|
concurrency: options.concurrency
|
|
39136
39158
|
});
|
|
39137
|
-
writeOxlintConfig(configPath, buildConfig(extendsPaths));
|
|
39159
|
+
writeOxlintConfig(configPath, buildConfig({ extendsPaths }));
|
|
39138
39160
|
try {
|
|
39139
39161
|
return await runBatches();
|
|
39140
39162
|
} catch (error) {
|
|
39163
|
+
const reactHooksJsDropNote = reactHooksJsPluginDropNote(error);
|
|
39164
|
+
if (reactHooksJsDropNote !== null) {
|
|
39165
|
+
writeOxlintConfig(configPath, buildConfig({
|
|
39166
|
+
extendsPaths,
|
|
39167
|
+
disableReactHooksJsPlugin: true
|
|
39168
|
+
}));
|
|
39169
|
+
const diagnostics = await runBatches();
|
|
39170
|
+
onPartialFailure?.(reactHooksJsDropNote);
|
|
39171
|
+
return diagnostics;
|
|
39172
|
+
}
|
|
39141
39173
|
if (extendsPaths.length === 0) throw error;
|
|
39142
|
-
writeOxlintConfig(configPath, buildConfig([]));
|
|
39174
|
+
writeOxlintConfig(configPath, buildConfig({ extendsPaths: [] }));
|
|
39143
39175
|
return await runBatches();
|
|
39144
39176
|
}
|
|
39145
39177
|
} finally {
|
|
@@ -40971,6 +41003,11 @@ const createProjectGraph = (options) => {
|
|
|
40971
41003
|
}
|
|
40972
41004
|
};
|
|
40973
41005
|
};
|
|
41006
|
+
const toProjectRelative = (projectDirectory, filePath) => {
|
|
41007
|
+
const relative = Path.relative(projectDirectory, filePath).replace(/\\/g, "/");
|
|
41008
|
+
if (relative.length === 0 || relative.startsWith("../") || Path.isAbsolute(relative)) return null;
|
|
41009
|
+
return relative;
|
|
41010
|
+
};
|
|
40974
41011
|
const resolveCacheFilePath = (projectDirectory) => {
|
|
40975
41012
|
const nodeModules = path.join(projectDirectory, "node_modules");
|
|
40976
41013
|
if (fs.existsSync(nodeModules)) return path.join(nodeModules, ".cache", "react-doctor", "lint-cache.json");
|
|
@@ -41031,11 +41068,6 @@ const createLintCache = (input) => {
|
|
|
41031
41068
|
};
|
|
41032
41069
|
const OVERLAY_TEMP_PREFIX = "react-doctor-lsp-";
|
|
41033
41070
|
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
41071
|
/**
|
|
41040
41072
|
* Writes the live (possibly unsaved) content of the target files into a
|
|
41041
41073
|
* throwaway temp tree that mirrors the project, alongside the well-known
|
|
@@ -41049,7 +41081,7 @@ const materializeOverlay = (input) => {
|
|
|
41049
41081
|
const relativePaths = [];
|
|
41050
41082
|
try {
|
|
41051
41083
|
for (const filePath of input.files) {
|
|
41052
|
-
const relative = toProjectRelative
|
|
41084
|
+
const relative = toProjectRelative(input.projectDirectory, filePath);
|
|
41053
41085
|
if (relative === null) continue;
|
|
41054
41086
|
const content = input.readText(filePath);
|
|
41055
41087
|
if (content === null) continue;
|
|
@@ -41097,11 +41129,6 @@ const materializeOverlay = (input) => {
|
|
|
41097
41129
|
throw error;
|
|
41098
41130
|
}
|
|
41099
41131
|
};
|
|
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
41132
|
/**
|
|
41106
41133
|
* Resolves a diagnostic's (possibly relative, possibly overlay-temp)
|
|
41107
41134
|
* file path back to the canonical absolute path inside the real project.
|
|
@@ -42358,5 +42385,5 @@ const startLanguageServer = () => {
|
|
|
42358
42385
|
};
|
|
42359
42386
|
//#endregion
|
|
42360
42387
|
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=
|
|
42388
|
+
!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]="42683a4c-8da1-5155-bf9c-b9a69b9718e6")}catch(e){}}();
|
|
42389
|
+
//# debugId=42683a4c-8da1-5155-bf9c-b9a69b9718e6
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-doctor",
|
|
3
|
-
"version": "0.5.6-dev.
|
|
3
|
+
"version": "0.5.6-dev.93b796d",
|
|
4
4
|
"description": "Your agent writes bad React. This catches it",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"accessibility",
|
|
@@ -64,7 +64,7 @@
|
|
|
64
64
|
"vscode-languageserver": "^9.0.1",
|
|
65
65
|
"vscode-languageserver-textdocument": "^1.0.12",
|
|
66
66
|
"vscode-uri": "^3.1.0",
|
|
67
|
-
"oxlint-plugin-react-doctor": "0.5.6-dev.
|
|
67
|
+
"oxlint-plugin-react-doctor": "0.5.6-dev.93b796d"
|
|
68
68
|
},
|
|
69
69
|
"devDependencies": {
|
|
70
70
|
"@types/babel__code-frame": "^7.27.0",
|
|
@@ -72,9 +72,9 @@
|
|
|
72
72
|
"@xterm/headless": "^6.0.0",
|
|
73
73
|
"commander": "^14.0.3",
|
|
74
74
|
"ora": "^9.4.0",
|
|
75
|
-
"@react-doctor/
|
|
75
|
+
"@react-doctor/core": "0.5.6",
|
|
76
76
|
"@react-doctor/language-server": "0.5.6",
|
|
77
|
-
"@react-doctor/
|
|
77
|
+
"@react-doctor/api": "0.5.6"
|
|
78
78
|
},
|
|
79
79
|
"engines": {
|
|
80
80
|
"node": "^20.19.0 || >=22.13.0"
|