1 |
|
2 | import ts = require('typescript-api');
|
3 | import path = require('path');
|
4 | import events = require('events');
|
5 |
|
6 |
|
7 | export function compile(files: string[], options?: any, callback?: Function) {
|
8 | return new BatchCompiler().compile(files, options, callback);
|
9 | }
|
10 |
|
11 | export interface ICompilerOptions {
|
12 | |
13 |
|
14 |
|
15 | declaration?: boolean;
|
16 | |
17 |
|
18 |
|
19 | help?: boolean;
|
20 | |
21 |
|
22 |
|
23 |
|
24 | mapRoot?: string;
|
25 | |
26 |
|
27 |
|
28 | module?: string;
|
29 | /**
|
30 | * Warn on expressions and declarations with any implied 'any' type.
|
31 | */
|
32 | noImplicitAny?: boolean;
|
33 | /**
|
34 | * Skip resolution and preprocessing.
|
35 | */
|
36 | noResolve?: boolean;
|
37 | /**
|
38 | * Concatenate and emit output to a single file.
|
39 | */
|
40 | out?: string;
|
41 | /**
|
42 | * Redirect output structure to the directory.
|
43 | */
|
44 | outDir?: string;
|
45 | /**
|
46 | * Do not emit comments to output.
|
47 | */
|
48 | removeComments?: boolean;
|
49 | /**
|
50 | * Generates corresponding .map file.
|
51 | */
|
52 | sourcemap?: boolean;
|
53 | /**
|
54 | * Specifies the location where debugger should locate TypeScript
|
55 | * files instead of source locations.
|
56 | */
|
57 | sourceRoot?: string;
|
58 | /**
|
59 | * Specify ECMAScript target version: 'ES3' (default), or 'ES5'
|
60 | */
|
61 | target?: string;
|
62 | /**
|
63 | * Print the compiler's version: 0.9.5.0
|
64 | */
|
65 | version?: string;
|
66 | /**
|
67 | * Watch input files.
|
68 | */
|
69 | watch?: boolean;
|
70 | /**
|
71 | * Insert command line options and files from a file.
|
72 | */
|
73 | optionsFile?: string;
|
74 | /**
|
75 | * Skip writing the output files.
|
76 | */
|
77 | skipWrite?: boolean;
|
78 | }
|
79 |
|
80 | export class BatchCompiler extends events.EventEmitter {
|
81 |
|
82 | private _skipWrite = false;
|
83 | private _compiler: ts.BatchCompiler;
|
84 |
|
85 | constructor() {
|
86 | super();
|
87 | this.redirectErrors();
|
88 | this._compiler = new ts.BatchCompiler(ts.IO);
|
89 | (<any>process).mainModule.filename = require.resolve('typescript');
|
90 | }
|
91 |
|
92 | private redirectErrors() {
|
93 | ts.IO.stderr.Write = (s: string) => {
|
94 | this.emit('error', s);
|
95 | };
|
96 | ts.IO.stderr.WriteLine = (s: string) => {
|
97 | ts.IO.stderr.Write(s + '\n');
|
98 | };
|
99 | ts.IO.stdout.Write = (s: string) => {
|
100 | this.emit('info', s);
|
101 | };
|
102 | ts.IO.stdout.WriteLine = (s: string) => {
|
103 | ts.IO.stdout.Write(s + '\n');
|
104 | };
|
105 | (<any>ts.BatchCompiler).prototype.addDiagnostic = function(diagnostic: ts.Diagnostic) {
|
106 | var diagnosticInfo = diagnostic.info();
|
107 | if (diagnosticInfo.category === 1 ) {
|
108 | this.hasErrors = true;
|
109 | }
|
110 | var errorLocation = '';
|
111 | if (diagnostic.fileName()) {
|
112 | errorLocation = diagnostic.fileName() + "(" + (diagnostic.line() + 1) + "," + (diagnostic.character() + 1) + "): ";
|
113 | }
|
114 | this.ioHost.stderr.WriteLine(errorLocation + diagnostic.message());
|
115 | };
|
116 | }
|
117 |
|
118 | compile(files: string[], options?: any, callback?: Function) {
|
119 | handleOverloads.call(this);
|
120 | handleSkipWrite.call(this);
|
121 | setupArguments(args => {
|
122 | ts.IO.arguments = args;
|
123 | this._batchCompile(callback);
|
124 | });
|
125 |
|
126 | function handleOverloads() {
|
127 | if (typeof options === 'function') {
|
128 | callback = options;
|
129 | options = {};
|
130 | } else if (typeof callback !== 'function') {
|
131 | callback = () => {};
|
132 | }
|
133 | }
|
134 |
|
135 | function handleSkipWrite() {
|
136 | options = options || {};
|
137 | this._skipWrite = options.skipWrite;
|
138 | delete options.skipWrite;
|
139 | }
|
140 |
|
141 | function setupArguments(cb: Function) {
|
142 | var args = argify(options);
|
143 | args.push.apply(args, files);
|
144 | cb(args);
|
145 | }
|
146 | }
|
147 |
|
148 | private _batchCompile(callback: Function) {
|
149 | var compiler = <any>this._compiler;
|
150 |
|
151 | ts.CompilerDiagnostics.diagnosticWriter = { Alert: (s: string) => { compiler.ioHost.printLine(s); } };
|
152 |
|
153 | if (compiler.parseOptions()) {
|
154 | compiler.logger = compiler.compilationSettings.gatherDiagnostics() ? new DiagnosticsLogger((<any>this).ioHost) : new ts.NullLogger();
|
155 |
|
156 | if (compiler.compilationSettings.watch()) {
|
157 |
|
158 | compiler.watchFiles();
|
159 | callback(null);
|
160 | return;
|
161 | }
|
162 |
|
163 | compiler.resolve();
|
164 | this._compile(callback);
|
165 | } else {
|
166 | callback(new Error('Error parsing compiler options'));
|
167 | }
|
168 |
|
169 | if (compiler.hasErrors) {
|
170 | callback(new Error('Unspecified error'));
|
171 | }
|
172 | }
|
173 |
|
174 | private _compile(callback: Function) {
|
175 | var compiler = <any>this._compiler;
|
176 | var tsCompiler = new ts.TypeScriptCompiler(compiler.logger, compiler.compilationSettings);
|
177 |
|
178 | compiler.resolvedFiles.forEach(resolvedFile => {
|
179 | var sourceFile = compiler.getSourceFile(resolvedFile.path);
|
180 | tsCompiler.addFile(resolvedFile.path, sourceFile.scriptSnapshot, sourceFile.byteOrderMark, 0, false, resolvedFile.referencedFiles);
|
181 | });
|
182 |
|
183 | var results: ts.OutputFile[] = [];
|
184 | for (var it = tsCompiler.compile((path: string) => compiler.resolvePath(path)); it.moveNext();) {
|
185 | var result = it.current();
|
186 |
|
187 | result.diagnostics.forEach(d => compiler.addDiagnostic(d));
|
188 | if (!this._skipWrite && !compiler.tryWriteOutputFiles(result.outputFiles)) {
|
189 | callback(new Error('Error writing to output file'));
|
190 | }
|
191 | Array.prototype.push.apply(results, result.outputFiles);
|
192 | }
|
193 | callback(null, results);
|
194 | }
|
195 | }
|
196 |
|
197 | class DiagnosticsLogger implements ts.ILogger {
|
198 | constructor(public ioHost: ts.IIO) {
|
199 | }
|
200 | public information(): boolean { return false; }
|
201 | public debug(): boolean { return false; }
|
202 | public warning(): boolean { return false; }
|
203 | public error(): boolean { return false; }
|
204 | public fatal(): boolean { return false; }
|
205 | public log(s: string): void {
|
206 | this.ioHost.stdout.WriteLine(s);
|
207 | }
|
208 | }
|
209 |
|
210 | function argify(options: ICompilerOptions): string[] {
|
211 | var args = [];
|
212 | Object.keys(options).forEach(key => {
|
213 | var value = options[key];
|
214 | if (!value) {
|
215 | return;
|
216 | }
|
217 | if (key === 'optionsFile') {
|
218 | args.push('@' + value);
|
219 | return;
|
220 | }
|
221 | var flag = '-';
|
222 | if (key.length !== 1) {
|
223 | flag += '-';
|
224 | }
|
225 | args.push(flag + key);
|
226 | if (typeof value !== 'boolean') {
|
227 | args.push(value);
|
228 | }
|
229 | });
|
230 | return args;
|
231 | }
|
232 |
|
233 | export class OutputFile extends ts.OutputFile {
|
234 |
|
235 | }
|