How Node.js handles ES6 modules

How Node.js handles ES6 modules

Learn JavaScript and you’ll find that it has modules in two formats.

One is the ES6 module, referred to as ESM; the other is the CommonJS module dedicated to Node.js, referred to as CJS. The two modules are not compatible.

Many people use Node.js and only use require()load modules, and they don’t know what to do when they encounter ES6 modules. This article will talk about how to use ES6 modules in Node.js.

Differences between the two modules

ES6 modules are very different from CommonJS modules.

Syntax Above, CommonJS modules use require()load and module.exportsoutput, ES6 modules use importand export.

The usage above require()is synchronous loading, and the following code must wait for the command to be executed before it is executed. importCommands are loaded asynchronously, or to be more precise, ES6 modules have a separate static analysis phase, and the analysis of dependencies is done in that phase, and the lowest-level module is executed first.

Node.js distinction

Node.js requires ES6 modules to have .mjssuffixed filenames. That is to say, as long as the script file uses importor commands, the suffix name exportmust be used . .mjsWhen Node.js encounters a .mjsfile, it thinks it’s an ES6 module, and strict mode is enabled by default and doesn’t have to be specified at the top of each module file "use strict".

If you don’t want to change the suffix name to , you can specify the field as .mjsin the project package.jsonfile .typemodule

{
   "type": "module"
}

Once set, the JS scripts in this directory are interpreted as ES6 modules.

$ node my-app.js

If you want to use the CommonJS module at this time, you need to change the suffix name of the CommonJS script to .cjs. If there is no typefield, or if the typefield is commonjs, the .jsscript is interpreted as a CommonJS module.

To sum up in one sentence: .mjsfiles are always loaded as ES6 modules, .cjsfiles are always loaded as CommonJS modules, and .jsthe loading of files depends on the settings of the fields package.jsoninside .type

Note, try not to mix ES6 modules with CommonJS modules. requireThe command cannot load .mjsthe file, and an error will be reported. Only importthe command can load the .mjsfile. Conversely, commands .mjscannot be used in files , they must be used .requireimport

CommonJS modules load ES6 modules

CommonJS require()commands cannot load ES6 modules, and an error will be reported. You can only use import()this method to load them.

(async () => {
  await import('./my-app.mjs');
})();

The above code can be run in a CommonJS module.

require()One reason why ES6 modules are not supported is that they are loaded synchronously, and ES6 modules can use top-level awaitcommands inside them, so they cannot be loaded synchronously.

ES6 modules load CommonJS modules

The commands of ES6 modules importcan load CommonJS modules, but only as a whole, not just a single output item.

// right
import packageMain from 'commonjs-package';

// error
import { method } from 'commonjs-package';

This is because ES6 modules need to support static code analysis, and the output interface of CommonJS modules module.exportsis an object that cannot be statically analyzed, so it can only be loaded as a whole.

Loading a single output item can be written as follows.

import packageMain from 'commonjs-package';
const { method } = packageMain;

Modules that support both formats at the same time

It’s easy for a module to support both CommonJS and ES6 formats.

If the original module is in ES6 format, then an overall output interface needs to be given, for example export default obj, so that CommonJS can be used import()for loading.

If the original module is in CommonJS format, then a wrapper layer can be added.

import cjsModule from '../index.js';
export const foo = cjsModule.foo; 

The above code first inputs the CommonJS module as a whole, and then outputs the named interface as needed.

You can change the suffix of this file to .mjs, or put it in a subdirectory, and then put a separate package.jsonfile in this subdirectory, indicating { type: "module" }.

Another way is to specify the respective loading entry of the two format modules in package.jsonthe field of the file .exports

"exports":{ 
    "require": "./index.js",
    "import": "./esm/wrapper.js" 
}

The above code specifies require()and import, loading the module will automatically switch to a different entry file.