pruneImports
uses tcmts.core.ts.parse, which uses tcmts.core.ts.compiler, which uses process
Drop the imports an edit stopped using: names after binds and doesn't mention that before did. Imports that
were already unused are left as they are (the file's author's business); an import with nothing left goes.
import { applyEdits, unusedImports } from "#codebase/tcmts/cli/mirror"; import type { Edit } from "#codebase/tcmts/cli/mirror"; import { parse } from "#codebase/tcmts/core/ts"; import { isImportDeclaration as ts_isImportDeclaration, isNamespaceImport as ts_isNamespaceImport, } from "typescript/ast"; export function pruneImports(file: string, before: string, after: string): string { const was = unusedImports(file, before); const gone = [...unusedImports(file, after)].filter((n) => !was.has(n)); if (!gone.length) return after; const sf = parse(file, after); const edits: Edit[] = []; for (const st of sf.statements) { const clause = ts_isImportDeclaration(st) ? st.importClause : undefined; if (!clause) continue; const bindings = clause.namedBindings; const names = [...(clause.name ? [clause.name.text] : []), ...(bindings && ts_isNamespaceImport(bindings) ? [bindings.name.text] : bindings ? bindings.elements.map((e) => e.name.text) : [])]; if (!names.some((n) => gone.includes(n))) continue; if (names.every((n) => gone.includes(n))) { edits.push({ start: st.getStart(sf), end: after[st.end] === "\n" ? st.end + 1 : st.end, text: "" }); continue; } // some of its names stay: take out the others if (bindings && !ts_isNamespaceImport(bindings)) { const keep = bindings.elements.filter((e) => !gone.includes(e.name.text)); if (keep.length < bindings.elements.length) edits.push({ start: bindings.getStart(sf), end: bindings.end, text: `{ ${keep.map((e) => e.getText(sf)).join(", ")} }` }); } } return applyEdits(after, edits); }