Skip to content

Folders as Modules docs: Inaccuracy regarding missing main file? #22464

Description

@kfranqueiro
  • Version: 8.11.4 and 10.9.0
  • Platform: Mac OS X
  • Subsystem: require

The 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).

Activity

  1. TomCoded commented on Aug 23, 2018

    @TomCoded
    Contributor

    @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.)

  2. kfranqueiro commented on Aug 24, 2018

    @kfranqueiro
    Author

    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.

  3. ljharb commented on Aug 26, 2018

    @ljharb
    SponsorMember

    This seems like a bug - I’m reasonably sure it didn’t used to act this way. What happens in older node versions? Can we figure out when it changed?

  4. TomCoded commented on Aug 26, 2018

    @TomCoded
    Contributor

    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.

  5. guybedford commented on Aug 26, 2018

    @guybedford
    Contributor

    Any idea from which version this started happening?

    //cc @bmeck

  6. ljharb commented on Aug 26, 2018

    @ljharb
    SponsorMember

    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).

  7. TomCoded commented on Aug 27, 2018

    @TomCoded
    Contributor

    @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).

  8. ljharb commented on Aug 27, 2018

    @ljharb
    SponsorMember

    It’d be worth it to test 0.8 and 0.10; 0.12 and 1 were when a number of things started changing.

  9. bmeck commented on Aug 27, 2018

    @bmeck
    Member

    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.

  10. ljharb commented on Aug 27, 2018

    @ljharb
    SponsorMember

    having an unresolvable explicit main tho doesn’t seem like it should fall back to something else.

  11. bmeck commented on Aug 27, 2018

    @bmeck
    Member

    @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.

  12. TomCoded commented on Aug 27, 2018

    @TomCoded
    Contributor

    0.12, 0.10, and 0.08 have the same behavior.

  13. ljharb commented on Aug 27, 2018

    @ljharb
    SponsorMember

    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.

  14. kfranqueiro commented on Aug 27, 2018

    @kfranqueiro
    Author

    having an unresolvable explicit main tho doesn’t 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.)

  15. ljharb commented on Aug 27, 2018

    @ljharb
    SponsorMember

    Perhaps we could start by issuing a runtime warning when this happens.

  16. TomCoded commented on Aug 29, 2018

    @TomCoded
    Contributor

    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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions