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

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

Powered by Google App Engine
This is Rietveld 408576698