First I'll give some smaller bits of feedback, and then at the end propose something that integrates all the feedback.
I hope they can considered independently: if you do not like the proposal, feel free to just take in the feedback. I tried to order the feedback in order of "craziness" with the first item being least crazy. ======== Feedback ======== Some of these just may be drift between current thinking and what is on the wiki. Sorry for anything that is already covered by current thinking, I mostly just have the wiki as a reference: ----------- 1) Multiple mrls for System.load(): -------------- Dave Herman's latest post on "Synchronous module loading in ES6": http://calculist.org/blog/2012/03/29/synchronous-module-loading-in-es6/ mentions only allowing System.load() in inline HTML script tags. I'm assuming that is similar to the "Loader.prototype.load( mrl, callback, errback )" API in: http://wiki.ecmascript.org/doku.php?id=harmony:module_loaders Since System.load() is the only allowed option in HTML script tags, it should allow specifying more than one mrl in the call. Either: System.load("jquery.js", "underscore.js", function ($, _) {}) or System.load(["jquery.js", "underscore.js"], function ($, _) {}) ----------- 2) Default module path resolution ----------- In the "module path resolution" thread, Dave mentioned that the module_loaders resolve API should allow for some custom path resolution logic. However it would be good to specify the default logic. For the default logic, it would be great to see it be: baseUrl + ID + ".js" unless the ID contains a protocol, or starts with a "/", then ID is assumed to be an URL and used as-is. I'm sure the real default behavior needs a stronger definition, and I would like to see the equivalent of AMD loaders's "paths" config as an option for default config, but the main point is that in code, the IDs for importing/referencing would look like this: import $ from "jquery"; instead of this: import $ from "jquery.js"; I am just using an arbitrary import example, not sure what the most current import syntax is. Nothing else implied here for "import", but the ability to use "jquery" for the name and it works by default given the rules above. This is a similar module ID-to-path resolution used by AMD loaders today, and it has worked out well for us because it allows setting up references to "implementation providers" vs. particular URLs. This is nice when using a few libraries that depend on the same piece of functionality. A concrete example: I use jQuery in my app, and I use Backbone, which also uses jQuery. Ideally my script that directly uses jQuery and the Backbone module can both do: import $ from "jquery"; and it gets resolved to the same URL. With the "paths" config that is possible in AMD loaders, we can map that "jquery" to be a CDN version, or a local version, or a local version that is actually Zepto, just something that can stand in for jQuery. I like the idea of having a simpler "paths" config for that instead of requiring the developer to implement a resolver function or worse include a library to set up the resolver (might as well just use an AMD loader then :). ----------- 3) Modules IDs as strings ----------- This item of feedback is assuming that the way to get better optimized code delivery is to "concatenate" module files together. However, even if that is not the case for browser delivery, I still believe that allowing a way to combine a set of modules together in a file helps just with code sharing -- it is easier to handle and pass around a single JS file for distribution, but the library dev may still want to work with the modules separately on disk. With that assumption, module IDs should always be strings. This makes it easier to combine modules together and match the module to the string used in the import call: module "jquery" { } module "foo" { import $ from "jquery"; } This means that "foo" will get a handle on the "jquery" module. By using string IDs even in the "module {}" part, it makes it easier to match the references to the module provider, particularly for combined files. ----------- 4) export call ----------- There was mention in "simpler, sweeter syntax for modules" thread by Dave that maybe with syntax like this: import $ from "jquery.js"; that having a way to export a function may not be needed. However, I still feel some sort of "export call" is needed, or some way to export a function as the module value, to give parity with the runtime calls: System.load('jquery.js', function ($) { }); It would be awkward to do this: System.load('jquery.js', function (something) { var $ = something.$; }); ----------- 5) Compile time linking ----------- There is a tension between the runtime calls like System.load and the compile time linking of the module/import syntax. The runtime capabilities cannot be removed. However, I believe it would simplify the story for an end user if the the compile time linking is removed. While the compile time linking may give some kinds of type checks/export name checking, it is only one level deep, it does not help with this sort of checking: //Compile time checking can make sure //'jquery.js does export a $ import $ from 'jquery.js'; //However, it cannot help check if foo is //a real property $.foo(); Similar story for prototypical properties on constructor functions. New possibilities open up if this the compile time stuff is removed, and I believe it simplifies the end user's day-to-day interaction with modules (more below). ----------- 6) Import syntax ----------- If the compile time linking/checking is removed, module referencing gets simpler. No more import stuff, just use "from" to indicate the value is fetched from a module: let $ from "jquery"; let {search, fetch} from "service"; let {*} from "math"; Normal var/let and destructuring is used. The only new thing is "from" which indicates a module source. It only allow a string literal for the module name. Not sure if that last * example is valid, but maybe allow it to be valid to support the "import *" cases. If it is untenable, then throw it out. Node and AMD users have done just fine without having *. ----------- 7) Object destructuring ----------- Not module-specific, but it comes up in modules. In the "simpler, sweeter syntax for modules", Brendan wrapped up that mini-thread it up by it just needs to be documented and taught, but I cannot help calling it out again, particularly since I do not post very often to this list. No need to respond to this item, I just want to put my voice behind the backwards-ness of: let { draw: drawWidget } = widget; It will always stick out because object literals are constructed the other way, and object literals show up in the code next to these destructuring calls. Even if you know the rules, scanning code always requires a mental discontinuity jump to parse out what is going on. Yes, there is logic behind the way it is, but it scans wrong, takes the developer out of the flow. Sorry, had to get that out. ======== Proposal ======== Here is how module syntax might look given the feedback above: module "foo" { let $ from "jquery", {search, fetch} from "service"; //Use return to specify the exports, or optionally //use exports.foo = to assign values to properties //on an exports object, for use in circular dependencies return function () {}; } The module "foo" {} wrapper would *not* be needed in a file that is just declaring a module (think how node modules are constructed today). It is just for inlined module declarations, like when a bunch of modules are combined together. For the unwrapped case, it means allowing "return" for unwrapped modules in a place that normally has not been allowed. Hopefully that can be worked out if a module is in play and not a Program (I am not a grammarian, may have that wrong). To allow existing libraries to work in non ES.next and ES.next environments, perhaps also allow using an API form of the above: module("foo", function (from, exports) { var $ = from("jquery"), service = from("service"), search = service.search, fetch = service.fetch; //Use return to specify the exports, or optionally //use exports.foo = to assign values to properties //on an exports object, for use in circular dependencies return function () {}; }); If "module" does not work for an API name, this last form is close enough that the AMD "define" could be used instead, along with swapping "require" for "from". But that is a minor naming bikeshed. I would try to match the ES syntax names if possible. The main point is to have an API that could be runtime-checked for libraries that want to live in both the old and new world. James _______________________________________________ es-discuss mailing list [email protected] https://mail.mozilla.org/listinfo/es-discuss

