NodeCloud Code generation tool

Building a transformer using TypeScript Compiler API- Week-3 GSoC’21

Mohit Bhat
Leopards Lab
Published in
4 min readJul 11, 2021

--

Hey Welcome to week 3 of my GSoC Journey, today we will be Discussing on how to transform, merge classes, functions and other stuff from ast using typescript compiler API and will also share my progress of week 3

Tools in typescript to transform data

Okay First lets understand what are some cool tools in typescript compiler api, with which we can play and do the magical stuff

  • To check type of node in AST:
ts.isXyz(node)
Example:
ts.isVariableDeclaration(node) //To check is it a variable
ts.isMethodDeclaration(node) //To check is it a method/function

There are many more you can use according to your needs

  • To create a new node
ts.createXyz(...) 
ts.createIdentifier('world') // This creates a Identifier with name world
ts.createFunctionDeclaration(.....)// To create a function
  • To update a node
ts.updateXyz(node, ...)
ts.updateVariableDeclaration() // This updates a variable
  • For updating source file
ts.updateSourceFileNode(sourceFile, ...)
  • Setting a nodes original node
ts.setOriginalNode(newNode, originalNode)
  • Set things
ts.setXyz(...)
  • Add things
ts.addXyz(...)

These were some functions that is available under ts and factory creator for your use. There are many more functions but these are what is needed for my project for now

The dummy data/class

You might thinking what is dummy class, so basically as I am building a package for common cloud, what we normally do is we make classes and define functions in them. Here also we are doing same but our moto is to attain automation. Instead of going through all classes, importing packages in our code and then using it, I am defining a dummy class which I would have done normally with blanks on places of names, sdk function names etc. Then I am parsing the cloud SDK’s, getting all functions and then updating nodes in dummy class file according to the extracted data. This is done by transform function which I will be going to write. Lets deep dive technically into this:

The dummy class:

class ClassName {
/**
*
* @param {module} do DO SDK
* @param {object} options SDK options
*/
constructor(dosdk, dotoken) {
this._DO = dosdk;
this._instance = new this._DO(dotoken);
this._sdkClassName=this._instance.SDKClassName;
}
function() {
return new Promise((resolve, reject) => {
this._sdkClassName.SDKFunctionName()
.then(data => resolve(data))
.catch(err => reject(err));
});
}
}
module.exports = ClassName;

So here you can see this is a dummy class with a constructor and one function. you can also see instead of actual sdk class name it written “sdkClassName”. Also there is no name of function. But don’t worry we will update these by our transformation function by building ast of this dummy class and then using typescript tools to update indentifiers, names, nodes, etc of that ast.

Writing the transform function

Lets understand how transform function looks like:

import * as ts from 'typescript';

const transformer: ts.TransformerFactory<ts.SourceFile> = context => {
return sourceFile => {
const visitor = (node: ts.Node): ts.Node => {
if (ts.isIdentifier(node)) {
switch (node.escapedText) {
case 'babel':
+ return ts.createIdentifier('typescript');

case 'plugins':
+ return ts.createIdentifier('transformers');
}
}

return ts.visitEachChild(node, visitor, context);
};

return ts.visitNode(sourceFile, visitor);
};
};

export default transformer;

This is a basic transformation function : Lets understand this in details:

  • First line ts.TransformerFactory<ts.SourceFile> is a generic interface that we have extended, you can understand generic types in details here
  • we are passing context and inside the function we are returning the source file, which is again a function, these are arrow functions of JavaScript
  • Inside it we are defining the visitor, which will visit each node
  • As we are traversing the ast, we are checking if we have encountered a identifier node, if yes change its name according to the defined cases
  • Inside switch, as per the case, we are creating a new Identifier node and returning it

The flow is like this in this recursive function=>pass context

=> return sourceFile function which in turn returns VisitNode function of TypeScript taking visitor function as parameter

=>This SourceFile function contains a visitor function which checks and VisitEachChild by returning VisitEachChild function of Typescript

So basically we visit like this….

SourceFile(AST)=>Nodes=>There child Nodes=>return back to visit next node and it childs

You might say we are returning and not doing anything, but this is managed by typescript function internally.

NodeCloud Code generation Module at glance

My weekly Update

So I did pretty same as above in this week, I made a transformer which takes data from generator and transforms the dummy class with that data and generates a DO class for a specific service like kubernetes, compute etc.

I made Three Different functions in Transformer

  • addIdentifiers
  • addFunctions
  • addComments

And then was the transform function which would take the above transformer as input and do the stuff

Also to print output/Node we need to use Node Printer as normal writing of ast is not possible and results in error

const printer: ts.Printer = ts.createPrinter({
newLine: ts.NewLineKind.LineFeed,
removeComments: false
});

And to add comments in generated class code we are using SyntheticLeadingComment function of typescript

function addMultiLineComment(node, comment: string) {
ts.addSyntheticLeadingComment(
node,
ts.SyntaxKind.MultiLineCommentTrivia,
comment,
true
);
}

This Week was quite hectic but was an ultimate fun. In weekly meeting we discussed on testing and few other important points that need to be considered.

With This my code Generation module is almost completed and only the yml file generation and testing is left which is the agenda of next two weeks.

So Till then GoodBye✌️ and Have Fun!�

Follow me here…. Linkedin, Github, Twitter

--

--

Mohit Bhat
Leopards Lab

Blockchain & Full Stack Developer | GSoC’21 @ SCoReLab | Certified Ethereum Developer | Ethereum India Fellow | SIH2020 Finalist | Postman Student Expert