npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

webpack-polyfill-injector

v3.0.2

Published

Webpack plugin to automatically inject polyfills into your bundle

Downloads

893

Readme

npm deps test coverage

Webpack Polyfill Injector Plugin

This plugin uses polyfills from polyfill-library and inserts them into your bundle.

The benefits of this plugin are:

  • Users with modern browsers will not be punished:
    • The script size increases only by some very small checks to see if the browser supports all required features natively (i.e. no polyfills are necessary)
    • If all required features are supported natively, then there is no additional HTTP request.
  • It does not rely on an external service (e.g. a CDN). All scripts will be included in your bundle.
    • Failures of external services providing polyfills won't affect your users.
    • Serving all scripts from the same domain avoids problems with browser-plugins like NoScript or uMatrix.
  • If a browser does not support some features, then only the polyfills required for that specific browser are loaded, all together in a single additional HTTP request.
    • All possible combinations of polyfills will be bundled as separate files. No magic at runtime! The files can be served by any dumb webserver.
    • If you don't feel well with 2^n - 1 bundled files for n polyfills, then there is an option to only bundle a single file containing all n polyfills. All browsers that are missing any feature will have to load this file, potentially wasting bandwith on unnecessary polyfills.
  • The plugin even works for the Promise polyfill. Webpack's chunk loading internally uses Promise, therefore this plugin implements its own loading technique.

Install

Note: The current version of this plugin requires webpack 4, for older webpack please use ^1.0.2.

yarn add webpack-polyfill-injector --dev

or

npm install webpack-polyfill-injector --save-dev

Usage

const PolyfillInjectorPlugin = require('webpack-polyfill-injector');

module.exports = {
    entry: {
        app: `webpack-polyfill-injector?${JSON.stringify({
            modules: ['./resources/js/app.js'] // list your entry modules for the `app` entry chunk
        })}!` // don't forget the trailing exclamation mark!
    },
    output: {...},
    module: {...},
    plugins: [
        new PolyfillInjectorPlugin({
            polyfills: [
                'Promise',
                'Array.prototype.find',
            ]
        })
    ]
};

You always need to use webpack-polyfill-injector as loader in your entry chunks and as plugin in the plugin array. They both take an options object as argument, where the plugin options specify default settings that the loader can override.

Loader and plugin work hand-in-hand as follows:

  • The loader creates a small module that checks whether any of the specified polyfills are required.
    • If the browser supports all required features natively, then the entry modules are executed directly (by doing require(...) for every module listed in the modules option.)
    • If the browser misses some features, then the appropriate polyfill file will be loaded (by inserting a new script tag to the head of the html page). Afterwards, your entry modules will be executed.
    • It is possible to use the loader multiple times with different configurations, for example to inject polyfills into several entry chunks, possibly with different lists of polyfills.
  • The plugin emits the files containing the polyfills.

Options

The following options can be specified (for both loader and plugin, where loader options override plugin options):

| Option | Type | Default | Description | |--------------|----------------------|---------|-------------| | polyfills | Array of Strings | none | List of features that are required. Browsers will load polyfills for all features that are not supported natively.You can use any polyfill from polyfill-library. See also this list, but keep in mind that webpack-polyfill-injector creates staticly bundled files and therefore does not use the User-Agent string to determine which polyfills will be required, neither is there a default set. You need to explicitly list everything that is required by your application code. | | modules | Array of Strings | none | List of modules that are part of the current entry chunk. If you used to have {name: ["./file1.js", "./file2.js"]} as entry point then you will configure {name: 'webpack-polyfill-injector?{modules:["./file1.js","./file2.js"]}!'}.You can specify anything that can also be written inside a require() call, e.g. 'my-awesome-loader!./some-file.js'. | | excludes | Array of Strings | [] | List of polyfills that should not be added even though another polyfill depends on it. | | singleFile | Boolean | false | Whether to create only a single file for all polyfills instead of 2^n - 1 files (one per subset). This will decrease your bundle size, but increase the bandwidth usage for browsers that support some (but not all) features natively. | | filename | String | output.filename | The path and filename for generated polyfill files. The default is to use whatever is specified in output.filename. Make sure to include the [name] placeholder or use different filename settings if the list of polyfills differs between some entry chunks. | banner | String | '/*! For detailed credits and licence information see https://github.com/financial-times/polyfill-library */\n' | The banner that is inserted on the top of all generated polyfill files. |

Technical Details

Consider the configuration:

{
    entry: {
        app: `webpack-polyfill-injector?${JSON.stringify({
            modules: [
                './your/first/module/for/this/entry.js',
                './your/second/module/for/this/entry.js'
            ]
        })}!`
    },
    output: {
        filename: `js/[name].js`,
        publicPath: '/'
    },
    plugins: [
        new PolyfillInjectorPlugin({
            polyfills: [
                'Promise',
                'Array.prototype.find',
            ]
        })
    ]
}

The loader creates this entry module in js/app.js:

function main() {
    require('./your/first/module/for/this/entry.js');
    require('./your/second/module/for/this/entry.js');
}
var polyfills = function() {
    return [
        /* Promise */ ('Promise' in this) ? 0 : 1,
        /* Array.prototype.find */ ('find' in Array.prototype) ? 0 : 1
    ];
}.call(window);
if (polyfills.indexOf(1) === -1) {
    main();
} else {
    var js = document.createElement('script');
    js.src = "/js/polyfills." + polyfills.join('') + '.js';
    js.onload = main;
    js.onerror = function onError(message) {
        console.error('Could not load the polyfills: ' + message);
    };
    document.head.appendChild(js);
}

The plugin creates the files js/polyfills.01.js (containing the Array.prototype.find polyfill), js/polyfills.10.js (containing the Promise polyfill), and js/polyfills.11.js (containing both polyfills).

If a single polyfill file is created (singleFile option or only one polyfill specified), then the code generated by the loader simplifies:

function main() {
    require('./your/first/module/for/this/entry.js');
    require('./your/second/module/for/this/entry.js');
}
if (function() {
    return /* Promise */ !('Promise' in this) ||
        /* Array.prototype.find */ !('find' in Array.prototype);
}.call(window)) {
    var js = document.createElement('script');
    js.src = "/js/polyfills.js";
    js.onload = main;
    js.onerror = function onError(message) {
        console.error('Could not load the polyfills: ' + message);
    };
    document.head.appendChild(js);
} else {
    main();
}

Note that in both cases, the detectors are wrapped in a function which is bound to window. This is due to some detectors using this instead of window, e.g. Promise tests 'Promise' in this.