15

I'm trying to get a handle on Node and ES modules. Specifically how/if you can omit the file extension from the path string value of the import statement (and optionally get VSCode to autocomplete those paths).

I understand you can either gives files the .mjs extension or set "type" = "modules" in the package.json but both approaches lead to the following problems.

  1. VSCode won't autocomplete the path if the file extension is .mjs, it only sees the file if it's .js. However if it is .js the autocomplete omits the extension from the string and the import fails until I add it manually.
  2. Trying to use a library like graphql inside my own modules also fails because all the import statements between the .mjs files in the graphql module have been written omitting the extension from the string.

SO... when is not including the extension valid with ES6 module imports, and is there anyway to get this condition enabled with NodeJS?

John S.
  • 159
  • 1
  • 4

1 Answers1

14

The node.js ES6 module implementation specifically does not do automatic file extension resolution as documented in https://nodejs.org/api/esm.html#esm_customizing_esm_specifier_resolution_algorithm :

The current specifier resolution does not support all default behavior of the CommonJS loader. One of the behavior differences is automatic resolution of file extensions and the ability to import directories that have an index file.

However this can be changed by a command line argument --experimental-specifier-resolution=[mode]

As such not giving a file extension is invalid by default but can be made valid depending on how you run node.js.

However, there are systems implemented before the ES6 spec was written that implements ES6-like import syntax such as Typescript and Babel. These systems assumed you can exclude file extensions in your imports. If you are using such a system to compile your ES6 imports to ES5 syntax you can exclude file extensions, sometimes, depending on if the version of the compiler you are using supports it.

slebetman
  • 109,858
  • 19
  • 140
  • 171
  • 1
    The ES6 specification does not require anything at all, it says that the host is responsible for resolving a module specifier to a module. – Bergi Aug 18 '20 at 06:07
  • Maybe you were thinking of the HTML5 spec, which does require a fully resolvable path for ES6 modules used natively in the browser, but OP is using nodejs. – Bergi Aug 18 '20 at 06:09
  • Hmm yes. But node.js implements the fully resolvable path "spec" as described in: https://nodejs.org/api/esm.html#esm_customizing_esm_specifier_resolution_algorithm. Quote: `One of the behavior differences is automatic resolution of file extensions`. I thought this was ES6 defined – slebetman Aug 18 '20 at 06:12
  • 1
    @Bergi Updated my answer to node docs instead of appealing to ES6 – slebetman Aug 18 '20 at 06:17
  • Thanks for the link, I had not been able to find that information myself, though it seems like it was in an appropriate location. I'm a little confused though by my second point RE: libraries like `graphql`, I guess it has either been written, with the flag as a requirement to run or not intended to be used with ES6 modules in nodejs? That said a solution I've been shown is the ability to use an import statement on an expected CommonJS package then destructure values spearately. `import GQLPkg from 'graphql'; const { GraphQLScalarType } = GQLPkg;` – John S. Aug 18 '20 at 15:56