I have changed the code of a project made in NodeJS to Typescript.
Everything is working fine, except for the fact that apparently a third-part package (file-type, ) does not seem to accept the require
that is generated in the piled .js
files.
To change that, I have to change the "module" property of tsconfig.json to another value other than "monjs". However, it breaks the code and generates a lot of problems.
My tsconfig.json:
{
"pilerOptions": {
"target": "es6",
"module": "monjs",
"allowJs": true,
"lib": ["ES6"],
"esModuleInterop": true,
"moduleResolution": "node",
"outDir": "build",
"rootDir": "src",
"skipLibCheck": true,
"strict": true
}
}
The error I get:
const filetype = __importStar(require("file-type"));
^
Error [ERR_REQUIRE_ESM]: require() of ES Module C:\Users\user\Desktop\repos\art-api\node_modules\file-type\index.js from C:\Users\user\Desktop\repos\art-api\build\middlewares\process-image.js not supported.
Instead change the require of index.js in C:\Users\user\Desktop\repos\art-api\build\middlewares\process-image.js to a dynamic import() which is available in all CommonJS modules.
at Object.<anonymous> (C:\Users\user\Desktop\repos\art-api\build\middlewares\process-image.js:33:31)
at Object.<anonymous> (C:\Users\user\Desktop\repos\art-api\build\controllers\artworkControllers.js:19:25)
at Object.<anonymous> (C:\Users\user\Desktop\repos\art-api\build\routers\artworkRouter.js:7:30)
at Object.<anonymous> (C:\Users\user\Desktop\repos\art-api\build\server.js:9:41) {
code: 'ERR_REQUIRE_ESM'
}
Apparently, the problem is that the code generated in JavaScript conflicts with the code of the package, that uses the ES6 export syntax. If this is correct, how can I fix this issue? Is there a way to generate .js code with import syntax only for this particular package, or some workaround like that? Other parts of the code don't give me any problems, only the import of this package(file-type).
Just in case, this is the index.js of 'file-type', where it has the import the piler is plaining about:
import * as strtok3 from 'strtok3';
import {fileTypeFromTokenizer} from './core.js';
export async function fileTypeFromFile(path) {
const tokenizer = await strtok3.fromFile(path);
try {
return await fileTypeFromTokenizer(tokenizer);
} finally {
await tokenizer.close();
}
}
export * from './core.js';
I have changed the code of a project made in NodeJS to Typescript.
Everything is working fine, except for the fact that apparently a third-part package (file-type, https://www.npmjs./package/file-type) does not seem to accept the require
that is generated in the piled .js
files.
To change that, I have to change the "module" property of tsconfig.json to another value other than "monjs". However, it breaks the code and generates a lot of problems.
My tsconfig.json:
{
"pilerOptions": {
"target": "es6",
"module": "monjs",
"allowJs": true,
"lib": ["ES6"],
"esModuleInterop": true,
"moduleResolution": "node",
"outDir": "build",
"rootDir": "src",
"skipLibCheck": true,
"strict": true
}
}
The error I get:
const filetype = __importStar(require("file-type"));
^
Error [ERR_REQUIRE_ESM]: require() of ES Module C:\Users\user\Desktop\repos\art-api\node_modules\file-type\index.js from C:\Users\user\Desktop\repos\art-api\build\middlewares\process-image.js not supported.
Instead change the require of index.js in C:\Users\user\Desktop\repos\art-api\build\middlewares\process-image.js to a dynamic import() which is available in all CommonJS modules.
at Object.<anonymous> (C:\Users\user\Desktop\repos\art-api\build\middlewares\process-image.js:33:31)
at Object.<anonymous> (C:\Users\user\Desktop\repos\art-api\build\controllers\artworkControllers.js:19:25)
at Object.<anonymous> (C:\Users\user\Desktop\repos\art-api\build\routers\artworkRouter.js:7:30)
at Object.<anonymous> (C:\Users\user\Desktop\repos\art-api\build\server.js:9:41) {
code: 'ERR_REQUIRE_ESM'
}
Apparently, the problem is that the code generated in JavaScript conflicts with the code of the package, that uses the ES6 export syntax. If this is correct, how can I fix this issue? Is there a way to generate .js code with import syntax only for this particular package, or some workaround like that? Other parts of the code don't give me any problems, only the import of this package(file-type).
Just in case, this is the index.js of 'file-type', where it has the import the piler is plaining about:
import * as strtok3 from 'strtok3';
import {fileTypeFromTokenizer} from './core.js';
export async function fileTypeFromFile(path) {
const tokenizer = await strtok3.fromFile(path);
try {
return await fileTypeFromTokenizer(tokenizer);
} finally {
await tokenizer.close();
}
}
export * from './core.js';
Share
Improve this question
edited Oct 21, 2022 at 21:41
wrongbyte
asked Jan 24, 2022 at 21:19
wrongbytewrongbyte
3334 silver badges13 bronze badges
6
- 1 I encountered the exact same issue earlier today trying to use npmjs./package/callsites with typescript, wasn't able to find a solution. – axtck Commented Jan 24, 2022 at 21:38
- 2 FWIW, here's the Node.js documentation on using ESM and CommonJS together. I don't know how TypeScript has to be configured to handle this though. – FZs Commented Jan 24, 2022 at 22:03
- @axtck I finally found a way to solve it, try to test if this solution works for you – wrongbyte Commented Jan 31, 2022 at 1:06
- @wrongbyte will take a look! – axtck Commented Feb 6, 2022 at 12:09
- "I have changed the code of a project made in NodeJS to Typescript." - are you saying that you had a working version of the project in plain JS? What import style did it use? – Bergi Commented May 4, 2022 at 22:28
2 Answers
Reset to default 4Short answer: no; you will have to choose either CommonJS or ES6 modules.
Well, after some research I was able to understand and fix the issue.
The point is that the package I am using, file-type, does not have backwards patibility with CommonJS require()
syntax. Therefore, when setting
"module": "monJS"
In my tsconfig.json
file, the equivalent transpiled javascript file would use require()
; more specifically, this way:
const file_type_1 = __importDefault(require("file-type"));
Or some variations like:
const filetype = __importStar(require("file-type"));
It conflicted with the index.js
file of file-type
, which uses a default export, as you can see on the code posted along with my question.
So, as I intended to keep using this package, I had to change the entire import/export system of the files, what involved the following steps:
1 - Change "module": "monJS"
to "module": "ES6"
on tsconfig.json
It will probably result on errors such as "Cannot use import statement outside a module". It happens because we've just changed the syntax of transpiled .js files, that now have the import/export
syntax instead of require()
.
In order to make NodeJS handle this, we must follow the second step:
2 - Add "type":"module"
on package.json
3 - Change extension of relative imports
Second step will solve the first problem, but then we will have to use the full path when importing files - it is, adding .js
on the relative imports. So,
import getImageRouter from './routers/imageRouter';
bees
import getImageRouter from './routers/imageRouter.js';
Yes, even though it seems weird, we use the .js extension on the imports. Typescript will be smart enough to resolve it for us during pilation time.
These steps solved the issue for me.
References for further reading:
Great explanation of tsconfig.json
properties: https://medium./@tommedema/typescript-confusion-tsconfig-json-module-moduleresolution-target-lib-explained-65db2c44b491
ECMAScript Modules in Node.js: https://www.typescriptlang/docs/handbook/esm-node.html
A bit more about importing files with .js extension: Appending .js extension on relative import statements during Typescript pilation (ES6 modules)
As a maintainer of file-type
we remendation to migrate your project to ESM, as the best future proof solution.
But if you want to stick to CommonJS with your TypeScript project, you can load the pure file-type
ES-Module the following way:
import {loadEsm} from 'load-esm';
(async () => {
const fileType = await loadEsm<typeof import('file-type')>('file-type');
})();
Possibly loading of an ES-Module is less of a problem starting from Node.js version 22, as require()
seems to have some ES-Module loading capabilities.