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

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: Support generating inlined source maps and wrapping module contents within a JavaScript eval block … 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 | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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
167 /// Wrap each module in an JavaScript eval call.
168 /// This makes debugging concatenated sources more productive due to the eval
169 /// script sourceURL directive.
170 final bool wrapInEval;
171
164 /// Whether to emit a summary file containing API signatures. 172 /// Whether to emit a summary file containing API signatures.
165 /// 173 ///
166 /// This is required for a modular build process. 174 /// This is required for a modular build process.
167 final bool summarizeApi; 175 final bool summarizeApi;
168 176
169 /// The file extension for summaries. 177 /// The file extension for summaries.
170 final String summaryExtension; 178 final String summaryExtension;
171 179
172 /// Whether to preserve metdata only accessible via mirrors 180 /// Whether to preserve metdata only accessible via mirrors
173 final bool emitMetadata; 181 final bool emitMetadata;
(...skipping 27 matching lines...) Expand all
201 /// Supporting the syntax: 209 /// Supporting the syntax:
202 /// * Chrome Canary (51) 210 /// * Chrome Canary (51)
203 /// * Firefox 211 /// * Firefox
204 /// 212 ///
205 /// Not yet supporting: 213 /// Not yet supporting:
206 /// * Atom (1.5.4) 214 /// * Atom (1.5.4)
207 /// * Electron (0.36.3) 215 /// * Electron (0.36.3)
208 // TODO(ochafik): Simplify this code when our target platforms catch up. 216 // TODO(ochafik): Simplify this code when our target platforms catch up.
209 final bool destructureNamedParams; 217 final bool destructureNamedParams;
210 218
219 /// Mapping from absolte file path to paths to use in source maps.
220 final Map<String, String> fileMappings;
221
211 const CompilerOptions( 222 const CompilerOptions(
212 {this.sourceMap: true, 223 {this.sourceMap: true,
213 this.sourceMapComment: true, 224 this.sourceMapComment: true,
225 this.inlineSourceMap: false,
226 this.wrapInEval: false,
214 this.summarizeApi: true, 227 this.summarizeApi: true,
215 this.summaryExtension: 'sum', 228 this.summaryExtension: 'sum',
216 this.unsafeForceCompile: false, 229 this.unsafeForceCompile: false,
217 this.emitMetadata: false, 230 this.emitMetadata: false,
218 this.closure: false, 231 this.closure: false,
219 this.destructureNamedParams: false, 232 this.destructureNamedParams: false,
220 this.hoistInstanceCreation: true, 233 this.hoistInstanceCreation: true,
221 this.hoistSignatureTypes: false, 234 this.hoistSignatureTypes: false,
222 this.nameTypeTests: true, 235 this.nameTypeTests: true,
223 this.hoistTypeTests: true, 236 this.hoistTypeTests: true,
224 this.useAngular2Whitelist: false}); 237 this.useAngular2Whitelist: false,
238 this.fileMappings: const {}});
225 239
226 CompilerOptions.fromArguments(ArgResults args) 240 CompilerOptions.fromArguments(ArgResults args)
227 : sourceMap = args['source-map'], 241 : sourceMap = args['source-map'],
228 sourceMapComment = args['source-map-comment'], 242 sourceMapComment = args['source-map-comment'],
243 inlineSourceMap = args['inline-source-map'],
244 wrapInEval = args['wrap-in-eval'],
229 summarizeApi = args['summarize'], 245 summarizeApi = args['summarize'],
230 summaryExtension = args['summary-extension'], 246 summaryExtension = args['summary-extension'],
231 unsafeForceCompile = args['unsafe-force-compile'], 247 unsafeForceCompile = args['unsafe-force-compile'],
232 emitMetadata = args['emit-metadata'], 248 emitMetadata = args['emit-metadata'],
233 closure = args['closure-experimental'], 249 closure = args['closure-experimental'],
234 destructureNamedParams = args['destructure-named-params'], 250 destructureNamedParams = args['destructure-named-params'],
235 hoistInstanceCreation = args['hoist-instance-creation'], 251 hoistInstanceCreation = args['hoist-instance-creation'],
236 hoistSignatureTypes = args['hoist-signature-types'], 252 hoistSignatureTypes = args['hoist-signature-types'],
237 nameTypeTests = args['name-type-tests'], 253 nameTypeTests = args['name-type-tests'],
238 hoistTypeTests = args['hoist-type-tests'], 254 hoistTypeTests = args['hoist-type-tests'],
239 useAngular2Whitelist = args['unsafe-angular2-whitelist']; 255 useAngular2Whitelist = args['unsafe-angular2-whitelist'],
256 fileMappings = _parseFileMappings(args['file-mapping']);
240 257
241 static void addArguments(ArgParser parser) { 258 static void addArguments(ArgParser parser) {
242 parser 259 parser
243 ..addFlag('summarize', help: 'emit an API summary file', defaultsTo: true) 260 ..addFlag('summarize', help: 'emit an API summary file', defaultsTo: true)
244 ..addOption('summary-extension', 261 ..addOption('summary-extension',
245 help: 'file extension for Dart summary files', 262 help: 'file extension for Dart summary files',
246 defaultsTo: 'sum', 263 defaultsTo: 'sum',
247 hide: true) 264 hide: true)
248 ..addFlag('source-map', help: 'emit source mapping', defaultsTo: true) 265 ..addFlag('source-map', help: 'emit source mapping', defaultsTo: true)
249 ..addFlag('source-map-comment', 266 ..addFlag('source-map-comment',
250 help: 'adds a sourceMappingURL comment to the end of the JS,\n' 267 help: 'adds a sourceMappingURL comment to the end of the JS,\n'
251 'disable if using X-SourceMap header', 268 'disable if using X-SourceMap header',
252 defaultsTo: true, 269 defaultsTo: true,
253 hide: true) 270 hide: true)
271 ..addFlag('inline-source-map',
272 help: 'emit source mapping inline', defaultsTo: false)
273 ..addFlag('wrap-in-eval',
274 help: 'wrap library definition in eval', defaultsTo: false)
254 ..addFlag('emit-metadata', 275 ..addFlag('emit-metadata',
255 help: 'emit metadata annotations queriable via mirrors', 276 help: 'emit metadata annotations queriable via mirrors',
256 defaultsTo: false) 277 defaultsTo: false)
257 ..addFlag('closure-experimental', 278 ..addFlag('closure-experimental',
258 help: 'emit Closure Compiler-friendly code (experimental)', 279 help: 'emit Closure Compiler-friendly code (experimental)',
259 defaultsTo: false) 280 defaultsTo: false)
260 ..addFlag('destructure-named-params', 281 ..addFlag('destructure-named-params',
261 help: 'Destructure named parameters', defaultsTo: false, hide: true) 282 help: 'Destructure named parameters', defaultsTo: false, hide: true)
262 ..addFlag('unsafe-force-compile', 283 ..addFlag('unsafe-force-compile',
263 help: 'Compile code even if it has errors. ಠ_ಠ\n' 284 help: 'Compile code even if it has errors. ಠ_ಠ\n'
264 'This has undefined behavior!', 285 'This has undefined behavior!',
265 defaultsTo: false, 286 defaultsTo: false,
266 hide: true) 287 hide: true)
267 ..addFlag('hoist-instance-creation', 288 ..addFlag('hoist-instance-creation',
268 help: 'Hoist the class type from generic instance creations', 289 help: 'Hoist the class type from generic instance creations',
269 defaultsTo: true, 290 defaultsTo: true,
270 hide: true) 291 hide: true)
271 ..addFlag('hoist-signature-types', 292 ..addFlag('hoist-signature-types',
272 help: 'Hoist types from class signatures', 293 help: 'Hoist types from class signatures',
273 defaultsTo: false, 294 defaultsTo: false,
274 hide: true) 295 hide: true)
275 ..addFlag('name-type-tests', 296 ..addFlag('name-type-tests',
276 help: 'Name types used in type tests', defaultsTo: true, hide: true) 297 help: 'Name types used in type tests', defaultsTo: true, hide: true)
277 ..addFlag('hoist-type-tests', 298 ..addFlag('hoist-type-tests',
278 help: 'Hoist types used in type tests', defaultsTo: true, hide: true) 299 help: 'Hoist types used in type tests', defaultsTo: true, hide: true)
279 ..addFlag('unsafe-angular2-whitelist', defaultsTo: false, hide: true); 300 ..addFlag('unsafe-angular2-whitelist', defaultsTo: false, hide: true)
301 ..addOption('file-mapping',
vsm 2016/09/13 22:08:20 Can you include an example of how this is used (e.
vsm 2016/09/13 22:17:32 This might make more sense as --bazel-mapping. Th
Jennifer Messerly 2016/09/13 22:43:39 yeah ... if some of these options are meant to go
302 help:
303 '--file-mapping=/full/path/to/library.dart,to/library.dart uses\n'
304 'to/library.dart as the short_path for library.dart".',
305 allowMultiple: true,
306 splitCommas: false);
307 }
308
309 static Map<String, String> _parseFileMappings(Iterable argument) {
310 var mappings = <String, String>{};
311 for (var mapping in argument) {
312 var splitMapping = mapping.split(',');
313 if (splitMapping.length >= 2) {
314 mappings[path.absolute(splitMapping[0])] = splitMapping[1];
315 }
316 }
317 return mappings;
280 } 318 }
281 } 319 }
282 320
283 /// A unit of Dart code that can be built into a single JavaScript module. 321 /// A unit of Dart code that can be built into a single JavaScript module.
284 class BuildUnit { 322 class BuildUnit {
285 /// The name of this module. 323 /// The name of this module.
286 final String name; 324 final String name;
287 325
288 /// All library names are relative to this path/prefix. 326 /// All library names are relative to this path/prefix.
289 final String libraryRoot; 327 final String libraryRoot;
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after
355 sourceMap = sourceMapContext.sourceMap; 393 sourceMap = sourceMapContext.sourceMap;
356 printer = sourceMapContext; 394 printer = sourceMapContext;
357 } else { 395 } else {
358 printer = new JS.SimpleJavaScriptPrintingContext(); 396 printer = new JS.SimpleJavaScriptPrintingContext();
359 } 397 }
360 398
361 var tree = transformModuleFormat(format, moduleTree); 399 var tree = transformModuleFormat(format, moduleTree);
362 tree.accept( 400 tree.accept(
363 new JS.Printer(opts, printer, localNamer: new JS.TemporaryNamer(tree))); 401 new JS.Printer(opts, printer, localNamer: new JS.TemporaryNamer(tree)));
364 402
365 if (options.sourceMap && options.sourceMapComment) { 403 Map builtMap;
366 var relativeMapUrl = path 404 if (options.sourceMap && sourceMap != null) {
367 .toUri(path.relative(path.fromUri(mapUrl), from: path.dirname(jsUrl))) 405 builtMap =
368 .toString(); 406 placeSourceMap(sourceMap.build(jsUrl), mapUrl, options.fileMappings);
369 assert(path.dirname(jsUrl) == path.dirname(mapUrl)); 407
370 printer.emit('\n//# sourceMappingURL=$relativeMapUrl\n'); 408 if (options.sourceMapComment) {
409 var relativeMapUrl = path
410 .toUri(
411 path.relative(path.fromUri(mapUrl), from: path.dirname(jsUrl)))
412 .toString();
413 assert(path.dirname(jsUrl) == path.dirname(mapUrl));
414 printer.emit('\n//# sourceMappingURL=');
415 if (options.inlineSourceMap) {
416 var bytes = UTF8.encode(JSON.encode(builtMap));
417 var base64 = BASE64.encode(bytes);
418 printer..emit('data:application/json;base64,')..emit(base64);
419 } else {
420 printer.emit(relativeMapUrl);
421 }
422 printer.emit('\n');
423 }
371 } 424 }
372 425
373 Map builtMap;
374 if (sourceMap != null) {
375 builtMap = placeSourceMap(sourceMap.build(jsUrl), mapUrl);
376 }
377 return new JSModuleCode(printer.getText(), builtMap); 426 return new JSModuleCode(printer.getText(), builtMap);
378 } 427 }
379 428
380 /// Similar to [getCode] but immediately writes the resulting files. 429 /// Similar to [getCode] but immediately writes the resulting files.
381 /// 430 ///
382 /// If [mapPath] is not supplied but [options.sourceMap] is set, mapPath 431 /// If [mapPath] is not supplied but [options.sourceMap] is set, mapPath
383 /// will default to [jsPath].map. 432 /// will default to [jsPath].map.
384 void writeCodeSync(ModuleFormat format, String jsPath, [String mapPath]) { 433 void writeCodeSync(ModuleFormat format, String jsPath) {
385 if (mapPath == null) mapPath = jsPath + '.map'; 434 String mapPath = jsPath + '.map';
386 var code = getCode(format, jsPath, mapPath); 435 var code = getCode(format, jsPath, mapPath);
387 new File(jsPath).writeAsStringSync(code.code); 436 var c = code.code;
388 if (code.sourceMap != null) { 437 if (options.wrapInEval) {
Jennifer Messerly 2016/09/13 23:00:33 this is not the right place for this. I'm not sure
438 // Add sourceURL to improve the debugging experience.
439 c += '\n//# sourceURL=${name}.js\n';
440 c = 'eval(${JSON.encode(c)});\n';
441 }
442 new File(jsPath).writeAsStringSync(c);
443 if (code.sourceMap != null && !options.inlineSourceMap) {
389 new File(mapPath).writeAsStringSync(JSON.encode(code.sourceMap)); 444 new File(mapPath).writeAsStringSync(JSON.encode(code.sourceMap));
390 } 445 }
391 } 446 }
392 } 447 }
393 448
394 /// The output of compiling a JavaScript module in a particular format. 449 /// The output of compiling a JavaScript module in a particular format.
395 class JSModuleCode { 450 class JSModuleCode {
396 /// The JavaScript code for this module. 451 /// The JavaScript code for this module.
397 /// 452 ///
398 /// If a [sourceMap] is available, this will include the `sourceMappingURL` 453 /// If a [sourceMap] is available, this will include the `sourceMappingURL`
399 /// comment at end of the file. 454 /// comment at end of the file.
400 final String code; 455 final String code;
401 456
402 /// The JSON of the source map, if generated, otherwise `null`. 457 /// The JSON of the source map, if generated, otherwise `null`.
403 /// 458 ///
404 /// The source paths will initially be absolute paths. They can be adjusted 459 /// The source paths will initially be absolute paths. They can be adjusted
405 /// using [placeSourceMap]. 460 /// using [placeSourceMap].
406 final Map sourceMap; 461 final Map sourceMap;
407 462
408 JSModuleCode(this.code, this.sourceMap); 463 JSModuleCode(this.code, this.sourceMap);
409 } 464 }
410 465
411 /// Adjusts the source paths in [sourceMap] to be relative to [sourceMapPath], 466 /// Adjusts the source paths in [sourceMap] to be relative to [sourceMapPath],
412 /// and returns the new map. 467 /// and returns the new map.
413 // TODO(jmesserly): find a new home for this. 468 // TODO(jmesserly): find a new home for this.
414 Map placeSourceMap(Map sourceMap, String sourceMapPath) { 469 Map placeSourceMap(
470 Map sourceMap, String sourceMapPath, Map<String, String> fileMappings) {
415 var dir = path.dirname(sourceMapPath); 471 var dir = path.dirname(sourceMapPath);
472 var map = new Map.from(sourceMap);
473 var list = new List.from(map['sources']);
474 map['sources'] = list;
475 String transformUri(String uri) {
476 var match = fileMappings[path.absolute(uri)];
477 if (match != null) return match;
416 478
417 var map = new Map.from(sourceMap); 479 // Fall back to a relative path.
418 List list = new List.from(map['sources']); 480 return path.toUri(path.relative(path.fromUri(uri), from: dir)).toString();
419 map['sources'] = list; 481 }
420 String relative(String uri) => 482
421 path.toUri(path.relative(path.fromUri(uri), from: dir)).toString();
422 for (int i = 0; i < list.length; i++) { 483 for (int i = 0; i < list.length; i++) {
423 list[i] = relative(list[i]); 484 list[i] = transformUri(list[i]);
424 } 485 }
425 map['file'] = relative(map['file']); 486 map['file'] = transformUri(map['file']);
426 return map; 487 return map;
427 } 488 }
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698