Bringing Modern JavaScript to Libraries
Tags: javascript, webdev, react, performance
Published: 2020-07-31 18:45:16


> **tl;dr:** To bring modern JavaScript to our libraries, we should adopt a new `"browser2017"` [conditional export](https://nodejs.org/api/esm.html#esm_conditional_exports) key. The `"browser2017"` key points to modern code that targets modern browsers, without the polyfills that bloat our bundles. This change requires support from bundlers and adoption from package authors.

## Background
Although modern browsers represent [over 90% of web traffic](https://caniuse.com/#feat=es6-module), many websites still transpile JavaScript to ES5 to support the &lt;10% still stuck on older browsers like IE 11. To do this, most websites transpile their code and deliver polyfills which reimplement functionality already included in modern browsers. This produces larger bundles, which mean longer load and parse times for everyone.

#### The module/nomodule pattern
In 2017, the [module/no module pattern](https://philipwalton.com/articles/deploying-es2015-code-in-production-today/) began being recommended as a solution to this problem. Leveraging the fact that newer browsers support `<script type="module">` and older browsers don’t, we can do the following:

```html
<script type="module" src="bundle.modern.js"></script>
<script nomodule src="bundle.legacy.js"></script>
```

This technique serves newer browsers the ES2017 `index.modern.js` bundle and older browsers the polyfilled ES5 `index.legacy.js` bundle. Though [there's a bit more complexity involved](https://github.com/johnstew/differential-serving#tests), it provides a mechanism for the majority of users to take advantage of ES2017 syntax without needing to rely on user agent detection or dynamic hosting.

## Problem
Though the module/nomodule pattern has introduced a mechanism to serve modern bundles, there’s still one glaring problem: **virtually all our third-party dependencies ([and thus the majority of our JavaScript code](https://medium.com/npm-inc/this-year-in-javascript-2018-in-review-and-npms-predictions-for-2019-3a3d7e5298ef#:~:text=97%25%20of%20the%20code%20in%20a%20modern%20web%20application%20comes%20from%20npm)) are stuck in ES5**.  We’ve left transpilation to package authors, but have established **no mechanism for them to publish a modern version of their code.** Until we develop a standard for doing so, applications cannot truly reap the benefits of modern JavaScript. Conditional exports can provide that standard.  

* * *

## Proposal: `"browser2017"` Conditional Export
In January 2020, Node v13.7.0 [announced](https://nodejs.org/en/blog/release/v13.7.0/) official support for [conditional exports](https://github.com/nodejs/node/pull/29978). Conditional exports allow packages to specify per-environment entry points via an `"exports"` package.json field. For example, a library might do the following:

```jsonc
// my-library's package.json
{
    "name": "my-library",
    "main": "./index-node.js",
    "module": "./index.production.mjs",
    "browser": "./index.production.js",
    "exports": {
        "node": "./index-node.js", // Node.js build
        "development": "./index.development.mjs", // browser development build
        "default": "./index.production.js" // browser ES5 production build
    }
}
```

From here, based on which conditions get matched, a bundler or runtime like Node.js can select the most appropriate entry point to use when resolving the module.

With conditional exports introduced, we finally have an opportunity for packages to offer a modern version of their code. To that end, we propose standardizing a new conditional exports key, `"browser2017"`:

```jsonc
// my-library's package.json
{
    "name": "my-library",
    "main": "./index-node.js",
    "module": "./index.production.mjs",
    "browser": "./index.production.js",
    "exports": {
        "node": "./index-node.js", // Node.js build
        "development": "./index.development.mjs", // browser development build
        "browser2017": "./index.browser2017.mjs", // browser modern production build
        "default": "./index.production.js" // browser ES5 production build
    }
}
```

The `"browser2017"` key specifies an ES module entry point that uses JavaScript features available in browsers that support `<script type="module">`. That translates to Chrome 61+, Edge 16+, Firefox 60+ and Safari 10.1+.

These targets pair cleanly with the module/nomodule pattern, eliminating polyfills for:
*   All ES2015 features (classes, arrow functions, maps, sets) excluding tail-call optimization
*   All ES2016 features (array.includes(), exponentiation operator)
*   Most ES2017 features (async/await, Object.entries())

    >Note: The <code>"browser2017"</code> key only approximates ECMAScript 2017. This is because browsers implement the ECMAScript spec independently and arbitrarily. For example, no browsers other than Safari have implemented ES2015’s tail-call optimization and both Firefox and Safari have broken/partial implementations of ES2017’s shared memory and atomics features.

Naming the key `"browser2017"` might seem confusing, since its semantics don’t map exactly to ECMAScript 2017 but rather serve as an alias to the browsers that support `<script type="module">`. However, the name clearly communicates to developers that it represents a certain syntax level, and that syntax level most closely corresponds to ES2017.

<table>
  <thead><tr><th>Feature Supported</th><th>Chrome</th>
<th>Edge</th><th>Firefox</th><th>Safari</th></tr></thead>
  <tr><td>&lt;script type=&quot;module&quot;&gt;</td><td>61+</td><td>16+</td><td>60+</td><td>10.1+</td></tr>
<tr><td>All ES2017 features (excluding atomics+shared memory)</td><td>58+</td><td>16+</td><td>53+</td><td>10.1+</td></tr>
</table>

Packages can generate this entry point using either @babel/preset-env’s [targets.esmodules option](https://babeljs.io/docs/en/babel-preset-env#targetsesmodules), or the TypeScript compiler’s [ES2017 target](https://webhint.io/docs/user-guide/hints/hint-typescript-config/target/).

##### Library Size by Transpilation Target
One of the benefits of publishing modern JavaScript is that newer syntax is generally much smaller than polyfilled ES5 syntax. The table below shows size differences for some popular libraries:

<table>
  <thead>
   <tr>
   <th>Library
   </th>
   <th>ES5
   </th>
   <th>"browser2017"
   </th>
  </tr>
</thead>
  <tr>
   <td>bowser
   </td>
   <td>25.2 KB
   </td>
   <td>23.3 KB <b>(-7.5%)</b></td>
  </tr>
  <tr>
   <td>swr
   </td>
   <td>24.0 KB
   </td>
   <td>14.4 KB <b>(-40.0%)</b>
   </td>
  </tr>
  <tr>
   <td>reactstrap
   </td>
   <td>225.0 KB
   </td>
   <td>197.5 KB <b>(-12.1%)</b>
   </td>
  </tr>
  <tr>
   <td>react-popper
   </td>
   <td>11.3KB
   </td>
   <td>9.75KB <b>(-13.7%)</b>
   </td>
  </tr>
</table>

<sub><sup>*Data gathered using unminified and uncompressed output</sup><sub>

Furthermore, some library authors are forced to write in legacy syntax, as transpiled modern code can sometimes be significantly slower or larger than its legacy counterpart. Establishing a `"browser2017"` entry point would enable these authors to instead write in modern syntax and optimize for modern browsers.

#### Adoption from Package Authors
For many package authors who already write their source code in modern syntax, supporting this could be as simple as adding another target to their build process. For example, if Rollup is used:

##### Example rollup.config.js
```javascript
export default [
    // existing config
    {
        input: 'src/main.js',
        output: { file: pkg.main, format: 'es' },
        plugins: [ babel({exclude: 'node_modules/**'}) ]
    },

    // additional "browser2017" config
    {
        input: 'src/main.js',
        output: { file: pkg.exports.browser, format: 'es' },
        plugins: [
            babel({
                exclude: 'node_modules/**',
                presets: [['@babel/preset-env', {
                    targets: { "esmodules": true }
                }]],
            })
        ]
    }
];
```

#### Support from Bundlers
Before it can be consumed by applications, the `"browser2017"` conditional export needs support from existing tooling. Currently however, most tools have yet to implement support for conditional exports at all. This is documented below:

<table>
<thead>
  <tr>
   <th>Bundler / Tool
   </th>
   <th>Export Maps
   </th>
   <th>Conditional Maps
   </th>
  </tr>
</thead>
  <tr>
   <td><a href="https://nodejs.org/">Node.js</a>
   </td>
   <td><a href="https://nodejs.org/api/esm.html#esm_package_exports">shipped</a>
   </td>
   <td><a href="https://nodejs.org/api/esm.html#esm_package_exports">shipped</a>
   </td>
  </tr>
  <tr>
   <td><a href="https://webpack.js.org/">Webpack</a>
   </td>
   <td><a href="https://github.com/webpack/enhanced-resolve/blob/4eb55a849d335701b8396cc4f2726006d27baed7/lib/processExportsField.js">implemented</a>
   </td>
   <td><a href="https://github.com/webpack/enhanced-resolve/blob/4eb55a849d335701b8396cc4f2726006d27baed7/lib/processExportsField.js#L53">implemented</a>
   </td>
  </tr>
  <tr>
   <td><a href="https://rollupjs.org/">Rollup</a>
   </td>
   <td><a href="https://github.com/rollup/plugins/issues/362">not implemented</a>
   </td>
   <td>not implemented
   </td>
  </tr>
<tr>
   <td><a href="http://browserify.org/">Browserify</a>
   </td>
   <td><a href="https://github.com/browserify/resolve/pull/224">not implemented</a>
   </td>
   <td>not implemented
   </td>
  </tr>
  <tr>
   <td><a href="https://parceljs.org/">Parcel</a>
   </td>
   <td><span style="text-decoration:underline;">not implemented</span>
   </td>
   <td>not implemented
   </td>
  </tr>
  <tr>
   <td><a href="https://www.npmjs.com/esm">esm</a>
   </td>
   <td><a href="https://github.com/standard-things/esm/issues/863">not implemented</a>
   </td>
   <td>not implemented
   </td>
  </tr>
  <tr>
   <td><a href="https://www.snowpack.dev/">Snowpack</a>
   </td>
   <td><a href="https://github.com/pikapkg/snowpack/pull/539">implemented</a>
   </td>
   <td><a href="https://twitter.com/gr2m/status/1235745781870555137">not implemented</a>
   </td>
  </tr>
  <tr>
   <td><a href="https://github.com/vuejs/vite">Vite</a>
   </td>
   <td>not implemented
   </td>
   <td>not implemented
   </td>
  </tr>
  <tr>
   <td><a href="https://www.npmjs.com/package/es-dev-server">es-dev-server</a>
   </td>
   <td><a href="https://twitter.com/LarsdenBakker18/status/1258044686155108352">not implemented</a>
   </td>
   <td>not implemented
   </td>
  </tr>
</table>

#### Drawbacks
The `"browser2017"` conditional export enables publishing ES2017 syntax, but what about ES2018+ features? We would still pay the cost of transpiling features like [object rest/spread](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax#:~:text=Spread%20in%20object%20literals) and [for await...of](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for-await...of). Furthermore, the `"browser2017"` key isn't futureproof. By the time ES2025 arrives, `"browser2017"` may be considered legacy.

## Alternative Solution: Multiple Entry Points by Year
One solution is to add additional entry points each year:

```jsonc
// my-library's package.json
{
    "name": "my-library",
    "main": "./index-node.js",
    "module": "./index.production.mjs",
    "browser": "./index.production.js",
    "exports": {
        "node": "./index-node.js",
        "development": "./index.development.mjs",
        "browser": {
            "2020": "./index.2020.mjs",
            "2019": "./index.2019.mjs",
            "2018": "./index.2018.mjs",
            "2017": "./index.2017.mjs"
        },
        "default": "./index.production.js"
    }
}
```

Though the module/nomodule pattern cannot take advantage of `"browser2018"`+ keys, other techniques can. For example, a website can serve ES2019 code by doing any of the following:
*   Using user-agent sniffing
*   [Dynamically loading bundles](https://jasonformat.com/modern-script-loading/#:~:text=Option%201)
*   Choosing to entirely abandon support for older browsers.

#### Drawbacks
##### Drawbacks of ES2018+ Differential Loading Techniques
However, each of the aforementioned mechanisms have their drawbacks and thus have not garnered much adoption. User-agent sniffing is complex and error-prone, and dynamic loading does not allow for preloading ([source](https://jasonformat.com/modern-script-loading/)). A [static solution was proposed](https://docs.google.com/document/d/1kOLu53dYzwElJZ6JBgMR137-Rdj8cDc_rX2YkPFYUsY/edit#heading=h.kixnwvcm52lq) in 2019, but was met with standardization challenges. At the earliest, [import maps](https://github.com/WICG/import-maps#general-url-like-specifier-remapping) might give us a technique for a `"browser2021"` key or some form of differential loading.

##### Diminishing Improvements in Size
It’s also worth highlighting that ECMAScript versions after ES2017 contain fewer features with less adoption, so additional entry points might not have a significant impact on bundle size.

###### Features by ECMAScript Year
<table>
<thead>
  <tr>
   <th>es2015
   </th>
   <th>es2016
   </th>
   <th>es2017
   </th>
   <th>es2018
   </th>
   <th>es2019
   </th>
   <th>es2020
   </th>
   <th>es2021+
   </th>
  </tr>
</thead>
  <tr>
   <td>const, let
   </td>
   <td>** operator
   </td>
   <td>async/await
   </td>
   <td>Object Spread/Rest
   </td>
   <td>Array.flat, Array.flatMap
   </td>
   <td>String.matchAll
   </td>
   <td>String.replaceAll
   </td>
  </tr>
  <tr>
   <td>Template literals
   </td>
   <td>Array.includes
   </td>
   <td>String padding
   </td>
   <td>Promise.finally
   </td>
   <td>Object.fromEntries
   </td>
   <td>BigInt
   </td>
   <td>Promise.any
   </td>
  </tr>
  <tr>
   <td>Destructuring
   </td>
   <td>
   </td>
   <td>Object.{values, entries, …}
   </td>
   <td>RegExp features
   </td>
   <td>Optional catch binding
   </td>
   <td>Promise.allSettled
   </td>
   <td>Logical Assignment
   </td>
  </tr>
  <tr>
   <td>Arrow functions
   </td>
   <td>
   </td>
   <td>Atomics
   </td>
   <td>for await...of
   </td>
   <td>
   </td>
   <td>globalThis
   </td>
   <td>… to be decided
   </td>
  </tr>
  <tr>
   <td>Classes
   </td>
   <td>
   </td>
   <td>Shared Memory
   </td>
   <td>
   </td>
   <td>
   </td>
   <td>Optional chaining
   </td>
   <td>
   </td>
  </tr>
  <tr>
   <td>Promises
   </td>
   <td>
   </td>
   <td>
   </td>
   <td>
   </td>
   <td>
   </td>
   <td>Nullish coalescing
   </td>
   <td>
   </td>
  </tr>
  <tr>
   <td> <strong><a href="https://kangax.github.io/compat-table/es6/">... a lot more</a></strong>
   </td>
   <td>
   </td>
   <td>
   </td>
   <td>
   </td>
   <td>
   </td>
   <td>
   </td>
   <td>
   </td>
  </tr>
</table>

###### Library Size by Transpilation Target
Compared to the `"browser2017"` target, transpiling to a `"browser2019"` target tends to result in only very small reductions in size.

<table>
<thead>
  <tr>
   <th>Library
   </th>
   <th>ES5
   </th>
   <th>"browser2017"
   </th>
   <th>"browser2019"
   </th>
  </tr>
</thead>
  <tr>
   <td>bowser
   </td>
   <td>25.2 KB
   </td>
   <td>23.3 KB <b>(-7.5%)</b>
   </td>
   <td>23.3 KB <b>(-0%)</b>
   </td>
  </tr>
  <tr>
   <td>swr
   </td>
   <td>24.0 KB
   </td>
   <td>14.4 KB <b>(-40.0%)</b>
   </td>
   <td>13.8 KB <b>(-4.2%)</b>
   </td>
  </tr>
  <tr>
   <td>reactstrap
   </td>
   <td>225.0 KB
   </td>
   <td>197.5 KB <b>(-12.1%)</b>
   </td>
   <td>197.5 KB <b>(-0%)</b>
   </td>
  </tr>
  <tr>
   <td>react-popper
   </td>
   <td>11.3KB
   </td>
   <td>9.75KB <b>(-13.7%)</b>
   </td>
   <td>8.98 KB <b>(-7.9%)</b>
   </td>
  </tr>
</table>
<sub><sup>*Data gathered using unminified and uncompressed output</sup><sub>

###### Maximum Polyfill Size by Transpilation Target
In practice, the size of polyfills depends on which features are actually used. However, we can estimate the maximum size of polyfills (the size assuming every unsupported feature is polyfilled) for each transpilation target. This data is useful for comparison, but it should be noted that the values for es2017 and es2019 include significant over-polyfilling as a result of technical constraints that can be addressed.

<table>
<thead>
  <tr>
   <th>Transpilation Target
   </th>
<th>Browsers</th>
   <th>Maximum Polyfill Size
   </th>
  </tr>
</thead>
  <tr>
   <td>ES5
   </td>
<td>IE11+</td>
   <td>97.6 KB
   </td>
  </tr>
  <tr>
   <td><code>"browser2017"</code>
   </td>
<td>CH 61, Edge 16, FF 60, SF 10.1</td>
   <td>59.5 KB
   </td>
  </tr>
  <tr>
   <td><code>"browser2019"</code>
   </td>
<td>CH 73, Edge 79, FF 64, SF 12.1</td>
   <td>39.5 KB
   </td>
  </tr>
</table>

<sub><sup>* Data gathered using minified and uncompressed output. Includes only [ECMAScript features polyfilled by babel+core-js](https://kangax.github.io/compat-table/es2016plus/).
</sup><sub>

##### Complexity
At least for now, yearly entry points might only further complicate the package authoring process. They would require year-to-year community-wide agreements upon what browser versions are considered part of a given year, and for package authors to correctly follow those definitions. Given the decentralized nature of the JavaScript ecosystem, it’s important to take into account that simpler solutions are easier to adopt.

In the future, it might make sense to add another entry point only once a substantial amount of new features have been released, or after [a new differential loading mechanism](https://wicg.github.io/import-maps/) becomes available. At that point, we could extend the less granular `"browser2017"`, `"browser2021"`, and `"browser2027"` entry points, with each year serving as an alias for a set of targeted browsers. Tools like [@babel/preset-env](https://babeljs.io/docs/en/babel-preset-env) could potentially adopt these aliases and abstract their precise definitions.


## Alternative Solution: `"esnext"` entry point
<em>Note: This is nearly identical to Webpack’s [proposed <b>“browser”</b> entry point](https://gist.github.com/sokra/e032a0f17c1721c71cfced6f14516c62#:~:text=browser:%20Compatible%20with%20current%20Spec)</em>

We can see that:
*   Application developers are the only ones who can know their target browsers
*   Maintaining multiple package variations is a pain point for package authors
*   Application developers already have transpilation integrated into their build process for their own code

Given the above, what if we shift the burden of transpilation away from package authors and onto application developers? A generic `"esnext"` export map key could point to code containing any stable ECMAScript feature as of the package’s publish date. With this knowledge, application developers could transpile all packages to work with their target browsers.

```jsonc
// my-library's package.json
{
    "name": "my-library",
    "main": "./index-node.js"
    "module": "./index.production.mjs",
    "browser": "./index.production.js",
    "exports": {
        "node": "./index-node.js",
        "development": "./index.development.mjs",
        "esnext": "./index.esnext.mjs",
        "default": "./index.production.js"
    }
}
```

Both package authors and application developers would no longer need to worry about what syntax level a package is published in. Ideally, this solution would enable JavaScript libraries to always provide the most modern output - even as the definition of “modern” changes.

#### Drawbacks
##### Migrating to Transpiling node_modules
The JavaScript ecosystem has a long-ingrained belief that we shouldn’t have to transpile `node_modules`, and our tooling reflects this. Since libraries are already transpiled prior to being published, most applications have configured Babel to exclude transpiling `node_modules`. Moving to an `"esnext"` entry point would require application developers to move away from pre-transpiled dependencies, instead adopting slower fully-transpiled builds. The build impact could be alleviated to some degree through caching and limiting transpiling to production builds. Some tools have already adopted this approach, including Parcel and Create React App. This change would also require tooling changes to selectively transpile only packages that expose an “esnext” entry point.

##### Silent Breakages
A moving `"esnext"` target has the potential to cause silent breakages in applications. For example, ES2021 could introduce [Observable](https://github.com/tc39/proposal-observable) to the standard library. If an npm library starts to use Observable in its `"esnext"` entry point, older versions of Babel [would not polyfill Observable but output no errors or warnings](https://babeljs.io/repl#?browsers=chrome%2071&build=&builtIns=usage&spec=false&loose=false&code_lz=GYVwdgxgLglg9mABAGxgZygUzACk8zAW2ygBpFMA3EgOQENiBKRAbwChFPEAnTKEbkjCYA7ogDyAIzSZulOpII4402dW6IAvAD5WHLgYD0hxAGFedLIjpIqJRAAsbAEwIaRDmBAeIZYZ2iIzpZ0iFBwYQ6YvjBgANb6BpwEUI4ubloU1GCpOogqMnKyAHTCAB5QeNlQjADcbIlJxogAglBQdN6R0XY5af5ujQb4RCTFdM7OAKLVADLoWMLcVbQMmOROA7LkUNwgmHUNSVzNAEp8AkihEAQ2IAAOiKCQsAiIHl4-IjDIyIgQNgg-G6WXsGAshCGXF4_EEiBwzDy7GOx2aABE-J0fFAoqC-ptXLIntw4IQQSNiDkoccKWNeIQ4NQZiR5hhsLIVjl6MQNultmE9gd6ijEABfYVcUWHUVAA&debug=false&forceAllTransforms=true&shippedProposals=false&circleciRepo=&evaluate=false&fileSize=false&timeTravel=false&sourceType=module&lineWrap=true&presets=env%2Cenv&prettier=false&targets=&version=7.10.5&externalPlugins=). For application developers who don’t update their transpilation tooling, this error would go uncaught until reaching testing or even production. Adding [more metadata in our package.json](https://gist.github.com/sokra/e032a0f17c1721c71cfced6f14516c62#:~:text=I%20would%20consider%20this%20is) could be one approach to solving this. Even with this information, it may still be difficult or impossible to reliably determine the publish date for an installed package: npm injects the publish date into local package.json files when installing, but other tools like Yarn do not.

## Solutions Comparison
<table>
<thead>
  <tr>
   <th>Solution
   </th>
   <th>Pros
   </th>
   <th>Cons
   </th>
  </tr>
</thead>
  <tr>
   <td>browser2017
   </td>
   <td>
<ul>

<li>Simplest solution

<li>Precise definition tied to a set of browsers

<li>Applications do not need to transpile dependencies

<li>Requires minor changes in tooling/configuration

<li>Package authors control how their package gets transpiled
</li>
</ul>
   </td>
   <td>
<ul>

<li>Misses out on ES2018+ syntax

<li>We might need to introduce a “browser2025” entry point in the future

<li>Does not support all ES2017 syntax; can be misinterpreted
</li>
</ul>
   </td>
  </tr>
  <tr>
   <td>browser2017</br> browser2018</br> browser2019 </br>...
   </td>
   <td>
<ul>

<li>Gives applications power to target any syntax level

<li>Applications do not need to transpile dependencies

<li>Package authors control how their package gets transpiled

<li>Requires minor changes in tooling/configuration
</li>
</ul>
   </td>
   <td>
<ul>

<li>There is currently no <strong>static</strong> differential loading mechanism for serving ES2018+ syntax

<li>ES2018+ entry points currently would not significantly reduce size

<li>Complicates package authoring process
</li>
</ul>
   </td>
  </tr>
  <tr>
   <td>esnext
   </td>
   <td>
<ul>

<li>Gives applications full power in determining their target browsers

<li>Future-proof; libraries will always be using the latest syntax

<li>Simplifies package authoring process
</li>
</ul>
   </td>
   <td>
<ul>

<li>There is currently no <strong>static</strong> differential loading mechanism for serving ES2018+ syntax

<li>Slow production builds; can be alleviated with caching

<li>Tooling must be built to selectively transpile node_modules

<li>Can cause silent breakage for package users

<li>Package authors have no control over how their packages get transpiled
</li>
</ul>
   </td>
  </tr>
</table>

## Looking Forward
A pre-transpiled `"browser2017"` conditional export unlocks most of the potential benefits of modern JavaScript. However, in the future we might need subsequent "browser2021" and "browser2027" fields.

In contrast, `"esnext"` is futureproof but requires a solution that addresses silent breakage and versioning consensus before it can be viable. It also requires many changes in existing tooling and configurations.

Our applications stand to benefit from serving modern JavaScript. Whichever mechanism we choose, we need to consider how it affects each part of the ecosystem: bundlers, library authors, and application developers.
  
***I'd love to hear your thoughts*** :smiley:***! Feel free to leave a comment or suggestion below*** :point_down:***.***

* * *

## Other Resources
*   [@sokra Introduces conditional exports to Webpack](https://gist.github.com/sokra/e032a0f17c1721c71cfced6f14516c62)
*   [On Consuming and Publishing ES2015+ packages - Henry Zhu](https://babeljs.io/blog/2018/06/26/on-consuming-and-publishing-es2015+-packages)
*   [Deploying ES2015+ Code in Production - Philip Walton](https://philipwalton.com/articles/deploying-es2015-code-in-production-today/)
*   [Modern Bundling - Jovi De Croock](https://dev.to/jovidecroock/modern-bundling-4chm)
*   [Create React App Introduces Transpilation of node_modules](https://github.com/facebook/create-react-app/issues/1125)
*   [The Babel Podcast: Compiling Your Dependencies - Henry Zhu and Jason Miller](https://podcasts.google.com/feed/aHR0cHM6Ly9mZWVkcy50cmFuc2lzdG9yLmZtL3RoZS1iYWJlbC1wb2RjYXN0/episode/OGM5ZmNlNTYtYTY0Yy00ODhkLWE3MTUtMDkyODVhNzM5ZTFk?ved=0CAkQ38oDahcKEwiQhp7Z6r_qAhUAAAAAHQAAAAAQAQ)
*   [Kangax compat-table](https://kangax.github.io/compat-table/)
*   [Twitter Discussion about jsnext:main](https://twitter.com/RReverser/status/657601803954057216)