react-doctor 0.0.37 → 0.0.39
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 +22 -8
- package/dist/browser.d.ts +2 -0
- package/dist/browser.js +3 -0
- package/dist/cli.js +139 -86
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +347 -239
- package/dist/index.js.map +1 -1
- package/dist/process-browser-diagnostics-Cahx3_oy.d.ts +131 -0
- package/dist/process-browser-diagnostics-Cahx3_oy.d.ts.map +1 -0
- package/dist/process-browser-diagnostics-DpaZeYLI.js +365 -0
- package/dist/process-browser-diagnostics-DpaZeYLI.js.map +1 -0
- package/dist/worker.d.ts +2 -0
- package/dist/worker.js +3 -0
- package/package.json +9 -1
|
@@ -0,0 +1,131 @@
|
|
|
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
|
+
sourceFileCount: number;
|
|
12
|
+
}
|
|
13
|
+
interface Diagnostic {
|
|
14
|
+
filePath: string;
|
|
15
|
+
plugin: string;
|
|
16
|
+
rule: string;
|
|
17
|
+
severity: "error" | "warning";
|
|
18
|
+
message: string;
|
|
19
|
+
help: string;
|
|
20
|
+
line: number;
|
|
21
|
+
column: number;
|
|
22
|
+
category: string;
|
|
23
|
+
weight?: number;
|
|
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
|
+
//#endregion
|
|
45
|
+
//#region src/core/calculate-score-locally.d.ts
|
|
46
|
+
declare const calculateScoreLocally: (diagnostics: Diagnostic[]) => ScoreResult;
|
|
47
|
+
//#endregion
|
|
48
|
+
//#region src/utils/calculate-score-browser.d.ts
|
|
49
|
+
declare const calculateScore: (diagnostics: Diagnostic[]) => Promise<ScoreResult | null>;
|
|
50
|
+
//#endregion
|
|
51
|
+
//#region src/adapters/browser/diagnose.d.ts
|
|
52
|
+
interface BrowserDiagnoseInput {
|
|
53
|
+
rootDirectory: string;
|
|
54
|
+
project: ProjectInfo;
|
|
55
|
+
projectFiles: Record<string, string>;
|
|
56
|
+
lintDiagnostics: Diagnostic[];
|
|
57
|
+
deadCodeDiagnostics?: Diagnostic[];
|
|
58
|
+
userConfig?: ReactDoctorConfig | null;
|
|
59
|
+
score?: ScoreResult | null;
|
|
60
|
+
}
|
|
61
|
+
interface BrowserDiagnoseResult {
|
|
62
|
+
diagnostics: Diagnostic[];
|
|
63
|
+
score: ScoreResult | null;
|
|
64
|
+
project: ProjectInfo;
|
|
65
|
+
elapsedMilliseconds: number;
|
|
66
|
+
}
|
|
67
|
+
declare const diagnose: (input: BrowserDiagnoseInput) => Promise<BrowserDiagnoseResult>;
|
|
68
|
+
//#endregion
|
|
69
|
+
//#region src/core/diagnose-core.d.ts
|
|
70
|
+
interface DiagnoseCoreOptions {
|
|
71
|
+
lint?: boolean;
|
|
72
|
+
deadCode?: boolean;
|
|
73
|
+
includePaths?: string[];
|
|
74
|
+
lintIncludePaths?: string[] | undefined;
|
|
75
|
+
}
|
|
76
|
+
interface DiagnoseCoreResult {
|
|
77
|
+
diagnostics: Diagnostic[];
|
|
78
|
+
score: ScoreResult | null;
|
|
79
|
+
project: ProjectInfo;
|
|
80
|
+
elapsedMilliseconds: number;
|
|
81
|
+
}
|
|
82
|
+
interface DiagnoseRunnerContext {
|
|
83
|
+
resolvedDirectory: string;
|
|
84
|
+
projectInfo: ProjectInfo;
|
|
85
|
+
userConfig: ReactDoctorConfig | null;
|
|
86
|
+
lintIncludePaths: string[] | undefined;
|
|
87
|
+
isDiffMode: boolean;
|
|
88
|
+
}
|
|
89
|
+
interface DiagnoseCoreDeps {
|
|
90
|
+
rootDirectory: string;
|
|
91
|
+
readFileLinesSync: (filePath: string) => string[] | null;
|
|
92
|
+
loadUserConfig: () => ReactDoctorConfig | null;
|
|
93
|
+
discoverProjectInfo: () => ProjectInfo;
|
|
94
|
+
calculateDiagnosticsScore: (diagnostics: Diagnostic[]) => Promise<ScoreResult | null>;
|
|
95
|
+
getExtraDiagnostics?: () => Diagnostic[];
|
|
96
|
+
createRunners: (context: DiagnoseRunnerContext) => {
|
|
97
|
+
runLint: () => Promise<Diagnostic[]>;
|
|
98
|
+
runDeadCode: () => Promise<Diagnostic[]>;
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
declare const diagnoseCore: (deps: DiagnoseCoreDeps, options?: DiagnoseCoreOptions) => Promise<DiagnoseCoreResult>;
|
|
102
|
+
//#endregion
|
|
103
|
+
//#region src/adapters/browser/diagnose-browser.d.ts
|
|
104
|
+
interface DiagnoseBrowserInput {
|
|
105
|
+
rootDirectory: string;
|
|
106
|
+
project: ProjectInfo;
|
|
107
|
+
projectFiles: Record<string, string>;
|
|
108
|
+
userConfig?: ReactDoctorConfig | null;
|
|
109
|
+
runOxlint: (input: {
|
|
110
|
+
lintIncludePaths: string[] | undefined;
|
|
111
|
+
customRulesOnly: boolean;
|
|
112
|
+
}) => Promise<Diagnostic[]>;
|
|
113
|
+
}
|
|
114
|
+
declare const diagnoseBrowser: (input: DiagnoseBrowserInput, options?: DiagnoseCoreOptions) => Promise<DiagnoseCoreResult>;
|
|
115
|
+
//#endregion
|
|
116
|
+
//#region src/adapters/browser/process-browser-diagnostics.d.ts
|
|
117
|
+
interface ProcessBrowserDiagnosticsInput {
|
|
118
|
+
rootDirectory: string;
|
|
119
|
+
projectFiles: Record<string, string>;
|
|
120
|
+
diagnostics: Diagnostic[];
|
|
121
|
+
userConfig?: ReactDoctorConfig | null;
|
|
122
|
+
score?: ScoreResult | null;
|
|
123
|
+
}
|
|
124
|
+
interface ProcessBrowserDiagnosticsResult {
|
|
125
|
+
diagnostics: Diagnostic[];
|
|
126
|
+
score: ScoreResult | null;
|
|
127
|
+
}
|
|
128
|
+
declare const processBrowserDiagnostics: (input: ProcessBrowserDiagnosticsInput) => Promise<ProcessBrowserDiagnosticsResult>;
|
|
129
|
+
//#endregion
|
|
130
|
+
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 };
|
|
131
|
+
//# sourceMappingURL=process-browser-diagnostics-Cahx3_oy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"process-browser-diagnostics-Cahx3_oy.d.ts","names":[],"sources":["../src/types.ts","../src/core/calculate-score-locally.ts","../src/utils/calculate-score-browser.ts","../src/adapters/browser/diagnose.ts","../src/core/diagnose-core.ts","../src/adapters/browser/diagnose-browser.ts","../src/adapters/browser/process-browser-diagnostics.ts"],"mappings":";KAAY,WAAA;AAAA,KAEA,SAAA;AAAA,UAWK,WAAA;EACf,aAAA;EACA,WAAA;EACA,YAAA;EACA,SAAA,EAAW,SAAA;EACX,aAAA;EACA,gBAAA;EACA,eAAA;AAAA;AAAA,UAiCe,UAAA;EACf,QAAA;EACA,MAAA;EACA,IAAA;EACA,QAAA;EACA,OAAA;EACA,IAAA;EACA,IAAA;EACA,MAAA;EACA,QAAA;EACA,MAAA;AAAA;AAAA,UA4Be,WAAA;EACf,KAAA;EACA,KAAA;AAAA;AAAA,UAkEe,uBAAA;EACf,KAAA;EACA,KAAA;AAAA;AAAA,UAGe,iBAAA;EACf,MAAA,GAAS,uBAAA;EACT,IAAA;EACA,QAAA;EACA,OAAA;EACA,IAAA;EACA,MAAA,GAAS,WAAA;EACT,eAAA;EACA,KAAA;EACA,cAAA;AAAA;;;cCvIW,qBAAA,GAAyB,WAAA,EAAa,UAAA,OAAe,WAAA;;;cChCrD,cAAA,GAAwB,WAAA,EAAa,UAAA,OAAe,OAAA,CAAQ,WAAA;;;UCDxD,oBAAA;EACf,aAAA;EACA,OAAA,EAAS,WAAA;EACT,YAAA,EAAc,MAAA;EACd,eAAA,EAAiB,UAAA;EACjB,mBAAA,GAAsB,UAAA;EACtB,UAAA,GAAa,iBAAA;EACb,KAAA,GAAQ,WAAA;AAAA;AAAA,UAGO,qBAAA;EACf,WAAA,EAAa,UAAA;EACb,KAAA,EAAO,WAAA;EACP,OAAA,EAAS,WAAA;EACT,mBAAA;AAAA;AAAA,cAGW,QAAA,GAAkB,KAAA,EAAO,oBAAA,KAAuB,OAAA,CAAQ,qBAAA;;;UCjBpD,mBAAA;EACf,IAAA;EACA,QAAA;EACA,YAAA;EACA,gBAAA;AAAA;AAAA,UAGe,kBAAA;EACf,WAAA,EAAa,UAAA;EACb,KAAA,EAAO,WAAA;EACP,OAAA,EAAS,WAAA;EACT,mBAAA;AAAA;AAAA,UAGe,qBAAA;EACf,iBAAA;EACA,WAAA,EAAa,WAAA;EACb,UAAA,EAAY,iBAAA;EACZ,gBAAA;EACA,UAAA;AAAA;AAAA,UAGe,gBAAA;EACf,aAAA;EACA,iBAAA,GAAoB,QAAA;EACpB,cAAA,QAAsB,iBAAA;EACtB,mBAAA,QAA2B,WAAA;EAC3B,yBAAA,GAA4B,WAAA,EAAa,UAAA,OAAiB,OAAA,CAAQ,WAAA;EAClE,mBAAA,SAA4B,UAAA;EAC5B,aAAA,GAAgB,OAAA,EAAS,qBAAA;IACvB,OAAA,QAAe,OAAA,CAAQ,UAAA;IACvB,WAAA,QAAmB,OAAA,CAAQ,UAAA;EAAA;AAAA;AAAA,cAIlB,YAAA,GACX,IAAA,EAAM,gBAAA,EACN,OAAA,GAAS,mBAAA,KACR,OAAA,CAAQ,kBAAA;;;UCrCM,oBAAA;EACf,aAAA;EACA,OAAA,EAAS,WAAA;EACT,YAAA,EAAc,MAAA;EACd,UAAA,GAAa,iBAAA;EACb,SAAA,GAAY,KAAA;IACV,gBAAA;IACA,eAAA;EAAA,MACI,OAAA,CAAQ,UAAA;AAAA;AAAA,cAGH,eAAA,GACX,KAAA,EAAO,oBAAA,EACP,OAAA,GAAS,mBAAA,KAAwB,OAAA,CAAA,kBAAA;;;UCdlB,8BAAA;EACf,aAAA;EACA,YAAA,EAAc,MAAA;EACd,WAAA,EAAa,UAAA;EACb,UAAA,GAAa,iBAAA;EACb,KAAA,GAAQ,WAAA;AAAA;AAAA,UAGO,+BAAA;EACf,WAAA,EAAa,UAAA;EACb,KAAA,EAAO,WAAA;AAAA;AAAA,cAGI,yBAAA,GACX,KAAA,EAAO,8BAAA,KACN,OAAA,CAAQ,+BAAA"}
|
|
@@ -0,0 +1,365 @@
|
|
|
1
|
+
//#region src/constants.ts
|
|
2
|
+
const JSX_FILE_PATTERN = /\.(tsx|jsx)$/;
|
|
3
|
+
const PERFECT_SCORE = 100;
|
|
4
|
+
const SCORE_GOOD_THRESHOLD = 75;
|
|
5
|
+
const SCORE_OK_THRESHOLD = 50;
|
|
6
|
+
const SCORE_API_URL = "https://www.react.doctor/api/score";
|
|
7
|
+
const FETCH_TIMEOUT_MS = 1e4;
|
|
8
|
+
const GIT_LS_FILES_MAX_BUFFER_BYTES = 50 * 1024 * 1024;
|
|
9
|
+
const ERROR_RULE_PENALTY = 1.5;
|
|
10
|
+
const WARNING_RULE_PENALTY = .75;
|
|
11
|
+
const GIT_SHOW_MAX_BUFFER_BYTES = 10 * 1024 * 1024;
|
|
12
|
+
|
|
13
|
+
//#endregion
|
|
14
|
+
//#region src/core/calculate-score-locally.ts
|
|
15
|
+
const getScoreLabel = (score) => {
|
|
16
|
+
if (score >= SCORE_GOOD_THRESHOLD) return "Great";
|
|
17
|
+
if (score >= SCORE_OK_THRESHOLD) return "Needs work";
|
|
18
|
+
return "Critical";
|
|
19
|
+
};
|
|
20
|
+
const countUniqueRules = (diagnostics) => {
|
|
21
|
+
const errorRules = /* @__PURE__ */ new Set();
|
|
22
|
+
const warningRules = /* @__PURE__ */ new Set();
|
|
23
|
+
for (const diagnostic of diagnostics) {
|
|
24
|
+
const ruleKey = `${diagnostic.plugin}/${diagnostic.rule}`;
|
|
25
|
+
if (diagnostic.severity === "error") errorRules.add(ruleKey);
|
|
26
|
+
else warningRules.add(ruleKey);
|
|
27
|
+
}
|
|
28
|
+
return {
|
|
29
|
+
errorRuleCount: errorRules.size,
|
|
30
|
+
warningRuleCount: warningRules.size
|
|
31
|
+
};
|
|
32
|
+
};
|
|
33
|
+
const scoreFromRuleCounts = (errorRuleCount, warningRuleCount) => {
|
|
34
|
+
const penalty = errorRuleCount * ERROR_RULE_PENALTY + warningRuleCount * WARNING_RULE_PENALTY;
|
|
35
|
+
return Math.max(0, Math.round(PERFECT_SCORE - penalty));
|
|
36
|
+
};
|
|
37
|
+
const calculateScoreLocally = (diagnostics) => {
|
|
38
|
+
const { errorRuleCount, warningRuleCount } = countUniqueRules(diagnostics);
|
|
39
|
+
const score = scoreFromRuleCounts(errorRuleCount, warningRuleCount);
|
|
40
|
+
return {
|
|
41
|
+
score,
|
|
42
|
+
label: getScoreLabel(score)
|
|
43
|
+
};
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
//#endregion
|
|
47
|
+
//#region src/core/try-score-from-api.ts
|
|
48
|
+
const parseScoreResult = (value) => {
|
|
49
|
+
if (typeof value !== "object" || value === null) return null;
|
|
50
|
+
if (!("score" in value) || !("label" in value)) return null;
|
|
51
|
+
const scoreValue = Reflect.get(value, "score");
|
|
52
|
+
const labelValue = Reflect.get(value, "label");
|
|
53
|
+
if (typeof scoreValue !== "number" || typeof labelValue !== "string") return null;
|
|
54
|
+
return {
|
|
55
|
+
score: scoreValue,
|
|
56
|
+
label: labelValue
|
|
57
|
+
};
|
|
58
|
+
};
|
|
59
|
+
const tryScoreFromApi = async (diagnostics, fetchImplementation) => {
|
|
60
|
+
const controller = new AbortController();
|
|
61
|
+
const timeoutId = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
|
|
62
|
+
try {
|
|
63
|
+
const response = await fetchImplementation(SCORE_API_URL, {
|
|
64
|
+
method: "POST",
|
|
65
|
+
headers: { "Content-Type": "application/json" },
|
|
66
|
+
body: JSON.stringify({ diagnostics }),
|
|
67
|
+
signal: controller.signal
|
|
68
|
+
});
|
|
69
|
+
if (!response.ok) return null;
|
|
70
|
+
return parseScoreResult(await response.json());
|
|
71
|
+
} catch {
|
|
72
|
+
return null;
|
|
73
|
+
} finally {
|
|
74
|
+
clearTimeout(timeoutId);
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
//#endregion
|
|
79
|
+
//#region src/utils/calculate-score-browser.ts
|
|
80
|
+
const calculateScore = async (diagnostics) => await tryScoreFromApi(diagnostics, fetch) ?? calculateScoreLocally(diagnostics);
|
|
81
|
+
|
|
82
|
+
//#endregion
|
|
83
|
+
//#region src/utils/match-glob-pattern.ts
|
|
84
|
+
const REGEX_SPECIAL_CHARACTERS = /[.+^${}()|[\]\\]/g;
|
|
85
|
+
const compileGlobPattern = (pattern) => {
|
|
86
|
+
const normalizedPattern = pattern.replace(/\\/g, "/").replace(/^\//, "");
|
|
87
|
+
let regexSource = "^";
|
|
88
|
+
let characterIndex = 0;
|
|
89
|
+
while (characterIndex < normalizedPattern.length) if (normalizedPattern[characterIndex] === "*" && normalizedPattern[characterIndex + 1] === "*") if (normalizedPattern[characterIndex + 2] === "/") {
|
|
90
|
+
regexSource += "(?:.+/)?";
|
|
91
|
+
characterIndex += 3;
|
|
92
|
+
} else {
|
|
93
|
+
regexSource += ".*";
|
|
94
|
+
characterIndex += 2;
|
|
95
|
+
}
|
|
96
|
+
else if (normalizedPattern[characterIndex] === "*") {
|
|
97
|
+
regexSource += "[^/]*";
|
|
98
|
+
characterIndex++;
|
|
99
|
+
} else if (normalizedPattern[characterIndex] === "?") {
|
|
100
|
+
regexSource += "[^/]";
|
|
101
|
+
characterIndex++;
|
|
102
|
+
} else {
|
|
103
|
+
regexSource += normalizedPattern[characterIndex].replace(REGEX_SPECIAL_CHARACTERS, "\\$&");
|
|
104
|
+
characterIndex++;
|
|
105
|
+
}
|
|
106
|
+
regexSource += "$";
|
|
107
|
+
return new RegExp(regexSource);
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
//#endregion
|
|
111
|
+
//#region src/utils/is-ignored-file.ts
|
|
112
|
+
const toRelativePath = (filePath, rootDirectory) => {
|
|
113
|
+
const normalizedFilePath = filePath.replace(/\\/g, "/");
|
|
114
|
+
const normalizedRoot = rootDirectory.replace(/\\/g, "/").replace(/\/$/, "") + "/";
|
|
115
|
+
if (normalizedFilePath.startsWith(normalizedRoot)) return normalizedFilePath.slice(normalizedRoot.length);
|
|
116
|
+
return normalizedFilePath.replace(/^\.\//, "");
|
|
117
|
+
};
|
|
118
|
+
const compileIgnoredFilePatterns = (userConfig) => Array.isArray(userConfig?.ignore?.files) ? userConfig.ignore.files.map(compileGlobPattern) : [];
|
|
119
|
+
const isFileIgnoredByPatterns = (filePath, rootDirectory, patterns) => {
|
|
120
|
+
if (patterns.length === 0) return false;
|
|
121
|
+
const relativePath = toRelativePath(filePath, rootDirectory);
|
|
122
|
+
return patterns.some((pattern) => pattern.test(relativePath));
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
//#endregion
|
|
126
|
+
//#region src/utils/filter-diagnostics.ts
|
|
127
|
+
const resolveCandidateReadPath = (rootDirectory, filePath) => {
|
|
128
|
+
const normalizedFile = filePath.replace(/\\/g, "/");
|
|
129
|
+
if (normalizedFile.startsWith("/") || /^[a-zA-Z]:\//.test(normalizedFile) || /^[a-zA-Z]:\\/.test(filePath)) return filePath;
|
|
130
|
+
return `${rootDirectory.replace(/\\/g, "/").replace(/\/$/, "")}/${normalizedFile.replace(/^\.\//, "")}`;
|
|
131
|
+
};
|
|
132
|
+
const OPENING_TAG_PATTERN = /<([A-Z][\w.]*)/;
|
|
133
|
+
const DISABLE_NEXT_LINE_PATTERN = /\/\/\s*react-doctor-disable-next-line\b(?:\s+(.+))?/;
|
|
134
|
+
const DISABLE_LINE_PATTERN = /\/\/\s*react-doctor-disable-line\b(?:\s+(.+))?/;
|
|
135
|
+
const createFileLinesCache = (rootDirectory, readFileLinesSync) => {
|
|
136
|
+
const cache = /* @__PURE__ */ new Map();
|
|
137
|
+
return (filePath) => {
|
|
138
|
+
const cached = cache.get(filePath);
|
|
139
|
+
if (cached !== void 0) return cached;
|
|
140
|
+
const lines = readFileLinesSync(resolveCandidateReadPath(rootDirectory, filePath));
|
|
141
|
+
cache.set(filePath, lines);
|
|
142
|
+
return lines;
|
|
143
|
+
};
|
|
144
|
+
};
|
|
145
|
+
const isInsideTextComponent = (lines, diagnosticLine, textComponentNames) => {
|
|
146
|
+
for (let lineIndex = diagnosticLine - 1; lineIndex >= 0; lineIndex--) {
|
|
147
|
+
const match = lines[lineIndex].match(OPENING_TAG_PATTERN);
|
|
148
|
+
if (!match) continue;
|
|
149
|
+
const fullTagName = match[1];
|
|
150
|
+
const leafTagName = fullTagName.includes(".") ? fullTagName.split(".").at(-1) ?? fullTagName : fullTagName;
|
|
151
|
+
return textComponentNames.has(fullTagName) || textComponentNames.has(leafTagName);
|
|
152
|
+
}
|
|
153
|
+
return false;
|
|
154
|
+
};
|
|
155
|
+
const isRuleSuppressed = (commentRules, ruleId) => {
|
|
156
|
+
if (!commentRules?.trim()) return true;
|
|
157
|
+
return commentRules.split(/[,\s]+/).some((rule) => rule.trim() === ruleId);
|
|
158
|
+
};
|
|
159
|
+
const filterIgnoredDiagnostics = (diagnostics, config, rootDirectory, readFileLinesSync) => {
|
|
160
|
+
const ignoredRules = new Set(Array.isArray(config.ignore?.rules) ? config.ignore.rules : []);
|
|
161
|
+
const ignoredFilePatterns = compileIgnoredFilePatterns(config);
|
|
162
|
+
const textComponentNames = new Set(Array.isArray(config.textComponents) ? config.textComponents : []);
|
|
163
|
+
const hasTextComponents = textComponentNames.size > 0;
|
|
164
|
+
const getFileLines = createFileLinesCache(rootDirectory, readFileLinesSync);
|
|
165
|
+
return diagnostics.filter((diagnostic) => {
|
|
166
|
+
const ruleIdentifier = `${diagnostic.plugin}/${diagnostic.rule}`;
|
|
167
|
+
if (ignoredRules.has(ruleIdentifier)) return false;
|
|
168
|
+
if (isFileIgnoredByPatterns(diagnostic.filePath, rootDirectory, ignoredFilePatterns)) return false;
|
|
169
|
+
if (hasTextComponents && diagnostic.rule === "rn-no-raw-text" && diagnostic.line > 0) {
|
|
170
|
+
const lines = getFileLines(diagnostic.filePath);
|
|
171
|
+
if (lines && isInsideTextComponent(lines, diagnostic.line, textComponentNames)) return false;
|
|
172
|
+
}
|
|
173
|
+
return true;
|
|
174
|
+
});
|
|
175
|
+
};
|
|
176
|
+
const filterInlineSuppressions = (diagnostics, rootDirectory, readFileLinesSync) => {
|
|
177
|
+
const getFileLines = createFileLinesCache(rootDirectory, readFileLinesSync);
|
|
178
|
+
return diagnostics.filter((diagnostic) => {
|
|
179
|
+
if (diagnostic.line <= 0) return true;
|
|
180
|
+
const lines = getFileLines(diagnostic.filePath);
|
|
181
|
+
if (!lines) return true;
|
|
182
|
+
const ruleId = `${diagnostic.plugin}/${diagnostic.rule}`;
|
|
183
|
+
const currentLine = lines[diagnostic.line - 1];
|
|
184
|
+
if (currentLine) {
|
|
185
|
+
const lineMatch = currentLine.match(DISABLE_LINE_PATTERN);
|
|
186
|
+
if (lineMatch && isRuleSuppressed(lineMatch[1], ruleId)) return false;
|
|
187
|
+
}
|
|
188
|
+
if (diagnostic.line >= 2) {
|
|
189
|
+
const previousLine = lines[diagnostic.line - 2];
|
|
190
|
+
if (previousLine) {
|
|
191
|
+
const nextLineMatch = previousLine.match(DISABLE_NEXT_LINE_PATTERN);
|
|
192
|
+
if (nextLineMatch && isRuleSuppressed(nextLineMatch[1], ruleId)) return false;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
return true;
|
|
196
|
+
});
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
//#endregion
|
|
200
|
+
//#region src/utils/merge-and-filter-diagnostics.ts
|
|
201
|
+
const mergeAndFilterDiagnostics = (mergedDiagnostics, directory, userConfig, readFileLinesSync) => {
|
|
202
|
+
return filterInlineSuppressions(userConfig ? filterIgnoredDiagnostics(mergedDiagnostics, userConfig, directory, readFileLinesSync) : mergedDiagnostics, directory, readFileLinesSync);
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
//#endregion
|
|
206
|
+
//#region src/core/build-result.ts
|
|
207
|
+
const buildDiagnoseTimedResult = async (input) => {
|
|
208
|
+
const diagnostics = mergeAndFilterDiagnostics(input.mergedDiagnostics, input.rootDirectory, input.userConfig, input.readFileLinesSync);
|
|
209
|
+
const elapsedMilliseconds = globalThis.performance.now() - input.startTime;
|
|
210
|
+
return {
|
|
211
|
+
diagnostics,
|
|
212
|
+
score: input.score !== void 0 ? input.score : await input.calculateDiagnosticsScore(diagnostics),
|
|
213
|
+
elapsedMilliseconds
|
|
214
|
+
};
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
//#endregion
|
|
218
|
+
//#region src/adapters/browser/create-browser-read-file-lines.ts
|
|
219
|
+
const normalizeKey = (rootDirectory, filePath) => {
|
|
220
|
+
const normalizedRoot = rootDirectory.replace(/\\/g, "/").replace(/\/$/, "");
|
|
221
|
+
const normalizedPath = filePath.replace(/\\/g, "/");
|
|
222
|
+
if (normalizedPath.startsWith(normalizedRoot + "/")) return normalizedPath.slice(normalizedRoot.length + 1);
|
|
223
|
+
return normalizedPath.replace(/^\.\//, "");
|
|
224
|
+
};
|
|
225
|
+
const createBrowserReadFileLinesSync = (rootDirectory, projectFiles) => {
|
|
226
|
+
return (absoluteOrRelativePath) => {
|
|
227
|
+
const content = projectFiles[normalizeKey(rootDirectory, absoluteOrRelativePath)];
|
|
228
|
+
if (content === void 0) return null;
|
|
229
|
+
return content.split("\n");
|
|
230
|
+
};
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
//#endregion
|
|
234
|
+
//#region src/adapters/browser/diagnose.ts
|
|
235
|
+
const diagnose = async (input) => {
|
|
236
|
+
if (!input.project.reactVersion) throw new Error("No React dependency found in package.json");
|
|
237
|
+
const readFileLinesSync = createBrowserReadFileLinesSync(input.rootDirectory, input.projectFiles);
|
|
238
|
+
const userConfig = input.userConfig ?? null;
|
|
239
|
+
const deadCodeDiagnostics = input.deadCodeDiagnostics ?? [];
|
|
240
|
+
const mergedDiagnostics = [...input.lintDiagnostics, ...deadCodeDiagnostics];
|
|
241
|
+
const startTime = globalThis.performance.now();
|
|
242
|
+
const timed = await buildDiagnoseTimedResult({
|
|
243
|
+
mergedDiagnostics,
|
|
244
|
+
rootDirectory: input.rootDirectory,
|
|
245
|
+
userConfig,
|
|
246
|
+
readFileLinesSync,
|
|
247
|
+
startTime,
|
|
248
|
+
score: input.score,
|
|
249
|
+
calculateDiagnosticsScore: calculateScore
|
|
250
|
+
});
|
|
251
|
+
return {
|
|
252
|
+
diagnostics: timed.diagnostics,
|
|
253
|
+
score: timed.score,
|
|
254
|
+
project: input.project,
|
|
255
|
+
elapsedMilliseconds: timed.elapsedMilliseconds
|
|
256
|
+
};
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
//#endregion
|
|
260
|
+
//#region src/core/build-diagnose-result.ts
|
|
261
|
+
const buildDiagnoseResult = (params) => ({
|
|
262
|
+
diagnostics: params.diagnostics,
|
|
263
|
+
score: params.score,
|
|
264
|
+
project: params.project,
|
|
265
|
+
elapsedMilliseconds: params.elapsedMilliseconds
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
//#endregion
|
|
269
|
+
//#region src/utils/jsx-include-paths.ts
|
|
270
|
+
const computeJsxIncludePaths = (includePaths) => includePaths.length > 0 ? includePaths.filter((filePath) => JSX_FILE_PATTERN.test(filePath)) : void 0;
|
|
271
|
+
|
|
272
|
+
//#endregion
|
|
273
|
+
//#region src/core/diagnose-core.ts
|
|
274
|
+
const diagnoseCore = async (deps, options = {}) => {
|
|
275
|
+
const { includePaths = [] } = options;
|
|
276
|
+
const isDiffMode = includePaths.length > 0;
|
|
277
|
+
const startTime = globalThis.performance.now();
|
|
278
|
+
const resolvedDirectory = deps.rootDirectory;
|
|
279
|
+
const projectInfo = deps.discoverProjectInfo();
|
|
280
|
+
const userConfig = deps.loadUserConfig();
|
|
281
|
+
const effectiveLint = options.lint ?? userConfig?.lint ?? true;
|
|
282
|
+
const effectiveDeadCode = options.deadCode ?? userConfig?.deadCode ?? true;
|
|
283
|
+
if (!projectInfo.reactVersion) throw new Error("No React dependency found in package.json");
|
|
284
|
+
const lintIncludePaths = options.lintIncludePaths !== void 0 ? options.lintIncludePaths : computeJsxIncludePaths(includePaths);
|
|
285
|
+
const { runLint, runDeadCode } = deps.createRunners({
|
|
286
|
+
resolvedDirectory,
|
|
287
|
+
projectInfo,
|
|
288
|
+
userConfig,
|
|
289
|
+
lintIncludePaths,
|
|
290
|
+
isDiffMode
|
|
291
|
+
});
|
|
292
|
+
const emptyDiagnostics = [];
|
|
293
|
+
const lintPromise = effectiveLint ? runLint().catch((error) => {
|
|
294
|
+
console.error("Lint failed:", error);
|
|
295
|
+
return emptyDiagnostics;
|
|
296
|
+
}) : Promise.resolve(emptyDiagnostics);
|
|
297
|
+
const deadCodePromise = effectiveDeadCode && !isDiffMode ? runDeadCode().catch((error) => {
|
|
298
|
+
console.error("Dead code analysis failed:", error);
|
|
299
|
+
return emptyDiagnostics;
|
|
300
|
+
}) : Promise.resolve(emptyDiagnostics);
|
|
301
|
+
const [lintDiagnostics, deadCodeDiagnostics] = await Promise.all([lintPromise, deadCodePromise]);
|
|
302
|
+
const environmentDiagnostics = deps.getExtraDiagnostics?.() ?? [];
|
|
303
|
+
const timed = await buildDiagnoseTimedResult({
|
|
304
|
+
mergedDiagnostics: [
|
|
305
|
+
...lintDiagnostics,
|
|
306
|
+
...deadCodeDiagnostics,
|
|
307
|
+
...environmentDiagnostics
|
|
308
|
+
],
|
|
309
|
+
rootDirectory: resolvedDirectory,
|
|
310
|
+
userConfig,
|
|
311
|
+
readFileLinesSync: deps.readFileLinesSync,
|
|
312
|
+
startTime,
|
|
313
|
+
calculateDiagnosticsScore: deps.calculateDiagnosticsScore
|
|
314
|
+
});
|
|
315
|
+
return buildDiagnoseResult({
|
|
316
|
+
diagnostics: timed.diagnostics,
|
|
317
|
+
score: timed.score,
|
|
318
|
+
project: projectInfo,
|
|
319
|
+
elapsedMilliseconds: timed.elapsedMilliseconds
|
|
320
|
+
});
|
|
321
|
+
};
|
|
322
|
+
|
|
323
|
+
//#endregion
|
|
324
|
+
//#region src/adapters/browser/diagnose-browser.ts
|
|
325
|
+
const diagnoseBrowser = async (input, options = {}) => {
|
|
326
|
+
const readFileLinesSync = createBrowserReadFileLinesSync(input.rootDirectory, input.projectFiles);
|
|
327
|
+
return diagnoseCore({
|
|
328
|
+
rootDirectory: input.rootDirectory,
|
|
329
|
+
readFileLinesSync,
|
|
330
|
+
loadUserConfig: () => input.userConfig ?? null,
|
|
331
|
+
discoverProjectInfo: () => input.project,
|
|
332
|
+
calculateDiagnosticsScore: calculateScore,
|
|
333
|
+
createRunners: ({ lintIncludePaths, userConfig }) => ({
|
|
334
|
+
runLint: () => input.runOxlint({
|
|
335
|
+
lintIncludePaths,
|
|
336
|
+
customRulesOnly: userConfig?.customRulesOnly ?? false
|
|
337
|
+
}),
|
|
338
|
+
runDeadCode: async () => []
|
|
339
|
+
})
|
|
340
|
+
}, options);
|
|
341
|
+
};
|
|
342
|
+
|
|
343
|
+
//#endregion
|
|
344
|
+
//#region src/adapters/browser/process-browser-diagnostics.ts
|
|
345
|
+
const processBrowserDiagnostics = async (input) => {
|
|
346
|
+
const readFileLinesSync = createBrowserReadFileLinesSync(input.rootDirectory, input.projectFiles);
|
|
347
|
+
const userConfig = input.userConfig ?? null;
|
|
348
|
+
const timed = await buildDiagnoseTimedResult({
|
|
349
|
+
mergedDiagnostics: input.diagnostics,
|
|
350
|
+
rootDirectory: input.rootDirectory,
|
|
351
|
+
userConfig,
|
|
352
|
+
readFileLinesSync,
|
|
353
|
+
startTime: globalThis.performance.now(),
|
|
354
|
+
score: input.score,
|
|
355
|
+
calculateDiagnosticsScore: calculateScore
|
|
356
|
+
});
|
|
357
|
+
return {
|
|
358
|
+
diagnostics: timed.diagnostics,
|
|
359
|
+
score: timed.score
|
|
360
|
+
};
|
|
361
|
+
};
|
|
362
|
+
|
|
363
|
+
//#endregion
|
|
364
|
+
export { calculateScore as a, diagnose as i, diagnoseBrowser as n, calculateScoreLocally as o, diagnoseCore as r, processBrowserDiagnostics as t };
|
|
365
|
+
//# sourceMappingURL=process-browser-diagnostics-DpaZeYLI.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"process-browser-diagnostics-DpaZeYLI.js","names":["calculateScoreBrowser","calculateScoreBrowser","calculateScoreBrowser"],"sources":["../src/constants.ts","../src/core/calculate-score-locally.ts","../src/core/try-score-from-api.ts","../src/utils/calculate-score-browser.ts","../src/utils/match-glob-pattern.ts","../src/utils/is-ignored-file.ts","../src/utils/filter-diagnostics.ts","../src/utils/merge-and-filter-diagnostics.ts","../src/core/build-result.ts","../src/adapters/browser/create-browser-read-file-lines.ts","../src/adapters/browser/diagnose.ts","../src/core/build-diagnose-result.ts","../src/utils/jsx-include-paths.ts","../src/core/diagnose-core.ts","../src/adapters/browser/diagnose-browser.ts","../src/adapters/browser/process-browser-diagnostics.ts"],"sourcesContent":["export const SOURCE_FILE_PATTERN = /\\.(tsx?|jsx?)$/;\n\nexport const JSX_FILE_PATTERN = /\\.(tsx|jsx)$/;\n\nexport const MILLISECONDS_PER_SECOND = 1000;\n\nexport const ERROR_PREVIEW_LENGTH_CHARS = 200;\n\nexport const PERFECT_SCORE = 100;\n\nexport const SCORE_GOOD_THRESHOLD = 75;\n\nexport const SCORE_OK_THRESHOLD = 50;\n\nexport const SCORE_BAR_WIDTH_CHARS = 50;\n\nexport const SUMMARY_BOX_HORIZONTAL_PADDING_CHARS = 1;\n\nexport const SUMMARY_BOX_OUTER_INDENT_CHARS = 2;\n\nexport const SCORE_API_URL = \"https://www.react.doctor/api/score\";\n\nexport const SHARE_BASE_URL = \"https://www.react.doctor/share\";\n\nexport const FETCH_TIMEOUT_MS = 10_000;\n\nexport const GIT_LS_FILES_MAX_BUFFER_BYTES = 50 * 1024 * 1024;\n\n// HACK: Windows CreateProcessW limits total command-line length to 32,767 chars.\n// Use a conservative threshold to leave room for the executable path and quoting overhead.\nexport const SPAWN_ARGS_MAX_LENGTH_CHARS = 24_000;\n\n// HACK: oxlint can SIGABRT on very large file sets due to memory pressure.\n// Cap each batch to avoid OOM crashes on projects with 100+ source files.\nexport const OXLINT_MAX_FILES_PER_BATCH = 500;\n\nexport const OFFLINE_MESSAGE = \"Score calculated locally (offline mode).\";\n\nexport const DEFAULT_BRANCH_CANDIDATES = [\"main\", \"master\"];\n\nexport const ERROR_RULE_PENALTY = 1.5;\n\nexport const WARNING_RULE_PENALTY = 0.75;\n\nexport const MAX_KNIP_RETRIES = 5;\n\nexport const OXLINT_NODE_REQUIREMENT = \"^20.19.0 || >=22.12.0\";\n\nexport const OXLINT_RECOMMENDED_NODE_MAJOR = 24;\n\nexport const GIT_SHOW_MAX_BUFFER_BYTES = 10 * 1024 * 1024;\n\nexport const IGNORED_DIRECTORIES = new Set([\"node_modules\", \"dist\", \"build\", \"coverage\"]);\n","import {\n ERROR_RULE_PENALTY,\n PERFECT_SCORE,\n SCORE_GOOD_THRESHOLD,\n SCORE_OK_THRESHOLD,\n WARNING_RULE_PENALTY,\n} from \"../constants.js\";\nimport type { Diagnostic, ScoreResult } from \"../types.js\";\n\nconst getScoreLabel = (score: number): string => {\n if (score >= SCORE_GOOD_THRESHOLD) return \"Great\";\n if (score >= SCORE_OK_THRESHOLD) return \"Needs work\";\n return \"Critical\";\n};\n\nconst countUniqueRules = (\n diagnostics: Diagnostic[],\n): { errorRuleCount: number; warningRuleCount: number } => {\n const errorRules = new Set<string>();\n const warningRules = new Set<string>();\n\n for (const diagnostic of diagnostics) {\n const ruleKey = `${diagnostic.plugin}/${diagnostic.rule}`;\n if (diagnostic.severity === \"error\") {\n errorRules.add(ruleKey);\n } else {\n warningRules.add(ruleKey);\n }\n }\n\n return { errorRuleCount: errorRules.size, warningRuleCount: warningRules.size };\n};\n\nconst scoreFromRuleCounts = (errorRuleCount: number, warningRuleCount: number): number => {\n const penalty = errorRuleCount * ERROR_RULE_PENALTY + warningRuleCount * WARNING_RULE_PENALTY;\n return Math.max(0, Math.round(PERFECT_SCORE - penalty));\n};\n\nexport const calculateScoreLocally = (diagnostics: Diagnostic[]): ScoreResult => {\n const { errorRuleCount, warningRuleCount } = countUniqueRules(diagnostics);\n const score = scoreFromRuleCounts(errorRuleCount, warningRuleCount);\n return { score, label: getScoreLabel(score) };\n};\n","import { FETCH_TIMEOUT_MS, SCORE_API_URL } from \"../constants.js\";\nimport type { Diagnostic, ScoreResult } from \"../types.js\";\n\ninterface ScoreRequestFetch {\n (input: string | URL, init?: RequestInit): Promise<Response>;\n}\n\nconst parseScoreResult = (value: unknown): ScoreResult | null => {\n if (typeof value !== \"object\" || value === null) return null;\n if (!(\"score\" in value) || !(\"label\" in value)) return null;\n const scoreValue = Reflect.get(value, \"score\");\n const labelValue = Reflect.get(value, \"label\");\n if (typeof scoreValue !== \"number\" || typeof labelValue !== \"string\") return null;\n return { score: scoreValue, label: labelValue };\n};\n\nexport const tryScoreFromApi = async (\n diagnostics: Diagnostic[],\n fetchImplementation: ScoreRequestFetch,\n): Promise<ScoreResult | null> => {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);\n\n try {\n const response = await fetchImplementation(SCORE_API_URL, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ diagnostics }),\n signal: controller.signal,\n });\n\n if (!response.ok) return null;\n\n return parseScoreResult(await response.json());\n } catch {\n return null;\n } finally {\n clearTimeout(timeoutId);\n }\n};\n","import type { Diagnostic, ScoreResult } from \"../types.js\";\nimport { calculateScoreLocally } from \"../core/calculate-score-locally.js\";\nimport { tryScoreFromApi } from \"../core/try-score-from-api.js\";\n\nexport { calculateScoreLocally } from \"../core/calculate-score-locally.js\";\n\nexport const calculateScore = async (diagnostics: Diagnostic[]): Promise<ScoreResult | null> =>\n (await tryScoreFromApi(diagnostics, fetch)) ?? calculateScoreLocally(diagnostics);\n","const REGEX_SPECIAL_CHARACTERS = /[.+^${}()|[\\]\\\\]/g;\n\nexport const compileGlobPattern = (pattern: string): RegExp => {\n const normalizedPattern = pattern.replace(/\\\\/g, \"/\").replace(/^\\//, \"\");\n\n let regexSource = \"^\";\n let characterIndex = 0;\n\n while (characterIndex < normalizedPattern.length) {\n if (\n normalizedPattern[characterIndex] === \"*\" &&\n normalizedPattern[characterIndex + 1] === \"*\"\n ) {\n if (normalizedPattern[characterIndex + 2] === \"/\") {\n regexSource += \"(?:.+/)?\";\n characterIndex += 3;\n } else {\n regexSource += \".*\";\n characterIndex += 2;\n }\n } else if (normalizedPattern[characterIndex] === \"*\") {\n regexSource += \"[^/]*\";\n characterIndex++;\n } else if (normalizedPattern[characterIndex] === \"?\") {\n regexSource += \"[^/]\";\n characterIndex++;\n } else {\n regexSource += normalizedPattern[characterIndex].replace(REGEX_SPECIAL_CHARACTERS, \"\\\\$&\");\n characterIndex++;\n }\n }\n\n regexSource += \"$\";\n return new RegExp(regexSource);\n};\n\nexport const matchGlobPattern = (filePath: string, pattern: string): boolean => {\n const normalizedPath = filePath.replace(/\\\\/g, \"/\");\n return compileGlobPattern(pattern).test(normalizedPath);\n};\n","import type { ReactDoctorConfig } from \"../types.js\";\nimport { compileGlobPattern } from \"./match-glob-pattern.js\";\n\nconst toRelativePath = (filePath: string, rootDirectory: string): string => {\n const normalizedFilePath = filePath.replace(/\\\\/g, \"/\");\n const normalizedRoot = rootDirectory.replace(/\\\\/g, \"/\").replace(/\\/$/, \"\") + \"/\";\n\n if (normalizedFilePath.startsWith(normalizedRoot)) {\n return normalizedFilePath.slice(normalizedRoot.length);\n }\n\n return normalizedFilePath.replace(/^\\.\\//, \"\");\n};\n\nexport const compileIgnoredFilePatterns = (userConfig: ReactDoctorConfig | null): RegExp[] =>\n Array.isArray(userConfig?.ignore?.files) ? userConfig.ignore.files.map(compileGlobPattern) : [];\n\nexport const isFileIgnoredByPatterns = (\n filePath: string,\n rootDirectory: string,\n patterns: RegExp[],\n): boolean => {\n if (patterns.length === 0) {\n return false;\n }\n\n const relativePath = toRelativePath(filePath, rootDirectory);\n return patterns.some((pattern) => pattern.test(relativePath));\n};\n","import type { Diagnostic, ReactDoctorConfig } from \"../types.js\";\nimport { compileIgnoredFilePatterns, isFileIgnoredByPatterns } from \"./is-ignored-file.js\";\n\nconst resolveCandidateReadPath = (rootDirectory: string, filePath: string): string => {\n const normalizedFile = filePath.replace(/\\\\/g, \"/\");\n if (\n normalizedFile.startsWith(\"/\") ||\n /^[a-zA-Z]:\\//.test(normalizedFile) ||\n /^[a-zA-Z]:\\\\/.test(filePath)\n ) {\n return filePath;\n }\n const root = rootDirectory.replace(/\\\\/g, \"/\").replace(/\\/$/, \"\");\n return `${root}/${normalizedFile.replace(/^\\.\\//, \"\")}`;\n};\n\nconst OPENING_TAG_PATTERN = /<([A-Z][\\w.]*)/;\nconst DISABLE_NEXT_LINE_PATTERN = /\\/\\/\\s*react-doctor-disable-next-line\\b(?:\\s+(.+))?/;\nconst DISABLE_LINE_PATTERN = /\\/\\/\\s*react-doctor-disable-line\\b(?:\\s+(.+))?/;\n\nconst createFileLinesCache = (\n rootDirectory: string,\n readFileLinesSync: (filePath: string) => string[] | null,\n) => {\n const cache = new Map<string, string[] | null>();\n\n return (filePath: string): string[] | null => {\n const cached = cache.get(filePath);\n if (cached !== undefined) return cached;\n const absolutePath = resolveCandidateReadPath(rootDirectory, filePath);\n const lines = readFileLinesSync(absolutePath);\n cache.set(filePath, lines);\n return lines;\n };\n};\n\nconst isInsideTextComponent = (\n lines: string[],\n diagnosticLine: number,\n textComponentNames: Set<string>,\n): boolean => {\n for (let lineIndex = diagnosticLine - 1; lineIndex >= 0; lineIndex--) {\n const match = lines[lineIndex].match(OPENING_TAG_PATTERN);\n if (!match) continue;\n const fullTagName = match[1];\n const leafTagName = fullTagName.includes(\".\")\n ? (fullTagName.split(\".\").at(-1) ?? fullTagName)\n : fullTagName;\n return textComponentNames.has(fullTagName) || textComponentNames.has(leafTagName);\n }\n return false;\n};\n\nconst isRuleSuppressed = (commentRules: string | undefined, ruleId: string): boolean => {\n if (!commentRules?.trim()) return true;\n return commentRules.split(/[,\\s]+/).some((rule) => rule.trim() === ruleId);\n};\n\nexport const filterIgnoredDiagnostics = (\n diagnostics: Diagnostic[],\n config: ReactDoctorConfig,\n rootDirectory: string,\n readFileLinesSync: (filePath: string) => string[] | null,\n): Diagnostic[] => {\n const ignoredRules = new Set(Array.isArray(config.ignore?.rules) ? config.ignore.rules : []);\n const ignoredFilePatterns = compileIgnoredFilePatterns(config);\n const textComponentNames = new Set(\n Array.isArray(config.textComponents) ? config.textComponents : [],\n );\n const hasTextComponents = textComponentNames.size > 0;\n const getFileLines = createFileLinesCache(rootDirectory, readFileLinesSync);\n\n return diagnostics.filter((diagnostic) => {\n const ruleIdentifier = `${diagnostic.plugin}/${diagnostic.rule}`;\n if (ignoredRules.has(ruleIdentifier)) {\n return false;\n }\n\n if (isFileIgnoredByPatterns(diagnostic.filePath, rootDirectory, ignoredFilePatterns)) {\n return false;\n }\n\n if (hasTextComponents && diagnostic.rule === \"rn-no-raw-text\" && diagnostic.line > 0) {\n const lines = getFileLines(diagnostic.filePath);\n if (lines && isInsideTextComponent(lines, diagnostic.line, textComponentNames)) {\n return false;\n }\n }\n\n return true;\n });\n};\n\nexport const filterInlineSuppressions = (\n diagnostics: Diagnostic[],\n rootDirectory: string,\n readFileLinesSync: (filePath: string) => string[] | null,\n): Diagnostic[] => {\n const getFileLines = createFileLinesCache(rootDirectory, readFileLinesSync);\n\n return diagnostics.filter((diagnostic) => {\n if (diagnostic.line <= 0) return true;\n\n const lines = getFileLines(diagnostic.filePath);\n if (!lines) return true;\n\n const ruleId = `${diagnostic.plugin}/${diagnostic.rule}`;\n\n const currentLine = lines[diagnostic.line - 1];\n if (currentLine) {\n const lineMatch = currentLine.match(DISABLE_LINE_PATTERN);\n if (lineMatch && isRuleSuppressed(lineMatch[1], ruleId)) return false;\n }\n\n if (diagnostic.line >= 2) {\n const previousLine = lines[diagnostic.line - 2];\n if (previousLine) {\n const nextLineMatch = previousLine.match(DISABLE_NEXT_LINE_PATTERN);\n if (nextLineMatch && isRuleSuppressed(nextLineMatch[1], ruleId)) return false;\n }\n }\n\n return true;\n });\n};\n","import type { Diagnostic, ReactDoctorConfig } from \"../types.js\";\nimport { filterIgnoredDiagnostics, filterInlineSuppressions } from \"./filter-diagnostics.js\";\n\nexport const mergeAndFilterDiagnostics = (\n mergedDiagnostics: Diagnostic[],\n directory: string,\n userConfig: ReactDoctorConfig | null,\n readFileLinesSync: (filePath: string) => string[] | null,\n): Diagnostic[] => {\n const filtered = userConfig\n ? filterIgnoredDiagnostics(mergedDiagnostics, userConfig, directory, readFileLinesSync)\n : mergedDiagnostics;\n return filterInlineSuppressions(filtered, directory, readFileLinesSync);\n};\n","import type { Diagnostic, ReactDoctorConfig, ScoreResult } from \"../types.js\";\nimport { mergeAndFilterDiagnostics } from \"../utils/merge-and-filter-diagnostics.js\";\n\nexport interface BuildDiagnoseResultInput {\n mergedDiagnostics: Diagnostic[];\n rootDirectory: string;\n userConfig: ReactDoctorConfig | null;\n readFileLinesSync: (filePath: string) => string[] | null;\n startTime: number;\n score?: ScoreResult | null;\n calculateDiagnosticsScore: (diagnostics: Diagnostic[]) => Promise<ScoreResult | null>;\n}\n\nexport interface BuildDiagnoseTimedResult {\n diagnostics: Diagnostic[];\n score: ScoreResult | null;\n elapsedMilliseconds: number;\n}\n\nexport const buildDiagnoseTimedResult = async (\n input: BuildDiagnoseResultInput,\n): Promise<BuildDiagnoseTimedResult> => {\n const diagnostics = mergeAndFilterDiagnostics(\n input.mergedDiagnostics,\n input.rootDirectory,\n input.userConfig,\n input.readFileLinesSync,\n );\n const elapsedMilliseconds = globalThis.performance.now() - input.startTime;\n const score =\n input.score !== undefined ? input.score : await input.calculateDiagnosticsScore(diagnostics);\n return { diagnostics, score, elapsedMilliseconds };\n};\n","const normalizeKey = (rootDirectory: string, filePath: string): string => {\n const normalizedRoot = rootDirectory.replace(/\\\\/g, \"/\").replace(/\\/$/, \"\");\n const normalizedPath = filePath.replace(/\\\\/g, \"/\");\n if (normalizedPath.startsWith(normalizedRoot + \"/\")) {\n return normalizedPath.slice(normalizedRoot.length + 1);\n }\n return normalizedPath.replace(/^\\.\\//, \"\");\n};\n\nexport const createBrowserReadFileLinesSync = (\n rootDirectory: string,\n projectFiles: Record<string, string>,\n): ((absoluteOrRelativePath: string) => string[] | null) => {\n return (absoluteOrRelativePath: string): string[] | null => {\n const key = normalizeKey(rootDirectory, absoluteOrRelativePath);\n const content = projectFiles[key];\n if (content === undefined) return null;\n return content.split(\"\\n\");\n };\n};\n","import type { Diagnostic, ProjectInfo, ReactDoctorConfig, ScoreResult } from \"../../types.js\";\nimport { buildDiagnoseTimedResult } from \"../../core/build-result.js\";\nimport { calculateScore as calculateScoreBrowser } from \"../../utils/calculate-score-browser.js\";\nimport { createBrowserReadFileLinesSync } from \"./create-browser-read-file-lines.js\";\n\nexport interface BrowserDiagnoseInput {\n rootDirectory: string;\n project: ProjectInfo;\n projectFiles: Record<string, string>;\n lintDiagnostics: Diagnostic[];\n deadCodeDiagnostics?: Diagnostic[];\n userConfig?: ReactDoctorConfig | null;\n score?: ScoreResult | null;\n}\n\nexport interface BrowserDiagnoseResult {\n diagnostics: Diagnostic[];\n score: ScoreResult | null;\n project: ProjectInfo;\n elapsedMilliseconds: number;\n}\n\nexport const diagnose = async (input: BrowserDiagnoseInput): Promise<BrowserDiagnoseResult> => {\n if (!input.project.reactVersion) {\n throw new Error(\"No React dependency found in package.json\");\n }\n\n const readFileLinesSync = createBrowserReadFileLinesSync(input.rootDirectory, input.projectFiles);\n const userConfig = input.userConfig ?? null;\n const deadCodeDiagnostics = input.deadCodeDiagnostics ?? [];\n const mergedDiagnostics = [...input.lintDiagnostics, ...deadCodeDiagnostics];\n const startTime = globalThis.performance.now();\n\n const timed = await buildDiagnoseTimedResult({\n mergedDiagnostics,\n rootDirectory: input.rootDirectory,\n userConfig,\n readFileLinesSync,\n startTime,\n score: input.score,\n calculateDiagnosticsScore: calculateScoreBrowser,\n });\n\n return {\n diagnostics: timed.diagnostics,\n score: timed.score,\n project: input.project,\n elapsedMilliseconds: timed.elapsedMilliseconds,\n };\n};\n","import type { Diagnostic, ProjectInfo, ScoreResult } from \"../types.js\";\n\ninterface BuildDiagnoseResultParams {\n diagnostics: Diagnostic[];\n project: ProjectInfo;\n elapsedMilliseconds: number;\n score: ScoreResult | null;\n}\n\ninterface DiagnoseResultShape {\n diagnostics: Diagnostic[];\n score: ScoreResult | null;\n project: ProjectInfo;\n elapsedMilliseconds: number;\n}\n\nexport const buildDiagnoseResult = (params: BuildDiagnoseResultParams): DiagnoseResultShape => ({\n diagnostics: params.diagnostics,\n score: params.score,\n project: params.project,\n elapsedMilliseconds: params.elapsedMilliseconds,\n});\n","import { JSX_FILE_PATTERN } from \"../constants.js\";\n\nexport const computeJsxIncludePaths = (includePaths: string[]): string[] | undefined =>\n includePaths.length > 0\n ? includePaths.filter((filePath) => JSX_FILE_PATTERN.test(filePath))\n : undefined;\n","import type { Diagnostic, ProjectInfo, ReactDoctorConfig, ScoreResult } from \"../types.js\";\nimport { buildDiagnoseResult } from \"./build-diagnose-result.js\";\nimport { buildDiagnoseTimedResult } from \"./build-result.js\";\nimport { computeJsxIncludePaths } from \"../utils/jsx-include-paths.js\";\n\nexport interface DiagnoseCoreOptions {\n lint?: boolean;\n deadCode?: boolean;\n includePaths?: string[];\n lintIncludePaths?: string[] | undefined;\n}\n\nexport interface DiagnoseCoreResult {\n diagnostics: Diagnostic[];\n score: ScoreResult | null;\n project: ProjectInfo;\n elapsedMilliseconds: number;\n}\n\nexport interface DiagnoseRunnerContext {\n resolvedDirectory: string;\n projectInfo: ProjectInfo;\n userConfig: ReactDoctorConfig | null;\n lintIncludePaths: string[] | undefined;\n isDiffMode: boolean;\n}\n\nexport interface DiagnoseCoreDeps {\n rootDirectory: string;\n readFileLinesSync: (filePath: string) => string[] | null;\n loadUserConfig: () => ReactDoctorConfig | null;\n discoverProjectInfo: () => ProjectInfo;\n calculateDiagnosticsScore: (diagnostics: Diagnostic[]) => Promise<ScoreResult | null>;\n getExtraDiagnostics?: () => Diagnostic[];\n createRunners: (context: DiagnoseRunnerContext) => {\n runLint: () => Promise<Diagnostic[]>;\n runDeadCode: () => Promise<Diagnostic[]>;\n };\n}\n\nexport const diagnoseCore = async (\n deps: DiagnoseCoreDeps,\n options: DiagnoseCoreOptions = {},\n): Promise<DiagnoseCoreResult> => {\n const { includePaths = [] } = options;\n const isDiffMode = includePaths.length > 0;\n\n const startTime = globalThis.performance.now();\n const resolvedDirectory = deps.rootDirectory;\n const projectInfo = deps.discoverProjectInfo();\n const userConfig = deps.loadUserConfig();\n\n const effectiveLint = options.lint ?? userConfig?.lint ?? true;\n const effectiveDeadCode = options.deadCode ?? userConfig?.deadCode ?? true;\n\n if (!projectInfo.reactVersion) {\n throw new Error(\"No React dependency found in package.json\");\n }\n\n const lintIncludePaths =\n options.lintIncludePaths !== undefined\n ? options.lintIncludePaths\n : computeJsxIncludePaths(includePaths);\n\n const { runLint, runDeadCode } = deps.createRunners({\n resolvedDirectory,\n projectInfo,\n userConfig,\n lintIncludePaths,\n isDiffMode,\n });\n\n const emptyDiagnostics: Diagnostic[] = [];\n\n const lintPromise = effectiveLint\n ? runLint().catch((error: unknown) => {\n console.error(\"Lint failed:\", error);\n return emptyDiagnostics;\n })\n : Promise.resolve(emptyDiagnostics);\n\n const deadCodePromise =\n effectiveDeadCode && !isDiffMode\n ? runDeadCode().catch((error: unknown) => {\n console.error(\"Dead code analysis failed:\", error);\n return emptyDiagnostics;\n })\n : Promise.resolve(emptyDiagnostics);\n\n const [lintDiagnostics, deadCodeDiagnostics] = await Promise.all([lintPromise, deadCodePromise]);\n const environmentDiagnostics = deps.getExtraDiagnostics?.() ?? [];\n const mergedDiagnostics = [...lintDiagnostics, ...deadCodeDiagnostics, ...environmentDiagnostics];\n const timed = await buildDiagnoseTimedResult({\n mergedDiagnostics,\n rootDirectory: resolvedDirectory,\n userConfig,\n readFileLinesSync: deps.readFileLinesSync,\n startTime,\n calculateDiagnosticsScore: deps.calculateDiagnosticsScore,\n });\n\n return buildDiagnoseResult({\n diagnostics: timed.diagnostics,\n score: timed.score,\n project: projectInfo,\n elapsedMilliseconds: timed.elapsedMilliseconds,\n });\n};\n","import type { Diagnostic, ProjectInfo, ReactDoctorConfig } from \"../../types.js\";\nimport type { DiagnoseCoreOptions } from \"../../core/diagnose-core.js\";\nimport { diagnoseCore } from \"../../core/diagnose-core.js\";\nimport { calculateScore as calculateScoreBrowser } from \"../../utils/calculate-score-browser.js\";\nimport { createBrowserReadFileLinesSync } from \"./create-browser-read-file-lines.js\";\n\nexport interface DiagnoseBrowserInput {\n rootDirectory: string;\n project: ProjectInfo;\n projectFiles: Record<string, string>;\n userConfig?: ReactDoctorConfig | null;\n runOxlint: (input: {\n lintIncludePaths: string[] | undefined;\n customRulesOnly: boolean;\n }) => Promise<Diagnostic[]>;\n}\n\nexport const diagnoseBrowser = async (\n input: DiagnoseBrowserInput,\n options: DiagnoseCoreOptions = {},\n) => {\n const readFileLinesSync = createBrowserReadFileLinesSync(input.rootDirectory, input.projectFiles);\n\n return diagnoseCore(\n {\n rootDirectory: input.rootDirectory,\n readFileLinesSync,\n loadUserConfig: () => input.userConfig ?? null,\n discoverProjectInfo: () => input.project,\n calculateDiagnosticsScore: calculateScoreBrowser,\n createRunners: ({ lintIncludePaths, userConfig }) => ({\n runLint: () =>\n input.runOxlint({\n lintIncludePaths,\n customRulesOnly: userConfig?.customRulesOnly ?? false,\n }),\n runDeadCode: async () => [],\n }),\n },\n options,\n );\n};\n","import type { Diagnostic, ReactDoctorConfig, ScoreResult } from \"../../types.js\";\nimport { buildDiagnoseTimedResult } from \"../../core/build-result.js\";\nimport { calculateScore as calculateScoreBrowser } from \"../../utils/calculate-score-browser.js\";\nimport { createBrowserReadFileLinesSync } from \"./create-browser-read-file-lines.js\";\n\nexport interface ProcessBrowserDiagnosticsInput {\n rootDirectory: string;\n projectFiles: Record<string, string>;\n diagnostics: Diagnostic[];\n userConfig?: ReactDoctorConfig | null;\n score?: ScoreResult | null;\n}\n\nexport interface ProcessBrowserDiagnosticsResult {\n diagnostics: Diagnostic[];\n score: ScoreResult | null;\n}\n\nexport const processBrowserDiagnostics = async (\n input: ProcessBrowserDiagnosticsInput,\n): Promise<ProcessBrowserDiagnosticsResult> => {\n const readFileLinesSync = createBrowserReadFileLinesSync(input.rootDirectory, input.projectFiles);\n const userConfig = input.userConfig ?? null;\n const timed = await buildDiagnoseTimedResult({\n mergedDiagnostics: input.diagnostics,\n rootDirectory: input.rootDirectory,\n userConfig,\n readFileLinesSync,\n startTime: globalThis.performance.now(),\n score: input.score,\n calculateDiagnosticsScore: calculateScoreBrowser,\n });\n return { diagnostics: timed.diagnostics, score: timed.score };\n};\n"],"mappings":"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"}
|
package/dist/worker.d.ts
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { a as diagnoseBrowser, d as diagnose, f as calculateScore, i as DiagnoseBrowserInput, l as BrowserDiagnoseInput, n as ProcessBrowserDiagnosticsResult, o as DiagnoseCoreOptions, p as calculateScoreLocally, r as processBrowserDiagnostics, s as DiagnoseCoreResult, t as ProcessBrowserDiagnosticsInput, u as BrowserDiagnoseResult } from "./process-browser-diagnostics-Cahx3_oy.js";
|
|
2
|
+
export { type BrowserDiagnoseInput, type BrowserDiagnoseResult, type DiagnoseBrowserInput, type DiagnoseCoreOptions, type DiagnoseCoreResult, type ProcessBrowserDiagnosticsInput, type ProcessBrowserDiagnosticsResult, calculateScore, calculateScoreLocally, diagnose, diagnoseBrowser, processBrowserDiagnostics };
|
package/dist/worker.js
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { a as calculateScore, i as diagnose, n as diagnoseBrowser, o as calculateScoreLocally, t as processBrowserDiagnostics } from "./process-browser-diagnostics-DpaZeYLI.js";
|
|
2
|
+
|
|
3
|
+
export { calculateScore, calculateScoreLocally, diagnose, diagnoseBrowser, processBrowserDiagnostics };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-doctor",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.39",
|
|
4
4
|
"description": "Diagnose and fix performance issues in your React app",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"diagnostics",
|
|
@@ -36,9 +36,17 @@
|
|
|
36
36
|
"types": "./dist/index.d.ts",
|
|
37
37
|
"default": "./dist/index.js"
|
|
38
38
|
},
|
|
39
|
+
"./browser": {
|
|
40
|
+
"types": "./dist/browser.d.ts",
|
|
41
|
+
"default": "./dist/browser.js"
|
|
42
|
+
},
|
|
39
43
|
"./oxlint-plugin": {
|
|
40
44
|
"types": "./dist/react-doctor-plugin.d.ts",
|
|
41
45
|
"default": "./dist/react-doctor-plugin.js"
|
|
46
|
+
},
|
|
47
|
+
"./worker": {
|
|
48
|
+
"types": "./dist/worker.d.ts",
|
|
49
|
+
"default": "./dist/worker.js"
|
|
42
50
|
}
|
|
43
51
|
},
|
|
44
52
|
"dependencies": {
|