requireThe Folders as Modules docs say the following, for both 8.x and 10.x:
If the file specified by the 'main' entry of package.json is missing and can not be resolved, Node.js will report the entire module as missing with the default error:
Error: Cannot find module 'some-library'
However, in practice this doesn't seem to be the whole story. It seems that it will gracefully fall back to index.js anyway if it exists.
Given the following index.js and package.json:
console.log('This is index');
{
"name": "test",
"main": "doesnotexist.js"
}
If you run node ., you don't get an error - you get This is index.
There is another sentence later in the docs:
If there is no package.json file present in the directory, then Node.js will attempt to load an index.js or index.node file out of that directory.
This would seem to also be applying in the case where there _is_ a package.json, but main points to a file that doesn't exist.
Am I missing something, or do these docs need to be clarified?
PS: If you remove index.js from the above example, an error occurs but still doesn't seem to match what's in the docs - it doesn't say it can't find what's referenced by main; it simply says it cannot find the path you referenced (e.g. . rather than doesnotexist above).
@kfranqueiro Does the proposed change look good?
On the PS, I am getting the "cannot find module" error specified in the docs when I remove index.js (Although the module it is attempting to find is at the working directory if you are invoking node '.' at the command line.)
Yeah, the latest proposed change seems like it captures it. Thanks.
RE the PS, I realized just now that I was misinterpreting the docs, so you're right. Sorry for the confusion on my part there.
This seems like a bug - I鈥檓 reasonably sure it didn鈥檛 used to act this way. What happens in older node versions? Can we figure out when it changed?
Works that way for me with 1.0.0 on nvm.
If the file specified by main is missing but a default index file is present, would we prefer to throw an error?
That condition would suggest a bug in the module. Either (1) the module maintainer wants the default index file called but has the wrong main file listed in the package.json file or (2) the main file is missing from the module AND the module happens to have a file with a name matching a default index file. In case (1) the fallback would be reasonable, but in case (2) it results in undefined behavior. (Or rather, it results in _accidentally_-defined behavior from the POV of the module writer.)
Fixing to throw an error instead of undefined behavior would be a breaking change impacting any modules which have the bug but presently work.
Any idea from which version this started happening?
//cc @bmeck
I would expect that if package.json exists, and "main" is present but unresolveable, that it would throw an error. If "main" is absent or package.json is absent, then I would probably expect a default "main" to apply - ie, ./index (plus the appropriate extension resolution).
@ljharb Yes, that sounds like a reasonable behavior.
@guybedford I'm not sure, but I tested a few versions with nvm and they all behaved this way. (The earliest I tested was 1.0.0).
It鈥檇 be worth it to test 0.8 and 0.10; 0.12 and 1 were when a number of things started changing.
To my knowledge this has always been the case. I tend to avoid "main" personally and can't remember a time where I had to have a "main". Given that all phases or searching for files catches errors and tries the next location, I'd assume this to be expected but with poor docs.
having an unresolvable explicit main tho doesn鈥檛 seem like it should fall back to something else.
@ljharb I'm neutral to this behavior, we do have precedent for EPERM failing fast though; however, this is a ENOENT error. I choose no sides on what the behavior should be. Early error seems safer though, especially given fallthrough behavior of require.
0.12, 0.10, and 0.08 have the same behavior.
In that case, the behavior is consistent and we should indeed update the docs. Separately, it'd be great to update the behavior to throw when "main" is present and unresolvable.
having an unresolvable explicit main tho doesn鈥檛 seem like it should fall back to something else.
That's what surprised me and caused me to check the docs and open this bug. IMO it would be nice to be able to detect that this is happening somehow; it's potentially more confusing for something to happen to surprisingly function but perhaps not in the intended way. But at least if we update the docs it might raise awareness of the actual behavior.
Background: I originally discovered this along with a co-worker because a repo I work on is considering updating its main to point to transpiled code under a dist folder (it currently points to index.js which is ES2015 code). Somehow our CI continued to pass as usual on a partial PR, but local tests failed. The reason why was because dist didn't exist at all when tests ran on CI, and it silently fell back to index.js and thus acted as if nothing had changed.
(I'm not trying to say my problem should be Node's problem; just giving an example of how the current silent fallback behavior can be confusing.)
Perhaps we could start by issuing a runtime warning when this happens.
A run-time warning sounds like a good approach. It would let people track down the source of unexplained behavior and would warn module developers to fix their modules, but it wouldn't be a breaking change.