Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

"Cannot find module" when main file not index.js with experimental-specifier-resolution=node #32103

Closed
dandv opened this issue Mar 5, 2020 · 15 comments · Fixed by #38979
Closed
Labels
confirmed-bug Issues with confirmed bugs. esm Issues and PRs related to the ECMAScript Modules implementation. help wanted Issues that need assistance from volunteers or PRs that need help to proceed.

Comments

@dandv
Copy link
Contributor

dandv commented Mar 5, 2020

  • Version: 13.9.0
  • Platform: Linux

What steps will reproduce the bug?

  1. Clone https://github.com/dandv/node-cant-find-module-with-main-not-index.js
  2. npm start

What is the expected behavior?

The script should display Success!, and does do so if mypackage/Lib.js is renamed to mypackage/index.js.

What do you see instead?

internal/modules/esm/resolve.js:61
  let url = moduleWrapResolve(specifier, parentURL);
            ^

Error: Cannot find module /home/dandv/prg/node-cant-find-module-with-main-not-index.js/mypackage imported from /home/dandv/prg/node-cant-find-module-with-main-not-index.js/run.js
    at Loader.defaultResolve [as _resolve] (internal/modules/esm/resolve.js:61:13)
    at Loader.resolve (internal/modules/esm/loader.js:85:40)
    at Loader.getModuleJob (internal/modules/esm/loader.js:191:28)
    at ModuleWrap.<anonymous> (internal/modules/esm/module_job.js:42:40)
    at link (internal/modules/esm/module_job.js:41:36) {
  code: 'ERR_MODULE_NOT_FOUND'
}

Additional information

I'm trying to run node with -experimental-specifier-resolution=node because TypeScript can't output .mjs files and I want to use extension-less import statements. I prefer to use Lib.js instead of index.js to distinguish in my IDE between the main files of multiple packages in my monorepo that otherwise would all look like index.js.

@MylesBorins MylesBorins added the esm Issues and PRs related to the ECMAScript Modules implementation. label Mar 6, 2020
@MylesBorins
Copy link
Contributor

/cc @nodejs/modules

@ljharb
Copy link
Member

ljharb commented Mar 6, 2020

The repro link seems correct to me; if so, this seems like a bug.

If you use explicit extensions, and don't use experimental specifier resolution, what happens?

@jkrems
Copy link
Contributor

jkrems commented Mar 6, 2020

I'm trying to run node with -experimental-specifier-resolution=node because TypeScript can't output .mjs files and I want to use extension-less import statements.

TypeScript has only limited support for ES modules so far. But in many cases the following workaround works:

  1. Add "type": "module" to package.json.
  2. Use import './some-file.js' in your TypeScript source code. TypeScript will find .ts files if you use .js in the specifier.
  3. Run the app without the experimental resolution flag.

(That's the more verbose version of Jordan's "If you use explicit extensions". :))

@dandv
Copy link
Contributor Author

dandv commented Mar 7, 2020

If you use explicit extensions, and don't use experimental specifier resolution, what happens?

Then the script runs correctly.

@MylesBorins
Copy link
Contributor

So the issue appears to be that to support that we introduced for experimental-specifier-resolution does not respect the package.json main field. I can get the example to work by changing the Lib.js file to be index.js. This is definitely a bug in the support for experimental resolution. I'm not 100% where the bug lives but this is where we should be resolving package main for experimental resolution

https://github.com/nodejs/node/blob/master/src/module_wrap.cc#L1180-L1186

This is where it seems like where we are doing the resolution itself

https://github.com/nodejs/node/blob/master/src/module_wrap.cc#L826-L862

It is possible that some order of operations bug is not even checking for the package.main... and tbh the work we've done around exports is going to confuse this a bit too. I'll try and find some time to dig in but this is going to have to be lower priority for me personally, so if anyone else wants to pick this up please go ahead!

as an aside, thanks so much for these amazing bug reports @dandv

@guybedford guybedford added the confirmed-bug Issues with confirmed bugs. label Mar 12, 2020
@lingsamuel
Copy link
Contributor

lingsamuel commented Apr 1, 2020

I figured out the exact reason.
Note: Related C++ code is rewritten using JS in this commit, new related code location link:
https://github.com/nodejs/node/blob/master/lib/internal/modules/esm/resolve.js#L594
More comments can be found in the historical C++ file in the commit link above.

function moduleResolve(specifier /* string */, base /* URL */) { /* -> URL */
  // Order swapped from spec for minor perf gain.
  // Ok since relative URLs cannot parse as URLs.
  let resolved;
  if (shouldBeTreatedAsRelativeOrAbsolutePath(specifier)) {
    resolved = new URL(specifier, base);
  } else {
    try {
      resolved = new URL(specifier);
    } catch {
      return packageResolve(specifier, base);
    }
  }
  return finalizeResolution(resolved, base);
}

The parameter specifier is ./mypackage here, so it passes the relative path check and returns as is directly. So the package main resolve related code won't run.

I am working on this, maybe I will submit a PR in about a few days.

@csvan
Copy link

csvan commented May 5, 2020

EDIT: This is working as intended, I misunderstood the spec. See https://nodejs.org/api/esm.html#esm_import_specifiers

Leaving the below for history only.

Seeing the same thing on Node 14.1, OSX. In my package.json I have

"type": "module",

I then have one file, server.js, which in turn imports another file, serverModule.js:

server.js

import startServer from './serverModule';

startServer({});

serverModule.js

import args from './get-args';
// ......
export default startServer;

Now, I notice that adding .js to any import in the chain solves it for that particular file. For example, this solves the import of serverModule.js in server.js:

import startServer from './serverModule.js';

But then in turn it complains about import args from './get-args'; in that file, and so on. It seems like file endings are required, but that runs contrary to the description here: https://medium.com/@nodejs/announcing-core-node-js-support-for-ecmascript-modules-c5d6dc29b663

Files ending in .js, or extensionless files, when the nearest parent package.json file contains a top-level field “type” with a value of “module”.

@pujakjha
Copy link

Thanks! This solution worked for me.

@ryzokuken
Copy link
Contributor

This seems resolved, closing. Feel free to reopen if something needs to be discussed further, thank you.

@guybedford
Copy link
Contributor

@ryzokuken I believe this actually still remains an implementation bug and I believe my suggestion in #32612 (comment) might be related to the fix here.

That said, we might end up deprecating this flag before the fix at this rate...

@ryzokuken ryzokuken reopened this Jun 29, 2020
@MylesBorins MylesBorins added the help wanted Issues that need assistance from volunteers or PRs that need help to proceed. label Jul 2, 2020
@aduh95 aduh95 closed this as completed in 4e17ffc Jun 13, 2021
danielleadams pushed a commit that referenced this issue Jun 14, 2021
PR-URL: #38979
Fixes: #32103
Fixes: #38739
Reviewed-By: Bradley Farias <[email protected]>
Reviewed-By: Guy Bedford <[email protected]>
danielleadams pushed a commit that referenced this issue Jun 17, 2021
PR-URL: #38979
Fixes: #32103
Fixes: #38739
Reviewed-By: Bradley Farias <[email protected]>
Reviewed-By: Guy Bedford <[email protected]>
richardlau pushed a commit that referenced this issue Jul 19, 2021
PR-URL: #38979
Fixes: #32103
Fixes: #38739
Reviewed-By: Bradley Farias <[email protected]>
Reviewed-By: Guy Bedford <[email protected]>
richardlau pushed a commit that referenced this issue Jul 20, 2021
PR-URL: #38979
Fixes: #32103
Fixes: #38739
Reviewed-By: Bradley Farias <[email protected]>
Reviewed-By: Guy Bedford <[email protected]>
@scanner77
Copy link

Where's the solution after this much of comments and showing expertise? No solution is found in github forum. Yall show how much yall know. I come here often to github to see some solutions to the problem. There's always talking. Please provide the solutions in simple words my experts. Some people are naive out there. Not everybody pros like yall

@aduh95
Copy link
Contributor

aduh95 commented Jul 23, 2021

The solution is to upgrade to Node.js v16.4.0 or later, or wait for v14.17.4 once it is released next week. I'll try to backport it to v12.x as well.
EDIT: The patch has landed on v12.22.4, v14.17.4 and v16.4.0.

aduh95 added a commit to aduh95/node that referenced this issue Jul 28, 2021
richardlau pushed a commit that referenced this issue Jul 28, 2021
PR-URL: #38979
Backport-PR-URL: #39497
Fixes: #32103
Fixes: #38739
Reviewed-By: Bradley Farias <[email protected]>
Reviewed-By: Guy Bedford <[email protected]>
richardlau pushed a commit that referenced this issue Jul 28, 2021
PR-URL: #38979
Backport-PR-URL: #39497
Fixes: #32103
Fixes: #38739
Reviewed-By: Bradley Farias <[email protected]>
Reviewed-By: Guy Bedford <[email protected]>
foxxyz pushed a commit to foxxyz/node that referenced this issue Oct 18, 2021
@JoshMcCullough
Copy link

This still seems like a bug in Node 16.15.0. Why would we need to include the .js suffix for Node to find our modules? I'm not looking to import things in Typescript by adding .js to the end of the path, when the file is .ts -- even though TS might support this, it's not the proper solution.

What am I missing here?

@renatoam
Copy link

renatoam commented Jun 5, 2022

I'm in the same page as @JoshMcCullough. As far as I got it from the previous comments, this problem would be solved and we wouldn't need to put the extension explicitly in our imports. I could solve this by adding --experimental-modules --es-module-specifier-resolution=node to scripts, either for prod or dev.

Is there something that we can do to solve this?

Node version: 16.15.0
Typescript version: ^4.7.3

Example:

"scripts": {
    "build": "npx tsc",
    "start": "node --experimental-modules --es-module-specifier-resolution=node dist/index.js",
},

@Maikpwwq
Copy link

Config ["type": "module"] in package.json, then update tsconfig.json to ts-node's ESM support and pass the loader flag to node in scripts, node --loader ts-node/esm ./index.ts. tsconfig.json:

{
  "compilerOptions": {
    "strict": true,
    "module": "ESNext", // ES2020
    "target": "ES2020",
    "moduleResolution": "Node",
    "lib": ["DOM", "DOM.Iterable", "ESNext"],
    "types": ["vite/client"],
    "jsx": "react-jsx",
    "skipLibCheck": true,
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true
  },
  "ts-node": {
    "experimentalSpecifierResolution": "node",
    "transpileOnly": true,
    "esm": true,
  }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
confirmed-bug Issues with confirmed bugs. esm Issues and PRs related to the ECMAScript Modules implementation. help wanted Issues that need assistance from volunteers or PRs that need help to proceed.
Projects
None yet
15 participants