fix: use import type for GraphConfig, remove verbatim-module-syntax exclusion

The verbatim-module-syntax lint rule was correctly flagging that
GraphConfig is only used in a type position (typeof GraphConfig). Since
typeof resolves purely at the type level, import type works fine here
and is the correct form. No lint exclusion needed.

Also: deno fmt across all files (markdown line wrapping).
This commit is contained in:
2026-05-28 13:38:42 +00:00
parent b0298663dc
commit bb544469fd
34 changed files with 1279 additions and 617 deletions

View File

@@ -1,4 +1,3 @@
import { parse } from "@std/flags";
import * as path from "@std/path";
@@ -37,31 +36,31 @@ interface Stats {
function filterDiagnostics(
diagnostics: LintDiagnostic[],
options: FilterOptions
options: FilterOptions,
): LintDiagnostic[] {
let result = diagnostics;
if (options.codes) {
const codes = new Set(options.codes);
result = result.filter(d => codes.has(d.code));
result = result.filter((d) => codes.has(d.code));
}
if (options.files) {
const filePatterns = options.files.map(f => new RegExp(f));
result = result.filter(d =>
filePatterns.some(pattern => pattern.test(d.filename))
const filePatterns = options.files.map((f) => new RegExp(f));
result = result.filter((d) =>
filePatterns.some((pattern) => pattern.test(d.filename))
);
}
return result;
}
function groupDiagnostics(
diagnostics: LintDiagnostic[],
groupBy: "code" | "file"
groupBy: "code" | "file",
): Record<string, LintDiagnostic[]> {
const groups: Record<string, LintDiagnostic[]> = {};
for (const diag of diagnostics) {
const key = groupBy === "code" ? diag.code : diag.filename;
if (!groups[key]) {
@@ -69,24 +68,24 @@ function groupDiagnostics(
}
groups[key].push(diag);
}
return groups;
}
function calculateStats(diagnostics: LintDiagnostic[]): Stats {
const byCode: Record<string, number> = {};
const byFile: Record<string, number> = {};
for (const diag of diagnostics) {
byCode[diag.code] = (byCode[diag.code] || 0) + 1;
byFile[diag.filename] = (byFile[diag.filename] || 0) + 1;
}
return {
total: diagnostics.length,
byCode,
byFile,
filesWithIssues: Object.keys(byFile).length
filesWithIssues: Object.keys(byFile).length,
};
}
@@ -94,14 +93,14 @@ function printStats(stats: Stats, topN: number = 10) {
console.log("\n=== LINT ISSUE STATISTICS ===");
console.log(`Total issues: ${stats.total}`);
console.log(`Files with issues: ${stats.filesWithIssues}`);
console.log(`\nTop ${topN} issue types:`);
const sortedByCode = Object.entries(stats.byCode).sort((a, b) => b[1] - a[1]);
for (let i = 0; i < Math.min(topN, sortedByCode.length); i++) {
const [code, count] = sortedByCode[i];
console.log(` ${code}: ${count}`);
}
console.log(`\nTop ${topN} files with most issues:`);
const sortedByFile = Object.entries(stats.byFile).sort((a, b) => b[1] - a[1]);
for (let i = 0; i < Math.min(topN, sortedByFile.length); i++) {
@@ -113,29 +112,33 @@ function printStats(stats: Stats, topN: number = 10) {
function printGroupedDiagnostics(
groups: Record<string, LintDiagnostic[]>,
groupBy: "code" | "file",
limit?: number
limit?: number,
) {
const sortedEntries = Object.entries(groups).sort(
(a, b) => b[1].length - a[1].length
(a, b) => b[1].length - a[1].length,
);
const entriesToShow = limit ? sortedEntries.slice(0, limit) : sortedEntries;
for (const [key, diagnostics] of entriesToShow) {
console.log(`\n${groupBy.toUpperCase()}: ${key} (${diagnostics.length} issues)`);
console.log(
`\n${groupBy.toUpperCase()}: ${key} (${diagnostics.length} issues)`,
);
// Show first 5 issues for each group to avoid overwhelming output
const issuesToShow = Math.min(5, diagnostics.length);
for (let i = 0; i < issuesToShow; i++) {
const diag = diagnostics[i];
console.log(
` ${path.basename(diag.filename)}:${diag.range.start.line + 1}:${diag.range.start.col + 1} - ${diag.message}`
` ${path.basename(diag.filename)}:${diag.range.start.line + 1}:${
diag.range.start.col + 1
} - ${diag.message}`,
);
}
if (diagnostics.length > issuesToShow) {
console.log(` ... and ${diagnostics.length - issuesToShow} more issues`);
}
}
if (limit && sortedEntries.length > limit) {
console.log(`\n... and ${sortedEntries.length - limit} more groups`);
}
@@ -147,14 +150,14 @@ async function runDenoLint(): Promise<LintResult> {
stdout: "piped",
stderr: "piped",
});
const { code, stdout, stderr } = await command.output();
if (code !== 0 && code !== 1) { // Deno lint returns 1 when there are lint issues
const errorOutput = new TextDecoder().decode(stderr);
throw new Error(`Lint command failed:\n${errorOutput}`);
}
const output = new TextDecoder().decode(stdout);
return JSON.parse(output);
}
@@ -167,13 +170,13 @@ async function main() {
g: "group",
h: "help",
s: "stats",
l: "limit"
l: "limit",
},
string: ["file", "code", "group"],
boolean: ["help", "stats"],
default: { limit: 0 } // 0 means no limit
default: { limit: 0 }, // 0 means no limit
});
if (args.help) {
console.log(`
Usage: deno run analyze_lint.ts [options] [lint-output.json]
@@ -195,9 +198,9 @@ Examples:
`);
return;
}
let lintResult: LintResult;
// Read from file or run lint command
if (args._.length > 0) {
const filePath = String(args._[0]);
@@ -206,44 +209,51 @@ Examples:
} else {
lintResult = await runDenoLint();
}
// Apply filters
const filterOptions: FilterOptions = {};
if (args.code) {
filterOptions.codes = args.code.split(",").map(c => c.trim());
filterOptions.codes = args.code.split(",").map((c) => c.trim());
}
if (args.file) {
// Handle multiple file patterns
filterOptions.files = Array.isArray(args.file)
? args.file
: [args.file];
filterOptions.files = Array.isArray(args.file) ? args.file : [args.file];
}
const filteredDiagnostics = filterDiagnostics(
lintResult.diagnostics,
filterOptions
filterOptions,
);
// Show statistics if requested
if (args.stats) {
const stats = calculateStats(filteredDiagnostics);
printStats(stats);
}
// Group or show all diagnostics
if (args.group) {
const groups = groupDiagnostics(filteredDiagnostics, args.group as "code" | "file");
printGroupedDiagnostics(groups, args.group as "code" | "file", (args.limit as number) || undefined);
const groups = groupDiagnostics(
filteredDiagnostics,
args.group as "code" | "file",
);
printGroupedDiagnostics(
groups,
args.group as "code" | "file",
(args.limit as number) || undefined,
);
} else if (!args.stats) {
// Only show JSON output if neither stats nor grouping is requested
console.log(JSON.stringify({ diagnostics: filteredDiagnostics }, null, 2));
}
if (!args.stats) {
console.log(`\nFound ${filteredDiagnostics.length} issues matching criteria`);
console.log(
`\nFound ${filteredDiagnostics.length} issues matching criteria`,
);
}
}
if (import.meta.main) {
main().catch(console.error);
}
}