Thanks for the proposal. This is actually related to a discussion we
had some time ago that amounted to creating a standard module
transport format suitable for script injection, which we've since
implemented in Narwhal and should definitely bring forward for
CommonJS standardization again.
http://groups.google.com/group/commonjs/browse_thread/thread/b40a38b42f248c0c
The schema serves all the same purposes, but looks like:
require.register({
id: {
"factory": function (require, exports, module) {
…
},
"depends": ["a", "b", "c", …]
},
…
});
This supports bundles as well as single module transport, and can with
varying degrees of difficulty depending on need, be automatically
complied from CommonJS modules where the first ellipsis is the text of
the "id" module and the dependencies are scraped with static analysis.
The module metadata could also be extended in the future.
Kris Kowal
Kris,
Thanks for the pointer. In the thread you pointed to, as Jonathan
points out, I am looking for something that works well in the browser,
and does not require a lot of boilerplate so I can author it by hand
for static files loaded by the browser. I also want something that
could run as-is in a server-side JS environment if possible.
As long as those things are met, then the particulars are less
important. But we've had a good amount of feedback in Dojo that using
synchronous XHR calls + eval() are not ideal.
Some comments while browsing through the links in that other thread:
It seems like some modules, like system and print are given special
status in the function wrapper. That seems to blur the line between a
module loader and a specific module collection.
As for any additional metadata for a module, for the run format
proposal, we could place that after the function(){} wrapper for the
module:
run(
"a",
["b", "c"],
function(b, c) {
},
{
"author": "John Doe"
}
);
If version targeting is required, I favor putting the version of the
module in its name, separated by a comma:
run(
"a,v1",
["b", "c"],
function(b, c) {
}
);
and if module a only wants b version 3's .colorize() method, using #
to reference it:
run(
"a,v1",
["b,v3#colorize", "c"],
function(colorize, c) {
}
);
James
On Wed, Oct 28, 2009 at 12:02 PM, James Burke <jrb...@gmail.com> wrote:
> I put up a proposal for an alternate module format, see here:
This is similar in spirit to the compiled module format for Caja, for example:
where, as you can see, we embed compiler artifacts extracted from the
code, like debugging information and the list of modules that are
loaded by the code, into the module format.
We do not, however, use this as the actual text that programmers type in.
Ihab
--
Ihab A.B. Awad, Palo Alto, CA
I agree. There will have to be some boilerplate, though, which is
strictly less awesome than no boilerplate at all. So, I would
additionally like this format to be easy to build from CommonJS
modules. These goals *should* be reconcilable.
> As long as those things are met, then the particulars are less
> important. But we've had a good amount of feedback in Dojo that using
> synchronous XHR calls + eval() are not ideal.
Right, which is why *some* boilerplate is necessary. Some would go so
far as to say that XHR+eval is not acceptable in any production
environment. I'll have to draw up a flow graph on the technically
viable solutions at some point, but it suffices to say we're on the
same page.
> It seems like some modules, like system and print are given special
> status in the function wrapper. That seems to blur the line between a
> module loader and a specific module collection.
"system" and "print" are not specified by CommonJS. It has been
decided that "system" as a free variable certainly never will be [1].
"print" has not been proposed, but it's still on the list of things
that it would be nice to inject into module scope. Both "system" and
"print" are available in Narwhal, but we take the stance that any uses
of these names in the standard library are bugs.
Speaking of which, it's becoming more likely in Narwhal that we'll
take an approach to module factory functions more like Ihab, Wes, and
Hannes's projects, where we pass an object to the module factory
functions that contains stuff that ought to be in scope. If that were
the case, the boilerplate for a "Module in Transit" from CommonJS
would probably look more like:
require.register({id: {
"depends": ["a", "b", "c", …],
"factory": function (___) {
var require = ___.require;
var exports = ___.exports;
var module = ___.module;
…
};
})
For the hand-written case, this would work:
require.register({id: {
"depends": ["a"],
"factory": function (_) {
var a = _.require("a");
…
}
});
Injecting the modules directly as arguments would obviate the
possibility of compiling CommonJS modules to this transit format.
> As for any additional metadata for a module, for the run format
> proposal, we could place that after the function(){} wrapper for the
> module:
>
> run(
> "a",
> ["b", "c"],
> function(b, c) {
>
> },
> {
> "author": "John Doe"
> }
> );
This gets at the heart of what you're looking for, I think: really
cutting the fat on the syntax. I think it would be more efficient to
get through the bikeshedding on IRC at #commonjs.
The reason for going with "require" as the name space is to reduce the
global footprint. There would need to be *some* footprint, and you
might want to be able to use "require" in inline scripts somewhere to
load the program module. Additionally, "require" would be masked by
"require" in the module's local scope, so it would not even be a free
variable observable within a module.
The reason for calling it "register" as opposed to "run" is to free
the implication that the module is run in-place at the time of
declaration, which would not be the case if it is declared before its
transitive dependencies have become available (it depends an "a", "b",
and "c", and "c" depends on "d", so you can't execute it until all of
those are registered). Also, the module itself might not be a
transitive dependency of the "main" program module, in which case it
would not be executed and any of its side-effects would not be
desired.
The reason for going with an object as the first argument is to permit
bundles of modules to be sent down in a single file. My original
proposal called for register(id, factory), but the need for additional
metadata (the dependencies, and presumably other stuff eventually) and
the need for bundles brought us to the current syntax. With some
sacrifice in complexity, we could presumably support an additional
argument form for the "as-brief-as-possible" handwritten module
transport case.
> If version targeting is required, I favor putting the version of the
> module in its name, separated by a comma:
We do not presently specify anything for versions, but this would work
with the current proposal as a module identifier convention.
> and if module a only wants b version 3's .colorize() method, using #
> to reference it:
>
> run(
> "a,v1",
> ["b,v3#colorize", "c"],
> function(colorize, c) {
>
> }
> );
An old version of Chiron supported require("id#key"), but I've dropped
it because it costs far more in the complexity of the loader than the
complexity of having to dereference it in JavaScript, and this group
is [wisely] reticent about standardizing [superfluous] conveniences.
Kris Kowal
[1] http://wiki.commonjs.org/wiki/System/ArchivedShowOfHands