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

Side by Side Diff: pkg/dev_compiler/lib/src/compiler/compiler.dart

Issue 2337213003: Support generating inlined source maps and wrapping module contents within a JavaScript eval block … (Closed)
Patch Set: Refactor based on John's offline comments. 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
OLDNEW
1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a 2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file. 3 // BSD-style license that can be found in the LICENSE file.
4 4
5 import 'dart:collection' show HashSet, Queue; 5 import 'dart:collection' show HashSet, Queue;
6 import 'dart:convert' show JSON; 6 import 'dart:convert' show BASE64, JSON, UTF8;
7 import 'dart:io' show File; 7 import 'dart:io' show File;
8 import 'package:analyzer/dart/element/element.dart' show LibraryElement; 8 import 'package:analyzer/dart/element/element.dart' show LibraryElement;
9 import 'package:analyzer/analyzer.dart' 9 import 'package:analyzer/analyzer.dart'
10 show AnalysisError, CompilationUnit, ErrorSeverity; 10 show AnalysisError, CompilationUnit, ErrorSeverity;
11 import 'package:analyzer/file_system/file_system.dart' show ResourceProvider; 11 import 'package:analyzer/file_system/file_system.dart' show ResourceProvider;
12 import 'package:analyzer/src/generated/engine.dart' show AnalysisContext; 12 import 'package:analyzer/src/generated/engine.dart' show AnalysisContext;
13 import 'package:analyzer/src/generated/source.dart' show DartUriResolver; 13 import 'package:analyzer/src/generated/source.dart' show DartUriResolver;
14 import 'package:analyzer/src/generated/source_io.dart' 14 import 'package:analyzer/src/generated/source_io.dart'
15 show Source, SourceKind, UriResolver; 15 show Source, SourceKind, UriResolver;
16 import 'package:analyzer/src/summary/package_bundle_reader.dart' 16 import 'package:analyzer/src/summary/package_bundle_reader.dart'
(...skipping 137 matching lines...) Expand 10 before | Expand all | Expand 10 after
154 /// Whether to emit the source mapping file. 154 /// Whether to emit the source mapping file.
155 /// 155 ///
156 /// This supports debugging the original source code instead of the generated 156 /// This supports debugging the original source code instead of the generated
157 /// code. 157 /// code.
158 final bool sourceMap; 158 final bool sourceMap;
159 159
160 /// If [sourceMap] is emitted, this will emit a `sourceMappingUrl` comment 160 /// If [sourceMap] is emitted, this will emit a `sourceMappingUrl` comment
161 /// into the output JavaScript module. 161 /// into the output JavaScript module.
162 final bool sourceMapComment; 162 final bool sourceMapComment;
163 163
164 /// Whether to emit the source mapping file inline as a data url.
165 final bool inlineSourceMap;
166
164 /// Whether to emit a summary file containing API signatures. 167 /// Whether to emit a summary file containing API signatures.
165 /// 168 ///
166 /// This is required for a modular build process. 169 /// This is required for a modular build process.
167 final bool summarizeApi; 170 final bool summarizeApi;
168 171
169 /// The file extension for summaries. 172 /// The file extension for summaries.
170 final String summaryExtension; 173 final String summaryExtension;
171 174
172 /// Whether to preserve metdata only accessible via mirrors 175 /// Whether to preserve metdata only accessible via mirrors
173 final bool emitMetadata; 176 final bool emitMetadata;
(...skipping 27 matching lines...) Expand all
201 /// Supporting the syntax: 204 /// Supporting the syntax:
202 /// * Chrome Canary (51) 205 /// * Chrome Canary (51)
203 /// * Firefox 206 /// * Firefox
204 /// 207 ///
205 /// Not yet supporting: 208 /// Not yet supporting:
206 /// * Atom (1.5.4) 209 /// * Atom (1.5.4)
207 /// * Electron (0.36.3) 210 /// * Electron (0.36.3)
208 // TODO(ochafik): Simplify this code when our target platforms catch up. 211 // TODO(ochafik): Simplify this code when our target platforms catch up.
209 final bool destructureNamedParams; 212 final bool destructureNamedParams;
210 213
214 /// Mapping from absolute file paths to bazel short path to substitute in
215 /// source maps.
216 final Map<String, String> bazelMapping;
217
211 const CompilerOptions( 218 const CompilerOptions(
212 {this.sourceMap: true, 219 {this.sourceMap: true,
213 this.sourceMapComment: true, 220 this.sourceMapComment: true,
221 this.inlineSourceMap: false,
214 this.summarizeApi: true, 222 this.summarizeApi: true,
215 this.summaryExtension: 'sum', 223 this.summaryExtension: 'sum',
216 this.unsafeForceCompile: false, 224 this.unsafeForceCompile: false,
217 this.emitMetadata: false, 225 this.emitMetadata: false,
218 this.closure: false, 226 this.closure: false,
219 this.destructureNamedParams: false, 227 this.destructureNamedParams: false,
220 this.hoistInstanceCreation: true, 228 this.hoistInstanceCreation: true,
221 this.hoistSignatureTypes: false, 229 this.hoistSignatureTypes: false,
222 this.nameTypeTests: true, 230 this.nameTypeTests: true,
223 this.hoistTypeTests: true, 231 this.hoistTypeTests: true,
224 this.useAngular2Whitelist: false}); 232 this.useAngular2Whitelist: false,
233 this.bazelMapping: const {}});
225 234
226 CompilerOptions.fromArguments(ArgResults args) 235 CompilerOptions.fromArguments(ArgResults args)
227 : sourceMap = args['source-map'], 236 : sourceMap = args['source-map'],
228 sourceMapComment = args['source-map-comment'], 237 sourceMapComment = args['source-map-comment'],
238 inlineSourceMap = args['inline-source-map'],
229 summarizeApi = args['summarize'], 239 summarizeApi = args['summarize'],
230 summaryExtension = args['summary-extension'], 240 summaryExtension = args['summary-extension'],
231 unsafeForceCompile = args['unsafe-force-compile'], 241 unsafeForceCompile = args['unsafe-force-compile'],
232 emitMetadata = args['emit-metadata'], 242 emitMetadata = args['emit-metadata'],
233 closure = args['closure-experimental'], 243 closure = args['closure-experimental'],
234 destructureNamedParams = args['destructure-named-params'], 244 destructureNamedParams = args['destructure-named-params'],
235 hoistInstanceCreation = args['hoist-instance-creation'], 245 hoistInstanceCreation = args['hoist-instance-creation'],
236 hoistSignatureTypes = args['hoist-signature-types'], 246 hoistSignatureTypes = args['hoist-signature-types'],
237 nameTypeTests = args['name-type-tests'], 247 nameTypeTests = args['name-type-tests'],
238 hoistTypeTests = args['hoist-type-tests'], 248 hoistTypeTests = args['hoist-type-tests'],
239 useAngular2Whitelist = args['unsafe-angular2-whitelist']; 249 useAngular2Whitelist = args['unsafe-angular2-whitelist'],
250 bazelMapping = _parseBazelMappings(args['bazel-mapping']);
240 251
241 static void addArguments(ArgParser parser) { 252 static void addArguments(ArgParser parser) {
242 parser 253 parser
243 ..addFlag('summarize', help: 'emit an API summary file', defaultsTo: true) 254 ..addFlag('summarize', help: 'emit an API summary file', defaultsTo: true)
244 ..addOption('summary-extension', 255 ..addOption('summary-extension',
245 help: 'file extension for Dart summary files', 256 help: 'file extension for Dart summary files',
246 defaultsTo: 'sum', 257 defaultsTo: 'sum',
247 hide: true) 258 hide: true)
248 ..addFlag('source-map', help: 'emit source mapping', defaultsTo: true) 259 ..addFlag('source-map', help: 'emit source mapping', defaultsTo: true)
249 ..addFlag('source-map-comment', 260 ..addFlag('source-map-comment',
250 help: 'adds a sourceMappingURL comment to the end of the JS,\n' 261 help: 'adds a sourceMappingURL comment to the end of the JS,\n'
251 'disable if using X-SourceMap header', 262 'disable if using X-SourceMap header',
252 defaultsTo: true, 263 defaultsTo: true,
253 hide: true) 264 hide: true)
265 ..addFlag('inline-source-map',
266 help: 'emit source mapping inline', defaultsTo: false)
254 ..addFlag('emit-metadata', 267 ..addFlag('emit-metadata',
255 help: 'emit metadata annotations queriable via mirrors', 268 help: 'emit metadata annotations queriable via mirrors',
256 defaultsTo: false) 269 defaultsTo: false)
257 ..addFlag('closure-experimental', 270 ..addFlag('closure-experimental',
258 help: 'emit Closure Compiler-friendly code (experimental)', 271 help: 'emit Closure Compiler-friendly code (experimental)',
259 defaultsTo: false) 272 defaultsTo: false)
260 ..addFlag('destructure-named-params', 273 ..addFlag('destructure-named-params',
261 help: 'Destructure named parameters', defaultsTo: false, hide: true) 274 help: 'Destructure named parameters', defaultsTo: false, hide: true)
262 ..addFlag('unsafe-force-compile', 275 ..addFlag('unsafe-force-compile',
263 help: 'Compile code even if it has errors. ಠ_ಠ\n' 276 help: 'Compile code even if it has errors. ಠ_ಠ\n'
264 'This has undefined behavior!', 277 'This has undefined behavior!',
265 defaultsTo: false, 278 defaultsTo: false,
266 hide: true) 279 hide: true)
267 ..addFlag('hoist-instance-creation', 280 ..addFlag('hoist-instance-creation',
268 help: 'Hoist the class type from generic instance creations', 281 help: 'Hoist the class type from generic instance creations',
269 defaultsTo: true, 282 defaultsTo: true,
270 hide: true) 283 hide: true)
271 ..addFlag('hoist-signature-types', 284 ..addFlag('hoist-signature-types',
272 help: 'Hoist types from class signatures', 285 help: 'Hoist types from class signatures',
273 defaultsTo: false, 286 defaultsTo: false,
274 hide: true) 287 hide: true)
275 ..addFlag('name-type-tests', 288 ..addFlag('name-type-tests',
276 help: 'Name types used in type tests', defaultsTo: true, hide: true) 289 help: 'Name types used in type tests', defaultsTo: true, hide: true)
277 ..addFlag('hoist-type-tests', 290 ..addFlag('hoist-type-tests',
278 help: 'Hoist types used in type tests', defaultsTo: true, hide: true) 291 help: 'Hoist types used in type tests', defaultsTo: true, hide: true)
279 ..addFlag('unsafe-angular2-whitelist', defaultsTo: false, hide: true); 292 ..addFlag('unsafe-angular2-whitelist', defaultsTo: false, hide: true)
293 ..addOption('bazel-mapping',
294 help:
295 '--bazel-mapping=genfiles/to/library.dart,to/library.dart uses \n'
296 'to/library.dart as the path for library.dart in source maps.',
297 allowMultiple: true,
298 splitCommas: false,
299 hide: true);
300 }
301
302 static Map<String, String> _parseBazelMappings(Iterable argument) {
303 var mappings = <String, String>{};
304 for (var mapping in argument) {
305 var splitMapping = mapping.split(',');
306 if (splitMapping.length >= 2) {
307 mappings[path.absolute(splitMapping[0])] = splitMapping[1];
308 }
309 }
310 return mappings;
280 } 311 }
281 } 312 }
282 313
283 /// A unit of Dart code that can be built into a single JavaScript module. 314 /// A unit of Dart code that can be built into a single JavaScript module.
284 class BuildUnit { 315 class BuildUnit {
285 /// The name of this module. 316 /// The name of this module.
286 final String name; 317 final String name;
287 318
288 /// All library names are relative to this path/prefix. 319 /// All library names are relative to this path/prefix.
289 final String libraryRoot; 320 final String libraryRoot;
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after
336 /// True if this library was successfully compiled. 367 /// True if this library was successfully compiled.
337 bool get isValid => moduleTree != null; 368 bool get isValid => moduleTree != null;
338 369
339 /// Gets the source code and source map for this JS module, given the 370 /// Gets the source code and source map for this JS module, given the
340 /// locations where the JS file and map file will be served from. 371 /// locations where the JS file and map file will be served from.
341 /// 372 ///
342 /// Relative URLs will be used to point from the .js file to the .map file 373 /// Relative URLs will be used to point from the .js file to the .map file
343 // 374 //
344 // TODO(jmesserly): this should match our old logic, but I'm not sure we are 375 // TODO(jmesserly): this should match our old logic, but I'm not sure we are
345 // correctly handling the pointer from the .js file to the .map file. 376 // correctly handling the pointer from the .js file to the .map file.
346 JSModuleCode getCode(ModuleFormat format, String jsUrl, String mapUrl) { 377 JSModuleCode getCode(
378 ModuleFormat format, bool singleOutFile, String jsUrl, String mapUrl) {
347 var opts = new JS.JavaScriptPrintingOptions( 379 var opts = new JS.JavaScriptPrintingOptions(
348 emitTypes: options.closure, 380 emitTypes: options.closure,
349 allowKeywordsInProperties: true, 381 allowKeywordsInProperties: true,
350 allowSingleLineIfStatements: true); 382 allowSingleLineIfStatements: true);
351 JS.SimpleJavaScriptPrintingContext printer; 383 JS.SimpleJavaScriptPrintingContext printer;
352 SourceMapBuilder sourceMap; 384 SourceMapBuilder sourceMap;
353 if (options.sourceMap) { 385 if (options.sourceMap) {
354 var sourceMapContext = new SourceMapPrintingContext(); 386 var sourceMapContext = new SourceMapPrintingContext();
355 sourceMap = sourceMapContext.sourceMap; 387 sourceMap = sourceMapContext.sourceMap;
356 printer = sourceMapContext; 388 printer = sourceMapContext;
357 } else { 389 } else {
358 printer = new JS.SimpleJavaScriptPrintingContext(); 390 printer = new JS.SimpleJavaScriptPrintingContext();
359 } 391 }
360 392
361 var tree = transformModuleFormat(format, moduleTree); 393 var tree = transformModuleFormat(format, singleOutFile, moduleTree);
362 tree.accept( 394 tree.accept(
363 new JS.Printer(opts, printer, localNamer: new JS.TemporaryNamer(tree))); 395 new JS.Printer(opts, printer, localNamer: new JS.TemporaryNamer(tree)));
364 396
365 if (options.sourceMap && options.sourceMapComment) { 397 Map builtMap;
366 var relativeMapUrl = path 398 if (options.sourceMap && sourceMap != null) {
367 .toUri(path.relative(path.fromUri(mapUrl), from: path.dirname(jsUrl))) 399 builtMap =
368 .toString(); 400 placeSourceMap(sourceMap.build(jsUrl), mapUrl, options.bazelMapping);
369 assert(path.dirname(jsUrl) == path.dirname(mapUrl)); 401
370 printer.emit('\n//# sourceMappingURL=$relativeMapUrl\n'); 402 if (options.sourceMapComment) {
403 var relativeMapUrl = path
404 .toUri(
405 path.relative(path.fromUri(mapUrl), from: path.dirname(jsUrl)))
406 .toString();
407 assert(path.dirname(jsUrl) == path.dirname(mapUrl));
408 printer.emit('\n//# sourceMappingURL=');
409 if (options.inlineSourceMap) {
410 var bytes = UTF8.encode(JSON.encode(builtMap));
411 var base64 = BASE64.encode(bytes);
412 printer..emit('data:application/json;base64,')..emit(base64);
413 } else {
414 printer.emit(relativeMapUrl);
415 }
416 printer.emit('\n');
417 }
371 } 418 }
372 419
373 Map builtMap;
374 if (sourceMap != null) {
375 builtMap = placeSourceMap(sourceMap.build(jsUrl), mapUrl);
376 }
377 return new JSModuleCode(printer.getText(), builtMap); 420 return new JSModuleCode(printer.getText(), builtMap);
378 } 421 }
379 422
380 /// Similar to [getCode] but immediately writes the resulting files. 423 /// Similar to [getCode] but immediately writes the resulting files.
381 /// 424 ///
382 /// If [mapPath] is not supplied but [options.sourceMap] is set, mapPath 425 /// If [mapPath] is not supplied but [options.sourceMap] is set, mapPath
383 /// will default to [jsPath].map. 426 /// will default to [jsPath].map.
384 void writeCodeSync(ModuleFormat format, String jsPath, [String mapPath]) { 427 void writeCodeSync(ModuleFormat format, bool singleOutFile, String jsPath) {
385 if (mapPath == null) mapPath = jsPath + '.map'; 428 String mapPath = jsPath + '.map';
386 var code = getCode(format, jsPath, mapPath); 429 var code = getCode(format, singleOutFile, jsPath, mapPath);
387 new File(jsPath).writeAsStringSync(code.code); 430 var c = code.code;
388 if (code.sourceMap != null) { 431 if (singleOutFile) {
432 // In singleOutFile mode we wrap each module in an eval statement to
433 // leverage sourceURL to improve the debugging experience when source maps
434 // are not enabled.
435 c += '\n//# sourceURL=${name}.js\n';
436 c = 'eval(${JSON.encode(c)});\n';
437 }
438 new File(jsPath).writeAsStringSync(c);
439 if (code.sourceMap != null && !options.inlineSourceMap) {
389 new File(mapPath).writeAsStringSync(JSON.encode(code.sourceMap)); 440 new File(mapPath).writeAsStringSync(JSON.encode(code.sourceMap));
390 } 441 }
391 } 442 }
392 } 443 }
393 444
394 /// The output of compiling a JavaScript module in a particular format. 445 /// The output of compiling a JavaScript module in a particular format.
395 class JSModuleCode { 446 class JSModuleCode {
396 /// The JavaScript code for this module. 447 /// The JavaScript code for this module.
397 /// 448 ///
398 /// If a [sourceMap] is available, this will include the `sourceMappingURL` 449 /// If a [sourceMap] is available, this will include the `sourceMappingURL`
399 /// comment at end of the file. 450 /// comment at end of the file.
400 final String code; 451 final String code;
401 452
402 /// The JSON of the source map, if generated, otherwise `null`. 453 /// The JSON of the source map, if generated, otherwise `null`.
403 /// 454 ///
404 /// The source paths will initially be absolute paths. They can be adjusted 455 /// The source paths will initially be absolute paths. They can be adjusted
405 /// using [placeSourceMap]. 456 /// using [placeSourceMap].
406 final Map sourceMap; 457 final Map sourceMap;
407 458
408 JSModuleCode(this.code, this.sourceMap); 459 JSModuleCode(this.code, this.sourceMap);
409 } 460 }
410 461
411 /// Adjusts the source paths in [sourceMap] to be relative to [sourceMapPath], 462 /// Adjusts the source paths in [sourceMap] to be relative to [sourceMapPath],
412 /// and returns the new map. 463 /// and returns the new map.
413 // TODO(jmesserly): find a new home for this. 464 // TODO(jmesserly): find a new home for this.
414 Map placeSourceMap(Map sourceMap, String sourceMapPath) { 465 Map placeSourceMap(
466 Map sourceMap, String sourceMapPath, Map<String, String> bazelMappings) {
415 var dir = path.dirname(sourceMapPath); 467 var dir = path.dirname(sourceMapPath);
468 var map = new Map.from(sourceMap);
469 var list = new List.from(map['sources']);
470 map['sources'] = list;
471 String transformUri(String uri) {
472 var match = bazelMappings[path.absolute(uri)];
473 if (match != null) return match;
416 474
417 var map = new Map.from(sourceMap); 475 // Fall back to a relative path.
418 List list = new List.from(map['sources']); 476 return path.toUri(path.relative(path.fromUri(uri), from: dir)).toString();
419 map['sources'] = list; 477 }
420 String relative(String uri) =>
421 path.toUri(path.relative(path.fromUri(uri), from: dir)).toString();
422 for (int i = 0; i < list.length; i++) { 478 for (int i = 0; i < list.length; i++) {
423 list[i] = relative(list[i]); 479 list[i] = transformUri(list[i]);
424 } 480 }
425 map['file'] = relative(map['file']); 481 map['file'] = transformUri(map['file']);
426 return map; 482 return map;
427 } 483 }
OLDNEW
« no previous file with comments | « pkg/dev_compiler/lib/src/compiler/command.dart ('k') | pkg/dev_compiler/lib/src/compiler/module_builder.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698