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