blankImports
uses tcmts.core.ts.parse, which uses tcmts.core.ts.compiler, which uses process
A module's text without the imports of these names, blanked (spaces where they were) so nothing else moves.
import { parse } from "#codebase/tcmts/core/ts"; import { isImportDeclaration as ts_isImportDeclaration, isNamespaceImport as ts_isNamespaceImport, } from "typescript/ast"; export function blankImports(file: string, text: string, names: Set<string>): string { const sf = parse(file, text); const blanks: [number, number][] = []; for (const st of sf.statements) { if (!ts_isImportDeclaration(st) || !st.importClause) continue; const c = st.importClause; const b = c.namedBindings; const elements = b && !ts_isNamespaceImport(b) ? [...b.elements] : []; const locals = [ ...(c.name ? [c.name.text] : []), ...(b && ts_isNamespaceImport(b) ? [b.name.text] : []), ...elements.map((e) => e.name.text), ]; if (!locals.some((l) => names.has(l))) continue; if (locals.every((l) => names.has(l)) || (b && ts_isNamespaceImport(b))) { blanks.push([st.getStart(sf), st.end]); continue; } // the default, with its comma if (c.name && names.has(c.name.text)) blanks.push([c.name.getStart(sf), text.indexOf(",", c.name.end) + 1]); const kept = elements.filter((e) => !names.has(e.name.text)); const lastKept = kept.at(-1); // none of the braces' kept (only the default): the braces go, with the comma before them if (!lastKept && b && c.name) blanks.push([c.name.end, b.end]); else elements.forEach((e, i) => { if (!names.has(e.name.text)) return; // after the last one kept: from it, so its comma goes with them; before: with the comma that follows it const next = elements[i + 1]; if (lastKept && e.pos > lastKept.pos) blanks.push([lastKept.end, e.end]); else if (next) blanks.push([e.getStart(sf), next.getStart(sf)]); }); } let out = text; for (const [from, to] of blanks.sort((x, y) => y[0] - x[0])) out = out.slice(0, from) + out.slice(from, to).replace(/[^\n]/g, " ") + out.slice(to); return out; }