- ES Modules in Node Today!
- Im excited to announce the release of @std/esm (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
- A tale of two module formats
- With ESM landing in browsers, attention is turning to Nodes future ESM support. Unlike browsers, which have an out-of-band parse goal signal and no prior module format, support for ESM in Node is a bit moreprickly. Nodes legacy module format, a CommonJS (CJS) variant, is a big reason for Nodes popularity, but CJS also complicates Nodes future ESM support. As a refresher, lets 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 Nicolás Bevacquas excellent post.
- Because CJS is not compatible with ESM, a distinction must be made. After much discussion, Node has settled on 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 .json file, Node will happily load and JSON.parse the result.
- ESM support is slated to land, unflagged, in Node 10 anytime between Oct. 2018 and May 20 20. 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 2020, after Node 8 support ends, to go all in
- Ship ESM and transpiled CJS sources, inflating package size and shouldering the responsibility of 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.
- Bridge building
- Enter the @std/esm loader, a user-land package designed to bridge the module gap. Since Node supports most ES2015 features, @std/esm 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 Babel and webpack.
- Playing well with other loaders like babel-register
- (using .babelrc "modules":false )
- Only processing files of packages that opt-in with a @std/esm configuration object or @std/esm as a dependency, dev dependency, or peer dependency in their package.json
- 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, @std/esm performs minimal, inline 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 its 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@std/esm config while module B consumes module C with cjs compat rules enabled)
- More compliance opportunities
- (i.e. @std/esm can enforce Nodes ESM rules for environment variables, error codes, path protocol and resolution, etc.)
- Standard features
- Defaults are important. The @std/esm loader strives to be as spec-compliant as possible while following Nodes ESM rules. This means, by default, ESM requires the use of the .mjs extension.
- Out of the box @std/esm just works, no configuration necessary, and supports:
- import / export
- Dynamic import
- Improved errors
- Live bindings
- Loading .mjs files as ESM
- The file URI scheme
- Node 4+
- Unlockables
- Developers have strong opinions on just about everything. To accommodate, @std/esm allows unlocking extra features 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 named exports of CJS modules
- Top-level await in main modules
- Loading gzipped modules
- Performance
- Before I continue let me qualify the following section:
- Its still super early, mileage may vary, results are hand wavey, etc.
- Testing was done using Node 9 compiled from PR #14369, which enables built-in ESM support. I measured the time taken to load the 643 modules of lodash-es, converted to .mjs, against a baseline run loading nothing. Keep in mind the @std/esm cache is good for the lifetime of the unmodified file. Ideally, that means youll 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 @std/esm no cache run was ~1.56 milliseconds per module
- Secondary @std/esm cached runs were ~0.54 milliseconds per module
- Initial results look very promising, with cached @std/esm loads achieving near built-in performance! Im sure, with your help, parse and runtime performance will continue to improve.
- Getting started
- Run
- npm i --save @std/esm
- in your app or package directory.
- There are three ways to enable ESM with @std/esm.
- Enable ESM with a CJS bridge:
- index.js
- // Provide options as a parameter, environment variable, or rc file.
- require = require("@std/esm")(module/*, options*/)
- module.exports = require("./main.mjs").default
- 2. Enable ESM in the Node CLI with the -r option:
- node -r @std/esm main.mjs
- 3. Enable ESM in the Node REPL:
- node -r @std/esm
- Meteors might
- The @std/esm loader wouldnt exist without Ben Newman, creator of the Reify compiler from which @std/esm is forked. Hes proven the loader implementation in production at Meteor, since May 2016, in tens of thousands of Meteor apps!
- All green thumbs
- Even though @std/esm has just been released its already had a positive impact on several related projects by:
- Fixing Acorns strict mode pragma detection and aligning parser APIs
- Improving dynamic import support of Babel and related Acorn plugin
- (
- acorn-dynamic-import
- is used by webpack for code splitting)
- Improving property iteration order in Chakra
- Improving the parse/load time and spec compliance of Reify
- (Reify is used by Meteor for ESM support)
- Improving JS engine performance for webpack 3
- (Chakra, SpiderMonkey, V8)
- Inspiring a fast top-level parser proof of concept
- Spurring champions of
- export * as ns from "mod"
- and
- export default from "mod"
- proposals
- Whats next
- Like many developers, I want ES modules yesterday. I plan to use @std/esm in Lodash 5 to not only transition to ESM but also leverage features like gzip module support to greatly reduce its package size.
- The @std/esm loader is available on GitHub. Its my hope that others are as excited and as energized as I am. ES modules are here! This is just the start. Whats next is up to you. I look forward to seeing where you take it.
- Final Thought
- While this is not a Microsoft release, were proud to have a growing number of core contributors to fundamental JavaScript frameworks, libraries, and utilities at Microsoft. Contributors like Maggie Pint of Moment.js, Matthew Podwysocki of ReactiveX, Nolan Lawson of PouchDB, Patrick Kettner of Modernizr, Rob Eisenberg of Aurelia, Sean Larkin of webpack, and Tom Dale of Ember, 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. Im happy to guest post this news on the Microsoft Edge blog to share our enthusiasm with the community!
来源: https://juejin.im/entry/5aa497a7f265da237d02a03d