Skip to content

moduleResolution: bundler diverges from bundlers following node semantics in regards to default import #54102

Description

Bug Report

🔎 Search Terms

moduleResolution bundler node webpack default namespace

🕗 Version & Regression Information

  • This is the behavior in every version I tried: 5.0.4 (the only one with moduleResolution: bundler

⏯ Playground Link

Repro case can be found here

🙁 Actual behavior

When resolving a .d.mts file that proxies to .d.ts in a CJS package with moduleResolution: bundler, TypeScript complains with an error like:

Property 'default' does not exist on type 'string'.ts(2339)

The reason that it complains about it is that TypeScript is resolving default import as if the .d.ts in this CJS package could export the "real" default export. webpack actually follows semantics coined by node and it loads the namespace object as the default export of that "cjs file".

So the type-level reality diverges there from runtime. At runtime, with webpack - it works exactly like it would work in node. TypeScript thinks that the intention was to load module.exports.default there though.

🙂 Expected behavior

I'm not exactly sure what's the expected behavior here because "resolving" like a bundler is quite under-specified and bundlers are not quite consistent when it comes to this. Even when we check this very same repro, we can see that I had to use defaultIsModuleExports: true in Rollup for it to behave the same. By default, Rollup behaves in the same way as TypeScript does.

cc Andrew Branch (@andrewbranch)

Activity

  1. fatcerberus commented on May 2, 2023

    @fatcerberus

    Isn’t this controlled by allowSyntheticDefaultImports?

  2. andrewbranch commented on May 2, 2023

    @andrewbranch
    Member

    Yeah, I’m aware of this, but of course it’s not a moduleResolution issue. Really, it’s something that module should handle. I believe behavior differs between different bundlers, which is, in technical terms, a huge bummer. What’s really bad, IMO, is that last I checked, Webpack does the Node behavior for .{m,c}js files, but not the equivalent TS extensions, which violates a pretty core assumption we have about the substitutability of declaration files, TS files, and JS files. This is already on my radar to investigate soon.

  3. Andarist commented on May 2, 2023

    @Andarist
    ContributorAuthor

    If you ever need a hug... I'm here for you.

  4. andrewbranch commented on May 17, 2023

    @andrewbranch
    Member

    I forked https://sokra.github.io/interop-test to https://andrewbranch.github.io/interop-test, removed a bunch of import and export variations that weren’t super interesting to me (there are still a ton), and added .ts/.mts cases to esbuild and Webpack. I need to add similar TS cases to Rollup, and I’d like to add Parcel, Bun, and perhaps others. But it shows basically what we’ve already established in this issue:

    • Both esbuild and Webpack give a Node-ESM-like interop treatment to default imports in .mjs files (that is, it always synthesizes a default; it doesn’t care about whether the target module has a __esModule marker).
    • esbuild extends this same treatment to .mts files since it understands TypeScript out of the box. Webpack does not.

    At first, it struck me as odd that these bundlers voluntarily adopted interop restrictions that Node tried very hard to avoid, but couldn’t for technical reasons. But doing so is the best way for them to handle code written for Node—it’s a cross-compatibility strategy. I think it would be best to push all bundlers toward doing this, if they’re not already. But since TypeScript isn’t yet set up to model this behavior (without also adopting nodenext module resolution), it seems inevitable that if we do, it will be toggleable with compiler options somehow. So even if we can’t get bundlers to be consistent between each other on whether they apply this Node-compat behavior, users will be able to configure TypeScript for whatever their bundler does.

    However, it’s not possible for us to model bundlers like Webpack that apply the Node-compat behavior to .mjs files, but not to .mts files. That’s something we’ll need to push for across the bundler/TS-runtime ecosystem (I’m assuming Webpack isn’t the only one that will need changes) before any TS option can work.

  5. Andarist commented on May 17, 2023

    @Andarist
    ContributorAuthor

    However, it’s not possible for us to model bundlers like Webpack that apply the Node-compat behavior to .mjs files, but not to .mts files. That’s something we’ll need to push for across the bundler/TS-runtime ecosystem (I’m assuming Webpack isn’t the only one that will need changes) before any TS option can work.

    I recall that you mentioned knowing about this issue for quite some time. Is there some tracking issue about this in webpack? Did their team comment on this anyhow in the past?

  6. andrewbranch commented on May 17, 2023

    @andrewbranch
    Member

    I don’t know; I’m in the process of figuring out exactly what to propose, and plan to talk to Sean soon. I haven’t mentioned it earlier because it was something I noticed in passing while focusing on module resolution, and needed to double check that I wasn’t just holding it wrong 🥴

  7. andrewbranch commented on May 30, 2023

    @andrewbranch
    Member
  8. andrewbranch commented on May 28, 2024

    @andrewbranch
    Member

    Mateusz Burzyński (@Andarist) do you remember how you originally encountered this issue, i.e. what libraries were involved?

  9. Andarist commented on May 29, 2024

    @Andarist
    ContributorAuthor

    Andrew Branch (@andrewbranch) likely it comes from me and Emma Hamilton (@emmatown) trying to implement our library building scheme that is meant to avoid dual package hazard and to provide consistent module shape across formats. We ended up emitting an extra proxy files to work around it. Notice the _default vs default dance introduced here: preconstruct/preconstruct#546

  10. khallmark commented on Nov 3, 2024

    @khallmark

    Andrew Branch (@andrewbranch) what ever came of this change as well as this webpack issue?

  11. andrewbranch commented on Nov 4, 2024

    @andrewbranch
    Member

    Our current opinion is that the prevalence of people having problems with this doesn’t merit additional confusing configuration.

  12. added
    SuggestionAn idea for TypeScript
    and removed
    Needs InvestigationThis issue needs a team member to investigate its status.
    Fix AvailableA PR has been opened for this issue
    on Nov 6, 2024
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

    Awaiting More FeedbackThis means we'd like to hear from more people who would be helped by this featureSuggestionAn idea for TypeScript

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions