| OLD | NEW | 
|---|
| (Empty) |  | 
|  | 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 | 
|  | 3 // BSD-style license that can be found in the LICENSE file. | 
|  | 4 | 
|  | 5 import 'package:args/args.dart' show ArgParser, ArgResults; | 
|  | 6 import 'package:analyzer/analyzer.dart' | 
|  | 7     show AnalysisError, CompilationUnit, ErrorSeverity; | 
|  | 8 import 'package:analyzer/src/generated/engine.dart' show AnalysisContext; | 
|  | 9 import 'package:analyzer/src/generated/java_engine.dart' show AnalysisException; | 
|  | 10 import 'package:analyzer/src/generated/source_io.dart' show Source, SourceKind; | 
|  | 11 import 'package:func/func.dart' show Func1; | 
|  | 12 import 'package:path/path.dart' as path; | 
|  | 13 | 
|  | 14 import '../analyzer/context.dart' | 
|  | 15     show AnalyzerOptions, createAnalysisContextWithSources; | 
|  | 16 import 'extension_types.dart' show ExtensionTypeSet; | 
|  | 17 import 'code_generator.dart' show CodeGenerator; | 
|  | 18 import 'error_helpers.dart' show errorSeverity, formatError, sortErrors; | 
|  | 19 | 
|  | 20 /// Compiles a set of Dart files into a single JavaScript module. | 
|  | 21 /// | 
|  | 22 /// For a single [BuildUnit] definition, this will produce a [JSModuleFile]. | 
|  | 23 /// Those objects are record types that record the data consumed and produced | 
|  | 24 /// for a single compile. | 
|  | 25 /// | 
|  | 26 /// This class exists to cache global state associated with a single in-memory | 
|  | 27 /// AnalysisContext, such as information about extension types in the Dart SDK. | 
|  | 28 /// It can be used once to produce a single module, or reused to save warm-up | 
|  | 29 /// time. (Currently there is no warm up, but there may be in the future.) | 
|  | 30 /// | 
|  | 31 /// The SDK source code is assumed to be immutable for the life of this class. | 
|  | 32 /// | 
|  | 33 /// For all other files, it is up to the [AnalysisContext] to decide whether or | 
|  | 34 /// not any caching is performed. By default an analysis context will assume | 
|  | 35 /// sources are immutable for the life of the context, and cache information | 
|  | 36 /// about them. | 
|  | 37 class ModuleCompiler { | 
|  | 38   final AnalysisContext context; | 
|  | 39   final _extensionTypes = new ExtensionTypeSet(); | 
|  | 40 | 
|  | 41   ModuleCompiler.withContext(this.context); | 
|  | 42 | 
|  | 43   ModuleCompiler(AnalyzerOptions analyzerOptions) | 
|  | 44       : this.withContext(createAnalysisContextWithSources(analyzerOptions)); | 
|  | 45 | 
|  | 46   /// Compiles a single Dart build unit into a JavaScript module. | 
|  | 47   /// | 
|  | 48   /// *Warning* - this may require resolving the entire world. | 
|  | 49   /// If that is not desired, the analysis context must be pre-configured using | 
|  | 50   /// summaries before calling this method. | 
|  | 51   JSModuleFile compile(BuildUnit unit, CompilerOptions options) { | 
|  | 52     var trees = <CompilationUnit>[]; | 
|  | 53     var errors = <AnalysisError>[]; | 
|  | 54 | 
|  | 55     for (var sourcePath in unit.sources) { | 
|  | 56       String sourceUri = sourcePath; | 
|  | 57       if (path.isRelative(sourcePath)) { | 
|  | 58         sourceUri = path.absolute(sourceUri); | 
|  | 59       } | 
|  | 60       sourceUri = path.toUri(sourceUri).toString(); | 
|  | 61       Source source = context.sourceFactory.forUri(sourceUri); | 
|  | 62       if (source == null) { | 
|  | 63         throw new AnalysisException('could not create a source for $sourcePath.' | 
|  | 64             ' The file name is in the wrong format or was not found.'); | 
|  | 65       } | 
|  | 66 | 
|  | 67       // Ignore parts. They need to be handled in the context of their library. | 
|  | 68       if (context.getKindOf(source) == SourceKind.PART) { | 
|  | 69         continue; | 
|  | 70       } | 
|  | 71 | 
|  | 72       var resolvedTree = context.resolveCompilationUnit2(source, source); | 
|  | 73       trees.add(resolvedTree); | 
|  | 74       errors.addAll(context.computeErrors(source)); | 
|  | 75 | 
|  | 76       var library = resolvedTree.element.library; | 
|  | 77       for (var part in library.parts) { | 
|  | 78         trees.add(context.resolveCompilationUnit(part.source, library)); | 
|  | 79         errors.addAll(context.computeErrors(part.source)); | 
|  | 80       } | 
|  | 81     } | 
|  | 82 | 
|  | 83     sortErrors(context, errors); | 
|  | 84     var messages = <String>[]; | 
|  | 85     for (var e in errors) { | 
|  | 86       var m = formatError(context, e); | 
|  | 87       if (m != null) messages.add(m); | 
|  | 88     } | 
|  | 89 | 
|  | 90     if (!options.unsafeForceCompile && | 
|  | 91         errors.any((e) => errorSeverity(context, e) == ErrorSeverity.ERROR)) { | 
|  | 92       return new JSModuleFile.invalid(unit.name, messages); | 
|  | 93     } | 
|  | 94 | 
|  | 95     var codeGenerator = new CodeGenerator(context, options, _extensionTypes); | 
|  | 96     return codeGenerator.compile(unit, trees, messages); | 
|  | 97   } | 
|  | 98 } | 
|  | 99 | 
|  | 100 enum ModuleFormat { es6, legacy, node } | 
|  | 101 | 
|  | 102 ModuleFormat parseModuleFormat(String s) => { | 
|  | 103       'es6': ModuleFormat.es6, | 
|  | 104       'node': ModuleFormat.node, | 
|  | 105       'legacy': ModuleFormat.legacy | 
|  | 106     }[s]; | 
|  | 107 | 
|  | 108 class CompilerOptions { | 
|  | 109   /// Whether to emit the source mapping file. | 
|  | 110   /// | 
|  | 111   /// This supports debugging the original source code instead of the generated | 
|  | 112   /// code. | 
|  | 113   final bool sourceMap; | 
|  | 114 | 
|  | 115   /// If [sourceMap] is emitted, this will emit a `sourceMappingUrl` comment | 
|  | 116   /// into the output JavaScript module. | 
|  | 117   final bool sourceMapComment; | 
|  | 118 | 
|  | 119   /// Whether to emit a summary file containing API signatures. | 
|  | 120   /// | 
|  | 121   /// This is required for a modular build process. | 
|  | 122   final bool summarizeApi; | 
|  | 123 | 
|  | 124   /// Whether to force compilation of code with static errors. | 
|  | 125   final bool unsafeForceCompile; | 
|  | 126 | 
|  | 127   /// Whether to emit Closure Compiler-friendly code. | 
|  | 128   final bool closure; | 
|  | 129 | 
|  | 130   /// Enable ES6 destructuring of named parameters. Off by default. | 
|  | 131   /// | 
|  | 132   /// Older V8 versions do not accept default values with destructuring in | 
|  | 133   /// arrow functions yet (e.g. `({a} = {}) => 1`) but happily accepts them | 
|  | 134   /// with regular functions (e.g. `function({a} = {}) { return 1 }`). | 
|  | 135   /// | 
|  | 136   /// Supporting the syntax: | 
|  | 137   /// * Chrome Canary (51) | 
|  | 138   /// * Firefox | 
|  | 139   /// | 
|  | 140   /// Not yet supporting: | 
|  | 141   /// * Atom (1.5.4) | 
|  | 142   /// * Electron (0.36.3) | 
|  | 143   // TODO(ochafik): Simplify this code when our target platforms catch up. | 
|  | 144   final bool destructureNamedParams; | 
|  | 145 | 
|  | 146   /// Which module format to support. | 
|  | 147   /// Currently 'es6' and 'legacy' are supported. | 
|  | 148   final ModuleFormat moduleFormat; | 
|  | 149 | 
|  | 150   const CompilerOptions( | 
|  | 151       {this.sourceMap: true, | 
|  | 152       this.sourceMapComment: true, | 
|  | 153       this.summarizeApi: true, | 
|  | 154       this.unsafeForceCompile: false, | 
|  | 155       this.closure: false, | 
|  | 156       this.destructureNamedParams: false, | 
|  | 157       this.moduleFormat: ModuleFormat.legacy}); | 
|  | 158 | 
|  | 159   CompilerOptions.fromArguments(ArgResults args) | 
|  | 160       : sourceMap = args['source-map'], | 
|  | 161         sourceMapComment = args['source-map-comment'], | 
|  | 162         summarizeApi = args['summarize'], | 
|  | 163         unsafeForceCompile = args['unsafe-force-compile'], | 
|  | 164         closure = args['closure-experimental'], | 
|  | 165         destructureNamedParams = args['destructure-named-params'], | 
|  | 166         moduleFormat = parseModuleFormat(args['modules']); | 
|  | 167 | 
|  | 168   static ArgParser addArguments(ArgParser parser) => parser | 
|  | 169     ..addFlag('summarize', help: 'emit an API summary file', defaultsTo: true) | 
|  | 170     ..addFlag('source-map', help: 'emit source mapping', defaultsTo: true) | 
|  | 171     ..addFlag('source-map-comment', | 
|  | 172         help: 'adds a sourceMappingURL comment to the end of the JS,\n' | 
|  | 173             'disable if using X-SourceMap header', | 
|  | 174         defaultsTo: true) | 
|  | 175     ..addOption('modules', | 
|  | 176         help: 'module pattern to emit', | 
|  | 177         allowed: ['es6', 'legacy', 'node'], | 
|  | 178         allowedHelp: { | 
|  | 179           'es6': 'es6 modules', | 
|  | 180           'legacy': 'a custom format used by dartdevc, similar to AMD', | 
|  | 181           'node': 'node.js modules (https://nodejs.org/api/modules.html)' | 
|  | 182         }, | 
|  | 183         defaultsTo: 'legacy') | 
|  | 184     ..addFlag('closure-experimental', | 
|  | 185         help: 'emit Closure Compiler-friendly code (experimental)', | 
|  | 186         defaultsTo: false) | 
|  | 187     ..addFlag('destructure-named-params', | 
|  | 188         help: 'Destructure named parameters', defaultsTo: false) | 
|  | 189     ..addFlag('unsafe-force-compile', | 
|  | 190         help: 'Compile code even if it has errors. ಠ_ಠ\n' | 
|  | 191             'This has undefined behavior!', | 
|  | 192         defaultsTo: false); | 
|  | 193 } | 
|  | 194 | 
|  | 195 /// A unit of Dart code that can be built into a single JavaScript module. | 
|  | 196 class BuildUnit { | 
|  | 197   /// The name of this module. | 
|  | 198   final String name; | 
|  | 199 | 
|  | 200   /// The list of sources in this module. | 
|  | 201   /// | 
|  | 202   /// The set of Dart files can be arbitrarily large, but it must contain | 
|  | 203   /// complete libraries including all of their parts, as well as all libraries | 
|  | 204   /// that are part of a library cycle. | 
|  | 205   final List<String> sources; | 
|  | 206 | 
|  | 207   /// Given an imported library URI, this will determine to what Dart/JS module | 
|  | 208   /// it belongs to. | 
|  | 209   // TODO(jmesserly): we should replace this with another way of tracking | 
|  | 210   // build units. | 
|  | 211   final Func1<Source, String> libraryToModule; | 
|  | 212 | 
|  | 213   BuildUnit(this.name, this.sources, this.libraryToModule); | 
|  | 214 } | 
|  | 215 | 
|  | 216 /// The output of Dart->JS compilation. | 
|  | 217 /// | 
|  | 218 /// This contains the file contents of the JS module, as well as a list of | 
|  | 219 /// Dart libraries that are contained in this module. | 
|  | 220 class JSModuleFile { | 
|  | 221   /// The name of this module. | 
|  | 222   final String name; | 
|  | 223 | 
|  | 224   /// The list of messages (errors and warnings) | 
|  | 225   final List<String> errors; | 
|  | 226 | 
|  | 227   /// The JavaScript code for this module. | 
|  | 228   /// | 
|  | 229   /// If a [sourceMap] is available, this will include the `sourceMappingURL` | 
|  | 230   /// comment at end of the file. | 
|  | 231   final String code; | 
|  | 232 | 
|  | 233   /// The JSON of the source map, if generated, otherwise `null`. | 
|  | 234   /// | 
|  | 235   /// The source paths will initially be absolute paths. They can be adjusted | 
|  | 236   /// using [placeSourceMap]. | 
|  | 237   final Map sourceMap; | 
|  | 238 | 
|  | 239   /// The binary contents of the API summary file, including APIs from each of | 
|  | 240   /// the [libraries] in this module. | 
|  | 241   final List<int> summaryBytes; | 
|  | 242 | 
|  | 243   JSModuleFile( | 
|  | 244       this.name, this.errors, this.code, this.sourceMap, this.summaryBytes); | 
|  | 245 | 
|  | 246   JSModuleFile.invalid(this.name, this.errors) | 
|  | 247       : code = null, | 
|  | 248         sourceMap = null, | 
|  | 249         summaryBytes = null; | 
|  | 250 | 
|  | 251   /// True if this library was successfully compiled. | 
|  | 252   bool get isValid => code != null; | 
|  | 253 | 
|  | 254   /// Adjusts the source paths in [sourceMap] to be relative to [sourceMapPath], | 
|  | 255   /// and returns the new map. | 
|  | 256   /// | 
|  | 257   /// See also [writeSourceMap]. | 
|  | 258   Map placeSourceMap(String sourceMapPath) { | 
|  | 259     var dir = path.dirname(sourceMapPath); | 
|  | 260 | 
|  | 261     var map = new Map.from(this.sourceMap); | 
|  | 262     List list = new List.from(map['sources']); | 
|  | 263     map['sources'] = list; | 
|  | 264     for (int i = 0; i < list.length; i++) { | 
|  | 265       list[i] = path.relative(list[i], from: dir); | 
|  | 266     } | 
|  | 267     return map; | 
|  | 268   } | 
|  | 269 } | 
| OLD | NEW | 
|---|