| OLD | NEW |
| 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2015, 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 'package:args/args.dart' show ArgParser, ArgResults; | 5 import 'package:args/args.dart' show ArgParser, ArgResults; |
| 6 import 'package:path/path.dart' as path; | 6 import 'package:path/path.dart' as path; |
| 7 | 7 |
| 8 import '../js_ast/js_ast.dart'; | 8 import '../js_ast/js_ast.dart'; |
| 9 import 'js_names.dart'; | 9 import 'js_names.dart'; |
| 10 | 10 |
| (...skipping 28 matching lines...) Expand all Loading... |
| 39 if (format is String) { | 39 if (format is String) { |
| 40 return [parseModuleFormat(format)]; | 40 return [parseModuleFormat(format)]; |
| 41 } | 41 } |
| 42 return (format as List<String>).map(parseModuleFormat).toList(); | 42 return (format as List<String>).map(parseModuleFormat).toList(); |
| 43 } | 43 } |
| 44 | 44 |
| 45 /// Adds an option to the [argParser] for choosing the module format, optionally | 45 /// Adds an option to the [argParser] for choosing the module format, optionally |
| 46 /// [allowMultiple] formats to be specified, with each emitted into a separate | 46 /// [allowMultiple] formats to be specified, with each emitted into a separate |
| 47 /// file. | 47 /// file. |
| 48 void addModuleFormatOptions(ArgParser argParser, {bool allowMultiple: false}) { | 48 void addModuleFormatOptions(ArgParser argParser, {bool allowMultiple: false}) { |
| 49 argParser.addOption('modules', | 49 argParser |
| 50 help: 'module pattern to emit', | 50 ..addOption('modules', |
| 51 allowed: [ | 51 help: 'module pattern to emit', |
| 52 'es6', | 52 allowed: [ |
| 53 'common', | 53 'es6', |
| 54 'amd', | 54 'common', |
| 55 'legacy', // deprecated | 55 'amd', |
| 56 'node', // renamed to commonjs | 56 'legacy', // deprecated |
| 57 'all' // to emit all flavors for the SDK | 57 'node', // renamed to commonjs |
| 58 ], | 58 'all' // to emit all flavors for the SDK |
| 59 allowedHelp: { | 59 ], |
| 60 'es6': 'ECMAScript 6 modules', | 60 allowedHelp: { |
| 61 'common': 'CommonJS/Node.js modules', | 61 'es6': 'ECMAScript 6 modules', |
| 62 'amd': 'AMD/RequireJS modules' | 62 'common': 'CommonJS/Node.js modules', |
| 63 }, | 63 'amd': 'AMD/RequireJS modules' |
| 64 allowMultiple: allowMultiple, | 64 }, |
| 65 defaultsTo: 'amd'); | 65 allowMultiple: allowMultiple, |
| 66 defaultsTo: 'amd') |
| 67 ..addFlag('single-out-file', |
| 68 help: 'emit output so that libraries can be concatenated together into ' |
| 69 'a single file. Only compatible with legacy and amd module formats.'
, |
| 70 defaultsTo: false); |
| 66 } | 71 } |
| 67 | 72 |
| 68 /// Transforms an ES6 [module] into a given module [format]. | 73 /// Transforms an ES6 [module] into a given module [format]. |
| 69 /// | 74 /// |
| 70 /// If the format is [ModuleFormat.es6] this will return [module] unchanged. | 75 /// If the format is [ModuleFormat.es6] this will return [module] unchanged. |
| 71 /// | 76 /// |
| 72 /// Because JS ASTs are immutable the resulting module will share as much | 77 /// Because JS ASTs are immutable the resulting module will share as much |
| 73 /// structure as possible with the original. The transformation is a shallow one | 78 /// structure as possible with the original. The transformation is a shallow one |
| 74 /// that affects the top-level module items, especially [ImportDeclaration]s and | 79 /// that affects the top-level module items, especially [ImportDeclaration]s and |
| 75 /// [ExportDeclaration]s. | 80 /// [ExportDeclaration]s. |
| 76 Program transformModuleFormat(ModuleFormat format, Program module) { | 81 Program transformModuleFormat( |
| 82 ModuleFormat format, bool singleOutFile, Program module) { |
| 77 switch (format) { | 83 switch (format) { |
| 78 case ModuleFormat.legacy: | 84 case ModuleFormat.legacy: |
| 79 return new LegacyModuleBuilder().build(module); | 85 return new LegacyModuleBuilder(singleOutFile).build(module); |
| 80 case ModuleFormat.common: | 86 case ModuleFormat.common: |
| 81 return new CommonJSModuleBuilder().build(module); | 87 return new CommonJSModuleBuilder(singleOutFile).build(module); |
| 82 case ModuleFormat.amd: | 88 case ModuleFormat.amd: |
| 83 return new AmdModuleBuilder().build(module); | 89 return new AmdModuleBuilder(singleOutFile).build(module); |
| 84 case ModuleFormat.es6: | 90 case ModuleFormat.es6: |
| 91 assert(singleOutFile == false); |
| 85 return module; | 92 return module; |
| 86 } | 93 } |
| 87 return null; // unreachable. suppresses a bogus analyzer message | 94 return null; // unreachable. suppresses a bogus analyzer message |
| 88 } | 95 } |
| 89 | 96 |
| 90 /// Base class for compiling ES6 modules into various ES5 module patterns. | 97 /// Base class for compiling ES6 modules into various ES5 module patterns. |
| 91 /// | 98 /// |
| 92 /// This is a helper class for utilities and state that is shared by several | 99 /// This is a helper class for utilities and state that is shared by several |
| 93 /// module transformers. | 100 /// module transformers. |
| 94 // TODO(jmesserly): "module transformer" might be a better name than builder. | 101 // TODO(jmesserly): "module transformer" might be a better name than builder. |
| (...skipping 28 matching lines...) Expand all Loading... |
| 123 } | 130 } |
| 124 | 131 |
| 125 visitStatement(Statement node) { | 132 visitStatement(Statement node) { |
| 126 statements.add(node); | 133 statements.add(node); |
| 127 } | 134 } |
| 128 } | 135 } |
| 129 | 136 |
| 130 /// Generates modules for with our legacy `dart_library.js` loading mechanism. | 137 /// Generates modules for with our legacy `dart_library.js` loading mechanism. |
| 131 // TODO(jmesserly): remove this and replace with something that interoperates. | 138 // TODO(jmesserly): remove this and replace with something that interoperates. |
| 132 class LegacyModuleBuilder extends _ModuleBuilder { | 139 class LegacyModuleBuilder extends _ModuleBuilder { |
| 140 /// The legacy module format always generates output compatible with a single |
| 141 /// file mode. |
| 142 LegacyModuleBuilder(bool singleOutFile); |
| 143 |
| 133 Program build(Program module) { | 144 Program build(Program module) { |
| 134 // Collect imports/exports/statements. | 145 // Collect imports/exports/statements. |
| 135 visitProgram(module); | 146 visitProgram(module); |
| 136 | 147 |
| 137 // Build import parameters. | 148 // Build import parameters. |
| 138 var exportsVar = new TemporaryId('exports'); | 149 var exportsVar = new TemporaryId('exports'); |
| 139 var parameters = <TemporaryId>[exportsVar]; | 150 var parameters = <TemporaryId>[exportsVar]; |
| 140 var importNames = <Expression>[]; | 151 var importNames = <Expression>[]; |
| 141 var importStatements = <Statement>[]; | 152 var importStatements = <Statement>[]; |
| 142 for (var import in imports) { | 153 for (var import in imports) { |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 180 js.commentExpression( | 191 js.commentExpression( |
| 181 "Imports", new ArrayInitializer(importNames, multiline: true)), | 192 "Imports", new ArrayInitializer(importNames, multiline: true)), |
| 182 resultModule | 193 resultModule |
| 183 ]); | 194 ]); |
| 184 return new Program(<ModuleItem>[moduleDef]); | 195 return new Program(<ModuleItem>[moduleDef]); |
| 185 } | 196 } |
| 186 } | 197 } |
| 187 | 198 |
| 188 /// Generates CommonJS modules (used by Node.js). | 199 /// Generates CommonJS modules (used by Node.js). |
| 189 class CommonJSModuleBuilder extends _ModuleBuilder { | 200 class CommonJSModuleBuilder extends _ModuleBuilder { |
| 201 final bool singleOutFile; |
| 202 |
| 203 CommonJSModuleBuilder(this.singleOutFile) { |
| 204 // singleOutFile mode is not currently supported by the CommonJS module |
| 205 // builder. |
| 206 assert(singleOutFile == false); |
| 207 } |
| 208 |
| 190 Program build(Program module) { | 209 Program build(Program module) { |
| 191 var importStatements = <Statement>[]; | 210 var importStatements = <Statement>[]; |
| 192 | 211 |
| 193 // Collect imports/exports/statements. | 212 // Collect imports/exports/statements. |
| 194 visitProgram(module); | 213 visitProgram(module); |
| 195 | 214 |
| 196 for (var import in imports) { | 215 for (var import in imports) { |
| 197 // TODO(jmesserly): we could use destructuring here. | 216 // TODO(jmesserly): we could use destructuring here. |
| 198 var moduleVar = | 217 var moduleVar = |
| 199 new TemporaryId(pathToJSIdentifier(import.from.valueWithoutQuotes)); | 218 new TemporaryId(pathToJSIdentifier(import.from.valueWithoutQuotes)); |
| (...skipping 29 matching lines...) Expand all Loading... |
| 229 // This extra level of indirection should be unnecessary. | 248 // This extra level of indirection should be unnecessary. |
| 230 var block = | 249 var block = |
| 231 js.statement("(function() { 'use strict'; #; })()", [statements]); | 250 js.statement("(function() { 'use strict'; #; })()", [statements]); |
| 232 | 251 |
| 233 return new Program([block]); | 252 return new Program([block]); |
| 234 } | 253 } |
| 235 } | 254 } |
| 236 | 255 |
| 237 /// Generates AMD modules (used in browsers with RequireJS). | 256 /// Generates AMD modules (used in browsers with RequireJS). |
| 238 class AmdModuleBuilder extends _ModuleBuilder { | 257 class AmdModuleBuilder extends _ModuleBuilder { |
| 258 final bool singleOutFile; |
| 259 |
| 260 AmdModuleBuilder(this.singleOutFile); |
| 261 |
| 239 Program build(Program module) { | 262 Program build(Program module) { |
| 240 var importStatements = <Statement>[]; | 263 var importStatements = <Statement>[]; |
| 241 | 264 |
| 242 // Collect imports/exports/statements. | 265 // Collect imports/exports/statements. |
| 243 visitProgram(module); | 266 visitProgram(module); |
| 244 | 267 |
| 245 var dependencies = <LiteralString>[]; | 268 var dependencies = <LiteralString>[]; |
| 246 var fnParams = <Parameter>[]; | 269 var fnParams = <Parameter>[]; |
| 247 for (var import in imports) { | 270 for (var import in imports) { |
| 248 // TODO(jmesserly): we could use destructuring once Atom supports it. | 271 // TODO(jmesserly): we could use destructuring once Atom supports it. |
| (...skipping 21 matching lines...) Expand all Loading... |
| 270 assert(names != null); | 293 assert(names != null); |
| 271 for (var name in names) { | 294 for (var name in names) { |
| 272 exportedProps.add(new Property(js.string(name.name), name)); | 295 exportedProps.add(new Property(js.string(name.name), name)); |
| 273 } | 296 } |
| 274 } | 297 } |
| 275 statements.add(js.comment('Exports:')); | 298 statements.add(js.comment('Exports:')); |
| 276 statements.add( | 299 statements.add( |
| 277 new Return(new ObjectInitializer(exportedProps, multiline: true))); | 300 new Return(new ObjectInitializer(exportedProps, multiline: true))); |
| 278 } | 301 } |
| 279 | 302 |
| 280 var block = js.statement("define(#, function(#) { 'use strict'; #; });", | 303 var block = singleOutFile |
| 281 [new ArrayInitializer(dependencies), fnParams, statements]); | 304 ? js.statement("define(#, #, function(#) { 'use strict'; #; });", [ |
| 305 js.string(module.name, "'"), |
| 306 new ArrayInitializer(dependencies), |
| 307 fnParams, |
| 308 statements |
| 309 ]) |
| 310 : js.statement("define(#, function(#) { 'use strict'; #; });", |
| 311 [new ArrayInitializer(dependencies), fnParams, statements]); |
| 282 | 312 |
| 283 return new Program([block]); | 313 return new Program([block]); |
| 284 } | 314 } |
| 285 } | 315 } |
| 286 | 316 |
| 287 /// Escape [name] to make it into a valid identifier. | 317 /// Escape [name] to make it into a valid identifier. |
| 288 String pathToJSIdentifier(String name) { | 318 String pathToJSIdentifier(String name) { |
| 289 return toJSIdentifier(path.basenameWithoutExtension(name)); | 319 return toJSIdentifier(path.basenameWithoutExtension(name)); |
| 290 } | 320 } |
| 291 | 321 |
| (...skipping 18 matching lines...) Expand all Loading... |
| 310 // Ensure the identifier first character is not numeric and that the whole | 340 // Ensure the identifier first character is not numeric and that the whole |
| 311 // identifier is not a keyword. | 341 // identifier is not a keyword. |
| 312 if (result.startsWith(new RegExp('[0-9]')) || invalidVariableName(result)) { | 342 if (result.startsWith(new RegExp('[0-9]')) || invalidVariableName(result)) { |
| 313 return '\$$result'; | 343 return '\$$result'; |
| 314 } | 344 } |
| 315 return result; | 345 return result; |
| 316 } | 346 } |
| 317 | 347 |
| 318 // Invalid characters for identifiers, which would need to be escaped. | 348 // Invalid characters for identifiers, which would need to be escaped. |
| 319 final _invalidCharInIdentifier = new RegExp(r'[^A-Za-z_$0-9]'); | 349 final _invalidCharInIdentifier = new RegExp(r'[^A-Za-z_$0-9]'); |
| OLD | NEW |