valueImports

tcmts.cli.mirror.valueImports · #h6cb2wbx · term · runs on the server

uses tcmts.core.ts.parse, which uses tcmts.core.ts.compiler, which uses process

text with its type-only imports of names made value imports, for code that now uses them as values: import type { Placement } becomes import { Placement }, and one of several names moves to an import of its own.

import { applyEdits } from "#codebase/tcmts/cli/mirror";
import type { Edit } from "#codebase/tcmts/cli/mirror";
import { isTypeOnlyImport, parse } from "#codebase/tcmts/core/ts";
import { isImportDeclaration as ts_isImportDeclaration, isNamedImports as ts_isNamedImports } from "typescript/ast";

export function valueImports(file: string, text: string, names: Set<string>): string {
  if (!names.size) return text;
  const sf = parse(file, text);
  const edits: Edit[] = [];
  for (const st of sf.statements) {
    if (!ts_isImportDeclaration(st) || !st.importClause) continue;
    const clause = st.importClause;
    const named = clause.namedBindings && ts_isNamedImports(clause.namedBindings) ? [...clause.namedBindings.elements] : [];
    if (isTypeOnlyImport(clause)) {
      const moving = named.filter((e) => names.has(e.name.text));
      const whole = (clause.name && names.has(clause.name.text)) || (moving.length && moving.length === named.length);
      if (whole) {
        // `import type` → `import`
        const at = text.indexOf("type", st.getStart(sf) + "import".length);
        edits.push({ start: at, end: at + "type".length + (/\s/.exec(text[at + 4] ?? "") ? 1 : 0), text: "" });
      } else if (moving.length) {
        const from = text.slice(st.moduleSpecifier.getStart(sf), st.moduleSpecifier.end);
        const list = (es: typeof named) => es.map((e) => text.slice(e.getStart(sf), e.end)).join(", ");
        edits.push({ start: st.getStart(sf), end: st.end, text: `import type { ${list(named.filter((e) => !names.has(e.name.text)))} } from ${from};\nimport { ${list(moving)} } from ${from};` });
      }
      continue;
    }
    // `import { type Placement }` → `import { Placement }`
    for (const e of named) {
      if (!e.isTypeOnly || !names.has(e.name.text)) continue;
      const at = e.getStart(sf);
      edits.push({ start: at, end: text.indexOf("type", at) + "type".length + 1, text: "" });
    }
  }
  return applyEdits(text, edits);
}

uses

used by