Skip to content
This repository has been archived by the owner on Oct 5, 2021. It is now read-only.

Commit

Permalink
feat: Implement instrumentation as a TypeScript Compiler Transformer
Browse files Browse the repository at this point in the history
Initial implementation of #41
  • Loading branch information
urish committed Jun 20, 2018
1 parent bf3239d commit 2857214
Show file tree
Hide file tree
Showing 2 changed files with 115 additions and 0 deletions.
23 changes: 23 additions & 0 deletions packages/typewiz-core/src/transformer.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import * as ts from 'typescript';
import { transformSourceCode } from './transformer';

function astPrettyPrint(sourceText: string) {
const printer: ts.Printer = ts.createPrinter();
return printer.printFile(ts.createSourceFile('test.ts', sourceText, ts.ScriptTarget.Latest));
}

describe('transformer', () => {
it('should instrument function parameters without types', () => {
const input = `function (a) { return 5; }`;
expect(transformSourceCode(input, 'test.ts')).toMatch(
astPrettyPrint(`function (a) { $_$twiz("a", a, 11, "test.ts", {}); return 5; }`),
);
});

it('should instrument class method parameters', () => {
const input = `class Foo { bar(a) { return 5; } }`;
expect(transformSourceCode(input, 'test.ts')).toMatch(
astPrettyPrint(`class Foo { bar(a) { $_$twiz("a", a, 17, "test.ts", {}); return 5; } }`),
);
});
});
92 changes: 92 additions & 0 deletions packages/typewiz-core/src/transformer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import * as ts from 'typescript';

function updateFunction(node: ts.FunctionDeclaration, instrumentStatements: ReadonlyArray<ts.Statement>) {
return ts.updateFunctionDeclaration(
node,
node.decorators,
node.modifiers,
node.asteriskToken,
node.name,
node.typeParameters,
node.parameters,
node.type,
ts.createBlock([...instrumentStatements, ...(node.body ? node.body.statements : [])]),
);
}

function updateMethod(node: ts.MethodDeclaration, instrumentStatements: ReadonlyArray<ts.Statement>) {
return ts.updateMethod(
node,
node.decorators,
node.modifiers,
node.asteriskToken,
node.name,
node.questionToken,
node.typeParameters,
node.parameters,
node.type,
ts.createBlock([...instrumentStatements, ...(node.body ? node.body.statements : [])]),
);
}

export function visitorFactory(ctx: ts.TransformationContext, source: ts.SourceFile) {
const visitor: ts.Visitor = (node: ts.Node): ts.Node => {
if (ts.isFunctionDeclaration(node) || ts.isMethodDeclaration(node)) {
const instrumentStatements: ts.Statement[] = [];
for (const param of node.parameters) {
if (!param.type && !param.initializer && node.body) {
const typeInsertionPos = param.name.getEnd() + (param.questionToken ? 1 : 0);
// const opts: IExtraOptions = {};
// if (isArrow) {
// opts.arrow = true;
// }
// if (!hasParensAroundArguments(node)) {
// opts.parens = [node.parameters[0].getStart(), node.parameters[0].getEnd()];
// }
instrumentStatements.push(
ts.createStatement(
ts.createCall(
ts.createIdentifier('$_$twiz'),
[],
[
ts.createLiteral(param.name.getText()),
ts.createIdentifier(param.name.getText()),
ts.createNumericLiteral(typeInsertionPos.toString()),
ts.createLiteral(source.fileName),
ts.createObjectLiteral(), // TODO: opts
],
),
),
);
}
if (ts.isFunctionDeclaration(node)) {
return updateFunction(node, instrumentStatements);
}
if (ts.isMethodDeclaration(node)) {
return updateMethod(node, instrumentStatements);
}
}
}

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

return visitor;
}

export function transformer() {
return (ctx: ts.TransformationContext): ts.Transformer<ts.SourceFile> => {
return (source: ts.SourceFile) => ts.visitNode(source, visitorFactory(ctx, source));
};
}

export function transformSourceFile(sourceFile: ts.SourceFile) {
return ts.transform(sourceFile, [transformer()]).transformed[0];
}

export function transformSourceCode(sourceText: string, fileName: string) {
const sourceFile = ts.createSourceFile(fileName, sourceText, ts.ScriptTarget.Latest, true);
const transformed = transformSourceFile(sourceFile);
const printer: ts.Printer = ts.createPrinter();
return printer.printFile(transformed);
}

0 comments on commit 2857214

Please sign in to comment.