Giter Site home page Giter Site logo

rangermeier / webpack-babel-multi-target-plugin Goto Github PK

View Code? Open in Web Editor NEW

This project forked from danielschaffer/webpack-babel-multi-target-plugin

0.0 1.0 0.0 1.58 MB

A Webpack plugin that works with Babel to allow production deployment of ES2015 builds targeted to modern browsers, with an ES5 fallback for legacy browsers.

TypeScript 99.37% JavaScript 0.41% Shell 0.22%

webpack-babel-multi-target-plugin's Introduction

webpack-babel-multi-target-plugin

This project, inspired by Phil Walton's article Deploying es2015 Code in Production Today, adds tooling to simplify the additional configuration with a Webpack plugin, BabelMultiTargetPlugin.

Setup and Configuration

NPM

Using the plugin requires making a few small changes to your existing webpack configuration:

  • Replace any instances of 'babel-loader' with BabelMultiTargetPlugin.loader()

    • Do not use a Loader configuration object here - see Options Reference below for information on customizing options for 'babel-loader'
  • Set resolve.mainFields to include es2015, which allows webpack to load the es2015 modules if a package provides them according to the Angular Package Format. Additional field names may be added to support other package standards.

  • Add an instance of BabelMultiTargetPlugin to the webpack configuration's plugins property

  • BabelMultiTargetPlugin does not require any configuration - but can be customized (see Options Reference below)

  • Remove any .babelrc - see Options Reference below for setting preset options

  • TypeScript

    • Loader rules must use BabelMultiTargetPlugin.loader() after your compiler loader (remember, loaders are run bottom to top)
    • Set tsconfig to target es6 or higher
  • Vue

    • Replace 'vue-loader' with BabelMultiTargetPlugin.loader('vue-loader')

Upgrading from v1.x

  • Change usages of BabelMultiTargetPlugin.loader to BabelMultiTargetPlugin.loader()

Configuration Defaults

BabelMultiTargetPlugin does not require any options to be set. The default behavior is:

  • Generate "modern" and "legacy" bundles.

  • The "modern" bundle assets will have their filenames appended with .modern, while the "legacy" bundle assets will remain the same. This enables these assets to be deployed without breaking anything since it will still have the required polyfills.

  • "modern" browsers are the last 2 versions of each browser, excluding versions that don't support <script type="module">

Options Reference

  • babel.plugins (string[]) - a list of Babel plugins to use. @babel/plugin-syntax-dynamic-import is included automatically.
  • babel.presetOptions (BabelPresetOptions) - options passed to @babel/preset-env. See Babel's preset-env options documentation for more info.
    • Default: { modules: false, useBuiltIns: 'usage' }
    • IMPORTANT: modules is forced to false to avoid problems with transformed commonjs modules
  • doNotTarget (RegExp[]) - an array of RegExp patterns for modules which will be excluded from targeting (see How It Works below)
  • exclude (RegExp[]) - an array of RegExp patterns for modules which will be excluded from transpiling
  • targets ({ [browserProfile: string]: BabelTargetOptions }) - a map of browser profiles to target definitions. This is used to control the transpilation for each browser target. See Configuration Defaults above for default values.
    • targets[browserProfile].key (string) - Used internally to identify the target, and is appended to the filename of an asset if tagAssetsWithKey is set to true. Defaults to browserProfile if not set.
    • targets[browserProfile].tagAssetsWithKey (boolean) - Determines whether the key is appended to the filename of the target's assets. Defaults to true for the "modern" target, and false for the "legacy" target. Only one target can have this property set to false.
    • targets[browserProfile].browsers Defines the browserslist used by @babel/preset-env for this target.
    • targets[browserProfile].esModule (boolean) - Determines whether this target can be referenced by a <script type="module"> tag. Only one target may have this property set to true.
    • targets[browserProfile].noModule (boolean) - Determines whether this target can be referenced by a <script nomodule> tag. Only one target may have this property set to true.

Configuration Examples

Basic Usage

// webpack.config.js

const BabelMultiTargetPlugin = require('webpack-babel-multi-target-plugin').BabelMultiTargetPlugin;

module.exports = {

    entry: 'src/main.js',

    resolve: {
        mainFields: [
            'es2015',
            'module',
            'main',
        ],
    },

    module: {
        rules: [
            {
                test: /\.js$/,
                use: [
                    BabelMultiTargetPlugin.loader(),
                ],
            },
        ],
    },

    plugins: [
        new BabelMultiTargetPlugin(),
    ],

};

TypeScript

// webpack.config.js

const BabelMultiTargetPlugin = require('webpack-babel-multi-target-plugin').BabelMultiTargetPlugin;

module.exports = {

    entry: 'src/main.ts',

    resolve: {
        mainFields: [
            'es2015',
            'module',
            'main',
        ],
    },

    module: {
        rules: [
            {
                test: /\.js$/,
                use: [
                    BabelMultiTargetPlugin.loader(),
                ],
            },
            {
                test: /\.ts$/,
                use: [
                    BabelMultiTargetPlugin.loader(),
                    'awesome-typescript-loader'
                ],
                options: {
                    useCache: true,
                    cacheDirectory: 'node_modules/.cache/awesome-typescript-loader',
                },
            },
        ],
    },

    plugins: [
        new BabelMultiTargetPlugin(),
    ],

};

With Options

// webpack.config.js

const BabelMultiTargetPlugin = require('webpack-babel-multi-target-plugin').BabelMultiTargetPlugin;

module.exports = {

    entry: 'src/main.js',

    resolve: {
        mainFields: [
            'es2015',
            'module',
            'main',
        ],
    },

    module: {
        rules: [
            {
                test: /\.js$/,
                use: [
                    BabelMultiTargetPlugin.loader(),
                ],
            },
        ],
    },

    plugins: [
        new BabelMultiTargetPlugin({

            babel: {
                // babel preset-env plugin options go here
            },

            // excludes the untargetable-library module from being targeted
            doNotTarget: [
                /node_modules\/untargetable-library/,
            ],

            // excludes the transpiling-trouble module from being transpiled
            exclude: [
                /node_modules\/transpiling-trouble/
            ],

            // swap which target gets the name appended
            targets: {

                // results in the "modern" bundle being output as main.js
                // the default is main.modern.js
                modern: {
                    tagAssetsWithKey: false,
                },

                // results in the "legacy" bundle being output as main.old-and-broke.js
                // the default is main.js
                legacy: {
                    key: 'old-and-broke',
                    tagAssetsWithKey: true,
                },
            },
        }),
    ],

};

Don't Transpile ES5-only Libraries

Some libraries may cause runtime errors if they are transpiled - often, they will already have been transpiled by Babel as part of the author's publishing process. These errors may look like:

Cannot assign to read only property 'exports' of object '\#\<Object\>'

or

__webpack_require__(...) is not a function

These libraries most likely need to be excluded from Babel's transpilation. While the plugin will automatically attempt to filter out CommonJs modules, you can also specify libraries to be excluded in the BabelMultiTargetPlugin constructor:

new BabelMultiTargetPlugin({
    exclude: [
        /node_modules\/some-es5-library/,
        /node_modules\/another-es5-library/,
    ],
});

Example Projects

Several simple use cases are provided to show how the plugin works.

Install Example Project Dependencies

# installs dependencies for all example projects; requires bash
npm run install-examples

Build the Example Projects

# builds all example projects
npm run examples

# build just the specified example projects
npm run angular-five typescript-plain

Example Project Dev Server

# builds and serves all example projects
npm start

# builds and serves just the specified example projects
npm start angular-five typescript-plain

Examples will be available at http://HOST:PORT/examples/EXAMPLE_NAME.

How It Works

This plugin works by effectively duplicating each entry point, and giving it a target. Each target corresponds to a browser definition that is passed to Babel. As the compilation processes each entry point, the target filters down from the entry point through each of its dependencies. Once the compilation is complete, any CSS outputs are merged into a single module so they are not duplicated (since CSS will be the same regardless of ES supported level). If HtmlWebpackPlugin is being used, the script tags are updated to use the appropriate type="module" and nomodule attributes.

Blind Targeting

In some circumstances, such as lazy-loaded routes and modules with Angular, Vue, and ES6 dynamic imports, it may not be possible to determine the entry point of a module. In these cases, the plugin will assign the module a target on its own. It does this by creating an array of the targets, and removing and assigning one target each time it encounters a given resource.

If you encounter a BlindTargetingError while attempting to use this plugin, please create an issue with a simple reproduction.

Benefits

  • Automatically sets up your index HTML files with both "modern" and "legacy" bundles

  • Uses ES2015 source when available, and attempts to automatically avoid re-transpiling ES5/CommonJs code

  • Avoid using between 30-70 KB of polyfill code on browsers that don't need them (depends on project size and features used)

Caveats

  • Increased build time - since the plugin duplicates entry points, everything has to be done twice. This can be helped with appropriate cache configurations where they are available (Babel, TypeScript, etc), but it may make sense to avoid using this plugin during development.

  • May not play nice with hard-source-webpack-plugin

  • Code Splitting - Since CommonJs dependencies can be shared between "modern" and "legacy" bundles, apps with multiple entries or lazy-loaded modules may end up with a large number of "vendor" chunks.

  • Angular Apps: if a dependency does not provide ES modules and imports @angular/core as a CommonJs dependency (e.g. require('@angular/core')), things will break, particularly when using lazy routing modules.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.