sindresorhus/file-type

fileTypeFromFile not found

kenn opened this issue · 16 comments

kenn commented

Description

import { fileTypeFromFile } from 'file-type'

'"file-type"' has no exported member named 'fileTypeFromFile'. Did you mean 'fileTypeFromBlob'?ts(2724)

Existing Issue Check

  • I have searched the existing issues and could not find any related to my problem.

ESM (ECMAScript Module) Requirement Acknowledgment

  • My project is an ESM project and my package.json contains the following entry: "type": "module".

File-Type Scope Acknowledgment

  • I understand that file-type detects binary file types and not text or other formats.

Can you describe your environment @kenn ?
Which JavaScript engine are you using? Node.js (which version?), browser?
What value is "type" in your package.json?

kenn commented

Pretty standard setup — node v20, Remix / Vite, and yes, "type": "module". I see the issue in VSCode.

Image

fileTypeFromFile is a Node.js (back-end) specific function. This Node.js engine only function is only exported via the Node.js entry point:

file-type/package.json

Lines 14 to 24 in 8dfed93

"exports": {
".": {
"node": {
"types": "./index.d.ts",
"import": "./index.js"
},
"default": {
"types": "./core.d.ts",
"import": "./core.js"
}
},

Ref: https://nodejs.org/api/packages.html#conditional-exports

The error message indicates that fileTypeFromBlob can be resolved, so something on your end has decided the typings should not be loaded as a Node.js engine, but seems to take the "default" (front-end) route, which is lacking the Node.js functions.

kenn commented

Ah I see, setting override in tsconfig.json fixed the problem. Thanks!

    "paths": {
      "~/*": ["./app/*"],
      "file-type": ["./node_modules/file-type/index.d.ts"] // Added override for file-type types
    },
kenn commented

Um, didn't work. Do you need more information to figure out the issue?

SyntaxError: The requested module 'file-type' does not provide an export named 'fileTypeFromFile'
    at ModuleJob._instantiate (node:internal/modules/esm/module_job:132:21)
    at async ModuleJob.run (node:internal/modules/esm/module_job:214:5)
    at async ModuleLoader.import (node:internal/modules/esm/loader:329:24)
    at async loadESM (node:internal/process/esm_loader:28:7)
    at async handleMainPromise (node:internal/modules/run_main:113:12)

This probably comes down to the fact that the "moduleResolution": "bundler" setting in tsconfig.json (e.g. default in create-next-app setups) instructs TS to not pick up the node conditional export.

Another "solution" would be to add "customConditions": ["node"] to the tsconfig.json, but I suspect this will cause trouble down the line when using modules client-side (in the browser) and suddenly their node exports are being used.

Interestingly, file-type is the first module with which I ran into this problem. Would love to hear about a better fix for this.

Good suggestions @soulchild, stating the Node API dependency somehow in Typescript configuration, is in imho the way to go.

Interestingly, file-type is the first module with which I ran into this problem.

Not so many ECMAScript modules have a Node specific entry point defined I guess.

kenn commented

I'm still getting the same error as of v20.3.0, fyi

It looks like that the result of TypeScript compiler option moduleResolution set to bundler is causing the Node entry point not being imported. Not much we can change about that.

To workaround, you now have the following options:

Option #​1

Set the node condition in the TypeScript compiler option:

{
  "compilerOptions": {
    "target": "es2022",
    "moduleResolution": "bundler",
    "customConditions": ["node"] // Set this condition
  }
}

Option #​2

Import the Node entry point via a subpath import:

import { fileTypeFromFile } from 'file-type/node'

Do you mind trying out both options, and let us know the result @kenn ?

@Borewit @sindresorhus

The problem is not related to the the Typescript setup IMHO.
The core.js literally does not export this funtion

Image
kenn commented

Option 1 didn't work

import { fileTypeFromFile } from 'file-type'
         ^

SyntaxError: The requested module 'file-type' does not provide an export named 'fileTypeFromFile'

But Option 2 worked! Thank you @Borewit !

Maybe we should just update README?

Option 1 didn't work

Weird that the Custom Conditions (option 1) does not work, that is literately the use case provided by the TypeScript documentation, how the Custom Conditions can be used.

But Option 2 worked! Thank you @Borewit !

Great, good to have that confirmed!

Maybe we should just update README?

I agree, I think this is worth mentioning in the README, that if for some reason the import conditions do not work, users can force importing the Node entry point via the subpath.

@cschroeter

The core.js literally does not export this funtion

Correct, and it should not, as this is the default entry point. Node specific function should not be exposed via the default entry point, they should only be exposed via the node entry point (./index.js).

Please read the full discussion and it will start to make sense.

@Borewit

If a function is only available in a specific context (e.g., Node), this should be reflected in the import itself. eg.

import { fileTypeFromFile } from 'file-type/node'

IMHO, export resolution should only be used to switch between different formats (ESM/CJS).

@cschroeter

IMHO, export resolution should only be used to switch between different formats (ESM/CJS).

You aren’t a big fan of automatic transmissions in cars either, I suppose?

Setting node statically in the customConditions (option 1) works as well.

{
   "moduleResolution": "bundler",
   "customConditions": ["node"], // Set this condition
}

Using music-metadata with the same conditional export:
Evidence: lowvisiondave/music-metadata-typescript-repro#1