@aipanel/core 1.3.4 → 1.3.5
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/es/common/constants.d.ts +3 -2
- package/es/common/constants.mjs +2 -1
- package/es/common/diagnostics.d.ts +199 -0
- package/es/common/diagnostics.mjs +226 -0
- package/es/common/options.d.ts +0 -2
- package/es/index.d.ts +1 -0
- package/es/index.mjs +1 -0
- package/es/node/diagnostics.d.ts +52 -34
- package/es/node/diagnostics.mjs +496 -177
- package/es/node.d.ts +1 -0
- package/es/node.mjs +1 -0
- package/lib/common/constants.cjs +2 -1
- package/lib/common/constants.d.ts +3 -2
- package/lib/common/diagnostics.cjs +261 -0
- package/lib/common/diagnostics.d.ts +199 -0
- package/lib/common/options.d.ts +0 -2
- package/lib/index.cjs +2 -0
- package/lib/index.d.ts +1 -0
- package/lib/node/diagnostics.cjs +490 -183
- package/lib/node/diagnostics.d.ts +52 -34
- package/lib/node.cjs +2 -0
- package/lib/node.d.ts +1 -0
- package/package.json +1 -1
package/lib/node/diagnostics.cjs
CHANGED
|
@@ -28,16 +28,13 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
28
28
|
var diagnostics_exports = {};
|
|
29
29
|
__export(diagnostics_exports, {
|
|
30
30
|
DIAGNOSTICS_TOOL_DESCRIPTION: () => DIAGNOSTICS_TOOL_DESCRIPTION,
|
|
31
|
+
collectDiagnostics: () => collectDiagnostics,
|
|
31
32
|
findAllTsconfigDirs: () => findAllTsconfigDirs,
|
|
32
33
|
findTsconfigDir: () => findTsconfigDir,
|
|
33
|
-
formatDiagnosticsSections: () => formatDiagnosticsSections,
|
|
34
34
|
isJsFile: () => isJsFile,
|
|
35
|
-
lintFiles: () => lintFiles,
|
|
36
|
-
lintSectionTitle: () => lintSectionTitle,
|
|
37
35
|
omittedFindingsHint: () => omittedFindingsHint,
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
runProjectDiagnostics: () => runProjectDiagnostics,
|
|
36
|
+
renderDiagnostics: () => renderDiagnostics,
|
|
37
|
+
runDiagnostics: () => runDiagnostics,
|
|
41
38
|
runTypeCheck: () => runTypeCheck,
|
|
42
39
|
runTypeChecksForFiles: () => runTypeChecksForFiles,
|
|
43
40
|
tscSectionTitle: () => tscSectionTitle
|
|
@@ -45,31 +42,23 @@ __export(diagnostics_exports, {
|
|
|
45
42
|
module.exports = __toCommonJS(diagnostics_exports);
|
|
46
43
|
var import_node_fs = __toESM(require("node:fs"));
|
|
47
44
|
var import_node_path = __toESM(require("node:path"));
|
|
45
|
+
var import_node_url = require("node:url");
|
|
48
46
|
var import_execa = require("execa");
|
|
49
47
|
var import_node_module = require("node:module");
|
|
50
48
|
var import_constants = require("../common/constants.cjs");
|
|
49
|
+
var import_diagnostics = require("../common/diagnostics.cjs");
|
|
51
50
|
var import_node_logger = require("./node-logger.cjs");
|
|
52
51
|
const import_meta = {};
|
|
53
52
|
const log = (0, import_node_logger.createLogger)("Diagnostics");
|
|
54
|
-
const JS_EXTENSIONS =
|
|
55
|
-
".js",
|
|
56
|
-
".jsx",
|
|
57
|
-
".ts",
|
|
58
|
-
".tsx",
|
|
59
|
-
".mjs",
|
|
60
|
-
".cjs",
|
|
61
|
-
".mts",
|
|
62
|
-
".cts",
|
|
63
|
-
".vue"
|
|
64
|
-
]);
|
|
53
|
+
const JS_EXTENSIONS = new Set(import_diagnostics.SOURCE_EXTENSIONS);
|
|
65
54
|
function isJsFile(filePath) {
|
|
66
55
|
return JS_EXTENSIONS.has(import_node_path.default.extname(filePath));
|
|
67
56
|
}
|
|
68
57
|
const DIAGNOSTICS_TOOL_DESCRIPTION = [
|
|
69
|
-
"\u8FD0\u884C Lint
|
|
58
|
+
"\u8FD0\u884C\u9879\u76EE\u914D\u7F6E\u7684\u4EE3\u7801\u68C0\u67E5\uFF08\u9ED8\u8BA4 Lint \u4E0E TypeScript \u7C7B\u578B\u8BCA\u65AD\uFF09\uFF0C\u8FD4\u56DE\u8BCA\u65AD\u7ED3\u679C\u3002",
|
|
70
59
|
"",
|
|
71
60
|
"**\u652F\u6301\u7684\u6587\u4EF6\u7C7B\u578B**\uFF1A",
|
|
72
|
-
`- Lint\uFF1AJavaScript / TypeScript / Vue \u6E90\u7801\uFF08${[...JS_EXTENSIONS].map((e) => `*${e}`).join(" ")}\uFF09\uFF1B\
|
|
61
|
+
`- Lint\uFF1AJavaScript / TypeScript / Vue \u6E90\u7801\uFF08${[...JS_EXTENSIONS].map((e) => `*${e}`).join(" ")}\uFF09\uFF1B\u9879\u76EE\u540C\u65F6\u88C5\u6709 ESLint \u4E0E oxlint \u65F6\u4E92\u8865\u8FD0\u884C\uFF08\u8BCA\u65AD\u6309\u6765\u6E90\u6807\u6CE8\uFF09`,
|
|
73
62
|
"- TypeScript \u7C7B\u578B\u68C0\u67E5\uFF1A*.ts *.tsx *.vue",
|
|
74
63
|
"",
|
|
75
64
|
"**\u4F55\u65F6\u4F7F\u7528\u6B64\u5DE5\u5177**\uFF1A",
|
|
@@ -99,53 +88,14 @@ function resolvePackageBin(basePkgJson, pkgName, binName) {
|
|
|
99
88
|
}
|
|
100
89
|
});
|
|
101
90
|
}
|
|
102
|
-
|
|
103
|
-
function loadESLint(workspace) {
|
|
104
|
-
const basePkgJson = import_node_path.default.join(workspace, "package.json");
|
|
105
|
-
return cachedResolve(_eslintClassByBase, basePkgJson, () => {
|
|
106
|
-
log.debug("Loading eslint", { workspace });
|
|
107
|
-
try {
|
|
108
|
-
const req = (0, import_node_module.createRequire)(basePkgJson);
|
|
109
|
-
const eslintModule = req("eslint");
|
|
110
|
-
const eslintClass = eslintModule.ESLint ?? eslintModule.FlatESLint ?? null;
|
|
111
|
-
log.debug("eslint loaded", { hasClass: !!eslintClass });
|
|
112
|
-
return eslintClass;
|
|
113
|
-
} catch (e) {
|
|
114
|
-
log.warn("eslint not found", { error: e.message });
|
|
115
|
-
return null;
|
|
116
|
-
}
|
|
117
|
-
});
|
|
118
|
-
}
|
|
119
|
-
async function lintFiles(pattern, cwd, warnLimit = 5) {
|
|
120
|
-
const eslintClass = loadESLint(cwd);
|
|
121
|
-
const [eslintOutput, oxlintOutput] = await Promise.all([
|
|
122
|
-
eslintClass ? runEslintFiles(eslintClass, pattern, cwd, warnLimit) : Promise.resolve(null),
|
|
123
|
-
runOxlintFiles(pattern, cwd, warnLimit)
|
|
124
|
-
]);
|
|
125
|
-
const engines = [];
|
|
126
|
-
const texts = [];
|
|
127
|
-
const diagnostics = [];
|
|
128
|
-
for (const output of [eslintOutput, oxlintOutput]) {
|
|
129
|
-
if (!output) continue;
|
|
130
|
-
engines.push(...output.engines ?? []);
|
|
131
|
-
if (output.text) texts.push(output.text);
|
|
132
|
-
diagnostics.push(...output.diagnostics ?? []);
|
|
133
|
-
}
|
|
134
|
-
if (engines.length === 0) {
|
|
135
|
-
return {
|
|
136
|
-
text: `[Lint] \u672A\u8FD0\u884C\uFF1A\u65E0\u6CD5\u5728 workspace "${cwd}" \u89E3\u6790\u5230 eslint \u6216 oxlint`,
|
|
137
|
-
diagnostics: []
|
|
138
|
-
};
|
|
139
|
-
}
|
|
140
|
-
return { text: texts.join("\n\n") || void 0, diagnostics, engines };
|
|
141
|
-
}
|
|
142
|
-
function formatLintMessages(messages, engine, warnLimit) {
|
|
143
|
-
if (messages.length === 0) return { engines: [engine.label] };
|
|
91
|
+
function formatLintMessages(messages, engine, warnLimit, minSeverity = "warning") {
|
|
144
92
|
const ESLINT_ERROR = 2;
|
|
145
93
|
const ESLINT_WARN = 1;
|
|
94
|
+
const kept = minSeverity === "error" ? messages.filter((m) => m.severity === ESLINT_ERROR) : messages;
|
|
95
|
+
if (kept.length === 0) return {};
|
|
146
96
|
const lines = [];
|
|
147
|
-
const errors =
|
|
148
|
-
const warnings =
|
|
97
|
+
const errors = kept.filter((m) => m.severity === ESLINT_ERROR);
|
|
98
|
+
const warnings = kept.filter((m) => m.severity === ESLINT_WARN);
|
|
149
99
|
if (errors.length > 0) {
|
|
150
100
|
lines.push(
|
|
151
101
|
...errors.map(
|
|
@@ -175,35 +125,9 @@ function formatLintMessages(messages, engine, warnLimit) {
|
|
|
175
125
|
}));
|
|
176
126
|
return {
|
|
177
127
|
text: lines.length > 0 ? lines.join("\n") : void 0,
|
|
178
|
-
diagnostics
|
|
179
|
-
engines: [engine.label]
|
|
128
|
+
diagnostics
|
|
180
129
|
};
|
|
181
130
|
}
|
|
182
|
-
async function runEslintFiles(eslintClass, pattern, cwd, warnLimit) {
|
|
183
|
-
try {
|
|
184
|
-
const eslint = new eslintClass({ cwd });
|
|
185
|
-
const results = await eslint.lintFiles(pattern);
|
|
186
|
-
const messages = results.flatMap(
|
|
187
|
-
(r) => (r.messages ?? []).map((m) => ({ ...m, filePath: r.filePath }))
|
|
188
|
-
);
|
|
189
|
-
log.debug("ESLint lint", {
|
|
190
|
-
pattern,
|
|
191
|
-
fileCount: results.length,
|
|
192
|
-
messageCount: messages.length
|
|
193
|
-
});
|
|
194
|
-
return formatLintMessages(messages, { label: "ESLint", source: "eslint" }, warnLimit);
|
|
195
|
-
} catch (err) {
|
|
196
|
-
log.warn("ESLint failed", { pattern, error: err.message });
|
|
197
|
-
return {
|
|
198
|
-
text: `[ESLint] \u8FD0\u884C\u5931\u8D25\uFF1A${err.message}\uFF08\u4EC5\u663E\u793A TypeScript \u8BCA\u65AD\uFF09`,
|
|
199
|
-
diagnostics: [],
|
|
200
|
-
engines: ["ESLint"]
|
|
201
|
-
};
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
function resolveOxlintBin(workspace) {
|
|
205
|
-
return resolvePackageBin(import_node_path.default.join(workspace, "package.json"), "oxlint", "oxlint");
|
|
206
|
-
}
|
|
207
131
|
function oxlintRuleName(code) {
|
|
208
132
|
if (!code) return null;
|
|
209
133
|
const match = /\(([^)]*)\)$/.exec(code);
|
|
@@ -246,39 +170,6 @@ function parseOxlintOutput(stdout, cwd) {
|
|
|
246
170
|
];
|
|
247
171
|
});
|
|
248
172
|
}
|
|
249
|
-
async function runOxlintFiles(pattern, cwd, warnLimit) {
|
|
250
|
-
const bin = resolveOxlintBin(cwd);
|
|
251
|
-
if (!bin) return {};
|
|
252
|
-
const result = await (0, import_execa.execa)(
|
|
253
|
-
"node",
|
|
254
|
-
[bin, "--format=json", "--ignore-pattern", "node_modules", pattern],
|
|
255
|
-
{ cwd, timeout: 6e4, maxBuffer: 50 * 1024 * 1024, reject: false }
|
|
256
|
-
);
|
|
257
|
-
if (result.timedOut || result.isTerminated || result.isMaxBuffer) {
|
|
258
|
-
log.warn("oxlint timed out", { pattern });
|
|
259
|
-
return {
|
|
260
|
-
text: "[oxlint] \u8FD0\u884C\u5931\u8D25\uFF1A\u68C0\u67E5\u8D85\u65F6\uFF0C\u8BF7\u5C1D\u8BD5\u7F29\u5C0F\u68C0\u67E5\u8303\u56F4\u3002",
|
|
261
|
-
diagnostics: [],
|
|
262
|
-
engines: ["oxlint"]
|
|
263
|
-
};
|
|
264
|
-
}
|
|
265
|
-
try {
|
|
266
|
-
const messages = parseOxlintOutput(result.stdout, cwd);
|
|
267
|
-
log.debug("oxlint lint", {
|
|
268
|
-
pattern,
|
|
269
|
-
messageCount: messages.length,
|
|
270
|
-
stderr: result.stderr || void 0
|
|
271
|
-
});
|
|
272
|
-
return formatLintMessages(messages, { label: "oxlint", source: "oxlint" }, warnLimit);
|
|
273
|
-
} catch (e) {
|
|
274
|
-
log.warn("oxlint failed", { pattern, error: e.message });
|
|
275
|
-
return {
|
|
276
|
-
text: `[oxlint] \u8FD0\u884C\u5931\u8D25\uFF1A${e.message}`,
|
|
277
|
-
diagnostics: [],
|
|
278
|
-
engines: ["oxlint"]
|
|
279
|
-
};
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
173
|
const BUNDLED_ENGINE_KEY = "<bundled>";
|
|
283
174
|
const _engineCache = /* @__PURE__ */ new Map();
|
|
284
175
|
const _tsVersionCache = /* @__PURE__ */ new Map();
|
|
@@ -569,47 +460,9 @@ async function runTypeChecksForFiles(files, cwd) {
|
|
|
569
460
|
}
|
|
570
461
|
return results;
|
|
571
462
|
}
|
|
572
|
-
async function runAllChecks(pattern, cwd) {
|
|
573
|
-
log.debug("runAllChecks", { pattern, cwd });
|
|
574
|
-
const [eslintOutput, tscOutput] = await Promise.all([
|
|
575
|
-
lintFiles(pattern, cwd),
|
|
576
|
-
runTypeCheck(pattern, cwd)
|
|
577
|
-
]);
|
|
578
|
-
return { eslintOutput, tscOutput };
|
|
579
|
-
}
|
|
580
|
-
async function runAllChecksForFiles(files, cwd) {
|
|
581
|
-
const targets = [...new Set(files.map((file) => import_node_path.default.resolve(file)))];
|
|
582
|
-
const tscByFile = await runTypeChecksForFiles(targets, cwd);
|
|
583
|
-
const results = /* @__PURE__ */ new Map();
|
|
584
|
-
for (const file of targets) {
|
|
585
|
-
results.set(file, {
|
|
586
|
-
eslintOutput: await lintFiles(file, cwd),
|
|
587
|
-
tscOutput: tscByFile.get(file) ?? { rawOutput: "", exitCode: 0 }
|
|
588
|
-
});
|
|
589
|
-
}
|
|
590
|
-
return results;
|
|
591
|
-
}
|
|
592
|
-
async function runProjectDiagnostics(workspace) {
|
|
593
|
-
const tscDirs = import_node_fs.default.existsSync(import_node_path.default.join(workspace, "tsconfig.json")) ? [workspace] : findAllTsconfigDirs(workspace);
|
|
594
|
-
log.debug("Tsc dirs to check", { count: tscDirs.length, dirs: tscDirs });
|
|
595
|
-
const [eslintOutput, ...tscOutputs] = await Promise.all([
|
|
596
|
-
lintFiles(".", workspace, 10),
|
|
597
|
-
...tscDirs.map((dir) => runTypeCheck(void 0, dir))
|
|
598
|
-
]);
|
|
599
|
-
const mergedTsc = {
|
|
600
|
-
rawOutput: tscOutputs.flatMap((o) => o.rawOutput).filter(Boolean).join("\n"),
|
|
601
|
-
exitCode: tscOutputs.reduce((max, o) => Math.max(max, o.exitCode), 0),
|
|
602
|
-
diagnostics: tscOutputs.flatMap((o) => o.diagnostics ?? []),
|
|
603
|
-
source: tscOutputs.find((o) => o.source)?.source
|
|
604
|
-
};
|
|
605
|
-
return { eslintOutput, tscOutput: mergedTsc };
|
|
606
|
-
}
|
|
607
463
|
function tscSectionTitle(tscOutput) {
|
|
608
464
|
return tscOutput.source ?? "tsc";
|
|
609
465
|
}
|
|
610
|
-
function lintSectionTitle(lintOutput) {
|
|
611
|
-
return lintOutput.engines?.length ? lintOutput.engines.join(" + ") : "ESLint";
|
|
612
|
-
}
|
|
613
466
|
function omittedFindingsHint(omitted) {
|
|
614
467
|
return `\u2026\u8FD8\u6709 ${omitted} \u6761\uFF0C\u5B8C\u6574\u7ED3\u679C\u8BF7\u8C03\u7528 run_diagnostics`;
|
|
615
468
|
}
|
|
@@ -621,39 +474,493 @@ function boundFindings(text, maxFindings) {
|
|
|
621
474
|
"\n"
|
|
622
475
|
);
|
|
623
476
|
}
|
|
624
|
-
function
|
|
625
|
-
const
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
477
|
+
function displayPath(cwd, file) {
|
|
478
|
+
const rel = import_node_path.default.relative(cwd, file);
|
|
479
|
+
return rel.startsWith("..") || import_node_path.default.isAbsolute(rel) ? file : rel;
|
|
480
|
+
}
|
|
481
|
+
function targetFiles(target) {
|
|
482
|
+
if (target.kind === "file") return [import_node_path.default.resolve(target.file)];
|
|
483
|
+
if (target.kind === "edited") return [...new Set(target.files.map((file) => import_node_path.default.resolve(file)))];
|
|
484
|
+
return [];
|
|
485
|
+
}
|
|
486
|
+
function splitsByFile(target) {
|
|
487
|
+
return target.kind === "edited";
|
|
488
|
+
}
|
|
489
|
+
function filterBySeverity(items, severity) {
|
|
490
|
+
return severity === "error" ? items.filter((item) => item.severity === import_constants.SEVERITY_ERROR) : [...items];
|
|
491
|
+
}
|
|
492
|
+
function renderDiagnosticLines(items) {
|
|
493
|
+
return items.map((item) => {
|
|
494
|
+
const level = item.severity === import_constants.SEVERITY_ERROR ? "ERROR" : "WARN";
|
|
495
|
+
const file = item.file ?? "";
|
|
496
|
+
const { line, character } = item.range.start;
|
|
497
|
+
return `${level} [${file}:${line + 1}:${character + 1}] ${item.message}`;
|
|
498
|
+
}).join("\n");
|
|
499
|
+
}
|
|
500
|
+
function entryToDiagnosticItem(entry, source) {
|
|
501
|
+
return {
|
|
502
|
+
file: entry.file,
|
|
503
|
+
severity: entry.severity === "error" ? import_constants.SEVERITY_ERROR : import_constants.SEVERITY_WARN,
|
|
504
|
+
range: {
|
|
505
|
+
start: { line: entry.line - 1, character: entry.column - 1 },
|
|
506
|
+
end: { line: entry.line - 1, character: entry.column - 1 }
|
|
507
|
+
},
|
|
508
|
+
message: entry.message,
|
|
509
|
+
source
|
|
510
|
+
};
|
|
511
|
+
}
|
|
512
|
+
function normalizeEntries(value, source) {
|
|
513
|
+
if (value === void 0) return [];
|
|
514
|
+
if (!Array.isArray(value)) throw new Error("diagnostics \u5FC5\u987B\u662F\u6570\u7EC4");
|
|
515
|
+
return value.map((raw, index) => {
|
|
516
|
+
const at = `diagnostics[${index}]`;
|
|
517
|
+
if (!raw || typeof raw !== "object") throw new Error(`${at} \u5FC5\u987B\u662F\u5BF9\u8C61`);
|
|
518
|
+
const entry = raw;
|
|
519
|
+
if (typeof entry.file !== "string" || !entry.file)
|
|
520
|
+
throw new Error(`${at}.file \u5FC5\u987B\u662F\u975E\u7A7A\u5B57\u7B26\u4E32`);
|
|
521
|
+
if (typeof entry.line !== "number" || entry.line < 1)
|
|
522
|
+
throw new Error(`${at}.line \u5FC5\u987B\u662F \u2265 1 \u7684\u6570\u5B57`);
|
|
523
|
+
if (typeof entry.column !== "number" || entry.column < 1)
|
|
524
|
+
throw new Error(`${at}.column \u5FC5\u987B\u662F \u2265 1 \u7684\u6570\u5B57`);
|
|
525
|
+
if (entry.severity !== "error" && entry.severity !== "warning")
|
|
526
|
+
throw new Error(`${at}.severity \u5FC5\u987B\u662F "error" \u6216 "warning"`);
|
|
527
|
+
if (typeof entry.message !== "string") throw new Error(`${at}.message \u5FC5\u987B\u662F\u5B57\u7B26\u4E32`);
|
|
528
|
+
return entryToDiagnosticItem(entry, source);
|
|
529
|
+
});
|
|
530
|
+
}
|
|
531
|
+
function extractJsonValue(raw) {
|
|
532
|
+
const trimmed = raw.trim();
|
|
533
|
+
try {
|
|
534
|
+
return JSON.parse(trimmed);
|
|
535
|
+
} catch {
|
|
536
|
+
const starts = [raw.indexOf("{"), raw.indexOf("[")].filter((index) => index >= 0);
|
|
537
|
+
if (starts.length === 0) throw new Error("\u672A\u627E\u5230 JSON \u8F93\u51FA");
|
|
538
|
+
const start = Math.min(...starts);
|
|
539
|
+
const close = raw[start] === "{" ? "}" : "]";
|
|
540
|
+
const end = raw.lastIndexOf(close);
|
|
541
|
+
if (end <= start) throw new Error("\u672A\u627E\u5230 JSON \u8F93\u51FA");
|
|
542
|
+
return JSON.parse(raw.slice(start, end + 1));
|
|
632
543
|
}
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
544
|
+
}
|
|
545
|
+
function parseEslintJson(raw, cwd) {
|
|
546
|
+
const parsed = extractJsonValue(raw);
|
|
547
|
+
if (!Array.isArray(parsed)) throw new Error("\u9876\u5C42\u5FC5\u987B\u662F\u6570\u7EC4");
|
|
548
|
+
const all = parsed.flatMap(
|
|
549
|
+
(file) => (file.messages ?? []).map((message) => ({
|
|
550
|
+
...message,
|
|
551
|
+
filePath: file.filePath ? import_node_path.default.resolve(cwd, file.filePath) : ""
|
|
552
|
+
}))
|
|
553
|
+
);
|
|
554
|
+
return {
|
|
555
|
+
messages: all.filter((message) => message.filePath && !isEslintIgnoreNotice(message)),
|
|
556
|
+
ignored: all.filter((message) => message.filePath && isEslintIgnoreNotice(message)).map((m) => m.filePath)
|
|
557
|
+
};
|
|
558
|
+
}
|
|
559
|
+
function isEslintIgnoreNotice(message) {
|
|
560
|
+
return (message.ruleId ?? null) === null && typeof message.message === "string" && message.message.startsWith("File ignored because");
|
|
561
|
+
}
|
|
562
|
+
function filterTscOutputBySeverity(rawOutput, minSeverity) {
|
|
563
|
+
if (minSeverity === "warning" || !rawOutput) return rawOutput;
|
|
564
|
+
const errorLinePat = /^(.+?)\((\d+),(\d+)\):\s+(error|warning)\s+TS\d+:/;
|
|
565
|
+
const kept = [];
|
|
566
|
+
let keep = false;
|
|
567
|
+
for (const line of rawOutput.split("\n")) {
|
|
568
|
+
const match = errorLinePat.exec(line);
|
|
569
|
+
if (match) keep = match[4] === "error";
|
|
570
|
+
else if (!/^\s/.test(line)) keep = false;
|
|
571
|
+
if (keep) kept.push(line);
|
|
637
572
|
}
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
573
|
+
return kept.join("\n");
|
|
574
|
+
}
|
|
575
|
+
function applyTscSeverity(result, minSeverity) {
|
|
576
|
+
if (minSeverity === "warning") return result;
|
|
577
|
+
return {
|
|
578
|
+
...result,
|
|
579
|
+
rawOutput: filterTscOutputBySeverity(result.rawOutput, minSeverity),
|
|
580
|
+
diagnostics: (result.diagnostics ?? []).filter((item) => item.severity === import_constants.SEVERITY_ERROR)
|
|
581
|
+
};
|
|
582
|
+
}
|
|
583
|
+
function emptySectionText(kind, phase, severity) {
|
|
584
|
+
if (phase !== "manual") return void 0;
|
|
585
|
+
if (kind === "typecheck") return "\u6CA1\u6709\u53D1\u73B0\u7C7B\u578B\u9519\u8BEF";
|
|
586
|
+
return severity === "error" ? "\u6CA1\u6709\u53D1\u73B0\u9519\u8BEF" : "\u6CA1\u6709\u53D1\u73B0\u95EE\u9898";
|
|
587
|
+
}
|
|
588
|
+
function buildSection(name, target, outcome, phase, severity, kind) {
|
|
589
|
+
const diagnostics = filterBySeverity(outcome.diagnostics ?? [], severity);
|
|
590
|
+
const explicit = (outcome.text ?? "").trim();
|
|
591
|
+
const body = explicit || (diagnostics.length > 0 ? renderDiagnosticLines(diagnostics) : emptySectionText(kind, phase, severity) ?? "");
|
|
592
|
+
const notes = phase === "manual" ? (outcome.notes ?? []).filter(Boolean) : [];
|
|
593
|
+
const text = [body, ...notes].filter(Boolean).join("\n\n");
|
|
594
|
+
return {
|
|
595
|
+
name,
|
|
596
|
+
...target ? { target } : {},
|
|
597
|
+
...text ? { text } : {},
|
|
598
|
+
diagnostics
|
|
599
|
+
};
|
|
600
|
+
}
|
|
601
|
+
function resolveCommandSource(check, cwd) {
|
|
602
|
+
if (check.command) {
|
|
603
|
+
return { command: check.command, prefix: [], label: check.command };
|
|
604
|
+
}
|
|
605
|
+
const binName = check.bin;
|
|
606
|
+
const bin = resolvePackageBin(import_node_path.default.join(cwd, "package.json"), binName, binName);
|
|
607
|
+
if (!bin) {
|
|
608
|
+
return {
|
|
609
|
+
command: "node",
|
|
610
|
+
prefix: [],
|
|
611
|
+
label: binName,
|
|
612
|
+
error: `\u9879\u76EE\u91CC\u89E3\u6790\u4E0D\u5230 ${binName}\uFF08\u8BF7\u5148\u5B89\u88C5\u8BE5\u4F9D\u8D56\uFF0C\u6216\u6539\u7528 command \u6307\u5B9A\u53EF\u6267\u884C\u6587\u4EF6\uFF09`
|
|
613
|
+
};
|
|
614
|
+
}
|
|
615
|
+
return { command: "node", prefix: [bin], label: binName };
|
|
616
|
+
}
|
|
617
|
+
async function runCommand(check, source, argv, cwd) {
|
|
618
|
+
const result = await (0, import_execa.execa)(source.command, [...source.prefix, ...argv], {
|
|
619
|
+
cwd,
|
|
620
|
+
timeout: check.timeoutMs ?? import_diagnostics.COMMAND_CHECK_TIMEOUT_MS,
|
|
621
|
+
maxBuffer: 10 * 1024 * 1024,
|
|
622
|
+
reject: false,
|
|
623
|
+
stdin: "ignore"
|
|
624
|
+
});
|
|
625
|
+
return {
|
|
626
|
+
stdout: String(result.stdout ?? ""),
|
|
627
|
+
stderr: String(result.stderr ?? ""),
|
|
628
|
+
exitCode: typeof result.exitCode === "number" ? result.exitCode : void 0,
|
|
629
|
+
failed: result.failed,
|
|
630
|
+
timedOut: Boolean(result.timedOut || result.isTerminated),
|
|
631
|
+
overflowed: Boolean(result.isMaxBuffer),
|
|
632
|
+
message: result.originalMessage
|
|
633
|
+
};
|
|
634
|
+
}
|
|
635
|
+
function commandFailureText(check, source, outcome) {
|
|
636
|
+
if (outcome.timedOut) {
|
|
637
|
+
return `[${check.name}] \u8FD0\u884C\u5931\u8D25\uFF1A\u68C0\u67E5\u8D85\u65F6\uFF08${check.timeoutMs ?? import_diagnostics.COMMAND_CHECK_TIMEOUT_MS}ms\uFF09\uFF0C\u53EF\u63D0\u9AD8 diagnostics.timeoutMs \u6216\u7F29\u5C0F\u68C0\u67E5\u8303\u56F4\u3002`;
|
|
638
|
+
}
|
|
639
|
+
if (outcome.overflowed) return `[${check.name}] \u8FD0\u884C\u5931\u8D25\uFF1A\u8F93\u51FA\u8D85\u8FC7\u4E0A\u9650\uFF0C\u8BF7\u7F29\u5C0F\u68C0\u67E5\u8303\u56F4\u3002`;
|
|
640
|
+
if (outcome.failed && outcome.exitCode === void 0) {
|
|
641
|
+
return `[${check.name}] \u8FD0\u884C\u5931\u8D25\uFF1A\u65E0\u6CD5\u6267\u884C ${source.label}\uFF08${outcome.message ?? "spawn failed"}\uFF09\u3002`;
|
|
642
|
+
}
|
|
643
|
+
return void 0;
|
|
644
|
+
}
|
|
645
|
+
function rawCommandOutput(outcome) {
|
|
646
|
+
return [outcome.stdout, outcome.stderr].map((text) => text.trim()).filter(Boolean).join("\n");
|
|
647
|
+
}
|
|
648
|
+
function expandArgs(args, files) {
|
|
649
|
+
return args.flatMap((arg) => {
|
|
650
|
+
if (arg === "{file}") return files[0] ? [files[0]] : [];
|
|
651
|
+
if (arg === "{files}") return [...files];
|
|
652
|
+
return [
|
|
653
|
+
arg.replace(
|
|
654
|
+
/\{file\}|\{files\}/g,
|
|
655
|
+
(match) => match === "{file}" ? files[0] ?? "" : files.join(" ")
|
|
656
|
+
)
|
|
657
|
+
];
|
|
658
|
+
});
|
|
659
|
+
}
|
|
660
|
+
async function runAdapterModule(check, outcome, cwd, files) {
|
|
661
|
+
const adapterPath = import_node_path.default.resolve(cwd, check.adapter);
|
|
662
|
+
const fail = (reason) => ({
|
|
663
|
+
text: `[${check.name}] \u9002\u914D\u5668\u4E0D\u53EF\u7528\uFF1A${check.adapter}\uFF08${reason}\uFF09`,
|
|
664
|
+
diagnostics: []
|
|
665
|
+
});
|
|
666
|
+
let moduleExports;
|
|
667
|
+
try {
|
|
668
|
+
moduleExports = await import((0, import_node_url.pathToFileURL)(adapterPath).href);
|
|
669
|
+
} catch (error) {
|
|
670
|
+
return fail(error instanceof Error ? error.message : String(error));
|
|
671
|
+
}
|
|
672
|
+
if (typeof moduleExports.default !== "function") {
|
|
673
|
+
return fail("\u6A21\u5757\u5FC5\u987B default export \u4E00\u4E2A\u9002\u914D\u5668\u51FD\u6570");
|
|
674
|
+
}
|
|
675
|
+
const input = {
|
|
676
|
+
name: check.name,
|
|
677
|
+
stdout: outcome.stdout,
|
|
678
|
+
stderr: outcome.stderr,
|
|
679
|
+
exitCode: outcome.exitCode,
|
|
680
|
+
files: [...files],
|
|
681
|
+
cwd
|
|
682
|
+
};
|
|
683
|
+
let output;
|
|
684
|
+
try {
|
|
685
|
+
output = await moduleExports.default(
|
|
686
|
+
input
|
|
687
|
+
);
|
|
688
|
+
} catch (error) {
|
|
689
|
+
return fail(error instanceof Error ? error.message : String(error));
|
|
690
|
+
}
|
|
691
|
+
if (!output || typeof output !== "object") return fail("\u9002\u914D\u5668\u5FC5\u987B\u8FD4\u56DE\u5BF9\u8C61");
|
|
692
|
+
try {
|
|
693
|
+
const diagnostics = normalizeEntries(output.diagnostics, check.name);
|
|
694
|
+
return { text: diagnostics.length > 0 ? void 0 : output.text, diagnostics };
|
|
695
|
+
} catch (error) {
|
|
696
|
+
return fail(error instanceof Error ? error.message : String(error));
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
function parseStylelintJson(raw, cwd) {
|
|
700
|
+
const parsed = extractJsonValue(raw);
|
|
701
|
+
if (!Array.isArray(parsed)) throw new Error("\u9876\u5C42\u5FC5\u987B\u662F\u6570\u7EC4");
|
|
702
|
+
return parsed.flatMap((file) => {
|
|
703
|
+
const filePath = file.source ? import_node_path.default.resolve(cwd, file.source) : "";
|
|
704
|
+
if (!filePath) return [];
|
|
705
|
+
return (file.warnings ?? []).map((warning) => ({
|
|
706
|
+
severity: warning.severity === "error" ? 2 : 1,
|
|
707
|
+
line: warning.line ?? 1,
|
|
708
|
+
column: warning.column ?? 1,
|
|
709
|
+
endLine: warning.endLine,
|
|
710
|
+
endColumn: warning.endColumn,
|
|
711
|
+
message: warning.text ?? "\u672A\u77E5\u8BCA\u65AD",
|
|
712
|
+
ruleId: warning.rule ?? null,
|
|
713
|
+
filePath
|
|
714
|
+
}));
|
|
715
|
+
});
|
|
716
|
+
}
|
|
717
|
+
function adaptBuiltinFormat(format, check, raw, cwd, severity) {
|
|
718
|
+
const lintFromMessages = (messages) => {
|
|
719
|
+
const output = formatLintMessages(
|
|
720
|
+
messages,
|
|
721
|
+
{ label: check.name, source: check.name },
|
|
722
|
+
10,
|
|
723
|
+
severity
|
|
724
|
+
);
|
|
725
|
+
return { text: output.text, diagnostics: output.diagnostics ?? [] };
|
|
726
|
+
};
|
|
727
|
+
switch (format) {
|
|
728
|
+
case "tsc":
|
|
729
|
+
return {
|
|
730
|
+
text: filterTscOutputBySeverity(raw, severity),
|
|
731
|
+
diagnostics: parseTscDiags(raw, void 0, cwd, check.name)
|
|
732
|
+
};
|
|
733
|
+
case "eslint-json": {
|
|
734
|
+
const { messages, ignored } = parseEslintJson(raw, cwd);
|
|
735
|
+
return {
|
|
736
|
+
...lintFromMessages(messages),
|
|
737
|
+
notes: ignored.map((file) => `${file} \u4E0D\u5728 ESLint \u914D\u7F6E\u8303\u56F4\u5185\uFF0C\u672A\u68C0\u67E5`)
|
|
738
|
+
};
|
|
739
|
+
}
|
|
740
|
+
case "oxlint-json":
|
|
741
|
+
return lintFromMessages(parseOxlintOutput(raw, cwd));
|
|
742
|
+
case "stylelint-json":
|
|
743
|
+
return lintFromMessages(parseStylelintJson(raw, cwd));
|
|
744
|
+
case "aipanel-json": {
|
|
745
|
+
const value = extractJsonValue(raw);
|
|
746
|
+
const payload = Array.isArray(value) ? { diagnostics: value } : value;
|
|
747
|
+
const diagnostics = normalizeEntries(payload?.diagnostics, check.name);
|
|
748
|
+
return { text: diagnostics.length > 0 ? void 0 : payload?.text, diagnostics };
|
|
749
|
+
}
|
|
750
|
+
case "text":
|
|
751
|
+
default:
|
|
752
|
+
return { text: raw, diagnostics: [] };
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
function expandLinterPreset(check) {
|
|
756
|
+
const spec = import_diagnostics.BUILTIN_LINTERS[check.builtin];
|
|
757
|
+
return {
|
|
758
|
+
name: spec.label,
|
|
759
|
+
bin: spec.bin ?? spec.package,
|
|
760
|
+
args: check.args ?? [...spec.args],
|
|
761
|
+
...check.projectArgs ?? spec.projectArgs ? { projectArgs: [...check.projectArgs ?? spec.projectArgs ?? []] } : {},
|
|
762
|
+
extensions: check.extensions ?? [...spec.extensions],
|
|
763
|
+
format: check.format ?? spec.format,
|
|
764
|
+
...check.run ? { run: check.run } : {},
|
|
765
|
+
...check.cwd ? { cwd: check.cwd } : {},
|
|
766
|
+
...check.timeoutMs ? { timeoutMs: check.timeoutMs } : {}
|
|
767
|
+
};
|
|
768
|
+
}
|
|
769
|
+
async function runCommandCheck(check, target, phase, severity, optional = false) {
|
|
770
|
+
const cwd = check.cwd ? import_node_path.default.resolve(target.cwd, check.cwd) : target.cwd;
|
|
771
|
+
const args = check.args ?? [];
|
|
772
|
+
const hasPlaceholder = args.some((arg) => arg.includes("{file}") || arg.includes("{files}"));
|
|
773
|
+
const perFile = args.some((arg) => arg.includes("{file}"));
|
|
774
|
+
const split = splitsByFile(target);
|
|
775
|
+
const targeted = target.kind !== "project";
|
|
776
|
+
const files = targeted ? targetFiles(target).filter((file) => (0, import_diagnostics.matchesExtensions)(file, check.extensions)) : [];
|
|
777
|
+
if (targeted && hasPlaceholder && files.length === 0) {
|
|
778
|
+
log.debug("skip command check: no matching file target", { name: check.name, phase });
|
|
779
|
+
return [];
|
|
780
|
+
}
|
|
781
|
+
const source = resolveCommandSource(check, cwd);
|
|
782
|
+
if (source.error) {
|
|
783
|
+
if (optional && phase === "edit") {
|
|
784
|
+
log.debug("skip uninstalled builtin linter", { name: check.name });
|
|
785
|
+
return [];
|
|
786
|
+
}
|
|
787
|
+
const text = optional ? `[${check.name}] \u672A\u8FD0\u884C\uFF1A${source.error}` : `[${check.name}] ${source.error}`;
|
|
788
|
+
return [buildSection(check.name, void 0, { text }, phase, severity, "command")];
|
|
789
|
+
}
|
|
790
|
+
const execute = async (argvTemplate, fileSet) => {
|
|
791
|
+
const outcome = await runCommand(check, source, expandArgs(argvTemplate, fileSet), cwd);
|
|
792
|
+
const failure = commandFailureText(check, source, outcome);
|
|
793
|
+
if (failure) return { text: failure, diagnostics: [] };
|
|
794
|
+
const raw = rawCommandOutput(outcome);
|
|
795
|
+
try {
|
|
796
|
+
if (check.adapter) return await runAdapterModule(check, outcome, cwd, fileSet);
|
|
797
|
+
return adaptBuiltinFormat(check.format ?? "text", check, raw, cwd, severity);
|
|
798
|
+
} catch (error) {
|
|
799
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
800
|
+
const excerpt = raw.replace(/\s+/g, " ").trim().slice(0, 200);
|
|
801
|
+
return {
|
|
802
|
+
text: `[${check.name}] \u8FD0\u884C\u5931\u8D25\uFF1A\u8F93\u51FA\u89E3\u6790\u5931\u8D25\uFF08${reason}\uFF09${excerpt ? `\uFF1A${excerpt}` : ""}`,
|
|
803
|
+
diagnostics: []
|
|
804
|
+
};
|
|
805
|
+
}
|
|
806
|
+
};
|
|
807
|
+
const targetLabel = (file) => split ? displayPath(target.cwd, file) : void 0;
|
|
808
|
+
if (!hasPlaceholder) {
|
|
809
|
+
return [
|
|
810
|
+
buildSection(check.name, void 0, await execute(args, []), phase, severity, "command")
|
|
811
|
+
];
|
|
812
|
+
}
|
|
813
|
+
if (target.kind === "project") {
|
|
814
|
+
if (!check.projectArgs) {
|
|
815
|
+
log.debug("skip command check: no project args", { name: check.name });
|
|
816
|
+
return [];
|
|
817
|
+
}
|
|
818
|
+
return [
|
|
819
|
+
buildSection(
|
|
820
|
+
check.name,
|
|
821
|
+
void 0,
|
|
822
|
+
await execute(check.projectArgs, []),
|
|
823
|
+
phase,
|
|
824
|
+
severity,
|
|
825
|
+
"command"
|
|
826
|
+
)
|
|
827
|
+
];
|
|
828
|
+
}
|
|
829
|
+
if (perFile) {
|
|
830
|
+
const sections = [];
|
|
831
|
+
for (const file of files) {
|
|
832
|
+
const produced2 = await execute(args, [file]);
|
|
833
|
+
sections.push(
|
|
834
|
+
buildSection(check.name, targetLabel(file), produced2, phase, severity, "command")
|
|
835
|
+
);
|
|
836
|
+
}
|
|
837
|
+
return sections;
|
|
838
|
+
}
|
|
839
|
+
const produced = await execute(args, files);
|
|
840
|
+
const label = files.length === 1 ? targetLabel(files[0]) : void 0;
|
|
841
|
+
return [buildSection(check.name, label, produced, phase, severity, "command")];
|
|
842
|
+
}
|
|
843
|
+
async function runProjectTypeCheck(workspace, severity) {
|
|
844
|
+
const dirs = import_node_fs.default.existsSync(import_node_path.default.join(workspace, "tsconfig.json")) ? [workspace] : findAllTsconfigDirs(workspace);
|
|
845
|
+
log.debug("Tsc dirs to check", { count: dirs.length, dirs });
|
|
846
|
+
const results = await Promise.all(dirs.map((dir) => runTypeCheck(void 0, dir)));
|
|
847
|
+
return applyTscSeverity(
|
|
848
|
+
{
|
|
849
|
+
rawOutput: results.flatMap((output) => output.rawOutput).filter(Boolean).join("\n"),
|
|
850
|
+
exitCode: results.reduce((max, output) => Math.max(max, output.exitCode), 0),
|
|
851
|
+
diagnostics: results.flatMap((output) => output.diagnostics ?? []),
|
|
852
|
+
source: results.find((output) => output.source)?.source
|
|
853
|
+
},
|
|
854
|
+
severity
|
|
855
|
+
);
|
|
856
|
+
}
|
|
857
|
+
async function runTypecheckCheck(check, target, phase, severity) {
|
|
858
|
+
const cwd = target.cwd;
|
|
859
|
+
const split = splitsByFile(target);
|
|
860
|
+
const files = target.kind === "project" ? [] : targetFiles(target).filter(
|
|
861
|
+
(file) => (0, import_diagnostics.matchesExtensions)(file, check.extensions ?? import_diagnostics.SOURCE_EXTENSIONS)
|
|
862
|
+
);
|
|
863
|
+
if (target.kind !== "project" && files.length === 0) {
|
|
864
|
+
log.debug("skip builtin check: no matching file target", { builtin: check.builtin, phase });
|
|
865
|
+
return [];
|
|
866
|
+
}
|
|
867
|
+
if (split) {
|
|
868
|
+
const results = await runTypeChecksForFiles(files, cwd);
|
|
869
|
+
return files.map((file) => {
|
|
870
|
+
const result2 = applyTscSeverity(
|
|
871
|
+
results.get(import_node_path.default.resolve(file)) ?? { rawOutput: "", exitCode: 0 },
|
|
872
|
+
severity
|
|
873
|
+
);
|
|
874
|
+
return buildSection(
|
|
875
|
+
tscSectionTitle(result2),
|
|
876
|
+
displayPath(cwd, file),
|
|
877
|
+
{ text: result2.rawOutput.trim(), diagnostics: result2.diagnostics },
|
|
878
|
+
phase,
|
|
879
|
+
severity,
|
|
880
|
+
"typecheck"
|
|
881
|
+
);
|
|
882
|
+
});
|
|
883
|
+
}
|
|
884
|
+
const result = target.kind === "file" ? applyTscSeverity(await runTypeCheck(files[0], cwd), severity) : await runProjectTypeCheck(cwd, severity);
|
|
885
|
+
return [
|
|
886
|
+
buildSection(
|
|
887
|
+
tscSectionTitle(result),
|
|
888
|
+
void 0,
|
|
889
|
+
{ text: result.rawOutput.trim(), diagnostics: result.diagnostics },
|
|
890
|
+
phase,
|
|
891
|
+
severity,
|
|
892
|
+
"typecheck"
|
|
893
|
+
)
|
|
894
|
+
];
|
|
895
|
+
}
|
|
896
|
+
async function runDiagnostics(target, policy, phase) {
|
|
897
|
+
const checks = policy.checks.filter((check) => (0, import_diagnostics.checkRunsInPhase)(check, phase));
|
|
898
|
+
log.debug("runDiagnostics", { phase, target: target.kind, checks: checks.length });
|
|
899
|
+
const results = await Promise.all(
|
|
900
|
+
checks.map((check) => {
|
|
901
|
+
if ((0, import_diagnostics.isTypecheckCheck)(check)) {
|
|
902
|
+
return runTypecheckCheck(check, target, phase, policy.severity);
|
|
903
|
+
}
|
|
904
|
+
if ((0, import_diagnostics.isLinterPreset)(check)) {
|
|
905
|
+
return runCommandCheck(expandLinterPreset(check), target, phase, policy.severity, true);
|
|
906
|
+
}
|
|
907
|
+
if ((0, import_diagnostics.isCommandCheck)(check)) {
|
|
908
|
+
return runCommandCheck(check, target, phase, policy.severity);
|
|
909
|
+
}
|
|
910
|
+
return Promise.resolve([]);
|
|
911
|
+
})
|
|
912
|
+
);
|
|
913
|
+
const sections = results.flat().filter((section) => section.text !== void 0 || section.diagnostics.length > 0);
|
|
914
|
+
if (sections.length === 0 && phase === "manual") {
|
|
915
|
+
return {
|
|
916
|
+
sections: [
|
|
917
|
+
{
|
|
918
|
+
name: "\u68C0\u67E5",
|
|
919
|
+
text: "\u6CA1\u6709\u53EF\u8FD0\u884C\u7684\u68C0\u67E5\uFF1A\u5DF2\u914D\u7F6E\u7684\u68C0\u67E5\u90FD\u4E0D\u5339\u914D\u672C\u8F6E\u76EE\u6807\uFF08\u68C0\u67E5\u7684 extensions / run \u9636\u6BB5 / \u76EE\u6807\u7C7B\u578B\uFF09\u3002",
|
|
920
|
+
diagnostics: []
|
|
921
|
+
}
|
|
922
|
+
]
|
|
923
|
+
};
|
|
924
|
+
}
|
|
925
|
+
return { sections };
|
|
926
|
+
}
|
|
927
|
+
function collectDiagnostics(result) {
|
|
928
|
+
return result.sections.flatMap((section) => section.diagnostics);
|
|
929
|
+
}
|
|
930
|
+
function renderDiagnostics(result, options = {}) {
|
|
931
|
+
const sections = options.onlyFindings ? result.sections.filter((section) => (section.text ?? "").trim()) : result.sections;
|
|
932
|
+
if (sections.length === 0) return "";
|
|
933
|
+
const groups = /* @__PURE__ */ new Map();
|
|
934
|
+
for (const section of sections) {
|
|
935
|
+
const key = section.target ?? "";
|
|
936
|
+
const group = groups.get(key);
|
|
937
|
+
if (group) group.push(section);
|
|
938
|
+
else groups.set(key, [section]);
|
|
939
|
+
}
|
|
940
|
+
const blocks = [];
|
|
941
|
+
for (const [key, group] of groups) {
|
|
942
|
+
const body = group.map((section) => {
|
|
943
|
+
const text = boundFindings(section.text, options.maxFindingsPerSection);
|
|
944
|
+
return `## ${section.name}
|
|
641
945
|
|
|
642
|
-
${
|
|
946
|
+
${text || "\u6CA1\u6709\u53D1\u73B0\u95EE\u9898"}`;
|
|
947
|
+
}).join("\n\n");
|
|
948
|
+
blocks.push(key ? `### ${key}
|
|
949
|
+
|
|
950
|
+
${body}` : body);
|
|
951
|
+
}
|
|
952
|
+
return blocks.join("\n\n");
|
|
643
953
|
}
|
|
644
954
|
// Annotate the CommonJS export names for ESM import in node:
|
|
645
955
|
0 && (module.exports = {
|
|
646
956
|
DIAGNOSTICS_TOOL_DESCRIPTION,
|
|
957
|
+
collectDiagnostics,
|
|
647
958
|
findAllTsconfigDirs,
|
|
648
959
|
findTsconfigDir,
|
|
649
|
-
formatDiagnosticsSections,
|
|
650
960
|
isJsFile,
|
|
651
|
-
lintFiles,
|
|
652
|
-
lintSectionTitle,
|
|
653
961
|
omittedFindingsHint,
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
runProjectDiagnostics,
|
|
962
|
+
renderDiagnostics,
|
|
963
|
+
runDiagnostics,
|
|
657
964
|
runTypeCheck,
|
|
658
965
|
runTypeChecksForFiles,
|
|
659
966
|
tscSectionTitle
|