This article is published by Dr. Axel on 2ality, a personal website. The full article analyzes the new features of Node.js12 module in detail.

  • Original text: 2 ality.com/2019/04/nod…
  • Translator: Li Chu Wu

Node.js 12 (released on April 23) brings improved support for ECMAScript modules. It implements the second phase of the plan released late last year. Currently, this support is available in experimental-modules.

Read on to learn how this new support for ECMAScript modules works.

Brief note: The file extension.mjs is more convenient, but.js can also be enabled for ES modules.

1. Terms and abbreviations used in this article

  • CommonJS Module (CJS) : Refers to the original Node.js module standard.
  • ECMAScript Module (ES module, ESM) : Refers to a module standardized by the ECMAScript specification.
  • Package. prop refers to package.json of prop.

2. Module specifier

A module specifier is a string that identifies a module. They work slightly differently in the CommonJS module than in the ES module. Before we look at the differences, we need to understand the different categories of module specifiers.

2.1 Category of module specifiers

In the ES module, we distinguish specifiers of the following categories. These categories are derived from the CommonJS module.

  • Relative path: starts with a point. Ex. :
./some/other/module.mjs .. /.. /lib/counter.mjsCopy the code
  • Absolute path: Starts with a slash. Ex. :
/home/jane/file-tools.mjs
Copy the code
  • Urls: Includes protocols (technically, paths are also urls). Ex. :
'https://example.com/some-module.mjs'
'file:///home/john/tmp/main.mjs'
Copy the code
  • Bare path: Does not start with a dot, slash, or protocol, and consists of a single file name without an extension. Example:
'lodash'
'the-package'
Copy the code
  • Deep import path: Starts with a bare path and has at least one slash. Ex. :
'the-package/dist/the-module.mjs'
Copy the code

2.2 CommonJS module specifier

This is how CommonJS handles module specifiers:

  • CommonJS does not support urls as specifiers.
  • Relative and absolute paths are handled as expected.
  • You can load the directory foo as a module:
    • If you have a file foo/index.js
    • If there is a file in foo/package.json whose property “main” points to the module file.
  • Resolve the raw path and deep import path based on the directory found by node_modules:
    • In the same directory as the import module
    • In the parent of the directory
    • The other.
  • If the specifier X does not reference a file, the system tries to use the specifierX.js.X.jsonandX.node.

In addition, CommonJS modules can access two special module global variables:

  • __filename: contains the path of the current module.
  • __dirname: Contains the path to the parent directory of the current module.

Source of this section: Module page in the Node.js documentation.

2.3 ES module specifier in Node.js

  • With the exception of bare paths, all specifiers must refer to actual files. In contrast to CommonJS, ESM does not add missing file extensions.
  • File only: protocol that supports URL specifiers.
  • Currently, absolute paths are not supported. As a workaround, you can use file:///. URL at the beginning.
  • Relative paths are resolved in the Web browser – relative to the path of the current module.
  • The bare path is resolved relative to the node_modules directory. Modules referenced by the bare path are specified through package.main (similar to CJS).
  • The deep import path is also resolved relative to the node_modules directory.
  • Importing directories is not supported. In other words, neither package.main (for packages only) nor index.* works.

All built-in Node.js modules are available via the bare path and are named ESM exports. Such as:

import * as path from 'path';
import * as assert from 'assert';

assert.equal(
  path.join('a/b/c'.'.. /d'), 'a/b/d');
Copy the code

2.4 File name extension

Node.js supports the following default file extensions:

  • MJS is used for the ES module

  • CJS is the CommonJS module file extension for ESM or CommonJS. Which one it is, depending on package.type, has two Settings:

  • Commonjs (default) : Files with a.js extension or no extension are resolved to commonjs.

    "type": "commonjs"
    Copy the code
  • Module: files with the.js extension or without the extension are resolved to ESM.

    "type": "module"
    Copy the code

To find a package.json given file, Node.js will search in the same directory as the file, parent directory, etc.

2.5 Interpret non-file source code as CommonJS or ESM

Not all source code for Node.js implementations comes from files. You can also send code –print via stdin –eval. The command line option –input-type allows you to specify how to interpret such code:

  • As CommonJS (default) : –input-type= CommonJS
  • As ESM: –input-type=module

3. Interoperability

3.1 Importing CommonJS from the ESM

Currently, there are two options for importing CommonJS modules from ES modules.

Consider the following CommonJS module.

// common.cjs
module.exports = {
  foo: 123,
};
Copy the code

The first option is to import it by default (support for named imports may be added in the future) :

// es1.mjs
import * as assert from 'assert';

import common from './common.cjs'; // default import
assert.equal(common.foo, 123);
Copy the code

The second option is to use createRequire() :

// es2.mjs
import * as assert from 'assert';

import {createRequire} from 'module';
const require = createRequire(import.meta.url);

const common = require('./common.cjs');
assert.equal(common.foo, 123);
Copy the code

3.2 Importing ESM from CommonJS

If you want to import an ES module from a CommonJS module, you can use the import() operator.

For example, use the following ES module:

// es.mjs
export const bar = 'abc';
Copy the code

Here we import it from the CommonJS module:

// common.cjs
const assert = require('assert');

async function main() {
  const es = await import('./es.mjs');
  assert.equal(es.bar, 'abc');
}
main();
Copy the code

4. Various other functions

4.1 the import. Meta. Url

Since __filename and __dirname are not available in the ES module, we need an alternative. Import.meta. url is another option. It contains file: a URL with an absolute path. Such as:

'file:///Users/rauschma/my-module.mjs'
Copy the code

Note: url.fileurltopath () is used to extract the path -new url (). Pathname does not always work:

import * as assert from 'assert';
import {fileURLToPath} from 'url';

//::::: Unix :::::

const urlStr1 = 'file:///tmp/with%20space.txt';
assert.equal(
  new URL(urlStr1).pathname, '/tmp/with%20space.txt');
assert.equal(
  fileURLToPath(urlStr1), '/tmp/with space.txt');

const urlStr2 = 'file:///home/thor/Mj%C3%B6lnir.txt';
assert.equal(
  new URL(urlStr2).pathname, '/home/thor/Mj%C3%B6lnir.txt');
assert.equal(
  fileURLToPath(urlStr2), '/ home/thor/Mjolnir. TXT');

//::::: Windows :::::

const urlStr3 = 'file:///C:/dir/';
assert.equal(
  new URL(urlStr3).pathname, '/C:/dir/');
assert.equal(
  fileURLToPath(urlStr3), 'C:\\dir\\');
Copy the code

The next section demonstrates the use of import.meta.url and url.fileurltopath ().

Conversely, url.fileurltopath () is url.pathtoFileurl () : it converts the path to a fileURL.

4.2 fs. Promises

Fs.promises contains the Promisified version of fsAPI and works as expected

import {fileURLToPath} from 'url';
import {promises as fs} from 'fs';

async function main() {
  // The path of the current module
  const pathname = fileURLToPath(import.meta.url);
  const str = await fs.readFile(pathname, {encoding: 'UTF-8'});
  console.log(str);
}
main();

Copy the code

4.3 – experimental – json – modules

Using the flag –experimental-json-modules, node.js loads the. Json file as JSON.

Take a JSON file as an example. Data. JSON:

{
  "first": "Jane"."last": "Doe"
}
Copy the code

It can be imported from the ES module as follows (if both ESM and JSON module flags are used) :

import * as assert from 'assert';
import data from './data.json';

assert.deepEqual(
  data,
  {first: "Jane", last: "Doe"});
Copy the code

5. ES module on NPM

Currently, ES module references on NPM can be made in one of two ways:

  • require('mylib')
  • import from 'mylib

You can’t do both (deep import paths are the logical solution). We are working to change that. It may be done through the more powerful capabilities of package.main.

Before the feature is ready, the following requirements are placed on the personnel handling the feature:

"Please do not release any ES module packages for Node.js until this is resolved."Copy the code

6. Use the ES module on Node.js

Starting with Node.js 12, using the ES module on Node.js has the following options:

  • Library: ESM is maintained by John-David Dalton. Esm also supports older versions of Node.js.

  • Flag–experimental-modules

When Node.js 12 reaches LTS status, the ESM support flag will likely be removed in October 2019.

Finally, welcome everyone to pay attention to the public number front small garden, I will publish original articles here regularly.