fileBindings

tcmts.cli.main.fileBindings · #crntwoba · term · runs on the server

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

What each name a scratch file binds is: a definition of the codebase, by path (one it declares, at <namespace>.<name> or as its // tcm: path line says, or one it imports from #codebase/…), or an export of a module outside it (node:path#join). Two files binding a name alike mean the same by it.

import { declarations, joinName } from "#codebase/tcmts/cli/mirror";
import { parse } from "#codebase/tcmts/core/ts";
import {
  isImportDeclaration as ts_isImportDeclaration, isNamespaceImport as ts_isNamespaceImport,
  isStringLiteral as ts_isStringLiteral,
} from "typescript/ast";

export function fileBindings(file: string, text: string, ns: string): Map<string, string> {
  const out = new Map<string, string>();
  for (const st of parse(file, text).statements) {
    if (!ts_isImportDeclaration(st) || !st.importClause || !ts_isStringLiteral(st.moduleSpecifier))
      continue;
    const spec = st.moduleSpecifier.text;
    const own = /^#codebase(?:\/(.+))?$/.exec(spec);
    const of = (name: string) =>
      own ? `path:${joinName((own[1] ?? "").split("/").join("."), name)}` : `ext:${spec}#${name}`;
    const c = st.importClause;
    if (c.name) out.set(c.name.text, of("default"));
    const b = c.namedBindings;
    if (b && ts_isNamespaceImport(b)) out.set(b.name.text, of("*"));
    else if (b)
      for (const e of b.elements) out.set(e.name.text, of((e.propertyName ?? e.name).text));
  }
  for (const [name, d] of declarations(file, text)) {
    const above = /\/\/ tcm: path (\S+)\s*$/.exec(
      text.slice(0, d.start).split("\n").slice(-2).join("\n"),
    );
    out.set(name, `path:${above?.[1] ?? joinName(ns, name)}`);
  }
  return out;
}

uses

used by