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] outp ut. | |
vsm
2016/04/14 00:18:24
nit length
Jennifer Messerly
2016/04/14 18:28:40
Done.
| |
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. | |
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.isAbsolute(sourcePath)) { | |
58 sourceUri = path.toUri(sourcePath).toString(); | |
59 } | |
60 Source source = context.sourceFactory.forUri(sourceUri); | |
61 if (source == null) { | |
62 throw new AnalysisException('could not create a source for $sourcePath.' | |
63 ' The file name is in the wrong format or was not found.'); | |
64 } | |
65 | |
66 // Ignore parts. They need to be handled in the context of their library. | |
67 if (context.getKindOf(source) == SourceKind.PART) { | |
68 continue; | |
69 } | |
70 | |
71 var resolvedTree = context.resolveCompilationUnit2(source, source); | |
72 trees.add(resolvedTree); | |
73 errors.addAll(context.computeErrors(source)); | |
74 | |
75 var library = resolvedTree.element.library; | |
76 for (var part in library.parts) { | |
77 trees.add(context.resolveCompilationUnit(part.source, library)); | |
78 errors.addAll(context.computeErrors(part.source)); | |
79 } | |
80 } | |
81 | |
82 sortErrors(context, errors); | |
83 var messages = <String>[]; | |
84 for (var e in errors) { | |
85 var m = formatError(context, e); | |
86 if (m != null) messages.add(m); | |
87 } | |
88 | |
89 if (!options.unsafeForceCompile && | |
90 errors.any((e) => errorSeverity(context, e) == ErrorSeverity.ERROR)) { | |
91 return new JSModuleFile.invalid(unit.name, messages); | |
92 } | |
93 | |
94 var codeGenerator = new CodeGenerator(context, options, _extensionTypes); | |
95 return codeGenerator.compile(unit, trees, messages); | |
96 } | |
97 } | |
98 | |
99 enum ModuleFormat { es6, legacy, node } | |
100 | |
101 ModuleFormat parseModuleFormat(String s) => { | |
102 'es6': ModuleFormat.es6, | |
103 'node': ModuleFormat.node, | |
104 'legacy': ModuleFormat.legacy | |
105 }[s]; | |
106 | |
107 class CompilerOptions { | |
108 /// Whether to emit the source mapping file. | |
109 /// | |
110 /// This supports debugging the original source code instead of the generated | |
111 /// code. | |
112 final bool sourceMap; | |
113 | |
114 /// If [sourceMap] is emitted, this will emit a `sourceMappingUrl` comment | |
115 /// into the output JavaScript module. | |
116 final bool sourceMapComment; | |
117 | |
118 /// Whether to emit a summary file containing API signatures. | |
119 /// | |
120 /// This is required for a modular build process. | |
121 final bool summarizeApi; | |
122 | |
123 /// Whether to force compilation of code with static errors. | |
124 final bool unsafeForceCompile; | |
125 | |
126 /// Whether to emit Closure Compiler-friendly code. | |
127 final bool closure; | |
128 | |
129 /// Enable ES6 destructuring of named parameters. Off by default. | |
130 /// | |
131 /// Older V8 versions do not accept default values with destructuring in | |
132 /// arrow functions yet (e.g. `({a} = {}) => 1`) but happily accepts them | |
133 /// with regular functions (e.g. `function({a} = {}) { return 1 }`). | |
134 /// | |
135 /// Supporting the syntax: | |
136 /// * Chrome Canary (51) | |
137 /// * Firefox | |
138 /// | |
139 /// Not yet supporting: | |
140 /// * Atom (1.5.4) | |
141 /// * Electron (0.36.3) | |
142 // TODO(ochafik): Simplify this code when our target platforms catch up. | |
143 final bool destructureNamedParams; | |
144 | |
145 /// Which module format to support. | |
146 /// Currently 'es6' and 'legacy' are supported. | |
147 final ModuleFormat moduleFormat; | |
148 | |
149 const CompilerOptions( | |
150 {this.sourceMap: true, | |
151 this.sourceMapComment: true, | |
152 this.summarizeApi: true, | |
153 this.unsafeForceCompile: false, | |
154 this.closure: false, | |
155 this.destructureNamedParams: false, | |
156 this.moduleFormat: ModuleFormat.legacy}); | |
157 | |
158 CompilerOptions.fromArguments(ArgResults args) | |
159 : sourceMap = args['source-map'], | |
160 sourceMapComment = args['source-map-comment'], | |
161 summarizeApi = args['summarize'], | |
162 unsafeForceCompile = args['unsafe-force-compile'], | |
163 closure = args['closure-experimental'], | |
164 destructureNamedParams = args['destructure-named-params'], | |
165 moduleFormat = parseModuleFormat(args['modules']); | |
166 | |
167 static ArgParser addArguments(ArgParser parser) => parser | |
168 ..addFlag('summarize', help: 'emit an API summary file', defaultsTo: true) | |
169 ..addFlag('source-map', help: 'emit source mapping', defaultsTo: true) | |
170 ..addFlag('source-map-comment', | |
171 help: 'adds a sourceMappingURL comment to the end of the JS,\n' | |
172 'disable if using X-SourceMap header', | |
173 defaultsTo: true) | |
174 ..addOption('modules', | |
175 help: 'module pattern to emit', | |
176 allowed: ['es6', 'legacy', 'node'], | |
177 allowedHelp: { | |
178 'es6': 'es6 modules', | |
179 'legacy': 'a custom format used by dartdevc, similar to AMD', | |
180 'node': 'node.js modules (https://nodejs.org/api/modules.html)' | |
181 }, | |
182 defaultsTo: 'legacy') | |
183 ..addFlag('closure-experimental', | |
184 help: 'emit Closure Compiler-friendly code (experimental)', | |
185 defaultsTo: false) | |
186 ..addFlag('destructure-named-params', | |
187 help: 'Destructure named parameters', defaultsTo: false) | |
188 ..addFlag('unsafe-force-compile', | |
189 help: 'Compile code even if it has errors. ಠ_ಠ\n' | |
190 'This has undefined behavior!', | |
191 defaultsTo: false); | |
192 } | |
193 | |
194 /// A unit of Dart code that can be built into a single JavaScript module. | |
195 class BuildUnit { | |
196 /// The name of this module. | |
197 final String name; | |
198 | |
199 /// The list of sources in this module. | |
200 /// | |
201 /// The set of Dart files can be arbitrarily large, but it must contain | |
202 /// complete libraries including all of their parts, as well as all libraries | |
203 /// that are part of a library cycle. | |
204 final List<String> sources; | |
205 | |
206 /// Given an imported library URI, this will determine to what Dart/JS module | |
207 /// it belongs to. | |
208 // TODO(jmesserly): we should replace this with another way of tracking | |
209 // build units. | |
210 final Func1<Source, String> libraryToModule; | |
211 | |
212 BuildUnit(this.name, this.sources, this.libraryToModule); | |
213 } | |
214 | |
215 /// The output of Dart->JS compilation. | |
216 /// | |
217 /// This contains the file contents of the JS module, as well as a list of | |
218 /// Dart libraries that are contained in this module. | |
219 class JSModuleFile { | |
220 /// The name of this module. | |
221 final String name; | |
222 | |
223 /// The list of messages (errors and warnings) | |
224 final List<String> errors; | |
225 | |
226 /// The JavaScript code for this module. | |
227 /// | |
228 /// If a [sourceMap] is available, this will include the `sourceMappingURL` | |
229 /// comment at end of the file. | |
230 final String code; | |
231 | |
232 /// The JSON of the source map, if generated, otherwise `null`. | |
233 /// | |
234 /// The source paths will initially be absolute paths. They can be adjusted | |
235 /// using [placeSourceMap]. | |
236 final Map sourceMap; | |
237 | |
238 /// The binary contents of the API summary file, including APIs from each of | |
239 /// the [libraries] in this module. | |
240 final List<int> summaryBytes; | |
241 | |
242 JSModuleFile( | |
243 this.name, this.errors, this.code, this.sourceMap, this.summaryBytes); | |
244 | |
245 JSModuleFile.invalid(this.name, this.errors) | |
246 : code = null, | |
247 sourceMap = null, | |
248 summaryBytes = null; | |
249 | |
250 /// True if this library was successfully compiled. | |
251 bool get isValid => code != null; | |
252 | |
253 /// Adjusts the source paths in [sourceMap] to be relative to [sourceMapPath], | |
254 /// and returns the new map. | |
255 /// | |
256 /// See also [writeSourceMap]. | |
257 Map placeSourceMap(String sourceMapPath) { | |
258 var dir = path.dirname(sourceMapPath); | |
259 | |
260 var map = new Map.from(this.sourceMap); | |
261 List list = new List.from(map['sources']); | |
262 map['sources'] = list; | |
263 for (int i = 0; i < list.length; i++) { | |
264 list[i] = path.relative(list[i], from: dir); | |
265 } | |
266 return map; | |
267 } | |
268 } | |
OLD | NEW |