You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
This repository was archived by the owner on Sep 2, 2023. It is now read-only.
Repository navigation
This repository was archived by the owner on Sep 2, 2023. It is now read-only.
UPDATE: I think this is actually the biggest problem: #323 (comment) , #352
The below, while not super pleasant, could be worked around with smarter transpilers relatively easily.
Before, it was possible to write ES modules (or TypeScript modules), and publish both CommonJS and ES modules with a simple module transformation via babel (I've actually started doing this already, so my modules can be treeshaken).
Now we can't write:
import{foo}from'./bar';console.log(foo);
We have to write:
import{foo}from'./bar.mjs';console.log(foo);
Which means that now any transpiler also needs to rewrite the actual import path. I don't think any currently have this functionality, because until now this wasn't an issue.
This is very different from how the ecosystem has worked so far. For example, TypeScript and Webpack both work just fine with the extension-less module resolution. I would guess rollup does too.
I thought the previous design made much more sense.
When I import something, naturally I expect to get an ES module, preferring the .mjs extension, probably falling back on a .js CommonJS module exported as an ES module.
If I require something, naturally I expect to get a CommonJS module, preferring the .js extension which can't really be changed for backwards compatibility, probably falling back on loading a .mjs ES module exported as a CommonJS module.
I get that some people don't like the new file extension, but I don't think it makes sense to create new problems and add ambiguity just to keep it.
Reacted by Jordan Harband, Michaël Zasso, Charles Samborski, Raj Chandra, Emmanuel Meric de Bellefon, Wasuwat Limsuparhat and Taylor Beeston
Again tho, enabling extension resolution does not in any way prevent “browser compat” - this is purely a question of whether you want the preferences of one group (browser compat folks) to oppress another (back compat folks) by making the feature off by default - because on by default causes no damage, but off by default does.
In node, that would have to resolve to something in node_modules right? You're not expected to write import {something} from 'node_modules/some-module/index.mjs'; right?
@weswigham I fully understand the reasoning and I'm not by any means wanting to inform TypeScript what to do. I'm simply stating my opinion that seems to be shared by others that this behaviour could be seen as unintuitive and there are possible alternative designs that could be seen to be more intuitive. For example, treating relative specifiers only (as defined in HTML) as permitting extension mapping through configuration could be one mechanism. The reason I'm stating this opinion is only to counter your assertion that Node.js put TypeScript in this situation, when we must look at the fact that TypeScript is making design decisions here, and the blame cannot be directed to Node.js, when making those design decisions without adequate user education is the underlying reason for confusion.
We are refraining from designing something to cover up what the runtime does not do, but users still want. That is very different than designing something in conflict with the runtime. The runtime doesn't support any kind of extension mapping thing, nor does it have a great need to. We do not want to be extending the resolver at compile time.
We do not want to be extending the resolver at compile time.
I must still admit I don't understand why. Also, isn't paths already an extension? TypeScript also has to integrate with the Node resolver for type lookups - is that not a resolver extension too?
I must still admit I don't understand why. Also, isn't paths already an extension?
It only provides a way to look up type information - it provides .d.ts locations for .js files, since they (used to) often be held in a folder separate from normal dependencies; moreover this has no effects on paths actually in your code, or on runtime behavior.
It was designed to be used with amd modules, not cjs or esm; it's from a time where bower was still in active use. Some people use it nowadays for local monorepo development, because the local package linkages there can be very nonstandard, and the monorepo layout/tool in use may not know how to handle TS types on its own. (Though normally just building them into your packages just works)
TypeScript also has to integrate with the Node resolver for type lookups - is that not a resolver extension too?
No, we reimplement the resolver wholesale; we run in browser runtimes where node's resolver isn't available, but we'll still analyze code intended for node. That's a small part of why we care so much, we have to maintain a parallel implementation of the thing, and not just the current version of it, but most recent past versions, too (and then provide flags for old/new behavior, depending - that's why we've been trying to wait for all the new stuff exports to stabilize, so it can all be behind one setting; so you could consider that support "extensions", but it'd feel disingenuous to say caring more about back compat then node itself qualifies as such). The more differences there are between past and current versions, the more confused our users get.
Putting these two arguments aside, the main argument to consider is that there is a file system mapping happening. The module at file:///path/to/project/src/file.ts is being mapped into a module at file:///path/to/project/dist/file.js (or file.mjs). Because the extension of the file is changing, the linkage between the modules before and after the transform is changing. The invariant that holds in all module systems is that relative paths with a file extension are always supported. Thus a file with a .ts extension can be remapped to a file with a .js or .mjs extension as part of the file remapping process so long as it is a relative specifier. All of this happens without any resolver extension being necessary and is the same type of mapping performed by standard build tools like RollupJS and esbuild code splitting.
All of this happens without any resolver extension being necessary and is the same type of mapping performed by standard build tools like RollupJS and esbuild code splitting.
Hold it there; those tools bundle their own resolver into the bundle. They no longer care about extensions at all - webpack even goes so far as just just use IDs to refer to each module anywhere it's precalculated the links. Point is, we don't bundle a big runtime like that.
I specifically did not mention Webpack - I'm referring to code splitting outputs from RollupJS and esbuild, which only rely on relative specifiers to exact file extensions as the basic primitive (with externals of course).
It seems to me that the real failure here wasn't in our inability to reach a consensus on this issue, as it's clear that there are diverging opinions that simply won't be reconciled; our failure was in not coming up with a definitive way to resolve this question. We created the --es-module-specifier-resolution flag with the expectation that user feedback would be clear enough and plentiful enough to tell us which way to go; but I think it's safe to say that that hasn't happened.
Because it is safe to say that nobody use native ESM in node. None of my projects at work or private can be converted. Most of the tooling lack proper support: berry, typescript, webpack, eslint, jest. Support is either partial or would require massive amount of work with little to no benefit. Lots of people have transpillers in theirs toolchain that compile ESM to CommonJS and are by large unaware. That why you do not see a lot of feedback. It is more visible in corresponding tools repositories where people are harassing maintainers.
I think many people like me just wait for things to "Just work". I am observing ESM progress in node for the last 3 years now from initial work in node-eps. This is moving incredibly slowly because of changes in resolution. You cannot expect adoption if a an migration is to change every relative import statement and every index.js. That why sooner or later all tools will be forced to support re-writing paths.
node.js was so late in the ESM party but want to change everybody assumptions about resolution.
I'm sorry, but I don't find any of these arguments persuasive; nor do I find this ongoing debate a productive use of our time. @ljharb at least opened this issue to try to collect feedback from across the web although it's been hijacked; and @rauschma opened a Twitter poll:
Excluding the “just show me the results” folks, that’s 39% for mandatory extensions, 30% for automatic resolution, and 31% that don’t care; from a sample of 294 votes.
I think efforts like this poll are the way forward to try to build support for any potential change, if one is desired.
people who prefer providing extensions can provide them regardless of what the default here is. what i see from that poll is 30% of devs saying they miss a functionality, that's pretty huge.
Reacted by Wesley Wigham, Jordan Harband, danilo neves cruz, Pavel Muset, Santi Albo, Wasuwat Limsuparhat and Abhijeet Singh
Seriously; if 30% of your users said they wanted something that didn't affect what the rest of your users could do, I don't know why you wouldn't consider it...
Reacted by Jordan Harband, danilo neves cruz, Pavel Muset and Abhijeet Singh
Excluding the “just show me the results” folks, that’s 39% for mandatory extensions, 30% for automatic resolution, and 31% that don’t care; from a sample of 294 votes.
Again, another poll that is just misleading. Workaround only applies to external dependencies. Not only you suggested workaround is bad because lead to fragmentation but do not address core issue.
As result your node.js application written in typescript can end up with something like this
importmoduleform'main-module-package';// with single mainimportcjsSubmodulefrom'main-module-cjs/submodule'//cjs modules still search for extension ?importsubmoduleMappingfrom'es-module-package/submodule';// with subpath mappingimportsubmoduleNoMappingfrom'es-module-package/submodule.js';// without subpath mapping importbaseform'../base/index.js'// index.js as no longer supportedimportlocalfrom'./local-module.js'// relative imports need to have extension
Now in my typescript code I need to know how modules can be imported. For relative imports I need to import using transpilled extension and for existing code I need to refactor or import index.js.
Reacted by Jordan Harband, Pavel Muset and Wasuwat Limsuparhat
Activity
ljharb commentedon Apr 28, 2019
nodejs/node#27407
ljharb commentedon Apr 28, 2019
nodejs/node#27408
AlexanderOMara commentedon May 1, 2019
Is there any information available on why file extension resolution was disabled?
GeoffreyBooth commentedon May 1, 2019
@AlexanderOMara See
sheerun commentedon May 7, 2019
AlexanderOMara commentedon May 7, 2019
UPDATE: I think this is actually the biggest problem: #323 (comment) , #352
The below, while not super pleasant, could be worked around with smarter transpilers relatively easily.
Before, it was possible to write ES modules (or TypeScript modules), and publish both CommonJS and ES modules with a simple module transformation via babel (I've actually started doing this already, so my modules can be treeshaken).
Now we can't write:
We have to write:
Which means that now any transpiler also needs to rewrite the actual import path. I don't think any currently have this functionality, because until now this wasn't an issue.
This is very different from how the ecosystem has worked so far. For example, TypeScript and Webpack both work just fine with the extension-less module resolution. I would guess rollup does too.
I thought the previous design made much more sense.
When I
importsomething, naturally I expect to get an ES module, preferring the.mjsextension, probably falling back on a.jsCommonJS module exported as an ES module.If I
requiresomething, naturally I expect to get a CommonJS module, preferring the.jsextension which can't really be changed for backwards compatibility, probably falling back on loading a.mjsES module exported as a CommonJS module.I get that some people don't like the new file extension, but I don't think it makes sense to create new problems and add ambiguity just to keep it.
MylesBorins commentedon May 15, 2019
Some feedback in favor of not having extension searching on by default
Twitter poll about compat: 65% of 1295 people favor Browser compat
@jhnns on twitter: https://twitter.com/Jhnnns/status/1003201464716726272
@mhart on twitter: https://twitter.com/hichaelmart/status/1039529625100185600
@brianleroux on twitter: https://twitter.com/brianleroux/status/1039653429272952832
Announcement tweet for PR had 150k impressions and no negative feedback
Tweet asking for feedback
Obviously none of this is scientific... but thought I could offer some balance to the feedback
ljharb commentedon May 15, 2019
Again tho, enabling extension resolution does not in any way prevent “browser compat” - this is purely a question of whether you want the preferences of one group (browser compat folks) to oppress another (back compat folks) by making the feature off by default - because on by default causes no damage, but off by default does.
AlexanderOMara commentedon May 15, 2019
Is browser compatibility really even possible? As soon as you import another module, isn't browser compatibility lost?
In node, that would have to resolve to something in
node_modulesright? You're not expected to writeimport {something} from 'node_modules/some-module/index.mjs';right?Am I missing something here?
134 remaining items
guybedford commentedon Aug 5, 2020
@weswigham I fully understand the reasoning and I'm not by any means wanting to inform TypeScript what to do. I'm simply stating my opinion that seems to be shared by others that this behaviour could be seen as unintuitive and there are possible alternative designs that could be seen to be more intuitive. For example, treating relative specifiers only (as defined in HTML) as permitting extension mapping through configuration could be one mechanism. The reason I'm stating this opinion is only to counter your assertion that Node.js put TypeScript in this situation, when we must look at the fact that TypeScript is making design decisions here, and the blame cannot be directed to Node.js, when making those design decisions without adequate user education is the underlying reason for confusion.
weswigham commentedon Aug 5, 2020
We are refraining from designing something to cover up what the runtime does not do, but users still want. That is very different than designing something in conflict with the runtime. The runtime doesn't support any kind of extension mapping thing, nor does it have a great need to. We do not want to be extending the resolver at compile time.
guybedford commentedon Aug 5, 2020
I must still admit I don't understand why. Also, isn't
pathsalready an extension? TypeScript also has to integrate with the Node resolver for type lookups - is that not a resolver extension too?guybedford commentedon Aug 5, 2020
Also, in theory a file mapping is not a resolver extension it is a file system remapping.
devsnek commentedon Aug 5, 2020
Let it be known that people who do not use typescript also dislike the lack of extension resolution.
weswigham commentedon Aug 5, 2020
.d.tslocations for.jsfiles, since they (used to) often be held in a folder separate from normal dependencies; moreover this has no effects on paths actually in your code, or on runtime behavior.amdmodules, notcjsoresm; it's from a time wherebowerwas still in active use. Some people use it nowadays for local monorepo development, because the local package linkages there can be very nonstandard, and the monorepo layout/tool in use may not know how to handle TS types on its own. (Though normally just building them into your packages just works)No, we reimplement the resolver wholesale; we run in browser runtimes where node's resolver isn't available, but we'll still analyze code intended for
node. That's a small part of why we care so much, we have to maintain a parallel implementation of the thing, and not just the current version of it, but most recent past versions, too (and then provide flags for old/new behavior, depending - that's why we've been trying to wait for all the new stuffexportsto stabilize, so it can all be behind one setting; so you could consider that support "extensions", but it'd feel disingenuous to say caring more about back compat then node itself qualifies as such). The more differences there are between past and current versions, the more confused our users get.guybedford commentedon Aug 5, 2020
Putting these two arguments aside, the main argument to consider is that there is a file system mapping happening. The module at
file:///path/to/project/src/file.tsis being mapped into a module atfile:///path/to/project/dist/file.js(orfile.mjs). Because the extension of the file is changing, the linkage between the modules before and after the transform is changing. The invariant that holds in all module systems is that relative paths with a file extension are always supported. Thus a file with a.tsextension can be remapped to a file with a.jsor.mjsextension as part of the file remapping process so long as it is a relative specifier. All of this happens without any resolver extension being necessary and is the same type of mapping performed by standard build tools like RollupJS and esbuild code splitting.weswigham commentedon Aug 5, 2020
Hold it there; those tools bundle their own resolver into the bundle. They no longer care about extensions at all - webpack even goes so far as just just use IDs to refer to each module anywhere it's precalculated the links. Point is, we don't bundle a big runtime like that.
guybedford commentedon Aug 5, 2020
I specifically did not mention Webpack - I'm referring to code splitting outputs from RollupJS and esbuild, which only rely on relative specifiers to exact file extensions as the basic primitive (with externals of course).
gengjiawen commentedon Aug 5, 2020
--es-module-specifier-resolutionflag is unlikely works. Is it okay we put another field inpackage.jsonfor this ?chyzwar commentedon Aug 5, 2020
Because it is safe to say that nobody use native ESM in node. None of my projects at work or private can be converted. Most of the tooling lack proper support: berry, typescript, webpack, eslint, jest. Support is either partial or would require massive amount of work with little to no benefit. Lots of people have transpillers in theirs toolchain that compile ESM to CommonJS and are by large unaware. That why you do not see a lot of feedback. It is more visible in corresponding tools repositories where people are harassing maintainers.
I think many people like me just wait for things to "Just work". I am observing ESM progress in node for the last 3 years now from initial work in node-eps. This is moving incredibly slowly because of changes in resolution. You cannot expect adoption if a an migration is to change every relative import statement and every index.js. That why sooner or later all tools will be forced to support re-writing paths.
node.js was so late in the ESM party but want to change everybody assumptions about resolution.
GeoffreyBooth commentedon Aug 5, 2020
I'm sorry, but I don't find any of these arguments persuasive; nor do I find this ongoing debate a productive use of our time. @ljharb at least opened this issue to try to collect feedback from across the web although it's been hijacked; and @rauschma opened a Twitter poll:
Excluding the “just show me the results” folks, that’s 39% for mandatory extensions, 30% for automatic resolution, and 31% that don’t care; from a sample of 294 votes.
I think efforts like this poll are the way forward to try to build support for any potential change, if one is desired.
devsnek commentedon Aug 5, 2020
people who prefer providing extensions can provide them regardless of what the default here is. what i see from that poll is 30% of devs saying they miss a functionality, that's pretty huge.
weswigham commentedon Aug 5, 2020
Seriously; if 30% of your users said they wanted something that didn't affect what the rest of your users could do, I don't know why you wouldn't consider it...
chyzwar commentedon Aug 5, 2020
Again, another poll that is just misleading. Workaround only applies to external dependencies. Not only you suggested workaround is bad because lead to fragmentation but do not address core issue.
As result your node.js application written in typescript can end up with something like this
Now in my typescript code I need to know how modules can be imported. For relative imports I need to import using transpilled extension and for existing code I need to refactor or import index.js.