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

Adding support for ESM references without a .js extension #46006

Closed
staplespeter opened this issue Dec 29, 2022 · 12 comments
Closed

Adding support for ESM references without a .js extension #46006

staplespeter opened this issue Dec 29, 2022 · 12 comments
Labels
duplicate Issues and PRs that are duplicates of other issues or PRs.

Comments

@staplespeter
Copy link

staplespeter commented Dec 29, 2022

What is the problem this feature will solve?

When running JS files output from tsc without explicit .js extensions on implicit .ts module imports, Node.js produces an error.

File X.ts:
import A from './Y';

File Y.ts:
export default const A = 10;

tsc output is to X.js and Y.js with content being identical. node X.js produces the following:
Error [ERR_MODULE_NOT_FOUND]: Cannot find module 'Y' imported from X.js

File X.ts must contain:
import A from './Y.js';
for this import to work.

It seems there has been much discussion on this for many years, with the TypeScript team refusing to add support for File X.ts:
import A from './Y';
transpiling to File X.js:
import A from './Y.js';

See

In places the TypeScript dev team have insisted that Node.js should be resolving the ESM dependencies correctly. Frankly either tsc or Node.js can resolve this issue. Doing so would really help A LOT of people. It would be good to see some collaboration between TypeScript and Node.js teams to make this ESM support work cleanly.

What is the feature you are proposing to solve the problem?

Add module resolution for ESM imports without an explicit .js extension when imports are within the package.

What alternatives have you considered?

Crap workarounds:

  • Using .js extension in all imports
  • Renaming output files post-transpilation

A seamless solution for the toolchain is what is required. Current solutions are improper, or are hacks, or leave the burden on the dev to effectively manually add the .js extensions. These are programs, they are meant to do the grunt work for devs.

@bnoordhuis
Copy link
Member

Node already supports what you're asking, if I understand you correctly:

https://nodejs.org/docs/latest-v18.x/api/esm.html#import-specifiers - bare specifiers

@climba03003
Copy link
Contributor

climba03003 commented Dec 30, 2022

Node already supports what you're asking,

The ask above is about Relative specifiers which requires file extension.
Even with Bare specifiers, developer is required to specify all the possible files in exports if they would like their user to access all those without file extension. And it is not possible within the package itself.

@bnoordhuis
Copy link
Member

Okay, cc @nodejs/modules then. I have a feeling this has already been discussed to death though.

@bnoordhuis bnoordhuis added the esm Issues and PRs related to the ECMAScript Modules implementation. label Dec 30, 2022
@GeoffreyBooth
Copy link
Member

This is a duplicate of so many that I'm not going to bother looking it up. Newer versions of TypeScript have solved this issue.

@GeoffreyBooth GeoffreyBooth added duplicate Issues and PRs that are duplicates of other issues or PRs. and removed feature request Issues that request new features to be added to Node.js. esm Issues and PRs related to the ECMAScript Modules implementation. labels Dec 30, 2022
@GeoffreyBooth GeoffreyBooth closed this as not planned Won't fix, can't repro, duplicate, stale Dec 30, 2022
@staplespeter
Copy link
Author

staplespeter commented Dec 31, 2022

@GeoffreyBooth I'm sure this is a duplicate. I raised this here as i encountered a runtime error because I was never instructed (EVER) to add .js extensions to my ESM TS import statements. After researching the issue many people are still pissed at this TS issue. I thought perhaps a solution between Node and TS devs should have been agreed.

Newer versions of TS have not solved this issue - at least in mine and many others views. I can still transpile TS files with ESM imports with no extension and they will still fail at runtime. The solution you mention is simply to instruct devs to ensure that the imports have .js extensions.

I'm not really sure why Node couldn't resolve ESM imports without an extension

  • Is it ESM or CJS?
  • It's ESM, look for file with .js extension
  • Not there, look for file with /index.js appended
  • Not there, iterate through others cases in some kind of priority order until resolved
  • Not resloved, error

I believe TS should output the correct extension as they are targeting a known platform, but TS devs have chosen that devs must use .js extensions during coding. These files don't exist. Using .ts in imports results in a build error. It's very backwards. And after 5 years of this issue only in the last month has instruction been added to the TS docs to state that .js extensions must be used. It's frankly pathetic.

So in the spirit of cooperative solutions I thought maybe Node could/would do something to help devs.

But sadly it seems both parties seem to not really care about the development experience of using their tools. I appreciate that this has been discussed before (i've searched and read previous TS issues on this but not Node, apologies), and i'm not pointing the finger at anyone here, I'm just frustrated that the customers requirements are not put first.

In any case, as has been recommended, i'm going to use Deno in the future.

@JakobJingleheimer
Copy link
Contributor

@staplespeter I understand your frustration at the pain the TS team have inflicted upon you. It is entirely possible in node to work around TS's design flaw with a set-and-forget custom loader (we have even written a simple one for you: https://github.com/nodejs/loaders-test/tree/main/typescript-loader).

@climba03003
Copy link
Contributor

climba03003 commented Dec 31, 2022

The example doesn't seem to provide a method to ignore the extension.
So, if one of the loader need to be provide.

  1. Need to search for the actual file.
  2. Need to transform and add the .js / .mjs extension inside the code. (Maybe it doesn't need to.)

To me, it is asking the user to re-invent the logic (CJS Loader) inside node to workaround the problem.

@JakobJingleheimer
Copy link
Contributor

To me, it is asking the user to re-invent the logic (CJS Loader) inside node to workaround the problem.

We have provided a feature that hugely reduces the level of effort needed from you to work around TypeScript's design flaw. You can expand on the loader I linked above to do so (and if you do a small bit of digging, you'll find an earlier implementation that did include the specific mismatched file extension workaround). I use that expanded version myself in my day-job.

ts-node (and possibly others) also provide a loader that fully handles this and much more than the bare-bones example provided above.

@climba03003
Copy link
Contributor

climba03003 commented Dec 31, 2022

and if you do a small bit of digging, you'll find an earlier implementation that did include the specific mismatched file extension workaround

It is not what the request above. The request is about allow esm relative import with no file extension.
Generally speaking, the loader need to teach nodejs to find the actual file. No transform is needed since the invalid part is only about the import path.

ts-node (and possibly others) also provide a loader that fully handles this and much more than the bare-bones example provided above.

It should be the closest loader to look at, but complicated for beginner.
https://github.com/TypeStrong/ts-node/blob/main/src/esm.ts

One sad thing about using the loader is that, when you publish the package.
The user are also required to use the custom loader.

@GeoffreyBooth
Copy link
Member

I raised this here as i encountered a runtime error because I was never instructed (EVER) to add .js extensions to my ESM TS import statements.

That’s what the TypeScript docs instruct you to do: https://www.typescriptlang.org/docs/handbook/esm-node.html

See also microsoft/TypeScript#37582 and microsoft/TypeScript#51669.

If you don’t like the solutions that the Node and TypeScript teams have recommended, you’re welcome to implement your own customizations via the Loaders API as @JakobJingleheimer recommended. We have no plans to provide anything further in this area.

@staplespeter
Copy link
Author

@GeoffreyBooth the Typescript Doc's were changed this month's, 5 YEARS after this issue was initially raised. Up until then all users were instructed to not add .js extensions and VSCode automatically added imports without an extension (default option, and still does). I don't think this instruction is an acceptable solution in any case, as have many thousands of others over these years.

While I thank you your suggestion to spend lots of time implementing a feature compilers or runtime environments should have responsibility for, I will instead just use Deno, which apparently takes care of all this for me.

This choice is a direct reflection on the lack of intention of development teams to make their products work seamlessly together. I don't want to continuously encounter these frustrating issues

@JakobJingleheimer
Copy link
Contributor

The problem you suffer is legitimate; however, it is due to a poor decision by a third party, and we have no intention of burdening ourselves with their problems.

That said, I am locking this issue to avoid further distraction.

We are happy to engage in fruitful collaboration; however, the comments after this was closed provide nothing we do not already know (and discussed ad nauseam), continuously cite issues of a third party, and disregard the very viable (including off-the-shelf) solutions available.

If you have a suggestion for a general improvement that is not specific to supporting a single poor choice of a third party, we are all ears. Please open an issue detailing your proposal.

If you do not like TypeScript's shortcomings, don't use it.

@nodejs nodejs locked and limited conversation to collaborators Dec 31, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
duplicate Issues and PRs that are duplicates of other issues or PRs.
Projects
None yet
Development

No branches or pull requests

5 participants