Propsal: add setExports function in module API

6 views
Skip to first unread message

Isaac Z. Schlueter

unread,
Oct 4, 2009, 5:10:47 PM10/4/09
to nodejs
It would be very handy to have a way to set the exports object all at
once, rather than having to set properties one-by-one.

For instance, say you want to have one module that decorates another.

// bar.js
var f = require("/foo.js");
f.bar = function () { return "bar" };
// want to export everything in f, but can't without iterating all
over it,
// and breaking any "this" contexts

This would be kinda neat:

// bar.js
var f = require("/foo.js");
f.bar = ...
return f;

Of course, using return like this presumes that the module javascript
is being run inside a function--which it is in nodejs, but this feels
like modules are starting to get a bit too familiar with the
implementation details. Also, while nodejs does it this way, the
CommonJS projects might not be, and it's nice to have a fairly
interoperable require() function.

Also, return isn't just "set the results", it's also a break
statement. For those reasons, I think this might be a bit better:

// bar.js
var f = require("/foo.js");
f.bar = ...
setExports(f);

then you could call setExports(whatever) to "wipe clean", or you could
export something other than a simple object. (Ie, something with a
constructor, etc.)

The code change is fairly minor, but it's deep, so may have
ramifications I'm not seeing. I'd appreciate any feedback.

http://github.com/isaacs/node/commit/da3e31deb3eb7eb6d75dc57fef7d2916c3a2d54c

--i

Bluebie

unread,
Oct 4, 2009, 9:10:04 PM10/4/09
to nodejs
I believe if you actually set 'this = blah' it will have the effect of
replacing the eventually exported object, from what I understand of
the module system.
> http://github.com/isaacs/node/commit/da3e31deb3eb7eb6d75dc57fef7d2916...
>
> --i

Isaac Z. Schlueter

unread,
Oct 4, 2009, 9:45:01 PM10/4/09
to nodejs
On Oct 4, 6:10 pm, Bluebie <b...@creativepony.com> wrote:
> I believe if you actually set 'this = blah'

Nope. JavaScript does not allow assigning to "this".

node> f = require("/foo.js")
/Users/isaacs/.npm/foo.js:1
function (__filename, exports, require, include, setExports) { this =
require(
^^^^
SyntaxError: Invalid left-hand side in assignment
at [object Object].<anonymous> (node.js:231:32)
at [object Object].<anonymous> (file.js:35:22)
at [object Object].wait (events.js:52:7)
at require (node.js:207:31)
at [object Object].<anonymous> (eval at readline (/Users/isaacs/
dev/js/node/lib/repl.js:49:8))
at [object Object].readline (/Users/isaacs/dev/js/node/lib/repl.js:
49:18)

--i

Ryan Dahl

unread,
Oct 5, 2009, 5:54:37 AM10/5/09
to nod...@googlegroups.com
On Sun, Oct 4, 2009 at 11:10 PM, Isaac Z. Schlueter <i...@foohack.com> wrote:
>
> The code change is fairly minor, but it's deep, so may have
> ramifications I'm not seeing.  I'd appreciate any feedback.
>
> http://github.com/isaacs/node/commit/da3e31deb3eb7eb6d75dc57fef7d2916c3a2d54c

I like the idea.

I'd rather not add to the growing global namespace. Technically this
isn't global, but practically it is.

It would also be nice to be able to extend the exports by multiple
modules, like mixins. This would probably require copying objects over
- but that's okay - I think.

var x = require("/x.js");
var y = require("/y.js")
exports.foo = "bar";
node.mixin(exports, x);
node.mixin(exports, y);

?

Isaac Z. Schlueter

unread,
Oct 5, 2009, 1:59:57 PM10/5/09
to nodejs
I think a mixin sugar method is a great idea, but it belongs in a
separate module devoted to those kinds of language extension. (I have
a few methods that I always end up defining, mostly to easily create
callbacks and do currying/binding.)

However, that's not really the issue, since you can do that already.

Let's say, in foo.js, you have this:

exports.foo = function () { puts(this.bar) };
exports.bar = "baz";

So, this works as expected:

var f = require("foo.js");
f.bar = "asdf";
f.foo() // puts("asdf")

However, let's say you have bar.js, like this:

var f = require("foo.js");
for (var i in f) exports[i] = f[i];

If I wanted them to be the *same* object, then that doesn't work. For
instance:

var b = require("bar.js");
var f = require("foo.js");
f.bar = "asdf";
b.foo() // expect "asdf", actual "bar"

What I'm looking for is a way to include one file, and have it sort of
symlink to the exports object of another file. A filesystem symlink
between files won't work, because the relative paths will be messed
up. Also, I'd prefer to make this effect explicit, since it's not
something you'd want to do except in certain situations. (Like, for
instance, if you have a library file at /some/strange/location.js and
want to link it to /packagename.js.)

Another option, of course, is just to set the exports.__proto__.
However, I'm really looking for a reference, not a child, because that
would introduce oddness in hasOwnProperty() checks.

> I'd rather not add to the growing global namespace. Technically this
> isn't global, but practically it is.

That's a very good point.

What about if it was a static function hanging off node or
node.Module? It's not really any more complexity in the code, and it
would probably be beneficial to group setExports in a happier home.

Anyone care to bikeshed on the name? Is it clear what it does? I
also was thinking that just export() might make sense (in that I'm
telling it to "export this thing here"), but that could be confusing.
Looks too much like "exports.foo", and one might be inclined to export
() a bunch of things and expect them to all be made available, when in
actuality, only the last one would win.

--i

Tim Caswell

unread,
Oct 5, 2009, 2:14:14 PM10/5/09
to nod...@googlegroups.com
How about optionally allowing the return value of the library to be
the exports object. Then your "link" file would be:

return require("other_file.js")

If course I would prefer just setting a path variable as a solution to
the long path name problem.

Martin Hunt

unread,
Oct 5, 2009, 3:08:19 PM10/5/09
to nod...@googlegroups.com
I think it would be interesting to allow a require statement to return a function rather just an object.  I would like to be able to write:

exports = function() { ... }

or 

return function() { ... }

Would it be possible to allow direct assignment to the exports object?

Ryan Dahl

unread,
Oct 5, 2009, 3:13:46 PM10/5/09
to nod...@googlegroups.com
On Mon, Oct 5, 2009 at 9:08 PM, Martin Hunt <mgh...@gmail.com> wrote:
>
> Would it be possible to allow direct assignment to the exports object?

Yes. We were discussing it on IRC and came to this solution

__module.exports = xxx;

You need commit d67288b64388f501c871c3ff264734a4418071ca to do that.

Reply all
Reply to author
Forward
0 new messages