| OLD | NEW |
| 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 Loading... |
| 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 Loading... |
| 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 Loading... |
| 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 } |
| OLD | NEW |