Skip to content

Commit

Permalink
Dev/subclass registration (#1)
Browse files Browse the repository at this point in the history
* Changed the package name and version.

* Bumped the version of typescript and disabled strict property initialization for tests to work.

* Implemented subclass registration.

* Changed the package info.

* Fixed the indentation.

* Fixed the indentation.
  • Loading branch information
efreeti authored Apr 5, 2019
1 parent ede3f22 commit 0210012
Show file tree
Hide file tree
Showing 5 changed files with 103 additions and 25 deletions.
12 changes: 6 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "tsruntime",
"version": "2.1.2",
"name": "ts-runtime-reflection",
"version": "1.0.0",
"main": "./dist/index.js",
"typings": "./dist/index.d.ts",
"scripts": {
Expand All @@ -14,17 +14,17 @@
},
"repository": {
"type": "git",
"url": "https://github.com/goloveychuk/tsruntime.git"
"url": "https://github.com/efreeti/ts-runtime-reflection.git"
},
"keywords": [
"typescript",
"reflect",
"runtime"
],
"bugs": {
"url": "https://github.com/goloveychuk/tsruntime/issues"
"url": "https://github.com/efreeti/ts-runtime-reflection/issues"
},
"homepage": "https://github.com/goloveychuk/tsruntime",
"homepage": "https://github.com/efreeti/ts-runtime-reflection",
"license": "MIT",
"devDependencies": {
"@types/node": "^7.0.18",
Expand All @@ -39,7 +39,7 @@
"karma-webpack": "~2.0.3",
"reflect-metadata": "^0.1.10",
"rimraf": "^2.6.1",
"typescript": "2.6.1",
"typescript": "2.8.1",
"webpack": "2.2.0"
}
}
74 changes: 65 additions & 9 deletions src/transformer.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@

import * as ts from 'typescript';
import { Types, MetadataKey, REFLECTIVE_KEY } from './types';
import { Types, TypeMetadataKey, SubclassMetadataKey, REFLECTIVE_KEY } from './types';
import * as tse from './typescript-extended'


Expand Down Expand Up @@ -315,17 +315,70 @@ function Transformer(program: ts.Program, context: ts.TransformationContext) {


let currentScope: ts.SourceFile | ts.CaseBlock | ts.ModuleBlock | ts.Block;
function addDecorator(oldDecorators: ts.NodeArray<ts.Decorator> | undefined, exp: any) {

function combineDecorators(group1: ts.NodeArray<ts.Decorator> | undefined, group2: ts.NodeArray<ts.Decorator> | undefined) {
let newDecorators = []
if (oldDecorators !== undefined) {
newDecorators.push(...oldDecorators)
if (group1 !== undefined) {
newDecorators.push(...group1)
}
if (group2 !== undefined) {
newDecorators.push(...group2)
}
const decCall = ts.createCall(ts.createIdentifier('Reflect.metadata'), undefined, [ts.createLiteral(MetadataKey), exp])
const dec = ts.createDecorator(decCall)
newDecorators.push(dec)
return ts.createNodeArray<ts.Decorator>(newDecorators)
}

function createTypeDecorator(exp: any) {
return ts.createNodeArray<ts.Decorator>([ts.createDecorator(ts.createCall(
ts.createIdentifier('Reflect.metadata'), undefined, [ts.createLiteral(TypeMetadataKey), exp]
))])
}

function createParentClassDecorators(node: ts.InterfaceTypeWithDeclaredMembers, ctx: Ctx): ts.NodeArray<ts.Decorator> {
return ts.createNodeArray<ts.Decorator>((node.getBaseTypes() || []).reduce((result, base) => {
if (base.flags & ts.TypeFlags.Object && (<ts.ObjectType>base).objectFlags & ts.ObjectFlags.Reference) {
const reference = <ts.TypeReference> base;
const symbol = reference.target.getSymbol();

if (!(reference.target.objectFlags & ts.ObjectFlags.Tuple) && symbol && symbol.valueDeclaration) {
return result.concat([ts.createDecorator(
ts.createFunctionExpression(
undefined,
undefined,
undefined,
undefined,
[ts.createParameter(undefined, undefined, undefined, 'target')],
undefined,
ts.createBlock([
ts.createStatement(
ts.createCall(ts.createIdentifier('Reflect.defineMetadata'), undefined, [
ts.createLiteral(SubclassMetadataKey),
ts.createArrayLiteral([
ts.createSpread(
ts.createLogicalOr(
ts.createCall(ts.createIdentifier('Reflect.getMetadata'), undefined, [
ts.createLiteral(SubclassMetadataKey),
getIdentifierForSymbol(reference.target, ctx)
]),
ts.createArrayLiteral([])
)
),
ts.createIdentifier('target')
]),
getIdentifierForSymbol(reference.target, ctx)
])
)
])
)
)])
} else {
return result;
}
} else {
return result;
}
}, <Array<ts.Decorator>>[]));
}

function visitPropertyDeclaration(node: tse.PropertyDeclaration, allprops: ts.PropertyName[]) {
allprops.push(node.name)
const type = checker.getTypeAtLocation(node)
Expand All @@ -336,7 +389,7 @@ function Transformer(program: ts.Program, context: ts.TransformationContext) {
}
serializedType.initializer = initializerExp
const objLiteral = makeLiteral(serializedType)
const newDecorators = addDecorator(node.decorators, objLiteral)
const newDecorators = combineDecorators(node.decorators, createTypeDecorator(objLiteral))
let newNode = ts.getMutableClone(node);
newNode.decorators = newDecorators
return newNode
Expand Down Expand Up @@ -392,7 +445,10 @@ function Transformer(program: ts.Program, context: ts.TransformationContext) {

const newNode = ts.getMutableClone(node);
newNode.members = newMembers
newNode.decorators = addDecorator(node.decorators, classTypeExp)
newNode.decorators = combineDecorators(
combineDecorators(node.decorators, createTypeDecorator(classTypeExp)),
createParentClassDecorators(<ts.InterfaceTypeWithDeclaredMembers>type, { node })
)
return newNode
}
function onBeforeVisitNode(node: ts.Node) {
Expand Down
11 changes: 8 additions & 3 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,11 +107,16 @@ export const Reflective = ReflectiveFactory(function (target: any) {



export const MetadataKey = "tsruntime:type"
export const TypeMetadataKey = "ts-runtime-reflection:type"
export const SubclassMetadataKey = "ts-runtime-reflection:subtypes"


export function getSubclasses(target: Function): Function[] | undefined {
return Reflect.getMetadata(SubclassMetadataKey, target)
}

export function getType(target: Function): Types.Type | undefined {
return Reflect.getMetadata(MetadataKey, target)
return Reflect.getMetadata(TypeMetadataKey, target)
}

export function mustGetType(target: Function): Types.Type {
Expand All @@ -135,5 +140,5 @@ export function mustGetPropType(target: Function, propertyKey: string | symbol |


export function getPropType(target: Function, propertyKey: string | symbol | number): Types.Type | undefined {
return Reflect.getMetadata(MetadataKey, target.prototype, propertyKey as any)
return Reflect.getMetadata(TypeMetadataKey, target.prototype, propertyKey as any)
}
30 changes: 23 additions & 7 deletions tests/class_decoration.spec.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import {
Reflective,
Types,
getType,
getPropType
Reflective,
Types,
getType,
getPropType,
getSubclasses
} from '../src';

const TypeKind = Types.TypeKind;
Expand All @@ -24,19 +25,34 @@ class TestClass extends Array<string> {
}
}

describe('Class Decoration', () => {
@Reflective
class ParentClass {}

@Reflective
class Subclass1 extends ParentClass {}

@Reflective
class Subclass2 extends ParentClass {}

describe('Class decoration', () => {
it('should decorate null properties', () => {
const ptype = getType(TestClass) as Types.ClassType
expect(ptype.kind).toEqual(TypeKind.Class)
expect(ptype.name).toEqual('TestClass')
expect(ptype.extends).toEqual({kind: TypeKind.Reference, type: Array, arguments: [{kind: TypeKind.String} as any]})

expect(ptype.props).toEqual(['str', 'str-str', 42])

expect(getPropType(TestClass, 42)).toEqual({kind: TypeKind.String})

});
});

describe('Sub classes registration', () => {
it('should register all subclasses', () => {
const subclasses = getSubclasses(ParentClass)!
expect(subclasses.length).toEqual(2)
expect(subclasses[0]).toEqual(Subclass1)
expect(subclasses[1]).toEqual(Subclass2)
});
});

1 change: 1 addition & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
"noFallthroughCasesInSwitch": true,
"removeComments": true,
"strict": true,
"strictPropertyInitialization": false,
"lib": ["dom", "es2015"]
},
"include": [
Expand Down

0 comments on commit 0210012

Please sign in to comment.