///
import ts = require('typescript-api');
import path = require('path');
import events = require('events');
export function compile(files: string[], options?: any, callback?: Function) {
return new BatchCompiler().compile(files, options, callback);
}
export interface ICompilerOptions {
/**
* Generates corresponding .d.ts file.
*/
declaration?: boolean;
/**
* Print this message.
*/
help?: boolean;
/**
* Specifies the location where debugger should locate map files
* instead of generated locations.
*/
mapRoot?: string;
/**
* Specify module code generation: 'commonjs' or 'amd'
*/
module?: string;
/**
* Warn on expressions and declarations with any implied 'any' type.
*/
noImplicitAny?: boolean;
/**
* Skip resolution and preprocessing.
*/
noResolve?: boolean;
/**
* Concatenate and emit output to a single file.
*/
out?: string;
/**
* Redirect output structure to the directory.
*/
outDir?: string;
/**
* Do not emit comments to output.
*/
removeComments?: boolean;
/**
* Generates corresponding .map file.
*/
sourcemap?: boolean;
/**
* Specifies the location where debugger should locate TypeScript
* files instead of source locations.
*/
sourceRoot?: string;
/**
* Specify ECMAScript target version: 'ES3' (default), or 'ES5'
*/
target?: string;
/**
* Print the compiler's version: 0.9.5.0
*/
version?: string;
/**
* Watch input files.
*/
watch?: boolean;
/**
* Insert command line options and files from a file.
*/
optionsFile?: string;
/**
* Skip writing the output files.
*/
skipWrite?: boolean;
}
export class BatchCompiler extends events.EventEmitter {
private _skipWrite = false;
private _compiler: ts.BatchCompiler;
constructor() {
super();
this.redirectErrors();
this._compiler = new ts.BatchCompiler(ts.IO);
(process).mainModule.filename = require.resolve('typescript');
}
private redirectErrors() {
ts.IO.stderr.Write = (s: string) => {
this.emit('error', s);
};
ts.IO.stderr.WriteLine = (s: string) => {
ts.IO.stderr.Write(s + '\n');
};
ts.IO.stdout.Write = (s: string) => {
this.emit('info', s);
};
ts.IO.stdout.WriteLine = (s: string) => {
ts.IO.stdout.Write(s + '\n');
};
(ts.BatchCompiler).prototype.addDiagnostic = function(diagnostic: ts.Diagnostic) {
var diagnosticInfo = diagnostic.info();
if (diagnosticInfo.category === 1 /* Error */) {
this.hasErrors = true;
}
var errorLocation = '';
if (diagnostic.fileName()) {
errorLocation = diagnostic.fileName() + "(" + (diagnostic.line() + 1) + "," + (diagnostic.character() + 1) + "): ";
}
this.ioHost.stderr.WriteLine(errorLocation + diagnostic.message());
};
}
compile(files: string[], options?: any, callback?: Function) {
handleOverloads.call(this);
handleSkipWrite.call(this);
setupArguments(args => {
ts.IO.arguments = args;
this._batchCompile(callback);
});
function handleOverloads() {
if (typeof options === 'function') {
callback = options;
options = {};
} else if (typeof callback !== 'function') {
callback = () => {};
}
}
function handleSkipWrite() {
options = options || {};
this._skipWrite = options.skipWrite;
delete options.skipWrite;
}
function setupArguments(cb: Function) {
var args = argify(options);
args.push.apply(args, files);
cb(args);
}
}
private _batchCompile(callback: Function) {
var compiler = this._compiler;
ts.CompilerDiagnostics.diagnosticWriter = { Alert: (s: string) => { compiler.ioHost.printLine(s); } };
if (compiler.parseOptions()) {
compiler.logger = compiler.compilationSettings.gatherDiagnostics() ? new DiagnosticsLogger((this).ioHost) : new ts.NullLogger();
if (compiler.compilationSettings.watch()) {
// Watch will cause the program to stick around as long as the files exist
compiler.watchFiles();
callback(null);
return;
}
compiler.resolve();
this._compile(callback);
} else {
callback(new Error('Error parsing compiler options'));
}
if (compiler.hasErrors) {
callback(new Error('Unspecified error'));
}
}
private _compile(callback: Function) {
var compiler = this._compiler;
var tsCompiler = new ts.TypeScriptCompiler(compiler.logger, compiler.compilationSettings);
compiler.resolvedFiles.forEach(resolvedFile => {
var sourceFile = compiler.getSourceFile(resolvedFile.path);
tsCompiler.addFile(resolvedFile.path, sourceFile.scriptSnapshot, sourceFile.byteOrderMark, /*version:*/ 0, /*isOpen:*/ false, resolvedFile.referencedFiles);
});
var results: ts.OutputFile[] = [];
for (var it = tsCompiler.compile((path: string) => compiler.resolvePath(path)); it.moveNext();) {
var result = it.current();
result.diagnostics.forEach(d => compiler.addDiagnostic(d));
if (!this._skipWrite && !compiler.tryWriteOutputFiles(result.outputFiles)) {
callback(new Error('Error writing to output file'));
}
Array.prototype.push.apply(results, result.outputFiles);
}
callback(null, results);
}
}
class DiagnosticsLogger implements ts.ILogger {
constructor(public ioHost: ts.IIO) {
}
public information(): boolean { return false; }
public debug(): boolean { return false; }
public warning(): boolean { return false; }
public error(): boolean { return false; }
public fatal(): boolean { return false; }
public log(s: string): void {
this.ioHost.stdout.WriteLine(s);
}
}
function argify(options: ICompilerOptions): string[] {
var args = [];
Object.keys(options).forEach(key => {
var value = options[key];
if (!value) {
return;
}
if (key === 'optionsFile') {
args.push('@' + value);
return;
}
var flag = '-';
if (key.length !== 1) {
flag += '-';
}
args.push(flag + key);
if (typeof value !== 'boolean') {
args.push(value);
}
});
return args;
}
export class OutputFile extends ts.OutputFile {
}