Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Transformation API (official TypeScript nightly build) #2

Merged
merged 26 commits into from
Mar 24, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
467997f
Created dev branch for further testing.
fabiandev Feb 1, 2017
f8f5ca1
Transition to the official transformer API.
fabiandev Mar 14, 2017
0cb7a9b
Package and linter changes.
fabiandev Mar 15, 2017
e01141a
Added util for common actions, where no context is needed.
fabiandev Mar 15, 2017
03f65e6
Generator for recurring node structures.
fabiandev Mar 15, 2017
37d7b68
Added and removed mutators. Changes to base class.
fabiandev Mar 15, 2017
6fa626c
Moved common actions to util. Fixes.
fabiandev Mar 15, 2017
d6f6925
Change to latest mutator instances.
fabiandev Mar 15, 2017
4c646aa
Restore parent nodes. Determine correct scope. Other improvements and…
fabiandev Mar 15, 2017
4c39f47
Package exports.
fabiandev Mar 15, 2017
7fb159e
Temporary test files.
fabiandev Mar 15, 2017
e28cd08
Util fixes and additions.
fabiandev Mar 15, 2017
ec1c3a7
Minor code style change.
fabiandev Mar 15, 2017
5adb539
Added binary expression mutator. Other improvements.
fabiandev Mar 16, 2017
7004d0f
Check implicit types. Added ability to get type of any identifier.
fabiandev Mar 16, 2017
dc0c571
Changes to linter rules.
fabiandev Mar 16, 2017
9dab0f6
Temporary test output.
fabiandev Mar 16, 2017
4fdb52d
Move next code to src.
fabiandev Mar 16, 2017
bde9a8e
Identified missing type generators.
fabiandev Mar 16, 2017
3c8282e
Implemented more types.
fabiandev Mar 23, 2017
7125b14
Added .tsr to gitignore.
fabiandev Mar 23, 2017
6582d37
Minor improvements.
fabiandev Mar 23, 2017
7eac131
Code improvements.
fabiandev Mar 23, 2017
371c771
Changes to examples.
fabiandev Mar 23, 2017
28044e1
Moved first pass visitor to archive.
fabiandev Mar 23, 2017
ebc9d7d
Changes to tsconfig excludes.
fabiandev Mar 23, 2017
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ logs
pids
*.pid
*.seed
.tsr
File renamed without changes.
50 changes: 50 additions & 0 deletions dist/bin/handlers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
"use strict";
const cp = require("child_process");
const path = require("path");
const bus_1 = require("../bus");
const child = cp.fork(path.join(__dirname, './status'));
child.send({ message: 'init' });
child.on('exit', () => {
process.exit();
});
function handleError(error) {
child.send({ message: 'error', payload: error });
}
process.on('uncaughtException', handleError);
process.on('unhandledRejection', handleError);
bus_1.bus.on('error', handleError);
bus_1.bus.on('main.start', (filePaths) => {
child.send({ message: 'start', payload: filePaths.length });
});
bus_1.bus.on('main.done', (filePaths) => {
child.send({ message: 'end' });
});
bus_1.bus.on('transform.file.start', (filePath) => {
child.send({ message: 'fileStart', payload: filePath });
});
bus_1.bus.on('transform.file.readError', (filePath) => {
child.send({ message: 'fileReadError', payload: filePath });
});
bus_1.bus.on('transform.file.done', (filePath) => {
child.send({ message: 'fileEnd', payload: filePath });
});
bus_1.bus.on('write.start', (num) => {
child.send({ message: 'writeStart', payload: num });
});
bus_1.bus.on('write.end', (num) => {
child.send({ message: 'writeEnd', payload: num });
});
bus_1.bus.on('write.file.start', (filePath) => {
child.send({ message: 'fileWriteStart', payload: filePath });
});
bus_1.bus.on('transform.file.done', (filePath) => {
child.send({ message: 'fileWriteEnd', payload: filePath });
});
// process.on('SIGINT', () => {
// child.send({message: 'term'});
// });
//
// process.on('SIGTERM', () => {
// process.stdin.resume();
// child.send({message: 'term'});
// });
1 change: 1 addition & 0 deletions dist/bin/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
import './handlers';
58 changes: 58 additions & 0 deletions dist/bin/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
#!/usr/bin/env node
"use strict";
require("./handlers");
const program = require("commander");
const transform_1 = require("../transform");
const writer_1 = require("../writer");
const pkg = require('../../package.json');
const config = {};
const writerConfig = writer_1.DEFAULT_CONFIG;
let write = false;
function defaultAction() {
const files = program.args.filter((value) => {
return typeof value === 'string';
});
transform_1.default(files)
.then((compilerResult) => {
if (write) {
const writer = new writer_1.Writer(compilerResult);
writer.writeAll();
}
});
}
function setNoAssertConst() {
config.assertConst = true;
}
function setEncoding(encoding) {
config.encoding = encoding;
writerConfig.encoding = encoding;
}
function setWrite(location, base) {
write = true;
writerConfig.writePath = location || writerConfig.writePath;
writerConfig.basePath = base || writerConfig.basePath;
}
program
.version(pkg.version)
.description(`--------- ts-runtime ---------
Inserts runtime type checks and
emits pretty printed TypeScript.
--------------------------------`)
.usage('[options] <file ...>')
.option('-w, --write', 'persist files', setWrite)
.option('-e, --encoding <encoding>', 'set file encoding. defaults to utf8', setEncoding)
.option('--no-assert-const', 'turn off const declaration checks.', setNoAssertConst)
.on('--help', () => {
console.log(' Examples:');
console.log();
console.log(' $ tsr --no-assert-const file.ts');
console.log(' $ tsr --write ./out ./src ./src/file1.ts ./src/file2.ts');
console.log();
});
program.parse(process.argv);
if (!process.argv.slice(2).length) {
program.outputHelp();
}
else {
defaultAction();
}
File renamed without changes.
98 changes: 98 additions & 0 deletions dist/bin/status.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
"use strict";
const path = require("path");
const ora = require("ora");
const chalk = require("chalk");
const symbols = require("log-symbols");
let spinner;
let fileCount = 0;
let processed = 0;
let errors = 0;
process.on('uncaughtException', (error) => {
status.error(error);
});
process.on('unhandledRejection', (reason, p) => {
status.error(new Error(reason));
});
process.on('SIGINT', () => {
status.term();
process.exit();
});
process.on('SIGTERM', () => {
status.term();
process.exit();
});
process.on('message', (data) => {
if (typeof status[data.message] === 'function') {
status[data.message](data.payload);
}
});
const status = {};
status.init = () => {
spinner = null;
fileCount = 0;
processed = 0;
errors = 0;
spinner = ora();
};
status.start = (num) => {
fileCount = num;
spinner.text = chalk.bold(`Got ${num} file${num === 1 ? '' : 's'} to process:`);
spinner.stopAndPersist();
spinner.text = `${processed}/${fileCount}`;
spinner.start();
return spinner;
};
status.end = () => {
if (errors > 0) {
spinner.stopAndPersist({ symbol: symbols.warning, text: chalk.yellow(`All processing finished, but there ${errors === 1 ? 'was' : 'were'} ${errors} error${errors === 1 ? '' : 's'}`) });
}
else {
spinner.succeed(chalk.green('All files have been processed'));
}
process.exit(0);
};
status.term = () => {
spinner.fail(chalk.red('Processing was interrupted'));
};
status.fileStart = (filePath) => {
processed++;
const countStr = chalk.gray(`[${processed}/${fileCount}]`);
spinner.text = `${countStr} Processing ${path.basename(filePath)}`;
};
status.fileEnd = (filePath) => {
const countStr = chalk.gray(`[${processed}/${fileCount}]`);
spinner.succeed(`${countStr} Done processing ${path.basename(filePath)}`);
spinner.start();
};
status.fileFail = (filePath) => {
errors++;
const countStr = chalk.gray(`[${processed}/${fileCount}]`);
spinner.fail(`${countStr} Error processing ${path.basename(filePath)}`);
spinner.start();
};
status.fileReadError = (filePath) => {
errors++;
const countStr = chalk.gray(`[${processed}/${fileCount}]`);
spinner.fail(`${countStr} Could not read ${path.basename(filePath)}`);
spinner.start();
};
status.writeStart = (num) => {
spinner.text = `Writing files`;
};
status.writeEnd = (num) => {
spinner.succeed(`Wrote ${num} files`);
};
status.fileWriteStart = (filePath) => {
};
status.fileWriteEnd = (filePath) => {
};
status.error = (err) => {
errors++;
if (typeof err === 'string') {
spinner.fail(chalk.red(err));
}
if (err instanceof Error) {
spinner.fail(chalk.red(err.message));
}
process.exit();
};
1 change: 1 addition & 0 deletions dist/bus.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { EventBus, bus } from './bus/Bus';
4 changes: 4 additions & 0 deletions dist/bus.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
"use strict";
var Bus_1 = require("./bus/Bus");
exports.EventBus = Bus_1.EventBus;
exports.bus = Bus_1.bus;
6 changes: 6 additions & 0 deletions dist/bus/Bus.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/// <reference types="node" />
import * as EventEmitter from 'events';
export declare class EventBus extends EventEmitter {
}
export declare const bus: EventBus;
export default bus;
8 changes: 8 additions & 0 deletions dist/bus/Bus.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
"use strict";
const EventEmitter = require("events");
class EventBus extends EventEmitter {
}
exports.EventBus = EventBus;
exports.bus = new EventBus();
Object.defineProperty(exports, "__esModule", { value: true });
exports.default = exports.bus;
File renamed without changes.
7 changes: 7 additions & 0 deletions dist/compiler.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
"use strict";
var Compiler_1 = require("./compiler/Compiler");
exports.Compiler = Compiler_1.Compiler;
var CompilerMode_1 = require("./compiler/CompilerMode");
exports.CompilerMode = CompilerMode_1.CompilerMode;
var default_config_1 = require("./compiler/default_config");
exports.DEFAULT_CONFIG = default_config_1.DEFAULT_CONFIG;
16 changes: 16 additions & 0 deletions dist/compiler/Compiler.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import * as ts from 'typescript';
import { Config } from '../config';
import { Transformer } from './transformers';
import CompilerResult from './CompilerResult';
export declare class Compiler {
protected config: Config;
protected transformers: Transformer[];
constructor(config: Config, transformers?: Transformer[]);
process(): Promise<CompilerResult>;
protected onSubstituteNode(context: ts.EmitContext, node: ts.Node): ts.Node;
private transformFile(filePath, transformers);
private visit(node);
private visitChildrenFirst(node);
private visitParentFirst(node);
}
export default Compiler;
95 changes: 95 additions & 0 deletions dist/compiler/Compiler.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
"use strict";
// import * as fs from 'fs';
const path = require("path");
const ts = require("typescript");
const transformers_1 = require("./transformers");
const bus_1 = require("../bus");
const CompilerMode_1 = require("./CompilerMode");
class Compiler {
constructor(config, transformers) {
this.config = config;
this.transformers = [];
const transformersToEnable = transformers || transformers_1.DEFAULT_TRANSFORMERS;
transformers_1.Transformer.DEFAULT_CONFIG = config;
const enabledTtransformers = Object.keys(transformersToEnable)
.map((key) => {
return transformers ?
transformers[key] :
new transformersToEnable[key]();
});
this.transformers.push(...enabledTtransformers);
}
process() {
bus_1.bus.emit('compiler.start', this.config);
const toTransform = [];
const transform = (context) => (sourceFile) => {
context.onSubstituteNode = this.onSubstituteNode.bind(this);
for (const transformer of this.transformers) {
for (const substitution of transformer.getSubstitutions()) {
context.enableSubstitution(substitution);
}
}
return sourceFile;
};
for (const file of this.config.files) {
toTransform.push(this.transformFile(file, [transform]));
}
// Do not reject this promise, if individual files fail
return Promise.all(toTransform.map(p => p.catch(e => e)))
.then(results => {
bus_1.bus.emit('compiler.done', this.config);
return {
config: this.config,
fileResults: results,
};
});
}
onSubstituteNode(context, node) {
let substitutedNode = node;
let parent = node.parent;
for (const transformer of this.transformers) {
substitutedNode = transformer.process(substitutedNode, context);
}
return substitutedNode;
}
transformFile(filePath, transformers) {
bus_1.bus.emit('transform.file.start', filePath);
return new Promise((resolve, reject) => {
filePath = path.normalize(path.join(process.cwd(), filePath));
const fileName = path.basename(filePath);
const source = ts.sys.readFile(filePath, this.config.encoding);
if (source === undefined) {
bus_1.bus.emit('transform.file.readError', filePath);
return reject(`Error reading ${filePath}`);
}
let sourceFile = ts.createSourceFile(fileName, source, this.config.languageVersion || ts.ScriptTarget.Latest, this.config.setParentNodes || true, this.config.scriptKind || ts.ScriptKind.TS);
if (this.config.mode === CompilerMode_1.default.Visit) {
transformers = [];
sourceFile = this.visit(sourceFile);
}
const result = ts.emit(sourceFile, transformers).result;
bus_1.bus.emit('transform.file.done', filePath);
resolve({
fileName,
filePath,
result,
});
});
}
visit(node) {
return this.config.visitChildrenFirst ?
this.visitChildrenFirst(node) :
this.visitParentFirst(node);
}
visitChildrenFirst(node) {
node = ts.visitEachChild(node, this.visitChildrenFirst.bind(this));
return this.onSubstituteNode(undefined, node);
}
visitParentFirst(node) {
node = this.onSubstituteNode(undefined, node);
return ts.visitEachChild(node, this.visitParentFirst.bind(this));
}
}
exports.Compiler = Compiler;
Object.defineProperty(exports, "__esModule", { value: true });
exports.default = Compiler;
9 changes: 9 additions & 0 deletions dist/compiler/CompilerConfig.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import CompilerMode from './CompilerMode';
import TransformerConfig from './transformers/TransformerConfig';
export interface CompilerConfig extends TransformerConfig {
files?: string[];
encoding?: string;
mode?: CompilerMode;
visitChildrenFirst?: boolean;
}
export default CompilerConfig;
1 change: 1 addition & 0 deletions dist/compiler/CompilerConfig.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"use strict";
5 changes: 5 additions & 0 deletions dist/compiler/CompilerMode.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export declare enum CompilerMode {
Substitute = 1,
Visit = 2,
}
export default CompilerMode;
9 changes: 9 additions & 0 deletions dist/compiler/CompilerMode.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
"use strict";
(function (CompilerMode) {
CompilerMode[CompilerMode["Substitute"] = 1] = "Substitute";
CompilerMode[CompilerMode["Visit"] = 2] = "Visit";
})(exports.CompilerMode || (exports.CompilerMode = {}));
var CompilerMode = exports.CompilerMode;
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.default = CompilerMode;
7 changes: 7 additions & 0 deletions dist/compiler/CompilerResult.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import FileResult from './FileResult';
import { Config } from '../config';
export interface CompilerResult {
config: Config;
fileResults: FileResult[];
}
export default CompilerResult;
1 change: 1 addition & 0 deletions dist/compiler/CompilerResult.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"use strict";
Loading