Modules 1.1 comments

131 views
Skip to first unread message

Mikeal Rogers

unread,
Mar 22, 2010, 2:06:11 PM3/22/10
to comm...@googlegroups.com
In reviewing the Modules 1.1 specification I ran in to a few hickups trying to figure out what some of the points mean within the CouchDB view server implementation.

CouchDB is a weird case, modules aren't loaded over http or the filesystem but instead are strings attached to the design document. This is nice because we don't have to do any IO to load modules and each design document becomes it's own sandbox.  This case is a little odd next to the existing Modules implementations but as we create more sandboxed implementations this approach might be more common since it allows a sandbox to exclude access to sockets and the filesystem entirely.
1.5 The "require" function may have a "main" property that is read-only, don't delete and represents the top-level "module" object of the program. If this property is provided, it must be referentially identical to the "module" object of the main program.
Ok, so I have two problems with this. I don't know of a way other than defineProperty that allows you to set a read-only don't delete attribute like this so does this mean this specification only works with ES5?

The other problem I have is that the whole idea of a "main" is specific to SSJS, which is fine if the property is not required but I would hope that if not applicable require.main would be set to null for easier comparison operations and the attribute actually be required.  

In fact, when attempting to use this attribute in a Modules 1.0 implementation ( require.main === module ) is always true because both are undefined, which is exactly the opposite behavior you would want.
3.1 The "module" object must have a read-only, don't delete "id" property that is the top-level "id" of the module. The "id" property must be such that require(module.id) will return the exports object from which the module.id originated. (That is to say module.id can be passed to another module, and requiring that must return the original module).
Again, read-only === ES5.
3.2 The "module" object may have a "uri" String that is the fully-qualified URI to the resource from which the module was created. The "uri" property must not exist in a sandbox.
Does this mean that the attribute must start with http:// or file:// or some other standard URI prefix? This clearly isn't applicable when importing packages inside of a sandbox that doesn't allow IO which is fine since it's optional but this attribute might be in conflict with point 1 in the Unspecified section which states this specification does not define how modules are stored.

Ash Berlin

unread,
Mar 22, 2010, 2:30:24 PM3/22/10
to comm...@googlegroups.com
On 22 Mar 2010, at 18:06, Mikeal Rogers wrote:
In reviewing the Modules 1.1 specification I ran in to a few hickups trying to figure out what some of the points mean within the CouchDB view server implementation.

CouchDB is a weird case, modules aren't loaded over http or the filesystem but instead are strings attached to the design document. This is nice because we don't have to do any IO to load modules and each design document becomes it's own sandbox.  This case is a little odd next to the existing Modules implementations but as we create more sandboxed implementations this approach might be more common since it allows a sandbox to exclude access to sockets and the filesystem entirely.
1.5 The "require" function may have a "main" property that is read-only, don't delete and represents the top-level "module" object of the program. If this property is provided, it must be referentially identical to the "module" object of the main program.
Ok, so I have two problems with this. I don't know of a way other than defineProperty that allows you to set a read-only don't delete attribute like this so does this mean this specification only works with ES5?

*All* JS embeddings let you set this that the C level. If this really can't be done from within CouchDB its not a gross abuse to just set it as a normal property.


The other problem I have is that the whole idea of a "main" is specific to SSJS, which is fine if the property is not required but I would hope that if not applicable require.main would be set to null for easier comparison operations and the attribute actually be required.  

A property not present and setting to to undefined (not null) is very similar. The only difference is the the "in" operator. In what cases does the property being null differ from the implicit undefined value?


In fact, when attempting to use this attribute in a Modules 1.0 implementation ( require.main === module ) is always true because both are undefined, which is exactly the opposite behavior you would want.

You'd have to check that the module is not undefined first, but hopefully there will be fewer 1.0 impls out there going forward so this problem should vanish over time.

3.1 The "module" object must have a read-only, don't delete "id" property that is the top-level "id" of the module. The "id" property must be such that require(module.id) will return the exports object from which the module.id originated. (That is to say module.id can be passed to another module, and requiring that must return the original module).
Again, read-only === ES5.

Same case as first point - its mainly to make things behave a bit tighter but is not strictly needed.

3.2 The "module" object may have a "uri" String that is the fully-qualified URI to the resource from which the module was created. The "uri" property must not exist in a sandbox.
Does this mean that the attribute must start with http:// or file:// or some other standard URI prefix? This clearly isn't applicable when importing packages inside of a sandbox that doesn't allow IO which is fine since it's optional but this attribute might be in conflict with point 1 in the Unspecified section which states this specification does not define how modules are stored.

No, any scheme: you like is perfectly valid. Something like "couchdb://db/collection/id#moduleName" would be fine, or any of the many possible ways you could combine/generate something unique. Fully qualified just means 'make it as global as is reasonable for your embedding' (or thats what I meant it to mean - I added the .url property)

-ash

Mikeal Rogers

unread,
Mar 22, 2010, 2:56:31 PM3/22/10
to comm...@googlegroups.com
On Mon, Mar 22, 2010 at 11:30 AM, Ash Berlin <ash_flu...@firemirror.com> wrote:
On 22 Mar 2010, at 18:06, Mikeal Rogers wrote:
In reviewing the Modules 1.1 specification I ran in to a few hickups trying to figure out what some of the points mean within the CouchDB view server implementation.

CouchDB is a weird case, modules aren't loaded over http or the filesystem but instead are strings attached to the design document. This is nice because we don't have to do any IO to load modules and each design document becomes it's own sandbox.  This case is a little odd next to the existing Modules implementations but as we create more sandboxed implementations this approach might be more common since it allows a sandbox to exclude access to sockets and the filesystem entirely.
1.5 The "require" function may have a "main" property that is read-only, don't delete and represents the top-level "module" object of the program. If this property is provided, it must be referentially identical to the "module" object of the main program.
Ok, so I have two problems with this. I don't know of a way other than defineProperty that allows you to set a read-only don't delete attribute like this so does this mean this specification only works with ES5?

*All* JS embeddings let you set this that the C level. If this really can't be done from within CouchDB its not a gross abuse to just set it as a normal property.

requiring modification at the C level to support Modules 1.1 seems like an unnecessary barrier to entry. All I'm saying is that the spec should call out that some implementations SHOULD set this attribute to read-only don't delete but that failing to do so does not mean you are out of compliance with the spec.
 


In fact, when attempting to use this attribute in a Modules 1.0 implementation ( require.main === module ) is always true because both are undefined, which is exactly the opposite behavior you would want.

You'd have to check that the module is not undefined first, but hopefully there will be fewer 1.0 impls out there going forward so this problem should vanish over time.


So, here is the problem. I write my module for Modules 1.1 assuming the environment is 1.1 compatible. But people running the module on a 1.0 environment will get behavior that is exactly the opposite of what I intended. I now, as the developer, have to add additional code to provide reverse compatibility with Modules 1.0.

I just think that there is a way to do this cleanly that is backwards compatible with 1.0.



3.2 The "module" object may have a "uri" String that is the fully-qualified URI to the resource from which the module was created. The "uri" property must not exist in a sandbox.
Does this mean that the attribute must start with http:// or file:// or some other standard URI prefix? This clearly isn't applicable when importing packages inside of a sandbox that doesn't allow IO which is fine since it's optional but this attribute might be in conflict with point 1 in the Unspecified section which states this specification does not define how modules are stored.

No, any scheme: you like is perfectly valid. Something like "couchdb://db/collection/id#moduleName" would be fine, or any of the many possible ways you could combine/generate something unique. Fully qualified just means 'make it as global as is reasonable for your embedding' (or thats what I meant it to mean - I added the .url property)

Ok, maybe we can get this example added to the spec for better clarification.

Wes Garland

unread,
Mar 22, 2010, 3:09:50 PM3/22/10
to comm...@googlegroups.com
On Mon, Mar 22, 2010 at 2:06 PM, Mikeal Rogers <mikeal...@gmail.com> wrote:
Ok, so I have two problems with this. I don't know of a way other than defineProperty that allows you to set a read-only don't delete attribute like this so does this mean this specification only works with ES5?

JS_DefineProperty() will do this with flags JSPROP_READONLY | JSPROP_PERMANENT

The other problem I have is that the whole idea of a "main" is specific to SSJS, which is fine if the property is not required but I would hope that if not applicable require.main would be set to null for easier comparison operations and the attribute actually be required. 

"main" is whatever context the top-most require() runs in.  You can't have modules in isolation, something has to require them. That's main.


3.2 The "module" object may have a "uri" String that is the fully-qualified URI to the resource from which the module was created. The "uri" property must not exist in a sandbox.
Does this mean that the attribute must start with http:// or file:// or some other standard URI prefix? This clearly isn't applicable when importing packages inside of a sandbox that doesn't allow IO which is fine since it's optional but this attribute might be in conflict with point 1 in the Unspecified section which states this specification does not define how modules are stored.

"may" -- you can ignore that clause and still be compliant.

Wes

--
Wesley W. Garland
Director, Product Development
PageMail, Inc.
+1 613 542 2787 x 102

Daniel Friesen

unread,
Mar 22, 2010, 3:10:13 PM3/22/10
to comm...@googlegroups.com
Mikeal Rogers wrote:
> ...

>
> 1.5 The "require" function may have a "main" property that is
> read-only, don't delete and represents the top-level "module"
> object of the program. If this property is provided, it must be
> referentially identical to the "module" object of the main program.
>
> Ok, so I have two problems with this. I don't know of a way other than
> defineProperty that allows you to set a read-only don't delete
> attribute like this so does this mean this specification only works
> with ES5?
>
> The other problem I have is that the whole idea of a "main" is
> specific to SSJS, which is fine if the property is not required but I
> would hope that if not applicable require.main would be set to null
> for easier comparison operations and the attribute actually be required.
>
> In fact, when attempting to use this attribute in a Modules 1.0
> implementation ( require.main === module ) is always true because both
> are undefined, which is exactly the opposite behavior you would want.
If modules is not defined shouldn't that code raise a ReferenceError
unless module was explicitly defined using var module; in that scope or
was present in the global object?

~Daniel Friesen (Dantman, Nadir-Seen-Fire) [http://daniel.friesen.name]

Mikeal Rogers

unread,
Mar 22, 2010, 4:39:54 PM3/22/10
to comm...@googlegroups.com
On Mon, Mar 22, 2010 at 12:09 PM, Wes Garland <w...@page.ca> wrote:

On Mon, Mar 22, 2010 at 2:06 PM, Mikeal Rogers <mikeal...@gmail.com> wrote:
Ok, so I have two problems with this. I don't know of a way other than defineProperty that allows you to set a read-only don't delete attribute like this so does this mean this specification only works with ES5?

JS_DefineProperty() will do this with flags JSPROP_READONLY | JSPROP_PERMANENT

The other problem I have is that the whole idea of a "main" is specific to SSJS, which is fine if the property is not required but I would hope that if not applicable require.main would be set to null for easier comparison operations and the attribute actually be required. 

"main" is whatever context the top-most require() runs in.  You can't have modules in isolation, something has to require them. That's main.

It is entirely possible to define a sandbox with a require function that itself cannot be required. For instance, CouchDB show and list definitions MUST be function literals and are not expected to be valid CommonJS modules but they do have a require function that can import CommonJS modules defined within the same design document.

The assumption should not be that ANY environment that has a require function is itself a valid module, only that a module that is imported with a require statement is a valid module.

Since the "main" environment in this case is not a valid CommonJS module I can't support this attribute.

-Mikeal

Kris Kowal

unread,
Mar 22, 2010, 5:42:11 PM3/22/10
to comm...@googlegroups.com
On Mon, Mar 22, 2010 at 11:06 AM, Mikeal Rogers <mikeal...@gmail.com> wrote:
> In reviewing the Modules 1.1 specification I ran in to a few hickups trying
> to figure out what some of the points mean within the CouchDB view server
> implementation.

I think you've made a lot of good points. If you have the time to
fork Modules/1.1 (and also Packages/1.0) as a lettered proposal and
then make some changes (so we can watch the diffs), I think we could
be on our way to another point revision of both.

In verbiage, I'd rather not introduce the "should" moral imperative.
Let's use "may" or "must" strictly, and fill out any surrounding
criteria so the behaviors are detectable.

You raise a good point about the require.main == module or
require.main === module idiom (I'm using == in Narwhal since we have a
migration shim from the older proposal where require.main ===
module.id). We should specify a working solution for systems that
have no "main", preferably backward compatible. I'd like to hear
ideas.

Kris Kowal

Irakli Gozalishvili

unread,
Mar 22, 2010, 8:02:14 PM3/22/10
to comm...@googlegroups.com
Hi Mikeal,

I think in order to create read only properties in non es5 env you can use __defineGetter__ and return a property from getter.


--
You received this message because you are subscribed to the Google Groups "CommonJS" group.
To post to this group, send email to comm...@googlegroups.com.
To unsubscribe from this group, send email to commonjs+u...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/commonjs?hl=en.

Wes Garland

unread,
Mar 22, 2010, 8:40:11 PM3/22/10
to comm...@googlegroups.com
> It is entirely possible to define a sandbox with a require function that itself cannot be required

Not if I read the modules specification correctly when I was implementing -- there is even a test suite test that tests this, I believe.

Now I'm not saying this is good or bad -- just pointing it out in case the modules proposal gets evolved one more step.


> For instance, CouchDB show and list definitions MUST be function literals and are not
> expected to be valid CommonJS modules

What scope object do you use for evaluating these functions?  Do you execute them with your global object as the scope object?

How I set GPSEE up is to make the global object a slightly special-cased module scope; it has exports, module, and require just like any other module scope.   It's slightly special-cased because a) it doesn't executed as the result of a require statement, and b) I take steps make sure require(module.id) works as expected.

Incidentally, are you supporting arbitrary native modules, or only JS-language modules?  If you want to support native modules, it's probably worth our while to work together, at least at the module C API level.

Mikeal Rogers

unread,
Mar 22, 2010, 11:22:01 PM3/22/10
to comm...@googlegroups.com
On Mon, Mar 22, 2010 at 5:40 PM, Wes Garland <w...@page.ca> wrote:
> It is entirely possible to define a sandbox with a require function that itself cannot be required

Not if I read the modules specification correctly when I was implementing -- there is even a test suite test that tests this, I believe.

Now I'm not saying this is good or bad -- just pointing it out in case the modules proposal gets evolved one more step.

I'll have to look at all the tests before I take a stab at the next version of the spec.
 


> For instance, CouchDB show and list definitions MUST be function literals and are not
> expected to be valid CommonJS modules

What scope object do you use for evaluating these functions?  Do you execute them with your global object as the scope object?

That's a little bit complicated. Show and List functions are evaled in a sandbox with evalcx and then run with .apply(ddoc, arguments).

The eval for require is quite different:

http://github.com/apache/couchdb/blob/trunk/share/server/util.js#L53

It's wrapped in a function and evaled with evalcs in a sandbox, then it's called with .apply(sandbox, arguments)

The tricky part is wrapping a new require function for each module with a reference to it's parent since there is no other way to keep track of where the module was imported in the property tree to support relative imports.
 

How I set GPSEE up is to make the global object a slightly special-cased module scope; it has exports, module, and require just like any other module scope.   It's slightly special-cased because a) it doesn't executed as the result of a require statement, and b) I take steps make sure require(module.id) works as expected.

Incidentally, are you supporting arbitrary native modules, or only JS-language modules?  If you want to support native modules, it's probably worth our while to work together, at least at the module C API level.

We don't do anything in C. It's all stuff that we pipe directly to the spidermonkey js process which is started as a subprocess from Erlang. At some later date we may run this directly inside of an Erlang "process" using erlang_js.

-Mikeal

Mikeal Rogers

unread,
Mar 22, 2010, 11:25:33 PM3/22/10
to comm...@googlegroups.com
On Mon, Mar 22, 2010 at 2:42 PM, Kris Kowal <cowber...@gmail.com> wrote:
On Mon, Mar 22, 2010 at 11:06 AM, Mikeal Rogers <mikeal...@gmail.com> wrote:
> In reviewing the Modules 1.1 specification I ran in to a few hickups trying
> to figure out what some of the points mean within the CouchDB view server
> implementation.

I think you've made a lot of good points.  If you have the time to
fork Modules/1.1 (and also Packages/1.0) as a lettered proposal and
then make some changes (so we can watch the diffs), I think we could
be on our way to another point revision of both.

I'm not hugely familiar with mediawiki, is there a way to do this other than just cut and paste?
 

In verbiage, I'd rather not introduce the "should" moral imperative.
Let's use "may" or "must" strictly, and fill out any surrounding
criteria so the behaviors are detectable.

In the case that a certain behavior is not required but encouraged is there a specific verbiage we should use?
 

You raise a good point about the require.main == module or
require.main === module idiom (I'm using == in Narwhal since we have a
migration shim from the older proposal where require.main ===
module.id).  We should specify a working solution for systems that
have no "main", preferably backward compatible.  I'd like to hear
ideas.

One solution is to set require.main to {} when not applicable. I don't like the way that it looks, but it's truthy so it won't compare well against undefined unlike null.

-Mikeal

Kris Kowal

unread,
Mar 23, 2010, 12:09:51 AM3/23/10
to comm...@googlegroups.com
On Mon, Mar 22, 2010 at 8:25 PM, Mikeal Rogers <mikeal...@gmail.com> wrote:
> I'm not hugely familiar with mediawiki, is there a way to do this other than
> just cut and paste?

Alas, nothing better than copy and paste.

> In the case that a certain behavior is not required but encouraged is there
> a specific verbiage we should use?

Not really. We don't have any formal guidelines or patterns. I try to
keep non-normative, untestable stuff in an italicized ''Note:'' block
or in a footnote.

> One solution is to set require.main to {} when not applicable. I don't like
> the way that it looks, but it's truthy so it won't compare well against
> undefined unlike null.

Sounds good to me.

Kris Kowal

Mikeal Rogers

unread,
Mar 23, 2010, 9:28:33 PM3/23/10
to comm...@googlegroups.com
Ok, I "forked" the 1.1 specification and wrote up a draft for 1.2

http://wiki.commonjs.org/wiki/Modules/1.2

I also made sure to stagger and comment my changes so it's easy to view the diffs from the 1.1 proposal

http://wiki.commonjs.org/index.php?title=Modules/1.2&action=history

My goal with with this draft is to only refine key points of the 1.1 specification to allow CouchDB and future implementations to write compliant implementations and not to introduce new features or deprecations so that most, if not all, existing 1.1 implementations would already be compliant with 1.2

-Mikeal



On Mon, Mar 22, 2010 at 2:42 PM, Kris Kowal <cowber...@gmail.com> wrote:

Daniel Friesen

unread,
Mar 23, 2010, 9:50:01 PM3/23/10
to comm...@googlegroups.com
"If the top-level object of a program is not a module this attribute
must be set to an object even if it is empty."
I don't quite like the wording of "an object", defining what it is more
would be nice.

Also perhaps a side note, but if implementations are using an actual
Module class (the interoperablejs tests used to have tests for a
require.Module) and making module objects instances of that, it would be
nice for the object to have the same prototype.

~Daniel Friesen (Dantman, Nadir-Seen-Fire) [http://daniel.friesen.name]

> module.id <http://module.id>). We should specify a working


> solution for systems that
> have no "main", preferably backward compatible. I'd like to hear
> ideas.
>
> Kris Kowal
>
> --
> You received this message because you are subscribed to the Google
> Groups "CommonJS" group.
> To post to this group, send email to comm...@googlegroups.com

> <mailto:comm...@googlegroups.com>.


> To unsubscribe from this group, send email to
> commonjs+u...@googlegroups.com

> <mailto:commonjs%2Bunsu...@googlegroups.com>.

Kris Kowal

unread,
Mar 23, 2010, 9:53:50 PM3/23/10
to comm...@googlegroups.com
On Tue, Mar 23, 2010 at 6:28 PM, Mikeal Rogers <mikeal...@gmail.com> wrote:
> Ok, I "forked" the 1.1 specification and wrote up a draft for 1.2
>
> http://wiki.commonjs.org/wiki/Modules/1.2
>
> I also made sure to stagger and comment my changes so it's easy to view the
> diffs from the 1.1 proposal
>
> http://wiki.commonjs.org/index.php?title=Modules/1.2&action=history
>
> My goal with with this draft is to only refine key points of the 1.1
> specification to allow CouchDB and future implementations to write compliant
> implementations and not to introduce new features or deprecations so that
> most, if not all, existing 1.1 implementations would already be compliant
> with 1.2

Thanks, these revisions look great.

The only verbiage that worries me is "top-level object". This could
mean a variety of things, some of which are terribly wrong. I think
this has to mean: "the "module" object in the context of the first
module instantiated of the running program. If you agree, I'll patch
the draft myself.

I also don't think it's necessary to equivocate about what "main"
should be if there is no main. I think it's fine to state explicitly
that it must be an empty object, maybe even something specific so
people can test whether there is a main; though, the obvious
candidate, "global", is flawed since there will likely be future ES
strict worlds where such an object does not exist.

Kris Kowal

Wes Garland

unread,
Mar 23, 2010, 10:39:35 PM3/23/10
to comm...@googlegroups.com
Mikeal;

On Mon, Mar 22, 2010 at 11:22 PM, Mikeal Rogers <mikeal...@gmail.com> wrote:
The tricky part is wrapping a new require function for each module with a reference to it's parent since there is no other way to keep track of where the module was imported in the property tree to support relative imports.

I fell for that trap the first time I implemented CommonJS modules in GPSEE.   It turns out that you can completely break the require/parent relationship if you instanciate a new require for each scope (evalcx sandbox in your case) and have each instance of require know it's canonical module name.  In my case I buried the information in the 1st reserved slot on the C side. If your require is pure-JS, you could presumably use a non-enumerable property or something.

We don't do anything in C. It's all stuff that we pipe directly to the spidermonkey js process which is started as a subprocess from Erlang. At some later date we may run this directly inside of an Erlang "process" using erlang_js.

You may want to re-think your approach of using the JS shell + evalcx for this;
<jorendorff>    ...doing what? I hope nobody's using evalcx for ...anything
<jorendorff>    it's meant as a testing knob
<Wes->    jorendorff: yes, couchdb is using it to implement commonjs modules
<jorendorff>    Wes-: It's a testing version of https://developer.mozilla.org/En/Components.utils.evalInSandbox
<jorendorff>    Wes-: You might want to warn them that that's rather crazy...
<jorendorff>    um, anyway, I think you'll find instanceof works the same way as in the browser;
<jorendorff>    which is to say, each module has its own Array.prototype

At the very least, it is generally expected in CommonJS that things like instanceof do not break across the module boundary; I'm not sure if it made it into the spec but it's well understood and certainly violates the law of least surprises:

js> var moduleScope = evalcx('');
js> var moduleExports = evalcx('var exports={a:[]};', moduleScope);
js> print(moduleScope.a instanceof Array)
false

If you're interested in coming up with a replace for the JS shell that behaves a little bit differently (better support for CommonJS), be sure to hit me up for info/code/help.  You know where to find me on IRC. I think couch is a great product, and am happy to lend a hand as time permits.

Wes

-- 

Mikeal Rogers

unread,
Mar 23, 2010, 11:24:51 PM3/23/10
to comm...@googlegroups.com
On Tue, Mar 23, 2010 at 6:53 PM, Kris Kowal <cowber...@gmail.com> wrote:
On Tue, Mar 23, 2010 at 6:28 PM, Mikeal Rogers <mikeal...@gmail.com> wrote:
> Ok, I "forked" the 1.1 specification and wrote up a draft for 1.2
>
> http://wiki.commonjs.org/wiki/Modules/1.2
>
> I also made sure to stagger and comment my changes so it's easy to view the
> diffs from the 1.1 proposal
>
> http://wiki.commonjs.org/index.php?title=Modules/1.2&action=history
>
> My goal with with this draft is to only refine key points of the 1.1
> specification to allow CouchDB and future implementations to write compliant
> implementations and not to introduce new features or deprecations so that
> most, if not all, existing 1.1 implementations would already be compliant
> with 1.2

Thanks, these revisions look great.

The only verbiage that worries me is "top-level object".  This could
mean a variety of things, some of which are terribly wrong.  I think
this has to mean: "the "module" object in the context of the first
module instantiated of the running program.  If you agree, I'll patch
the draft myself.

I wavered about what to say here.

What we really mean is the highest level scope that executes require(). In the case that this scope is a module, we mean the module, but in the case that a require statement is given to a sandbox environment that itself is not a module then it wouldn't mean "module".

Saying "first module instantiated of the running program" is misleading for the CouchDB case since the top level execution environment isn't an instantiated module.

To illustrate the problem this is what section 1.5.3 says using "top-level module": "If the top-level module of a program is not a module this attribute must be set to an object even if it is empty. "

I'm sure there is a way to phrase this that is less confusing but I'm sure some additions would need to be made to the later 1.5.2 and 1.5.3 sections to make them more explicit.


I also don't think it's necessary to equivocate about what "main"
should be if there is no main. I think it's fine to state explicitly
that it must be an empty object, maybe even something specific so
people can test whether there is a main; though, the obvious
candidate, "global", is flawed since there will likely be future ES
strict worlds where such an object does not exist.

So, what I mean to say is "this must at least be {} but you can do whatever you want if your top-level scope is not a module" but perhaps the phrasing is inadequate currently as: "If the top-level object of a program is not a module this attribute must be set to an object even if it is empty."

-Mikeal

Kris Kowal

unread,
Mar 24, 2010, 1:10:05 AM3/24/10
to comm...@googlegroups.com
Mikeal,

The idea you're expressing in this proposal is indeed more subtle than
I was expecting. Thanks.

On Tue, Mar 23, 2010 at 8:24 PM, Mikeal Rogers
<mikeal...@gmail.com> wrote:
>> The only verbiage that worries me is "top-level object".  This
>> could mean a variety of things, some of which are terribly wrong.
>>  I think this has to mean: "the "module" object in the context of
>> the first module instantiated of the running program.  If you
>> agree, I'll patch the draft myself.

> So, what I mean to say is "this must at least be {} but you can do whatever


> you want if your top-level scope is not a module" but perhaps the phrasing
> is inadequate currently as: "If the top-level object of a program is not a
> module this attribute must be set to an object even if it is empty."

Taking a step back, the known purposes of require.main are:

1.) within a module, to detect whether the module is the entry point,
which dictates whether to take control of the program.

if (require.main == module) …

2.) to require the main module, which is handy for grabbing
configuration from its exports.

require(require.main.id)

Neither of these use-cases are relevant outside a module. In CouchDB,
the first case should never be trigged in a module, and the second
would never be done inside a module, and require() would have to throw
an error. In both cases, it would be sufficent for main to be an
empty object. It would be potentially misleading for it to be an
object with an "id" property, since the second case must fail.

So, I'm not sure it would be good to permit extensions to the "main"
object if it's not related to a real module. I'm interested if you
have a different use for "main".

I can see why you wouldn't want "the first module to be executed" to
provide the "main" object. The same argument could be made for
browsers, although I personally prefer there to be a single entry
point module for my client-side code, with no inline scripts or
wrapper scripts filled with islanded code. It might be possible to
achieve something like that if the external "require" function were
only permitted to be called once, to specify an entry point, but
that's entirely up to you as far as I'm concerned. Perhaps:

The "require" function may have a "main" property. The "main"
property must be an Object. The "main" object may be either empty
or be the "module" object in the context of one loaded module.

Kris Kowal

Mikeal Rogers

unread,
Mar 24, 2010, 1:39:01 AM3/24/10
to comm...@googlegroups.com
I can see the case for wanting to know information about top level scope even if it's not a module, but maybe the solution to that is to add a different property specific to the implementation and not try to cram it in to require.main.

For the CouchDB implementation I do think it would be nice if the module context had some information about the top-level scope (whether it's a show or list function, what the ddoc looks like) but the more I think about how to define these in main the more I realize it's just a bad idea and that I should just add some other custom property to require instead of overloading main.

+1 on your revision, it's much clearer


Kris Kowal

Kris Kowal

unread,
Mar 24, 2010, 1:53:14 AM3/24/10
to comm...@googlegroups.com
On Tue, Mar 23, 2010 at 10:39 PM, Mikeal Rogers <mikeal...@gmail.com> wrote:
> +1 on your revision, it's much clearer

Alright, here's the change:

http://wiki.commonjs.org/index.php?title=Modules%2F1.2&diff=2497&oldid=2494

Does anyone else have outstanding issues with these revisions? These
changes are very minor. I would venture to call it 1.1.1, or to back
port these changes over the existing 1.1 spec since I don't think they
will cause anyone to fall out of compatibility.

Kris Kowal

Daniel Friesen

unread,
Mar 24, 2010, 2:28:07 AM3/24/10
to comm...@googlegroups.com
I'm fine with .id being left out. I'm probably thinking of defining .url
on the require.main for any non-module main of my own.

I've had a strong issue with the restriction modules place on scope from
the beginning, and it makes things troublesome for my own code. So
MonkeyScript is being designed to work both with modules and classic
load style code (course they blend together very well). So the main
script executed can either be a module or a plain script (well actually
in the previous Lite you could also `monkeyscript
javascript:"print('foo');"` but that's a side point) in the case of
running a module require.main works as expected (potentially even using
an absolute file), but in the case of a normal script being executed
.main would be a non-module, but the main executed thing is still a file
on the system so I feel like putting the script's url into the .main.url
to match up with the behavior of when you execute a module.

~Daniel Friesen (Dantman, Nadir-Seen-Fire) [http://daniel.friesen.name]

> if (require.main == module) �


>
> 2.) to require the main module, which is handy for grabbing
> configuration from its exports.
>

> require(require.main.id <http://require.main.id>)

> <mailto:comm...@googlegroups.com>.


> To unsubscribe from this group, send email to
> commonjs+u...@googlegroups.com

> <mailto:commonjs%2Bunsu...@googlegroups.com>.

Ash Berlin

unread,
Mar 24, 2010, 8:08:45 AM3/24/10
to comm...@googlegroups.com

1.1.1 - using semver is good. (Of course there is nothing to say that going to here 1.2 is forbidden under semver)

Adding a .path property as well as a .uri property perhaps? I believe this has been mentioned in passing before. Path would be optional, uri (file:// if nothing else) should be required?

The other thing that is maybe worth adding is module.resource - use case is basicaly to access content next to a module on disk:

module.resource("image.jpg")

As it stands in flusspferd we have that returning a opened, readable stream, and module.resource.resolve(name) returning a path. But the feature is more important to me than the exact implementation details. Thoughts?

-ash

Wes Garland

unread,
Mar 24, 2010, 8:16:22 AM3/24/10
to comm...@googlegroups.com
On Wed, Mar 24, 2010 at 8:08 AM, Ash Berlin <ash_flu...@firemirror.com> wrote:
1.1.1 - using semver is good. (Of course there is nothing to say that going to here 1.2 is forbidden under semver)

+1.0.0
 
The other thing that is maybe worth adding is module.resource - use case is basicaly to access content next to a module on disk:

 module.resource("image.jpg")

I love this idea too.

Wes

Kris Kowal

unread,
Mar 24, 2010, 3:23:38 PM3/24/10
to comm...@googlegroups.com
On Wed, Mar 24, 2010 at 5:16 AM, Wes Garland <w...@page.ca> wrote:
>> 1.1.1 - using semver is good. (Of course there is nothing to say that
>> going to here 1.2 is forbidden under semver)
> +1.0.0

Ash, Wes,

Since we appear to agree about the work so far, I think we should
stamp and one of you ought to fork it. Adding .resource is definitely
a 1.2 feature, not just editorial. .uri and .path should not be
required, but we should specify them. But consensus isn't cheap and I
think we should reward Mikael with an expedient rev for his effort.

Kris Kowal

Hannes Wallnoefer

unread,
Mar 24, 2010, 4:40:00 PM3/24/10
to comm...@googlegroups.com
2010/3/24 Ash Berlin <ash_flu...@firemirror.com>:

>
> Adding a .path property as well as a .uri property perhaps? I believe this has been mentioned in passing before. Path would be optional, uri (file:// if nothing else) should be required?

In ringojs we have a module.directory property in addition to
module.path as a shortcut to fs.directory(module.path). It's quite
handy for a number of occasions, and I just mention that in case
somebody else thinks it's a good idea.

> The other thing that is maybe worth adding is module.resource - use case is basicaly to access content next to a module on disk:
>
>  module.resource("image.jpg")
>
> As it stands in flusspferd we have that returning a opened, readable stream, and module.resource.resolve(name) returning a path. But the feature is more important to me than the exact implementation details. Thoughts?

Would module.resource() use file or module semantics? In other words,
would "image.jpg" denote a resource relative to the current module, or
would that be a top-level resource and only "./image.jpg" be relative?
I'll argue that it has to be the latter, else it becomes impossible to
specify absolute ids/paths.

One limitation with module.resource() is that it is only usable from
within the module (apart from require.main, obviously). In ringo, we
have therefore chosen to implement this as an external utility
function called resolveId() that resolves a module id path relative to
another one:

http://ringojs.org/api/ringo/fileutils#resolveId

I think these are things to consider when adding an API/amendment for
accessing resources on the module path.

Hannes

> -ash

Ash Berlin

unread,
Mar 24, 2010, 7:25:47 PM3/24/10
to comm...@googlegroups.com
Double reply below.

On 24 Mar 2010, at 19:23, Kris Kowal wrote:
> On Wed, Mar 24, 2010 at 5:16 AM, Wes Garland <w...@page.ca> wrote:

>>> 1.1.1 - using semver is good. (Of course there is nothing to say that
>>> going to here 1.2 is forbidden under semver)

>> +1.0.0
>
> Ash, Wes,
>
> Since we appear to agree about the work so far, I think we should
> stamp and one of you ought to fork it. Adding .resource is definitely
> a 1.2 feature, not just editorial. .uri and .path should not be
> required, but we should specify them. But consensus isn't cheap and I
> think we should reward Mikael with an expedient rev for his effort.
>
> Kris Kowal

Good point.

On 24 Mar 2010, at 20:40, Hannes Wallnoefer wrote:
> 2010/3/24 Ash Berlin <ash_flu...@firemirror.com>:
>>
>> Adding a .path property as well as a .uri property perhaps? I believe this has been mentioned in passing before. Path would be optional, uri (file:// if nothing else) should be required?
>
> In ringojs we have a module.directory property in addition to
> module.path as a shortcut to fs.directory(module.path). It's quite
> handy for a number of occasions, and I just mention that in case
> somebody else thinks it's a good idea.

The way I envisage things module.directory would be the same as module.resource('.')

>
>> The other thing that is maybe worth adding is module.resource - use case is basicaly to access content next to a module on disk:
>>
>> module.resource("image.jpg")
>>
>> As it stands in flusspferd we have that returning a opened, readable stream, and module.resource.resolve(name) returning a path. But the feature is more important to me than the exact implementation details. Thoughts?
>
> Would module.resource() use file or module semantics? In other words,
> would "image.jpg" denote a resource relative to the current module, or
> would that be a top-level resource and only "./image.jpg" be relative?
> I'll argue that it has to be the latter, else it becomes impossible to
> specify absolute ids/paths.

File semantics i think. 'image.jpg' and './image.jpg' would both be the picture file that is in the same directory as the module. Except of course it could also return URL as string (or what ever else we decide) representing a HTTP resource, not just file on disk.

>
> One limitation with module.resource() is that it is only usable from
> within the module (apart from require.main, obviously). In ringo, we
> have therefore chosen to implement this as an external utility
> function called resolveId() that resolves a module id path relative to
> another one:
>
> http://ringojs.org/api/ringo/fileutils#resolveId
>
> I think these are things to consider when adding an API/amendment for
> accessing resources on the module path.

Agreed - the way I've gotten around this problem is to pass/require to be passed in the module object from the module in question. With the exception of the (proposed) setExports behaviour, is there any danger to providing a method to get this resource() behaviour for any module?

Kris Kowal

unread,
Mar 24, 2010, 7:37:07 PM3/24/10
to comm...@googlegroups.com
On Wed, Mar 24, 2010 at 4:25 PM, Ash Berlin
<ash_flu...@firemirror.com> wrote:
> Double reply below.
> [snip]

Let's start a new thread for .resource. I think this one has wrapped
around the planet a few times this week.

Kris Kowal

Reply all
Reply to author
Forward
0 new messages