'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