Skip to content
This repository was archived by the owner on Sep 2, 2023. It is now read-only.

Announcement blog post #291

Merged
merged 9 commits into from
Mar 26, 2019
Prev Previous commit
Next Next commit
Revisions per feedback
GeoffreyBooth committed Mar 14, 2019
commit 6dee5cd176612f56f9ca9b07682cceea0bd632c9
24 changes: 12 additions & 12 deletions doc/announcement.md
Original file line number Diff line number Diff line change
@@ -2,30 +2,32 @@

Back in 2017, Node.js 8.9.0 shipped experimental support for [ECMAScript modules](https://hacks.mozilla.org/2018/03/es-modules-a-cartoon-deep-dive/), known for their `import` and `export` statements. This support was behind the flag `--experimental-modules`.

A lot has happened since then. All major browsers [now support](https://caniuse.com/#feat=es6-module) ECMAScript modules (ES modules) via `<script type="module">`. Various projects have sprung up to make NPM packages with ES module sources available for use in browsers via `<script type="module">`. Support for [import maps](https://github.com/WICG/import-maps), which bring to browsers Node-style package names in `import` statements, [is coming to Chrome](https://groups.google.com/a/chromium.org/forum/#!msg/blink-dev/qYeQFqgFOyA/rXJapjMaEAAJ).
A lot has happened since then. All major browsers [now support](https://caniuse.com/#feat=es6-module) ECMAScript modules (ES modules) via `<script type="module">`. Various projects have sprung up to make npm packages with ES module sources available for use in browsers via `<script type="module">`. Support for [import maps](https://github.com/WICG/import-maps), which bring to browsers Node-style package names in `import` statements, [is coming to Chrome](https://groups.google.com/a/chromium.org/forum/#!msg/blink-dev/qYeQFqgFOyA/rXJapjMaEAAJ).

All this progress toward adoption of ES modules has increased the urgency for Node.js to ship its support for ES modules. Especially now that there are other runtimes and environments where ES modules are in use, it’s more important than ever that Node.js join the JavaScript standard.
All this progress toward adoption of ES modules has increased the urgency for Node.js to ship its support for ES modules. Now that there are other runtimes and environments where ES modules are in use, it’s more important than ever that Node.js support the JavaScript standard.

Yet Node’s initial ES modules support has remained in an experimental state, largely due to strong pushback from the community to several decisions that were made in its design. In response, the [Modules Team](https://github.com/nodejs/modules) was formed to take a fresh look at implementing support for ES modules in Node.js. This has led to a [new implementation](https://github.com/nodejs/ecmascript-modules) for supporting ES modules, that we are pleased to announce will ship as part of Node.js 12. It will replace the old `--experimental-modules` implementation, behind the same flag. We hope that this new implementation addresses many of the community’s concerns, and can ship as part of Node.js proper, without a flag, before Node.js 12 reaches [LTS status](https://nodejs.org/en/about/releases/) in October 2019.
Node’s initial ES modules support remained in an experimental state in order to allow the community time to provide feedback on that design. The [Modules Team](https://github.com/nodejs/modules) was formed to act on this feedback and ship first-class support for ES modules in Node.js. This has led to a [new implementation](https://github.com/nodejs/ecmascript-modules) for supporting ES modules, that we are pleased to announce will ship as part of Node.js 12. It will replace the old `--experimental-modules` implementation, behind the same flag. We hope that this new implementation addresses many of the community’s concerns, and can ship as part of Node.js proper, without a flag, before Node.js 12 reaches [LTS status](https://nodejs.org/en/about/releases/) in October 2019.

## What’s in `--experimental-modules`

Like the previous version, this new `--experimental-modules` adds support to Node.js for the following:

- [ES2015 `import` statements](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import) can reference JavaScript files that are themselves written using ES module syntax. Files can be referenced as relative URLs (`'./file.mjs'`), absolute `file://` URLs (`'file:///opt/app/file.mjs'`), package names (`'es-module-package'`) or paths within packages (`'es-module-package/lib/file.mjs'`).

- `import` statements that reference ES module files can specify both the default export (`import _ from 'es-module-package'`) or named exports (`import { shuffle } from 'es-module-package'`). All Node.js built-in packages like `fs` and `path` support both default and named exports.
- `import` statements that reference ES module files can specify both the default export (`import _ from 'es-module-package'`), named exports (`import { shuffle } from 'es-module-package'`) and namespace exports (`import * as fs from 'fs'`). All Node.js built-in packages like `fs` and `path` support all three types of exports.

- `import` statements that reference CommonJS files (all current JavaScript code written for Node.js, [using `require` and `module.exports`](https://nodejs.org/api/modules.html)) can use the CommonJS default export (`import _ from 'commonjs-package'`) only. _This is a work in progress and may change in the future._

- [`export` statements](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/export) in ES module files can specify both default and named exports for `import` statements to reference.

- Dynamic `import()` expressions can be used to import ES modules into either CommonJS or ES module files. Note that `import()` returns a promise.
- Dynamic `import()` expressions can be used to import ES modules from either CommonJS or ES module files. Note that `import()` returns a promise.

- `import.meta.url` provides the `file://` URL of the current ES module file.

- Node.js can be run with an ES module file as a program’s initial entry point.

- Files loaded as ES modules are loaded in strict mode, which in CommonJS requires adding `'use strict';` to the top of every file.

- Loaders can be written to modify Node’s runtime behavior with respect to ES modules. _This is still very much a work in progress._

So if that’s what’s carried over from the previous implementation, what’s new?
@@ -36,8 +38,6 @@ Yes, we heard [you](https://github.com/dherman/defense-of-dot-js/blob/master/pro

The `.mjs` extension is still supported, and still always explicitly means that its file uses the Module parse goal (and therefore is an ES module). We also created a new `.cjs` file extension, to explicitly signify that a file should be treated as CommonJS. (CommonJS is the other module system that Node.js supports, with `require` and `module.exports`.) These two extensions allow individual files to define how they should be loaded, as ES modules or as CommonJS.

As it turns out, a lot of people didn’t like needing to use a new file extension for JavaScript. Browsers don’t require it, tools that build and process JavaScript have issues with it, and languages that transpile into JavaScript such as TypeScript and CoffeeScript don’t necessarily know when to save their output as `.js` or as `.mjs`. So while `.mjs` and `.cjs` have use cases that only they can solve (ES module and CommonJS files side-by-side in the same folder, loose JavaScript files outside of any package) the modules group searched for solutions other than `.mjs` to let Node.js know when to interpret a file as an ES module.

We came up with two things: a `"type"` field in `package.json` and a `--type` flag. Their naming was inspired by browsers’ `<script type="module">`.

### `package.json` `"type"` field
@@ -56,19 +56,19 @@ When running a file, e.g. `node file.js`, Node follows an algorithm to determine

This flag provides a way to support ES module syntax for input via `--eval`, `--print` or `STDIN`.

## Mandatory file extensions
## Explicit filenames

In the new `--experimental-modules`, file extensions are mandatory in `import` statements: `import './file.js'`, not `import './file'`. However, the CommonJS-style automatic extension resolution behavior (`'./file'`) can be enabled via a new flag, `--es-module-specifier-resolution=node`. (Its inverse, the default, is `--es-module-specifier-resolution=explicit`.) Package names are still just package names, e.g. `import fs from 'fs'`.
By default in the new `--experimental-modules`, file extensions are mandatory in `import` statements: `import './file.js'`, not `import './file'`. However, the CommonJS-style automatic extension resolution behavior (`'./file'`) can be enabled via a new flag, `--es-module-specifier-resolution=node`. (Its inverse, the default, is `--es-module-specifier-resolution=explicit`.) Package names are still just package names, e.g. `import fs from 'fs'`.

This change isn’t because we’re trying to annoy an entirely new constituency now that `.mjs` is no longer required. We recognize that there are years’ worth of JavaScript out there written in the extensionless style, and this will feel like a huge breaking change for all that code. That’s why we’re providing the `--es-module-specifier-resolution=node` flag, and this is also subject to change before `--experimental-modules` itself ships unflagged.
We’re providing the `--es-module-specifier-resolution=node` flag to opt in to cjs-style extension and `index` resolution. We’ve turned it off by default to collect feedback on how users feel about using fully specified paths before we unflag the `--experimental-modules` implementation. You can find our discussion on the topic [here](https://github.com/nodejs/modules/issues/268).

We’re making this change to align with browsers, where [extensions are required](https://github.com/WICG/import-maps#extension-less-imports). Part of the reason for Node.js to support ES modules in the first place is because ES modules are the standard, and the point of aligning to standards is to increase compatibility and interoperability between systems. In this case, Node.js supporting ES modules means that more JavaScript code will be capable of running in either Node or in browsers, without requiring some kind of translation process to make the code compatible with one or the other. Of course, it will always remain possible for users to write code that can’t function cross-platform (JavaScript referencing `window`, or importing `fs`, for example) but by aligning as much as we can, we encourage code to be cross-platform by default.

Also, [import maps may bring support for automatic extension resolution to browsers](https://github.com/WICG/import-maps#extension-less-imports). It would be premature for Node to implement its own solution to supporting extensionless specifiers while efforts are underway to provide a cross-platform standard. Again, this is subject to change.
Also, [import maps may bring support for automatic extension resolution to browsers](https://github.com/WICG/import-maps#extension-less-imports). Should that proposal be accepted, Node is interested in supporting extensionless specifiers in a cross-platform compatible way.

## `module.createRequireFromPath`

The “CommonJS globals” `require`, `exports`, `module.exports`, `__filename`, `__dirname` are undefined in ES modules. However, [`module.createRequireFromPath()`](https://nodejs.org/api/modules.html#modules_module_createrequirefrompath_filename) can be used to create a CommonJS `require` function to be used in an ES module context.
The “CommonJS globals” `require`, `exports`, `module.exports`, `__filename`, `__dirname` are not defined in ES modules. However, [`module.createRequireFromPath()`](https://nodejs.org/api/modules.html#modules_module_createrequirefrompath_filename) can be used to create a CommonJS `require` function to be used in an ES module context.

## `import` for JavaScript only