'Is there a way to combine ES6 imports and commonJS require in TS compiled files?
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.com/package/file-type) does not seem to accept the require
that is generated in the compiled .js
files.
To change that, I have to change the "module" property of tsconfig.json to another value other than "commonjs". However, it breaks the code and generates a lot of problems.
My tsconfig.json:
{
"compilerOptions": {
"target": "es6",
"module": "commonjs",
"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 compiler is complaining 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';
Solution 1:[1]
Short 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 compatibility with CommonJS require()
syntax. Therefore, when setting
"module": "commonJS"
In my tsconfig.json
file, the equivalent compiled 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": "commonJS"
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 compiled .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';
becomes
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 compilation time.
These steps solved the issue for me.
References for further reading:
Great explanation of tsconfig.json
properties: https://medium.com/@tommedema/typescript-confusion-tsconfig-json-module-moduleresolution-target-lib-explained-65db2c44b491
ECMAScript Modules in Node.js: https://www.typescriptlang.org/docs/handbook/esm-node.html
A bit more about importing files with .js extension: Appending .js extension on relative import statements during Typescript compilation (ES6 modules)
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | wrongbyte |