Search code examples
javascriptnode.jstypescriptes6-modulescommonjs

How to import from a file with an .mts extension in a CJS-first project?


So I have here a fetchin.mts file:

import type { RequestInfo, RequestInit, Response } from "node-fetch";
const importDynamic = new Function("modulePath", "return import(modulePath);")

export async function fetch(url: URL | RequestInfo, init?: RequestInit) : Promise<Response> {
  const {default: fetch} = await importDynamic("node-fetch");
  return fetch(url, init);
}

When I try to import the fetch function on another file like this:

import {fetch} from "../utils/fetchin.mjs"

I get a ts error of :

The current file is a CommonJS module whose imports will produce 'require' calls; however, the referenced file is an ECMAScript module and cannot be imported with 'require'. Consider writing a dynamic 'import("../utils/fetchin.mjs")' call instead.
  To convert this file to an ECMAScript module, change its file extension to '.mts', or add the field `"type": "module"` to '/package.json'.ts(1479)

I already tried the suggestion like putting "type": "module" on package json but I still can't fix the ts error when importing it to another file and tried researching about this stuffs on google but I can't find any reference about it

Here is my tsconfig.json file:

{
  "extends": "@tsconfig/node18/tsconfig.json",
  "compilerOptions": {
    "removeComments": false,
    "preserveConstEnums": true,
    "outDir": "lib/",
    "sourceMap": true,
    "esModuleInterop": true,
    "strict": true
  },
  "ts-node": {
    "files": ["src/types/modules.d.ts"],
  }
}

I also saw some article that I need to downgrade tsconfig/node18 to tsconfig/node16 but I still don't get it. Help guys!


Solution

  • You haven't really said what file extension the other file importing fetch is using, but I'm going to assume it is a .ts extension, and that your package.json is using "type": "commonjs" (or not specified, so default to CJS). I'm also going to ignore the use of ts-node and focus on tsc.

    Here is the tsconfig.json (like yours but without the ts-node stuff):

    {
      "extends": "@tsconfig/node18/tsconfig.json",
      "compilerOptions": {
        "removeComments": false,
        "preserveConstEnums": true,
        "outDir": "lib/",
        "sourceMap": true,
        "esModuleInterop": true,
        "strict": true
      }
    }
    

    Simple fix is to use "type": "module" in your package.json. Then just run ./node_modules/.bin/tsc to get a successful build. I know you said you tried this, but you must have overlooked something because this does in fact work.

    If you want to continue using CJS, i.e. "type": "commonjs", then use a dynamic import when importing fetch from the .mts file.

    Lets call the other file other.ts:

    const importFetch = async () => {
      const { fetch } = await import('./fetchin.mjs')
    }
    
    importFetch()
    

    Now run tsc again like before to get a successful build, or run ts-node other.ts. This is by the way exactly what the error message was suggesting.