pruneImports

tcmts.cli.mirror.pruneImports · #dmbuky6v · term · runs on the server

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);
}

uses

used by