| Index: lib/src/compiler/compiler.dart
|
| diff --git a/lib/src/compiler/compiler.dart b/lib/src/compiler/compiler.dart
|
| index 77d03f06001903d13721d8df95cad6b419dd1298..d9b6b9630283595e72a25147f74b18daa4671e59 100644
|
| --- a/lib/src/compiler/compiler.dart
|
| +++ b/lib/src/compiler/compiler.dart
|
| @@ -3,6 +3,8 @@
|
| // BSD-style license that can be found in the LICENSE file.
|
|
|
| import 'dart:collection' show HashSet, Queue;
|
| +import 'dart:convert' show JSON;
|
| +import 'dart:io' show File;
|
| import 'package:analyzer/dart/element/element.dart' show LibraryElement;
|
| import 'package:analyzer/analyzer.dart'
|
| show AnalysisError, CompilationUnit, ErrorSeverity;
|
| @@ -17,12 +19,17 @@ import 'package:args/args.dart' show ArgParser, ArgResults;
|
| import 'package:args/src/usage_exception.dart' show UsageException;
|
| import 'package:func/func.dart' show Func1;
|
| import 'package:path/path.dart' as path;
|
| +import 'package:source_maps/source_maps.dart';
|
|
|
| import '../analyzer/context.dart'
|
| show AnalyzerOptions, createAnalysisContextWithSources;
|
| -import 'extension_types.dart' show ExtensionTypeSet;
|
| +import '../js_ast/js_ast.dart' as JS;
|
| import 'code_generator.dart' show CodeGenerator;
|
| import 'error_helpers.dart' show errorSeverity, formatError, sortErrors;
|
| +import 'extension_types.dart' show ExtensionTypeSet;
|
| +import 'js_names.dart' as JS;
|
| +import 'module_builder.dart' show transformModuleFormat, ModuleFormat;
|
| +import 'source_map_printer.dart' show SourceMapPrintingContext;
|
|
|
| /// Compiles a set of Dart files into a single JavaScript module.
|
| ///
|
| @@ -135,7 +142,7 @@ class ModuleCompiler {
|
|
|
| if (!options.unsafeForceCompile &&
|
| errors.any((e) => errorSeverity(context, e) == ErrorSeverity.ERROR)) {
|
| - return new JSModuleFile.invalid(unit.name, messages);
|
| + return new JSModuleFile.invalid(unit.name, messages, options);
|
| }
|
|
|
| var codeGenerator = new CodeGenerator(context, options, _extensionTypes);
|
| @@ -143,14 +150,6 @@ class ModuleCompiler {
|
| }
|
| }
|
|
|
| -enum ModuleFormat { es6, legacy, node }
|
| -
|
| -ModuleFormat parseModuleFormat(String s) => {
|
| - 'es6': ModuleFormat.es6,
|
| - 'node': ModuleFormat.node,
|
| - 'legacy': ModuleFormat.legacy
|
| - }[s];
|
| -
|
| class CompilerOptions {
|
| /// Whether to emit the source mapping file.
|
| ///
|
| @@ -209,10 +208,6 @@ class CompilerOptions {
|
| // TODO(ochafik): Simplify this code when our target platforms catch up.
|
| final bool destructureNamedParams;
|
|
|
| - /// Which module format to support.
|
| - /// Currently 'es6' and 'legacy' are supported.
|
| - final ModuleFormat moduleFormat;
|
| -
|
| const CompilerOptions(
|
| {this.sourceMap: true,
|
| this.sourceMapComment: true,
|
| @@ -222,7 +217,6 @@ class CompilerOptions {
|
| this.emitMetadata: false,
|
| this.closure: false,
|
| this.destructureNamedParams: false,
|
| - this.moduleFormat: ModuleFormat.legacy,
|
| this.hoistInstanceCreation: true,
|
| this.hoistSignatureTypes: false,
|
| this.nameTypeTests: true,
|
| @@ -238,7 +232,6 @@ class CompilerOptions {
|
| emitMetadata = args['emit-metadata'],
|
| closure = args['closure-experimental'],
|
| destructureNamedParams = args['destructure-named-params'],
|
| - moduleFormat = parseModuleFormat(args['modules']),
|
| hoistInstanceCreation = args['hoist-instance-creation'],
|
| hoistSignatureTypes = args['hoist-signature-types'],
|
| nameTypeTests = args['name-type-tests'],
|
| @@ -258,15 +251,6 @@ class CompilerOptions {
|
| 'disable if using X-SourceMap header',
|
| defaultsTo: true,
|
| hide: true)
|
| - ..addOption('modules',
|
| - help: 'module pattern to emit',
|
| - allowed: ['es6', 'legacy', 'node'],
|
| - allowedHelp: {
|
| - 'es6': 'es6 modules',
|
| - 'legacy': 'a custom format used by dartdevc, similar to AMD',
|
| - 'node': 'node.js modules (https://nodejs.org/api/modules.html)'
|
| - },
|
| - defaultsTo: 'legacy')
|
| ..addFlag('emit-metadata',
|
| help: 'emit metadata annotations queriable via mirrors',
|
| defaultsTo: false)
|
| @@ -301,7 +285,7 @@ class BuildUnit {
|
| /// The name of this module.
|
| final String name;
|
|
|
| - /// Library root. All library names are relative to this path/prefix.
|
| + /// All library names are relative to this path/prefix.
|
| final String libraryRoot;
|
|
|
| /// The list of sources in this module.
|
| @@ -331,46 +315,107 @@ class JSModuleFile {
|
| /// The list of messages (errors and warnings)
|
| final List<String> errors;
|
|
|
| - /// The JavaScript code for this module.
|
| - ///
|
| - /// If a [sourceMap] is available, this will include the `sourceMappingURL`
|
| - /// comment at end of the file.
|
| - final String code;
|
| + /// The AST that will be used to generate the [code] and [sourceMap] for this
|
| + /// module.
|
| + final JS.Program moduleTree;
|
|
|
| - /// The JSON of the source map, if generated, otherwise `null`.
|
| - ///
|
| - /// The source paths will initially be absolute paths. They can be adjusted
|
| - /// using [placeSourceMap].
|
| - final Map sourceMap;
|
| + /// The compiler options used to generate this module.
|
| + final CompilerOptions options;
|
|
|
| /// The binary contents of the API summary file, including APIs from each of
|
| - /// the [libraries] in this module.
|
| + /// the libraries in this module.
|
| final List<int> summaryBytes;
|
|
|
| JSModuleFile(
|
| - this.name, this.errors, this.code, this.sourceMap, this.summaryBytes);
|
| + this.name, this.errors, this.options, this.moduleTree, this.summaryBytes);
|
|
|
| - JSModuleFile.invalid(this.name, this.errors)
|
| - : code = null,
|
| - sourceMap = null,
|
| + JSModuleFile.invalid(this.name, this.errors, this.options)
|
| + : moduleTree = null,
|
| summaryBytes = null;
|
|
|
| /// True if this library was successfully compiled.
|
| - bool get isValid => code != null;
|
| + bool get isValid => moduleTree != null;
|
|
|
| - /// Adjusts the source paths in [sourceMap] to be relative to [sourceMapPath],
|
| - /// and returns the new map.
|
| + /// Gets the source code and source map for this JS module, given the
|
| + /// locations where the JS file and map file will be served from.
|
| ///
|
| - /// See also [writeSourceMap].
|
| - Map placeSourceMap(String sourceMapPath) {
|
| - var dir = path.dirname(sourceMapPath);
|
| -
|
| - var map = new Map.from(this.sourceMap);
|
| - List list = new List.from(map['sources']);
|
| - map['sources'] = list;
|
| - for (int i = 0; i < list.length; i++) {
|
| - list[i] = path.relative(list[i], from: dir);
|
| + /// Relative URLs will be used to point from the .js file to the .map file
|
| + //
|
| + // TODO(jmesserly): this should match our old logic, but I'm not sure we are
|
| + // correctly handling the pointer from the .js file to the .map file.
|
| + JSModuleCode getCode(ModuleFormat format, String jsUrl, String mapUrl) {
|
| + var opts = new JS.JavaScriptPrintingOptions(
|
| + emitTypes: options.closure,
|
| + allowKeywordsInProperties: true,
|
| + allowSingleLineIfStatements: true);
|
| + JS.SimpleJavaScriptPrintingContext printer;
|
| + SourceMapBuilder sourceMap;
|
| + if (options.sourceMap) {
|
| + var sourceMapContext = new SourceMapPrintingContext();
|
| + sourceMap = sourceMapContext.sourceMap;
|
| + printer = sourceMapContext;
|
| + } else {
|
| + printer = new JS.SimpleJavaScriptPrintingContext();
|
| + }
|
| +
|
| + var tree = transformModuleFormat(format, moduleTree);
|
| + tree.accept(
|
| + new JS.Printer(opts, printer, localNamer: new JS.TemporaryNamer(tree)));
|
| +
|
| + if (options.sourceMap && options.sourceMapComment) {
|
| + printer.emit('\n//# sourceMappingURL=$mapUrl\n');
|
| }
|
| - return map;
|
| +
|
| + Map builtMap;
|
| + if (sourceMap != null) {
|
| + builtMap = placeSourceMap(sourceMap.build(jsUrl), mapUrl);
|
| + }
|
| + return new JSModuleCode(printer.getText(), builtMap);
|
| + }
|
| +
|
| + /// Similar to [getCode] but immediately writes the resulting files.
|
| + ///
|
| + /// If [mapPath] is not supplied but [options.sourceMap] is set, mapPath
|
| + /// will default to [jsPath].map.
|
| + void writeCodeSync(ModuleFormat format, String jsPath, [String mapPath]) {
|
| + if (mapPath == null) mapPath = jsPath + '.map';
|
| + var code = getCode(format, jsPath, mapPath);
|
| + new File(jsPath).writeAsStringSync(code.code);
|
| + if (code.sourceMap != null) {
|
| + new File(mapPath).writeAsStringSync(JSON.encode(code.sourceMap));
|
| + }
|
| + }
|
| +}
|
| +
|
| +/// The output of compiling a JavaScript module in a particular format.
|
| +class JSModuleCode {
|
| + /// The JavaScript code for this module.
|
| + ///
|
| + /// If a [sourceMap] is available, this will include the `sourceMappingURL`
|
| + /// comment at end of the file.
|
| + final String code;
|
| +
|
| + /// The JSON of the source map, if generated, otherwise `null`.
|
| + ///
|
| + /// The source paths will initially be absolute paths. They can be adjusted
|
| + /// using [placeSourceMap].
|
| + final Map sourceMap;
|
| +
|
| + JSModuleCode(this.code, this.sourceMap);
|
| +}
|
| +
|
| +/// Adjusts the source paths in [sourceMap] to be relative to [sourceMapPath],
|
| +/// and returns the new map.
|
| +// TODO(jmesserly): find a new home for this.
|
| +Map placeSourceMap(Map sourceMap, String sourceMapPath) {
|
| + var dir = path.dirname(sourceMapPath);
|
| +
|
| + var map = new Map.from(sourceMap);
|
| + List list = new List.from(map['sources']);
|
| + map['sources'] = list;
|
| + for (int i = 0; i < list.length; i++) {
|
| + list[i] =
|
| + path.toUri(path.relative(path.fromUri(list[i]), from: dir)).toString();
|
| }
|
| + return map;
|
| }
|
|
|