That is my understanding too, but all import and module declarations
need to be at the top level of a module, not as part of if/else, and
require only takes string literals. So it results in dependencies
executing before the main body of the current module executes. I think
it is more subtle than that, but here is what is not allowed:
var name;
if (someCondition) {
name = 'a1';
} else {
name = 'a2';
}
module m = require(name);
or this:
module m;
if (someCondition) {
m = require('a1');
}else{
m = require('a2');
}
The above cases should be handled through the Module Loader's
Loader.load(moduleName, function(moduleRef){}); API, which looks like
an early evaluation model (the function callback to Loader.load does
not then need to do a require() for the module to execute it).
This also seems like it would not be allowed, because 'module' parents
can only be ProgramElement(load), ExportDeclaration(load) or
ModuleElement(load),
and those items can only have ProgramElement(load) or Program(load) as
parents:
try{
module m = require('first');
}catch(e){
module m = require('backup');
}
What is less clear to me, and I am trying to get more clarification,
is if something like this is allowed:
//baz.js
module foo = require('foo.js');
//foo.js:
export var name = 'foo';
module bar = require('bar.js');
var barName = bar.name;
//bar.js:
export var name = 'bar';
module foo = require('foo.js');
//This works because foo.js did the export before the require?
var fooName = foo.name;
I have asked about this on the es-discuss list:
https://mail.mozilla.org/pipermail/es-discuss/2011-May/014507.html
End result though is that harmony modules prevent dynamic/computed
require cases that are allowed by CommonJS Modules 1.1. These result
in the CommonJS use of lazy evaluation, and are anti-patterns/break
when used in an async loading context like the browser.
The lazy evaluation in CommonJS Modules 1.1 may make some forms of
circular dependencies easier, but that very small benefit is
outweighed by encouraging coding practices that do not work in a
browser context.
Note that early/late evaluation difference only affects a small
percentage of modules. The vast majority are fine in either approach.
James
End result though is that harmony modules prevent dynamic/computed
require cases that are allowed by CommonJS Modules 1.1. These result
in the CommonJS use of lazy evaluation, and are anti-patterns/break
when used in an async loading context like the browser.
Note that early/late evaluation difference only affects a small
percentage of modules. The vast majority are fine in either approach.
That is not what has shown up in practice, at least in Node module
usage. The root of my examples were things I saw when trying to
convert Node modules.
> the browser loader that I wrote once upon a time scraped the source for
> require()s.
By having a traditional CommonJS modules loader parse the source
before evaluating it and *only* allow loading of modules that were
specified by a string literal, that would be something, it would
shrink the gap between early and lazy eval to almost zero. Well that,
and if it mandated require() calls to be top-level calls in a module,
not part of other statements. The only thing left would be some types
of circular dependencies that are brittle anyway.
However, by introducing that more stringent CommonJS module loader,
you will break existing code. I don't think for instance, node.js,
would do such a change.
> I believe you can still dynamically load Harmony modules by using the module
> loader API.
Correct, but that Harmony API is a callback API. The CommonJS modules
that have a problem are the ones that do a computed require but expect
sync require access. At least for the cases I have seen in Node. The
try{ var a = require('a'); } catch(e) { var a = require('a1') } are
particularly troublesome to see.
James