On Wed, Jan 23, 2013 at 9:13 AM, Jacob Groundwater <
ja...@nodefly.com> wrote:
> Is `index.js` considered a non-preferred method of doing package imports? I
> really like it, but arguing a stable feature is pointless. I'm just curious
> what the reasons were for including it, and what has changed since?
>
> FOR SCIENCE!
index.js predates packages by quite a long time. It was the first
"load a packagey folder thing as if it was a single module" feature,
dating back to the 0.1.x days. It's not terrible. But personally I
prefer a main script in package.json, since you can name it something
more appropriate than "index.js", and somewhat like named functions,
it makes the debugging experience a bit nicer.
On Wed, Jan 23, 2013 at 6:18 AM, Austin William Wright
<
diamon...@users.sourceforge.net> wrote:
>> "Objectively correct"? Wow.
> If you have a problem with my logic, the correct thing to do would be
> describe what's actually wrong.
Fair enough. There are two major problems with your argument.
The first is a gross category error. You are asserting that the
semantics of the require() function can be in any sense "objectively
correct", when in fact we are discussing complicated trade-offs
involving subjective values.
Second, you are asserting that "correctness" is defined by adherence
to a spec that has nothing to do with Node, and never did.
> If you ignore the semantics of what
> operations like require() actually *mean* then you're going to run into
> problems, or stagnate growth.
Where have you been the last 3 years? "Stagnating growth" isn't a
problem we have. We have the problems that come from *not* stagnating
growth.
> So yes, there's an "objectively correct" way to do things. And you MUST NOT
> assume that the filesystem is the only way someone would want to dereference
> a module.
I don't assume that. But it IS the only way that someone is capable
of dereferencing a Node module. Look at the code. That's how it
works. If you want your modules to live somewhere else, mount it with
fuse. Apart from the buildin modules bundled in the binary, Node only
loads modules using the fs.
> But this response isn't unusual to see when people can't actually attack
> logic.
Ad hominem.
> The cost is the value of the next best alternative that must be given up.
> What's being given up? A few extra CPU cycles, worst case? That can't be it,
The cost is time spent debugging programs you didn't write. That is
the cost I was referring to, not CPU cycles. (Unnecessary stat()
calls at startup are not ideal, but they're also not particularly
costly.)
In the normal case, you have three types of things:
- Builtin module: require('fs')
- Package main: require('request')
- Local module: require('./mine.js')
Package mains and builtins are similar in their appearance, and when
you're working with your program, they're actually somewhat similar in
how you tend to think of them, as well. They're a thing "outside" the
current program that it depends on. This similarity in appearance is
still a bit annoying, but changing it would be overly destabilizing.
The fourth case, is depending on a local module within a file, and
that's what we're talking about here.
So, say that the file is at node_modules/foo/lib/utils/bar.js. Right
now, you can do require('foo/lib/utils/bar.js'). It's pretty clear
what file you're pulling out: it's lib/util/bar.js from the foo
package.
If we add directories.lib then foo/lib/utils/bar.js could become
require('foo/bar.js'). What is the advantage of this? What is the
cost?
The advantage is that you can export more things from your package
slightly more easily. There's a feature designed explicitly for that,
so the API communicates that such a thing is a good idea. (Most in
the node community are of the opinion that it's *not* a good idea!)
Another advantage is that you can have shorter strings in your
require() function. But you can also have that by just putting files
in the root of the foo package. That is a much simpler solution, and
simpler solutions are to be preferred unless they come along with a
cost that outweighs the value of their simplicity. What is the cost
of putting files in the root of your package? Clutter in your project
folder. But is that clutter a bad thing, necessarily, and exactly how
bad is it? The clutter is a motivation to build a more elegant
module. The API is communicating that such an approach is
ill-advised. (And this reflects the feelings of the node community.)
The cost of directories.lib is that it's an extra step in figuring out
how node programs work. Consider a module baz that depends on foo.
In baz, there is the line `var Bar = require('foo/bar.js')`. I am
using baz, and debugging an issue that seems to stem from this Bar
thing. So, I want to look up its implementation. I type `npm explore
foo` to go into that folder, or `npm edit foo` to open it up in my
editor. Now what? Which file should I edit? There's a package.json
here, and a lib folder. No bar.js. In lib, there's a folder called
"exports" and a folder called "utils" and a folder called "browser"
and a folder called "ringo". Each have a file called "bar.js" in
them. Now I have to open up package.json, and understand it.
(God help me if you're using "overlays", another feature that we have
flat out refused.)
It gets much much worse if you use the "modules" feature of
package.json. Now it's not just a folder, but an arbitrary mapping of
module IDs to filenames. The "mapping" feature makes it so that I
can't even reliably read your code, since require("request") might
actually be something entirely different from the "request" module.
It may seem like I'm painting a bleak picture. I'm not. I'm actually
describing a real-world situation that I personally encountered trying
to build a real product for real customers at Joyent, back when npm
supported this feature you guys want to revive.
Supporting the "main" field is as far as we go. Why "main", but not
"directories.lib" or "modules" or "mapping", especially when we
already have "index.js" support? Well, having 25 files named
"index.js" is not very helpful in the debugger, and most packages were
already using the "main" field when we started putting
node_modules/package awareness into the node module loader. Only a
few were using modules or directories.lib, and they didn't have much
traction already.
The API that provides a specific interface for "export one thing"
communicates that the correct way to build programs is with modules
that do one thing. This is widely acknowledged in the node community
as a best practice.
Because of this, the experience of looking up a single "main" module
is much less crazy-making in practice, especially since it's often the
only .js file in the package.
> the current Node.js implementation currently has to synchronously stat() for
> package.json, which should have been done away with long ago.
Why? Because sync IO is "icky"? It's only icky if it prevents you
from servicing requests. At start-up time, it's actually much faster,
and gets you to a running program sooner.
The solution is quite easy in practice: don't do require() after start
time, or suffer the consequences.
> As mentioned, require() may be extended to load things other than files.
> Take json, or coffeescript, for example.
Those aren't examples of "things other than files". JSON and
coffeescript are files.
> I think you suggested at one point
> that it'd be cool for require() to *load files off the Internet*. That's not
> a good idea, anymore?
The node team has rejected several proposals to do exactly that, and
I've been convinced that it's a bad idea, or at least, would require a
massive change to how Node works.
Just because I think something would be neat doesn't mean it's
actually a good idea. I AM a hypocrite, after all.
>> > Cross-platform compatibility is a highly desirable feature.
>>
>> Cool story.
>
> Then help do something about it.
"Cool story" is not a way of communicating agreement. It is a way of
dismissing a non sequitur.
"Desirability" is not an innate property of features. "Desire" is a
feeling a person has. Something is "desirable" if anyone desires it.
But really, the people in the Node community who care about being
compatible with pinf-js and ringo are a rounding error.
So, desirable to whom? Who feels this "desire" sensation? Not enough
people to matter to me, I'm afraid, and certainly not enough to make
the benefits outweigh the costs.
Seriously, if you are so convinced, prove it. Fork node, add this
feature, and then wait for the kudos to roll in. Or better yet, go
get Meteor and Vert.x to be compatible with Node, and let the CommonJS
experiment rest in peace.