@travetto/schema
Version:
Data type registry for runtime validation, reflection and binding.
102 lines (86 loc) • 3.18 kB
text/typescript
import ts from 'typescript';
import {
TransformerState, OnProperty, OnClass, AfterClass, DecoratorMeta, DocUtil, DeclarationUtil, OnGetter, OnSetter
} from '@travetto/transformer';
import { SchemaTransformUtil } from './transformer/util';
const inSchema = Symbol.for('@travetto/schema:schema');
const accessors = Symbol.for('@travetto/schema:accessors');
interface AutoState {
[inSchema]?: boolean;
[accessors]?: Set<string>;
}
const SCHEMA_IMPORT = '@travetto/schema/src/decorator/schema';
const COMMON_IMPORT = '@travetto/schema/src/decorator/common';
/**
* Processes `@Schema` to register class as a valid Schema
*/
export class SchemaTransformer {
/**
* Track schema on start
*/
static startSchema(state: AutoState & TransformerState, node: ts.ClassDeclaration, dec?: DecoratorMeta): ts.ClassDeclaration {
state[inSchema] = true;
state[accessors] = 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', SCHEMA_IMPORT)) {
modifiers.unshift(state.createDecorator(SCHEMA_IMPORT, 'Schema'));
}
if (comments.description) {
modifiers.push(state.createDecorator(COMMON_IMPORT, 'Describe', state.fromLiteral({
title: comments.description
})));
}
delete state[inSchema];
delete state[accessors];
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[inSchema] && !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[inSchema] && !ignore && DeclarationUtil.isPublic(node) && !state[accessors]?.has(node.name.getText())) {
state[accessors]?.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[inSchema] && !ignore && DeclarationUtil.isPublic(node) && !state[accessors]?.has(node.name.getText())) {
state[accessors]?.add(node.name.getText());
return SchemaTransformUtil.computeField(state, node);
}
return node;
}
}