Skip to content
This repository was archived by the owner on Apr 16, 2020. It is now read-only.

Latest commit

 

History

History
193 lines (135 loc) · 5.41 KB

File metadata and controls

193 lines (135 loc) · 5.41 KB

ECMAScript Modules

Stability: 1 - Experimental

Node.js contains support for ES Modules based upon the Node.js EP for ES Modules and the ESM Minimal Kernel.

The minimal feature set is designed to be compatible with all potential future implementations. Expect major changes in the implementation including interoperability support, specifier resolution, and default behavior.

Enabling

The --experimental-modules flag can be used to enable features for loading ESM modules.

Once this has been set, files ending with .mjs will be able to be loaded as ES Modules.

node --experimental-modules my-app.mjs

Features

Supported

Only the CLI argument for the main entry point to the program can be an entry point into an ESM graph. Dynamic import can also be used to create entry points into ESM graphs at runtime.

import.meta

  • {Object}

The import.meta metaproperty is an Object that contains the following property:

  • url {string} The absolute file: URL of the module.

Unsupported

Feature Reason
require('./foo.mjs') ES Modules have differing resolution and timing, use dynamic import

Notable differences between import and require

Only Support for .mjs

ESM must have the .mjs extension.

Mandatory file extensions

You must provide a file extension when using the import keyword.

No NODE_PATH

NODE_PATH is not part of resolving import specifiers. Please use symlinks if this behavior is desired.

No require.extensions

require.extensions is not used by import. The expectation is that loader hooks can provide this workflow in the future.

No require.cache

require.cache is not used by import. It has a separate cache.

URL based paths

ESM are resolved and cached based upon URL semantics. This means that files containing special characters such as # and ? need to be escaped.

Modules will be loaded multiple times if the import specifier used to resolve them have a different query or fragment.

import './foo.mjs?query=1'; // loads ./foo.mjs with query of "?query=1"
import './foo.mjs?query=2'; // loads ./foo.mjs with query of "?query=2"

For now, only modules using the file: protocol can be loaded.

CommonJS, JSON, and Native Modules

CommonJS, JSON, and Native modules can be used with module.createRequireFromPath().

// cjs.js
module.exports = 'cjs';

// esm.mjs
import { createRequireFromPath as createRequire } from 'module';
import { fileURLToPath as fromPath } from 'url';

const require = createRequire(fromPath(import.meta.url));

const cjs = require('./cjs');
cjs === 'cjs'; // true

Builtin modules

Builtin modules will provide named exports of their public API, as well as a default export which can be used for, among other things, modifying the named exports. Named exports of builtin modules are updated when the corresponding exports property is accessed, redefined, or deleted.

import EventEmitter from 'events';
const e = new EventEmitter();
import { readFile } from 'fs';
readFile('./foo.txt', (err, source) => {
  if (err) {
    console.error(err);
  } else {
    console.log(source);
  }
});
import fs, { readFileSync } from 'fs';

fs.readFileSync = () => Buffer.from('Hello, ESM');

fs.readFileSync === readFileSync;

Resolution Algorithm

Features

The resolver has the following properties:

  • FileURL-based resolution as is used by ES modules
  • Support for builtin module loading
  • Relative and absolute URL resolution
  • No default extensions
  • No folder mains
  • Bare specifier package resolution lookup through node_modules

Resolver Algorithm

The algorithm to resolve an ES module specifier is provided through ESM_RESOLVE:

ESM_RESOLVE(specifier, parentURL)

  1. Let resolvedURL be undefined.
  2. If specifier is a valid URL then,
    1. Set resolvedURL to the result of parsing and reserializing specifier as a URL.
  3. Otherwise, if specifier starts with "/", "./" or "../" then,
    1. Set resolvedURL to the URL resolution of specifier relative to parentURL.
  4. Otherwise,
    1. Note: specifier is now a bare specifier.
    2. Set resolvedURL the result of PACKAGE_RESOLVE(specifier, parentURL).
  5. If the file at resolvedURL does not exist then,
    1. Throw a Module Not Found error.
  6. Return resolvedURL.

PACKAGE_RESOLVE(packageSpecifier, parentURL)

  1. Assert: packageSpecifier is a bare specifier.
  2. If packageSpecifier is a Node.js builtin module then,
    1. Return the string "node:" concatenated with packageSpecifier.
  3. While parentURL contains a non-empty pathname,
    1. Set parentURL to the parent folder URL of parentURL.
    2. Let packageURL be the URL resolution of the string concatenation of parentURL, "/node_modules/" and "packageSpecifier".
    3. If the file at packageURL exists then,
      1. Return packageURL.
  4. Throw a Module Not Found error.