react-doctor 0.0.47 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +124 -233
- package/dist/cli.js +941 -226
- package/dist/eslint-plugin.d.ts +57 -0
- package/dist/eslint-plugin.js +6965 -0
- package/dist/index.d.ts +33 -2
- package/dist/index.js +908 -398
- package/dist/react-doctor-plugin.js +2010 -320
- package/package.json +9 -13
- package/dist/browser-BOxs7MrK.js +0 -359
- package/dist/browser-Dcq3yn-p.d.ts +0 -146
- package/dist/browser.d.ts +0 -2
- package/dist/browser.js +0 -2
- package/dist/worker.d.ts +0 -2
- package/dist/worker.js +0 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-doctor",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "Diagnose and fix React codebases for security, performance, correctness, accessibility, bundle-size, and architecture issues",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"accessibility",
|
|
@@ -48,32 +48,28 @@
|
|
|
48
48
|
"types": "./dist/index.d.ts",
|
|
49
49
|
"default": "./dist/index.js"
|
|
50
50
|
},
|
|
51
|
-
"./browser": {
|
|
52
|
-
"types": "./dist/browser.d.ts",
|
|
53
|
-
"default": "./dist/browser.js"
|
|
54
|
-
},
|
|
55
51
|
"./oxlint-plugin": {
|
|
56
52
|
"types": "./dist/react-doctor-plugin.d.ts",
|
|
57
53
|
"default": "./dist/react-doctor-plugin.js"
|
|
58
54
|
},
|
|
59
|
-
"./
|
|
60
|
-
"types": "./dist/
|
|
61
|
-
"default": "./dist/
|
|
55
|
+
"./eslint-plugin": {
|
|
56
|
+
"types": "./dist/eslint-plugin.d.ts",
|
|
57
|
+
"default": "./dist/eslint-plugin.js"
|
|
62
58
|
}
|
|
63
59
|
},
|
|
64
60
|
"dependencies": {
|
|
65
|
-
"agent-install": "0.0.
|
|
61
|
+
"agent-install": "0.0.5",
|
|
66
62
|
"commander": "^14.0.3",
|
|
67
|
-
"knip": "^6.
|
|
68
|
-
"ora": "^9.
|
|
69
|
-
"oxlint": "^1.
|
|
63
|
+
"knip": "^6.10.0",
|
|
64
|
+
"ora": "^9.4.0",
|
|
65
|
+
"oxlint": "^1.63.0",
|
|
70
66
|
"picocolors": "^1.1.1",
|
|
71
67
|
"prompts": "^2.4.2",
|
|
72
68
|
"typescript": ">=5.0.4 <7"
|
|
73
69
|
},
|
|
74
70
|
"devDependencies": {
|
|
75
71
|
"@types/prompts": "^2.4.9",
|
|
76
|
-
"eslint-plugin-react-hooks": "^7.
|
|
72
|
+
"eslint-plugin-react-hooks": "^7.1.1"
|
|
77
73
|
},
|
|
78
74
|
"peerDependencies": {
|
|
79
75
|
"eslint-plugin-react-hooks": "^6 || ^7"
|
package/dist/browser-BOxs7MrK.js
DELETED
|
@@ -1,359 +0,0 @@
|
|
|
1
|
-
//#region src/constants.ts
|
|
2
|
-
const JSX_FILE_PATTERN = /\.(tsx|jsx)$/;
|
|
3
|
-
const SCORE_API_URL = "https://www.react.doctor/api/score";
|
|
4
|
-
const FETCH_TIMEOUT_MS = 1e4;
|
|
5
|
-
const ERROR_RULE_PENALTY = 1.5;
|
|
6
|
-
const WARNING_RULE_PENALTY = .75;
|
|
7
|
-
const buildNoReactDependencyError = (directory) => `No React dependency found in ${directory}/package.json. Add "react" to dependencies (or peerDependencies) and re-run.`;
|
|
8
|
-
//#endregion
|
|
9
|
-
//#region src/core/calculate-score-locally.ts
|
|
10
|
-
const getScoreLabel = (score) => {
|
|
11
|
-
if (score >= 75) return "Great";
|
|
12
|
-
if (score >= 50) return "Needs work";
|
|
13
|
-
return "Critical";
|
|
14
|
-
};
|
|
15
|
-
const countUniqueRules = (diagnostics) => {
|
|
16
|
-
const errorRules = /* @__PURE__ */ new Set();
|
|
17
|
-
const warningRules = /* @__PURE__ */ new Set();
|
|
18
|
-
for (const diagnostic of diagnostics) {
|
|
19
|
-
const ruleKey = `${diagnostic.plugin}/${diagnostic.rule}`;
|
|
20
|
-
if (diagnostic.severity === "error") errorRules.add(ruleKey);
|
|
21
|
-
else warningRules.add(ruleKey);
|
|
22
|
-
}
|
|
23
|
-
return {
|
|
24
|
-
errorRuleCount: errorRules.size,
|
|
25
|
-
warningRuleCount: warningRules.size
|
|
26
|
-
};
|
|
27
|
-
};
|
|
28
|
-
const scoreFromRuleCounts = (errorRuleCount, warningRuleCount) => {
|
|
29
|
-
const penalty = errorRuleCount * ERROR_RULE_PENALTY + warningRuleCount * WARNING_RULE_PENALTY;
|
|
30
|
-
return Math.max(0, Math.round(100 - penalty));
|
|
31
|
-
};
|
|
32
|
-
const calculateScoreLocally = (diagnostics) => {
|
|
33
|
-
const { errorRuleCount, warningRuleCount } = countUniqueRules(diagnostics);
|
|
34
|
-
const score = scoreFromRuleCounts(errorRuleCount, warningRuleCount);
|
|
35
|
-
return {
|
|
36
|
-
score,
|
|
37
|
-
label: getScoreLabel(score)
|
|
38
|
-
};
|
|
39
|
-
};
|
|
40
|
-
//#endregion
|
|
41
|
-
//#region src/core/try-score-from-api.ts
|
|
42
|
-
const parseScoreResult = (value) => {
|
|
43
|
-
if (typeof value !== "object" || value === null) return null;
|
|
44
|
-
if (!("score" in value) || !("label" in value)) return null;
|
|
45
|
-
const scoreValue = Reflect.get(value, "score");
|
|
46
|
-
const labelValue = Reflect.get(value, "label");
|
|
47
|
-
if (typeof scoreValue !== "number" || typeof labelValue !== "string") return null;
|
|
48
|
-
return {
|
|
49
|
-
score: scoreValue,
|
|
50
|
-
label: labelValue
|
|
51
|
-
};
|
|
52
|
-
};
|
|
53
|
-
const stripFilePaths = (diagnostics) => diagnostics.map(({ filePath: _filePath, ...rest }) => rest);
|
|
54
|
-
const isAbortError = (error) => error instanceof Error && (error.name === "AbortError" || error.name === "TimeoutError");
|
|
55
|
-
const describeFailure = (error) => {
|
|
56
|
-
if (isAbortError(error)) return `timed out after ${FETCH_TIMEOUT_MS / 1e3}s`;
|
|
57
|
-
if (error instanceof Error && error.message) return error.message;
|
|
58
|
-
return String(error);
|
|
59
|
-
};
|
|
60
|
-
const tryScoreFromApi = async (diagnostics, fetchImplementation) => {
|
|
61
|
-
if (typeof fetchImplementation !== "function") return null;
|
|
62
|
-
const controller = new AbortController();
|
|
63
|
-
const timeoutId = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
|
|
64
|
-
try {
|
|
65
|
-
const response = await fetchImplementation(SCORE_API_URL, {
|
|
66
|
-
method: "POST",
|
|
67
|
-
headers: { "Content-Type": "application/json" },
|
|
68
|
-
body: JSON.stringify({ diagnostics: stripFilePaths(diagnostics) }),
|
|
69
|
-
signal: controller.signal
|
|
70
|
-
});
|
|
71
|
-
if (!response.ok) {
|
|
72
|
-
console.warn(`[react-doctor] Score API returned ${response.status} ${response.statusText} — using local scoring`);
|
|
73
|
-
return null;
|
|
74
|
-
}
|
|
75
|
-
return parseScoreResult(await response.json());
|
|
76
|
-
} catch (error) {
|
|
77
|
-
console.warn(`[react-doctor] Score API unreachable (${describeFailure(error)}) — using local scoring`);
|
|
78
|
-
return null;
|
|
79
|
-
} finally {
|
|
80
|
-
clearTimeout(timeoutId);
|
|
81
|
-
}
|
|
82
|
-
};
|
|
83
|
-
//#endregion
|
|
84
|
-
//#region src/utils/calculate-score-browser.ts
|
|
85
|
-
const getGlobalFetch = () => typeof fetch === "function" ? fetch : void 0;
|
|
86
|
-
const calculateScore = async (diagnostics, fetchImplementation = getGlobalFetch()) => await tryScoreFromApi(diagnostics, fetchImplementation) ?? calculateScoreLocally(diagnostics);
|
|
87
|
-
//#endregion
|
|
88
|
-
//#region src/utils/match-glob-pattern.ts
|
|
89
|
-
const REGEX_SPECIAL_CHARACTERS = /[.+^${}()|[\]\\]/g;
|
|
90
|
-
const compileGlobPattern = (pattern) => {
|
|
91
|
-
const normalizedPattern = pattern.replace(/\\/g, "/").replace(/^\//, "");
|
|
92
|
-
let regexSource = "^";
|
|
93
|
-
let characterIndex = 0;
|
|
94
|
-
while (characterIndex < normalizedPattern.length) if (normalizedPattern[characterIndex] === "*" && normalizedPattern[characterIndex + 1] === "*") if (normalizedPattern[characterIndex + 2] === "/") {
|
|
95
|
-
regexSource += "(?:.+/)?";
|
|
96
|
-
characterIndex += 3;
|
|
97
|
-
} else {
|
|
98
|
-
regexSource += ".*";
|
|
99
|
-
characterIndex += 2;
|
|
100
|
-
}
|
|
101
|
-
else if (normalizedPattern[characterIndex] === "*") {
|
|
102
|
-
regexSource += "[^/]*";
|
|
103
|
-
characterIndex++;
|
|
104
|
-
} else if (normalizedPattern[characterIndex] === "?") {
|
|
105
|
-
regexSource += "[^/]";
|
|
106
|
-
characterIndex++;
|
|
107
|
-
} else {
|
|
108
|
-
regexSource += normalizedPattern[characterIndex].replace(REGEX_SPECIAL_CHARACTERS, "\\$&");
|
|
109
|
-
characterIndex++;
|
|
110
|
-
}
|
|
111
|
-
regexSource += "$";
|
|
112
|
-
return new RegExp(regexSource);
|
|
113
|
-
};
|
|
114
|
-
//#endregion
|
|
115
|
-
//#region src/utils/is-ignored-file.ts
|
|
116
|
-
const toRelativePath = (filePath, rootDirectory) => {
|
|
117
|
-
const normalizedFilePath = filePath.replace(/\\/g, "/");
|
|
118
|
-
const normalizedRoot = rootDirectory.replace(/\\/g, "/").replace(/\/$/, "") + "/";
|
|
119
|
-
if (normalizedFilePath.startsWith(normalizedRoot)) return normalizedFilePath.slice(normalizedRoot.length);
|
|
120
|
-
return normalizedFilePath.replace(/^\.\//, "");
|
|
121
|
-
};
|
|
122
|
-
const compileIgnoredFilePatterns = (userConfig) => {
|
|
123
|
-
const files = userConfig?.ignore?.files;
|
|
124
|
-
if (!Array.isArray(files)) return [];
|
|
125
|
-
return files.filter((entry) => typeof entry === "string").map(compileGlobPattern);
|
|
126
|
-
};
|
|
127
|
-
const isFileIgnoredByPatterns = (filePath, rootDirectory, patterns) => {
|
|
128
|
-
if (patterns.length === 0) return false;
|
|
129
|
-
const relativePath = toRelativePath(filePath, rootDirectory);
|
|
130
|
-
return patterns.some((pattern) => pattern.test(relativePath));
|
|
131
|
-
};
|
|
132
|
-
//#endregion
|
|
133
|
-
//#region src/utils/filter-diagnostics.ts
|
|
134
|
-
const resolveCandidateReadPath = (rootDirectory, filePath) => {
|
|
135
|
-
const normalizedFile = filePath.replace(/\\/g, "/");
|
|
136
|
-
if (normalizedFile.startsWith("/") || /^[a-zA-Z]:\//.test(normalizedFile) || /^[a-zA-Z]:\\/.test(filePath)) return filePath;
|
|
137
|
-
return `${rootDirectory.replace(/\\/g, "/").replace(/\/$/, "")}/${normalizedFile.replace(/^\.\//, "")}`;
|
|
138
|
-
};
|
|
139
|
-
const OPENING_TAG_PATTERN = /<([A-Z][\w.]*)/;
|
|
140
|
-
const DISABLE_NEXT_LINE_PATTERN = /\/\/\s*react-doctor-disable-next-line\b(?:\s+(.+))?/;
|
|
141
|
-
const DISABLE_LINE_PATTERN = /\/\/\s*react-doctor-disable-line\b(?:\s+(.+))?/;
|
|
142
|
-
const createFileLinesCache = (rootDirectory, readFileLinesSync) => {
|
|
143
|
-
const cache = /* @__PURE__ */ new Map();
|
|
144
|
-
return (filePath) => {
|
|
145
|
-
const cached = cache.get(filePath);
|
|
146
|
-
if (cached !== void 0) return cached;
|
|
147
|
-
const lines = readFileLinesSync(resolveCandidateReadPath(rootDirectory, filePath));
|
|
148
|
-
cache.set(filePath, lines);
|
|
149
|
-
return lines;
|
|
150
|
-
};
|
|
151
|
-
};
|
|
152
|
-
const isInsideTextComponent = (lines, diagnosticLine, textComponentNames) => {
|
|
153
|
-
for (let lineIndex = diagnosticLine - 1; lineIndex >= 0; lineIndex--) {
|
|
154
|
-
const match = lines[lineIndex].match(OPENING_TAG_PATTERN);
|
|
155
|
-
if (!match) continue;
|
|
156
|
-
const fullTagName = match[1];
|
|
157
|
-
const leafTagName = fullTagName.includes(".") ? fullTagName.split(".").at(-1) ?? fullTagName : fullTagName;
|
|
158
|
-
return textComponentNames.has(fullTagName) || textComponentNames.has(leafTagName);
|
|
159
|
-
}
|
|
160
|
-
return false;
|
|
161
|
-
};
|
|
162
|
-
const isRuleSuppressed = (commentRules, ruleId) => {
|
|
163
|
-
if (!commentRules?.trim()) return true;
|
|
164
|
-
return commentRules.split(/[,\s]+/).some((rule) => rule.trim() === ruleId);
|
|
165
|
-
};
|
|
166
|
-
const filterIgnoredDiagnostics = (diagnostics, config, rootDirectory, readFileLinesSync) => {
|
|
167
|
-
const ignoredRules = new Set(Array.isArray(config.ignore?.rules) ? config.ignore.rules.filter((rule) => typeof rule === "string") : []);
|
|
168
|
-
const ignoredFilePatterns = compileIgnoredFilePatterns(config);
|
|
169
|
-
const textComponentNames = new Set(Array.isArray(config.textComponents) ? config.textComponents.filter((name) => typeof name === "string") : []);
|
|
170
|
-
const hasTextComponents = textComponentNames.size > 0;
|
|
171
|
-
const getFileLines = createFileLinesCache(rootDirectory, readFileLinesSync);
|
|
172
|
-
return diagnostics.filter((diagnostic) => {
|
|
173
|
-
const ruleIdentifier = `${diagnostic.plugin}/${diagnostic.rule}`;
|
|
174
|
-
if (ignoredRules.has(ruleIdentifier)) return false;
|
|
175
|
-
if (isFileIgnoredByPatterns(diagnostic.filePath, rootDirectory, ignoredFilePatterns)) return false;
|
|
176
|
-
if (hasTextComponents && diagnostic.rule === "rn-no-raw-text" && diagnostic.line > 0) {
|
|
177
|
-
const lines = getFileLines(diagnostic.filePath);
|
|
178
|
-
if (lines && isInsideTextComponent(lines, diagnostic.line, textComponentNames)) return false;
|
|
179
|
-
}
|
|
180
|
-
return true;
|
|
181
|
-
});
|
|
182
|
-
};
|
|
183
|
-
const filterInlineSuppressions = (diagnostics, rootDirectory, readFileLinesSync) => {
|
|
184
|
-
const getFileLines = createFileLinesCache(rootDirectory, readFileLinesSync);
|
|
185
|
-
return diagnostics.filter((diagnostic) => {
|
|
186
|
-
if (diagnostic.line <= 0) return true;
|
|
187
|
-
const lines = getFileLines(diagnostic.filePath);
|
|
188
|
-
if (!lines) return true;
|
|
189
|
-
const ruleId = `${diagnostic.plugin}/${diagnostic.rule}`;
|
|
190
|
-
const currentLine = lines[diagnostic.line - 1];
|
|
191
|
-
if (currentLine) {
|
|
192
|
-
const lineMatch = currentLine.match(DISABLE_LINE_PATTERN);
|
|
193
|
-
if (lineMatch && isRuleSuppressed(lineMatch[1], ruleId)) return false;
|
|
194
|
-
}
|
|
195
|
-
if (diagnostic.line >= 2) {
|
|
196
|
-
const previousLine = lines[diagnostic.line - 2];
|
|
197
|
-
if (previousLine) {
|
|
198
|
-
const nextLineMatch = previousLine.match(DISABLE_NEXT_LINE_PATTERN);
|
|
199
|
-
if (nextLineMatch && isRuleSuppressed(nextLineMatch[1], ruleId)) return false;
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
return true;
|
|
203
|
-
});
|
|
204
|
-
};
|
|
205
|
-
//#endregion
|
|
206
|
-
//#region src/utils/merge-and-filter-diagnostics.ts
|
|
207
|
-
const mergeAndFilterDiagnostics = (mergedDiagnostics, directory, userConfig, readFileLinesSync) => {
|
|
208
|
-
return filterInlineSuppressions(userConfig ? filterIgnoredDiagnostics(mergedDiagnostics, userConfig, directory, readFileLinesSync) : mergedDiagnostics, directory, readFileLinesSync);
|
|
209
|
-
};
|
|
210
|
-
//#endregion
|
|
211
|
-
//#region src/core/build-result.ts
|
|
212
|
-
const buildDiagnoseTimedResult = async (input) => {
|
|
213
|
-
const diagnostics = mergeAndFilterDiagnostics(input.mergedDiagnostics, input.rootDirectory, input.userConfig, input.readFileLinesSync);
|
|
214
|
-
const elapsedMilliseconds = globalThis.performance.now() - input.startTime;
|
|
215
|
-
return {
|
|
216
|
-
diagnostics,
|
|
217
|
-
score: input.score !== void 0 ? input.score : await input.calculateDiagnosticsScore(diagnostics),
|
|
218
|
-
elapsedMilliseconds
|
|
219
|
-
};
|
|
220
|
-
};
|
|
221
|
-
//#endregion
|
|
222
|
-
//#region src/adapters/browser/create-browser-read-file-lines.ts
|
|
223
|
-
const normalizeKey = (rootDirectory, filePath) => {
|
|
224
|
-
const normalizedRoot = rootDirectory.replace(/\\/g, "/").replace(/\/$/, "");
|
|
225
|
-
const normalizedPath = filePath.replace(/\\/g, "/");
|
|
226
|
-
if (normalizedPath.startsWith(normalizedRoot + "/")) return normalizedPath.slice(normalizedRoot.length + 1);
|
|
227
|
-
return normalizedPath.replace(/^\.\//, "");
|
|
228
|
-
};
|
|
229
|
-
const createBrowserReadFileLinesSync = (rootDirectory, projectFiles) => {
|
|
230
|
-
return (absoluteOrRelativePath) => {
|
|
231
|
-
const content = projectFiles[normalizeKey(rootDirectory, absoluteOrRelativePath)];
|
|
232
|
-
if (content === void 0) return null;
|
|
233
|
-
return content.split("\n");
|
|
234
|
-
};
|
|
235
|
-
};
|
|
236
|
-
//#endregion
|
|
237
|
-
//#region src/adapters/browser/diagnose.ts
|
|
238
|
-
const diagnose = async (input) => {
|
|
239
|
-
if (!input.project.reactVersion) throw new Error(buildNoReactDependencyError(input.rootDirectory));
|
|
240
|
-
const readFileLinesSync = createBrowserReadFileLinesSync(input.rootDirectory, input.projectFiles);
|
|
241
|
-
const userConfig = input.userConfig ?? null;
|
|
242
|
-
const deadCodeDiagnostics = input.deadCodeDiagnostics ?? [];
|
|
243
|
-
const mergedDiagnostics = [...input.lintDiagnostics, ...deadCodeDiagnostics];
|
|
244
|
-
const startTime = globalThis.performance.now();
|
|
245
|
-
const timed = await buildDiagnoseTimedResult({
|
|
246
|
-
mergedDiagnostics,
|
|
247
|
-
rootDirectory: input.rootDirectory,
|
|
248
|
-
userConfig,
|
|
249
|
-
readFileLinesSync,
|
|
250
|
-
startTime,
|
|
251
|
-
score: input.score,
|
|
252
|
-
calculateDiagnosticsScore: calculateScore
|
|
253
|
-
});
|
|
254
|
-
return {
|
|
255
|
-
diagnostics: timed.diagnostics,
|
|
256
|
-
score: timed.score,
|
|
257
|
-
project: input.project,
|
|
258
|
-
elapsedMilliseconds: timed.elapsedMilliseconds
|
|
259
|
-
};
|
|
260
|
-
};
|
|
261
|
-
//#endregion
|
|
262
|
-
//#region src/utils/jsx-include-paths.ts
|
|
263
|
-
const computeJsxIncludePaths = (includePaths) => includePaths.length > 0 ? includePaths.filter((filePath) => JSX_FILE_PATTERN.test(filePath)) : void 0;
|
|
264
|
-
//#endregion
|
|
265
|
-
//#region src/core/diagnose-core.ts
|
|
266
|
-
const diagnoseCore = async (deps, options = {}) => {
|
|
267
|
-
const { includePaths = [] } = options;
|
|
268
|
-
const isDiffMode = includePaths.length > 0;
|
|
269
|
-
const startTime = globalThis.performance.now();
|
|
270
|
-
const resolvedDirectory = deps.rootDirectory;
|
|
271
|
-
const projectInfo = deps.discoverProjectInfo();
|
|
272
|
-
const userConfig = deps.loadUserConfig();
|
|
273
|
-
const effectiveLint = options.lint ?? userConfig?.lint ?? true;
|
|
274
|
-
const effectiveDeadCode = options.deadCode ?? userConfig?.deadCode ?? true;
|
|
275
|
-
if (!projectInfo.reactVersion) throw new Error(buildNoReactDependencyError(deps.rootDirectory));
|
|
276
|
-
const lintIncludePaths = options.lintIncludePaths !== void 0 ? options.lintIncludePaths : computeJsxIncludePaths(includePaths);
|
|
277
|
-
const { runLint, runDeadCode } = deps.createRunners({
|
|
278
|
-
resolvedDirectory,
|
|
279
|
-
projectInfo,
|
|
280
|
-
userConfig,
|
|
281
|
-
lintIncludePaths,
|
|
282
|
-
isDiffMode
|
|
283
|
-
});
|
|
284
|
-
const emptyDiagnostics = [];
|
|
285
|
-
const lintPromise = effectiveLint ? runLint().catch((error) => {
|
|
286
|
-
console.error("Lint failed:", error);
|
|
287
|
-
return emptyDiagnostics;
|
|
288
|
-
}) : Promise.resolve(emptyDiagnostics);
|
|
289
|
-
const deadCodePromise = effectiveDeadCode && !isDiffMode ? runDeadCode().catch((error) => {
|
|
290
|
-
console.error("Dead code analysis failed:", error);
|
|
291
|
-
return emptyDiagnostics;
|
|
292
|
-
}) : Promise.resolve(emptyDiagnostics);
|
|
293
|
-
const [lintSettled, deadCodeSettled] = await Promise.allSettled([lintPromise, deadCodePromise]);
|
|
294
|
-
const lintDiagnostics = lintSettled.status === "fulfilled" ? lintSettled.value : emptyDiagnostics;
|
|
295
|
-
const deadCodeDiagnostics = deadCodeSettled.status === "fulfilled" ? deadCodeSettled.value : emptyDiagnostics;
|
|
296
|
-
if (lintSettled.status === "rejected") console.error("Lint rejected:", lintSettled.reason);
|
|
297
|
-
if (deadCodeSettled.status === "rejected") console.error("Dead code rejected:", deadCodeSettled.reason);
|
|
298
|
-
const environmentDiagnostics = deps.getExtraDiagnostics?.() ?? [];
|
|
299
|
-
const timed = await buildDiagnoseTimedResult({
|
|
300
|
-
mergedDiagnostics: [
|
|
301
|
-
...lintDiagnostics,
|
|
302
|
-
...deadCodeDiagnostics,
|
|
303
|
-
...environmentDiagnostics
|
|
304
|
-
],
|
|
305
|
-
rootDirectory: resolvedDirectory,
|
|
306
|
-
userConfig,
|
|
307
|
-
readFileLinesSync: deps.readFileLinesSync,
|
|
308
|
-
startTime,
|
|
309
|
-
calculateDiagnosticsScore: deps.calculateDiagnosticsScore
|
|
310
|
-
});
|
|
311
|
-
return {
|
|
312
|
-
diagnostics: timed.diagnostics,
|
|
313
|
-
score: timed.score,
|
|
314
|
-
project: projectInfo,
|
|
315
|
-
elapsedMilliseconds: timed.elapsedMilliseconds
|
|
316
|
-
};
|
|
317
|
-
};
|
|
318
|
-
//#endregion
|
|
319
|
-
//#region src/adapters/browser/diagnose-browser.ts
|
|
320
|
-
const diagnoseBrowser = async (input, options = {}) => {
|
|
321
|
-
const readFileLinesSync = createBrowserReadFileLinesSync(input.rootDirectory, input.projectFiles);
|
|
322
|
-
return diagnoseCore({
|
|
323
|
-
rootDirectory: input.rootDirectory,
|
|
324
|
-
readFileLinesSync,
|
|
325
|
-
loadUserConfig: () => input.userConfig ?? null,
|
|
326
|
-
discoverProjectInfo: () => input.project,
|
|
327
|
-
calculateDiagnosticsScore: calculateScore,
|
|
328
|
-
createRunners: ({ lintIncludePaths, userConfig }) => ({
|
|
329
|
-
runLint: () => input.runOxlint({
|
|
330
|
-
lintIncludePaths,
|
|
331
|
-
customRulesOnly: userConfig?.customRulesOnly ?? false
|
|
332
|
-
}),
|
|
333
|
-
runDeadCode: async () => []
|
|
334
|
-
})
|
|
335
|
-
}, options);
|
|
336
|
-
};
|
|
337
|
-
//#endregion
|
|
338
|
-
//#region src/adapters/browser/process-browser-diagnostics.ts
|
|
339
|
-
const processBrowserDiagnostics = async (input) => {
|
|
340
|
-
const readFileLinesSync = createBrowserReadFileLinesSync(input.rootDirectory, input.projectFiles);
|
|
341
|
-
const userConfig = input.userConfig ?? null;
|
|
342
|
-
const timed = await buildDiagnoseTimedResult({
|
|
343
|
-
mergedDiagnostics: input.diagnostics,
|
|
344
|
-
rootDirectory: input.rootDirectory,
|
|
345
|
-
userConfig,
|
|
346
|
-
readFileLinesSync,
|
|
347
|
-
startTime: globalThis.performance.now(),
|
|
348
|
-
score: input.score,
|
|
349
|
-
calculateDiagnosticsScore: calculateScore
|
|
350
|
-
});
|
|
351
|
-
return {
|
|
352
|
-
diagnostics: timed.diagnostics,
|
|
353
|
-
score: timed.score
|
|
354
|
-
};
|
|
355
|
-
};
|
|
356
|
-
//#endregion
|
|
357
|
-
export { calculateScore as a, diagnose as i, diagnoseBrowser as n, calculateScoreLocally as o, diagnoseCore as r, processBrowserDiagnostics as t };
|
|
358
|
-
|
|
359
|
-
//# sourceMappingURL=browser-BOxs7MrK.js.map
|
|
@@ -1,146 +0,0 @@
|
|
|
1
|
-
//#region src/types.d.ts
|
|
2
|
-
type FailOnLevel = "error" | "warning" | "none";
|
|
3
|
-
type Framework = "nextjs" | "vite" | "cra" | "remix" | "gatsby" | "expo" | "react-native" | "tanstack-start" | "unknown";
|
|
4
|
-
interface ProjectInfo {
|
|
5
|
-
rootDirectory: string;
|
|
6
|
-
projectName: string;
|
|
7
|
-
reactVersion: string | null;
|
|
8
|
-
framework: Framework;
|
|
9
|
-
hasTypeScript: boolean;
|
|
10
|
-
hasReactCompiler: boolean;
|
|
11
|
-
hasTanStackQuery: boolean;
|
|
12
|
-
sourceFileCount: number;
|
|
13
|
-
}
|
|
14
|
-
interface Diagnostic {
|
|
15
|
-
filePath: string;
|
|
16
|
-
plugin: string;
|
|
17
|
-
rule: string;
|
|
18
|
-
severity: "error" | "warning";
|
|
19
|
-
message: string;
|
|
20
|
-
help: string;
|
|
21
|
-
line: number;
|
|
22
|
-
column: number;
|
|
23
|
-
category: string;
|
|
24
|
-
}
|
|
25
|
-
interface ScoreResult {
|
|
26
|
-
score: number;
|
|
27
|
-
label: string;
|
|
28
|
-
}
|
|
29
|
-
interface ReactDoctorIgnoreConfig {
|
|
30
|
-
rules?: string[];
|
|
31
|
-
files?: string[];
|
|
32
|
-
}
|
|
33
|
-
interface ReactDoctorConfig {
|
|
34
|
-
ignore?: ReactDoctorIgnoreConfig;
|
|
35
|
-
lint?: boolean;
|
|
36
|
-
deadCode?: boolean;
|
|
37
|
-
verbose?: boolean;
|
|
38
|
-
diff?: boolean | string;
|
|
39
|
-
failOn?: FailOnLevel;
|
|
40
|
-
customRulesOnly?: boolean;
|
|
41
|
-
share?: boolean;
|
|
42
|
-
textComponents?: string[];
|
|
43
|
-
/**
|
|
44
|
-
* Whether to respect inline `// eslint-disable*` / `// oxlint-disable*`
|
|
45
|
-
* comments in source files. Default: `true`.
|
|
46
|
-
*
|
|
47
|
-
* File-level ignores (`.gitignore`, `.eslintignore`, `.oxlintignore`,
|
|
48
|
-
* `.prettierignore`, `.gitattributes` `linguist-vendored` /
|
|
49
|
-
* `linguist-generated`) are ALWAYS honored regardless of this option
|
|
50
|
-
* — they typically point at vendored or generated code that
|
|
51
|
-
* genuinely shouldn't be linted at all.
|
|
52
|
-
*
|
|
53
|
-
* Set to `false` for "audit mode": every inline suppression is
|
|
54
|
-
* neutralized so react-doctor reports every diagnostic regardless
|
|
55
|
-
* of historical hide-comments.
|
|
56
|
-
*/
|
|
57
|
-
respectInlineDisables?: boolean;
|
|
58
|
-
}
|
|
59
|
-
//#endregion
|
|
60
|
-
//#region src/core/calculate-score-locally.d.ts
|
|
61
|
-
declare const calculateScoreLocally: (diagnostics: Diagnostic[]) => ScoreResult;
|
|
62
|
-
//#endregion
|
|
63
|
-
//#region src/utils/calculate-score-browser.d.ts
|
|
64
|
-
declare const calculateScore: (diagnostics: Diagnostic[], fetchImplementation?: typeof fetch | undefined) => Promise<ScoreResult | null>;
|
|
65
|
-
//#endregion
|
|
66
|
-
//#region src/adapters/browser/diagnose.d.ts
|
|
67
|
-
interface BrowserDiagnoseInput {
|
|
68
|
-
rootDirectory: string;
|
|
69
|
-
project: ProjectInfo;
|
|
70
|
-
projectFiles: Record<string, string>;
|
|
71
|
-
lintDiagnostics: Diagnostic[];
|
|
72
|
-
deadCodeDiagnostics?: Diagnostic[];
|
|
73
|
-
userConfig?: ReactDoctorConfig | null;
|
|
74
|
-
score?: ScoreResult | null;
|
|
75
|
-
}
|
|
76
|
-
interface BrowserDiagnoseResult {
|
|
77
|
-
diagnostics: Diagnostic[];
|
|
78
|
-
score: ScoreResult | null;
|
|
79
|
-
project: ProjectInfo;
|
|
80
|
-
elapsedMilliseconds: number;
|
|
81
|
-
}
|
|
82
|
-
declare const diagnose: (input: BrowserDiagnoseInput) => Promise<BrowserDiagnoseResult>;
|
|
83
|
-
//#endregion
|
|
84
|
-
//#region src/core/diagnose-core.d.ts
|
|
85
|
-
interface DiagnoseCoreOptions {
|
|
86
|
-
lint?: boolean;
|
|
87
|
-
deadCode?: boolean;
|
|
88
|
-
includePaths?: string[];
|
|
89
|
-
lintIncludePaths?: string[] | undefined;
|
|
90
|
-
}
|
|
91
|
-
interface DiagnoseCoreResult {
|
|
92
|
-
diagnostics: Diagnostic[];
|
|
93
|
-
score: ScoreResult | null;
|
|
94
|
-
project: ProjectInfo;
|
|
95
|
-
elapsedMilliseconds: number;
|
|
96
|
-
}
|
|
97
|
-
interface DiagnoseRunnerContext {
|
|
98
|
-
resolvedDirectory: string;
|
|
99
|
-
projectInfo: ProjectInfo;
|
|
100
|
-
userConfig: ReactDoctorConfig | null;
|
|
101
|
-
lintIncludePaths: string[] | undefined;
|
|
102
|
-
isDiffMode: boolean;
|
|
103
|
-
}
|
|
104
|
-
interface DiagnoseCoreDeps {
|
|
105
|
-
rootDirectory: string;
|
|
106
|
-
readFileLinesSync: (filePath: string) => string[] | null;
|
|
107
|
-
loadUserConfig: () => ReactDoctorConfig | null;
|
|
108
|
-
discoverProjectInfo: () => ProjectInfo;
|
|
109
|
-
calculateDiagnosticsScore: (diagnostics: Diagnostic[]) => Promise<ScoreResult | null>;
|
|
110
|
-
getExtraDiagnostics?: () => Diagnostic[];
|
|
111
|
-
createRunners: (context: DiagnoseRunnerContext) => {
|
|
112
|
-
runLint: () => Promise<Diagnostic[]>;
|
|
113
|
-
runDeadCode: () => Promise<Diagnostic[]>;
|
|
114
|
-
};
|
|
115
|
-
}
|
|
116
|
-
declare const diagnoseCore: (deps: DiagnoseCoreDeps, options?: DiagnoseCoreOptions) => Promise<DiagnoseCoreResult>;
|
|
117
|
-
//#endregion
|
|
118
|
-
//#region src/adapters/browser/diagnose-browser.d.ts
|
|
119
|
-
interface DiagnoseBrowserInput {
|
|
120
|
-
rootDirectory: string;
|
|
121
|
-
project: ProjectInfo;
|
|
122
|
-
projectFiles: Record<string, string>;
|
|
123
|
-
userConfig?: ReactDoctorConfig | null;
|
|
124
|
-
runOxlint: (input: {
|
|
125
|
-
lintIncludePaths: string[] | undefined;
|
|
126
|
-
customRulesOnly: boolean;
|
|
127
|
-
}) => Promise<Diagnostic[]>;
|
|
128
|
-
}
|
|
129
|
-
declare const diagnoseBrowser: (input: DiagnoseBrowserInput, options?: DiagnoseCoreOptions) => Promise<DiagnoseCoreResult>;
|
|
130
|
-
//#endregion
|
|
131
|
-
//#region src/adapters/browser/process-browser-diagnostics.d.ts
|
|
132
|
-
interface ProcessBrowserDiagnosticsInput {
|
|
133
|
-
rootDirectory: string;
|
|
134
|
-
projectFiles: Record<string, string>;
|
|
135
|
-
diagnostics: Diagnostic[];
|
|
136
|
-
userConfig?: ReactDoctorConfig | null;
|
|
137
|
-
score?: ScoreResult | null;
|
|
138
|
-
}
|
|
139
|
-
interface ProcessBrowserDiagnosticsResult {
|
|
140
|
-
diagnostics: Diagnostic[];
|
|
141
|
-
score: ScoreResult | null;
|
|
142
|
-
}
|
|
143
|
-
declare const processBrowserDiagnostics: (input: ProcessBrowserDiagnosticsInput) => Promise<ProcessBrowserDiagnosticsResult>;
|
|
144
|
-
//#endregion
|
|
145
|
-
export { ScoreResult as _, diagnoseBrowser as a, diagnoseCore as c, diagnose as d, calculateScore as f, ReactDoctorConfig as g, ProjectInfo as h, DiagnoseBrowserInput as i, BrowserDiagnoseInput as l, Diagnostic as m, ProcessBrowserDiagnosticsResult as n, DiagnoseCoreOptions as o, calculateScoreLocally as p, processBrowserDiagnostics as r, DiagnoseCoreResult as s, ProcessBrowserDiagnosticsInput as t, BrowserDiagnoseResult as u };
|
|
146
|
-
//# sourceMappingURL=browser-Dcq3yn-p.d.ts.map
|
package/dist/browser.d.ts
DELETED
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import { _ as ScoreResult, a as diagnoseBrowser, c as diagnoseCore, d as diagnose, f as calculateScore, g as ReactDoctorConfig, h as ProjectInfo, i as DiagnoseBrowserInput, l as BrowserDiagnoseInput, m as Diagnostic, n as ProcessBrowserDiagnosticsResult, o as DiagnoseCoreOptions, p as calculateScoreLocally, r as processBrowserDiagnostics, s as DiagnoseCoreResult, t as ProcessBrowserDiagnosticsInput, u as BrowserDiagnoseResult } from "./browser-Dcq3yn-p.js";
|
|
2
|
-
export { BrowserDiagnoseInput, BrowserDiagnoseResult, DiagnoseBrowserInput, DiagnoseCoreOptions, DiagnoseCoreResult, Diagnostic, ProcessBrowserDiagnosticsInput, ProcessBrowserDiagnosticsResult, ProjectInfo, ReactDoctorConfig, ScoreResult, calculateScore, calculateScoreLocally, diagnose, diagnoseBrowser, diagnoseCore, processBrowserDiagnostics };
|
package/dist/browser.js
DELETED
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import { a as calculateScore, i as diagnose, n as diagnoseBrowser, o as calculateScoreLocally, r as diagnoseCore, t as processBrowserDiagnostics } from "./browser-BOxs7MrK.js";
|
|
2
|
-
export { calculateScore, calculateScoreLocally, diagnose, diagnoseBrowser, diagnoseCore, processBrowserDiagnostics };
|
package/dist/worker.d.ts
DELETED
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import { _ as ScoreResult, a as diagnoseBrowser, c as diagnoseCore, d as diagnose, f as calculateScore, g as ReactDoctorConfig, h as ProjectInfo, i as DiagnoseBrowserInput, l as BrowserDiagnoseInput, m as Diagnostic, n as ProcessBrowserDiagnosticsResult, o as DiagnoseCoreOptions, p as calculateScoreLocally, r as processBrowserDiagnostics, s as DiagnoseCoreResult, t as ProcessBrowserDiagnosticsInput, u as BrowserDiagnoseResult } from "./browser-Dcq3yn-p.js";
|
|
2
|
-
export { BrowserDiagnoseInput, BrowserDiagnoseResult, DiagnoseBrowserInput, DiagnoseCoreOptions, DiagnoseCoreResult, Diagnostic, ProcessBrowserDiagnosticsInput, ProcessBrowserDiagnosticsResult, ProjectInfo, ReactDoctorConfig, ScoreResult, calculateScore, calculateScoreLocally, diagnose, diagnoseBrowser, diagnoseCore, processBrowserDiagnostics };
|
package/dist/worker.js
DELETED
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import { a as calculateScore, i as diagnose, n as diagnoseBrowser, o as calculateScoreLocally, r as diagnoseCore, t as processBrowserDiagnostics } from "./browser-BOxs7MrK.js";
|
|
2
|
-
export { calculateScore, calculateScoreLocally, diagnose, diagnoseBrowser, diagnoseCore, processBrowserDiagnostics };
|