Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(24)

Side by Side Diff: lib/main.ts

Issue 2225953002: Strip more unused features. (Closed) Base URL: git@github.com:dart-lang/js_facade_gen.git@master
Patch Set: Created 4 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 require('source-map-support').install();
2 import {SourceMapGenerator} from 'source-map';
3 import * as fs from 'fs'; 1 import * as fs from 'fs';
4 import * as path from 'path'; 2 import * as path from 'path';
5 import * as ts from 'typescript'; 3 import * as ts from 'typescript';
6 4
7 import {TranspilerBase} from './base'; 5 import {Set, TranspilerBase} from './base';
6
8 import mkdirP from './mkdirp'; 7 import mkdirP from './mkdirp';
9 import CallTranspiler from './call';
10 import DeclarationTranspiler from './declaration'; 8 import DeclarationTranspiler from './declaration';
11 import ExpressionTranspiler from './expression'; 9 import * as merge from './merge';
12 import ModuleTranspiler from './module'; 10 import ModuleTranspiler from './module';
13 import StatementTranspiler from './statement';
14 import TypeTranspiler from './type'; 11 import TypeTranspiler from './type';
15 import LiteralTranspiler from './literal'; 12 import {FacadeConverter, NameRewriter} from './facade_converter';
16 import {FacadeConverter} from './facade_converter';
17 import * as dartStyle from 'dart-style'; 13 import * as dartStyle from 'dart-style';
18 14
19 export interface TranspilerOptions { 15 export interface TranspilerOptions {
20 /** 16 /**
21 * Fail on the first error, do not collect multiple. Allows easier debugging a s stack traces lead 17 * Fail on the first error, do not collect multiple. Allows easier debugging a s stack traces lead
22 * directly to the offending line. 18 * directly to the offending line.
23 */ 19 */
24 failFast?: boolean; 20 failFast?: boolean;
25 /** Whether to generate 'library a.b.c;' names from relative file paths. */ 21 /** Whether to generate 'library a.b.c;' names from relative file paths. */
26 generateLibraryName?: boolean; 22 generateLibraryName?: boolean;
27 /** Whether to generate source maps. */
28 generateSourceMap?: boolean;
29 /** 23 /**
30 * A base path to relativize absolute file paths against. This is useful for l ibrary name 24 * A base path to relativize absolute file paths against. This is useful for l ibrary name
31 * generation (see above) and nicer file names in error messages. 25 * generation (see above) and nicer file names in error messages.
32 */ 26 */
33 basePath?: string; 27 basePath?: string;
34 /** 28 /**
35 * Translate calls to builtins, i.e. seemlessly convert from `Array` to `List` , and convert the 29 * Use dart:html instead of the raw JavaScript DOM when generated Dart code.
36 * corresponding methods. Requires type checking.
37 */ 30 */
38 translateBuiltins?: boolean; 31 useHtml?: boolean;
39 /** 32 /**
40 * Enforce conventions of public/private keyword and underscore prefix 33 * Enforce conventions of public/private keyword and underscore prefix
41 */ 34 */
42 enforceUnderscoreConventions?: boolean; 35 enforceUnderscoreConventions?: boolean;
43 /** 36 /**
44 * Sets a root path to look for typings used by the facade converter. 37 * Sets a root path to look for typings used by the facade converter.
45 */ 38 */
46 typingsRoot?: string; 39 typingsRoot?: string;
40
41 /**
42 * Experimental JS Interop specific option to promote properties with function
43 * types to methods instead of properties with a function type. This the makes
44 * the Dart code more readable at the cost of disallowing setting the value of
45 * the property.
46 * Example JS library that benifits from this option:
47 * https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/chartjs/char t.d.ts
48 */
49 promoteFunctionLikeMembers?: boolean;
47 } 50 }
48 51
49 export const COMPILER_OPTIONS: ts.CompilerOptions = { 52 export const COMPILER_OPTIONS: ts.CompilerOptions = {
50 allowNonTsExtensions: true, 53 allowNonTsExtensions: true,
51 experimentalDecorators: true, 54 experimentalDecorators: true,
52 module: ts.ModuleKind.CommonJS, 55 module: ts.ModuleKind.CommonJS,
53 target: ts.ScriptTarget.ES5, 56 target: ts.ScriptTarget.ES5,
54 }; 57 };
55 58
59 /**
60 * Context to ouput code into.
61 */
62 export enum OutputContext {
63 Import = 0,
64 Header = 1,
65 Default = 2,
66 }
67
68 const NUM_OUTPUT_CONTEXTS = 3;
69
56 export class Transpiler { 70 export class Transpiler {
57 private output: Output; 71 private outputs: Output[];
72 private outputStack: Output[];
58 private currentFile: ts.SourceFile; 73 private currentFile: ts.SourceFile;
59 74 importsEmitted: Set;
60 // Comments attach to all following AST nodes before the next 'physical' token . Track the earliest 75 // Comments attach to all following AST nodes before the next 'physical' token . Track the earliest
61 // offset to avoid printing comments multiple times. 76 // offset to avoid printing comments multiple times.
62 private lastCommentIdx: number = -1; 77 private lastCommentIdx: number = -1;
63 private errors: string[] = []; 78 private errors: string[] = [];
64 79
65 private transpilers: TranspilerBase[]; 80 private transpilers: TranspilerBase[];
81 private declarationTranspiler: DeclarationTranspiler;
66 private fc: FacadeConverter; 82 private fc: FacadeConverter;
83 private nameRewriter: NameRewriter;
67 84
68 constructor(private options: TranspilerOptions = {}) { 85 constructor(private options: TranspilerOptions = {}) {
69 // TODO: Remove the angular2 default when angular uses typingsRoot. 86 this.nameRewriter = new NameRewriter();
70 this.fc = new FacadeConverter(this, options.typingsRoot || 'angular2/typings /'); 87 this.fc = new FacadeConverter(this, options.typingsRoot, this.nameRewriter, options.useHtml);
88 this.declarationTranspiler = new DeclarationTranspiler(
89 this, this.fc, options.enforceUnderscoreConventions, options.promoteFunc tionLikeMembers);
71 this.transpilers = [ 90 this.transpilers = [
72 new CallTranspiler(this, this.fc), // Has to come before StatementTranspi ler! 91 this.declarationTranspiler,
73 new DeclarationTranspiler(this, this.fc, options.enforceUnderscoreConventi ons),
74 new ExpressionTranspiler(this, this.fc),
75 new LiteralTranspiler(this, this.fc),
76 new ModuleTranspiler(this, this.fc, options.generateLibraryName), 92 new ModuleTranspiler(this, this.fc, options.generateLibraryName),
77 new StatementTranspiler(this),
78 new TypeTranspiler(this, this.fc), 93 new TypeTranspiler(this, this.fc),
79 ]; 94 ];
80 } 95 }
81 96
82 /** 97 /**
83 * Transpiles the given files to Dart. 98 * Transpiles the given files to Dart.
84 * @param fileNames The input files. 99 * @param fileNames The input files.
85 * @param destination Location to write files to. Creates files next to their sources if absent. 100 * @param destination Location to write files to. Creates files next to their sources if absent.
86 */ 101 */
87 transpile(fileNames: string[], destination?: string): void { 102 transpile(fileNames: string[], destination?: string): void {
88 if (this.options.basePath) { 103 if (this.options.basePath) {
89 this.options.basePath = this.normalizeSlashes(path.resolve(this.options.ba sePath)); 104 this.options.basePath = this.normalizeSlashes(path.resolve(this.options.ba sePath));
90 } 105 }
91 fileNames = fileNames.map((f) => this.normalizeSlashes(f)); 106 fileNames = fileNames.map((f) => this.normalizeSlashes(f));
92 let host = this.createCompilerHost(); 107 let host = this.createCompilerHost();
93 if (this.options.basePath && destination === undefined) { 108 if (this.options.basePath && destination === undefined) {
94 throw new Error( 109 throw new Error(
95 'Must have a destination path when a basePath is specified ' + this.op tions.basePath); 110 'Must have a destination path when a basePath is specified ' + this.op tions.basePath);
96 } 111 }
97 let destinationRoot = destination || this.options.basePath || ''; 112 let destinationRoot = destination || this.options.basePath || '';
98 let program = ts.createProgram(fileNames, this.getCompilerOptions(), host); 113 let program = ts.createProgram(fileNames, this.getCompilerOptions(), host);
99 if (this.options.translateBuiltins) { 114 this.fc.setTypeChecker(program.getTypeChecker());
100 this.fc.setTypeChecker(program.getTypeChecker()); 115 this.declarationTranspiler.setTypeChecker(program.getTypeChecker());
101 }
102 116
103 // Only write files that were explicitly passed in. 117 // Only write files that were explicitly passed in.
104 let fileSet: {[s: string]: boolean} = {}; 118 let fileSet: {[s: string]: boolean} = {};
105 fileNames.forEach((f) => fileSet[f] = true); 119 fileNames.forEach((f) => fileSet[f] = true);
120 let sourceFiles = program.getSourceFiles().filter((sourceFile) => fileSet[so urceFile.fileName]);
106 121
107 this.errors = []; 122 this.errors = [];
108 program.getSourceFiles() 123
109 .filter((sourceFile) => fileSet[sourceFile.fileName]) 124 sourceFiles.forEach((f: ts.SourceFile) => {
110 // Do not generate output for .d.ts files. 125 let dartCode = this.translate(f);
111 .filter((sourceFile: ts.SourceFile) => !sourceFile.fileName.match(/\.d\. ts$/)) 126 let outputFile = this.getOutputPath(path.resolve(f.fileName), destinationR oot);
112 .forEach((f: ts.SourceFile) => { 127 mkdirP(path.dirname(outputFile));
113 let dartCode = this.translate(f); 128 fs.writeFileSync(outputFile, dartCode);
114 let outputFile = this.getOutputPath(path.resolve(f.fileName), destinat ionRoot); 129 });
115 mkdirP(path.dirname(outputFile));
116 fs.writeFileSync(outputFile, dartCode);
117 });
118 this.checkForErrors(program); 130 this.checkForErrors(program);
119 } 131 }
120 132
121 translateProgram(program: ts.Program): {[path: string]: string} { 133 translateProgram(program: ts.Program): {[path: string]: string} {
122 if (this.options.translateBuiltins) { 134 this.fc.setTypeChecker(program.getTypeChecker());
123 this.fc.setTypeChecker(program.getTypeChecker()); 135 this.declarationTranspiler.setTypeChecker(program.getTypeChecker());
124 } 136
125 let paths: {[path: string]: string} = {}; 137 let paths: {[path: string]: string} = {};
126 this.errors = []; 138 this.errors = [];
127 program.getSourceFiles() 139 program.getSourceFiles()
128 .filter( 140 .filter(
129 (sourceFile: ts.SourceFile) => 141 (sourceFile: ts.SourceFile) =>
130 (!sourceFile.fileName.match(/\.d\.ts$/) && !!sourceFile.fileName .match(/\.[jt]s$/))) 142 (!sourceFile.fileName.match(/\.d\.ts$/) && !!sourceFile.fileName .match(/\.[jt]s$/)))
131 .forEach((f) => paths[f.fileName] = this.translate(f)); 143 .forEach((f) => paths[f.fileName] = this.translate(f));
132 this.checkForErrors(program); 144 this.checkForErrors(program);
133 return paths; 145 return paths;
134 } 146 }
(...skipping 27 matching lines...) Expand all
162 getCurrentDirectory: () => '', 174 getCurrentDirectory: () => '',
163 getNewLine: () => '\n', 175 getNewLine: () => '\n',
164 }; 176 };
165 compilerHost.resolveModuleNames = getModuleResolver(compilerHost); 177 compilerHost.resolveModuleNames = getModuleResolver(compilerHost);
166 return compilerHost; 178 return compilerHost;
167 } 179 }
168 180
169 // Visible for testing. 181 // Visible for testing.
170 getOutputPath(filePath: string, destinationRoot: string): string { 182 getOutputPath(filePath: string, destinationRoot: string): string {
171 let relative = this.getRelativeFileName(filePath); 183 let relative = this.getRelativeFileName(filePath);
172 let dartFile = relative.replace(/.(js|es6|ts)$/, '.dart'); 184 let dartFile = relative.replace(/.(js|es6|d\.ts|ts)$/, '.dart');
173 return this.normalizeSlashes(path.join(destinationRoot, dartFile)); 185 return this.normalizeSlashes(path.join(destinationRoot, dartFile));
174 } 186 }
175 187
188 public pushContext(context: OutputContext) { this.outputStack.push(this.output s[context]); }
189
190 public popContext() {
191 if (this.outputStack.length === 0) {
192 this.reportError(null, 'Attempting to pop output stack when already empty' );
193 }
194 this.outputStack.pop();
195 }
196
176 private translate(sourceFile: ts.SourceFile): string { 197 private translate(sourceFile: ts.SourceFile): string {
177 this.currentFile = sourceFile; 198 this.currentFile = sourceFile;
178 this.output = 199 this.outputs = [];
179 new Output(sourceFile, this.getRelativeFileName(), this.options.generate SourceMap); 200 this.outputStack = [];
201 this.importsEmitted = {};
202 for (let i = 0; i < NUM_OUTPUT_CONTEXTS; ++i) {
203 this.outputs.push(new Output());
204 }
205
180 this.lastCommentIdx = -1; 206 this.lastCommentIdx = -1;
207 merge.normalizeSourceFile(sourceFile);
208 this.pushContext(OutputContext.Default);
181 this.visit(sourceFile); 209 this.visit(sourceFile);
182 let result = this.output.getResult(); 210 this.popContext();
211 if (this.outputStack.length > 0) {
212 this.reportError(
213 sourceFile, 'Internal error managing output contexts. ' +
214 'Inconsistent push and pop context calls.');
215 }
216 let result = '';
217 for (let output of this.outputs) {
218 result += output.getResult();
219 }
183 return this.formatCode(result, sourceFile); 220 return this.formatCode(result, sourceFile);
184 } 221 }
185 222
186 private formatCode(code: string, context: ts.Node) { 223 private formatCode(code: string, context: ts.Node) {
187 let result = dartStyle.formatCode(code); 224 let result = dartStyle.formatCode(code);
188 if (result.error) { 225 if (result.error) {
189 this.reportError(context, result.error); 226 this.reportError(context, result.error);
190 } 227 }
191 return result.code; 228 return result.code;
192 } 229 }
193 230
194 private checkForErrors(program: ts.Program) { 231 private checkForErrors(program: ts.Program) {
195 let errors = this.errors; 232 let errors = this.errors;
196 233
197 let diagnostics = program.getGlobalDiagnostics().concat(program.getSyntactic Diagnostics()); 234 let diagnostics = program.getGlobalDiagnostics().concat(program.getSyntactic Diagnostics());
198 235
199 if ((errors.length || diagnostics.length) && this.options.translateBuiltins) { 236 if ((errors.length || diagnostics.length)) {
200 // Only report semantic diagnostics if ts2dart failed; this code is not a generic compiler, so 237 // Only report semantic diagnostics if facade generation failed; this
201 // only yields TS errors if they could be the cause of ts2dart issues. 238 // code is not a generic compiler, so only yields TS errors if they could
239 // be the cause of facade generation issues.
202 // This greatly speeds up tests and execution. 240 // This greatly speeds up tests and execution.
203 diagnostics = diagnostics.concat(program.getSemanticDiagnostics()); 241 diagnostics = diagnostics.concat(program.getSemanticDiagnostics());
204 } 242 }
205 243
206 let diagnosticErrs = diagnostics.map((d) => { 244 let diagnosticErrs = diagnostics.map((d) => {
207 let msg = ''; 245 let msg = '';
208 if (d.file) { 246 if (d.file) {
209 let pos = d.file.getLineAndCharacterOfPosition(d.start); 247 let pos = d.file.getLineAndCharacterOfPosition(d.start);
210 let fn = this.getRelativeFileName(d.file.fileName); 248 let fn = this.getRelativeFileName(d.file.fileName);
211 msg += ` ${fn}:${pos.line + 1}:${pos.character + 1}`; 249 msg += ` ${fn}:${pos.line + 1}:${pos.character + 1}`;
212 } 250 }
213 msg += ': '; 251 msg += ': ';
214 msg += ts.flattenDiagnosticMessageText(d.messageText, '\n'); 252 msg += ts.flattenDiagnosticMessageText(d.messageText, '\n');
215 return msg; 253 return msg;
216 }); 254 });
217 if (diagnosticErrs.length) errors = errors.concat(diagnosticErrs); 255 if (diagnosticErrs.length) errors = errors.concat(diagnosticErrs);
218 256
219 if (errors.length) { 257 if (errors.length) {
220 let e = new Error(errors.join('\n')); 258 let e = new Error(errors.join('\n'));
221 e.name = 'TS2DartError'; 259 e.name = 'DartFacadeError';
222 throw e; 260 throw e;
223 } 261 }
224 } 262 }
225 263
226 /** 264 /**
227 * Returns `filePath`, relativized to the program's `basePath`. 265 * Returns `filePath`, relativized to the program's `basePath`.
228 * @param filePath Optional path to relativize, defaults to the current file's path. 266 * @param filePath Optional path to relativize, defaults to the current file's path.
229 */ 267 */
230 getRelativeFileName(filePath?: string) { 268 getRelativeFileName(filePath?: string) {
231 if (filePath === undefined) filePath = path.resolve(this.currentFile.fileNam e); 269 if (filePath === undefined) filePath = path.resolve(this.currentFile.fileNam e);
232 // TODO(martinprobst): Use path.isAbsolute on node v0.12. 270 // TODO(martinprobst): Use path.isAbsolute on node v0.12.
233 if (this.normalizeSlashes(path.resolve('/x/', filePath)) !== filePath) { 271 if (this.normalizeSlashes(path.resolve('/x/', filePath)) !== filePath) {
234 return filePath; // already relative. 272 return filePath; // already relative.
235 } 273 }
236 let base = this.options.basePath || ''; 274 let base = this.options.basePath || '';
237 if (filePath.indexOf(base) !== 0 && !filePath.match(/\.d\.ts$/)) { 275 if (filePath.indexOf(base) !== 0 && !filePath.match(/\.d\.ts$/)) {
238 throw new Error(`Files must be located under base, got ${filePath} vs ${ba se}`); 276 throw new Error(`Files must be located under base, got ${filePath} vs ${ba se}`);
239 } 277 }
240 return this.normalizeSlashes(path.relative(base, filePath)); 278 return this.normalizeSlashes(path.relative(base, filePath));
241 } 279 }
242 280
243 emit(s: string) { this.output.emit(s); } 281 private get currentOutput(): Output { return this.outputStack[this.outputStack .length - 1]; }
244 emitNoSpace(s: string) { this.output.emitNoSpace(s); } 282
283 emit(s: string) { this.currentOutput.emit(s); }
284 emitNoSpace(s: string) { this.currentOutput.emitNoSpace(s); }
285 enterCodeComment() { return this.currentOutput.enterCodeComment(); }
286 exitCodeComment() { return this.currentOutput.exitCodeComment(); }
245 287
246 reportError(n: ts.Node, message: string) { 288 reportError(n: ts.Node, message: string) {
247 let file = n.getSourceFile() || this.currentFile; 289 let file = n.getSourceFile() || this.currentFile;
248 let fileName = this.getRelativeFileName(file.fileName); 290 let fileName = this.getRelativeFileName(file.fileName);
249 let start = n.getStart(file); 291 let start = n.getStart(file);
250 let pos = file.getLineAndCharacterOfPosition(start); 292 let pos = file.getLineAndCharacterOfPosition(start);
251 // Line and character are 0-based. 293 // Line and character are 0-based.
252 let fullMessage = `${fileName}:${pos.line + 1}:${pos.character + 1}: ${messa ge}`; 294 let fullMessage = `${fileName}:${pos.line + 1}:${pos.character + 1}: ${messa ge}`;
253 if (this.options.failFast) throw new Error(fullMessage); 295 if (this.options.failFast) throw new Error(fullMessage);
254 this.errors.push(fullMessage); 296 this.errors.push(fullMessage);
255 } 297 }
256 298
257 visit(node: ts.Node) { 299 visit(node: ts.Node) {
258 this.output.addSourceMapping(node); 300 if (!node) return;
259 let comments = ts.getLeadingCommentRanges(this.currentFile.text, node.getFul lStart()); 301 let comments = ts.getLeadingCommentRanges(this.currentFile.text, node.getFul lStart());
260 if (comments) { 302 if (comments) {
261 comments.forEach((c) => { 303 comments.forEach((c) => {
304 // Warning: the following check means that comments will only be
305 // emitted correctly if Dart code is emitted in the same order it
306 // appeared in the JavaScript AST.
262 if (c.pos <= this.lastCommentIdx) return; 307 if (c.pos <= this.lastCommentIdx) return;
263 this.lastCommentIdx = c.pos; 308 this.lastCommentIdx = c.pos;
264 let text = this.currentFile.text.substring(c.pos, c.end); 309 let text = this.currentFile.text.substring(c.pos, c.end);
265 this.emitNoSpace('\n'); 310 this.currentOutput.maybeLineBreak();
266 this.emit(this.translateComment(text)); 311 this.emit(this.translateComment(text));
267 if (c.hasTrailingNewLine) this.emitNoSpace('\n'); 312 if (c.hasTrailingNewLine) this.emitNoSpace('\n');
268 }); 313 });
269 } 314 }
270 315
271 for (let i = 0; i < this.transpilers.length; i++) { 316 for (let i = 0; i < this.transpilers.length; i++) {
272 if (this.transpilers[i].visitNode(node)) return; 317 if (this.transpilers[i].visitNode(node)) return;
273 } 318 }
274 319
275 this.reportError( 320 this.reportError(
276 node, 321 node,
277 'Unsupported node type ' + (<any>ts).SyntaxKind[node.kind] + ': ' + node .getFullText()); 322 'Unsupported node type ' + (<any>ts).SyntaxKind[node.kind] + ': ' + node .getFullText());
278 } 323 }
279 324
280 private normalizeSlashes(path: string) { return path.replace(/\\/g, '/'); } 325 private normalizeSlashes(path: string) { return path.replace(/\\/g, '/'); }
281 326
282 private translateComment(comment: string): string { 327 private translateComment(comment: string): string {
283 comment = comment.replace(/\{@link ([^\}]+)\}/g, '[$1]'); 328 comment = comment.replace(/\{@link ([^\}]+)\}/g, '[$1]');
284 329
285 // Remove the following tags and following comments till end of line. 330 // Remove the following tags and following comments till end of line.
286 comment = comment.replace(/@param.*$/gm, ''); 331 comment = comment.replace(/@param.*$/gm, '');
287 comment = comment.replace(/@throws.*$/gm, ''); 332 comment = comment.replace(/@throws.*$/gm, '');
288 comment = comment.replace(/@return.*$/gm, ''); 333 comment = comment.replace(/@return.*$/gm, '');
289 334
290 // Remove the following tags. 335 // Remove the following tags.
291 comment = comment.replace(/@module/g, ''); 336 comment = comment.replace(/@module/g, '');
292 comment = comment.replace(/@description/g, ''); 337 comment = comment.replace(/@description/g, '');
293 comment = comment.replace(/@deprecated/g, ''); 338 comment = comment.replace(/@deprecated/g, '');
294 339
340 // Hack to make comment indentation does not look as bad when dart format is
341 // run on files with multiple nested modules.
342 comment = comment.replace(/\n ( +[*])/g, '\n$1');
295 return comment; 343 return comment;
296 } 344 }
297 } 345 }
298 346
299 export function getModuleResolver(compilerHost: ts.CompilerHost) { 347 export function getModuleResolver(compilerHost: ts.CompilerHost) {
300 return (moduleNames: string[], containingFile: string): ts.ResolvedModule[] => { 348 return (moduleNames: string[], containingFile: string): ts.ResolvedModule[] => {
301 let res: ts.ResolvedModule[] = []; 349 let res: ts.ResolvedModule[] = [];
302 for (let mod of moduleNames) { 350 for (let mod of moduleNames) {
303 let lookupRes = 351 let lookupRes =
304 ts.nodeModuleNameResolver(mod, containingFile, COMPILER_OPTIONS, compi lerHost); 352 ts.nodeModuleNameResolver(mod, containingFile, COMPILER_OPTIONS, compi lerHost);
305 if (lookupRes.resolvedModule) { 353 if (lookupRes.resolvedModule) {
306 res.push(lookupRes.resolvedModule); 354 res.push(lookupRes.resolvedModule);
307 continue; 355 continue;
308 } 356 }
309 lookupRes = ts.classicNameResolver(mod, containingFile, COMPILER_OPTIONS, compilerHost); 357 lookupRes = ts.classicNameResolver(mod, containingFile, COMPILER_OPTIONS, compilerHost);
310 if (lookupRes.resolvedModule) { 358 if (lookupRes.resolvedModule) {
311 res.push(lookupRes.resolvedModule); 359 res.push(lookupRes.resolvedModule);
312 continue; 360 continue;
313 } 361 }
314 res.push(undefined); 362 res.push(undefined);
315 } 363 }
316 return res; 364 return res;
317 }; 365 };
318 } 366 }
319 367
320 class Output { 368 class Output {
321 private result: string = ''; 369 private result: string = '';
322 private column: number = 1; 370 private firstColumn: boolean = true;
323 private line: number = 1;
324 371
325 // Position information. 372 private insideCodeComment: boolean = false;
326 private generateSourceMap: boolean; 373 private codeCommentResult: string = '';
327 private sourceMap: SourceMapGenerator;
328 374
329 constructor( 375 /**
330 private currentFile: ts.SourceFile, private relativeFileName: string, 376 * Line break if the current line is not empty.
331 generateSourceMap: boolean) { 377 */
332 if (generateSourceMap) { 378 maybeLineBreak() {
333 this.sourceMap = new SourceMapGenerator({file: relativeFileName + '.dart'} ); 379 if (this.insideCodeComment) {
334 this.sourceMap.setSourceContent(relativeFileName, this.currentFile.text); 380 // Avoid line breaks inside code comments.
381 return;
382 }
383
384 if (!this.firstColumn) {
385 this.emitNoSpace('\n');
335 } 386 }
336 } 387 }
337 388
338 emit(str: string) { 389 emit(str: string) {
339 this.emitNoSpace(' '); 390 this.emitNoSpace(' ');
340 this.emitNoSpace(str); 391 this.emitNoSpace(str);
341 } 392 }
342 393
343 emitNoSpace(str: string) { 394 emitNoSpace(str: string) {
395 if (str.length === 0) return;
396 if (this.insideCodeComment) {
397 this.codeCommentResult += str;
398 return;
399 }
344 this.result += str; 400 this.result += str;
345 for (let i = 0; i < str.length; i++) { 401 this.firstColumn = str[str.length - 1] === '\n';
346 if (str[i] === '\n') {
347 this.line++;
348 this.column = 0;
349 } else {
350 this.column++;
351 }
352 }
353 } 402 }
354 403
355 getResult(): string { return this.result + this.generateSourceMapComment(); } 404 enterCodeComment() {
356 405 if (this.insideCodeComment) {
357 addSourceMapping(n: ts.Node) { 406 throw 'Cannot nest code comments';
358 if (!this.generateSourceMap) return; // source maps disabled. 407 }
359 let file = n.getSourceFile() || this.currentFile; 408 this.insideCodeComment = true;
360 let start = n.getStart(file); 409 this.codeCommentResult = '';
361 let pos = file.getLineAndCharacterOfPosition(start);
362
363 let mapping: SourceMap.Mapping = {
364 original: {line: pos.line + 1, column: pos.character},
365 generated: {line: this.line, column: this.column},
366 source: this.relativeFileName,
367 };
368
369 this.sourceMap.addMapping(mapping);
370 } 410 }
371 411
372 private generateSourceMapComment() { 412 exitCodeComment() {
373 if (!this.sourceMap) return ''; 413 if (!this.insideCodeComment) {
374 let base64map = new Buffer(JSON.stringify(this.sourceMap)).toString('base64' ); 414 throw 'Exit code comment called while not within a code comment.';
375 return '\n\n//# sourceMappingURL=data:application/json;base64,' + base64map; 415 }
416 this.insideCodeComment = false;
417 this.emit('/*');
418 let result = dartStyle.formatCode(this.codeCommentResult);
419 if (result.error) {
420 // Fall back to the raw expression if the formatter returns an error.
421 // Ideally the formatter would handle a wider range of dart expressions.
422 this.emit(this.codeCommentResult.trim());
423 } else {
424 this.emit(result.code.trim());
425 }
426 this.emit('*/');
427 // Don't really need an exact column, just need to track
428 // that we aren't on the first column.
429 this.firstColumn = false;
430 this.codeCommentResult = '';
431 }
432
433 getResult(): string {
434 if (this.insideCodeComment) {
435 throw 'Code comment not property terminated.';
436 }
437 return this.result;
376 } 438 }
377 } 439 }
378 440
379 // CLI entry point 441 // CLI entry point
380 if (require.main === module) { 442 if (require.main === module) {
381 let args = require('minimist')(process.argv.slice(2), {base: 'string'}); 443 let args = require('minimist')(process.argv.slice(2), {base: 'string'});
382 try { 444 try {
383 let transpiler = new Transpiler(args); 445 let transpiler = new Transpiler(args);
384 console.error('Transpiling', args._, 'to', args.destination); 446 console.error('Transpiling', args._, 'to', args.destination);
385 transpiler.transpile(args._, args.destination); 447 transpiler.transpile(args._, args.destination);
386 } catch (e) { 448 } catch (e) {
387 if (e.name !== 'TS2DartError') throw e; 449 if (e.name !== 'DartFacadeError') throw e;
388 console.error(e.message); 450 console.error(e.message);
389 process.exit(1); 451 process.exit(1);
390 } 452 }
391 } 453 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698