Skip to content
This repository was archived by the owner on Sep 2, 2023. It is now read-only.
This repository was archived by the owner on Sep 2, 2023. It is now read-only.

Feedback on extension resolution #323

Description

@ljharb

It seems like a good idea to capture somewhere the feedback we receive on extension resolution.

Activity

  1. ljharb commented on Apr 28, 2019

    @ljharb
    SponsorMemberAuthor
  2. ljharb commented on Apr 28, 2019

    @ljharb
    SponsorMemberAuthor
  3. added
    modules-agendaTo be discussed in a meeting
    surveysRelates to things where people to you what you don't want to but need to hear
    on Apr 28, 2019
  4. AlexanderOMara commented on May 1, 2019

    @AlexanderOMara

    Is there any information available on why file extension resolution was disabled?

  5. GeoffreyBooth commented on May 1, 2019

    @GeoffreyBooth
    Member
  6. sheerun commented on May 7, 2019

    @sheerun
  7. AlexanderOMara commented on May 7, 2019

    @AlexanderOMara

    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.

  8. MylesBorins commented on May 15, 2019

    @MylesBorins
    Contributor

    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

  9. ljharb commented on May 15, 2019

    @ljharb
    SponsorMemberAuthor

    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.

  10. AlexanderOMara commented on May 15, 2019

    @AlexanderOMara

    Is browser compatibility really even possible? As soon as you import another module, isn't browser compatibility lost?

    import {something} from 'some-module';
    console.log(something);

    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?

    Am I missing something here?

  11. 134 remaining items

  12. guybedford commented on Aug 5, 2020

    @guybedford
    Contributor

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

  13. weswigham commented on Aug 5, 2020

    @weswigham
    Contributor

    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.

  14. guybedford commented on Aug 5, 2020

    @guybedford
    Contributor

    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?

  15. guybedford commented on Aug 5, 2020

    @guybedford
    Contributor

    Also, in theory a file mapping is not a resolver extension it is a file system remapping.

  16. devsnek commented on Aug 5, 2020

    @devsnek
    Member

    Let it be known that people who do not use typescript also dislike the lack of extension resolution.

  17. weswigham commented on Aug 5, 2020

    @weswigham
    Contributor

    I must still admit I don't understand why. Also, isn't paths already an extension?

    1. 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.
    2. 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.

  18. guybedford commented on Aug 5, 2020

    @guybedford
    Contributor

    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.

  19. weswigham commented on Aug 5, 2020

    @weswigham
    Contributor

    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.

  20. guybedford commented on Aug 5, 2020

    @guybedford
    Contributor

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

  21. gengjiawen commented on Aug 5, 2020

    @gengjiawen
    Member

    --es-module-specifier-resolution flag is unlikely works. Is it okay we put another field in package.json for this ?

  22. chyzwar commented on Aug 5, 2020

    @chyzwar

    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.

  23. GeoffreyBooth commented on Aug 5, 2020

    @GeoffreyBooth
    Member

    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:

    image

    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.

  24. devsnek commented on Aug 5, 2020

    @devsnek
    Member

    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.

  25. weswigham commented on Aug 5, 2020

    @weswigham
    Contributor

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

  26. chyzwar commented on Aug 5, 2020

    @chyzwar

    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

    import module form 'main-module-package'; // with single main
    import cjsSubmodule from 'main-module-cjs/submodule' //cjs modules still search for extension ?
    import submoduleMapping from 'es-module-package/submodule'; // with subpath mapping
    import submoduleNoMapping from 'es-module-package/submodule.js'; // without subpath mapping 
    
    import base form '../base/index.js' // index.js as no longer supported
    import local from './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.

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

    cjsfeaturessurveysRelates to things where people to you what you don't want to but need to hear

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions