Skip to content

Commit

Permalink
Merge pull request #1 from fabiandev/ts-emit
Browse files Browse the repository at this point in the history
Transformations with unofficial TypeScript API
  • Loading branch information
fabiandev authored Jan 29, 2017
2 parents 043cec9 + f1c27f4 commit ae3d27f
Show file tree
Hide file tree
Showing 47 changed files with 447 additions and 84 deletions.
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,10 @@
"escodegen": "^1.8.1",
"esprima": "^3.1.3",
"flow-runtime": "^0.2.1",
"node-require-fallback": "^0.1.2",
"tcomb": "^3.2.16",
"ts-type-info": "^6.2.2",
"tspoon": "^1.0.254",
"typescript": "^2.1.5"
"typescript": "Microsoft/TypeScript#6c122bcf16c329a21da04d7b5256f20ecc0bbd1d"
}
}
82 changes: 39 additions & 43 deletions src/compiler/Compiler.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import * as fs from 'fs';
import * as path from 'path';
import { TranspilerOutput, Visitor, transpile } from 'tspoon';
import { TsRuntimeOptions } from '../options';
import { CompilerResult } from './CompilerResult';
import { CompilerConfig } from './CompilerConfig';
import { FileResult } from './FileResult';
import * as DEFAULT_VISITORS from './visitors/default_visitors';
import * as ts from 'typescript/built/local/typescript';
import CompilerResult from './CompilerResult';
import CompilerConfig from './CompilerConfig';
import FileResult from './FileResult';
import { Transformer, DEFAULT_TRANSFORMERS } from './transformers';

export class Compiler {

Expand All @@ -16,8 +15,21 @@ export class Compiler {
public process(): Promise<CompilerResult> {
const toTransform: Array<Promise<FileResult>> = [];

const transformers: ts.Transformer[] = Object.keys(DEFAULT_TRANSFORMERS).map((key: string) => {
const transformer = new (DEFAULT_TRANSFORMERS as any)[key]();

const transform: ts.Transformer = (context) => (f) => {
for (const substitution of transformer.getSubstitutions()) {
context.enableSubstitution(substitution);
}
context.onSubstituteNode = transformer.process.bind(transformer);
return f;
};
return transform;
});

for (const file of this.config.files) {
toTransform.push(this.transformFile(file));
toTransform.push(this.transformFile(file, transformers));
}

return Promise.all(toTransform)
Expand All @@ -26,56 +38,40 @@ export class Compiler {
config: this.config,
fileResults: results,
};
})
.catch(err => {
console.error(err);
});
}

private transformFile(file: string): Promise<FileResult> {
private transformFile(filePath: string, transformers: ts.Transformer[]): Promise<FileResult> {
return new Promise((resolve, reject) => {
fs.readFile(file, this.config.options.encoding, (err, source) => {
fs.readFile(filePath, this.config.options.encoding, (err, source) => {
if (err) {
return reject(`Error reading file ${file}`);
return reject(`Error reading file ${filePath}`);
}

const visitors = Object.keys(DEFAULT_VISITORS).map((key: string) => {
return new (DEFAULT_VISITORS as any)[key]();
});
const fileName = path.basename(filePath);

const transpiler = transpile(source, {
compilerOptions: this.config.options.compilerOptions,
sourceFileName: path.basename(file),
visitors,
});
const f = ts.createSourceFile(
fileName,
source,
this.config.options.compilerOptions.target || ts.ScriptTarget.Latest,
true,
ts.ScriptKind.TS,
);

this.reportFile(transpiler);
const result = ts.emit(f, transformers).result;

resolve({
transpiler,
file,
fileName,
filePath,
result,
});
});
});
}

private reportFile(transpiler: TranspilerOutput): boolean {
if (transpiler.diags) {
for (const d of transpiler.diags) {
const position = d.file.getLineAndCharacterOfPosition(d.start);

const name = d.file.fileName;
const line = position.line + 1;
const character = position.character;
const text = d.messageText;

console.error(`-> ${name}:${line}:${character}:${text}`);
}
}

if (transpiler.halted) {
console.error('Transpiler halted. Exiting now.');
process.exit(1);
}

return !!transpiler.diags;
}

}

export default Compiler;
8 changes: 5 additions & 3 deletions src/compiler/CompilerConfig.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { TsRuntimeOptions } from '../options/TsRuntimeOptions';
import { FileResult } from './FileResult';
import Options from '../options/Options';
import FileResult from './FileResult';

export interface CompilerConfig {
files: string[];
options: TsRuntimeOptions;
options: Options;
}

export default CompilerConfig;
6 changes: 4 additions & 2 deletions src/compiler/CompilerResult.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { FileResult } from './FileResult';
import { CompilerConfig } from './CompilerConfig';
import FileResult from './FileResult';
import CompilerConfig from './CompilerConfig';

export interface CompilerResult {
config: CompilerConfig;
fileResults: FileResult[];
}

export default CompilerResult;
9 changes: 5 additions & 4 deletions src/compiler/FileResult.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { TranspilerOutput } from 'tspoon';

export interface FileResult {
transpiler: TranspilerOutput;
file: string;
result: string;
filePath: string;
fileName: string
}

export default FileResult;
File renamed without changes.
Empty file.
Empty file.
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,10 @@ export function typeCalls(type: ts.TypeNode): null | any {
return callExpression;
}
default:
throw new Error('Node Type not supported.');
{
const callExpression = utils.ast.getCallExpression('any');
return callExpression;
}
// throw new Error('Node Type not supported.');
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import * as utils from '../utils';
export function variableAssignment(name: string, assignment: ts.Expression) {
const text = assignment.getText();
const callExpression = utils.ast.getCallExpression('assert', `_${name}Type`);
// const transpiled = ts.transpileModule(assignment.getText(), {});
// console.log(transpiled);
const toAssert = esprima.parse(assignment.getText()).body.pop();

callExpression.arguments.push((toAssert as any).expression);
Expand Down
2 changes: 2 additions & 0 deletions src/compiler/_archive/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import * as astUtil from './utils/ast';
export const ast = astUtil;
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,18 @@ export class VariableDeclarationVisitor implements Visitor {
}

public visit(node: ts.VariableDeclaration, context: VisitorContext, traverse: (...visitors: Visitor[]) => void): void {
console.log('-->', node.getText());
const typeDefinition = generate.typeDefinition(node.type, node.name.getText());

if (typeDefinition !== null) {
context.insertLine(node.parent.getStart(), utils.ast.toString(typeDefinition));
console.log(utils.ast.toString(typeDefinition));
}

if (node.initializer !== undefined) {
const assignment = generate.variableAssignment(node.name.getText(), node.initializer);
context.replace(node.getStart(), node.getEnd(), utils.ast.toString(assignment));
console.log(utils.ast.toString(assignment));
}
}

Expand Down
17 changes: 17 additions & 0 deletions src/compiler/_archive/visitors/VariableStatementVisitor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import * as ts from 'typescript';
import { Visitor, VisitorContext } from 'tspoon';
import { VariableDeclarationVisitor } from './VariableDeclarationVisitor';
import * as generate from '../generators';
import * as utils from '../utils';

export class VariableStatementVisitor implements Visitor {

public filter(node: ts.Node): boolean {
return node.kind === ts.SyntaxKind.VariableDeclaration;
}

public visit(node: ts.VariableDeclaration, context: VisitorContext, traverse: (...visitors: Visitor[]) => void): void {
traverse(new VariableDeclarationVisitor());
}

}
2 changes: 2 additions & 0 deletions src/compiler/_archive/visitors/default_visitors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { VariableDeclarationVisitor } from './VariableDeclarationVisitor';
// export { VariableStatementVisitor } from './VariableStatementVisitor';
32 changes: 32 additions & 0 deletions src/compiler/_archive/visitors_new/VariableDeclarationVisitor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import * as ts from 'typescript/built/local/typescript';
import { Visitor } from './Visitor';
import * as generator from '../generator';

export class VariableDeclarationVisitor extends Visitor {

public filter(node: ts.Node): boolean {
return node.kind === ts.SyntaxKind.VariableDeclaration;
}

public visit(node: ts.VariableDeclaration): ts.Node {
if (!node.type) {
return node;
}

const nodeName = node.name.getText();
const def = generator.typeDefinition(node.type, nodeName);

const val = ts.factory.createCall(
ts.factory.createPropertyAccess(ts.factory.createIdentifier(`_${nodeName}Type`), 'assert'),
[],
[node.initializer],
);

const check = ts.factory.updateVariableDeclaration(node, node.name, node.type, val);

const list = ts.factory.createVariableDeclarationList([def, check]);

return list;
}

}
9 changes: 9 additions & 0 deletions src/compiler/_archive/visitors_new/Visitor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import * as ts from 'typescript/built/local/typescript';

export abstract class Visitor {

protected abstract filter(node: ts.Node): boolean;

public abstract visit(node: ts.Node): ts.Node;

}
File renamed without changes.
4 changes: 4 additions & 0 deletions src/compiler/transformers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export { Transformer } from './transformers/Transformer';
export { VariableDeclarationTransformer } from './transformers/VariableDeclarationTransformer';
import * as DEFAULT_TRANSFORMERS from './transformers/default_transformers';
export { DEFAULT_TRANSFORMERS };
33 changes: 33 additions & 0 deletions src/compiler/transformers/Transformer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import * as ts from 'typescript/built/local/typescript';

export abstract class Transformer {

protected visited: ts.Node[] = [];

protected abstract substitution: ts.SyntaxKind | ts.SyntaxKind[];

public abstract onSubstituteNode(context: ts.EmitContext, node: ts.Node): ts.Node;

public getSubstitutions(): ts.SyntaxKind[] {
return !Array.isArray(this.substitution) ? [this.substitution] : this.substitution;
}

public getVisited() {
return this.visited;
}

public process(context: ts.EmitContext, node: ts.Node): ts.Node {
if (this.visited.indexOf(node) !== -1) {
return node;
}

this.visited.push(node);

// if (this.getSubstitutions().indexOf(node.kind) === -1) {
// return node;
// }

return this.onSubstituteNode(context, node);
}

}
58 changes: 58 additions & 0 deletions src/compiler/transformers/VariableDeclarationTransformer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import * as ts from 'typescript/built/local/typescript';
import { Transformer } from './Transformer';
import { generator } from '../utils';

export class VariableDeclarationTransformer extends Transformer {

protected substitution = ts.SyntaxKind.VariableDeclarationList;

public onSubstituteNode(context: ts.EmitContext, node: ts.VariableDeclarationList): ts.Node {
const declarations: ts.VariableDeclaration[] = [];

for (const declaration of node.declarations) {
declarations.push(...this.processDeclaration(declaration));
}

return ts.factory.updateVariableDeclarationList(node, declarations);
}

private processDeclaration(node: ts.VariableDeclaration): ts.VariableDeclaration[] {
if (!node.type) {
return [node];
}

if (node.parent.flags === ts.NodeFlags.Const) {
return this.processConstDeclaration(node);
}

return this.processLetDeclaration(node);
}

private processLetDeclaration(node: ts.VariableDeclaration): ts.VariableDeclaration[] {
const nodeName = node.name.getText();
const typeDefinition = generator.createTypeDefinition(node.type, `_${nodeName}Type`);

if (!node.initializer) {
return [typeDefinition, node];
}

const initializer = generator.createTypeCall(`_${nodeName}Type`, 'assert', [node.initializer]);
const assignment = ts.factory.updateVariableDeclaration(node, node.name, node.type, initializer);

return [typeDefinition, assignment];
}

private processConstDeclaration(node: ts.VariableDeclaration): ts.VariableDeclaration[] {
const nodeName = node.name.getText();
const typeCalls = generator.createTypeCalls(node.type);

const initializer = ts.factory.createCall(
ts.factory.createPropertyAccess(typeCalls, 'assert'), [], [node.initializer],
);

const assignment = ts.factory.updateVariableDeclaration(node, node.name, node.type, initializer);

return [assignment];
}

}
1 change: 1 addition & 0 deletions src/compiler/transformers/default_transformers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { VariableDeclarationTransformer } from './VariableDeclarationTransformer';
4 changes: 2 additions & 2 deletions src/compiler/utils.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
import * as astUtil from './utils/ast';
export const ast = astUtil;
import * as generator from './utils/generator';
export { generator };
Loading

0 comments on commit ae3d27f

Please sign in to comment.