ES Modules in Node Today!

Windows

Windows
Editor’s Note: Today’s post is a guest post from
Please, Log in or Register to view URLs content!
, a Program Manager on the Microsoft Edge team and creator of the popular Lodash JavaScript library, sharing the news of a new community project to bring ECMAScript modules to Node.


I’m excited to announce the release of
Please, Log in or Register to view URLs content!
(standard/esm), an opt-in, spec-compliant, ECMAScript (ES) module loader that enables a smooth transition between Node and ES module formats with near built-in performance! This fast, small, zero dependency package is all you need to enable ES modules in Node 4+ today!

e48a9647f9ddea05b28712249a7b7f13.gif

@std/esm used in the Node REPL

A tale of two module formats


With ESM
Please, Log in or Register to view URLs content!
, attention is turning to Node’s future ESM support. Unlike browsers, which have an out-of-band
Please, Log in or Register to view URLs content!
and no prior module format, support for ESM in Node is a bit more…prickly. Node’s legacy module format, a
Please, Log in or Register to view URLs content!
(CJS) variant, is a big reason for Node’s popularity, but CJS also complicates Node’s future ESM support. As a refresher, let’s look at an example of both module syntaxes.

CJS:

const a = require("./a")
module.exports = { a, b: 2 }

ESM:

import a from "./a"
export default { a, b: 2 }

Note: For more in-depth comparisons see
Please, Log in or Register to view URLs content!
excellent
Please, Log in or Register to view URLs content!
.

Because CJS is not compatible with ESM, a distinction must be made. After much discussion, Node has
Please, Log in or Register to view URLs content!
using the “.mjs” (modular JavaScript) file extension to signal the “module” parse goal. Node has a history of processing resources by file extension. For example, if you require a .jsonfile, Node will happily load and JSON.parse the result.

ESM support is slated to land, unflagged, in Node v10 around
Please, Log in or Register to view URLs content!
. This puts developers, esp. package authors, in a tough spot. They could choose to:

  • Go all in, shipping only ESM, and alienate users of older Node versions
  • Wait until Jan 1, 2020, the day after
    Please, Log in or Register to view URLs content!
    , to go all in
  • Ship both transpiled CJS and ESM sources, inflating package size and shouldering the responsibility for ensuring 1:1 behavior

None of those choices seem super appealing. The ecosystem needs something that meets it where it is to span the CJS to ESM gap.


The strength of Node.js has always been in the community and user-land packages.

— Sindre Sorhus (@sindresorhus)
Please, Log in or Register to view URLs content!



Bridge building


Enter the
Please, Log in or Register to view URLs content!
loader, a user-land package designed to bridge the module gap. Since Node
Please, Log in or Register to view URLs content!
most ES2015 features,
Please, Log in or Register to view URLs content!
is free to focus solely on enabling ESM.

The loader stays out of your way and tries to be a good neighbor by:

  • Not polluting stack traces
  • Working with your existing tools like
    Please, Log in or Register to view URLs content!
    and
    Please, Log in or Register to view URLs content!
    .
  • Playing well with other loaders like
    Please, Log in or Register to view URLs content!

    (using .babelrc
    Please, Log in or Register to view URLs content!
    )
  • Only processing files of packages that explicitly opt-in to having @std/esmas a dependency, dev dependency, or peer dependency
  • Supporting versioning
    (i.e. package “A” can depend on one version of @std/esm and package “B” on another)

Unlike existing ESM solutions which require shipping transpiled CJS,
Please, Log in or Register to view URLs content!
performs minimal source transformations on demand, processing and caching files at runtime. Processing files at runtime has a number of advantages.

  • Only process what is used, when it’s used
  • The same code is executed in all Node versions
  • Features are configurable by module consumers
    (e.g. module “A” consumes module “C” with the default
    Please, Log in or Register to view URLs content!
    config while module “B” consumes module “C” with cjs compat rules enabled)
  • More spec-compliance opportunities
    (i.e.
    Please, Log in or Register to view URLs content!
    can enforce
    Please, Log in or Register to view URLs content!
    for environment variables, error codes, path protocol and resolution, etc.)
Standard features


Defaults are important. The
Please, Log in or Register to view URLs content!
loader strives to be as spec-compliant as possible while following Node’s
Please, Log in or Register to view URLs content!
built-in behaviors. This means, by default, ESM requires the use of the .mjs extension.

Out of the box,
Please, Log in or Register to view URLs content!
just works, no configuration necessary, and supports:

Unlockables


Developers have strong opinions on just about everything. To accommodate, @std/esm allows
Please, Log in or Register to view URLs content!
with the "@std/esm" package.json field. Options include:

  • Enabling unambiguous module support (i.e. files with at least an import, export, or "use module" pragma are treated as ESM)
  • Supporting
    Please, Log in or Register to view URLs content!
    of CJS modules
  • Top-level await in main modules
  • Loading gzipped modules
Performance


Before I continue, let me qualify the following section:

It’s still super early, mileage may vary, and results may be hand wavey!

Testing was done using Node 9 compiled from
Please, Log in or Register to view URLs content!
, which enables built-in ESM support. I measured the
Please, Log in or Register to view URLs content!
taken to load the 643 modules of
Please, Log in or Register to view URLs content!
, converted to .mjs, against a baseline run loading nothing. Keep in mind the
Please, Log in or Register to view URLs content!
cache is good for the lifetime of the unmodified file. Ideally, that means you’ll only have a single non-cached load in production.

  • Loading CJS equivs was ~0.28 milliseconds per module
  • Loading built-in ESM was ~0.51 milliseconds per module
  • First
    Please, Log in or Register to view URLs content!
    no cache run was ~1.6 milliseconds per module
  • Secondary
    Please, Log in or Register to view URLs content!
    cached runs were ~0.54 milliseconds per module

Initial results look very promising, with cached
Please, Log in or Register to view URLs content!
loads achieving near built-in performance! I’m sure, with your help, parse and runtime performance will continue to improve.

Getting started

  1. Run npm i --save @std/esm in your app or package directory.
  2. Call require("@std/esm") before importing ES modules.

index.js:

require("@std/esm")
module.exports = require("./main.mjs").default

For package authors with sub modules:

// Have "foo" require only "@std/esm". require("foo") // Sub modules work! const bar = require("foo/bar").default

Enable ESM in the Node CLI by loading
Please, Log in or Register to view URLs content!
with
Please, Log in or Register to view URLs content!
:

node -r @std/esm file.mjs

Enable ESM in the Node REPL by loading
Please, Log in or Register to view URLs content!
upon entering:

$ node
> require("@std/esm")
@std/esm enabled
> import path from "path"
undefined
> path.join("hello", "world")
'hello/world'
Meteor’s might


The
Please, Log in or Register to view URLs content!
loader wouldn’t exist without
Please, Log in or Register to view URLs content!
, creator of the
Please, Log in or Register to view URLs content!
compiler from which
Please, Log in or Register to view URLs content!
is forked. He’s proven the loader implementation
Please, Log in or Register to view URLs content!
, since May 2016, in tens of thousands of Meteor apps!

All green thumbs


Even though
Please, Log in or Register to view URLs content!
has just been released, it’s already had a positive impact on several related projects:

What’s next


Like many developers, I want ES modules yesterday. I plan to use
Please, Log in or Register to view URLs content!
in Lodash v5 to not only transition to ESM but also leverage features like gzip module support to greatly reduce its package size.

The
Please, Log in or Register to view URLs content!
loader is
Please, Log in or Register to view URLs content!
. It’s my hope that others are as excited and as energized as I am. ES modules are here! This is just the start. What’s next is up to you. I look forward to seeing where you take it.

Final Thought


While this is not a Microsoft release, we’re proud to have a growing number of core contributors to fundamental JavaScript frameworks, libraries, and utilities at Microsoft. Contributors like
Please, Log in or Register to view URLs content!
of
Please, Log in or Register to view URLs content!
,
Please, Log in or Register to view URLs content!
of
Please, Log in or Register to view URLs content!
,
Please, Log in or Register to view URLs content!
of
Please, Log in or Register to view URLs content!
,
Please, Log in or Register to view URLs content!
of
Please, Log in or Register to view URLs content!
,
Please, Log in or Register to view URLs content!
of
Please, Log in or Register to view URLs content!
, and
Please, Log in or Register to view URLs content!
of
Please, Log in or Register to view URLs content!
, to name a few, who in addition to their roles at Microsoft, are helping shape the future of JavaScript and the web at large through standards engagement and ecosystem outreach. I’m happy to share this news on the Microsoft Edge blog to share our enthusiasm with the community!

Please, Log in or Register to view URLs content!
, Program Manager, Microsoft Edge

Please, Log in or Register to view URLs content!
 

Users who are viewing this thread

Top