@visionary_software/apt
v2026.5.10
Published
TypeScript-AST navigation helpers for annotation processing and compiler-integration plugins.
Downloads
71
Readme
apt — Annotation-Processing Utilities (TypeScript line)
Reusable TypeScript-AST navigation helpers for compiler-integration plugins (transformers, language-service plugins, codegen). Fills the gaps that javax.lang.model gave Java's annotation-processor authors for free, so plugin SPIs stay focused on their own domain shapes instead of leaking ts.Node traversal knowledge.
This is the TypeScript line. The Java line lives on java; the Kotlin K2/FIR port on kotlin.
Install
bun add @visionary_software/apt
npm i @visionary_software/apt
pnpm add @visionary_software/apt
yarn add @visionary_software/apttypescript is a peer dependency.
API (so far)
tagName(tag: ts.JSDocTag): string
The bare tag name without the leading @. For @precondition IsNotBlank returns "precondition"; for a custom @rangeBounds {...} returns "rangeBounds".
methodName(decl: ts.SignatureDeclaration): string
Diagnostic-friendly name of a callable: identifier text for named methods/functions, "<init>" for constructors (so new Widget(...) call sites read naturally in error messages), "<anonymous>" for arrow functions and unnamed function expressions.
enclosingClassName(decl: ts.SignatureDeclaration): string
Name of the type that owns a callable: enclosing class for MethodDeclaration/ConstructorDeclaration, enclosing interface for MethodSignature. "<anonymous>" for free-standing callables (top-level functions, function expressions, arrows).
parameterDescription(decl: ts.SignatureDeclaration, index: number): ParameterDescription
Read the parameter at index into a diagnostic-friendly { name, type } record. Returns a ParameterDescription (also exported):
interface ParameterDescription {
readonly name: string; // identifier text; "<anonymous>" for destructuring patterns
readonly type: string; // source text of the type annotation; "<inferred>" if absent
}getOverrideHierarchy(decl: ts.SignatureDeclaration, checker: ts.TypeChecker): readonly ts.SignatureDeclaration[]
Closest-first walk of a method's override chain — the seed at index 0, then super-methods on the enclosing class/interface's extends/implements chain in encounter order. Walks heritage clauses transitively (TypeScript's getBaseTypes only covers extends; implements requires the heritage-clause walk directly). Dedupes by symbol identity. Match is by member name only — signature compatibility is not consulted, since the contract-discovery use case only needs the same-named declaration on each supertype to scan it for annotations.
Free-standing callables and methods on classes with no supertypes return exactly [decl].
tagsOnRegistrationSite(useSiteTag: ts.JSDocTag, sourceFiles: readonly ts.SourceFile[]): readonly ts.JSDocTag[]
Generic JSDoc-typedef primitive — given a use-site JSDoc tag, find the @typedef whose name exactly matches the tag's name and return the JSDoc tags decorating that typedef. Searches across the given source files in order. Returns the empty array when no typedef matches.
Structurally analogous to Java apt's AllMetaAnnotations (which exposes meta-annotations on an annotation type) — generic, no DbC concepts. Lets any annotation processor / doc generator / type-system extension compose policies on top of "what's annotated about this tag's registration?". JSDoc tag composition is one-level (typedefs aren't typedef-able in turn), so this primitive is shallower than Java's recursive walker.
Match is exact-string. Case-bridging conventions (e.g. PascalCase typedef registers a camelCase tag) are the caller's concern; the caller normalizes before calling. apt does not bake naming policy into the primitive.
TypeScript peculiarities
- JSDoc-tag lookup walks
node.jsDocdirectly, notts.getJSDocTags. The convenience accessor caches its result, and that cache can omit@typedeftags after a node's.jsDocarray is replaced (which contrax's transformer does to recover JSDoc thattspc's default parsing mode strips). The internalfindTypedefByNamereads the underlying JSDoc tree directly so it stays correct across reparses. If you build your own typedef walker on top of apt's primitives, prefer thenode.jsDoc[i].tagsshape overts.getJSDocTags(node)for the same reason.
Why a separate module?
apt is a standalone annotation-processing / compiler-integration toolkit. Its first downstream consumer is the contrax DbC framework, but apt is not part of contrax — its surface is purely ts.AST navigation. Any compiler plugin that wants the same lego pieces can depend on apt without dragging in DbC concepts.
License
GPL-3.0-or-later. See COPYING. Contact Visionary Software Solutions for commercial licensing.
