skip to Main Content

I’ve heard that when importing ES modules, you’re supposed to include a file extension. However, I came across some Node projects that import ES modules without an extension, like in this example.

I’m wondering how they do that.

2

Answers


  1. That’s right, with ES you must include the extension. However, when you work with Typescript, it automatically recognizes the extension, and if you put it, it gives an error, unless you activate the feature ‘allowImportingTsExtensions’. Example:
    enter image description here

    I would say it depends on how it is set up in the company where you work.

    Login or Signup to reply.
  2. Regarding your statement

    I’ve heard that when importing ES modules, you’re supposed to include a file extension

    Yes, this is covered in the documentation for NodeJS ESM, under import-specifiers:

    Relative specifiers like ‘./startup.js’ or ‘../config.mjs’. They refer to a path relative to the location of the importing file. The file extension is always necessary for these.

    and under the Mandatory File Extensions section:

    A file extension must be provided when using the import keyword to resolve relative or absolute specifiers. Directory indexes (e.g. ‘./startup/index.js’) must also be fully specified.

    This behaviour matches how import behaves in browser environments, assuming a typically configured server.


    As for your next statement,

    However, I came across some Node projects that import ES modules without an extension, like in this example.

    The example you linked is for a TypeScript project, not a regular ESM JavaScript/NodeJS project.

    Looking at lines 4 and 5 of the project’s tsconfig.json, we can see that there is

            "module": "CommonJS",
            "moduleResolution": "Node",
    

    A good place to start is TypeScript’s documentation on Module Resolution. which explains how the extensions can be omitted.

    For example, under the description for "node10" (which aliases to node for backwards compatibility), the following is stated:

    It supports looking up packages from node_modules, loading directory index.js files, and omitting .js extensions in relative module specifiers.

    and again in the Module – References: Extensionless Relative Paths section, we see that:

    In some cases, the runtime or bundler allows omitting a .js file extension from a relative path. TypeScript supports this behavior where the moduleResolution setting and the context indicate that the runtime or bundler supports it:

    // @Filename: a.ts
    export {};
    // @Filename: b.ts
    import {} from "./a";
    

    If TypeScript determines that the runtime will perform a lookup for ./a.js given the module specifier "./a", then ./a.js will undergo extension substitution, and resolve to the file a.ts in this example.

    Hope this helps :).

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search