UNPKG

@travetto/runtime

Version:

Runtime for travetto applications.

104 lines (90 loc) 3.19 kB
import * as ts from 'typescript'; import { FunctionMetadataTag } from '@travetto/runtime'; import { CoreUtil, Import, SystemUtil, TransformerState } from '@travetto/transformer'; const RegisterImportSymbol = Symbol(); interface MetadataInfo { [RegisterImportSymbol]?: Import; } /** * Utils for registering function/class metadata at compile time */ export class MetadataRegistrationUtil { static REGISTER_IMPORT = '@travetto/runtime/src/function.ts'; static REGISTER_FN = 'registerFunction'; static isValid({ importName: imp }: TransformerState): boolean { return imp !== this.REGISTER_IMPORT; } static tag(state: TransformerState, node: ts.Node): FunctionMetadataTag { const text = node.getText(); const hash = SystemUtil.naiveHash(text); try { const range = CoreUtil.getRangeOf(state.source, node) ?? [0, 0]; if (ts.isMethodDeclaration(node) || ts.isFunctionDeclaration(node) || ts.isFunctionExpression(node)) { const bodyStart = CoreUtil.getRangeOf(state.source, node?.body?.statements[0])?.[0]; if (bodyStart) { range.push(bodyStart); } } return { hash, lines: range }; } catch { return { hash, lines: [0, 0] }; } } /** * Register metadata on a function */ static registerFunction(state: TransformerState & MetadataInfo, node: ts.FunctionDeclaration | ts.FunctionExpression, src?: ts.FunctionDeclaration | ts.FunctionExpression | ts.InterfaceDeclaration | ts.TypeAliasDeclaration ): void { // If we have a class like function state[RegisterImportSymbol] ??= state.importFile(this.REGISTER_IMPORT); const tag = this.tag(state, src ?? node); const meta = state.factory.createCallExpression( state.createAccess(state[RegisterImportSymbol].ident, this.REGISTER_FN), [], [ state.createIdentifier(node.name!.text), state.getModuleIdentifier(), state.fromLiteral(tag), ] ); state.addStatements([state.factory.createExpressionStatement(meta)]); } /** * Register metadata on a class */ static registerClass( state: TransformerState & MetadataInfo, node: ts.ClassDeclaration, cls: FunctionMetadataTag, methods?: Record<string, FunctionMetadataTag> ): ts.ClassDeclaration { state[RegisterImportSymbol] ??= state.importFile(this.REGISTER_IMPORT); const name = node.name?.escapedText.toString() ?? ''; const meta = state.factory.createCallExpression( state.createAccess(state[RegisterImportSymbol].ident, this.REGISTER_FN), [], [ state.createIdentifier(name), state.getModuleIdentifier(), state.fromLiteral(cls), state.extendObjectLiteral(methods ?? {}), state.fromLiteral(CoreUtil.isAbstract(node)), ] ); return state.factory.updateClassDeclaration( node, node.modifiers, node.name, node.typeParameters, node.heritageClauses, [ state.factory.createClassStaticBlockDeclaration( state.factory.createBlock([ state.factory.createExpressionStatement(meta) ]) ), ...node.members ] ); } }