| Index: lib/src/compiler/module_builder.dart
|
| diff --git a/lib/src/compiler/module_builder.dart b/lib/src/compiler/module_builder.dart
|
| index 6e72217f6d65df982eb17ce7384ef565e94f6e54..7a4e845d2a5c350cd04c83238b1e471eec0ecae2 100644
|
| --- a/lib/src/compiler/module_builder.dart
|
| +++ b/lib/src/compiler/module_builder.dart
|
| @@ -2,11 +2,91 @@
|
| // for details. All rights reserved. Use of this source code is governed by a
|
| // BSD-style license that can be found in the LICENSE file.
|
|
|
| +import 'package:args/args.dart' show ArgParser, ArgResults;
|
| import 'package:path/path.dart' as path;
|
|
|
| import '../js_ast/js_ast.dart';
|
| import 'js_names.dart';
|
|
|
| +/// The module format to emit.
|
| +enum ModuleFormat {
|
| + /// ECMAScript 6 module using import and export.
|
| + es6,
|
| +
|
| + /// CommonJS module (used in Node.js)
|
| + common,
|
| +
|
| + /// Asynchronous Module Definition (AMD, used in browsers)
|
| + amd,
|
| +
|
| + /// Dart Dev Compiler's legacy format (deprecated).
|
| + legacy
|
| +}
|
| +
|
| +/// Parses a string into a [ModuleFormat].
|
| +ModuleFormat parseModuleFormat(String s) => {
|
| + 'es6': ModuleFormat.es6,
|
| + 'common': ModuleFormat.common,
|
| + 'amd': ModuleFormat.amd,
|
| + // Deprecated:
|
| + 'node': ModuleFormat.common,
|
| + 'legacy': ModuleFormat.legacy
|
| + }[s];
|
| +
|
| +/// Parse the module format option added by [addModuleFormatOptions].
|
| +List<ModuleFormat> parseModuleFormatOption(ArgResults argResults) {
|
| + var format = argResults['modules'];
|
| + if (format is String) {
|
| + return [parseModuleFormat(format)];
|
| + }
|
| + return (format as List<String>).map(parseModuleFormat).toList();
|
| +}
|
| +
|
| +/// Adds an option to the [argParser] for choosing the module format, optionally
|
| +/// [allowMultiple] formats to be specified, with each emitted into a separate
|
| +/// file.
|
| +void addModuleFormatOptions(ArgParser argParser, {bool allowMultiple: false}) {
|
| + argParser.addOption('modules',
|
| + help: 'module pattern to emit',
|
| + allowed: [
|
| + 'es6',
|
| + 'common',
|
| + 'amd',
|
| + 'legacy', // deprecated
|
| + 'node', // renamed to commonjs
|
| + 'all' // to emit all flavors for the SDK
|
| + ],
|
| + allowedHelp: {
|
| + 'es6': 'ECMAScript 6 modules',
|
| + 'common': 'CommonJS/Node.js modules',
|
| + 'amd': 'AMD/RequireJS modules'
|
| + },
|
| + allowMultiple: allowMultiple,
|
| + defaultsTo: 'amd');
|
| +}
|
| +
|
| +/// Transforms an ES6 [module] into a given module [format].
|
| +///
|
| +/// If the format is [ModuleFormat.es6] this will return [module] unchanged.
|
| +///
|
| +/// Because JS ASTs are immutable the resulting module will share as much
|
| +/// structure as possible with the original. The transformation is a shallow one
|
| +/// that affects the top-level module items, especially [ImportDeclaration]s and
|
| +/// [ExportDeclaration]s.
|
| +Program transformModuleFormat(ModuleFormat format, Program module) {
|
| + switch (format) {
|
| + case ModuleFormat.legacy:
|
| + return new LegacyModuleBuilder().build(module);
|
| + case ModuleFormat.common:
|
| + return new CommonJSModuleBuilder().build(module);
|
| + case ModuleFormat.amd:
|
| + return new AmdModuleBuilder().build(module);
|
| + case ModuleFormat.es6:
|
| + return module;
|
| + }
|
| + return null; // unreachable. suppresses a bogus analyzer message
|
| +}
|
| +
|
| /// Base class for compiling ES6 modules into various ES5 module patterns.
|
| ///
|
| /// This is a helper class for utilities and state that is shared by several
|
| @@ -61,7 +141,7 @@ class LegacyModuleBuilder extends _ModuleBuilder {
|
| var importStatements = <Statement>[];
|
| for (var import in imports) {
|
| importNames.add(import.from);
|
| - // TODO(jmesserly): we could use destructuring once Atom supports it.
|
| + // TODO(jmesserly): we could use destructuring here.
|
| var moduleVar =
|
| new TemporaryId(pathToJSIdentifier(import.from.valueWithoutQuotes));
|
| parameters.add(moduleVar);
|
| @@ -105,8 +185,8 @@ class LegacyModuleBuilder extends _ModuleBuilder {
|
| }
|
| }
|
|
|
| -/// Generates node modules.
|
| -class NodeModuleBuilder extends _ModuleBuilder {
|
| +/// Generates CommonJS modules (used by Node.js).
|
| +class CommonJSModuleBuilder extends _ModuleBuilder {
|
| Program build(Program module) {
|
| var importStatements = <Statement>[];
|
|
|
| @@ -114,7 +194,7 @@ class NodeModuleBuilder extends _ModuleBuilder {
|
| visitProgram(module);
|
|
|
| for (var import in imports) {
|
| - // TODO(jmesserly): we could use destructuring once Atom supports it.
|
| + // TODO(jmesserly): we could use destructuring here.
|
| var moduleVar =
|
| new TemporaryId(pathToJSIdentifier(import.from.valueWithoutQuotes));
|
| importStatements
|
| @@ -122,7 +202,8 @@ class NodeModuleBuilder extends _ModuleBuilder {
|
|
|
| // TODO(jmesserly): optimize for the common case of a single import.
|
| for (var importName in import.namedImports) {
|
| - assert(!importName.isStar); // import * not supported yet.
|
| + // import * is not emitted by the compiler, so we don't support it here.
|
| + assert(!importName.isStar);
|
| var asName = importName.asName ?? importName.name;
|
| importStatements.add(js.statement(
|
| 'const # = #.#', [asName, moduleVar, importName.name.name]));
|
| @@ -135,7 +216,8 @@ class NodeModuleBuilder extends _ModuleBuilder {
|
| statements.add(js.comment('Exports:'));
|
| for (var export in exports) {
|
| var names = export.exportedNames;
|
| - assert(names != null); // export * not supported in legacy modules.
|
| + // export * is not emitted by the compiler, so we don't handle it here.
|
| + assert(names != null);
|
| for (var name in names) {
|
| statements
|
| .add(js.statement('#.# = #;', [exportsVar, name.name, name]));
|
| @@ -152,6 +234,56 @@ class NodeModuleBuilder extends _ModuleBuilder {
|
| }
|
| }
|
|
|
| +/// Generates AMD modules (used in browsers with RequireJS).
|
| +class AmdModuleBuilder extends _ModuleBuilder {
|
| + Program build(Program module) {
|
| + var importStatements = <Statement>[];
|
| +
|
| + // Collect imports/exports/statements.
|
| + visitProgram(module);
|
| +
|
| + var dependencies = <LiteralString>[];
|
| + var fnParams = <Parameter>[];
|
| + for (var import in imports) {
|
| + // TODO(jmesserly): we could use destructuring once Atom supports it.
|
| + var moduleVar =
|
| + new TemporaryId(pathToJSIdentifier(import.from.valueWithoutQuotes));
|
| + fnParams.add(moduleVar);
|
| + dependencies.add(import.from);
|
| +
|
| + // TODO(jmesserly): optimize for the common case of a single import.
|
| + for (var importName in import.namedImports) {
|
| + // import * is not emitted by the compiler, so we don't handle it here.
|
| + assert(!importName.isStar);
|
| + var asName = importName.asName ?? importName.name;
|
| + importStatements.add(js.statement(
|
| + 'const # = #.#', [asName, moduleVar, importName.name.name]));
|
| + }
|
| + }
|
| + statements.insertAll(0, importStatements);
|
| +
|
| + if (exports.isNotEmpty) {
|
| + var exportedProps = <Property>[];
|
| + for (var export in exports) {
|
| + var names = export.exportedNames;
|
| + // export * is not emitted by the compiler, so we don't handle it here.
|
| + assert(names != null);
|
| + for (var name in names) {
|
| + exportedProps.add(new Property(js.string(name.name), name));
|
| + }
|
| + }
|
| + statements.add(js.comment('Exports:'));
|
| + statements.add(
|
| + new Return(new ObjectInitializer(exportedProps, multiline: true)));
|
| + }
|
| +
|
| + var block = js.statement("define(#, function(#) { 'use strict'; #; });",
|
| + [new ArrayInitializer(dependencies), fnParams, statements]);
|
| +
|
| + return new Program([block]);
|
| + }
|
| +}
|
| +
|
| /// Escape [name] to make it into a valid identifier.
|
| String pathToJSIdentifier(String name) {
|
| return toJSIdentifier(path.basenameWithoutExtension(name));
|
|
|