reexportClassifier

tcmts.core.analyze.reexportClassifier · #d2pgi24i · term · runs on the server

uses tcmts.core.analyze.externalModule, which uses node:module

What a project module re-exports from a package under a name: export { x as name } from "pkg", a name of pkg's in export * from "pkg", or the package's namespace in export * as name from "pkg". Using it through the module is using the package's export.

import { externalModule } from "#codebase/tcmts/core/analyze";
import type { AnalyzeHost } from "#codebase/tcmts/core/analyze";
import type { TypeChecker } from "#codebase/tcmts/core/ts";
import type { ExtRef } from "#codebase/tcmts/core/types";
import {
  isExportDeclaration as ts_isExportDeclaration, isNamedExports as ts_isNamedExports,
  isStringLiteral as ts_isStringLiteral,
} from "typescript/ast";
import type { Node as ts_Node, SourceFile as ts_SourceFile } from "typescript/ast";

export function reexportClassifier(checker: TypeChecker, host: AnalyzeHost) {
  const cache = new Map<string, ExtRef | undefined>();
  const exportsOf = new Map<string, Set<string>>();
  return (sf: ts_SourceFile, name: string, at: ts_Node): ExtRef | undefined => {
    const key = `${sf.path}\0${name}`;
    if (cache.has(key)) return cache.get(key);
    let r: ExtRef | undefined;
    for (const st of sf.statements) {
      if (
        !ts_isExportDeclaration(st) ||
        !st.moduleSpecifier ||
        !ts_isStringLiteral(st.moduleSpecifier)
      )
        continue;
      const spec = st.moduleSpecifier.text;
      if (spec.startsWith(".") || spec.startsWith("/") || spec.startsWith("#")) continue;
      if (st.exportClause) {
        if (ts_isNamedExports(st.exportClause)) {
          const el = st.exportClause.elements.find((e) => e.name.text === name);
          if (el)
            r = externalModule(spec, sf.fileName, at, (el.propertyName ?? el.name).text, host);
        } else if (st.exportClause.name.text === name) {
          r = externalModule(spec, sf.fileName, at, "*", host);
        }
      } else {
        const k = `${sf.path}\0${spec}`;
        if (!exportsOf.has(k)) {
          const mod = checker.getSymbolAtLocation(st.moduleSpecifier);
          exportsOf.set(k, new Set(mod ? checker.getExportsOfModule(mod).map((e) => e.name) : []));
        }
        if (exportsOf.get(k)!.has(name)) r = externalModule(spec, sf.fileName, at, name, host);
      }
      if (r) break;
    }
    cache.set(key, r);
    return r;
  };
}

uses

used by