Custom Transform (Plugin ) System
References
https://dev.doctorevidence.com/how-to-write-a-typescript-transform-plugin-fc5308fdd943
This article (https://levelup.gitconnected.com/writing-typescript-custom-ast-transformer-part-1-7585d6916819) shows how to include transform in compile process, using a own compiler wrapper.
Why (Usage)
at this time (2020) the only way to pass compile time type information to run time (e.g. https://github.com/kimamula/ts-transformer-keys)
learn how tool chain works under the hood
"manual code review -> compile time transformation"
How it works
Compiler parse source code into AST (abstract syntax tree)
Transform manipulates AST
Then AST is written to target files ("emitted"), i.e. javascript
AST (Abstract Syntax Tree)
AST Viewer: https://ts-ast-viewer.com/#
This is a very useful tool, paste typescript code, get AST and code to generate that tree
Boilerplate & Examples
Boilerplate with visitor
import * as ts from ‘typescript’
export default function(/*opts?: Opts*/) {
function visitor(ctx: ts.TransformationContext, sf: ts.SourceFile) {
const visitor: ts.Visitor = (node: ts.Node): ts.VisitResult => {
// here we can check each node and potentially return
// new nodes if we want to leave the node as is, and
// continue searching through child nodes:
return ts.visitEachChild(node, visitor, ctx)
}
return visitor
}
return (ctx: ts.TransformationContext): ts.Transformer => {
return (sf: ts.SourceFile) => ts.visitNode(sf, visitor(ctx, sf))
}
}
AST node replacement
const visitor: ts.Visitor = (node: ts.Node): ts.VisitResult => {
if (ts.isCallExpression(node) &&
node.expression.getText(sf) == 'safely') {
// get the argument to safely
const target = node.arguments[0]
// check to make sure it is a property access, like "a.b"
if (ts.isPropertyAccessExpression(target) {
// return a binary expression with a && a.b
return ts.createBinary(
target.expression, // the left hand operand is the object
ts.SyntaxKind.AmpersandAmpersandToken, // the && operator
target) // the right hand operand is the full expression
}
}
// otherwise continue visiting all the nodes
return ts.visitEachChild(node, visitor, ctx)
}
https://github.com/microsoft/TypeScript/issues/14419#issuecomment-298814505
Using Transform
At the moment (2020 July) transformers still cannot be invoked through tsconfig.conf. Have to use programatically.
Alternatives:
- https://github.com/TypeStrong/ts-loader for Webpack
- https://github.com/TypeStrong/ts-node for REPL
- https://github.com/cevek/ttypescript for
tsc
replacement - Write your own compiler wrapper
Write own wrapper:
Compiler API: https://github.com/Microsoft/TypeScript/wiki/Using-the-Compiler-API
Read this: https://levelup.gitconnected.com/writing-typescript-custom-ast-transformer-part-1-7585d6916819
Patterns of Transform
Use a Visitor to visit each node (see boilerplate)
Screen the nodes to find the one we want to transform.
To manipulate:
- Return a replacement node