-
Notifications
You must be signed in to change notification settings - Fork 14
development
-
The TypeScript merger has his own objects that defines the respective TypeScrit AST node.
-
Each definition must have his own toString() and merge() methods.
-
If the new node object will have identifier and type, it must extends the GeneralInterface class. The GeneralInterface class only has identifier and type attributes as String type.
-
The new object class must has setter/getters for every attribute and add element methods for array attributes.
For example, a ClassDeclaration node is defined at the ClassDeclaration.ts:
export class ClassDeclaration extends GeneralInterface {
private decorators: Decorator [] = [];
private heritages: String[] = [];
private modifiers: String[] = [];
private methods: Method[] = [];
private properties: PropertyDeclaration[] = [];
private construct: Constructor = new Constructor();
}
the current definitions are:
-
TSFile:
-
Array of ImportClause
-
A ClassDeclaration
-
Array of VariableStatement
-
-
ClassDeclaration
-
Array of Decorator
-
Array of PropertyDeclaration
-
Array of Method
-
A Constructor
-
-
Method
-
Array of Decorator
-
Array of Parameter
-
A BodyMethod
-
-
PropertyDeclaration
-
Array of Decorator
-
Reading the TS code from the input files we get the AST. Afterwards, the AST is read in a loop to get the different node type. Once the node is a node type we want to map, just we apply the correspondent mapping method from the MappingTool. The mapping method are appliead in cascade starting from the TSFile mapping:
-
Read the source TS files:
let sourceFilePatch: ts.SourceFile = ts.createSourceFile(filePatch, fs.readFileSync(filePatch).toString(), ts.ScriptTarget.ES2016, false);
let sourceFile: ts.SourceFile = ts.createSourceFile(fileBase, fs.readFileSync(fileBase).toString(), ts.ScriptTarget.ES2016, false);
-
Looping over the AST:
sourceFile.getChildAt(0).getChildren().forEach(child => {
...
..
.
}
-
Every TypeScript node has a type number. This number can change from one version of TypeScript to another, so we must use the SyntaxKind library:
ts.SyntaxKind.ImportDeclaration ts.SyntaxKind.ClassDeclaration ... .. .
-
Map the AST node into our defined object (the start point will be the TSFile mapping):
export function mapFile(sourceFile: ts.SourceFile) {
let file: TSFile = new TSFile();
sourceFile.getChildAt(0).getChildren().forEach(child => {
switch(child.kind){
case ts.SyntaxKind.ImportDeclaration:
file.addImport(mapImport((<ts.ImportDeclaration>child)));
break;
case ts.SyntaxKind.ClassDeclaration:
file.addClass(mapClass(<ts.ClassDeclaration>child, sourceFile));
break;
case ts.SyntaxKind.VariableStatement:
file.addVariable(mapVariableStatement((<ts.VariableStatement>child), sourceFile));
break;
}
});
return file;
}
From here, every needed mapping will be called when needed.
In the same way as the mapping works, the merge is applied in cascade. Once we have all nodes mapped, just call the merge method from TSFile class. The merge strategy must be passed as argument to these methods.
baseFile.merge(patchFile, patchOverrides);
The merge method from TSFile will make use of MergeTool the perform the correspondent merge process for every definition.
merge(patchFile: TSFile, patchOverrides: boolean) {
mergeTools.mergeImports(this, patchFile);
mergeTools.mergeClass(this, patchFile, patchOverrides);
mergeTools.mergeVariables(this, patchFile, patchOverrides);
}
The merge for every definiton are called:
baseObject.merge(patchObject, patchOverrides)
Being baseObject and patchObject of the same definition type.
To get the resultant code as string, it works in the same way as the Map and merge proccess do. Just call the toString() method from the TSFile object and it will call every toString() method from every object when needed in cascade. The resultant string code can be written into a TS file if the '-o' argument is defined.