From f7b9fa414a2cff9a07ceddf94bccaccf59153164 Mon Sep 17 00:00:00 2001 From: Ketut Sandiarsa Date: Tue, 24 Jan 2017 20:20:55 +0800 Subject: [PATCH 1/9] Adding transformers --- .vscode/settings.json | 12 + gulpfile.js | 2 +- src/analyzers/class-analyzer.ts | 7 + src/analyzers/class-decorator-analyzer.ts | 6 - src/analyzers/helper.ts | 23 +- src/analyzers/metadata-analyzer.ts | 14 -- src/analyzers/method-analyzer.ts | 34 +-- src/analyzers/method-decorator-analyzer.ts | 41 +--- src/analyzers/module-analyzer.ts | 39 +-- src/core.ts | 72 +++++- src/index.ts | 11 +- src/transformer.ts | 101 +++----- src/transformers/constructor.ts | 24 ++ src/transformers/method.ts | 25 ++ src/transformers/parameter.ts | 13 + src/transformers/ts-child-decorator.ts | 52 ++++ src/transformers/ts-class.ts | 36 +++ src/transformers/ts-decorator.ts | 37 +++ src/transformers/ts-module.ts | 40 ++++ src/visitors/class-decorator-visitor.ts | 24 -- src/visitors/class-visitor.ts | 134 ----------- src/visitors/index.ts | 76 ------ src/visitors/method-decorator-visitor.ts | 33 --- src/visitors/module-visitor.ts | 41 ---- test/analyzers/class-analyzer.spec.ts | 21 +- .../class-decorator-analyzer.spec.ts | 24 +- test/analyzers/helper.spec.ts | 39 +++ test/analyzers/metadata-analyzer.spec.ts | 126 ---------- test/analyzers/method-analyzer.spec.ts | 8 +- .../method-decorator-analyzer.spec.ts | 111 +-------- test/analyzers/module-analyzer.spec.ts | 31 ++- test/helper.ts | 77 ------ test/myclass.ts | 18 +- test/transformer.spec.ts | 147 ------------ test/transformers/constructor.spec.ts | 37 +++ test/transformers/method.spec.ts | 38 +++ test/transformers/parameter.spec.ts | 28 +++ test/transformers/ts-child-decorator.spec.ts | 87 +++++++ test/transformers/ts-module.spec.ts | 5 + test/visitors/class-decorator.spec.ts | 73 ------ test/visitors/class-visitor.spec.ts | 224 ------------------ test/visitors/index.spec.ts | 70 ------ .../visitors/method-decorator-visitor.spec.ts | 170 ------------- test/visitors/module-visitor.spec.ts | 134 ----------- tsconfig.json | 2 +- 45 files changed, 678 insertions(+), 1689 deletions(-) create mode 100644 .vscode/settings.json delete mode 100644 src/analyzers/metadata-analyzer.ts create mode 100644 src/transformers/constructor.ts create mode 100644 src/transformers/method.ts create mode 100644 src/transformers/parameter.ts create mode 100644 src/transformers/ts-child-decorator.ts create mode 100644 src/transformers/ts-class.ts create mode 100644 src/transformers/ts-decorator.ts create mode 100644 src/transformers/ts-module.ts delete mode 100644 src/visitors/class-decorator-visitor.ts delete mode 100644 src/visitors/class-visitor.ts delete mode 100644 src/visitors/index.ts delete mode 100644 src/visitors/method-decorator-visitor.ts delete mode 100644 src/visitors/module-visitor.ts create mode 100644 test/analyzers/helper.spec.ts delete mode 100644 test/analyzers/metadata-analyzer.spec.ts delete mode 100644 test/transformer.spec.ts create mode 100644 test/transformers/constructor.spec.ts create mode 100644 test/transformers/method.spec.ts create mode 100644 test/transformers/parameter.spec.ts create mode 100644 test/transformers/ts-child-decorator.spec.ts create mode 100644 test/transformers/ts-module.spec.ts delete mode 100644 test/visitors/class-decorator.spec.ts delete mode 100644 test/visitors/class-visitor.spec.ts delete mode 100644 test/visitors/index.spec.ts delete mode 100644 test/visitors/method-decorator-visitor.spec.ts delete mode 100644 test/visitors/module-visitor.spec.ts diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..66be4d9 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,12 @@ +// Place your settings in this file to overwrite default and user settings. +{ + "files.exclude": { + "**/*.js.map": true, + //"**/*.js": {"when": "$(basename).ts"}, + //"**/*.d.ts": {"when": "$(basename).ts"}, + "**/.git": true, + "**/.svn": true, + "**/.hg": true, + "**/.DS_Store": true + } +} \ No newline at end of file diff --git a/gulpfile.js b/gulpfile.js index 36bee39..b86b67a 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -10,7 +10,7 @@ var gulp = require("gulp"), //******** BUILD ************* var tsProject = tsc.createProject("tsconfig.json", { - declaration: true, + declaration: false, noResolve: false, typescript: require("typescript") }); diff --git a/src/analyzers/class-analyzer.ts b/src/analyzers/class-analyzer.ts index 9953b1d..92cb556 100644 --- a/src/analyzers/class-analyzer.ts +++ b/src/analyzers/class-analyzer.ts @@ -43,7 +43,14 @@ export class ClassAnalyzer { && this.node.declarations[0].init && this.node.declarations[0].init.type == SyntaxKind.CallExpression && this.node.declarations[0].init.callee.type == SyntaxKind.FunctionExpression + && this.node.declarations[0].init.callee.body.type == SyntaxKind.BlockStatement && this.node.declarations[0].init.callee.id == null } + + getBaseClass(){ + if(this.isCandidate() && this.node.declarations[0].init.arguments.length > 0){ + return this.node.declarations[0].init.arguments[0].name + } + } } diff --git a/src/analyzers/class-decorator-analyzer.ts b/src/analyzers/class-decorator-analyzer.ts index 186f053..d7f2451 100644 --- a/src/analyzers/class-decorator-analyzer.ts +++ b/src/analyzers/class-decorator-analyzer.ts @@ -22,10 +22,4 @@ export class ClassDecoratorAnalyzer{ && this.node.right.type == SyntaxKind.CallExpression && HP.getMethodNameFromCallee(this.node.right.callee) == "__decorate" } - - getDecorators():MetaData[]{ - if(this.isDecorator()) - return HP.getDecorators(this.node.right.arguments[0]) - else return null; - } } \ No newline at end of file diff --git a/src/analyzers/helper.ts b/src/analyzers/helper.ts index 90a03cb..e5084d9 100644 --- a/src/analyzers/helper.ts +++ b/src/analyzers/helper.ts @@ -9,34 +9,13 @@ export function getMethodNameFromCallee(callee) { else return callee.name; } -/** - * require ArrayExpression - */ -export function getDecorators(arrayExpression) { - let result = []; - for (let x of arrayExpression.elements) { - if (isReservedDecorator(x)) continue; - result.push({ - type: "Decorator", - name: getMethodNameFromCallee(x.callee), - analysis: AnalysisType.Valid, - children: x.arguments.map(arg => { - type: "Parameter", - name: getDecoratorParameterName(arg), - analysis: AnalysisType.Valid, - children: [] - }) - }) - } - return result; -} - /** * require CallExpression */ export function isReservedDecorator(callExpression) { return getMethodNameFromCallee(callExpression.callee) == "__metadata" || getMethodNameFromCallee(callExpression.callee) == "__param" + || getMethodNameFromCallee(callExpression.callee) == "__decorate" } export function getDecoratorParameterName(param) { diff --git a/src/analyzers/metadata-analyzer.ts b/src/analyzers/metadata-analyzer.ts deleted file mode 100644 index 5a339d8..0000000 --- a/src/analyzers/metadata-analyzer.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { SyntaxKind, MetaData, AnalysisType, flag } from "../core" - -export class MetaDataAnalyzer { - constructor(private meta: MetaData, private metaParent: MetaData) { } - - //is has exported children? - hasExportedChildren() { - return this.meta.children.some(x => flag(x.analysis, AnalysisType.Exported)); - } - - hasValidChildren() { - return this.meta.children.some(x => flag(x.analysis, AnalysisType.Valid)); - } -} \ No newline at end of file diff --git a/src/analyzers/method-analyzer.ts b/src/analyzers/method-analyzer.ts index c69be23..735cfb4 100644 --- a/src/analyzers/method-analyzer.ts +++ b/src/analyzers/method-analyzer.ts @@ -4,51 +4,41 @@ import * as MH from "./helper" export class MethodAnalyzer { /** - * expect AssignmentExpression, CallExpression + * expect ExpressionStatement */ constructor(private node) { } - /** - * expect AssignmentExpression - */ + isMethod(className: String) { return this.isMethodStatement() - && this.node.left.object.object.name == className + && this.node.expression.left.object.object.name == className } private isMethodStatement() { - return this.node.type == SyntaxKind.AssignmentExpression - && this.node.left.type == SyntaxKind.MemberExpression - && this.node.left.object.type == SyntaxKind.MemberExpression - && this.node.left.object.property.name == "prototype" - && this.node.right.type == SyntaxKind.FunctionExpression + return this.node.type == SyntaxKind.ExpressionStatement + && this.node.expression.type == SyntaxKind.AssignmentExpression + && this.node.expression.left.type == SyntaxKind.MemberExpression + && this.node.expression.left.object.type == SyntaxKind.MemberExpression + && this.node.expression.left.object.property.name == "prototype" + && this.node.expression.right.type == SyntaxKind.FunctionExpression } - /** - * expect AssignmentExpression - */ getName() { if (this.isMethodStatement()) - return this.node.left.property.name + return this.node.expression.left.property.name else return null; } - /** - * require CallExpression - */ getClassName() { if (this.isMethodStatement()) - return this.node.left.object.object.name + return this.node.expression.left.object.object.name else return null; } - /** - * expect AssignmentExpression - */ getParams() { if (this.isMethodStatement()) - return this.node.right.params.map(x => x.name); + return this.node.expression.right.params.map(x => x.name); else return null; } diff --git a/src/analyzers/method-decorator-analyzer.ts b/src/analyzers/method-decorator-analyzer.ts index a209aa2..f241194 100644 --- a/src/analyzers/method-decorator-analyzer.ts +++ b/src/analyzers/method-decorator-analyzer.ts @@ -5,53 +5,20 @@ export class MethodDecoratorAnalyzer { constructor(private node) { } isDecorator() { - return HP.getMethodNameFromCallee(this.node.callee) == "__decorate" - && this.node.arguments.length == 4 - } - - getDecorators(): MetaData[] { - if (this.isDecorator()) - return HP.getDecorators(this.node.arguments[0]) - else return null; + return HP.getMethodNameFromCallee(this.node.expression.callee) == "__decorate" + && this.node.expression.arguments.length == 4 } getClassName() { if (this.isDecorator()) - return this.node.arguments[1].object.name; + return this.node.expression.arguments[1].object.name; else return null; } getMethodName() { if (this.isDecorator()) - return this.node.arguments[2].value - else return null; - } - - getParameters(index: number): MetaData[] { - if (this.isDecorator()) - return this.getParameterDecorators(this.getMethodName(), index) + return this.node.expression.arguments[2].value else return null; } - - private getParameterDecorators(methodName: string, index: number) { - let result = []; - for (let x of this.node.arguments[0].elements) { - if (HP.getMethodNameFromCallee(x.callee) == "__param" - && x.arguments[0].value == index) { - result.push({ - type: "Decorator", - name: HP.getMethodNameFromCallee(x.arguments[1].callee), - analysis: AnalysisType.Valid, - children: x.arguments[1].arguments.map(arg => { - type: "Parameter", - name: HP.getDecoratorParameterName(arg), - analysis: AnalysisType.Valid, - children: [] - }) - }) - } - } - return result; - } } diff --git a/src/analyzers/module-analyzer.ts b/src/analyzers/module-analyzer.ts index e2fd0c9..3282e1c 100644 --- a/src/analyzers/module-analyzer.ts +++ b/src/analyzers/module-analyzer.ts @@ -2,43 +2,44 @@ import { SyntaxKind } from "../core" export class ModuleAnalyzer { /** - * require CallExpression + * require ExpressionStatement */ constructor(private node) { } - /** - * require CallExpression - */ isCandidate() { /* - (function(Children){})(Children = (.Children) || ()) + (function(ModuleName){})(ModuleName = (.ModuleName) || ()) */ - return this.node.type == SyntaxKind.CallExpression - && this.node.arguments.length == 1 - && (this.node.arguments[0].type == SyntaxKind.AssignmentExpression - || this.node.arguments[0].type == SyntaxKind.LogicalExpression) - && this.node.arguments[0].left.name == this.node.callee.params[0].name + return this.node.type == SyntaxKind.ExpressionStatement + && this.node.expression.type == SyntaxKind.CallExpression + && this.node.expression.callee.type == SyntaxKind.FunctionExpression + && this.node.expression.callee.body.type == SyntaxKind.BlockStatement + && this.node.expression.arguments.length == 1 + && (this.node.expression.arguments[0].type == SyntaxKind.AssignmentExpression + || this.node.expression.arguments[0].type == SyntaxKind.LogicalExpression) + && this.node.expression.arguments[0].left.name == this.node.expression.callee.params[0].name + } + + getBody(){ + return this.node.expression.callee.body.body; } getName() { if (this.isCandidate()) - return this.node.arguments[0].left.name; + return this.node.expression.arguments[0].left.name; else return null; } - /** - * require CallExpression - */ isExported(parentName: string) { //(function(Children){})(Children = (Parent.Children) || ()) //(function(Children){})(Children = (exports.Children) || ()) - return this.node.arguments.length == 1 - && this.node.arguments[0].type == SyntaxKind.AssignmentExpression - && this.node.arguments[0].right.left.type == SyntaxKind.MemberExpression - && (this.node.arguments[0].right.left.object.name == parentName - || this.node.arguments[0].right.left.object.name == "exports"); + return this.node.expression.arguments.length == 1 + && this.node.expression.arguments[0].type == SyntaxKind.AssignmentExpression + && this.node.expression.arguments[0].right.left.type == SyntaxKind.MemberExpression + && (this.node.expression.arguments[0].right.left.object.name == parentName + || this.node.expression.arguments[0].right.left.object.name == "exports"); } } diff --git a/src/core.ts b/src/core.ts index a101226..7de8d90 100644 --- a/src/core.ts +++ b/src/core.ts @@ -19,14 +19,46 @@ export type MetadataType = "File" | "Module" | "Class" | "Function" | "Constructor" export interface MetaData { - type: MetadataType, - name: string, - analysis: AnalysisType, - children: MetaData[], - decorators?: MetaData[], + type: MetadataType + name: string + analysis: AnalysisType location?: {line: number, column: number} } +export interface DecoratorMetaData extends MetaData{ + type: "Decorator" + parameters: MetaData[] +} + +export interface ParameterMetaData extends MetaData{ + type: "Parameter" + decorators: DecoratorMetaData[] +} + +export interface MethodMetaData extends MetaData{ + type: "Method" + decorators: DecoratorMetaData[] + parameters: ParameterMetaData[] +} + +export interface ConstructorMetaData extends MetaData{ + type: "Constructor" + parameters: ParameterMetaData[] +} + +export interface ClassMetaData extends MetaData{ + type: "Class" + decorators: DecoratorMetaData[] + methods:MethodMetaData[] + baseClass:string + constructor: ConstructorMetaData +} + +export interface ParentMetaData extends MetaData{ + type: "Module" | "File" + children: MetaData[] +} + export interface Visitor { start(node, meta: MetaData, metaParent: MetaData): MetaData exit(node, meta: MetaData, metaParent: MetaData): MetaData @@ -65,13 +97,37 @@ export module SyntaxKind { export module Call { const META_DATA_KEY = "kamboja:Call.when"; - export function when(...kinds: string[]) { + export function when(kind: string) { return function (target, method, descriptor) { - Reflect.defineMetadata(META_DATA_KEY, kinds, target, method); + Reflect.defineMetadata(META_DATA_KEY, kind, target, method); } } export function getWhen(target, methodName: string) { - return Reflect.getMetadata(META_DATA_KEY, target, methodName); + return Reflect.getMetadata(META_DATA_KEY, target, methodName); + } +} + +export abstract class TransformerBase { + abstract transform(node, parent:MetaData) + + protected traverse(children: any[], metaData:MetaData, transformers: TransformerBase[]) { + let calls = this.getCalls(transformers) + for(let child of children){ + if(calls.some(x => x == child.type)){ + for(let transformer of transformers){ + transformer.transform(child, metaData) + } + } + } + } + + private getCalls(traversers:TransformerBase[]){ + let calls:string[] = [] + for(let traverser of traversers){ + let cal = Call.getWhen(traverser, "transform"); + calls.push(cal) + } + return calls; } } diff --git a/src/index.ts b/src/index.ts index fcd89af..f3bee1b 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,10 +1,9 @@ -import { MetaDataFactory, MetaData, MetadataType, AnalysisType } from "./core" -import { VisitorRegistry } from "./visitors" +import * as Core from "./core" import { Transformer } from "./transformer" -function transform(ast, fileName) { - let transformer = new Transformer(VisitorRegistry.getVisitor(new MetaDataFactory())) - return transformer.transform(ast, fileName); +function transform(ast, fileName:string) { + let transformer = new Transformer(fileName) + return transformer.transform(ast); } -export{transform, MetaData, MetadataType, AnalysisType} \ No newline at end of file +export{transform, Core} \ No newline at end of file diff --git a/src/transformer.ts b/src/transformer.ts index fee153e..9f9ea0b 100644 --- a/src/transformer.ts +++ b/src/transformer.ts @@ -1,80 +1,33 @@ -import { MetaData, SyntaxKind, AnalysisType } from "./core" -import { VisitorAggregator } from "./visitors" -import { MetaDataAnalyzer } from "./analyzers/metadata-analyzer" - -export class Transformer { - constructor(private visitor: VisitorAggregator) { } - - private traverseArray(array, meta: MetaData, metaParent: MetaData) { - array.forEach((value) => { - this.traverseNode(value, meta, metaParent); - }) - } - - private traverseNode(node, meta: MetaData, metaParent: MetaData) { - if (node == null) return; - //console.log(JSON.stringify(node.loc.start)) - let visitResult = this.visitor.start(node, meta, metaParent); - if (visitResult) { - meta.children.push(visitResult); - metaParent = meta; - } else { - visitResult = meta; - } - - switch (node.type) { - case SyntaxKind.File: - this.traverseNode(node.program, visitResult, metaParent); - break; - - case SyntaxKind.Program: - case SyntaxKind.BlockStatement: - case SyntaxKind.ClassBody: - this.traverseArray(node.body, visitResult, metaParent); - break; - - case SyntaxKind.VariableDeclaration: - this.traverseArray(node.declarations, visitResult, metaParent); - break; - - case SyntaxKind.VariableDeclarator: - this.traverseNode(node.init, visitResult, metaParent); - break; - - case SyntaxKind.CallExpression: - this.traverseNode(node.callee, visitResult, metaParent); - this.traverseArray(node.arguments, visitResult, metaParent); - break; - - case SyntaxKind.FunctionExpression: - case SyntaxKind.FunctionDeclaration: - case SyntaxKind.ClassDeclaration: - case SyntaxKind.ClassExpression: - this.traverseNode(node.body, visitResult, metaParent); - break; - - case SyntaxKind.ExpressionStatement: - this.traverseNode(node.expression, visitResult, metaParent); - break; +import { ClassAnalyzer } from "./analyzers/class-analyzer" +import { MetaData, ParentMetaData, SyntaxKind, Call, TransformerBase, AnalysisType } from "./core" +import { TypeScriptClassTransformer } from "./transformers/ts-class" +import { TsModuleTransformer } from "./transformers/ts-module" + +export class Transformer { + constructor(private fileName: string) {} + transform(ast){ + let file: ParentMetaData = { + type: "File", + name: this.fileName, + analysis: AnalysisType.Valid, + children: [], + location: ast.loc.start } - this.visitor.exit(node, visitResult, metaParent); + let fileTransformer = new FileTransformer(this.fileName) + fileTransformer.transform(ast, file) + return file; } +} - private traverse(node, meta: MetaData, metaParent: MetaData) { - this.traverseNode(node, meta, metaParent) +class FileTransformer extends TransformerBase { + constructor(private fileName: string) { + super() } - - transform(ast, filename: string) { - let result: MetaData = { - type: "File", - name: filename, - analysis: AnalysisType.Candidate, - children: [] - } - this.traverse(ast, result, null); - let analyser = new MetaDataAnalyzer(result, null); - if(analyser.hasValidChildren()) - result.analysis |= AnalysisType.Valid; - return result; + + transform(node, parent: MetaData) { + this.traverse(node.program.body, parent, [ + new TypeScriptClassTransformer(), + new TsModuleTransformer() + ]) } } \ No newline at end of file diff --git a/src/transformers/constructor.ts b/src/transformers/constructor.ts new file mode 100644 index 0000000..0309d85 --- /dev/null +++ b/src/transformers/constructor.ts @@ -0,0 +1,24 @@ +import { MethodAnalyzer } from "../analyzers/method-analyzer" +import { ParameterTransformer } from "./parameter" +import * as Core from "../core" + + +export class ConstructorTransformer extends Core.TransformerBase { + @Core.Call.when(Core.SyntaxKind.ExpressionStatement) + transform(node, parent: Core.ClassMetaData) { + if (node.type == Core.SyntaxKind.FunctionDeclaration + && node.id.name == parent.name) { + let constructor = { + type: "Constructor", + name: node.id.name, + analysis: Core.AnalysisType.Valid, + location: node.loc.start, + parameters: [] + } + parent.constructor = constructor; + this.traverse(node.params, constructor, [ + new ParameterTransformer() + ]) + } + } +} \ No newline at end of file diff --git a/src/transformers/method.ts b/src/transformers/method.ts new file mode 100644 index 0000000..62bb287 --- /dev/null +++ b/src/transformers/method.ts @@ -0,0 +1,25 @@ + +import { MethodAnalyzer } from "../analyzers/method-analyzer" +import { ParameterTransformer } from "./parameter" +import * as Core from "../core" + + +export class MethodTransformer extends Core.TransformerBase { + @Core.Call.when(Core.SyntaxKind.ExpressionStatement) + transform(node, parent: Core.ClassMetaData) { + let analyzer = new MethodAnalyzer(node); + if (analyzer.isMethod(parent.name)) { + let method = { + type: "Method", + name: analyzer.getName(), + analysis: Core.AnalysisType.Valid, + location: node.loc.start, + parameters: [] + } + parent.methods.push(method) + this.traverse(node.expression.right.params, method, [ + new ParameterTransformer() + ]) + } + } +} \ No newline at end of file diff --git a/src/transformers/parameter.ts b/src/transformers/parameter.ts new file mode 100644 index 0000000..829f2d8 --- /dev/null +++ b/src/transformers/parameter.ts @@ -0,0 +1,13 @@ +import * as Core from "../core" + +export class ParameterTransformer extends Core.TransformerBase { + @Core.Call.when(Core.SyntaxKind.Identifier) + transform(node, parent: Core.MethodMetaData | Core.ConstructorMetaData) { + parent.parameters.push({ + type: "Parameter", + name: node.name, + analysis: Core.AnalysisType.Valid, + location: node.loc.start + }) + } +} \ No newline at end of file diff --git a/src/transformers/ts-child-decorator.ts b/src/transformers/ts-child-decorator.ts new file mode 100644 index 0000000..d700c1b --- /dev/null +++ b/src/transformers/ts-child-decorator.ts @@ -0,0 +1,52 @@ +import * as H from "../analyzers/helper" +import * as Core from "../core" + + +export class TsChildDecorator extends Core.TransformerBase { + @Core.Call.when(Core.SyntaxKind.CallExpression) + transform(node, parent: Core.MethodMetaData | Core.ClassMetaData) { + if (!H.isReservedDecorator(node)) { + this.transformMethod(node, parent) + } + else if (H.getMethodNameFromCallee(node.callee) == "__param" + && parent.type == "Method" + && parent.parameters.length > 0) { + let parameter = parent.parameters[parseInt(node.arguments[0].value)] + this.transformParameter(node, parameter) + } + } + + private transformMethod(node, parent: Core.MethodMetaData|Core.ClassMetaData) { + if (!parent.decorators) parent.decorators = [] + let method = { + type: "Decorator", + name: node.callee.name, + analysis: Core.AnalysisType.Valid, + location: node.loc.start, + parameters: node.arguments.map(x => this.getParameter(x)) + } + parent.decorators.push(method) + } + + private transformParameter(node, parameter: Core.ParameterMetaData) { + if (!parameter.decorators) parameter.decorators = [] + let decorator = { + type: "Decorator", + name: H.getMethodNameFromCallee(node.arguments[1].callee), + analysis: Core.AnalysisType.Valid, + location: node.loc.start, + parameters: node.arguments[1].arguments + .map(x => this.getParameter(x)) + }; + parameter.decorators.push(decorator) + } + + private getParameter(x) { + return { + type: "Parameter", + name: H.getDecoratorParameterName(x), + analysis: Core.AnalysisType.Valid, + location: x.loc.start + }; + } +} \ No newline at end of file diff --git a/src/transformers/ts-class.ts b/src/transformers/ts-class.ts new file mode 100644 index 0000000..1519371 --- /dev/null +++ b/src/transformers/ts-class.ts @@ -0,0 +1,36 @@ +import { ClassAnalyzer } from "../analyzers/class-analyzer" +import { MethodTransformer } from "./method" +import { ConstructorTransformer } from "./constructor" +import { ParameterTransformer } from "./parameter" +import * as Core from "../core" + +export class TypeScriptClassTransformer extends Core.TransformerBase { + + transform(node, parent:Core.MetaData) { + let analyzer = new ClassAnalyzer(node); + if (analyzer.isCandidate()) { + let clazz = { + type: "Class", + name: analyzer.getName(), + baseClass: analyzer.getBaseClass(), + location: node.loc.start + } + this.traverse(node.declarations[0].init.callee.body.body, clazz, [ + new MethodTransformer(), + new ConstructorTransformer() + ]) + this.analyse(clazz, parent, analyzer) + } + } + + private analyse(clazz:Core.ClassMetaData, parent:Core.MetaData, analyzer:ClassAnalyzer){ + let hasConstructor = clazz.constructor; + let hasMethods = clazz.methods && clazz.methods.length > 0; + let isExported = analyzer.isExported(clazz.name, parent.name) + if(hasConstructor) clazz.analysis |= Core.AnalysisType.HasConstructor + if(hasMethods) clazz.analysis |= Core.AnalysisType.HasMethod; + if(isExported) clazz.analysis |= Core.AnalysisType.Exported; + if(hasMethods && isExported) clazz.analysis |= Core.AnalysisType.Valid + } +} + diff --git a/src/transformers/ts-decorator.ts b/src/transformers/ts-decorator.ts new file mode 100644 index 0000000..4f75af7 --- /dev/null +++ b/src/transformers/ts-decorator.ts @@ -0,0 +1,37 @@ +import * as H from "../analyzers/helper" +import { ClassDecoratorAnalyzer } from "../analyzers/class-decorator-analyzer" +import { MethodDecoratorAnalyzer } from "../analyzers/method-decorator-analyzer" +import * as Core from "../core" +import { TsChildDecorator } from "./ts-child-decorator" + +export class TsDecorator extends Core.TransformerBase { + @Core.Call.when(Core.SyntaxKind.ExpressionStatement) + transform(node, parent: Core.ParentMetaData) { + if (node.expression.type == Core.SyntaxKind.MemberExpression + && H.getMethodNameFromCallee(node.expression.callee) == "__decorate") { + this.transformMethod(node, parent) + } + else if (node.expression.type == Core.SyntaxKind.AssignmentExpression + && H.getMethodNameFromCallee(node.expression.callee) == "__decorate") { + this.transformClass(node, parent) + } + } + + private transformMethod(node, parent: Core.ParentMetaData) { + let analyzers = new MethodDecoratorAnalyzer(node) + if (analyzers.isDecorator()) { + let methodName = analyzers.getMethodName(); + let className = analyzers.getClassName(); + let clazz = parent.children.filter(x => x.name == className)[0]; + let method = clazz.methods.filter(x => x.name == methodName)[0] + this.traverse(node.expression.arguments, method, [ + new TsChildDecorator() + ]) + } + } + + private transformClass(node, parent: Core.ParentMetaData) { + + } + +} \ No newline at end of file diff --git a/src/transformers/ts-module.ts b/src/transformers/ts-module.ts new file mode 100644 index 0000000..8c3d6ea --- /dev/null +++ b/src/transformers/ts-module.ts @@ -0,0 +1,40 @@ +import { ModuleAnalyzer } from "../analyzers/module-analyzer" +import { TransformerBase, ParentMetaData, MetaData, MethodMetaData, ClassMetaData, SyntaxKind, Call, flag, AnalysisType } from "../core" +import { TypeScriptClassTransformer } from "./ts-class" + +export class TsModuleTransformer extends TransformerBase { + + constructor(private childTransformers?:TransformerBase[]){ + super() + } + + @Call.when(SyntaxKind.ExpressionStatement) + transform(node, parent: ParentMetaData) { + let analyzer = new ModuleAnalyzer(node) + if (analyzer.isCandidate() && (parent.type == "Module" || parent.type == "File")) { + let module: ParentMetaData = { + type: "Module", + analysis: AnalysisType.Candidate, + children: [], + location: node.loc.start, + name: analyzer.getName() + } + parent.children.push(module); + this.traverse(analyzer.getBody(), module, this.childTransformers) + this.analyse(module, parent, analyzer) + } + } + + private analyse(module: ParentMetaData, parent: MetaData, analyzer: ModuleAnalyzer) { + let connected = module.children.length > 0 + && module.children.some(x => flag(x.analysis, AnalysisType.Valid)) + let exported = analyzer.isExported(parent.type == "File" ? "exports" : parent.name); + let analysis = AnalysisType.Candidate; + if (exported) + analysis |= AnalysisType.Exported; + if (connected) + analysis |= AnalysisType.ConnectedWithChildren + if (exported && connected) + analysis |= AnalysisType.Valid + } +} diff --git a/src/visitors/class-decorator-visitor.ts b/src/visitors/class-decorator-visitor.ts deleted file mode 100644 index d6b74f7..0000000 --- a/src/visitors/class-decorator-visitor.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { Call, SyntaxKind, MetaData, AnalysisType, Visitor, MetaDataFactory, flag } from "../core" -import {ClassDecoratorAnalyzer} from "../analyzers/class-decorator-analyzer" - -export class ClassDecoratorVisitor implements Visitor { - constructor(private factory: MetaDataFactory) { } - - start(node, meta: MetaData, metaParent: MetaData): MetaData { - return null - } - - @Call.when(SyntaxKind.AssignmentExpression) - exit(node, meta: MetaData, metaParent: MetaData): MetaData { - let analyzer = new ClassDecoratorAnalyzer(node) - if ((meta.type == "Module" || meta.type == "File") - && analyzer.isDecorator()) { - let className = analyzer.getClassName(); - let decorators = analyzer.getDecorators() - let clazz = meta.children.filter(x => x.type == "Class" - && x.name == className)[0] - if (clazz) clazz.decorators = decorators; - } - return null; - } -} \ No newline at end of file diff --git a/src/visitors/class-visitor.ts b/src/visitors/class-visitor.ts deleted file mode 100644 index 48f9268..0000000 --- a/src/visitors/class-visitor.ts +++ /dev/null @@ -1,134 +0,0 @@ -import { Call, SyntaxKind, MetaData, AnalysisType, Visitor, MetaDataFactory, flag } from "../core" -import { ClassAnalyzer } from "../analyzers/class-analyzer" -import { MethodAnalyzer } from "../analyzers/method-analyzer" - -export class ClassVisitor implements Visitor { - constructor(private factory: MetaDataFactory) { } - - @Call.when( - SyntaxKind.VariableDeclaration, - SyntaxKind.FunctionDeclaration, - SyntaxKind.AssignmentExpression, - SyntaxKind.ClassExpression, - SyntaxKind.ClassMethod - ) - start(node, meta: MetaData, metaParent: MetaData): MetaData { - switch (node.type) { - case SyntaxKind.VariableDeclaration: - return this.createCandidate(node, metaParent); - case SyntaxKind.FunctionDeclaration: - return this.addHasConstructorFlag(node, meta) - case SyntaxKind.ClassExpression: - return this.createEs6Class(node, meta); - case SyntaxKind.ClassMethod: - return this.createEs6Method(node, meta) - default: - return this.addMethod(node, meta) - } - } - - @Call.when(SyntaxKind.AssignmentExpression) - exit(node, meta: MetaData, metaParent: MetaData): MetaData { - return this.addIsExportedFlag(node, meta) - } - - private createCandidate(node, meta: MetaData) { - let analyzer = new ClassAnalyzer(node) - if (analyzer.isCandidate()) { - let result = { - type: "Class", - name: analyzer.getName(), - analysis: AnalysisType.Candidate, - children: [], - }; - return this.factory.create(node, result, meta) - } - return null; - } - - private createEs6Class(node, meta) { - let result = { - type: "Class", - name: node.id.name, - analysis: AnalysisType.Candidate, - children: [], - }; - return this.factory.create(node, result, meta) - } - - private addHasConstructorFlag(node, meta: MetaData) { - if (meta.type == "Class" - && node.type == SyntaxKind.FunctionDeclaration - && node.id.name == meta.name - && flag(meta.analysis, AnalysisType.Candidate)) { - meta.analysis |= AnalysisType.HasConstructor; - - let result = { - type: "Constructor", - name: meta.name, - children: node.params.map(x => { - type: "Parameter", - name: x.name, - analysis: AnalysisType.Valid, - children: [] - }) - } - return this.factory.create(node, result, meta) - } - return null; - } - - private addMethod(node, meta: MetaData) { - let analyzer = new MethodAnalyzer(node) - if (meta.type == "Class" - && analyzer.isMethod(meta.name) - && flag(meta.analysis, AnalysisType.HasConstructor)) { - meta.analysis |= AnalysisType.HasMethod; - let parameters = analyzer.getParams().map(x => { - type: "Parameter", - name: x, - analysis: AnalysisType.Valid, - children: [], - }); - let result = { - type: "Method", - name: analyzer.getName(), - analysis: AnalysisType.Valid, - children: parameters - } - return this.factory.create(node, result, meta) - } - return null; - } - - private createEs6Method(node, meta:MetaData) { - let isctor = node.kind == "constructor"; - meta.analysis |= isctor ? AnalysisType.HasConstructor : AnalysisType.HasMethod - return { - type: isctor ? "Constructor" : "Method", - name: isctor ? meta.name : node.key.name, - children: node.params.map(x => { - type: "Parameter", - name: x.name, - analysis: AnalysisType.Valid, - children: [] - }) - } - } - - private addIsExportedFlag(node, meta: MetaData) { - let analyzer = new ClassAnalyzer(node); - let className = analyzer.getName(); - let clazz = meta.children.filter(x => x.name == className)[0] - if (clazz - && (meta.type == "Module" || meta.type == "File") - && analyzer.isExported(className, meta.name)) { - clazz.analysis |= AnalysisType.Exported; - if (flag(clazz.analysis, AnalysisType.HasConstructor) - && flag(clazz.analysis, AnalysisType.HasMethod)) { - clazz.analysis |= AnalysisType.Valid; - } - } - return null; - } -} diff --git a/src/visitors/index.ts b/src/visitors/index.ts deleted file mode 100644 index 971cbb5..0000000 --- a/src/visitors/index.ts +++ /dev/null @@ -1,76 +0,0 @@ -import { Visitor, Call, MetaData, MetaDataFactory } from "../core" -import { ClassVisitor } from "./class-visitor" -import { ModuleVisitor } from "./module-visitor" -import { ClassDecoratorVisitor } from "./class-decorator-visitor" -import { MethodDecoratorVisitor } from "./method-decorator-visitor" - -export type CallLocation = "start" | "exit" - -export class AggregatorStore { - start: { [key: string]: CallAggregator } = {} - exit: { [key: string]: CallAggregator } = {} -} - -export class CallAggregator { - visitors: Visitor[] = [] - constructor(private location: CallLocation) { } - - addVisitor(visitor: Visitor) { - this.visitors.push(visitor) - } - - visit(node, meta: MetaData, metaParent: MetaData) { - for (let visit of this.visitors) { - let result = visit[this.location](node, meta, metaParent) - if (result) return result; - } - return null; - } -} - -export class VisitorAggregator implements Visitor { - store: AggregatorStore = new AggregatorStore(); - - constructor(private visitors: Visitor[]) { - this.visitors.forEach(visitor => { - this.initAggregator(visitor, "start") - this.initAggregator(visitor, "exit") - }) - } - - private initAggregator(visitor: Visitor, location: CallLocation) { - let options = Call.getWhen(visitor, location); - if (options) { - options.forEach(syntaxKind => { - if (!this.store[location][syntaxKind]) - this.store[location][syntaxKind] = new CallAggregator(location); - this.store[location][syntaxKind].addVisitor(visitor); - }) - } - } - - private visit(location: CallLocation, node, meta: MetaData, metaParent: MetaData) { - let aggregator = this.store[location][node.type] - if (aggregator) return aggregator.visit(node, meta, metaParent) - return null; - } - - start(node, meta: MetaData, metaParent: MetaData): MetaData { - return this.visit("start", node, meta, metaParent); - } - - exit(node, meta: MetaData, metaParent: MetaData): MetaData { - return this.visit("exit", node, meta, metaParent); - } -} - -export module VisitorRegistry { - export function getVisitor(factory: MetaDataFactory) { - return new VisitorAggregator([ - new ClassVisitor(factory), - new ModuleVisitor(factory), - new ClassDecoratorVisitor(factory), - new MethodDecoratorVisitor(factory) - ]) - } -} diff --git a/src/visitors/method-decorator-visitor.ts b/src/visitors/method-decorator-visitor.ts deleted file mode 100644 index 5d7cd8d..0000000 --- a/src/visitors/method-decorator-visitor.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { Call, SyntaxKind, MetaData, AnalysisType, Visitor, MetaDataFactory, flag } from "../core" -import {MethodDecoratorAnalyzer} from "../analyzers/method-decorator-analyzer" - -export class MethodDecoratorVisitor implements Visitor { - constructor(private factory: MetaDataFactory) { } - - start(node, meta: MetaData, metaParent: MetaData): MetaData { - return null - } - - @Call.when(SyntaxKind.CallExpression) - exit(node, meta: MetaData, metaParent: MetaData): MetaData { - let analyzer = new MethodDecoratorAnalyzer(node) - if (analyzer.isDecorator()) { - let className = analyzer.getClassName(); - let methodName = analyzer.getMethodName() - let decorators = analyzer.getDecorators() - let clazz = meta.children.filter(x => x.type == "Class" - && x.name == className)[0] - if (clazz) { - let method = clazz.children.filter(x => x.type == "Method" - && x.name == methodName)[0] - if(method) { - method.decorators = decorators; - for(let i = 0; i < method.children.length; i++){ - method.children[0].decorators = analyzer.getParameters(i); - } - } - } - } - return null; - } -} \ No newline at end of file diff --git a/src/visitors/module-visitor.ts b/src/visitors/module-visitor.ts deleted file mode 100644 index ab4d0f7..0000000 --- a/src/visitors/module-visitor.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { Call, SyntaxKind, MetaData, AnalysisType, Visitor, MetaDataFactory, flag } from "../core" -import {ModuleAnalyzer} from "../analyzers/module-analyzer" -import { MetaDataAnalyzer } from "../analyzers/metadata-analyzer" - -export class ModuleVisitor implements Visitor { - constructor(private factory: MetaDataFactory) { } - - @Call.when(SyntaxKind.CallExpression) - start(node, meta: MetaData, metaParent: MetaData): MetaData { - let analyzer = new ModuleAnalyzer(node) - if (analyzer.isCandidate()) { - let result = { - type: "Module", - name: analyzer.getName(), - analysis: AnalysisType.Candidate, - children: [], - } - return this.factory.create(node, result, meta) - } - return null; - } - - @Call.when(SyntaxKind.CallExpression) - exit(node, meta: MetaData, metaParent: MetaData): MetaData { - let metaAnalizer = new MetaDataAnalyzer(meta, metaParent) - let analyzer = new ModuleAnalyzer(node) - //module is connected with its children - if (metaAnalizer.hasExportedChildren()) - meta.analysis |= AnalysisType.ConnectedWithChildren; - //module is exported - if (analyzer.isExported(metaParent ? metaParent.name : "")) - meta.analysis |= AnalysisType.Exported; - //valid if connected & exported - if (flag(meta.analysis, AnalysisType.ConnectedWithChildren) - && flag(meta.analysis, AnalysisType.Exported)) - meta.analysis |= AnalysisType.Valid; - - return null; - } - -} \ No newline at end of file diff --git a/test/analyzers/class-analyzer.spec.ts b/test/analyzers/class-analyzer.spec.ts index 6a997d1..24a94df 100644 --- a/test/analyzers/class-analyzer.spec.ts +++ b/test/analyzers/class-analyzer.spec.ts @@ -5,7 +5,6 @@ import * as Chai from "chai" describe("Class Analyzer", () => { - describe("isExported", () => { it("Should identify exported class with module", () => { let ast = JsParser.getAst(`MyModule.MyClass = MyClass`) @@ -51,11 +50,29 @@ describe("Class Analyzer", () => { Chai.expect(dummy.getName()).eq("MyClass"); }) - it("getName() should not error on any syntax", () => { + it("Should not error on any syntax", () => { let ast = JsParser.getAst(` var MyClass = null`) let dummy = new ClassAnalyzer(ast) Chai.expect(dummy.getName()).null; }) }) + + describe("getBaseClass", () => { + it("Should return class name properly", () => { + let ast = JsParser.getAst(` + var MyClass = (function (_super) { + }(MyBaseClass));`) + let dummy = new ClassAnalyzer(ast) + Chai.expect(dummy.getBaseClass()).eq("MyBaseClass"); + }) + + it("Should not error on class that has no base class", () => { + let ast = JsParser.getAst(` + var MyClass = (function () { + }());`) + let dummy = new ClassAnalyzer(ast) + Chai.expect(dummy.getBaseClass()).undefined; + }) + }) }) \ No newline at end of file diff --git a/test/analyzers/class-decorator-analyzer.spec.ts b/test/analyzers/class-decorator-analyzer.spec.ts index bef4e6e..5c6ac0e 100644 --- a/test/analyzers/class-decorator-analyzer.spec.ts +++ b/test/analyzers/class-decorator-analyzer.spec.ts @@ -1,6 +1,6 @@ import { AnalysisType, MetaData } from "../../src/core" import { ClassDecoratorAnalyzer } from "../../src/analyzers/class-decorator-analyzer" -import { JsParser, MH } from "../helper" +import { JsParser } from "../helper" import * as Chai from "chai" @@ -49,26 +49,4 @@ describe("Class Decorator Analyzer", () => { Chai.expect(dummy.getClassName()).null; }) }) - - describe("getDecorators", () => { - it("Should return decorators properly", () => { - let ast = JsParser.getAst(` - MyClass = tslib_1.__decorate([ - decoOne(), - tslib_1.__metadata("design:paramtypes", []) - ], MyClass); - `) - let dummy = new ClassDecoratorAnalyzer(ast.expression) - let result = dummy.getDecorators(); - Chai.expect(MH.validate(result[0], { type: "Decorator", name: "decoOne" }, null)).true - }) - - it("getDecorators() Should not error when supplied other syntax", () => { - let ast = JsParser.getAst(` - $(function(){}) - `) - let dummy = new ClassDecoratorAnalyzer(ast) - Chai.expect(dummy.getDecorators()).null; - }) - }) }) \ No newline at end of file diff --git a/test/analyzers/helper.spec.ts b/test/analyzers/helper.spec.ts new file mode 100644 index 0000000..9b75eae --- /dev/null +++ b/test/analyzers/helper.spec.ts @@ -0,0 +1,39 @@ +import { AnalysisType, MetaData, SyntaxKind } from "../../src/core" +import * as Helper from "../../src/analyzers/helper" +import { JsParser } from "../helper" +import * as Chai from "chai" + +describe("Analyzer helper", () => { + describe("getMethodNameFromCallee", () => { + it("Should return method name with member access", () => { + let ast = JsParser.getAst(`tslib.__decorate()`) + let result = Helper.getMethodNameFromCallee(ast.expression.callee) + Chai.expect(result).eq("__decorate"); + }) + it("Should return method name", () => { + let ast = JsParser.getAst(`__decorate()`) + let result = Helper.getMethodNameFromCallee(ast.expression.callee) + Chai.expect(result).eq("__decorate"); + }) + }) + + describe("isReservedDecorator", () => { + it("Should identify tslib __decorate decorator", () => { + let ast = JsParser.getAst(`tslib.__decorate()`) + let result = Helper.isReservedDecorator(ast.expression) + Chai.expect(result).eq(true); + }) + it("Should identify tslib __param decorator", () => { + let ast = JsParser.getAst(`tslib.__param()`) + let result = Helper.isReservedDecorator(ast.expression) + Chai.expect(result).eq(true); + }) + it("Should identify tslib __metadata decorator", () => { + let ast = JsParser.getAst(`tslib.__metadata()`) + let result = Helper.isReservedDecorator(ast.expression) + Chai.expect(result).eq(true); + }) + }) + + +}) \ No newline at end of file diff --git a/test/analyzers/metadata-analyzer.spec.ts b/test/analyzers/metadata-analyzer.spec.ts deleted file mode 100644 index 8158031..0000000 --- a/test/analyzers/metadata-analyzer.spec.ts +++ /dev/null @@ -1,126 +0,0 @@ - -import { AnalysisType, MetaData } from "../../src/core" -import { MetaDataAnalyzer } from "../../src/analyzers/metadata-analyzer" -import { JsParser } from "../helper" -import * as Chai from "chai" - - -describe("MetaData analyzer", () => { - it("Should identify exported children properly", () => { - let meta: MetaData = { - type: "Module", - name: "InnerModule", - analysis: AnalysisType.Candidate, - children: [{ - type: "Class", - name: "MyClass", - analysis: AnalysisType.Candidate - | AnalysisType.HasConstructor - | AnalysisType.HasMethod - | AnalysisType.Exported, - children: [] - }], - } - let test = new MetaDataAnalyzer(meta, null) - Chai.expect(test.hasExportedChildren()).true; - }) - - it("Should identify exported module children properly", () => { - let meta: MetaData = { - type: "Module", - name: "InnerModule", - analysis: AnalysisType.Candidate, - children: [{ - type: "Module", - name: "InnerInnerModule", - analysis: AnalysisType.Candidate - | AnalysisType.Exported, - children: [] - },{ - type: "Method", - name: "myMethod", - analysis: AnalysisType.Candidate, - children: [] - }], - } - let test = new MetaDataAnalyzer(meta, null) - Chai.expect(test.hasExportedChildren()).true; - }) - - it("Should identify exported children, even only one children", () => { - let meta: MetaData = { - type: "Module", - name: "InnerModule", - analysis: AnalysisType.Candidate, - children: [{ - type: "Class", - name: "MyClass", - analysis: AnalysisType.Candidate - | AnalysisType.HasConstructor - | AnalysisType.HasMethod - | AnalysisType.Exported, - children: [] - }, { - type: "Class", - name: "MyOtherClass", - analysis: AnalysisType.Candidate, - children: [] - }], - } - let test = new MetaDataAnalyzer(meta, null) - Chai.expect(test.hasExportedChildren()).true; - }) - - it("Should return false if has no children", () => { - let meta: MetaData = { - type: "Module", - name: "InnerModule", - children: [], - analysis: AnalysisType.Candidate - } - let test = new MetaDataAnalyzer(meta, null) - Chai.expect(test.hasExportedChildren()).false; - }) - - it("Should return false if all children not exported", () => { - let meta: MetaData = { - type: "Module", - name: "InnerModule", - children: [{ - type: "Class", - name: "MyClass", - analysis: AnalysisType.Candidate, - children: [] - }, { - type: "Class", - name: "MyOtherClass", - analysis: AnalysisType.Candidate, - children: [] - }], - analysis: AnalysisType.Candidate - } - let test = new MetaDataAnalyzer(meta, null) - Chai.expect(test.hasExportedChildren()).false; - }) - - it("Should identify valid children", () => { - let meta: MetaData = { - type: "Module", - name: "InnerModule", - analysis: AnalysisType.Candidate, - children: [{ - type: "Class", - name: "MyClass", - analysis: AnalysisType.Valid, - children: [] - }, { - type: "Class", - name: "MyOtherClass", - analysis: AnalysisType.Valid, - children: [] - }], - } - let test = new MetaDataAnalyzer(meta, null) - Chai.expect(test.hasValidChildren()).true; - }) -}) \ No newline at end of file diff --git a/test/analyzers/method-analyzer.spec.ts b/test/analyzers/method-analyzer.spec.ts index 58fcd4d..07bb224 100644 --- a/test/analyzers/method-analyzer.spec.ts +++ b/test/analyzers/method-analyzer.spec.ts @@ -7,13 +7,13 @@ import * as Chai from "chai" describe("Method Analyzer", () => { it("Should identify method candidate", () => { let ast = JsParser.getAst(`MyClass.prototype.myMethod = function(){}`) - let dummy = new MethodAnalyzer(ast.expression) + let dummy = new MethodAnalyzer(ast) Chai.expect(dummy.isMethod("MyClass")).true; }) it("Should return method name properly", () => { let ast = JsParser.getAst(`MyClass.prototype.myMethod = function(){}`) - let dummy = new MethodAnalyzer(ast.expression) + let dummy = new MethodAnalyzer(ast) Chai.expect(dummy.getName()).eq("myMethod"); }) @@ -25,7 +25,7 @@ describe("Method Analyzer", () => { it("Should return class name properly", () => { let ast = JsParser.getAst(`MyClass.prototype.myMethod = function(){}`) - let dummy = new MethodAnalyzer(ast.expression) + let dummy = new MethodAnalyzer(ast) Chai.expect(dummy.getClassName()).eq("MyClass"); }) @@ -37,7 +37,7 @@ describe("Method Analyzer", () => { it("Should return method parameters properly", () => { let ast = JsParser.getAst(`MyClass.prototype.myMethod = function(par1, par2){}`) - let dummy = new MethodAnalyzer(ast.expression) + let dummy = new MethodAnalyzer(ast) let params = dummy.getParams(); Chai.expect(params[0]).eq("par1"); Chai.expect(params[1]).eq("par2"); diff --git a/test/analyzers/method-decorator-analyzer.spec.ts b/test/analyzers/method-decorator-analyzer.spec.ts index 91283f1..150e8f2 100644 --- a/test/analyzers/method-decorator-analyzer.spec.ts +++ b/test/analyzers/method-decorator-analyzer.spec.ts @@ -1,6 +1,6 @@ import { AnalysisType, MetaData } from "../../src/core" import { MethodDecoratorAnalyzer } from "../../src/analyzers/method-decorator-analyzer" -import { JsParser, MH } from "../helper" +import { JsParser} from "../helper" import * as Chai from "chai" @@ -34,34 +34,6 @@ describe("Method Decorator Analyzer", () => { }) }) - describe("getDecorators", () => { - it("Should return decorator properly", () => { - let ast = JsParser.getAst(` - tslib_1.__decorate([ - decoOne(), - decoTwo("hello world!"), - tslib_1.__metadata("design:type", Function), - tslib_1.__metadata("design:paramtypes", []), - tslib_1.__metadata("design:returntype", void 0) - ], MyClass.prototype, "myMethod", null); - `) - let dummy = new MethodDecoratorAnalyzer(ast.expression) - let result = dummy.getDecorators(); - Chai.expect(MH.validate(result[0], { type: "Decorator", name: "decoOne" }, null)).eq(true); - Chai.expect(MH.validate(result[1], { type: "Decorator", name: "decoTwo" }, null)).eq(true); - Chai.expect(MH.validate(result[1].children[0], { type: "Parameter", name: "hello world!" }, null)).eq(true); - }) - - it("Should not error when provided different syntax", () => { - let ast = JsParser.getAst(` - var MyClass = null - `) - let dummy = new MethodDecoratorAnalyzer(ast) - let result = dummy.getDecorators(); - Chai.expect(result).null - }) - }) - describe("getClassName", () => { it("Should return class name properly", () => { let ast = JsParser.getAst(` @@ -110,85 +82,4 @@ describe("Method Decorator Analyzer", () => { }) }) - describe("getParameters", () => { - it("Should return parameter decorators properly", () => { - let ast = JsParser.getAst(` - tslib_1.__decorate([ - decoOne(), - tslib_1.__param(0, decoOne()), - ], MyClass.prototype, "myMethod", null); - `) - let dummy = new MethodDecoratorAnalyzer(ast.expression) - let result = dummy.getParameters(0) - Chai.expect(MH.validate(result[0], { type: "Decorator", name: "decoOne" }, null)).eq(true); - }) - - it("Should not error when provided different syntax", () => { - let ast = JsParser.getAst(` - var MyClass = null - `) - let dummy = new MethodDecoratorAnalyzer(ast) - Chai.expect(dummy.getParameters(0)).null - }) - - it("Should return parameter decorators with parameter properly", () => { - let ast = JsParser.getAst(` - tslib_1.__decorate([ - decoOne(), - tslib_1.__param(0, decoOne("hello world!")), - ], MyClass.prototype, "myMethod", null); - `) - let dummy = new MethodDecoratorAnalyzer(ast.expression) - let result = dummy.getParameters(0) - Chai.expect(MH.validate(result[0].children[0], { type: "Parameter", name: "hello world!" }, null)).eq(true); - }) - - it("Should return parameter decorators with identifier parameter properly", () => { - let ast = JsParser.getAst(` - tslib_1.__decorate([ - decoOne(), - tslib_1.__param(0, decoOne(MyClass)), - ], MyClass.prototype, "myMethod", null); - `) - let dummy = new MethodDecoratorAnalyzer(ast.expression) - let result = dummy.getParameters(0) - Chai.expect(MH.validate(result[0].children[0], { type: "Parameter", name: "MyClass" }, null)).eq(true); - }) - - it("Should return parameter decorators with numeric parameter properly", () => { - let ast = JsParser.getAst(` - tslib_1.__decorate([ - decoOne(), - tslib_1.__param(0, decoOne(200)), - ], MyClass.prototype, "myMethod", null); - `) - let dummy = new MethodDecoratorAnalyzer(ast.expression) - let result = dummy.getParameters(0) - Chai.expect(MH.validate(result[0].children[0], { type: "Parameter", name: 200 }, null)).eq(true); - }) - - it("Should return parameter decorators with boolean parameter properly", () => { - let ast = JsParser.getAst(` - tslib_1.__decorate([ - decoOne(), - tslib_1.__param(0, decoOne(false)), - ], MyClass.prototype, "myMethod", null); - `) - let dummy = new MethodDecoratorAnalyzer(ast.expression) - let result = dummy.getParameters(0) - Chai.expect(MH.validate(result[0].children[0], { type: "Parameter", name: false }, null)).eq(true); - }) - - it("Should not error when supplied with function call", () => { - let ast = JsParser.getAst(` - tslib_1.__decorate([ - decoOne(), - tslib_1.__param(0, decoOne(myFunction())), - ], MyClass.prototype, "myMethod", null); - `) - let dummy = new MethodDecoratorAnalyzer(ast.expression) - let result = dummy.getParameters(0) - Chai.expect(MH.validate(result[0].children[0], { type: "Parameter", name: "Unknown" }, null)).eq(true); - }) - }) }) \ No newline at end of file diff --git a/test/analyzers/module-analyzer.spec.ts b/test/analyzers/module-analyzer.spec.ts index 4ca6d5b..69c0050 100644 --- a/test/analyzers/module-analyzer.spec.ts +++ b/test/analyzers/module-analyzer.spec.ts @@ -1,17 +1,17 @@ -import { AnalysisType, MetaData } from "../../src/core" +import { AnalysisType, MetaData, SyntaxKind } from "../../src/core" import { ModuleAnalyzer } from "../../src/analyzers/module-analyzer" import { JsParser } from "../helper" import * as Chai from "chai" -describe("Call Expression Analyzer", () => { +describe("Module Analyzer", () => { describe("isCandidate", () => { it("Should identify module candidate properly", () => { let ast = JsParser.getAst(` (function (Children) { })(Children = Parent.Children || (Parent.Children = {}));`) - let dummy = new ModuleAnalyzer(ast.expression); + let dummy = new ModuleAnalyzer(ast); Chai.expect(dummy.isCandidate()).true; }) @@ -19,7 +19,7 @@ describe("Call Expression Analyzer", () => { let ast = JsParser.getAst(` (function (Children) { })(Children || (Children = {}));`) - let dummy = new ModuleAnalyzer(ast.expression); + let dummy = new ModuleAnalyzer(ast); Chai.expect(dummy.isCandidate()).eq(true); }) @@ -27,7 +27,7 @@ describe("Call Expression Analyzer", () => { let ast = JsParser.getAst(` (function () { })();`) - let dummy = new ModuleAnalyzer(ast.expression); + let dummy = new ModuleAnalyzer(ast); Chai.expect(dummy.isCandidate()).false; }) }) @@ -37,7 +37,7 @@ describe("Call Expression Analyzer", () => { let ast = JsParser.getAst(` (function (Children) { })(Children = Parent.Children || (Parent.Children = {}));`) - let dummy = new ModuleAnalyzer(ast.expression); + let dummy = new ModuleAnalyzer(ast); Chai.expect(dummy.getName()).eq("Children"); }) @@ -49,12 +49,23 @@ describe("Call Expression Analyzer", () => { }) }) + describe("getBody()", () => { + it("Should get module body properly", () => { + let ast = JsParser.getAst(` + (function (Children) { + (function(){}) + })(Children = Parent.Children || (Parent.Children = {}));`) + let dummy = new ModuleAnalyzer(ast); + Chai.expect(dummy.getBody().length).eq(1); + }) + }) + describe("isExported", () => { it("Should identify exported module", () => { let ast = JsParser.getAst(` (function (Children) { })(Children = exports.Children || (exports.Children = {}));`) - let dummy = new ModuleAnalyzer(ast.expression); + let dummy = new ModuleAnalyzer(ast); Chai.expect(dummy.isExported("")).eq(true); }) @@ -62,7 +73,7 @@ describe("Call Expression Analyzer", () => { let ast = JsParser.getAst(` (function (Children) { })(Children = Parent.Children || (Parent.Children = {}));`) - let dummy = new ModuleAnalyzer(ast.expression); + let dummy = new ModuleAnalyzer(ast); Chai.expect(dummy.isExported("Parent")).eq(true); }) @@ -70,7 +81,7 @@ describe("Call Expression Analyzer", () => { let ast = JsParser.getAst(` (function (Children) { })(Children || (Children = {}));`) - let dummy = new ModuleAnalyzer(ast.expression); + let dummy = new ModuleAnalyzer(ast); Chai.expect(dummy.isExported("Parent")).eq(false); }) @@ -78,7 +89,7 @@ describe("Call Expression Analyzer", () => { let ast = JsParser.getAst(` require('tslib'); `) - let dummy = new ModuleAnalyzer(ast.expression); + let dummy = new ModuleAnalyzer(ast); Chai.expect(dummy.isExported("")).eq(false); }) }) diff --git a/test/helper.ts b/test/helper.ts index 39f326d..bc855c4 100644 --- a/test/helper.ts +++ b/test/helper.ts @@ -8,80 +8,3 @@ export module JsParser { return complete ? ast : ast.program.body[0]; } } - -export module MH { - export function validate(meta: MetaData, properties, flags: number[]) { - let result = true; - if (properties) { - for (let prop in properties) { - if (meta[prop] != properties[prop]) - result = false; - } - } - if (flags && flags.length > 0) { - for (let fl of flags) { - if (!flag(meta.analysis, fl)) - result = false; - } - } - return result; - } - - export function validateParameters(meta: MetaData[], param: string[]) { - let validParams = true; - for (let i = 0; i < meta.length; i++) { - let result = validate(meta[i], { type: "Parameter", name: param[i] }, null) - if (!result) { - console.error(`Validation Error: Parameter ${param[i]} is invalid`) - validParams = false; - } - } - return validParams; - } - - export function validateMethod(meta: MetaData, name: string, param: string[]) { - let validMethod = validate(meta, { type: "Method", name: name }, null); - let validParams = validateParameters(meta.children, param) - if(!validMethod){ - console.error(`Validation Error: Method ${name} is not valid`) - } - return validMethod && validParams; - } - - export function validateConstructor(meta: MetaData, param: string[]) { - let validMethod = validate(meta.children[0], { type: "Constructor", name: meta.name }, null); - let validParams = validateParameters(meta.children[0].children, param) - if(!validMethod){ - console.error(`Validation Error: Constructor ${meta.name} is not valid`) - } - return validMethod && validParams; - } - export interface ClassValidationOption{ - name:string, - constructorParams?: string[], - methods?: Array<{name:string, params:string[]}> - flags?: number[] - } - export function validateClass(meta: MetaData, opt:ClassValidationOption) { - let validMethods = true; - let validFlag = true; - let validConstructor = true; - let validClass = validate(meta, { type: "Class", name: opt.name }, null); - if(opt.constructorParams){ - let result = validateConstructor(meta, opt.constructorParams) - if(!result) validConstructor = false - } - if (opt.methods) { - for (let i = 0; i < opt.methods.length; i++) { - let mtd = opt.methods[i] - let validMethod = validateMethod(meta.children[i + 1], mtd.name, mtd.params) - if (!validMethod) validMethods = false; - } - } - if(opt.flags){ - let result = validate(meta, null, opt.flags) - if(!result) validFlag = false - } - return validClass && validMethods && validConstructor && validFlag; - } -} \ No newline at end of file diff --git a/test/myclass.ts b/test/myclass.ts index 9b8da21..e91ffb4 100644 --- a/test/myclass.ts +++ b/test/myclass.ts @@ -9,9 +9,25 @@ function decoTwo(param: string) { export module MyModule { @decoTwo("helo world!") - export class MyClass { + export class MyBaseClass { constructor() { } @decoOne() myMethod( @decoOne() par1) { } } + export module ChildModule { + export class ChildClass extends MyBaseClass { + childMethod() { } + } + } + +} + +export class OuterClass{ + myOtherMethod(){} } + +export module ParentModule.ChildModule{ + export class MyClass{} +} + + diff --git a/test/transformer.spec.ts b/test/transformer.spec.ts deleted file mode 100644 index c9c3483..0000000 --- a/test/transformer.spec.ts +++ /dev/null @@ -1,147 +0,0 @@ -import { Transformer } from "../src/transformer" -import { VisitorRegistry } from "../src/visitors" -import { JsParser, MH } from "./helper" -import { flag, AnalysisType, MetaDataFactory } from "../src/core" -import * as Chai from "chai" - -describe("Transformer", () => { - it("Should transform class properly", () => { - let ast = JsParser.getAst(` - var MyClass = (function () { - function MyClass(par1, par2) { - } - MyClass.prototype.myMethod = function (par1, par2) { }; - return MyClass; - }()); - exports.MyClass = MyClass;`, true) - let test = new Transformer(VisitorRegistry.getVisitor(new MetaDataFactory())); - let result = test.transform(ast, ""); - //verify the root result is valid - Chai.expect(MH.validate(result, null, [AnalysisType.Valid])).true - //verify the class is valid - Chai.expect(MH.validateClass(result.children[0], { - name: "MyClass", - constructorParams: ["par1", "par2"], - methods: [ - { - name: "myMethod", - params: ["par1", "par2"] - }], - flags: [AnalysisType.Valid, - AnalysisType.HasMethod, - AnalysisType.HasConstructor, - AnalysisType.Exported] - })).true; - }) - - it("Should transform class with module properly", () => { - let ast = JsParser.getAst(` - var MyModule; - (function (MyModule) { - var MyClass = (function () { - function MyClass() { - } - MyClass.prototype.myMethod = function (par1, par2) { }; - return MyClass; - }()); - MyModule.MyClass = MyClass; - })(MyModule = exports.MyModule || (exports.MyModule = {}));`, true) - let test = new Transformer(VisitorRegistry.getVisitor(new MetaDataFactory())); - let result = test.transform(ast, ""); - //verify the root result is valid - Chai.expect(MH.validate(result, null, [AnalysisType.Valid])).true - //verify the module is valid - Chai.expect(MH.validate(result.children[0], { type: "Module", name: "MyModule" }, - [AnalysisType.Valid, AnalysisType.ConnectedWithChildren, AnalysisType.Exported])).true - //verify the class is valid - Chai.expect(MH.validateClass(result.children[0].children[0], { - name: "MyClass", - methods: [ - { - name: "myMethod", - params: ["par1", "par2"] - }], - flags: [AnalysisType.Valid, AnalysisType.HasMethod, AnalysisType.HasConstructor] - })).true; - }) - - it("Should identify non exported children", () => { - let ast = JsParser.getAst(` - var MyModule; - (function (MyModule) { - var MyClass = (function () { - function MyClass() { - } - MyClass.prototype.myMethod = function (par1, par2) { }; - return MyClass; - }()); //<------- MyClass is not exported - })(MyModule = exports.MyModule || (exports.MyModule = {}));`, true) - let test = new Transformer(VisitorRegistry.getVisitor(new MetaDataFactory())); - let result = test.transform(ast, ""); - //verify root result is not valid - Chai.expect(flag(result.analysis, AnalysisType.Valid)).eq(false) - //verify the module also is not valid - Chai.expect(flag(result.children[0].analysis, AnalysisType.Valid)).false; - //verify the module is not connected with children - Chai.expect(flag(result.children[0].analysis, AnalysisType.ConnectedWithChildren)).false; - //verify the class is not valid - Chai.expect(flag(result.children[0].children[0].analysis, AnalysisType.Valid)).false; - //verify the class is not exported - Chai.expect(flag(result.children[0].children[0].analysis, AnalysisType.Exported)).false; - }) - - it("Should identify decorator properly", () => { - let ast = JsParser.getAst(` - var MyClass = (function () { - function MyClass() { - } - MyClass.prototype.myMethod = function (par1) { }; - return MyClass; - }()); - tslib_1.__decorate([ - decoOne(), - tslib_1.__param(0, decoOne()) - ], MyClass.prototype, "myMethod", null); - MyClass = tslib_1.__decorate([ - decoOne(), - decoTwo("hello world!") - ], MyClass);`, true) - let test = new Transformer(VisitorRegistry.getVisitor(new MetaDataFactory())); - let result = test.transform(ast, ""); - Chai.expect(result.children[0].decorators.length).eq(2); - //verify class decorator 1 - Chai.expect(MH.validate(result.children[0].decorators[0], { type: "Decorator", name: "decoOne" }, null)).true - //verify class decorator 2 - Chai.expect(MH.validate(result.children[0].decorators[1], { type: "Decorator", name: "decoTwo" }, null)).true - //verify class decoartor 2 parameter - Chai.expect(MH.validate(result.children[0].decorators[1].children[0], { type: "Parameter", name: "hello world!" }, null)).true - //verify method decorator - Chai.expect(MH.validate(result.children[0].children[1].decorators[0], { type: "Decorator", name: "decoOne" }, null)).true - //verify parameter decorator - Chai.expect(MH.validate(result.children[0].children[1].children[0].decorators[0], { type: "Decorator", name: "decoOne" }, null)).true - }) - - it("Should transform ES6 class properly", () => { - let ast = JsParser.getAst(` - let MyClass = class MyClass { - constructor(par1, par2) { } - myMethod(par1, par2) { } - }; - exports.MyClass = MyClass;`, true) - let test = new Transformer(VisitorRegistry.getVisitor(new MetaDataFactory())); - let result = test.transform(ast, ""); - //verify the root result is valid - Chai.expect(MH.validate(result, null, [AnalysisType.Valid])).true - //verify the class is valid - Chai.expect(MH.validateClass(result.children[0], { - name: "MyClass", - constructorParams: ["par1", "par2"], - methods: [ - { - name: "myMethod", - params: ["par1", "par2"] - }], - flags: [AnalysisType.Valid, AnalysisType.HasMethod, AnalysisType.HasConstructor, AnalysisType.Exported] - })).true; - }) -}) \ No newline at end of file diff --git a/test/transformers/constructor.spec.ts b/test/transformers/constructor.spec.ts new file mode 100644 index 0000000..db446d9 --- /dev/null +++ b/test/transformers/constructor.spec.ts @@ -0,0 +1,37 @@ +import * as Core from "../../src/core" +import { ConstructorTransformer } from "../../src/transformers/constructor" +import { JsParser } from "../helper" +import * as Chai from "chai" + + +describe("ConstructorTransformer", () => { + + it("Should identify method properly", () => { + let ast = JsParser.getAst(` + function MyClass(par1) { }; + `) + let dummy = new ConstructorTransformer(); + let parent = { + type: "Class", + name: "MyClass", + analysis: Core.AnalysisType.Valid, + } + dummy.transform(ast, parent); + Chai.expect(parent.constructor).deep.eq({ + type: "Constructor", + name: "MyClass", + analysis: Core.AnalysisType.Valid, + location: { + column: 8, line: 2 + }, + parameters: [{ + type: "Parameter", + name: "par1", + location: { + column: 25, line: 2 + }, + analysis: Core.AnalysisType.Valid, + }] + }) + }) +}) \ No newline at end of file diff --git a/test/transformers/method.spec.ts b/test/transformers/method.spec.ts new file mode 100644 index 0000000..2cabd0c --- /dev/null +++ b/test/transformers/method.spec.ts @@ -0,0 +1,38 @@ +import * as Core from "../../src/core" +import { MethodTransformer } from "../../src/transformers/method" +import { JsParser } from "../helper" +import * as Chai from "chai" + + +describe("MethodTransformer", () => { + + it("Should identify method properly", () => { + let ast = JsParser.getAst(` + MyClass.prototype.myMethod = function (par1) { }; + `) + let dummy = new MethodTransformer(); + let parent = { + type: "Class", + name: "MyClass", + analysis: Core.AnalysisType.Valid, + methods: [] + } + dummy.transform(ast, parent); + Chai.expect(parent.methods[0]).deep.eq({ + type: "Method", + name: "myMethod", + analysis: Core.AnalysisType.Valid, + location: { + column: 8, line: 2 + }, + parameters: [{ + type: "Parameter", + name: "par1", + location: { + column: 47, line: 2 + }, + analysis: Core.AnalysisType.Valid, + }] + }) + }) +}) \ No newline at end of file diff --git a/test/transformers/parameter.spec.ts b/test/transformers/parameter.spec.ts new file mode 100644 index 0000000..9597fab --- /dev/null +++ b/test/transformers/parameter.spec.ts @@ -0,0 +1,28 @@ +import * as Core from "../../src/core" +import { ParameterTransformer } from "../../src/transformers/parameter" +import { JsParser } from "../helper" +import * as Chai from "chai" + + +describe("ParameterTransformer", () => { + + it("Should identify parameters properly", () => { + let ast = JsParser.getAst(`function MyFunction(par1, par2){}`) + let dummy = new ParameterTransformer(); + let parent = { + type: "Method", + name: "MyMethod", + analysis: Core.AnalysisType.Valid, + parameters: [] + } + dummy.transform(ast.params[0], parent); + Chai.expect(parent.parameters[0]).deep.eq({ + type: "Parameter", + name: "par1", + location: { + column: 20, line: 1 + }, + analysis: Core.AnalysisType.Valid, + }); + }) +}) \ No newline at end of file diff --git a/test/transformers/ts-child-decorator.spec.ts b/test/transformers/ts-child-decorator.spec.ts new file mode 100644 index 0000000..251567f --- /dev/null +++ b/test/transformers/ts-child-decorator.spec.ts @@ -0,0 +1,87 @@ +import * as Core from "../../src/core" +import { TsChildDecorator } from "../../src/transformers/ts-child-decorator" +import { JsParser } from "../helper" +import * as Chai from "chai" + + +describe("TsChildDecorator", () => { + + it("Should identify parameter decorator properly", () => { + let ast = JsParser.getAst(`tslib_1.__param(0, decoOne("param"))`) + let dummy = new TsChildDecorator(); + let parent = { + type: "Method", + name: "myMethod", + analysis: Core.AnalysisType.Valid, + parameters: [{ + type: "Parameter", + name: "par1" + }] + } + dummy.transform(ast.expression, parent); + Chai.expect(parent.parameters[0].decorators[0]).deep.eq({ + type: "Decorator", + name: "decoOne", + analysis: Core.AnalysisType.Valid, + location: { + column: 0, line: 1 + }, + parameters: [{ + type:"Parameter", + name:"param", + analysis: Core.AnalysisType.Valid, + location: { + column: 27, line: 1 + } + }] + }); + }) + + it("Should identify method decorator properly", () => { + let ast = JsParser.getAst(`decoOne("param")`) + let dummy = new TsChildDecorator(); + let parent = { + type: "Method", + name: "myMethod", + analysis: Core.AnalysisType.Valid, + parameters: [{ + type: "Parameter", + name: "par1" + }] + } + dummy.transform(ast.expression, parent); + Chai.expect(parent.decorators[0]).deep.eq({ + type: "Decorator", + name: "decoOne", + analysis: Core.AnalysisType.Valid, + location: { + column: 0, line: 1 + }, + parameters: [{ + type:"Parameter", + name:"param", + analysis: Core.AnalysisType.Valid, + location: { + column: 8, line: 1 + } + }] + }); + }) + + it("Should not error if provided __metadata", () => { + let ast = JsParser.getAst(`tslib_1.__metadata("design:type", Function)`) + let dummy = new TsChildDecorator(); + let parent = { + type: "Method", + name: "myMethod", + analysis: Core.AnalysisType.Valid, + parameters: [{ + type: "Parameter", + name: "par1" + }] + } + dummy.transform(ast.expression, parent); + Chai.expect(parent.decorators).undefined; + Chai.expect(parent.parameters[0].decorators).undefined + }) +}) \ No newline at end of file diff --git a/test/transformers/ts-module.spec.ts b/test/transformers/ts-module.spec.ts new file mode 100644 index 0000000..3dc569a --- /dev/null +++ b/test/transformers/ts-module.spec.ts @@ -0,0 +1,5 @@ +import * as Core from "../../src/core" +import { TsModuleTransformer } from "../../src/transformers/ts-module" +import { JsParser } from "../helper" +import * as Chai from "chai" + diff --git a/test/visitors/class-decorator.spec.ts b/test/visitors/class-decorator.spec.ts deleted file mode 100644 index cab8082..0000000 --- a/test/visitors/class-decorator.spec.ts +++ /dev/null @@ -1,73 +0,0 @@ -import { AnalysisType, MetaData, MetaDataFactory, flag } from "../../src/core" -import { ClassDecoratorVisitor } from "../../src/visitors/class-decorator-visitor" -import { JsParser, MH } from "../helper" -import * as Chai from "chai" - -describe("Class Decorator Visitor", () => { - let theModule: MetaData; - let dummy:ClassDecoratorVisitor; - - beforeEach(() => { - dummy = new ClassDecoratorVisitor(new MetaDataFactory()) - theModule = { - type: "Module", - name: "MyModule", - analysis: AnalysisType.Candidate, - children: [ { - type: "Class", - name: "MyClass", - analysis: AnalysisType.Candidate - | AnalysisType.HasConstructor - | AnalysisType.HasMethod, - children: [] - }] - } - }) - - it("Should identify decorator class properly", () => { - let ast = JsParser.getAst(` - MyClass = tslib_1.__decorate([ - decoOne(), - tslib_1.__metadata("design:paramtypes", []) - ], MyClass); - `) - let result = dummy.exit(ast.expression, theModule, null); - Chai.expect(theModule.children[0].decorators.length).eq(1); - Chai.expect(MH.validate(theModule.children[0].decorators[0], - {type: "Decorator", name: "decoOne"}, null)).true; - }) - - it("Should identify decorator class without tslib properly", () => { - let ast = JsParser.getAst(` - MyClass = __decorate([ - decoOne(), - __metadata("design:paramtypes", []) - ], MyClass); - `) - let result = dummy.exit(ast.expression, theModule, null); - Chai.expect(theModule.children[0].decorators.length).eq(1); - }) - - it("Should not add decorator to other class", () => { - let ast = JsParser.getAst(` - MyOtherClass = tslib_1.__decorate([ - decoOne(), - tslib_1.__metadata("design:paramtypes", []) - ], MyOtherClass); - `) - let result = dummy.exit(ast.expression, theModule, null); - Chai.expect(theModule.children[0].decorators).undefined; - }) - - it("Should return null on beforeChildren", () => { - let ast = JsParser.getAst(` - MyOtherClass = tslib_1.__decorate([ - decoOne(), - tslib_1.__metadata("design:paramtypes", []) - ], MyOtherClass); - `) - let result = dummy.start(ast.expression, theModule, null); - Chai.expect(result).null; - }) - -}) \ No newline at end of file diff --git a/test/visitors/class-visitor.spec.ts b/test/visitors/class-visitor.spec.ts deleted file mode 100644 index cbac760..0000000 --- a/test/visitors/class-visitor.spec.ts +++ /dev/null @@ -1,224 +0,0 @@ -import { AnalysisType, MetaData, MetaDataFactory, flag } from "../../src/core" -import { ClassVisitor } from "../../src/visitors/class-visitor" -import { JsParser, MH } from "../helper" -import * as Chai from "chai" - -describe("Class Visitor", () => { - let clazz: MetaData; - let dummy:ClassVisitor; - - beforeEach(() => { - dummy = new ClassVisitor(new MetaDataFactory()) - clazz = { - type: "Class", - name: "MyClass", - analysis: AnalysisType.Candidate, - children: [] - } - }) - - it("Should identify variable assignment to auto execute function as candidate", () => { - let ast = JsParser.getAst(` - var MyClass = (function(){ - - }()) - `) - let result = dummy.start(ast, null, null); - Chai.expect(flag(result.analysis, AnalysisType.Candidate)).eq(true); - }) - - it("Should identify class with inheritance", () => { - let ast = JsParser.getAst(` - var MyClass = (function(_super){ - - }(Parameter)) - `) - let result = dummy.start(ast, null, null); - Chai.expect(flag(result.analysis, AnalysisType.Candidate)).eq(true); - }) - - it("Should identify function declaration as constructor", () => { - let ast = JsParser.getAst(` - function MyClass(){} - `) - let result = dummy.start(ast, clazz, null); - Chai.expect(flag(clazz.analysis, AnalysisType.HasConstructor)).eq(true); - Chai.expect(result.type).eq("Constructor") - }) - - it("Should identify constructor with parameters", () => { - let ast = JsParser.getAst(` - function MyClass(par1, par2){} - `) - let result = dummy.start(ast, clazz, null); - clazz.children.push(result); - Chai.expect(flag(clazz.analysis, AnalysisType.HasConstructor)).eq(true); - Chai.expect(MH.validateConstructor(clazz, ["par1", "par2"])).true; - }) - - it("Should return null if meta type is not Class", () => { - let ast = JsParser.getAst(` - function MyClass(){} - `) - let result = dummy.start(ast, { - type: "Module", - name: "MyModule", - analysis: AnalysisType.Candidate, - children: [] - }, null); - Chai.expect(result).null - }) - - - - it("Should identify prototype assignment as method", () => { - let ast = JsParser.getAst(` - MyClass.prototype.myFunction = function(par1, par2){} - `) - clazz.analysis |= AnalysisType.HasConstructor; - let result = dummy.start(ast.expression, clazz, null); - Chai.expect(flag(clazz.analysis, AnalysisType.HasMethod)).eq(true); - Chai.expect(MH.validateMethod(result, "myFunction", ["par1", "par2"])).true - }) - - it("Should return null if supplied prototype assignment but meta type is not Class", () => { - let ast = JsParser.getAst(` - MyClass.prototype.myFunction = function(par1, par2){} - `) - let result = dummy.start(ast.expression, { - type: "Module", //<-- meta type module - name: "MyModule", - analysis: AnalysisType.Candidate, - children: [] - }, null); - Chai.expect(result).null; - }) - - it("Should verify as valid if has all flag (HasMethod, HasConstructor, Exported)", () => { - let ast = JsParser.getAst(` - ParentModule.MyClass = MyClass - `) - clazz.analysis |= AnalysisType.HasConstructor; - clazz.analysis |= AnalysisType.HasMethod; - let module: MetaData = { - type: "Module", - name: "ParentModule", - analysis: AnalysisType.Candidate, - children: [clazz] - } - dummy.exit(ast.expression, module, null); - Chai.expect(flag(clazz.analysis, AnalysisType.Exported)).eq(true); - Chai.expect(flag(clazz.analysis, AnalysisType.Valid)).eq(true); - }) - - it("Should return null if meta is not module", () => { - let ast = JsParser.getAst(` - ParentModule.MyClass = MyClass - `) - clazz.analysis |= AnalysisType.HasConstructor; - clazz.analysis |= AnalysisType.HasMethod; - let module: MetaData = { - type: "Class", - name: "ParentModule", - analysis: AnalysisType.Candidate, - children: [clazz] - } - let result = dummy.exit(ast.expression, module, null); - Chai.expect(result).null; - }) - - it("Should verify as valid if has flags (HasMethod, HasConstructor)", () => { - let ast = JsParser.getAst(` - ParentModule.MyClass = MyClass - `) - clazz.analysis |= AnalysisType.HasConstructor; - clazz.analysis |= AnalysisType.HasMethod; - let module: MetaData = { - type: "Module", - name: "ParentModule", - analysis: AnalysisType.Candidate, - children: [clazz] - } - dummy.exit(ast.expression, module, null); - Chai.expect(flag(clazz.analysis, AnalysisType.Exported)).eq(true); - Chai.expect(flag(clazz.analysis, AnalysisType.Valid)).eq(true); - }) - - it("Should not verify exported if different class name", () => { - let ast = JsParser.getAst(` - ParentModule.OtherClass = OtherClass - `) - clazz.analysis |= AnalysisType.HasConstructor; - clazz.analysis |= AnalysisType.HasMethod; - let module: MetaData = { - type: "Module", - name: "ParentModule", - analysis: AnalysisType.Candidate, - children: [clazz] - } - dummy.exit(ast.expression, module, null); - Chai.expect(flag(clazz.analysis, AnalysisType.Exported)).eq(false); - }) - - it("Should not verify as valid if doesn't have any of (HasMethod, HasConstructor)", () => { - let ast = JsParser.getAst(` - ParentModule.MyClass = MyClass - `) - clazz.analysis |= AnalysisType.HasConstructor; //<-- doesn't have method - let module: MetaData = { - type: "Module", - name: "ParentModule", - analysis: AnalysisType.Candidate, - children: [clazz] - } - dummy.exit(ast.expression, module, null); - Chai.expect(flag(clazz.analysis, AnalysisType.Exported)).eq(true); - Chai.expect(flag(clazz.analysis, AnalysisType.Valid)).eq(false); //<-- should not valid - }) - - it("Should identify es6 class properly", () => { - let ast = JsParser.getAst(` - var MyClass = class MyClass { - constructor() { } - myMethod(par1) { } - } - `) - clazz.analysis |= AnalysisType.HasConstructor; - clazz.analysis |= AnalysisType.HasMethod; - let module: MetaData = { - type: "Module", - name: "ParentModule", - analysis: AnalysisType.Candidate, - children: [clazz] - } - let result = dummy.start(ast.declarations[0].init, module, null); - Chai.expect(MH.validateClass(result, {name: "MyClass", flags: [AnalysisType.Candidate]})).true - }) - - it("Should identify es6 constructor properly", () => { - let ast = JsParser.getAst(` - class MyClass { - constructor(par1, par2) { } - myMethod(par1) { } - } - `) - - let result = dummy.start(ast.body.body[0], clazz, null); - clazz.children.push(result); - Chai.expect(flag(clazz.analysis, AnalysisType.HasConstructor)).true - Chai.expect(MH.validateConstructor(clazz, ["par1", "par2"])).true - }) - - it("Should identify es6 method properly", () => { - let ast = JsParser.getAst(` - class MyClass { - constructor(par1, par2) { } - myMethod(par1) { } - } - `) - clazz.analysis |= AnalysisType.HasConstructor; - let result = dummy.start(ast.body.body[1], clazz, null); - Chai.expect(flag(clazz.analysis, AnalysisType.HasMethod)).true - Chai.expect(MH.validateMethod(result, "myMethod", ["par1"])).true - }) -}) \ No newline at end of file diff --git a/test/visitors/index.spec.ts b/test/visitors/index.spec.ts deleted file mode 100644 index a379151..0000000 --- a/test/visitors/index.spec.ts +++ /dev/null @@ -1,70 +0,0 @@ -import { VisitorAggregator } from "../../src/visitors" -import { Visitor, MetaData, Call, SyntaxKind, AnalysisType } from "../../src/core" -import * as Chai from "chai" - -class DummyVisitorOne implements Visitor { - @Call.when(SyntaxKind.CallExpression, SyntaxKind.FunctionDeclaration) - start(node, meta: MetaData, metaParent: MetaData): MetaData { - if (node.visitorName != "DummyVisitorOne") return null; - return { - type: "Function", - name: "DummyVisitorOne", - analysis: AnalysisType.Candidate, - children: [] - } - } - - @Call.when(SyntaxKind.ClassDeclaration) - exit(node, meta: MetaData, metaParent: MetaData): MetaData { - return { - type: "Class", - name: "DummyVisitorOne", - analysis: AnalysisType.Candidate, - children: [] - } - } -} - -class DummyVisitorTwo implements Visitor { - @Call.when(SyntaxKind.CallExpression) - start(node, meta: MetaData, metaParent: MetaData): MetaData { - return null - } - - @Call.when(SyntaxKind.LogicalExpression) - exit(node, meta: MetaData, metaParent: MetaData): MetaData { - return { - type: "Class", - name: "DummyVisitorTwo", - analysis: AnalysisType.Candidate, - children: [] - } - } -} - -describe("Visitor Aggregator", () => { - let visitor: VisitorAggregator; - beforeEach(() => { - visitor = new VisitorAggregator([new DummyVisitorOne(), new DummyVisitorTwo()]) - }) - - it("Should register call aggregator properly", () => { - let result = visitor.exit({ type: SyntaxKind.LogicalExpression }, null, null) - Chai.expect(result.name).eq("DummyVisitorTwo"); - }) - - it("Should return null if no SyntaxKind handler", () => { - let result = visitor.exit({ type: SyntaxKind.File }, null, null) - Chai.expect(result).null; - }) - - it("Should execute correct handler on multiple handler", () => { - let result = visitor.start({ type: SyntaxKind.CallExpression, visitorName: "DummyVisitorOne" }, null, null) - Chai.expect(result.name).eq("DummyVisitorOne"); - }) - - it("Should execute correct handler on multiple handler", () => { - let result = visitor.start({ type: SyntaxKind.CallExpression, visitorName: "DummyVisitorTwo" }, null, null) - Chai.expect(result).null; - }) -}) \ No newline at end of file diff --git a/test/visitors/method-decorator-visitor.spec.ts b/test/visitors/method-decorator-visitor.spec.ts deleted file mode 100644 index 26619d7..0000000 --- a/test/visitors/method-decorator-visitor.spec.ts +++ /dev/null @@ -1,170 +0,0 @@ -import { AnalysisType, MetaData, MetaDataFactory, flag } from "../../src/core" -import { MethodDecoratorVisitor } from "../../src/visitors/method-decorator-visitor" -import { JsParser, MH } from "../helper" -import * as Chai from "chai" - -describe("Method Decorator Visitor", () => { - let theModule: MetaData; - let dummy: MethodDecoratorVisitor; - - beforeEach(() => { - dummy = new MethodDecoratorVisitor(new MetaDataFactory()) - theModule = { - type: "Module", - name: "MyModule", - analysis: AnalysisType.Candidate, - children: [{ - type: "Class", - name: "MyClass", - analysis: AnalysisType.Candidate - | AnalysisType.HasConstructor - | AnalysisType.HasMethod, - children: [{ - type: "Method", - name: "myMethod", - analysis: AnalysisType.Valid, - children: [] - }] - }] - } - }) - - it("Should identify method decorator properly", () => { - let ast = JsParser.getAst(` - tslib_1.__decorate([ - decoOne(), - decoTwo("hello world!"), - tslib_1.__metadata("design:type", Function), - tslib_1.__metadata("design:paramtypes", []), - tslib_1.__metadata("design:returntype", void 0) - ], MyClass.prototype, "myMethod", null); - `) - let result = dummy.exit(ast.expression, theModule, null); - Chai.expect(theModule.children[0].children[0].decorators.length).eq(2); - Chai.expect(MH.validate(theModule.children[0].children[0].decorators[0], - { type: "Decorator", name: "decoOne" }, null)).true; - Chai.expect(MH.validate(theModule.children[0].children[0].decorators[1], - { type: "Decorator", name: "decoTwo" }, null)).true; - Chai.expect(MH.validate(theModule.children[0].children[0].decorators[1].children[0], - { type: "Parameter", name: "hello world!" }, null)).true; - }) - - it("Should identify method decorator without tslib", () => { - let ast = JsParser.getAst(` - __decorate([ - decoOne(), - decoTwo("hello world!"), - __metadata("design:type", Function), - __metadata("design:paramtypes", []), - __metadata("design:returntype", void 0) - ], MyClass.prototype, "myMethod", null); - `) - let result = dummy.exit(ast.expression, theModule, null); - Chai.expect(theModule.children[0].children[0].decorators.length).eq(2); - Chai.expect(MH.validate(theModule.children[0].children[0].decorators[0], - { type: "Decorator", name: "decoOne" }, null)).true; - Chai.expect(MH.validate(theModule.children[0].children[0].decorators[1], - { type: "Decorator", name: "decoTwo" }, null)).true; - Chai.expect(MH.validate(theModule.children[0].children[0].decorators[1].children[0], - { type: "Parameter", name: "hello world!" }, null)).true; - }) - - it("Should identify parameter decorator", () => { - let ast = JsParser.getAst(` - __decorate([ - decoOne(), - __param(0, decoTwo("hello world!")), - __metadata("design:type", Function), - ], MyClass.prototype, "myMethod", null); - `) - //add parameter info on myMethod - theModule.children[0].children[0].children.push({ - type: "Parameter", - name: "par1", - analysis: AnalysisType.Valid, - children: [] - }) - let result = dummy.exit(ast.expression, theModule, null); - Chai.expect(MH.validate(theModule.children[0].children[0].children[0].decorators[0], - { type: "Decorator", name: "decoTwo" }, null)).true - }) - - it("Should not error when provided class decorator", () => { - let ast = JsParser.getAst(` - MyClass = tslib_1.__decorate([ - decoOne(), - tslib_1.__metadata("design:paramtypes", []) - ], MyClass); - `) - let result = dummy.exit(ast.expression, theModule, null); - Chai.expect(theModule.children[0].children[0].decorators).undefined; - }) - - it("Should not error when the class is not found", () => { - let ast = JsParser.getAst(` - tslib_1.__decorate([ - decoOne(), - decoTwo("hello world!"), - tslib_1.__metadata("design:type", Function), - tslib_1.__metadata("design:paramtypes", []), - tslib_1.__metadata("design:returntype", void 0) - ], MyClass.prototype, "myMethod", null); - `) - theModule.children[0] = { - type: "Class", - name: "MyOtherClass", - analysis: AnalysisType.Candidate - | AnalysisType.HasConstructor - | AnalysisType.HasMethod, - children: [{ - type: "Method", - name: "myMethod", - analysis: AnalysisType.Valid, - children: [] - }] - } - let result = dummy.exit(ast.expression, theModule, null); - Chai.expect(theModule.children[0].children[0].decorators).undefined; - }) - - it("Should not error when the method is not found", () => { - let ast = JsParser.getAst(` - tslib_1.__decorate([ - decoOne(), - decoTwo("hello world!"), - ], MyClass.prototype, "myMethod", null); - `) - theModule.children[0].children[0] = { - type: "Method", - name: "myOtherMethod", - analysis: AnalysisType.Valid, - children: [] - } - let result = dummy.exit(ast.expression, theModule, null); - Chai.expect(theModule.children[0].children[0].decorators).undefined; - }) - - it("Should return null on beforeChildren", () => { - let ast = JsParser.getAst(` - tslib_1.__decorate([ - decoOne(), - decoTwo("hello world!"), - ], MyClass.prototype, "myMethod", null); - `) - theModule.children[0] = { - type: "Class", - name: "MyOtherClass", - analysis: AnalysisType.Candidate - | AnalysisType.HasConstructor - | AnalysisType.HasMethod, - children: [{ - type: "Method", - name: "myMethod", - analysis: AnalysisType.Valid, - children: [] - }] - } - let result = dummy.start(ast.expression, theModule, null); - Chai.expect(result).null; - }) -}) \ No newline at end of file diff --git a/test/visitors/module-visitor.spec.ts b/test/visitors/module-visitor.spec.ts deleted file mode 100644 index 9d2e0ec..0000000 --- a/test/visitors/module-visitor.spec.ts +++ /dev/null @@ -1,134 +0,0 @@ -import { AnalysisType, MetaData, MetaDataFactory, flag } from "../../src/core" -import { ModuleVisitor } from "../../src/visitors/module-visitor" -import { JsParser } from "../helper" -import * as Chai from "chai" - - - -describe("Module Visitor", () => { - let dummy:ModuleVisitor; - beforeEach(() => { - dummy = new ModuleVisitor(new MetaDataFactory()) - }) - - it("Should identify module properly", () => { - let ast = JsParser.getAst(` - (function (MyModule) { - - })(MyModule = exports.MyModule || (exports.MyModule = {})); - `) - let result = dummy.start(ast.expression, null, null); - Chai.expect((result.analysis & AnalysisType.Candidate)).eq(AnalysisType.Candidate); - }) - - it("Should identify inner module properly", () => { - let ast = JsParser.getAst(` - (function (InnerModule) { - - })(InnerModule = MyModule.InnerModule || (MyModule.InnerModule = {})); - `) - let result = dummy.start(ast.expression, null, null); - Chai.expect((result.analysis & AnalysisType.Candidate)).eq(AnalysisType.Candidate); - }) - - it("Should identify non exported module", () => { - let ast = JsParser.getAst(` - (function (InnerModule) { - - })(InnerModule || (InnerModule = {})); - `) - let result = dummy.start(ast.expression, null, null); - Chai.expect((result.analysis & AnalysisType.Candidate)).eq(AnalysisType.Candidate); - }) - - it("Should return null if identification fail", () => { - let ast = JsParser.getAst(` - (function () { - - })(); - `) - let result = dummy.start(ast.expression, null, null); - Chai.expect(result).null - }) - - it("Should analyze and verify module", () => { - let ast = JsParser.getAst(` - (function (InnerModule) { - - })(InnerModule = MyModule.InnerModule || (MyModule.InnerModule = {})); - `) - let module:MetaData = { - type: "Module", - name: "InnerModule", - analysis: AnalysisType.Candidate, - children:[{ - type: "Class", - name: "MyClass", - analysis: AnalysisType.Exported, - children: [] - }] - } - let parent:MetaData = { - type: "Module", - name: "MyModule", - analysis: AnalysisType.Candidate, - children:[] - } - dummy.exit(ast.expression, module, parent); - Chai.expect(flag(module.analysis, AnalysisType.Valid)) - }) - - it("Should not valid if doesn't have exported children", () => { - let ast = JsParser.getAst(` - (function (InnerModule) { - - })(InnerModule = MyModule.InnerModule || (MyModule.InnerModule = {})); - `) - let module:MetaData = { - type: "Module", - name: "InnerModule", - analysis: AnalysisType.Candidate, - children:[{ - type: "Class", - name: "MyClass", - analysis: AnalysisType.Candidate, //<------ only have candidate - children: [] - }] - } - let parent:MetaData = { - type: "Module", - name: "MyModule", - analysis: AnalysisType.Candidate, - children:[] - } - dummy.exit(ast.expression, module, parent); - Chai.expect(flag(module.analysis, AnalysisType.Valid)).false - }) - - it("Should not valid if not exported", () => { - let ast = JsParser.getAst(` - (function (InnerModule) { - - })(InnerModule || (InnerModule = {})); - `) - let module:MetaData = { - type: "Module", - name: "InnerModule", - analysis: AnalysisType.Candidate, - children:[{ - type: "Class", - name: "MyClass", - analysis: AnalysisType.Exported, - children: [] - }] - } - let parent:MetaData = { - type: "Module", - name: "MyModule", - analysis: AnalysisType.Candidate, - children:[] - } - dummy.exit(ast.expression, module, parent); - Chai.expect(flag(module.analysis, AnalysisType.Valid)).false - }) -}) \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index 089f7a8..2c0b330 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -4,7 +4,7 @@ "module": "commonjs", "target": "es5", "noImplicitAny": false, - "sourceMap": false, + "sourceMap": true, "experimentalDecorators": true, "emitDecoratorMetadata": true, "importHelpers": true From 1b14fb35664e36770ff8448481192b5915d5b7b8 Mon Sep 17 00:00:00 2001 From: Ketut Sandiarsa Date: Wed, 25 Jan 2017 11:40:45 +0800 Subject: [PATCH 2/9] Adding transformers, and some tests --- .vscode/launch.json | 20 --- src/analyzers/class-analyzer.ts | 36 +++-- src/analyzers/class-decorator-analyzer.ts | 21 +-- src/analyzers/method-decorator-analyzer.ts | 6 +- src/core.ts | 12 -- src/index.ts | 33 ++++- src/transformer.ts | 10 +- src/transformers/constructor.ts | 2 +- src/transformers/method.ts | 1 + src/transformers/parameter.ts | 1 + src/transformers/ts-child-decorator.ts | 4 +- src/transformers/ts-class-export.ts | 19 +++ src/transformers/ts-class.ts | 23 +-- src/transformers/ts-decorator.ts | 40 ++--- src/transformers/ts-module.ts | 39 ++--- test/analyzers/class-analyzer.spec.ts | 18 ++- .../class-decorator-analyzer.spec.ts | 6 +- .../method-decorator-analyzer.spec.ts | 8 +- test/myclass.ts | 18 +-- test/transformers/ts-class-export.spec.ts | 56 +++++++ test/transformers/ts-class.spec.ts | 138 ++++++++++++++++++ test/transformers/ts-decorator.spec.ts | 125 ++++++++++++++++ 22 files changed, 503 insertions(+), 133 deletions(-) create mode 100644 src/transformers/ts-class-export.ts create mode 100644 test/transformers/ts-class-export.spec.ts create mode 100644 test/transformers/ts-class.spec.ts create mode 100644 test/transformers/ts-decorator.spec.ts diff --git a/.vscode/launch.json b/.vscode/launch.json index 8b83a63..24d9080 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -1,7 +1,4 @@ { - // Use IntelliSense to learn about possible Node.js debug attributes. - // Hover to view descriptions of existing attributes. - // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ { @@ -19,23 +16,6 @@ "env": { "NODE_ENV": "testing" } - }, - { - "type": "node", - "request": "launch", - "name": "Launch Program", - "program": "${workspaceRoot}/index.js", - "cwd": "${workspaceRoot}", - "outFiles": [], - "sourceMaps": true - }, - { - "type": "node", - "request": "attach", - "name": "Attach to Process", - "port": 5858, - "outFiles": [], - "sourceMaps": true } ] } \ No newline at end of file diff --git a/src/analyzers/class-analyzer.ts b/src/analyzers/class-analyzer.ts index 92cb556..c7e1c50 100644 --- a/src/analyzers/class-analyzer.ts +++ b/src/analyzers/class-analyzer.ts @@ -4,36 +4,44 @@ import * as HP from "./helper" export class ClassAnalyzer { /** - * expect AssignmentExpression, VariableDeclaration + * expect AssignmentExpression, ExpressionStatement */ constructor(private node) { } /** - * expect AssignmentExpression + * expect ExpressionStatement */ isExported(name, parentName) { return this.isExportedStatement() - && (this.node.left.object.name == parentName || this.node.left.object.name == "exports") - && this.node.right.name == name + && (this.node.expression.left.object.name == parentName || this.node.expression.left.object.name == "exports") + && this.node.expression.right.name == name } /** - * expect AssignmentExpression + * expect ExpressionStatement */ private isExportedStatement() { - return this.node.type == SyntaxKind.AssignmentExpression - && this.node.left.type == SyntaxKind.MemberExpression - && this.node.right.type == SyntaxKind.Identifier + return this.node.type == SyntaxKind.ExpressionStatement + && this.node.expression.type == SyntaxKind.AssignmentExpression + && this.node.expression.left.type == SyntaxKind.MemberExpression + && this.node.expression.right.type == SyntaxKind.Identifier } getName() { if (this.isExportedStatement()) - return this.node.right.name; + return this.node.expression.right.name; else if (this.isCandidate()) return this.node.declarations[0].id.name else return null; } + getParentName() { + if (this.isExportedStatement()) + return this.node.expression.left.object.name; + else + return null; + } + /** * expect VariableDeclaration */ @@ -47,10 +55,14 @@ export class ClassAnalyzer { && this.node.declarations[0].init.callee.id == null } - getBaseClass(){ - if(this.isCandidate() && this.node.declarations[0].init.arguments.length > 0){ - return this.node.declarations[0].init.arguments[0].name + getBaseClass() { + if (this.isCandidate() && this.node.declarations[0].init.arguments.length > 0) { + if (this.node.declarations[0].init.arguments[0].type == SyntaxKind.MemberExpression) + return this.node.declarations[0].init.arguments[0].property.name; + else + return this.node.declarations[0].init.arguments[0].name } + return null; } } diff --git a/src/analyzers/class-decorator-analyzer.ts b/src/analyzers/class-decorator-analyzer.ts index d7f2451..428e875 100644 --- a/src/analyzers/class-decorator-analyzer.ts +++ b/src/analyzers/class-decorator-analyzer.ts @@ -2,24 +2,25 @@ import { SyntaxKind, MetaData } from "../core" import * as HP from "./helper" -export class ClassDecoratorAnalyzer{ +export class ClassDecoratorAnalyzer { /** - * expect AssignmentExpression + * expect ExpressionStatement */ - constructor(private node){} + constructor(private node) { } - getClassName(){ - if (this.isDecorator()){ - return this.node.left.name; + getClassName() { + if (this.isDecorator()) { + return this.node.expression.left.name; } else return null; } isDecorator() { - return this.node.type == SyntaxKind.AssignmentExpression - && this.node.left.type == SyntaxKind.Identifier - && this.node.right.type == SyntaxKind.CallExpression - && HP.getMethodNameFromCallee(this.node.right.callee) == "__decorate" + return this.node.type == SyntaxKind.ExpressionStatement + && this.node.expression.type == SyntaxKind.AssignmentExpression + && this.node.expression.left.type == SyntaxKind.Identifier + && this.node.expression.right.type == SyntaxKind.CallExpression + && HP.getMethodNameFromCallee(this.node.expression.right.callee) == "__decorate" } } \ No newline at end of file diff --git a/src/analyzers/method-decorator-analyzer.ts b/src/analyzers/method-decorator-analyzer.ts index f241194..cddd142 100644 --- a/src/analyzers/method-decorator-analyzer.ts +++ b/src/analyzers/method-decorator-analyzer.ts @@ -2,10 +2,14 @@ import { SyntaxKind, MetaData, AnalysisType } from "../core" import * as HP from "./helper" export class MethodDecoratorAnalyzer { + /** + * expect ExpressionStatement + */ constructor(private node) { } isDecorator() { - return HP.getMethodNameFromCallee(this.node.expression.callee) == "__decorate" + return this.node.type == SyntaxKind.ExpressionStatement + && HP.getMethodNameFromCallee(this.node.expression.callee) == "__decorate" && this.node.expression.arguments.length == 4 } diff --git a/src/core.ts b/src/core.ts index 7de8d90..51647e2 100644 --- a/src/core.ts +++ b/src/core.ts @@ -59,18 +59,6 @@ export interface ParentMetaData extends MetaData{ children: MetaData[] } -export interface Visitor { - start(node, meta: MetaData, metaParent: MetaData): MetaData - exit(node, meta: MetaData, metaParent: MetaData): MetaData -} - -export class MetaDataFactory{ - create(node, meta:MetaData, metaParent:MetaData): MetaData{ - meta.location = node.loc.start; - return meta; - } -} - export module SyntaxKind { export const File = "File" export const Program = "Program" diff --git a/src/index.ts b/src/index.ts index f3bee1b..c5c9aa1 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,9 +1,36 @@ -import * as Core from "./core" +import { + AnalysisType, + ClassMetaData, + ConstructorMetaData, + DecoratorMetaData, + flag, + MetaData, + MetadataType, + MethodMetaData, + ParameterMetaData, + ParentMetaData, + SyntaxKind, + TransformerBase +} from "./core" import { Transformer } from "./transformer" -function transform(ast, fileName:string) { +function transform(ast, fileName: string) { let transformer = new Transformer(fileName) return transformer.transform(ast); } -export{transform, Core} \ No newline at end of file +export { + transform, + AnalysisType, + ClassMetaData, + ConstructorMetaData, + DecoratorMetaData, + flag, + MetaData, + MetadataType, + MethodMetaData, + ParameterMetaData, + ParentMetaData, + SyntaxKind, + TransformerBase +} \ No newline at end of file diff --git a/src/transformer.ts b/src/transformer.ts index 9f9ea0b..e5da669 100644 --- a/src/transformer.ts +++ b/src/transformer.ts @@ -1,7 +1,9 @@ import { ClassAnalyzer } from "./analyzers/class-analyzer" import { MetaData, ParentMetaData, SyntaxKind, Call, TransformerBase, AnalysisType } from "./core" -import { TypeScriptClassTransformer } from "./transformers/ts-class" +import { TsClassTransformer } from "./transformers/ts-class" import { TsModuleTransformer } from "./transformers/ts-module" +import { TsDecorator } from "./transformers/ts-decorator" +import { TsClassExporterTransformer } from "./transformers/ts-class-export" export class Transformer { constructor(private fileName: string) {} @@ -26,8 +28,10 @@ class FileTransformer extends TransformerBase { transform(node, parent: MetaData) { this.traverse(node.program.body, parent, [ - new TypeScriptClassTransformer(), - new TsModuleTransformer() + new TsClassTransformer(), + new TsModuleTransformer(), + new TsDecorator(), + new TsClassExporterTransformer() ]) } } \ No newline at end of file diff --git a/src/transformers/constructor.ts b/src/transformers/constructor.ts index 0309d85..cb10479 100644 --- a/src/transformers/constructor.ts +++ b/src/transformers/constructor.ts @@ -4,7 +4,7 @@ import * as Core from "../core" export class ConstructorTransformer extends Core.TransformerBase { - @Core.Call.when(Core.SyntaxKind.ExpressionStatement) + @Core.Call.when(Core.SyntaxKind.FunctionDeclaration) transform(node, parent: Core.ClassMetaData) { if (node.type == Core.SyntaxKind.FunctionDeclaration && node.id.name == parent.name) { diff --git a/src/transformers/method.ts b/src/transformers/method.ts index 62bb287..9e8ddc4 100644 --- a/src/transformers/method.ts +++ b/src/transformers/method.ts @@ -16,6 +16,7 @@ export class MethodTransformer extends Core.TransformerBase { location: node.loc.start, parameters: [] } + if(!parent.methods) parent.methods = [] parent.methods.push(method) this.traverse(node.expression.right.params, method, [ new ParameterTransformer() diff --git a/src/transformers/parameter.ts b/src/transformers/parameter.ts index 829f2d8..95ed5e4 100644 --- a/src/transformers/parameter.ts +++ b/src/transformers/parameter.ts @@ -3,6 +3,7 @@ import * as Core from "../core" export class ParameterTransformer extends Core.TransformerBase { @Core.Call.when(Core.SyntaxKind.Identifier) transform(node, parent: Core.MethodMetaData | Core.ConstructorMetaData) { + if(!parent.parameters) parent.parameters = [] parent.parameters.push({ type: "Parameter", name: node.name, diff --git a/src/transformers/ts-child-decorator.ts b/src/transformers/ts-child-decorator.ts index d700c1b..870ce35 100644 --- a/src/transformers/ts-child-decorator.ts +++ b/src/transformers/ts-child-decorator.ts @@ -17,7 +17,6 @@ export class TsChildDecorator extends Core.TransformerBase { } private transformMethod(node, parent: Core.MethodMetaData|Core.ClassMetaData) { - if (!parent.decorators) parent.decorators = [] let method = { type: "Decorator", name: node.callee.name, @@ -25,11 +24,11 @@ export class TsChildDecorator extends Core.TransformerBase { location: node.loc.start, parameters: node.arguments.map(x => this.getParameter(x)) } + if (!parent.decorators) parent.decorators = [] parent.decorators.push(method) } private transformParameter(node, parameter: Core.ParameterMetaData) { - if (!parameter.decorators) parameter.decorators = [] let decorator = { type: "Decorator", name: H.getMethodNameFromCallee(node.arguments[1].callee), @@ -38,6 +37,7 @@ export class TsChildDecorator extends Core.TransformerBase { parameters: node.arguments[1].arguments .map(x => this.getParameter(x)) }; + if (!parameter.decorators) parameter.decorators = [] parameter.decorators.push(decorator) } diff --git a/src/transformers/ts-class-export.ts b/src/transformers/ts-class-export.ts new file mode 100644 index 0000000..f5860e4 --- /dev/null +++ b/src/transformers/ts-class-export.ts @@ -0,0 +1,19 @@ +import { ClassAnalyzer } from "../analyzers/class-analyzer" +import * as Core from "../core" + + +export class TsClassExporterTransformer extends Core.TransformerBase { + transform(node, parent: Core.ParentMetaData) { + let analyzer = new ClassAnalyzer(node); + let parentName = analyzer.getParentName(); + let className = analyzer.getName(); + let clazz:Core.ClassMetaData; + if (parent.children) clazz = parent.children.filter(x => x.name == className)[0] + if (clazz && ((parent.type == "File" && parentName == "exports") + || (parent.type == "Module" && parentName == parent.name))) { + clazz.analysis |= Core.AnalysisType.Exported; + if(Core.flag(clazz.analysis, Core.AnalysisType.HasMethod)) + clazz.analysis |= Core.AnalysisType.Valid + } + } +} diff --git a/src/transformers/ts-class.ts b/src/transformers/ts-class.ts index 1519371..ea843e1 100644 --- a/src/transformers/ts-class.ts +++ b/src/transformers/ts-class.ts @@ -4,17 +4,20 @@ import { ConstructorTransformer } from "./constructor" import { ParameterTransformer } from "./parameter" import * as Core from "../core" -export class TypeScriptClassTransformer extends Core.TransformerBase { +export class TsClassTransformer extends Core.TransformerBase { - transform(node, parent:Core.MetaData) { + transform(node, parent: Core.ParentMetaData) { let analyzer = new ClassAnalyzer(node); if (analyzer.isCandidate()) { let clazz = { type: "Class", name: analyzer.getName(), baseClass: analyzer.getBaseClass(), - location: node.loc.start + location: node.loc.start, + analysis: Core.AnalysisType.Candidate } + if(!parent.children) parent.children = [] + parent.children.push(clazz) this.traverse(node.declarations[0].init.callee.body.body, clazz, [ new MethodTransformer(), new ConstructorTransformer() @@ -23,14 +26,16 @@ export class TypeScriptClassTransformer extends Core.TransformerBase { } } - private analyse(clazz:Core.ClassMetaData, parent:Core.MetaData, analyzer:ClassAnalyzer){ + + private analyse(clazz: Core.ClassMetaData, parent: Core.MetaData, analyzer: ClassAnalyzer) { + /* + TS class is not valid *YET* here, + validation done on Module/File level + */ let hasConstructor = clazz.constructor; let hasMethods = clazz.methods && clazz.methods.length > 0; - let isExported = analyzer.isExported(clazz.name, parent.name) - if(hasConstructor) clazz.analysis |= Core.AnalysisType.HasConstructor - if(hasMethods) clazz.analysis |= Core.AnalysisType.HasMethod; - if(isExported) clazz.analysis |= Core.AnalysisType.Exported; - if(hasMethods && isExported) clazz.analysis |= Core.AnalysisType.Valid + if (hasConstructor) clazz.analysis |= Core.AnalysisType.HasConstructor + if (hasMethods) clazz.analysis |= Core.AnalysisType.HasMethod; } } diff --git a/src/transformers/ts-decorator.ts b/src/transformers/ts-decorator.ts index 4f75af7..6d0c236 100644 --- a/src/transformers/ts-decorator.ts +++ b/src/transformers/ts-decorator.ts @@ -7,31 +7,31 @@ import { TsChildDecorator } from "./ts-child-decorator" export class TsDecorator extends Core.TransformerBase { @Core.Call.when(Core.SyntaxKind.ExpressionStatement) transform(node, parent: Core.ParentMetaData) { - if (node.expression.type == Core.SyntaxKind.MemberExpression - && H.getMethodNameFromCallee(node.expression.callee) == "__decorate") { - this.transformMethod(node, parent) + let ca = new ClassDecoratorAnalyzer(node) + let ma = new MethodDecoratorAnalyzer(node) + if (ma.isDecorator()) { + this.transformMethod(node, parent, ma) } - else if (node.expression.type == Core.SyntaxKind.AssignmentExpression - && H.getMethodNameFromCallee(node.expression.callee) == "__decorate") { - this.transformClass(node, parent) + else if (ca.isDecorator()) { + this.transformClass(node, parent, ca) } } - private transformMethod(node, parent: Core.ParentMetaData) { - let analyzers = new MethodDecoratorAnalyzer(node) - if (analyzers.isDecorator()) { - let methodName = analyzers.getMethodName(); - let className = analyzers.getClassName(); - let clazz = parent.children.filter(x => x.name == className)[0]; - let method = clazz.methods.filter(x => x.name == methodName)[0] - this.traverse(node.expression.arguments, method, [ - new TsChildDecorator() - ]) - } + private transformMethod(node, parent: Core.ParentMetaData, analyzers: MethodDecoratorAnalyzer) { + let methodName = analyzers.getMethodName(); + let className = analyzers.getClassName(); + let clazz = parent.children.filter(x => x.name == className)[0]; + let method = clazz.methods.filter(x => x.name == methodName)[0] + this.traverse(node.expression.arguments[0].elements, method, [ + new TsChildDecorator() + ]) } - private transformClass(node, parent: Core.ParentMetaData) { - + private transformClass(node, parent: Core.ParentMetaData, analyzer: ClassDecoratorAnalyzer) { + let className = analyzer.getClassName(); + let clazz = parent.children.filter(x => x.name == className)[0]; + this.traverse(node.expression.right.arguments[0].elements, clazz, [ + new TsChildDecorator() + ]) } - } \ No newline at end of file diff --git a/src/transformers/ts-module.ts b/src/transformers/ts-module.ts index 8c3d6ea..3b2750e 100644 --- a/src/transformers/ts-module.ts +++ b/src/transformers/ts-module.ts @@ -1,40 +1,43 @@ import { ModuleAnalyzer } from "../analyzers/module-analyzer" -import { TransformerBase, ParentMetaData, MetaData, MethodMetaData, ClassMetaData, SyntaxKind, Call, flag, AnalysisType } from "../core" -import { TypeScriptClassTransformer } from "./ts-class" +import * as Core from "../core" +import { TsClassTransformer } from "./ts-class" +import { TsDecorator } from "./ts-decorator" +import { TsClassExporterTransformer } from "./ts-class-export" -export class TsModuleTransformer extends TransformerBase { +export class TsModuleTransformer extends Core.TransformerBase { - constructor(private childTransformers?:TransformerBase[]){ - super() - } - - @Call.when(SyntaxKind.ExpressionStatement) - transform(node, parent: ParentMetaData) { + @Core.Call.when(Core.SyntaxKind.ExpressionStatement) + transform(node, parent: Core.ParentMetaData) { let analyzer = new ModuleAnalyzer(node) if (analyzer.isCandidate() && (parent.type == "Module" || parent.type == "File")) { - let module: ParentMetaData = { + let module: Core.ParentMetaData = { type: "Module", - analysis: AnalysisType.Candidate, + analysis: Core.AnalysisType.Candidate, children: [], location: node.loc.start, name: analyzer.getName() } parent.children.push(module); - this.traverse(analyzer.getBody(), module, this.childTransformers) + this.traverse(analyzer.getBody(), module, [ + new TsClassTransformer(), + new TsModuleTransformer(), + new TsDecorator(), + new TsClassExporterTransformer() + ]) this.analyse(module, parent, analyzer) } } - private analyse(module: ParentMetaData, parent: MetaData, analyzer: ModuleAnalyzer) { + private analyse(module: Core.ParentMetaData, parent: Core.MetaData, analyzer: ModuleAnalyzer) { let connected = module.children.length > 0 - && module.children.some(x => flag(x.analysis, AnalysisType.Valid)) + && module.children.some(x => Core.flag(x.analysis, Core.AnalysisType.Valid)) let exported = analyzer.isExported(parent.type == "File" ? "exports" : parent.name); - let analysis = AnalysisType.Candidate; + let analysis = Core.AnalysisType.Candidate; if (exported) - analysis |= AnalysisType.Exported; + analysis |= Core.AnalysisType.Exported; if (connected) - analysis |= AnalysisType.ConnectedWithChildren + analysis |= Core.AnalysisType.ConnectedWithChildren if (exported && connected) - analysis |= AnalysisType.Valid + analysis |= Core.AnalysisType.Valid } } diff --git a/test/analyzers/class-analyzer.spec.ts b/test/analyzers/class-analyzer.spec.ts index 24a94df..6d5dcfb 100644 --- a/test/analyzers/class-analyzer.spec.ts +++ b/test/analyzers/class-analyzer.spec.ts @@ -8,16 +8,18 @@ describe("Class Analyzer", () => { describe("isExported", () => { it("Should identify exported class with module", () => { let ast = JsParser.getAst(`MyModule.MyClass = MyClass`) - let dummy = new ClassAnalyzer(ast.expression) + let dummy = new ClassAnalyzer(ast) Chai.expect(dummy.isExported("MyClass", "MyModule")).eq(true); Chai.expect(dummy.getName()).eq("MyClass"); + Chai.expect(dummy.getParentName()).eq("MyModule"); }) it("Should identify exported class", () => { let ast = JsParser.getAst(`exports.MyClass = MyClass`) - let dummy = new ClassAnalyzer(ast.expression) + let dummy = new ClassAnalyzer(ast) Chai.expect(dummy.isExported("MyClass", "MyModule")).eq(true); Chai.expect(dummy.getName()).eq("MyClass"); + Chai.expect(dummy.getParentName()).eq("exports"); }) }) @@ -59,7 +61,7 @@ describe("Class Analyzer", () => { }) describe("getBaseClass", () => { - it("Should return class name properly", () => { + it("Should return base class name properly", () => { let ast = JsParser.getAst(` var MyClass = (function (_super) { }(MyBaseClass));`) @@ -67,12 +69,20 @@ describe("Class Analyzer", () => { Chai.expect(dummy.getBaseClass()).eq("MyBaseClass"); }) + it("Should return base class name with module", () => { + let ast = JsParser.getAst(` + var MyClass = (function (_super) { + }(MyModule.MyBaseClass));`) + let dummy = new ClassAnalyzer(ast) + Chai.expect(dummy.getBaseClass()).eq("MyBaseClass"); + }) + it("Should not error on class that has no base class", () => { let ast = JsParser.getAst(` var MyClass = (function () { }());`) let dummy = new ClassAnalyzer(ast) - Chai.expect(dummy.getBaseClass()).undefined; + Chai.expect(dummy.getBaseClass()).null; }) }) }) \ No newline at end of file diff --git a/test/analyzers/class-decorator-analyzer.spec.ts b/test/analyzers/class-decorator-analyzer.spec.ts index 5c6ac0e..7e3f440 100644 --- a/test/analyzers/class-decorator-analyzer.spec.ts +++ b/test/analyzers/class-decorator-analyzer.spec.ts @@ -13,7 +13,7 @@ describe("Class Decorator Analyzer", () => { tslib_1.__metadata("design:paramtypes", []) ], MyClass); `) - let dummy = new ClassDecoratorAnalyzer(ast.expression) + let dummy = new ClassDecoratorAnalyzer(ast) Chai.expect(dummy.isDecorator()).eq(true); }) @@ -24,7 +24,7 @@ describe("Class Decorator Analyzer", () => { __metadata("design:paramtypes", []) ], MyClass); `) - let dummy = new ClassDecoratorAnalyzer(ast.expression) + let dummy = new ClassDecoratorAnalyzer(ast) Chai.expect(dummy.isDecorator()).eq(true); }) }) @@ -37,7 +37,7 @@ describe("Class Decorator Analyzer", () => { tslib_1.__metadata("design:paramtypes", []) ], MyClass); `) - let dummy = new ClassDecoratorAnalyzer(ast.expression) + let dummy = new ClassDecoratorAnalyzer(ast) Chai.expect(dummy.getClassName()).eq("MyClass"); }) diff --git a/test/analyzers/method-decorator-analyzer.spec.ts b/test/analyzers/method-decorator-analyzer.spec.ts index 150e8f2..f371fbd 100644 --- a/test/analyzers/method-decorator-analyzer.spec.ts +++ b/test/analyzers/method-decorator-analyzer.spec.ts @@ -16,7 +16,7 @@ describe("Method Decorator Analyzer", () => { tslib_1.__metadata("design:returntype", void 0) ], MyClass.prototype, "myMethod", null); `) - let dummy = new MethodDecoratorAnalyzer(ast.expression) + let dummy = new MethodDecoratorAnalyzer(ast) Chai.expect(dummy.isDecorator()).eq(true); }) it("Should identify class decorator without tslib", () => { @@ -29,7 +29,7 @@ describe("Method Decorator Analyzer", () => { __metadata("design:returntype", void 0) ], MyClass.prototype, "myMethod", null); `) - let dummy = new MethodDecoratorAnalyzer(ast.expression) + let dummy = new MethodDecoratorAnalyzer(ast) Chai.expect(dummy.isDecorator()).eq(true); }) }) @@ -45,7 +45,7 @@ describe("Method Decorator Analyzer", () => { tslib_1.__metadata("design:returntype", void 0) ], MyClass.prototype, "myMethod", null); `) - let dummy = new MethodDecoratorAnalyzer(ast.expression) + let dummy = new MethodDecoratorAnalyzer(ast) Chai.expect(dummy.getClassName()).eq("MyClass"); }) @@ -69,7 +69,7 @@ describe("Method Decorator Analyzer", () => { tslib_1.__metadata("design:returntype", void 0) ], MyClass.prototype, "myMethod", null); `) - let dummy = new MethodDecoratorAnalyzer(ast.expression) + let dummy = new MethodDecoratorAnalyzer(ast) Chai.expect(dummy.getMethodName()).eq("myMethod"); }) diff --git a/test/myclass.ts b/test/myclass.ts index e91ffb4..21efd48 100644 --- a/test/myclass.ts +++ b/test/myclass.ts @@ -1,14 +1,13 @@ function decoOne() { - return function (...args) { }; + return function(...args) { }; } function decoTwo(param: string) { - return function (...args) { }; + return function(...args) { }; } export module MyModule { - @decoTwo("helo world!") export class MyBaseClass { constructor() { } @decoOne() @@ -22,12 +21,9 @@ export module MyModule { } -export class OuterClass{ - myOtherMethod(){} +@decoOne() +export class MyClass extends MyModule.MyBaseClass { + constructor() { super() } + @decoOne() + myMethod( @decoOne() par1) { } } - -export module ParentModule.ChildModule{ - export class MyClass{} -} - - diff --git a/test/transformers/ts-class-export.spec.ts b/test/transformers/ts-class-export.spec.ts new file mode 100644 index 0000000..48916fc --- /dev/null +++ b/test/transformers/ts-class-export.spec.ts @@ -0,0 +1,56 @@ +import * as Core from "../../src/core" +import { TsClassExporterTransformer } from "../../src/transformers/ts-class-export" +import { JsParser } from "../helper" +import * as Chai from "chai" + + +describe("TsClassExporterTransformer", () => { + + it("Should identify export class properly", () => { + let ast = JsParser.getAst(` + MyModule.MyClass = MyClass + `) + let dummy = new TsClassExporterTransformer(); + let parent = { + type: "Module", + name: "MyModule", + analysis: Core.AnalysisType.Valid, + children: [{ + type: "Class", + name: "MyClass", + analysis: Core.AnalysisType.Candidate | Core.AnalysisType.HasMethod, + methods: [] + }] + } + dummy.transform(ast, parent); + let clazz = parent.children[0]; + Chai.expect(Core.flag(clazz.analysis, Core.AnalysisType.Exported)).true + Chai.expect(Core.flag(clazz.analysis, Core.AnalysisType.Valid)).true + }) + + it("Should not error if provided TS class", () => { + let ast = JsParser.getAst(` + var MyClass = (function () { + function MyClass() { + } + MyClass.prototype.myMethod = function (par1) { }; + return MyClass; + }()); + `) + let dummy = new TsClassExporterTransformer(); + let parent = { + type: "Module", + name: "MyModule", + analysis: Core.AnalysisType.Valid, + children: [{ + type: "Class", + name: "MyClass", + analysis: Core.AnalysisType.Candidate, + methods: [] + }] + } + dummy.transform(ast, parent); + let clazz = parent.children[0]; + Chai.expect(Core.flag(clazz.analysis, Core.AnalysisType.Exported)).false; + }) +}) \ No newline at end of file diff --git a/test/transformers/ts-class.spec.ts b/test/transformers/ts-class.spec.ts new file mode 100644 index 0000000..c59987d --- /dev/null +++ b/test/transformers/ts-class.spec.ts @@ -0,0 +1,138 @@ + +import * as Core from "../../src/core" +import { TsClassTransformer } from "../../src/transformers/ts-class" +import { JsParser } from "../helper" +import * as Chai from "chai" + + +describe("TsClassTransformer", () => { + + it("Should identify class properly", () => { + let ast = JsParser.getAst(` + var MyClass = (function () { + function MyClass() { + } + MyClass.prototype.myMethod = function (par1) { }; + return MyClass; + }()); + `) + let dummy = new TsClassTransformer(); + let parent = { + type: "Module", + name: "MyModule", + analysis: Core.AnalysisType.Valid, + children: [] + } + dummy.transform(ast, parent); + let clazz = parent.children[0]; + Chai.expect(clazz).deep.eq({ + type: "Class", + name: "MyClass", + baseClass: null, + location: { + column: 8, line: 2 + }, + analysis: Core.AnalysisType.Candidate + | Core.AnalysisType.HasConstructor + | Core.AnalysisType.HasMethod, + constructor: { + type: "Constructor", + analysis: Core.AnalysisType.Valid, + location: { + column: 12, line: 3 + }, + name: "MyClass", + parameters: [] + }, + methods: [{ + type: "Method", + name: "myMethod", + analysis: Core.AnalysisType.Valid, + location: { + column: 12, line: 5 + }, + parameters: [{ + type: "Parameter", + name: "par1", + analysis: Core.AnalysisType.Valid, + location: { + column: 51, line: 5 + }, + }] + }] + }); + }) + + it("Should identify class with inheritance", () => { + let ast = JsParser.getAst(` + var MyClass = (function (_super) { + tslib_1.__extends(MyClass, _super); + function MyClass() { + return _super.call(this) || this; + } + MyClass.prototype.myMethod = function (par1) { }; + return MyClass; + }(MyModule.MyBaseClass)); + `) + let dummy = new TsClassTransformer(); + let parent = { + type: "Module", + name: "MyModule", + analysis: Core.AnalysisType.Valid, + children: [] + } + dummy.transform(ast, parent); + let clazz = parent.children[0]; + Chai.expect(clazz).deep.eq({ + type: "Class", + name: "MyClass", + baseClass: "MyBaseClass", + location: { + column: 8, line: 2 + }, + analysis: Core.AnalysisType.Candidate + | Core.AnalysisType.HasConstructor + | Core.AnalysisType.HasMethod, + constructor: { + type: "Constructor", + analysis: Core.AnalysisType.Valid, + location: { + column: 12, line: 4 + }, + name: "MyClass", + parameters: [] + }, + methods: [{ + type: "Method", + name: "myMethod", + analysis: Core.AnalysisType.Valid, + location: { + column: 12, line: 7 + }, + parameters: [{ + type: "Parameter", + name: "par1", + analysis: Core.AnalysisType.Valid, + location: { + column: 51, line: 7 + }, + }] + }] + }); + }) + + it("Should not error when provided 'exports'", () => { + let ast = JsParser.getAst(` + exports.MyClass = MyClass + `) + let dummy = new TsClassTransformer(); + let parent = { + type: "Module", + name: "MyModule", + analysis: Core.AnalysisType.Valid, + children: [] + } + dummy.transform(ast, parent); + Chai.expect(parent.children.length).eq(0); + }) +}) \ No newline at end of file diff --git a/test/transformers/ts-decorator.spec.ts b/test/transformers/ts-decorator.spec.ts new file mode 100644 index 0000000..c881280 --- /dev/null +++ b/test/transformers/ts-decorator.spec.ts @@ -0,0 +1,125 @@ +import * as Core from "../../src/core" +import { TsDecorator } from "../../src/transformers/ts-decorator" +import { JsParser } from "../helper" +import * as Chai from "chai" + + +describe("TsDecorator", () => { + + it("Should identify method decorator properly", () => { + let ast = JsParser.getAst(` + tslib_1.__decorate([ + decoOne("param"), + tslib_1.__param(0, decoOne("param")), + tslib_1.__metadata("design:type", Function), + tslib_1.__metadata("design:paramtypes", [Object]), + tslib_1.__metadata("design:returntype", void 0) + ], MyClass.prototype, "myMethod", null); + `) + let dummy = new TsDecorator(); + let parent = { + type: "Module", + name: "MyModule", + analysis: Core.AnalysisType.Valid, + children: [{ + type: "Class", + name: "MyClass", + analysis: Core.AnalysisType.Valid, + methods: [{ + type: "Method", + name: "myMethod", + analysis: Core.AnalysisType.Valid, + parameters: [{ + type: "Parameter", + name: "par1" + }] + }] + }] + } + dummy.transform(ast, parent); + let clazz = parent.children[0]; + Chai.expect(clazz.methods[0].decorators[0]).deep.eq({ + type: "Decorator", + name: "decoOne", + analysis: Core.AnalysisType.Valid, + location: { + column: 12, line: 3 + }, + parameters: [{ + type: "Parameter", + name: "param", + analysis: Core.AnalysisType.Valid, + location: { + column: 20, line: 3 + } + }] + }); + }) + + it("Should identify class decorator properly", () => { + let ast = JsParser.getAst(` + MyClass = tslib_1.__decorate([ + decoOne("param"), + tslib_1.__metadata("design:paramtypes", []) + ], MyClass); + `) + let dummy = new TsDecorator(); + let parent = { + type: "Module", + name: "MyModule", + analysis: Core.AnalysisType.Valid, + children: [{ + type: "Class", + name: "MyClass", + analysis: Core.AnalysisType.Valid, + methods: [] + }] + } + dummy.transform(ast, parent); + let clazz = parent.children[0]; + Chai.expect(clazz.decorators[0]).deep.eq({ + type: "Decorator", + name: "decoOne", + analysis: Core.AnalysisType.Valid, + location: { + column: 12, line: 3 + }, + parameters: [{ + type: "Parameter", + name: "param", + analysis: Core.AnalysisType.Valid, + location: { + column: 20, line: 3 + } + }] + }); + }) + + + + it("Should not error if provided TS class", () => { + let ast = JsParser.getAst(` + var MyClass = (function () { + function MyClass() { + } + MyClass.prototype.myMethod = function (par1) { }; + return MyClass; + }()); + `) + let dummy = new TsDecorator(); + let parent = { + type: "Module", + name: "MyModule", + analysis: Core.AnalysisType.Valid, + children: [{ + type: "Class", + name: "MyClass", + analysis: Core.AnalysisType.Valid, + methods: [] + }] + } + dummy.transform(ast, parent); + let clazz = parent.children[0]; + Chai.expect(clazz.decorators).undefined; + }) +}) \ No newline at end of file From 7cdd06dffc1d334434df55c931507350a6821b90 Mon Sep 17 00:00:00 2001 From: Ketut Sandiarsa Date: Wed, 25 Jan 2017 14:40:12 +0800 Subject: [PATCH 3/9] Adding unit test for transformer --- .vscode/settings.json | 2 + src/analyzers/class-analyzer.ts | 5 +- src/transformer.ts | 6 +- src/transformers/ts-child-decorator.ts | 2 +- src/transformers/ts-class.ts | 2 +- src/transformers/ts-decorator.ts | 21 ++- src/transformers/ts-module.ts | 7 +- test/dummy/dummy.ts | 22 +++ test/myclass.ts | 29 ---- test/transformer.spec.ts | 172 +++++++++++++++++++ test/transformers/ts-child-decorator.spec.ts | 10 +- test/transformers/ts-class.spec.ts | 4 +- test/transformers/ts-file.spec.ts | 0 test/transformers/ts-module.spec.ts | 51 +++++- 14 files changed, 273 insertions(+), 60 deletions(-) create mode 100644 test/dummy/dummy.ts delete mode 100644 test/myclass.ts create mode 100644 test/transformer.spec.ts create mode 100644 test/transformers/ts-file.spec.ts diff --git a/.vscode/settings.json b/.vscode/settings.json index 66be4d9..9e2d465 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -9,4 +9,6 @@ "**/.hg": true, "**/.DS_Store": true } +, +"vsicons.presets.angular": false } \ No newline at end of file diff --git a/src/analyzers/class-analyzer.ts b/src/analyzers/class-analyzer.ts index c7e1c50..2266c22 100644 --- a/src/analyzers/class-analyzer.ts +++ b/src/analyzers/class-analyzer.ts @@ -4,7 +4,7 @@ import * as HP from "./helper" export class ClassAnalyzer { /** - * expect AssignmentExpression, ExpressionStatement + * expect VariableDeclaration, ExpressionStatement */ constructor(private node) { } @@ -42,9 +42,6 @@ export class ClassAnalyzer { return null; } - /** - * expect VariableDeclaration - */ isCandidate() { return this.node.type == SyntaxKind.VariableDeclaration && this.node.declarations[0].type == SyntaxKind.VariableDeclarator diff --git a/src/transformer.ts b/src/transformer.ts index e5da669..763bbd2 100644 --- a/src/transformer.ts +++ b/src/transformer.ts @@ -15,17 +15,13 @@ export class Transformer { children: [], location: ast.loc.start } - let fileTransformer = new FileTransformer(this.fileName) + let fileTransformer = new FileTransformer() fileTransformer.transform(ast, file) return file; } } class FileTransformer extends TransformerBase { - constructor(private fileName: string) { - super() - } - transform(node, parent: MetaData) { this.traverse(node.program.body, parent, [ new TsClassTransformer(), diff --git a/src/transformers/ts-child-decorator.ts b/src/transformers/ts-child-decorator.ts index 870ce35..fb230af 100644 --- a/src/transformers/ts-child-decorator.ts +++ b/src/transformers/ts-child-decorator.ts @@ -2,7 +2,7 @@ import * as H from "../analyzers/helper" import * as Core from "../core" -export class TsChildDecorator extends Core.TransformerBase { +export class TsChildDecoratorTransformer extends Core.TransformerBase { @Core.Call.when(Core.SyntaxKind.CallExpression) transform(node, parent: Core.MethodMetaData | Core.ClassMetaData) { if (!H.isReservedDecorator(node)) { diff --git a/src/transformers/ts-class.ts b/src/transformers/ts-class.ts index ea843e1..4208bed 100644 --- a/src/transformers/ts-class.ts +++ b/src/transformers/ts-class.ts @@ -5,7 +5,7 @@ import { ParameterTransformer } from "./parameter" import * as Core from "../core" export class TsClassTransformer extends Core.TransformerBase { - + @Core.Call.when(Core.SyntaxKind.VariableDeclaration) transform(node, parent: Core.ParentMetaData) { let analyzer = new ClassAnalyzer(node); if (analyzer.isCandidate()) { diff --git a/src/transformers/ts-decorator.ts b/src/transformers/ts-decorator.ts index 6d0c236..b062f40 100644 --- a/src/transformers/ts-decorator.ts +++ b/src/transformers/ts-decorator.ts @@ -2,11 +2,12 @@ import * as H from "../analyzers/helper" import { ClassDecoratorAnalyzer } from "../analyzers/class-decorator-analyzer" import { MethodDecoratorAnalyzer } from "../analyzers/method-decorator-analyzer" import * as Core from "../core" -import { TsChildDecorator } from "./ts-child-decorator" +import { TsChildDecoratorTransformer } from "./ts-child-decorator" export class TsDecorator extends Core.TransformerBase { @Core.Call.when(Core.SyntaxKind.ExpressionStatement) transform(node, parent: Core.ParentMetaData) { + if (!parent.children) return; let ca = new ClassDecoratorAnalyzer(node) let ma = new MethodDecoratorAnalyzer(node) if (ma.isDecorator()) { @@ -21,17 +22,21 @@ export class TsDecorator extends Core.TransformerBase { let methodName = analyzers.getMethodName(); let className = analyzers.getClassName(); let clazz = parent.children.filter(x => x.name == className)[0]; - let method = clazz.methods.filter(x => x.name == methodName)[0] - this.traverse(node.expression.arguments[0].elements, method, [ - new TsChildDecorator() - ]) + if (clazz && clazz.methods) { + let method = clazz.methods.filter(x => x.name == methodName)[0] + this.traverse(node.expression.arguments[0].elements, method, [ + new TsChildDecoratorTransformer() + ]) + } } private transformClass(node, parent: Core.ParentMetaData, analyzer: ClassDecoratorAnalyzer) { let className = analyzer.getClassName(); let clazz = parent.children.filter(x => x.name == className)[0]; - this.traverse(node.expression.right.arguments[0].elements, clazz, [ - new TsChildDecorator() - ]) + if (clazz) { + this.traverse(node.expression.right.arguments[0].elements, clazz, [ + new TsChildDecoratorTransformer() + ]) + } } } \ No newline at end of file diff --git a/src/transformers/ts-module.ts b/src/transformers/ts-module.ts index 3b2750e..de5606c 100644 --- a/src/transformers/ts-module.ts +++ b/src/transformers/ts-module.ts @@ -32,12 +32,11 @@ export class TsModuleTransformer extends Core.TransformerBase { let connected = module.children.length > 0 && module.children.some(x => Core.flag(x.analysis, Core.AnalysisType.Valid)) let exported = analyzer.isExported(parent.type == "File" ? "exports" : parent.name); - let analysis = Core.AnalysisType.Candidate; if (exported) - analysis |= Core.AnalysisType.Exported; + module.analysis |= Core.AnalysisType.Exported; if (connected) - analysis |= Core.AnalysisType.ConnectedWithChildren + module.analysis |= Core.AnalysisType.ConnectedWithChildren if (exported && connected) - analysis |= Core.AnalysisType.Valid + module.analysis |= Core.AnalysisType.Valid } } diff --git a/test/dummy/dummy.ts b/test/dummy/dummy.ts new file mode 100644 index 0000000..1a0d6fc --- /dev/null +++ b/test/dummy/dummy.ts @@ -0,0 +1,22 @@ +function decoOne() { + return function (...args) { }; +} + +function decoTwo(param: string) { + return function (...args) { }; +} + +export module MyModule { + + export class MyBaseClass { + baseMethod(par1) { } + } + + @decoTwo("halo") + export class MyClass extends MyBaseClass { + constructor() { super() } + @decoOne() + myMethod( @decoOne() par1) { } + } +} + diff --git a/test/myclass.ts b/test/myclass.ts deleted file mode 100644 index 21efd48..0000000 --- a/test/myclass.ts +++ /dev/null @@ -1,29 +0,0 @@ -function decoOne() { - return function(...args) { }; -} - -function decoTwo(param: string) { - return function(...args) { }; -} - -export module MyModule { - - export class MyBaseClass { - constructor() { } - @decoOne() - myMethod( @decoOne() par1) { } - } - export module ChildModule { - export class ChildClass extends MyBaseClass { - childMethod() { } - } - } - -} - -@decoOne() -export class MyClass extends MyModule.MyBaseClass { - constructor() { super() } - @decoOne() - myMethod( @decoOne() par1) { } -} diff --git a/test/transformer.spec.ts b/test/transformer.spec.ts new file mode 100644 index 0000000..c992a50 --- /dev/null +++ b/test/transformer.spec.ts @@ -0,0 +1,172 @@ +import * as Core from "../src/core" +import { Transformer } from "../src/transformer" +import { JsParser } from "./helper" +import * as Chai from "chai" +import * as Fs from "fs" +import * as Path from "path" +import * as Babylon from "babylon" + + +describe("Transformer", () => { + + it("Should transform TypeScript generated file properly", () => { + let filename = "./dummy/dummy.js" + let code = Fs.readFileSync(Path.join(__dirname, filename)).toString() + let dummy = new Transformer(filename); + let ast = Babylon.parse(code); + let result = dummy.transform(ast); + Chai.expect(result).deep.eq({ + "type": "File", + "name": "./dummy/dummy.js", + "analysis": 1, + "children": [ + { + "type": "Module", + "analysis": 83, + "children": [ + { + "type": "Class", + "name": "MyBaseClass", + "baseClass": null, + "location": { + "line": 21, + "column": 4 + }, + "analysis": 31, + "constructor": { + "type": "Constructor", + "name": "MyBaseClass", + "analysis": 1, + "location": { + "line": 22, + "column": 8 + }, + "parameters": [] + }, + "methods": [ + { + "type": "Method", + "name": "baseMethod", + "analysis": 1, + "location": { + "line": 24, + "column": 8 + }, + "parameters": [ + { + "type": "Parameter", + "name": "par1", + "analysis": 1, + "location": { + "line": 24, + "column": 53 + } + } + ] + } + ] + }, + { + "type": "Class", + "name": "MyClass", + "baseClass": "MyBaseClass", + "location": { + "line": 28, + "column": 4 + }, + "analysis": 31, + "constructor": { + "type": "Constructor", + "name": "MyClass", + "analysis": 1, + "location": { + "line": 30, + "column": 8 + }, + "parameters": [] + }, + "methods": [ + { + "type": "Method", + "name": "myMethod", + "analysis": 1, + "location": { + "line": 33, + "column": 8 + }, + "parameters": [ + { + "type": "Parameter", + "name": "par1", + "analysis": 1, + "location": { + "line": 33, + "column": 47 + }, + "decorators": [ + { + "type": "Decorator", + "name": "decoOne", + "analysis": 1, + "location": { + "line": 38, + "column": 8 + }, + "parameters": [] + } + ] + } + ], + "decorators": [ + { + "type": "Decorator", + "name": "decoOne", + "analysis": 1, + "location": { + "line": 37, + "column": 8 + }, + "parameters": [] + } + ] + } + ], + "decorators": [ + { + "type": "Decorator", + "name": "decoTwo", + "analysis": 1, + "location": { + "line": 44, + "column": 8 + }, + "parameters": [ + { + "type": "Parameter", + "name": "halo", + "analysis": 1, + "location": { + "line": 44, + "column": 16 + } + } + ] + } + ] + } + ], + "location": { + "line": 20, + "column": 0 + }, + "name": "MyModule" + } + ], + "location": { + "line": 1, + "column": 0 + } + } + ) + }) +}) \ No newline at end of file diff --git a/test/transformers/ts-child-decorator.spec.ts b/test/transformers/ts-child-decorator.spec.ts index 251567f..e5eb6f1 100644 --- a/test/transformers/ts-child-decorator.spec.ts +++ b/test/transformers/ts-child-decorator.spec.ts @@ -1,14 +1,14 @@ import * as Core from "../../src/core" -import { TsChildDecorator } from "../../src/transformers/ts-child-decorator" +import { TsChildDecoratorTransformer } from "../../src/transformers/ts-child-decorator" import { JsParser } from "../helper" import * as Chai from "chai" -describe("TsChildDecorator", () => { +describe("TsChildDecoratorTransformer", () => { it("Should identify parameter decorator properly", () => { let ast = JsParser.getAst(`tslib_1.__param(0, decoOne("param"))`) - let dummy = new TsChildDecorator(); + let dummy = new TsChildDecoratorTransformer(); let parent = { type: "Method", name: "myMethod", @@ -39,7 +39,7 @@ describe("TsChildDecorator", () => { it("Should identify method decorator properly", () => { let ast = JsParser.getAst(`decoOne("param")`) - let dummy = new TsChildDecorator(); + let dummy = new TsChildDecoratorTransformer(); let parent = { type: "Method", name: "myMethod", @@ -70,7 +70,7 @@ describe("TsChildDecorator", () => { it("Should not error if provided __metadata", () => { let ast = JsParser.getAst(`tslib_1.__metadata("design:type", Function)`) - let dummy = new TsChildDecorator(); + let dummy = new TsChildDecoratorTransformer(); let parent = { type: "Method", name: "myMethod", diff --git a/test/transformers/ts-class.spec.ts b/test/transformers/ts-class.spec.ts index c59987d..2929139 100644 --- a/test/transformers/ts-class.spec.ts +++ b/test/transformers/ts-class.spec.ts @@ -25,7 +25,7 @@ describe("TsClassTransformer", () => { } dummy.transform(ast, parent); let clazz = parent.children[0]; - Chai.expect(clazz).deep.eq({ + Chai.expect(clazz).deep.eq({ type: "Class", name: "MyClass", baseClass: null, @@ -83,7 +83,7 @@ describe("TsClassTransformer", () => { } dummy.transform(ast, parent); let clazz = parent.children[0]; - Chai.expect(clazz).deep.eq({ + Chai.expect(clazz).deep.eq({ type: "Class", name: "MyClass", baseClass: "MyBaseClass", diff --git a/test/transformers/ts-file.spec.ts b/test/transformers/ts-file.spec.ts new file mode 100644 index 0000000..e69de29 diff --git a/test/transformers/ts-module.spec.ts b/test/transformers/ts-module.spec.ts index 3dc569a..ca0715c 100644 --- a/test/transformers/ts-module.spec.ts +++ b/test/transformers/ts-module.spec.ts @@ -1,5 +1,54 @@ -import * as Core from "../../src/core" +import * as Core from "../../src/core" import { TsModuleTransformer } from "../../src/transformers/ts-module" import { JsParser } from "../helper" import * as Chai from "chai" + +describe("TsModuleTransformer", () => { + + it("Should identify module properly", () => { + let ast = JsParser.getAst(` + (function (ChildModule) { + + })(ChildModule = MyModule.ChildModule || (MyModule.ChildModule = {})); + `) + let dummy = new TsModuleTransformer(); + let parent = { + type: "Module", + name: "MyModule", + analysis: Core.AnalysisType.Valid, + children: [] + } + dummy.transform(ast, parent); + Chai.expect(parent.children[0]).deep.eq({ + type: "Module", + name: "ChildModule", + location: { + column: 8, line: 2 + }, + analysis: Core.AnalysisType.Candidate + | Core.AnalysisType.Exported, + children: [] + }); + }) + + it("Should not error when provided class", () => { + let ast = JsParser.getAst(` + var MyClass = (function () { + function MyClass() { + } + MyClass.prototype.myMethod = function (par1) { }; + return MyClass; + }()); + `) + let dummy = new TsModuleTransformer(); + let parent = { + type: "Module", + name: "MyModule", + analysis: Core.AnalysisType.Valid, + children: [] + } + dummy.transform(ast, parent); + Chai.expect(parent.children.length).eq(0); + }) +}) \ No newline at end of file From 5977da24b214e13c8e653d786f483f84ea2be6f0 Mon Sep 17 00:00:00 2001 From: Ketut Sandiarsa Date: Wed, 25 Jan 2017 15:04:23 +0800 Subject: [PATCH 4/9] Merging from branch --- package.json | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 5c75dd0..932bc40 100644 --- a/package.json +++ b/package.json @@ -1,10 +1,12 @@ { "name": "kecubung", - "version": "0.0.1-dev001", + "version": "0.0.1", "description": "Javascript transformer to Type Metadata", "main": "lib/index.js", "scripts": { - "test": "gulp" + "test": "gulp", + "preversion": "npm test", + "postversion": "git push && git push --tags" }, "keywords": [], "author": "Ketut Sandiarsa ", @@ -35,4 +37,4 @@ "run-sequence": "^1.2.2", "typescript": "^2.0.10" } -} +} \ No newline at end of file From b447414cb5e505943856edfaf0a8eef6c5ea491a Mon Sep 17 00:00:00 2001 From: Ketut Sandiarsa Date: Wed, 25 Jan 2017 15:05:41 +0800 Subject: [PATCH 5/9] 0.0.2-0 --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 932bc40..29bb68b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "kecubung", - "version": "0.0.1", + "version": "0.0.2-0", "description": "Javascript transformer to Type Metadata", "main": "lib/index.js", "scripts": { @@ -37,4 +37,4 @@ "run-sequence": "^1.2.2", "typescript": "^2.0.10" } -} \ No newline at end of file +} From 8a0560a76709e51254df6ec55282893f0c259a8d Mon Sep 17 00:00:00 2001 From: ktutnik Date: Thu, 26 Jan 2017 14:05:08 +0800 Subject: [PATCH 6/9] Adding gulp clean --- gulpfile.js | 110 ++++++++++++++++++++++++++++++++------------------- package.json | 1 + 2 files changed, 70 insertions(+), 41 deletions(-) diff --git a/gulpfile.js b/gulpfile.js index b86b67a..6a0ec70 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -1,86 +1,114 @@ "use strict"; -var gulp = require("gulp"), - tsc = require("gulp-typescript"), +var gulp = require("gulp"), + tsc = require("gulp-typescript"), + del = require("del"), runSequence = require("run-sequence"), - mocha = require("gulp-mocha"), - istanbul = require("gulp-istanbul"); + mocha = require("gulp-mocha"), + istanbul = require("gulp-istanbul"); +//********CLEAN ************ + +gulp.task("clean-source", function (cb) { + return del([ + "./src/**/*.js", + "./src/**/*.d.ts", + "./src/**/*.js.map"], cb) +}) + +gulp.task("clean-test", function (cb) { + return del([ + "./test/**/*.js", + "./test/**/*.d.ts", + "./test/**/*.js.map"], cb) +}) + +gulp.task("clean-lib", function (cb) { + return del([ + "./lib/**/*.js", + "./lib/**/*.d.ts", + "./lib/**/*.js.map"], cb) +}) + + +gulp.task("clean", function (cb) { + runSequence("clean-source", "clean-test", "clean-lib", cb); +}); //******** BUILD ************* var tsProject = tsc.createProject("tsconfig.json", { declaration: false, noResolve: false, - typescript: require("typescript") + typescript: require("typescript") }); -gulp.task("build-source", function() { +gulp.task("build-source", function () { return gulp.src([ "src/**/**.ts" ]) - .pipe(tsProject()) - .on("error", function (err) { - process.exit(1); - }) - .pipe(gulp.dest("src/")); + .pipe(tsProject()) + .on("error", function (err) { + process.exit(1); + }) + .pipe(gulp.dest("src/")); }); var tsTestProject = tsc.createProject("tsconfig.json", { declaration: false, noResolve: false, - typescript: require("typescript") + typescript: require("typescript") }); -gulp.task("build-test", function() { +gulp.task("build-test", function () { return gulp.src([ "test/**/**.ts" ]) - .pipe(tsTestProject()) - .on("error", function (err) { - process.exit(1); - }) - .pipe(gulp.dest("test")); + .pipe(tsTestProject()) + .on("error", function (err) { + process.exit(1); + }) + .pipe(gulp.dest("test")); }); -gulp.task("build", function(cb) { - runSequence("build-source", "build-test", cb); +gulp.task("build", function (cb) { + runSequence("build-source", "build-test", cb); }); //******** TEST ************* -gulp.task("mocha", function() { - return gulp.src([ - "test/**/**.js" +gulp.task("mocha", function () { + return gulp.src([ + "test/**/**.js" ]) - .pipe(mocha({ui: "bdd"})) - .pipe(istanbul.writeReports()); + .pipe(mocha({ ui: "bdd" })) + .pipe(istanbul.writeReports()); }); -gulp.task("istanbul:hook", function() { - return gulp.src(["src/**/**.js"]) - // Covering files - .pipe(istanbul()) - // Force `require` to return covered files - .pipe(istanbul.hookRequire()); +gulp.task("istanbul:hook", function () { + return gulp.src(["src/**/**.js"]) + // Covering files + .pipe(istanbul()) + // Force `require` to return covered files + .pipe(istanbul.hookRequire()); }); -gulp.task("test", function(cb) { - runSequence("istanbul:hook", "mocha", cb); +gulp.task("test", function (cb) { + runSequence("istanbul:hook", "mocha", cb); }); //******** DISTRIBUTION ************* -gulp.task("dist", function() { - return gulp.src(["src/**/*.js", "src/**/*.d.ts"]) - .pipe(gulp.dest("lib/")); +gulp.task("dist", function () { + return gulp.src(["src/**/*.js", "src/**/*.d.ts"]) + .pipe(gulp.dest("lib/")); }); //******** DEFAULT ************* gulp.task("default", function (cb) { - runSequence( - "build", - "test", - "dist", - cb); + runSequence( + "build", + "test", + "dist", + cb); }); \ No newline at end of file diff --git a/package.json b/package.json index 29bb68b..4d918a3 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,7 @@ "babylon": "^6.14.1", "chai": "^3.5.0", "coveralls": "^2.11.15", + "del": "^2.2.2", "gulp": "^3.9.1", "gulp-istanbul": "^1.1.1", "gulp-mocha": "^3.0.1", From 51c4190b86da48b4066c1a11ab7f435d9dce6889 Mon Sep 17 00:00:00 2001 From: ktutnik Date: Thu, 26 Jan 2017 14:41:17 +0800 Subject: [PATCH 7/9] Fixing gulp distribution task, Fixing npm version automation --- gulpfile.js | 34 +++++++++++++++++++++++----------- package.json | 10 +++++----- 2 files changed, 28 insertions(+), 16 deletions(-) diff --git a/gulpfile.js b/gulpfile.js index 6a0ec70..ad10c41 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -95,20 +95,32 @@ gulp.task("istanbul:hook", function () { }); gulp.task("test", function (cb) { - runSequence("istanbul:hook", "mocha", cb); + runSequence("build", "istanbul:hook", "mocha", cb); }); //******** DISTRIBUTION ************* -gulp.task("dist", function () { +gulp.task("build-lib", function () { return gulp.src(["src/**/*.js", "src/**/*.d.ts"]) - .pipe(gulp.dest("lib/")); + .pipe(gulp.dest("lib/src/")); }); -//******** DEFAULT ************* -gulp.task("default", function (cb) { - runSequence( - "build", - "test", - "dist", - cb); -}); \ No newline at end of file +var tsDtsProject = tsc.createProject("tsconfig.json", { + declaration: true, + noResolve: false, + typescript: require("typescript") +}); + +gulp.task("build-dts", function() { + return gulp.src([ + "src/**/*.ts" + ]) + .pipe(tsDtsProject()) + .on("error", function (err) { + process.exit(1); + }) + .dts.pipe(gulp.dest("lib/dts")); +}); + +gulp.task("dist", function (cb) { + runSequence("clean-lib", "build", "build-lib", "build-dts", cb); +}); diff --git a/package.json b/package.json index 4d918a3..a77c392 100644 --- a/package.json +++ b/package.json @@ -2,20 +2,20 @@ "name": "kecubung", "version": "0.0.2-0", "description": "Javascript transformer to Type Metadata", - "main": "lib/index.js", + "main": "./lib/src/index.js", + "typings": "./lib/dts/index.d.ts", "scripts": { "test": "gulp", - "preversion": "npm test", + "preversion": "npm test && gulp dist", "postversion": "git push && git push --tags" }, - "keywords": [], + "keywords": ["reflection", "metadata", "typesystem", "babylon", "typescript"], "author": "Ketut Sandiarsa ", "license": "MIT", "repository": { "type": "git", "url": "https://github.com/kambojajs/kecubung" }, - "typings": "./lib/index.d.ts", "dependencies": { "reflect-metadata": "^0.1.9", "tslib": "^1.4.0" @@ -38,4 +38,4 @@ "run-sequence": "^1.2.2", "typescript": "^2.0.10" } -} +} \ No newline at end of file From 80a1bfb44ac4cad0227fc8e31b516585302ee881 Mon Sep 17 00:00:00 2001 From: ktutnik Date: Thu, 26 Jan 2017 14:48:29 +0800 Subject: [PATCH 8/9] Adding default on gulpfile --- gulpfile.js | 14 ++++++++++++-- package.json | 2 +- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/gulpfile.js b/gulpfile.js index ad10c41..336a411 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -95,7 +95,7 @@ gulp.task("istanbul:hook", function () { }); gulp.task("test", function (cb) { - runSequence("build", "istanbul:hook", "mocha", cb); + runSequence("istanbul:hook", "mocha", cb); }); //******** DISTRIBUTION ************* @@ -122,5 +122,15 @@ gulp.task("build-dts", function() { }); gulp.task("dist", function (cb) { - runSequence("clean-lib", "build", "build-lib", "build-dts", cb); + runSequence("build-lib", "build-dts", cb); }); + +/** DEFAULT */ +gulp.task("default", function (cb) { + runSequence( + "clean", + "build", + "test", + "dist", + cb); +}); \ No newline at end of file diff --git a/package.json b/package.json index a77c392..92f0621 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "typings": "./lib/dts/index.d.ts", "scripts": { "test": "gulp", - "preversion": "npm test && gulp dist", + "preversion": "gulp", "postversion": "git push && git push --tags" }, "keywords": ["reflection", "metadata", "typesystem", "babylon", "typescript"], From 1ee630d2857fef0d99e7a4d8162ac311be3ebfcc Mon Sep 17 00:00:00 2001 From: ktutnik Date: Thu, 26 Jan 2017 14:49:17 +0800 Subject: [PATCH 9/9] 0.0.2-1 --- package.json | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 92f0621..6805dcc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "kecubung", - "version": "0.0.2-0", + "version": "0.0.2-1", "description": "Javascript transformer to Type Metadata", "main": "./lib/src/index.js", "typings": "./lib/dts/index.d.ts", @@ -9,7 +9,13 @@ "preversion": "gulp", "postversion": "git push && git push --tags" }, - "keywords": ["reflection", "metadata", "typesystem", "babylon", "typescript"], + "keywords": [ + "reflection", + "metadata", + "typesystem", + "babylon", + "typescript" + ], "author": "Ketut Sandiarsa ", "license": "MIT", "repository": { @@ -38,4 +44,4 @@ "run-sequence": "^1.2.2", "typescript": "^2.0.10" } -} \ No newline at end of file +}