apiOf
uses node:path
The package's exports as its types declare them: the API a definition using it is checked against.
import type { Codebase } from "#codebase/tcmts/cli/codebase"; import { clip, kindOf } from "#codebase/tcmts/cli/package_pages"; import type { ApiEntry } from "#codebase/tcmts/cli/package_pages"; import { highlight } from "#codebase/tcmts/cli/pages"; import { createProgram } from "#codebase/tcmts/core/program"; import { declarationOf } from "#codebase/tcmts/core/ts"; import path from "node:path"; import { ModuleKind as ts_ModuleKind, SymbolFlags as ts_SymbolFlags, TypeFormatFlags as ts_TypeFormatFlags, } from "typescript/sync"; import type { Symbol as ts_Symbol } from "typescript/sync"; export function apiOf(cb: Codebase, alias: string): ApiEntry[] { // a file in the cache importing the package, never written: the package's types are what it resolves to const from = path.join(cb.lay.cacheDir, "__tcm_api__.ts"); const { program, checker } = createProgram([from], { ...cb.compilerOptions(), noEmit: true, skipLibCheck: true }, new Map([[from, `import ${JSON.stringify(alias)};\n`]])); const file = program.getResolvedModule(from, alias, ts_ModuleKind.ESNext)?.resolvedFileName; const mod = file && checker.getSymbolOfSourceFile(file); if (!mod) return []; const flags = ts_TypeFormatFlags.UseAliasDefinedOutsideCurrentScope | ts_TypeFormatFlags.WriteArrowStyleSignature; const entry = (name: string, sym: ts_Symbol): ApiEntry => { const s = sym.flags & ts_SymbolFlags.Alias ? checker.getAliasedSymbol(sym) : sym; const kind = kindOf(s); let signature = ""; try { // a type reads best as declared (its name alone says nothing) if (kind === "interface" || kind === "type" || kind === "enum") { const decl = declarationOf(s); signature = decl ? clip(decl.getText()) : ""; } else if (kind !== "namespace") signature = checker.typeToString(checker.getTypeOfSymbol(s), undefined, flags); } catch { signature = ""; } return { name, kind, signature, html: highlight(signature, [], new Set()), doc: s.getDocumentationComment(checker) }; }; const out = checker .getExportsOfModule(mod) .filter((s) => !s.name.startsWith("__")) .map((s) => entry(s.name, s)); // `export = fn` (CommonJS style): the module itself is the API const exportEquals = mod.getExports().get("export=" as never); if (exportEquals && !out.some((e) => e.kind === "function" || e.kind === "class")) out.unshift(entry("default", exportEquals)); return out.sort((a, b) => (a.name === "default" ? -1 : b.name === "default" ? 1 : a.name < b.name ? -1 : 1)).slice(0, 500); }
uses
- tcmts.cli.codebase.Codebase
- tcmts.cli.package_pages.ApiEntry
- tcmts.cli.package_pages.clip
- tcmts.cli.package_pages.kindOf
- tcmts.cli.pages.highlight
- tcmts.core.program.createProgram
- tcmts.core.ts.declarationOf