117 lines
3.1 KiB
TypeScript
117 lines
3.1 KiB
TypeScript
|
|
import * as fs from "fs";
|
||
|
|
import * as path from "path";
|
||
|
|
|
||
|
|
// --- CONFIGURATION ---
|
||
|
|
const ROOT_DIR: string = process.cwd();
|
||
|
|
const ROUTERS_DIR: string = path.join(ROOT_DIR, "src/server/api/routers");
|
||
|
|
const SCAN_DIRS: string[] = [path.join(ROOT_DIR, "src")];
|
||
|
|
const IGNORE_DIRS: string[] = ["node_modules", ".next", "dist"];
|
||
|
|
|
||
|
|
// Regex to find tRPC procedure definitions
|
||
|
|
const PROCEDURE_REGEX: RegExp =
|
||
|
|
/([a-zA-Z0-9_]+)\s*:\s*[a-zA-Z0-9_.]+\.(?:query|mutation|subscription)\s*\(/g;
|
||
|
|
|
||
|
|
interface ProcedureDefinition {
|
||
|
|
name: string;
|
||
|
|
file: string;
|
||
|
|
line: number;
|
||
|
|
}
|
||
|
|
|
||
|
|
function getAllFiles(dir: string, ext: string[] = [".ts", ".tsx"]): string[] {
|
||
|
|
let results: string[] = [];
|
||
|
|
if (!fs.existsSync(dir)) return results;
|
||
|
|
|
||
|
|
const list: string[] = fs.readdirSync(dir);
|
||
|
|
list.forEach((file: string) => {
|
||
|
|
const fullPath: string = path.join(dir, file);
|
||
|
|
const stat = fs.statSync(fullPath);
|
||
|
|
|
||
|
|
if (stat?.isDirectory()) {
|
||
|
|
if (!IGNORE_DIRS.includes(file)) {
|
||
|
|
results = results.concat(getAllFiles(fullPath, ext));
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
if (ext.some((e) => file.endsWith(e))) {
|
||
|
|
results.push(fullPath);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
});
|
||
|
|
return results;
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Calculates the line number of a specific index in a string
|
||
|
|
*/
|
||
|
|
function getLineNumber(content: string, index: number): number {
|
||
|
|
const linesBefore = content.substring(0, index).split("\n");
|
||
|
|
return linesBefore.length;
|
||
|
|
}
|
||
|
|
|
||
|
|
function run(): void {
|
||
|
|
console.log("🔍 Scanning for tRPC procedures...\n");
|
||
|
|
|
||
|
|
const routerFiles: string[] = getAllFiles(ROUTERS_DIR);
|
||
|
|
const procedures: ProcedureDefinition[] = [];
|
||
|
|
|
||
|
|
// 1. Extract procedure names and their specific line numbers
|
||
|
|
routerFiles.forEach((filePath: string) => {
|
||
|
|
const content: string = fs.readFileSync(filePath, "utf8");
|
||
|
|
let match: RegExpExecArray | null;
|
||
|
|
match = PROCEDURE_REGEX.exec(content);
|
||
|
|
while (match !== null) {
|
||
|
|
if (match[1]) {
|
||
|
|
procedures.push({
|
||
|
|
name: match[1],
|
||
|
|
file: filePath,
|
||
|
|
line: getLineNumber(content, match.index),
|
||
|
|
});
|
||
|
|
}
|
||
|
|
match = PROCEDURE_REGEX.exec(content);
|
||
|
|
}
|
||
|
|
});
|
||
|
|
|
||
|
|
console.log(`Found ${procedures.length} procedures. Checking for usage...\n`);
|
||
|
|
|
||
|
|
// 2. Get all files to scan
|
||
|
|
let scanFiles: string[] = [];
|
||
|
|
SCAN_DIRS.forEach((dir: string) => {
|
||
|
|
scanFiles = scanFiles.concat(getAllFiles(dir));
|
||
|
|
});
|
||
|
|
|
||
|
|
// 3. Check for usage
|
||
|
|
const usedNames = new Set<string>();
|
||
|
|
|
||
|
|
scanFiles.forEach((filePath: string) => {
|
||
|
|
const content: string = fs.readFileSync(filePath, "utf8");
|
||
|
|
|
||
|
|
procedures.forEach((proc) => {
|
||
|
|
if (filePath === proc.file) return;
|
||
|
|
|
||
|
|
// Search for name with word boundaries
|
||
|
|
const usageRegex = new RegExp(`\\b${proc.name}\\b`);
|
||
|
|
if (usageRegex.test(content)) {
|
||
|
|
usedNames.add(proc.name);
|
||
|
|
}
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
// 4. Report results
|
||
|
|
const unused = procedures.filter((p) => !usedNames.has(p.name));
|
||
|
|
|
||
|
|
if (unused.length === 0) {
|
||
|
|
console.log("✅ All good! No unused tRPC procedures found.");
|
||
|
|
} else {
|
||
|
|
console.log(
|
||
|
|
`🚨 Found ${unused.length} potentially unused tRPC procedures:\n`,
|
||
|
|
);
|
||
|
|
unused.forEach((p) => {
|
||
|
|
const relativePath: string = path.relative(ROOT_DIR, p.file);
|
||
|
|
// Format: path/to/file.ts:line
|
||
|
|
// Most IDEs (VS Code, WebStorm) make this clickable in the terminal
|
||
|
|
console.log(`- ${p.name} -> ${relativePath}:${p.line}`);
|
||
|
|
});
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
run();
|