importRefs

tcmts.cli.pages.importRefs · #tqoxfucl · term · runs on the server

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

The links in a page's imports: each name brought in from the codebase to its definition, #codebase/a/b to the namespace's page, and a package to its page (at the range the definition uses). Node's builtins aren't pages.

import type { Codebase } from "#codebase/tcmts/cli/codebase";
import type { HighlightRef } from "#codebase/tcmts/cli/pages";
import { isTypeOnlyImport, parse } from "#codebase/tcmts/core/ts";
import type { DefRecord } from "#codebase/tcmts/core/types";
import {
  isImportDeclaration as ts_isImportDeclaration, isNamedImports as ts_isNamedImports,
  isStringLiteral as ts_isStringLiteral,
} from "typescript/ast";

export function importRefs(cb: Codebase, text: string, rec: DefRecord): HighlightRef[] {
  const out: HighlightRef[] = [];
  const ranges = new Map<string, string>();
  for (const sp of rec.spans) {
    const m = sp.ext && /^npm:((?:@[^/@]+\/)?[^@/]+)@([^/#]+)/.exec(sp.ext.module);
    if (m) ranges.set(m[1]!, m[2]!);
  }
  const sf = parse("page.ts", text);
  for (const st of sf.statements) {
    if (!ts_isImportDeclaration(st) || !ts_isStringLiteral(st.moduleSpecifier)) continue;
    const spec = st.moduleSpecifier.text;
    const at = { start: st.moduleSpecifier.getStart(sf), end: st.moduleSpecifier.end };
    if (spec === "#codebase" || spec.startsWith("#codebase/")) {
      const ns = spec.slice("#codebase".length).replace(/^\//, "").replaceAll("/", ".");
      out.push({ ...at, namespace: ns });
      const b = st.importClause?.namedBindings;
      if (!b || !ts_isNamedImports(b)) continue;
      for (const el of b.elements) {
        const id = el.propertyName ?? el.name;
        const hs = cb.store.hashesAt(ns ? `${ns}.${id.text}` : id.text);
        // a name holding a type and a value: the one imported
        const typeOnly = isTypeOnlyImport(st.importClause) || el.isTypeOnly;
        const h = hs.find((x) => (cb.store.getDef(x)?.kind === "type") === typeOnly) ?? hs[0];
        if (h) out.push({ start: id.getStart(sf), end: id.end, hash: h });
      }
      continue;
    }
    const name = /^((?:@[^/]+\/)?[^/]+)/.exec(spec)?.[1];
    const range = name && ranges.get(name);
    if (name && range) out.push({ ...at, package: { name, range } });
  }
  return out;
}

uses

used by