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

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

Open
Andarist opened this issue May 2, 2023 · 7 comments Β· May be fixed by #58480 or #58526
Open
Assignees
Labels
Fix Available A PR has been opened for this issue Needs Investigation This issue needs a team member to investigate its status.

Comments

@Andarist
Copy link
Contributor

Andarist commented May 2, 2023

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

@fatcerberus
Copy link

Isn’t this controlled by allowSyntheticDefaultImports?

@andrewbranch
Copy link
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.

@andrewbranch andrewbranch self-assigned this May 2, 2023
@andrewbranch andrewbranch added the Needs Investigation This issue needs a team member to investigate its status. label May 2, 2023
@Andarist
Copy link
Contributor Author

Andarist commented May 2, 2023

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

@andrewbranch
Copy link
Member

andrewbranch commented May 17, 2023

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.

@Andarist
Copy link
Contributor Author

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?

@andrewbranch
Copy link
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 πŸ₯΄

@andrewbranch
Copy link
Member

@andrewbranch andrewbranch removed this from the TypeScript 5.5.0 milestone May 7, 2024
@andrewbranch andrewbranch added this to the TypeScript 5.6.0 milestone May 7, 2024
@andrewbranch andrewbranch linked a pull request May 8, 2024 that will close this issue
@typescript-bot typescript-bot added the Fix Available A PR has been opened for this issue label May 8, 2024
@andrewbranch andrewbranch linked a pull request May 13, 2024 that will close this issue
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Fix Available A PR has been opened for this issue Needs Investigation This issue needs a team member to investigate its status.
Projects
None yet
5 participants