ES5 Module Systems (was: Alternative proposal to privateName.public)
However, I am confused by the "module.exports = ..." part of your boilerplate. The main CommonJS wiki seems down at the moment, but looking at wiki.commonjs.org.mirrors.page.ca/articles/m/o/d/Modules.html on the mirror site, I could not find any support for this idiom. The closest I could find was < wiki.commonjs.org.mirrors.page.ca/articles/m/o/d/Modules_SetExports_9215.html>, which suggests it should read "module.setExports(...);" instead. Where does "module.exports = ..." come from?
module.exports = ... is a mechanism in node.js to overwrite the exports object entirely. (useful when you want to export, say a function rather then an object)
It seems to be similar to module.setExports and is there purely to support the implementation of the module system in node.js
Hi Axel, a nice demonstration of the simplicity of the core of AMD is the simplicity with which it can be implemented using a promise library.
Exactly; nice demo. Doing something comparably asynchronous with Node.js modules (without a compilation step) is much more hacky: www.2ality.com/2011/11/lobrow.html
({ define: typeof define === "function" ? define : function(A,F) { module.exports = F.apply(null, A.map(require)) } }). define([ "./module1", "./module2" ], function (module1, module2) { return ... } );
[1] www.2ality.com/2011/11/module-gap.html
I like this adapter, and have just changed code.google.com/p/es-lab/source/browse/trunk/src/ses/amdTest3.js to test that it works with our simple AMD Loader in translation-free SES-on-ES5 (similar to the above, at code.google.com/p/es-lab/source/browse/trunk/src/ses/makeSimpleAMDLoader.js). You can run this test by visiting es-lab.googlecode.com/svn/trunk/src/ses/explicit.html in a modern browser. If you see the line "AMD loader test...succeeded", then it did.
However, I am confused by the "module.exports = ..." part of your boilerplate. The main CommonJS wiki seems down at the moment, but looking at wiki.commonjs.org.mirrors.page.ca/articles/m/o/d/Modules.html on the mirror site, I could not find any support for this idiom. The closest I could find was wiki.commonjs.org.mirrors.page.ca/articles/m/o/d/Modules_SetExports_9215.html, which suggests it should read "module.setExports(...);" instead. Where does "module.exports = ..." come from?
It’s necessary for Node.js.
Node.js:
var module1 = require("./module1");
module1.foo();
var module2 = require("./module2");
export.bar = function() {
module2.baz();
}
AMD:
define([ "./module1", "./module2" ],
function (module1, module2) {
module1.foo();
return { // export
bar: function () {
module2.baz();
}
};
}
);
Hence, unless you add to exports
, Node.js won’t see your module’s contributions.
IIRC, Node.js deviates from CJS in that module.exports is an alias for exports. Without that alias, the boilerplate would be more complicated (you’d have to copy the properties of the object produced by F() over to exports). But I am focusing on Node.js, at the moment, so I can afford to rely on this functionality.
IMHO, the best option is still boilerplate (to conditionally turn an AMD into a Node.js module): ({ define: typeof define === "function" ? define : function(A,F) { module.exports = F.apply(null, A.map(require)) } }). define([ "./module1", "./module2" ], function (module1, module2) { return ... } );
[1] www.2ality.com/2011/11/module-gap.html
I like this adapter, and have just changed code.google.com/p/es-lab/source/browse/trunk/src/ses/amdTest3.js to test that it works with our simple AMD Loader in translation-free SES-on-ES5 (similar to the above, at code.google.com/p/es-lab/source/browse/trunk/src/ses/makeSimpleAMDLoader.js). You can run this test by visiting es-lab.googlecode.com/svn/trunk/src/ses/explicit.html in a modern browser. If you see the line "AMD loader test...succeeded", then it did.
As an aside, one benefit of the above boilerplate is that it is just a prefix (easier to copy and paste than solutions that are wrapped around an AMD).
On Mon, Dec 26, 2011 at 1:46 PM, Mark S. Miller <erights at google.com> wrote:
On Mon, Dec 26, 2011 at 12:15 PM, Axel Rauschmayer <axel at rauschma.de> wrote:
The adapters are all a bit cumbersome to use. IMHO, the best option is still boilerplate (to conditionally turn an AMD into a Node.js module):
({ define: typeof define === "function" ? define : function(A,F) { module.exports = F.apply(null, A.map(require)) }
}). define([ "./module1", "./module2" ], function (module1, module2) { return ... } );
This type of boilerplate may not be processed properly by AMD optimizers that insert module IDs into the define call when multiple modules are combined together. While the above may work with the requirejs optimizer, it is not guaranteed to work in the future. I still prefer the define() call is a non-property function call and not a call to an object method, to avoid possible conflicts with a module export value (uglifyjs has one such conflict). A bit more background in this message:
groups.google.com/group/nodejs-dev/msg/aaaf40dfeca04314
In that message I also mention encouraging the use of the "amdefine" adapter module in Node. Use of this module will help test drive a define() implementation that could be integrated into Node later, and by having Node packages use the module, it would give Node committers a way to scan the package.json info to find out if define() use is used enough to warrant consideration in their core.
"amdefine" also supports a callback-style require (with callback fired on nextTick()) and most of the loader plugin API, so it gives an idea how big a complete define() implementation in Node might be:
But if inline boilerplate is desired instead of a dependency on an npm-installed module, there are a set of options in this project:
which include boilerplates that also work in a "just use globals with ordered HTML script elements" browser setup. I personally prefer this one if AMD+Node support is desired:
umdjs/umd/blob/master/returnExports.js
I also encourage any ES.next module system to allow opt-in to an ES.next module system using a similar type of boilerplate. However, I am concerned that the declarative nature and new syntax needed for ES.next modules would make this difficult to do. I have given feedback offline to Dave Herman about it and was not going to bring it up more publicly until later, but given the talk of boilerplate, seems appropriate to mention it here.
James
This type of boilerplate may not be processed properly by AMD optimizers that insert module IDs into the define call when multiple modules are combined together. While the above may work with the requirejs optimizer, it is not guaranteed to work in the future. I still prefer the define() call is a non-property function call and not a call to an object method, to avoid possible conflicts with a module export value (uglifyjs has one such conflict).
I’d expect to remove my boilerplate either via an extra build step or via generic boilerplate support in the RequireJS optimizer (“remove all lines where '//amd-boilerplate' appears”).
I’ve chosen my format after considering two alternatives (which both had disadvantages).
Alternative 1: if (typeof define !== 'function') { var define = (require('amdefine'))(module); } define(...);
The above code relies on a quirk of of var
. If you wrap it in an IIFE then it will stop working (because the inner var define will always shadow an outer value).
Alternative 2: (function (define) { define(...); }(typeof define === 'function' ? define : require('amdefine'))(module));
Here the boilerplate appears both before and after the AMD code. That’s more intrusive and harder to copy/paste.
In that message I also mention encouraging the use of the "amdefine" adapter module in Node. Use of this module will help test drive a define() implementation that could be integrated into Node later, and by having Node packages use the module, it would give Node committers a way to scan the package.json info to find out if define() use is used enough to warrant consideration in their core.
I’m still examining all available, but eventually a campaign might be in order.
[Note change of Subject:]
On Mon, Dec 26, 2011 at 12:15 PM, Axel Rauschmayer <axel at rauschma.de> wrote:
Hi Axel, a nice demonstration of the simplicity of the core of AMD is the simplicity with which it can be implemented using a promise library. From < strawman:concurrency#amd_loader_lite
function makeSimpleAMDLoader(fetch, moduleMap = Map()) { var loader;
}
I like this adapter, and have just changed < code.google.com/p/es-lab/source/browse/trunk/src/ses/amdTest3.js> to
test that it works with our simple AMD Loader in translation-free SES-on-ES5 (similar to the above, at < code.google.com/p/es-lab/source/browse/trunk/src/ses/makeSimpleAMDLoader.js>).
You can run this test by visiting < es-lab.googlecode.com/svn/trunk/src/ses/explicit.html> in a modern
browser. If you see the line "AMD loader test...succeeded", then it did.
However, I am confused by the "module.exports = ..." part of your boilerplate. The main CommonJS wiki seems down at the moment, but looking at wiki.commonjs.org.mirrors.page.ca/articles/m/o/d/Modules.html
on the mirror site, I could not find any support for this idiom. The closest I could find was < wiki.commonjs.org.mirrors.page.ca/articles/m/o/d/Modules_SetExports_9215.html>,
which suggests it should read "module.setExports(...);" instead. Where does "module.exports = ..." come from?