When importing modules, I noticed sometimes imported files have their extension, example:
import { myFunc } from './foo.js';
Whereas other libraries, the imports do not:
import { myFunc } from './foo';
Is this related to ES modules vs CommonJS modules?
The answer to this question depends on your runtime and compilation environment, and also on wether you are using ES modules (the import syntax) or CommonJS modules (the require syntax). You will find below an overview about the most common cases.
Webpack (used by create-react-app and many others tools) works with ES modules like so:
If the path has a file extension, then the file is bundled straightaway. Otherwise, the file extension is resolved using the resolve.extensions option, which tells the resolver which extensions are acceptable for resolution, e.g.
.js,.jsx. More on the offical documentation.
If you are using ES modules with Node.js:
A file extension must be provided when using the
importkeyword to resolve relative or absolute specifiers. Directory indexes (e.g.'./startup/index.js') must also be fully specified. This behavior matches how import behaves in browser environments, assuming a typically configured server.
The
--experimental-specifier-resolution=[mode]flag can be used to customize the extension resolution algorithm. The default mode is explicit, which requires the full path to a module be provided to the loader. More on the official documentation.
If you are using ES modules in the browser without any compilation, there will be a request to a server to get the needed file, so you should add the extension. More on MDN's documentation.
If you are using CommonJS modules with Node.js:
If the exact filename is not found, then Node.js will attempt to load the required filename with the added extensions:
.js,.json, and finally.node. More on the official documentation.