How to define the top-level module of an application?

177 views
Skip to first unread message

Eric Bréchemier

unread,
Jan 6, 2012, 11:41:05 AM1/6/12
to amd-implement
I would like to attract your attention on this issue posted on the AMD
API project on GitHub [1].

I feel like the current specification misses:
- a method to define the main module in order to bootstrap the
asynchronous loading of all other modules, e.g. a global entry point
for the local require() function
- a method to configure the paths and other options of the loader

Cheers,

Eric Bréchemier

--

[1] Document entry point for a multi-module program
https://github.com/amdjs/amdjs-api/issues/8

James Burke

unread,
Jan 8, 2012, 8:00:36 PM1/8/12
to amd-im...@googlegroups.com
On Fri, Jan 6, 2012 at 8:41 AM, Eric Bréchemier
<eric.br...@gmail.com> wrote:
> I would like to attract your attention on this issue posted on the AMD
> API project on GitHub [1].
> [1] Document entry point for a multi-module program
> https://github.com/amdjs/amdjs-api/issues/8

Thanks for the heads up on the github issue. I think I *just* figured
out how administrators on the amdjs group can get notifications --
apparently "owners" do not get notifications by default, and I had to
create a separate "team" with those same members to apparently get
notifications.

At least I think it is supposed to work. We'll see if it works from
this point forward. Currently the owners/notification list are github
users rcgill, unscriptable, rbackhouse and me. Other implementers of
amd loaders can get access too, just ping me.

I responded in the ticket, but I think it is a bit early to specify
the top-level call, see the issue for more detail. We should continue
the discussion in the ticket since others have already responded in
that ticket vs this email thread.

James

Eric Bréchemier

unread,
Jan 30, 2012, 8:29:05 AM1/30/12
to amd-im...@googlegroups.com
This issue is still open: https://github.com/amdjs/amdjs-api/issues/8

Following James Burke's advice, I am moving the discussion back to the
mailing list. To summarize, we discussed the possibility to use
define() to bootstrap dynamic loading of top-level modules, but it
appears to be impractical due to optimizations which may delay the
execution of define(), sometimes indefinitely.

Instead of calling a method with a different name in each loader, can
we agree on a method to trigger the loading of the main module?

I propose to make the local require() method accessible at the top
level as define.require.

Currently, this method can only be accessed cross-loader by calling
define() and receiving the method as an argument. Since there is no
guarantee that the method provided to define() will run in the first
place, we are facing a chicken and egg problem, which would be solved
by giving access to the method through the global variable define.

The idea is to use:
define.require(["main"]);

instead of calling a different method in each loader:
requirejs(["main"]);
curl(["main"]);
...

Looking forward to your feedback,
Cheers,

Eric Bréchemier

James Burke

unread,
Feb 4, 2012, 5:25:33 PM2/4/12
to amd-im...@googlegroups.com
On Mon, Jan 30, 2012 at 5:29 AM, Eric Bréchemier
<eric.br...@gmail.com> wrote:
> The idea is to use:
> define.require(["main"]);
>
> instead of calling a different method in each loader:
> requirejs(["main"]);
> curl(["main"]);

I am open to figuring this out. As I work on volo, which can install
code dependencies, I can see value in a common top level call to at
least handle setting configuration information, like a 'map' config.
This would allow volo to work with any AMD loader, which would be
great.

I liked the idea of using require() and require.config(), but others
on this list believe it can be confused with the local require. Also,
in cases where there is an existing require() for some reason, it may
be nice to have a non-conflicting call.

If it is define.require(), would the config call be define.config()?

I would like it more if the top level call and the config call were
separate from define, since define is focused on module definition,
not loader configuration and bootstrapping. In other words, having a
separate API entry points for loader concerns vs. module definition
would be nice.

Given that, I'm still partial to a global require() and
require.config(), but open to other suggestions.

James

Eric Bréchemier

unread,
Feb 8, 2012, 8:03:50 AM2/8/12
to amd-im...@googlegroups.com
> I liked the idea of using require() and require.config(), but others
> on this list believe it can be confused with the local require. Also,
> in cases where there is an existing require() for some reason, it may
> be nice to have a non-conflicting call.

If another name is needed, I suggest "run".

For example:
define.run("main");
or
define.run("main",config); with an optional config object

Another approach would be to return something from define, e.g. a
promise, which may be used to bootstrap the loading:

var promise = define("main",function(){...});
promise.then(function(){
// loaded
});
promise.setup(config);
promise.run();

This is a more complex approach and I would personally favor a simple
function call.

Cheers,

Eric

James Burke

unread,
Feb 8, 2012, 3:10:01 PM2/8/12
to amd-im...@googlegroups.com
On Wed, Feb 8, 2012 at 5:03 AM, Eric Bréchemier
<eric.br...@gmail.com> wrote:
>> I liked the idea of using require() and require.config(), but others
>> on this list believe it can be confused with the local require. Also,
>> in cases where there is an existing require() for some reason, it may
>> be nice to have a non-conflicting call.
>
> If another name is needed, I suggest "run".
>
> For example:
> define.run("main");
> or
> define.run("main",config); with an optional config object

My bigger concern was placing the function under the 'define'
function. It is nice, for example in node, if they just had to
implement a simple define() function without picking up these calls,
since they have a different way to trigger module loading, and do not
have the same configuration needs as a web based module loader.

So given that, I still prefer require() and require.config(). Or
something else that is not put under define().

James

Eric Bréchemier

unread,
Feb 9, 2012, 7:55:51 AM2/9/12
to amd-im...@googlegroups.com
Could we use amd as a top-level object?

I think that amd.define(), amd.config() and amd.require() would make sense.

Defining require() at the top level with a different semantic than
CommonJS would probably be confusing.

Cheers,

Eric

Ryan Florence

unread,
Feb 9, 2012, 9:50:37 AM2/9/12
to amd-im...@googlegroups.com
`require` is the only thing that makes any sense to me. What's the reason for pushback against it? Just "one more global" or is it something else?

James Burke

unread,
Feb 9, 2012, 2:51:23 PM2/9/12
to amd-im...@googlegroups.com
On Thu, Feb 9, 2012 at 6:50 AM, Ryan Florence <rpflo...@gmail.com> wrote:
> `require` is the only thing that makes any sense to me. What's the reason for pushback against it? Just "one more global" or is it something else?

It seems that it can be confusing to talk about a global require vs. a
local require. I can see the point, but I like that the understanding
of the local require carries over into global require use.

There is also a concern about conflicting with a node-type
require(''), but that may not be an issue in practice. In Mozilla
Jetpacks, they have a sync-style require('') for their modules. But in
those cases, this sort of top level "start loading/tracing
dependencies" calls are not needed, and config is specified outside
the code, as I recall.

Eric's idea of 'amd' is interesting, but I still think that is mixing
up loader concerns with module format concerns -- the config() and top
level loading calls are loader concerns, where the AMD specs are
around module definition APIs. 'amdload' might be more accurate. But
then it acts a lot like require()...

It would be good to get input from John Hann, Rawld, Richard
Backhouse, other loader implementers.

James

rbackhouse

unread,
Feb 9, 2012, 5:50:47 PM2/9/12
to amd-implement
I am in agreement with James in that loader specific things such as
config and entry points should be kept separate from the module format
spec. I personally don't like having the entry point be called
"require" as it seems to be point of confusion with developers new to
AMD. I have run into quite a few dojo developers who were surprised to
discover that the global require that the dojo AMD loader provides
acts differently to the modules version.

Richard

On Feb 9, 2:51 pm, James Burke <jrbu...@gmail.com> wrote:

Adam Crabtree

unread,
Feb 9, 2012, 6:31:28 PM2/9/12
to amd-im...@googlegroups.com
@Richard I think you just hit the nail on the head. I forked the conversation to discuss this issue specifically a couple hours ago:

http://groups.google.com/group/amd-implement/browse_thread/thread/e2dc40bdb85948e

Basically, I see there as being 4 main concerns:

1. Definition spec (AMD)
2. Config spec
3. Loader implementations (network IO)

// The one that nobody talks about and conflates with #3
4. Module loading spec. Basically, is what should define the sync/local require and define implementations.

Even node separates out these concerns with the following parallels:

1. CJS modules
2. node_modules loading strategy
3. npm
4. synchronous require (caching based on path)

Cheers,
Adam Crabtree
--
Better a little with righteousness
       than much gain with injustice.
Proverbs 16:8

James Burke

unread,
Feb 9, 2012, 7:23:02 PM2/9/12
to amd-im...@googlegroups.com
On Thu, Feb 9, 2012 at 2:50 PM, rbackhouse <richarda...@gmail.com> wrote:
> I am in agreement with James in that loader specific things such as
> config and entry points should be kept separate from the module format
> spec. I personally don't like having the entry point be called
> "require" as it seems to be point of confusion with developers new to
> AMD. I have run into quite a few dojo developers who were surprised to
> discover that the global require that the dojo AMD loader provides
> acts differently to the modules version.

What are the differences? It seems to me:

* how relative paths are resolved, which makes sense since the global
require() as a different relative relationship
* possibly the existence of a require.config() on the global object?

James

James Burke

unread,
Feb 9, 2012, 7:25:47 PM2/9/12
to amd-im...@googlegroups.com
On Thu, Feb 9, 2012 at 3:31 PM, Adam Crabtree <atcra...@gmail.com> wrote:
> @Richard I think you just hit the nail on the head. I forked the
> conversation to discuss this issue specifically a couple hours ago:
>
> http://groups.google.com/group/amd-implement/browse_thread/thread/e2dc40bdb85948e
>
> Basically, I see there as being 4 main concerns:
>
> 1. Definition spec (AMD)
> 2. Config spec
> 3. Loader implementations (network IO)
>
> // The one that nobody talks about and conflates with #3
> 4. Module loading spec. Basically, is what should define the sync/local
> require and define implementations.
>
> Even node separates out these concerns with the following parallels:
>
> 1. CJS modules
> 2. node_modules loading strategy
> 3. npm
> 4. synchronous require (caching based on path)

Oh, if you mean to ask how synchronous require caches in AMD it should
be by module ID. It is implied from the spec since relative module IDs
are resolved relative to the reference module ID and not its path.
Node chose to use path.

James

rbackhouse

unread,
Feb 9, 2012, 7:50:59 PM2/9/12
to amd-implement
Yes, those and also understanding that the synchronous style of
require cannot be used on the global version.

Richard

On Feb 9, 7:23 pm, James Burke <jrbu...@gmail.com> wrote:

Taka Kojima

unread,
Mar 2, 2012, 5:24:52 PM3/2/12
to amd-implement
I think we can get away with only having define and no global require.

Your entry-point would look something like:

define("main", ["a","b","c"], function(a,b,c) {

});

If you need to specify configuration, we could do something like:

define("main", ["a","b","c","require"], function(a,b,c,require) {

require.config({
paths: {
"some/module" : "some/path"
}
});

a.doSomething();

});


In order to accomplish this, we have to support out-of-context named
modules, which means we can't defer define() calls until we hear a
script onLoad event.

This brings me to my next point, discussed here:
http://groups.google.com/group/amd-implement/browse_thread/thread/b684ff2c9db00a74

Essentially, it requires you to specify at the top of any file that
provides more than one module definition, a list of modules that are
defined later on in the script.

something like :

define(["a","b","c","d"]); at the top of each JS file (this would
usually only apply to built JS files, but doesn't have to).

or instead of define(), maybe:

define.promise(["a","b","c","d"]);

promise might not be the best name as it might confuse some, but
essentially you are "promising" to define these other modules at some
later point, letting the AMD loader know that it doesn't have to worry
about fetching them, they will be defined() shortly or in time.

A standard define.promise() type of implementation is really nice
because it let's you do a whole bunch of things outside of just
defining multiple modules in a single file.

Taka

Eric Bréchemier

unread,
Mar 3, 2012, 3:29:49 AM3/3/12
to amd-im...@googlegroups.com
I agree, this looks good.

Cheers,

Eric

John Hann

unread,
Mar 3, 2012, 10:42:53 AM3/3/12
to amd-im...@googlegroups.com, amd-implement
Hey Taka,

I can appreciate wanting to keep `require` out of the global scope. curl.js doesn't declare it globally by default to avoid confusion and common noob mistakes.

However, adding application bootstrap methods to the local require seems like the wrong direction to me. Here's why:

1. config() should be a one-time event, not something that devs feel they can do at any time. Allowing config at any time could easily make the app nondeterministic.

2. Adding more methods to the local require will not help us or our devs migrate to evolving module standards. The more we add to the local require, the harder it's going to be for all of us. curl.js only supports the standardized local require. There are no .config or .ready or other things on it.

Just declare a separate global for app bootstrap. That solves the problem. The few devs who need to create cross-loader code can do it by casting the loader-specific global (e.g. `curl`) to `require` quite easily.

-- John

Sent from my sonic screwdriver

On Mar 2, 2012, at 5:24 PM, Taka Kojima <ta...@gigafied.com> wrote:

> require.config

Taka Kojima

unread,
Mar 3, 2012, 3:00:44 PM3/3/12
to amd-im...@googlegroups.com
I am personally in favor of having two globals, I just don't know that `require` is the right name for one, for reasons already stated earlier in this thread. I do feel that it should be standardized though, along with config.

I was just trying to propose a solution that would work with the one pre-existing standard/global, define. I agree though, this isn't the best approach.

Taka

Rawld

unread,
Mar 6, 2012, 4:51:14 AM3/6/12
to amd-im...@googlegroups.com
On 02/09/2012 11:51 AM, James Burke wrote:
> On Thu, Feb 9, 2012 at 6:50 AM, Ryan Florence<rpflo...@gmail.com> wrote:
>> `require` is the only thing that makes any sense to me. What's the reason for pushback against it? Just "one more global" or is it something else?
> It seems that it can be confusing to talk about a global require vs. a
> local require. I can see the point, but I like that the understanding
> of the local require carries over into global require use.

Many of you have heard me rant about how we can't concern ourselves too
much with confused people so long as our API is rational. If a
programmer doesn't understand the difference between a global variable
(e.g., global require) and a lexically scoped variable (e.g., local
require), then I don't hold much hope for us to be able to fix that
person's skills with a wonderful API.

And the behavior of the two versions of require is really
identical...they both resolve modules with respect to their "defining"
module. In the case of global require, the "defining" module happens to
be the global space or root space...so a relative module is nonsensical.

Also, I'm not seeing this as a big pain point.

> There is also a concern about conflicting with a node-type
> require(''), but that may not be an issue in practice. In Mozilla
> Jetpacks, they have a sync-style require('') for their modules. But in
> those cases, this sort of top level "start loading/tracing
> dependencies" calls are not needed, and config is specified outside
> the code, as I recall.
>
> Eric's idea of 'amd' is interesting, but I still think that is mixing
> up loader concerns with module format concerns -- the config() and top
> level loading calls are loader concerns, where the AMD specs are
> around module definition APIs. 'amdload' might be more accurate. But
> then it acts a lot like require()...
>
> It would be good to get input from John Hann, Rawld, Richard
> Backhouse, other loader implementers.

I favor global require. If John (curl) doesn't want to do that, I think
that is fine. As he points out, any user switching to curl would just
need to alias a variable. That said, I'm not against some of the other
alternatives like amd().

I am against overloading define(). define's semantics are clear and
simple. Tarnishing that under the cover of unconfusing the confused
would be a shame.

--Rawld


Rawld

unread,
Mar 6, 2012, 4:57:42 AM3/6/12
to amd-im...@googlegroups.com
On 03/03/2012 07:42 AM, John Hann wrote:
> Hey Taka,
>
> I can appreciate wanting to keep `require` out of the global scope. curl.js doesn't declare it globally by default to avoid confusion and common noob mistakes.
>
> However, adding application bootstrap methods to the local require seems like the wrong direction to me. Here's why:
>
> 1. config() should be a one-time event, not something that devs feel they can do at any time. Allowing config at any time could easily make the app nondeterministic.
I disagree with this pov. Multiple configs could be rational and lib
devs should not presume to limit users from expressing solns under the
cover of "protecting users from themselves".

> 2. Adding more methods to the local require will not help us or our devs migrate to evolving module standards. The more we add to the local require, the harder it's going to be for all of us. curl.js only supports the standardized local require. There are no .config or .ready or other things on it.
>
> Just declare a separate global for app bootstrap. That solves the problem. The few devs who need to create cross-loader code can do it by casting the loader-specific global (e.g. `curl`) to `require` quite easily.

Generally agree. Though I think we need a standard way to pass config to
the loader.

--Rawld

Richard Backhouse

unread,
Mar 6, 2012, 5:33:39 AM3/6/12
to amd-im...@googlegroups.com
On 3/6/12 4:51 AM, Rawld wrote:
> Many of you have heard me rant about how we can't concern ourselves
> too much with confused people so long as our API is rational. If a
> programmer doesn't understand the difference between a global variable
> (e.g., global require) and a lexically scoped variable (e.g., local
> require), then I don't hold much hope for us to be able to fix that
> person's skills with a wonderful API.
>
> And the behavior of the two versions of require is really
> identical...they both resolve modules with respect to their "defining"
> module. In the case of global require, the "defining" module happens
> to be the global space or root space...so a relative module is
> nonsensical.
>
> Also, I'm not seeing this as a big pain point.
>
> --Rawld
>
>
Rawld, my biggest concern with the global require was how easy it was to
forget to include the ref in the define signature and use the global one
when the define version was expected. I haven't checked recently but
there were still places in the dojo codebase where this mistake had been
made. I would not consider the dojo contributors novice developers.

Richard

Ben Hockey

unread,
Mar 6, 2012, 10:17:25 AM3/6/12
to amd-im...@googlegroups.com


On Tuesday, March 6, 2012 5:33:39 AM UTC-5, rbackhouse wrote:

Rawld, my biggest concern with the global require was how easy it was to
forget to include the ref in the define signature and use the global one
when the define version was expected. I haven't checked recently but
there were still places in the dojo codebase where this mistake had been
made. I would not consider the dojo contributors novice developers.

Richard

as a side note: i just did a quick scan of dojo and dijit (ignoring tests) and i didn't find any problems.  i did find one inconsistency in dijit with respect to a pattern that is used in a number of places and the global require is used once (dijit/form/_FormWidget) vs the module require in other places - that might be the kind of thing you're referring to - but the logic wouldn't be affected because absolute module ids are being used.  i try to keep an eye on dojo and dijit to keep them sanitized so if you happen to find any inappropriate uses be sure to file a bug.

ben...

Rawld

unread,
Mar 6, 2012, 12:20:32 PM3/6/12
to amd-im...@googlegroups.com
Yeah, just to dovetail ben's comment...the dojo code base was contorted pretty badly as we tried to evolve the base at the same time the amd spec was evolving *and* mx backcompat. This situation is a lot different than the average dev using amd for the first time.

Also, assume you failed to specify the "require" dependency and then went on to use require() within a module factory...expecting a context require, yet actually using a global require. In that case there are two potential probs: 1) relative modules won't resolve, or 2) mapping is done with respect to the global context compared to a package context.

1) will often result in a 404 error
2) I agree could be vexing. But, then again, users doing per-package mapping are pretty advanced.

Also, how often do you actually need context require? Not never, but not that often either.

I agree that you've illuminated the key tripping point...which itself is critically important and should be highlighted in docs. I'm still not convinced it's enough to argue against global require. But, there is no way to prove right or wrong here...this issue is clearly a matter of taste. I'm willing to go the way of the group without resistance...with the exception of extending define(), which I think is dead wrong.

--Rawld

Eric Bréchemier

unread,
Apr 27, 2012, 9:31:13 AM4/27/12
to amd-im...@googlegroups.com
Can we move on and agree on a wording for the specification of the
global require?

The current specification [1] states that

"A global require function is one that is available in the global
scope, similar to define(). The behavior of global require() is not
defined by this API, and for interoperability it should not be
assumed. Normally there is an implementation-dependent API that will
kick off module loading."

I propose to replace it with:

"A global require() function is one that is available in the global
scope, like define(). The behavior of global require is similar to the
behavior of the local require() function, with the exception of ids
that are treated as absolute instead of being resolved relatively to
the id of the current module.

There is often an implementation-dependent API that will kick off
module loading; if interoperability with several loaders is needed,
the global require() should be used to load the top level modules
instead."

Please discuss and rephrase as needed to reach agreement.

Cheers,

Eric

--

[1] require -- amdjs/amdjs-api
https://github.com/amdjs/amdjs-api/wiki/require

James Burke

unread,
Apr 27, 2012, 12:48:42 PM4/27/12
to amd-im...@googlegroups.com
On Fri, Apr 27, 2012 at 6:31 AM, Eric Bréchemier
<eric.br...@gmail.com> wrote:
> I propose to replace it with:
>
> "A global require() function is one that is available in the global
> scope, like define(). The behavior of global require is similar to the
> behavior of the local require() function, with the exception of ids
> that are treated as absolute instead of being resolved relatively to
> the id of the current module.
>
> There is often an implementation-dependent API that will kick off
> module loading; if interoperability with several loaders is needed,
> the global require() should be used to load the top level modules
> instead."

That works for me. That is how it is now, at least for dojo and requirejs.

Rawld, John, Richard, anyone else?

James

Richard Backhouse

unread,
Apr 30, 2012, 7:03:14 AM4/30/12
to amd-im...@googlegroups.com
On 4/27/12 12:48 PM, James Burke wrote:
> On Fri, Apr 27, 2012 at 6:31 AM, Eric Br�chemier
Looks good although I still feel that more detail would help concerning
using the global require() in sync mode. Perhaps stating that the async
format should only be supported ? The recent discussion around phonegap
demonstrates that there is still confusion.

Richard

James Burke

unread,
Jun 26, 2012, 9:40:06 AM6/26/12
to amd-im...@googlegroups.com
On Mon, Apr 30, 2012 at 4:03 AM, Richard Backhouse
<richarda...@gmail.com> wrote:
> Looks good although I still feel that more detail would help concerning
> using the global require() in sync mode. Perhaps stating that the async
> format should only be supported ? The recent discussion around phonegap
> demonstrates that there is still confusion.

I finally updated the require() wiki to include a variation of what
Eric proposed for global require, that also mentions that only the
async callback style of require is expected for interoperation. See
this section:

https://github.com/amdjs/amdjs-api/wiki/require#wiki-localGlobal

James

Eric Bréchemier

unread,
Jun 27, 2012, 8:33:44 AM6/27/12
to amd-im...@googlegroups.com
On Tue, Jun 26, 2012 at 3:40 PM, James Burke <jrb...@gmail.com> wrote:
> (....)
> I finally updated the require() wiki to include a variation of what
> Eric proposed for global require, that also mentions that only the
> async callback style of require is expected for interoperation. See
> this section:
>
> https://github.com/amdjs/amdjs-api/wiki/require#wiki-localGlobal

Thanks James.

I posted a reply to the original question on Github [1].
I think that you may now close the issue.

Cheers,

Eric

--

[1] Document entry point for a multi-module program
https://github.com/amdjs/amdjs-api/issues/8
Reply all
Reply to author
Forward
0 new messages