Table of Contents
Babel-Preset-Edge could be described as the big brother of Babel-Preset-React-App (part of Create React App). Where the latter combines only a few basics, Babel-Preset-Edge is more innovative, flexible and automated.
Babel configurations start easily, but can quickly become complicated. Not to mention that everything that works has to be kept running for quite a while. Frequently this manual approach is very 🚨 time-consuming: combine all current Babel plugins in the correct order and update them regularly. This often involves copying and matching between different projects, teams and products. Babel-Preset-Edge offers a significant simplification here. One preset instead of your own manual configuration. Of course, this preset has settings, but it also does a lot automatically. Almost magical. 🧞
TLDR 🎉: Automatic Environment Detection + React/JSX + TypeScript + Macros + Fast Async + Import Optimizations + Automatic Production Mode + Ready for ESM Script Tags + Smart Minification.
- The preset builds upon Babel-Preset-Env and automatically reads your browser list configuration. The presets and plug-ins that are used run in loose mode by default, generating more efficient code than would be the case in very strict mode. In practical use this mode has proven itself and is sufficient, as long as one does not do very wacky things, completely.
- The preset also automatically detects the NodeJS version 🤓 based on the
engines
specification in yourpackage.json
. - It provides factory support for React and JSX. Through the configuration options you can also use the preset for Preact.
- Babel also offers processing TypeScript with recent releases. This is mainly interesting for parsing and bundling and does not offer any type checks. It is way faster though than processing files with the real typescript package.
- There are a few features that are often used, but not yet quite standard. These include object-rest-spread, class properties, and decorators. These three are of course automatically supported by the preset.
- There are always new ideas for language extensions. However, these are often not standard or are not even on their way to becoming standard. Here the solution of Kent C. Dodds with macros is much better. Instead of completely filling the namespace with new functions, its solution provides for the import of a macro. The code is therefore also directly understandable for Linter etc. This is a much rounder solution for solutions such as optimizing deep object accesses with
idx
or parsing GraphQL queries withgql
. - The preset does not offer the translation of generators nor transpilation of async/await to these. This has always been a half-baked solution with insanely complex and slow code. Furthermore, Fast-Async is a much better solution for the translation of Async/Await. 🏃 Way faster output code. And the best thing is that this only works if the target for which the translation is being made really needs it.
- To make output code as compact as possible and imports as efficient as possible, the preset rewrites imports from common utility libraries such as Lodash, Rambda and Async so that they are better captured and optimally bundled by the tree shaking of the Webpack and Rollup.
- With the built-in modern transpile mode, you can translate directly for the development of the software only for modern systems. That makes everything faster. And easier to debug often too. We recommend to use this mode during development.
- With EcmaScript modules, there is now the option in the browser to offer two differently translated versions using different script tags. With the "esm" transpile mode, this is also directly built into the preset.
- Babel-Preset-Edge automatically detects from the
NODE_ENV
variable that it is currently being executed inside a unit test. This is used directly to translate only for the current engine. - Speaking of
NODE_ENV
. This can of course also be set toproduction
. This already optimizes some things on the transpilation level. For example, the code minifier is more aggressive and some typical development constructs like PropTypes in React are expanded. In any case, even in development, the preset automatically removes dead branches of code and performs other optimizations on the fly. There is really no reason to get the complete code when publishing to NPM. There are SourceMaps and with great solutions like the SourceMapLoader from Webpack we can even look right through the sources. - And last but not least: We have built tests into the preset - of course - and can thus ensure to some extent that by updating the individual parts the whole thing continues to work. Who makes serious regression tests of the transpiler within their own application?
This is how you install it using NPM:
npm install -D babel-preset-edge
npm install core-js@3 @babel/runtime
and this is how your .babelrc
looks afterwards:
{
"presets": [
[ "edge", {
// options
}]
]
}
Note: As you can see you need both core-js
(Required Polyfills) and @babel/runtime
(Babel Helpers e.g. inherits
for ES5 classes) for correctly supporting the generated code. These are real dependencies, not just dev-dependencies of your distribution code.
This is the default target. It produces code which is compatible with both NodeJS and browsers.
The test
target is ideally suited for any test runner usage. It is enabled by default when no other target is given and NODE_ENV
is configured as test
. It exactly targets the current environment. It is probably not a good idea to use this target outside of testing.
When setting the target to browser
your build requirements will match the browserslist
configuration of your projects. This is ideal for all web related builds inside your application. It is not well suited for any publishing of libraries for other use cases.
This uses the engines/node
field from package.json
to automatically bundle your code in a way that it matches the capabilities of the specified NodeJS version.
The default is used when not running a test runner and when no other transpile
was defined. Transpilation applies all features from babel-preset-env
so that the code should be able to run on all ES5-capable engines. Ideally for publishing libs to NPM or bundling applications for the widest possible use case.
This output target is meant for making use of this idea by Jake Archibald. The idea is basically to use two different script tags. One for ESM capable browsers and another one for all legacy browsers. Transpilation output is somewhat identical to "ES2015+Async/Await" transpilation. As this is one is easier to deal with (on client-side) it's probably the better choice over es2015
for browser modules. See also Deploying ES2015+ Code in Production Today by Philip Walton
This follows the idea of https://angularjs.blogspot.de/2017/03/angular-400-now-available.html to offer
kind of a standardized es2015
compatible package which could be used in relatively modern engines.
This is independent from any specific browser lists. This configuration might be useful to e.g. offer
two different bundles of your application: one for classic browsers and one of es2015-compatible browsers.
This is our current set-up for a so-called modern development stack. It transpiles even less code than when using es2015
.
const modernTarget = {
node: "10.13.0",
browsers: [
"Safari >= 12.1",
"iOS >= 12.1",
"Edge >= 18",
"Chrome >= 72",
"ChromeAndroid >= 72",
"Firefox >= 67"
]
}
Using this setting is ideal during development to reduce overall amount of transpilation to a useful minimum to test with pretty up-to-date environments and browsers. It allows you to directly benefit from a lot of new features directly built-into Node v8 for example.
These are our default options. They can be tweaked by passing the required options to the preset.
// Whether to print hints on transpilation settings which were selected.
debug: false,
// One of the following:
// - "node": Targetting NodeJS (Outputs CommonJS modules, translates dynamic imports into require statements, ...)
// - "browser": Targetting Browsers (Expects Webpack, Parcel or Rollup post-processing, keeps dynamic import, ...)
// - "universal": Targetting both NodeJS and Browsers (Ideally suited for libraries published for both NodeJS and browsers via NPM)
// - "auto": Define target automatically based on NODE_ENV environment.
target: "auto",
// Choose which transpilation should be applied.
// One of the following:
// - "es5": Standard output for widest engine and browser support. Ideally suited for publishing to NPM.
// - "esm": Alternative to standard ES5 targetting only browsers which are capable of import ESM modules. This is probably the better solution for targetting browsers than the next option "es2015" as it targets quite the same browsers but does so in a way that we can use a simple duplicate script-tag to import either this or the default ES5 one. See also: https://jakearchibald.com/2017/es-modules-in-browsers/#nomodule-for-backwards-compatibility
// - "es2015": Alternative to standard ES5 reaching only modern engines and browsers which support at least all features of es2015. Might be a good alternative for publishing modern libraries to NPM or when using transpilation on all content - even `node_modules` in application code.
// - "modern": Uses a built-in definition of modern NodeJS and browser versions. This is interesting for local development of application as it accelerates features like hot-loading quite a bit.
// - "current": NodeJS only. Ideally for local running test suites, etc. Using the least amount of transpile for making code runnable locally.
// - "node": NodeJS only. Uses `engines` field in `package.json` to define the NodeJS version to target.
// - "browser": Browser only. Uses local "browserslist" config to determine transpilation target. Uses `BROWSERSLIST_ENV` if configured. Otherwise uses `env` passed through preset or via `NODE_ENV`.
// - "auto": Uses "browser" for `target: browser`. Uses "node" for `target: node`. Uses `es5` for `target: universal`.
// - {}: A custom object which is passed to `@babel/preset-env`
transpile: "auto",
// Select environment where we are in for the current job.
// One the the following:
// - "development": During development of applications, publishing of libraries containing debug code
// - "production": Publishing application to the public, publishing of clean non-debug code containing libraries and command line applications.
// - "test": Used for testing e.g. with Ava or Jest test runners.
// - "auto": Automatically using the `EDGE_ENV` or `NODE_ENV` environment variables.
env: "auto",
// Choose whether and how imports should be processed.
// - "cjs": Transpile module imports to CommonJS
// - false: Keep module imports
// - "auto": Automatic selection based on `target`.
modules: "auto",
// Choose automatically depending on target by default or use one of these for full control:
// - "node": For usage in NodeJS (e.g. produce binaries), publish NodeJS-only libraries.
// - false: Enhance imports with further details (chunk names, SSR support), but keep them functional as they are.
imports: false,
// Replace the function used when compiling JSX expressions. Default: React.
// See also: https://www.npmjs.com/package/@babel/preset-react
jsxPragma: null,
// Replace the component used when compiling JSX fragments. Default: React.
// See also: https://www.npmjs.com/package/@babel/preset-react
jsxPragmaFrag: null,
// Transpilation Settings: We default on a loose transpilation which is efficient
// but not overly compliant. If you experience issues it might be better to
// switch `looseMode` off. `specMode` on the other hand might produce
// 100% correct code, but tend to be large and slower as well.
looseMode: true,
specMode: false,
// Lodash Plugin Settings. Optimizes import statements for smaller bundles.
// The idea behind here is that some libraries are publishing individual functions into individual files.
// This helps tree-shaking until all libraries are correctly dealing with side-effect flags and bundlers have better support.
optimizeModules: [ "lodash", "async", "rambda", "recompose" ],
// Configuration for module lookup
sourceFolder: "./src",
// Whether to enable source map output
sourceMaps: true,
// Enable full compression on production scripts or basic compression for libraries or during development.
compression: true,
// Whether to apply more agressive minification. Automatically enabled when using `compression: true` and running in production env.
minified: null
If you like this: This preset is part of a larger whole. We use it primarily within our Edge Platform. If you are also developing React applications, have a look around. There are not only Babel configurations, but also Jest-presets, webpack tools, libraries to improve the localization possibilities in React, helpful extensions for the Express Server and more. We design this, of course, for optimal interaction. However, most solutions can also be used individually.
Apache License Version 2.0, January 2004
Copyright 2017-2019
Sebastian Software GmbH