Skip to content

Commit ae3d27f

Browse files
authored
Merge pull request #1 from fabiandev/ts-emit
Transformations with unofficial TypeScript API
2 parents 043cec9 + f1c27f4 commit ae3d27f

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+447
-84
lines changed

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,10 @@
1717
"escodegen": "^1.8.1",
1818
"esprima": "^3.1.3",
1919
"flow-runtime": "^0.2.1",
20+
"node-require-fallback": "^0.1.2",
2021
"tcomb": "^3.2.16",
2122
"ts-type-info": "^6.2.2",
2223
"tspoon": "^1.0.254",
23-
"typescript": "^2.1.5"
24+
"typescript": "Microsoft/TypeScript#6c122bcf16c329a21da04d7b5256f20ecc0bbd1d"
2425
}
2526
}

src/compiler/Compiler.ts

Lines changed: 39 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
import * as fs from 'fs';
22
import * as path from 'path';
3-
import { TranspilerOutput, Visitor, transpile } from 'tspoon';
4-
import { TsRuntimeOptions } from '../options';
5-
import { CompilerResult } from './CompilerResult';
6-
import { CompilerConfig } from './CompilerConfig';
7-
import { FileResult } from './FileResult';
8-
import * as DEFAULT_VISITORS from './visitors/default_visitors';
3+
import * as ts from 'typescript/built/local/typescript';
4+
import CompilerResult from './CompilerResult';
5+
import CompilerConfig from './CompilerConfig';
6+
import FileResult from './FileResult';
7+
import { Transformer, DEFAULT_TRANSFORMERS } from './transformers';
98

109
export class Compiler {
1110

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

18+
const transformers: ts.Transformer[] = Object.keys(DEFAULT_TRANSFORMERS).map((key: string) => {
19+
const transformer = new (DEFAULT_TRANSFORMERS as any)[key]();
20+
21+
const transform: ts.Transformer = (context) => (f) => {
22+
for (const substitution of transformer.getSubstitutions()) {
23+
context.enableSubstitution(substitution);
24+
}
25+
context.onSubstituteNode = transformer.process.bind(transformer);
26+
return f;
27+
};
28+
return transform;
29+
});
30+
1931
for (const file of this.config.files) {
20-
toTransform.push(this.transformFile(file));
32+
toTransform.push(this.transformFile(file, transformers));
2133
}
2234

2335
return Promise.all(toTransform)
@@ -26,56 +38,40 @@ export class Compiler {
2638
config: this.config,
2739
fileResults: results,
2840
};
41+
})
42+
.catch(err => {
43+
console.error(err);
2944
});
3045
}
3146

32-
private transformFile(file: string): Promise<FileResult> {
47+
private transformFile(filePath: string, transformers: ts.Transformer[]): Promise<FileResult> {
3348
return new Promise((resolve, reject) => {
34-
fs.readFile(file, this.config.options.encoding, (err, source) => {
49+
fs.readFile(filePath, this.config.options.encoding, (err, source) => {
3550
if (err) {
36-
return reject(`Error reading file ${file}`);
51+
return reject(`Error reading file ${filePath}`);
3752
}
3853

39-
const visitors = Object.keys(DEFAULT_VISITORS).map((key: string) => {
40-
return new (DEFAULT_VISITORS as any)[key]();
41-
});
54+
const fileName = path.basename(filePath);
4255

43-
const transpiler = transpile(source, {
44-
compilerOptions: this.config.options.compilerOptions,
45-
sourceFileName: path.basename(file),
46-
visitors,
47-
});
56+
const f = ts.createSourceFile(
57+
fileName,
58+
source,
59+
this.config.options.compilerOptions.target || ts.ScriptTarget.Latest,
60+
true,
61+
ts.ScriptKind.TS,
62+
);
4863

49-
this.reportFile(transpiler);
64+
const result = ts.emit(f, transformers).result;
5065

5166
resolve({
52-
transpiler,
53-
file,
67+
fileName,
68+
filePath,
69+
result,
5470
});
5571
});
5672
});
5773
}
5874

59-
private reportFile(transpiler: TranspilerOutput): boolean {
60-
if (transpiler.diags) {
61-
for (const d of transpiler.diags) {
62-
const position = d.file.getLineAndCharacterOfPosition(d.start);
63-
64-
const name = d.file.fileName;
65-
const line = position.line + 1;
66-
const character = position.character;
67-
const text = d.messageText;
68-
69-
console.error(`-> ${name}:${line}:${character}:${text}`);
70-
}
71-
}
72-
73-
if (transpiler.halted) {
74-
console.error('Transpiler halted. Exiting now.');
75-
process.exit(1);
76-
}
77-
78-
return !!transpiler.diags;
79-
}
80-
8175
}
76+
77+
export default Compiler;

src/compiler/CompilerConfig.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1-
import { TsRuntimeOptions } from '../options/TsRuntimeOptions';
2-
import { FileResult } from './FileResult';
1+
import Options from '../options/Options';
2+
import FileResult from './FileResult';
33

44
export interface CompilerConfig {
55
files: string[];
6-
options: TsRuntimeOptions;
6+
options: Options;
77
}
8+
9+
export default CompilerConfig;

src/compiler/CompilerResult.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1-
import { FileResult } from './FileResult';
2-
import { CompilerConfig } from './CompilerConfig';
1+
import FileResult from './FileResult';
2+
import CompilerConfig from './CompilerConfig';
33

44
export interface CompilerResult {
55
config: CompilerConfig;
66
fileResults: FileResult[];
77
}
8+
9+
export default CompilerResult;

src/compiler/FileResult.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
import { TranspilerOutput } from 'tspoon';
2-
31
export interface FileResult {
4-
transpiler: TranspilerOutput;
5-
file: string;
2+
result: string;
3+
filePath: string;
4+
fileName: string
65
}
6+
7+
export default FileResult;
File renamed without changes.

src/compiler/_archive/generators/class_definition.ts

Whitespace-only changes.

src/compiler/_archive/generators/interface_definition.ts

Whitespace-only changes.

src/compiler/generators/type_definition.ts renamed to src/compiler/_archive/generators/type_definition.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,10 @@ export function typeCalls(type: ts.TypeNode): null | any {
6262
return callExpression;
6363
}
6464
default:
65-
throw new Error('Node Type not supported.');
65+
{
66+
const callExpression = utils.ast.getCallExpression('any');
67+
return callExpression;
68+
}
69+
// throw new Error('Node Type not supported.');
6670
}
6771
}

src/compiler/generators/variable_assignment.ts renamed to src/compiler/_archive/generators/variable_assignment.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import * as utils from '../utils';
55
export function variableAssignment(name: string, assignment: ts.Expression) {
66
const text = assignment.getText();
77
const callExpression = utils.ast.getCallExpression('assert', `_${name}Type`);
8+
// const transpiled = ts.transpileModule(assignment.getText(), {});
9+
// console.log(transpiled);
810
const toAssert = esprima.parse(assignment.getText()).body.pop();
911

1012
callExpression.arguments.push((toAssert as any).expression);

src/compiler/_archive/utils.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
import * as astUtil from './utils/ast';
2+
export const ast = astUtil;
File renamed without changes.
File renamed without changes.

src/compiler/visitors/VariableDeclarationVisitor.ts renamed to src/compiler/_archive/visitors/VariableDeclarationVisitor.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,18 @@ export class VariableDeclarationVisitor implements Visitor {
1010
}
1111

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

1516
if (typeDefinition !== null) {
1617
context.insertLine(node.parent.getStart(), utils.ast.toString(typeDefinition));
18+
console.log(utils.ast.toString(typeDefinition));
1719
}
1820

1921
if (node.initializer !== undefined) {
2022
const assignment = generate.variableAssignment(node.name.getText(), node.initializer);
2123
context.replace(node.getStart(), node.getEnd(), utils.ast.toString(assignment));
24+
console.log(utils.ast.toString(assignment));
2225
}
2326
}
2427

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import * as ts from 'typescript';
2+
import { Visitor, VisitorContext } from 'tspoon';
3+
import { VariableDeclarationVisitor } from './VariableDeclarationVisitor';
4+
import * as generate from '../generators';
5+
import * as utils from '../utils';
6+
7+
export class VariableStatementVisitor implements Visitor {
8+
9+
public filter(node: ts.Node): boolean {
10+
return node.kind === ts.SyntaxKind.VariableDeclaration;
11+
}
12+
13+
public visit(node: ts.VariableDeclaration, context: VisitorContext, traverse: (...visitors: Visitor[]) => void): void {
14+
traverse(new VariableDeclarationVisitor());
15+
}
16+
17+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export { VariableDeclarationVisitor } from './VariableDeclarationVisitor';
2+
// export { VariableStatementVisitor } from './VariableStatementVisitor';
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import * as ts from 'typescript/built/local/typescript';
2+
import { Visitor } from './Visitor';
3+
import * as generator from '../generator';
4+
5+
export class VariableDeclarationVisitor extends Visitor {
6+
7+
public filter(node: ts.Node): boolean {
8+
return node.kind === ts.SyntaxKind.VariableDeclaration;
9+
}
10+
11+
public visit(node: ts.VariableDeclaration): ts.Node {
12+
if (!node.type) {
13+
return node;
14+
}
15+
16+
const nodeName = node.name.getText();
17+
const def = generator.typeDefinition(node.type, nodeName);
18+
19+
const val = ts.factory.createCall(
20+
ts.factory.createPropertyAccess(ts.factory.createIdentifier(`_${nodeName}Type`), 'assert'),
21+
[],
22+
[node.initializer],
23+
);
24+
25+
const check = ts.factory.updateVariableDeclaration(node, node.name, node.type, val);
26+
27+
const list = ts.factory.createVariableDeclarationList([def, check]);
28+
29+
return list;
30+
}
31+
32+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import * as ts from 'typescript/built/local/typescript';
2+
3+
export abstract class Visitor {
4+
5+
protected abstract filter(node: ts.Node): boolean;
6+
7+
public abstract visit(node: ts.Node): ts.Node;
8+
9+
}

src/compiler/transformers.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export { Transformer } from './transformers/Transformer';
2+
export { VariableDeclarationTransformer } from './transformers/VariableDeclarationTransformer';
3+
import * as DEFAULT_TRANSFORMERS from './transformers/default_transformers';
4+
export { DEFAULT_TRANSFORMERS };
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import * as ts from 'typescript/built/local/typescript';
2+
3+
export abstract class Transformer {
4+
5+
protected visited: ts.Node[] = [];
6+
7+
protected abstract substitution: ts.SyntaxKind | ts.SyntaxKind[];
8+
9+
public abstract onSubstituteNode(context: ts.EmitContext, node: ts.Node): ts.Node;
10+
11+
public getSubstitutions(): ts.SyntaxKind[] {
12+
return !Array.isArray(this.substitution) ? [this.substitution] : this.substitution;
13+
}
14+
15+
public getVisited() {
16+
return this.visited;
17+
}
18+
19+
public process(context: ts.EmitContext, node: ts.Node): ts.Node {
20+
if (this.visited.indexOf(node) !== -1) {
21+
return node;
22+
}
23+
24+
this.visited.push(node);
25+
26+
// if (this.getSubstitutions().indexOf(node.kind) === -1) {
27+
// return node;
28+
// }
29+
30+
return this.onSubstituteNode(context, node);
31+
}
32+
33+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import * as ts from 'typescript/built/local/typescript';
2+
import { Transformer } from './Transformer';
3+
import { generator } from '../utils';
4+
5+
export class VariableDeclarationTransformer extends Transformer {
6+
7+
protected substitution = ts.SyntaxKind.VariableDeclarationList;
8+
9+
public onSubstituteNode(context: ts.EmitContext, node: ts.VariableDeclarationList): ts.Node {
10+
const declarations: ts.VariableDeclaration[] = [];
11+
12+
for (const declaration of node.declarations) {
13+
declarations.push(...this.processDeclaration(declaration));
14+
}
15+
16+
return ts.factory.updateVariableDeclarationList(node, declarations);
17+
}
18+
19+
private processDeclaration(node: ts.VariableDeclaration): ts.VariableDeclaration[] {
20+
if (!node.type) {
21+
return [node];
22+
}
23+
24+
if (node.parent.flags === ts.NodeFlags.Const) {
25+
return this.processConstDeclaration(node);
26+
}
27+
28+
return this.processLetDeclaration(node);
29+
}
30+
31+
private processLetDeclaration(node: ts.VariableDeclaration): ts.VariableDeclaration[] {
32+
const nodeName = node.name.getText();
33+
const typeDefinition = generator.createTypeDefinition(node.type, `_${nodeName}Type`);
34+
35+
if (!node.initializer) {
36+
return [typeDefinition, node];
37+
}
38+
39+
const initializer = generator.createTypeCall(`_${nodeName}Type`, 'assert', [node.initializer]);
40+
const assignment = ts.factory.updateVariableDeclaration(node, node.name, node.type, initializer);
41+
42+
return [typeDefinition, assignment];
43+
}
44+
45+
private processConstDeclaration(node: ts.VariableDeclaration): ts.VariableDeclaration[] {
46+
const nodeName = node.name.getText();
47+
const typeCalls = generator.createTypeCalls(node.type);
48+
49+
const initializer = ts.factory.createCall(
50+
ts.factory.createPropertyAccess(typeCalls, 'assert'), [], [node.initializer],
51+
);
52+
53+
const assignment = ts.factory.updateVariableDeclaration(node, node.name, node.type, initializer);
54+
55+
return [assignment];
56+
}
57+
58+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { VariableDeclarationTransformer } from './VariableDeclarationTransformer';

src/compiler/utils.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
import * as astUtil from './utils/ast';
2-
export const ast = astUtil;
1+
import * as generator from './utils/generator';
2+
export { generator };

0 commit comments

Comments
 (0)