@travetto/schema
Version:
Data type registry for runtime validation, reflection and binding.
99 lines (84 loc) • 3.11 kB
text/typescript
import ts from 'typescript';
import {
TransformerState, OnProperty, OnClass, AfterClass, DocUtil, DeclarationUtil, OnGetter, OnSetter
} from '@travetto/transformer';
import { SchemaTransformUtil } from './transformer/util.ts';
const InSchemaSymbol = Symbol();
const AccessorsSymbol = Symbol();
interface AutoState {
[InSchemaSymbol]?: boolean;
[AccessorsSymbol]?: Set<string>;
}
/**
* Processes `@Schema` to register class as a valid Schema
*/
export class SchemaTransformer {
/**
* Track schema on start
*/
static startSchema(state: AutoState & TransformerState, node: ts.ClassDeclaration): ts.ClassDeclaration {
state[InSchemaSymbol] = true;
state[AccessorsSymbol] = new Set();
return node;
}
/**
* Mark the end of the schema, document
*/
static finalizeSchema(state: AutoState & TransformerState, node: ts.ClassDeclaration): ts.ClassDeclaration {
const modifiers = (node.modifiers ?? []).slice(0);
const comments = DocUtil.describeDocs(node);
if (!state.findDecorator(this, node, 'Schema', SchemaTransformUtil.SCHEMA_IMPORT)) {
modifiers.unshift(state.createDecorator(SchemaTransformUtil.SCHEMA_IMPORT, 'Schema'));
}
if (comments.description) {
modifiers.push(state.createDecorator(SchemaTransformUtil.COMMON_IMPORT, 'Describe', state.fromLiteral({
title: comments.description
})));
}
delete state[InSchemaSymbol];
delete state[AccessorsSymbol];
return state.factory.updateClassDeclaration(
node,
modifiers,
node.name,
node.typeParameters,
node.heritageClauses,
node.members
);
}
/**
* Handle all properties, while in schema
*/
static processSchemaField(state: TransformerState & AutoState, node: ts.PropertyDeclaration): ts.PropertyDeclaration {
const ignore = state.findDecorator(this, node, 'Ignore');
return state[InSchemaSymbol] && !ignore && DeclarationUtil.isPublic(node) ?
SchemaTransformUtil.computeField(state, node) : node;
}
/**
* Handle getters
*/
static processSchemaGetter(state: TransformerState & AutoState, node: ts.GetAccessorDeclaration): ts.GetAccessorDeclaration {
const ignore = state.findDecorator(this, node, 'Ignore');
if (state[InSchemaSymbol] && !ignore && DeclarationUtil.isPublic(node) && !state[AccessorsSymbol]?.has(node.name.getText())) {
state[AccessorsSymbol]?.add(node.name.getText());
return SchemaTransformUtil.computeField(state, node);
}
return node;
}
/**
* Handle setters
*/
static processSchemaSetter(state: TransformerState & AutoState, node: ts.SetAccessorDeclaration): ts.SetAccessorDeclaration {
const ignore = state.findDecorator(this, node, 'Ignore');
if (state[InSchemaSymbol] && !ignore && DeclarationUtil.isPublic(node) && !state[AccessorsSymbol]?.has(node.name.getText())) {
state[AccessorsSymbol]?.add(node.name.getText());
return SchemaTransformUtil.computeField(state, node);
}
return node;
}
}