Can't access node functions when tsconfig moduleResolution is set to "bundler"
alex-e-leon opened this issue · 8 comments
I'm using tsup to bundle my node project into a single file, and have moduleResolution: "bundler"
set in my tsconfig because a) it feels like the right choice as I'm using a bundler, and b) it allows me to ignore writing out file type extensions with esm.
Unfortunately it looks like file-type only exposes node functions like fileTypeFromFile
when moduleResolution is set to "node". I get that this is done to add extra safety and prevent consumers from importing functions that don't work in the environment that they are running, and 99% of projects with moduleResolution: "bundler"
are likely headed for the browser, but considering that bundling is valid for node projects as well, I wonder whether the exports should be relaxed for this?
Unfortunately it looks like file-type only exposes node functions like fileTypeFromFile when moduleResolution is set to "node".
file-type exports Node.js specific functions, like fileTypeFromFile
, to JavaScript engines of type "node".
The moduleResolution
, in your project, determines the module resolution strategy, and does, or could potentially, impact if the ESM exports
(field in package.json
) is correctly interpreted. More info: TypeScript: Module Resolution
Looks like "moduleResolution" = "bundler"
is desgined for Bundlers transpiling to CommonJs. Maybe give "node16"
a try.
functions that don't work in the environment that they are running, and 99% of projects with moduleResolution: "bundler" are likely headed for the browser, but considering that bundling is valid for node projects as well
That would break browser projects, they are not only ending up with functions they cannot use, but more problematic
are the Node.js API dependencies.
This is a problem your project, possibly your bundler, not in file-type
.
Hey @Borewit unfortunately the issue is either a mismatch between nodes spec and typescripts spec, and has nothing to do with the bundler itself. I haven't actually checked how the bundler behaves in this case, because I get a typescript error which is preventing me from bundling at all.
Node says for exports "node" - matches for any Node.js environment. Can be a CommonJS or ES module file. In most cases explicitly calling out the Node.js platform is not necessary.
While typescript says for moduleResolution: 'bundler' for use with bundlers. Like node16 and nodenext, this mode supports package.json "imports" and "exports", but unlike the Node.js resolution modes, bundler never requires file extensions on relative paths in imports.
However in spite of those 2 specs agreeing that bundler should behave like node16/nodenext most use cases for moduleResolution: bundler
typescript is not reading the node
export, I believe because most use cases for moduleResolution: bundler
are to bundle for the browser.
You can setup a quick example project to see that it is just this property in tsconfig which is preventing it from being imported.
In my case I've "fixed" the issue by switching from an ESM import to require, but I thought I'd raise it because commonJS isn't great and maybe there's a better way to expose the different functions in this situation, like exporting the "node" functions at a different path/name when moduleResolution is bundler.
@alex-e-leon Can you try this branch? explicit-node-subpath-export, b75fc02
npm install 'sindresorhus/file-type#b75fc026c899eb6be1e192d96032ad7a10737460'
Using subpath import file-type/node
I doubt it will solve your problem, as some of the sub-dependencies also rely on the node engine dependency.
You need to open an issue on the TypeScript repo about this. This package is defined correctly. The problem is that moduleResolution: bundler
assumes not node
, so they need to add a way to override that. It doesn't make sense to do anything here.
Hey, sorry for taking a while to get to this. Like I said I've already worked around it so it took me a while to get the time to come back to this.
@Borewit - your branch almost works. as there's no exported node.js
file it doesn't resolve, but if instead the exports look like:
"./node": {
"types": "./index.d.ts",
"import": "./index.js"
}
Then yes I can import successfully with import {fileTypeFromFile} from 'file-type/node';
@sindresorhus - you're right it's really an issue with typescript, but considering the current state of the landscape I figured you'd want to know about the issue and potentially include a work around until typescript + node figure it out. With a bit of tweaking @Borewit 's solution seems to work fine and I don't think leaves too big a footprint for a workaround. But it's not blocking me at all, so I'll leave it up to you to make the call.
Thanks both for being so responsive!