Create a gist now

Instantly share code, notes, and snippets.

@Couto /webpack.js
Last active Apr 11, 2018

What would you like to do?
Fetch polyfill with webpack
var webpack = require('webpack');
var HtmlWebpackPlugin = require('html-webpack-plugin');
var path = require('path');
var folders = {
APP: path.resolve(__dirname, '../app'),
BUILD: path.resolve(__dirname, '../build'),
BOWER: path.resolve(__dirname, '../bower_components'),
NPM: path.resolve(__dirname, '../node_modules')
};
var config = {
entry: {
app: [
'webpack/hot/dev-server',
"./js/app.js"
]
},
debug: true,
resolve: {
extensions: ['', '.js', '.jsx', '.scss'],
alias: {
//'es6-promise': path.join(folders.NPM, 'es6-promise', 'es6-promise.js'),
//'fetch': path.join(folders.NPM, 'whatwg-fetch', 'fetch.js'),
}
},
stats: {
colors: true,
reasons: true,
},
output: {
path: __dirname + '/build',
publicPath: '/',
filename: '[name].[hash].js',
chunkFilename: '[id].[hash].js'
},
module: {
loaders: [
{
test: /\.s?css$/,
exclude: /node_modules/,
loaders: [
'style',
'css',
'autoprefixer?browsers=last 2 version',
'sass?' + ['outputStyle=nested'].join('&')
]
},
{ test: /\.jsx?$/, loaders: ['react-hot', 'babel'], exclude: /node_modules/ },
{ test: /\.json$/, loader: 'json' },
]
},
plugins: [
new webpack.HotModuleReplacementPlugin(),
new webpack.ProvidePlugin({
'Promise': 'es6-promise', // Thanks Aaron (https://gist.github.com/Couto/b29676dd1ab8714a818f#gistcomment-1584602)
'fetch': 'imports?this=>global!exports?global.fetch!whatwg-fetch'
}),
//new webpack.optimize.CommonsChunkPlugin('app', null, false),
new webpack.NoErrorsPlugin(),
new HtmlWebpackPlugin({
template: path.resolve('./', 'index.html'),
webpackDevServer: '/webpack-dev-server.js'
})
]
};
module.exports = config;
@rhacker

This comment has been minimized.

Show comment Hide comment
@rhacker

rhacker Apr 5, 2015

thanks, fetch works perfectly here :)
Can you explain how do you inject fetch as webpack plugin ?

rhacker commented Apr 5, 2015

thanks, fetch works perfectly here :)
Can you explain how do you inject fetch as webpack plugin ?

@matiassingers

This comment has been minimized.

Show comment Hide comment
@matiassingers

matiassingers Apr 6, 2015

This is super awesome @Couto! 👍

If it's okay with you I'll write a quick blog post about including polyfills like fetch in webpack.

This is super awesome @Couto! 👍

If it's okay with you I'll write a quick blog post about including polyfills like fetch in webpack.

@Andreyco

This comment has been minimized.

Show comment Hide comment
@Andreyco

Andreyco Sep 15, 2015

Don't forget to install imports-loader and exports-loader, if you use this.

npm install imports-loader exports-loader --save

Don't forget to install imports-loader and exports-loader, if you use this.

npm install imports-loader exports-loader --save
@aaronj1335

This comment has been minimized.

Show comment Hide comment
@aaronj1335

aaronj1335 Sep 29, 2015

The ProvidePlugin works by hooking into the parser, and adding a dependency whenever it sees an identifier matching a property name of the object it's instantiated with. So with the above config, whenever the parser hits fetch, it will add a dependency on whatwg-fetch.

Since es6-promise isn't a valid identifier, I believe it should be:

        new webpack.ProvidePlugin({
            'Promise': 'es6-promise',
            'fetch': 'imports?this=>global!exports?global.fetch!whatwg-fetch'
        }),

When I changed es6-promise to Promise in my project, the output grew by ~3k (gzipped), so I believe you need to use Promise.

The ProvidePlugin works by hooking into the parser, and adding a dependency whenever it sees an identifier matching a property name of the object it's instantiated with. So with the above config, whenever the parser hits fetch, it will add a dependency on whatwg-fetch.

Since es6-promise isn't a valid identifier, I believe it should be:

        new webpack.ProvidePlugin({
            'Promise': 'es6-promise',
            'fetch': 'imports?this=>global!exports?global.fetch!whatwg-fetch'
        }),

When I changed es6-promise to Promise in my project, the output grew by ~3k (gzipped), so I believe you need to use Promise.

@Andersos

This comment has been minimized.

Show comment Hide comment
@Andersos

Andersos Oct 9, 2015

So I wanted to add the fetch polyfill so I added the dependencies (es6-promise, fetch, import-loader, export-loader) and these lines to my Webpack config file. But when running it in Safari 9 i get the following error from fetch.

TypeError: Object is not a constructor (evaluating 'new Promise')

Which seems to indicate Promise is not present when fetch is used. According to caniuse this version of Safari has support for Promises. So it might be es6-promise that is messing with Promise. Anyone else experiencing this?

If I remove the line:
'Promise': 'es6-promise',
Everything works as expected in Safari but I lose support for IE and Opera Mini since I'm not using a Promise polyfill.

Maybe this is a concern that should be raised over at es6-promise. Anyone know how I can solve this problem? 😄

Andersos commented Oct 9, 2015

So I wanted to add the fetch polyfill so I added the dependencies (es6-promise, fetch, import-loader, export-loader) and these lines to my Webpack config file. But when running it in Safari 9 i get the following error from fetch.

TypeError: Object is not a constructor (evaluating 'new Promise')

Which seems to indicate Promise is not present when fetch is used. According to caniuse this version of Safari has support for Promises. So it might be es6-promise that is messing with Promise. Anyone else experiencing this?

If I remove the line:
'Promise': 'es6-promise',
Everything works as expected in Safari but I lose support for IE and Opera Mini since I'm not using a Promise polyfill.

Maybe this is a concern that should be raised over at es6-promise. Anyone know how I can solve this problem? 😄

@Couto

This comment has been minimized.

Show comment Hide comment
@Couto

Couto Oct 14, 2015

@Andersos I've had some success on IE9 by using the following snippet:

new webpack.ProvidePlugin({
  Promise: 'imports?this=>global!exports?global.Promise!es6-promise',
  fetch: 'imports?this=>global!exports?global.fetch!whatwg-fetch',
  React: 'react'
})

So far this works because es6-promise calls the polyfill() automatically, right after the UMD snippet.

Also:
I haven't tested but, I'm pretty sure you can import the es6-promise and call the polyfill() function, something like:

import {polyfill} from 'es6-promise';
polyfill();

right at the top of your app's entry point.

Owner

Couto commented Oct 14, 2015

@Andersos I've had some success on IE9 by using the following snippet:

new webpack.ProvidePlugin({
  Promise: 'imports?this=>global!exports?global.Promise!es6-promise',
  fetch: 'imports?this=>global!exports?global.fetch!whatwg-fetch',
  React: 'react'
})

So far this works because es6-promise calls the polyfill() automatically, right after the UMD snippet.

Also:
I haven't tested but, I'm pretty sure you can import the es6-promise and call the polyfill() function, something like:

import {polyfill} from 'es6-promise';
polyfill();

right at the top of your app's entry point.

@Nosherwan

This comment has been minimized.

Show comment Hide comment
@Nosherwan

Nosherwan Nov 5, 2015

Hi @Couto I have had the same issues as @Andersos on firefox 37 and ie 11. As you suggested I am now importing the package and then calling the polyfill() method at the top of my app.js; however does this mean the polyfill will be added even if the browser implements Promise api?

Hi @Couto I have had the same issues as @Andersos on firefox 37 and ie 11. As you suggested I am now importing the package and then calling the polyfill() method at the top of my app.js; however does this mean the polyfill will be added even if the browser implements Promise api?

@willrowe

This comment has been minimized.

Show comment Hide comment
@willrowe

willrowe Nov 11, 2015

@Nosherwan @Couto @Andersos I was having the same issues with Promise and ended up simplifying it to the following:

new webpack.ProvidePlugin({
    'Promise': 'exports?global.Promise!es6-promise',
    'fetch': 'exports?self.fetch!whatwg-fetch'
})

This works in Chrome, Safari, and IE. The polyfills for both fetch and Promise are only used when needed.

@Nosherwan @Couto @Andersos I was having the same issues with Promise and ended up simplifying it to the following:

new webpack.ProvidePlugin({
    'Promise': 'exports?global.Promise!es6-promise',
    'fetch': 'exports?self.fetch!whatwg-fetch'
})

This works in Chrome, Safari, and IE. The polyfills for both fetch and Promise are only used when needed.

@Zoxive

This comment has been minimized.

Show comment Hide comment
@Zoxive

Zoxive Nov 13, 2015

I case anyone runs into this in the future.. if you are calling window.fetch inside your code. Use 'window.fetch' your config.

new webpack.ProvidePlugin({
    'Promise': 'exports?global.Promise!es6-promise',
    'window.fetch': 'exports?self.fetch!whatwg-fetch'
})

Zoxive commented Nov 13, 2015

I case anyone runs into this in the future.. if you are calling window.fetch inside your code. Use 'window.fetch' your config.

new webpack.ProvidePlugin({
    'Promise': 'exports?global.Promise!es6-promise',
    'window.fetch': 'exports?self.fetch!whatwg-fetch'
})
@finbarmaginn

This comment has been minimized.

Show comment Hide comment
@finbarmaginn

finbarmaginn Nov 18, 2015

Why do we need to add this as a plugin to webpack? Can't we just do import ('fetch') in our code to get the benefits?

Why do we need to add this as a plugin to webpack? Can't we just do import ('fetch') in our code to get the benefits?

@developit

This comment has been minimized.

Show comment Hide comment
@developit

developit Nov 30, 2015

In my testing this increased the bundle filesize by about 3kb compared to just importing the polyfills in my entrypoint. No clue why, perhaps it's skipped over by Webpack's de-duping?

In my testing this increased the bundle filesize by about 3kb compared to just importing the polyfills in my entrypoint. No clue why, perhaps it's skipped over by Webpack's de-duping?

@OliverJAsh

This comment has been minimized.

Show comment Hide comment
@OliverJAsh

OliverJAsh Dec 6, 2015

Unfortunately this means even new browsers will use the Promise polyfill 😢

Unfortunately this means even new browsers will use the Promise polyfill 😢

@OliverJAsh

This comment has been minimized.

Show comment Hide comment
@OliverJAsh

OliverJAsh Dec 6, 2015

My mistake, that's intended: jakearchibald/es6-promise#148

My mistake, that's intended: jakearchibald/es6-promise#148

@silvenon

This comment has been minimized.

Show comment Hide comment
@silvenon

silvenon Dec 21, 2015

@finbarmaginn I was wondering the same thing, but I think you have the benefit of deciding when to polyfill based on the environment. E.g. you probably don't want to polyfill promises when testing in Node.js because newer versions support them.

@finbarmaginn I was wondering the same thing, but I think you have the benefit of deciding when to polyfill based on the environment. E.g. you probably don't want to polyfill promises when testing in Node.js because newer versions support them.

@gbrassey

This comment has been minimized.

Show comment Hide comment
@gbrassey

gbrassey Jan 7, 2016

Is this still the preferred method to polyfill Promise and does it cause any errors? re: https://github.com/babel/babel-loader#custom-polyfills-eg-promise-library

gbrassey commented Jan 7, 2016

Is this still the preferred method to polyfill Promise and does it cause any errors? re: https://github.com/babel/babel-loader#custom-polyfills-eg-promise-library

@Nosherwan

This comment has been minimized.

Show comment Hide comment
@Nosherwan

Nosherwan Feb 6, 2016

I am curious just today I have learned that at least in babel 6 there is a 'babel-polyfill' (provides es6 env) that can be imported via the entry option in webpack.config.js like so:

var path = require('path');
var webpack = require('webpack');

module.exports = {
  entry: [
    'babel-polyfill',
    './src/main'
  ],
  output: {
      publicPath: '/',
      filename: 'main.js'
  },
  devtool: 'source-map',
  module: {
    loaders: [
      {
        test: /\.js$/,
        include: path.join(__dirname, 'src'),
        loader: 'babel-loader',
        query: {
          presets: ["es2015","react"],  
        }
      }
    ]
  },
  debug: true
};

Would this not make es6-promise polyfill redundant, and maybe it is smart enough to use browser implementation if provided?

I am curious just today I have learned that at least in babel 6 there is a 'babel-polyfill' (provides es6 env) that can be imported via the entry option in webpack.config.js like so:

var path = require('path');
var webpack = require('webpack');

module.exports = {
  entry: [
    'babel-polyfill',
    './src/main'
  ],
  output: {
      publicPath: '/',
      filename: 'main.js'
  },
  devtool: 'source-map',
  module: {
    loaders: [
      {
        test: /\.js$/,
        include: path.join(__dirname, 'src'),
        loader: 'babel-loader',
        query: {
          presets: ["es2015","react"],  
        }
      }
    ]
  },
  debug: true
};

Would this not make es6-promise polyfill redundant, and maybe it is smart enough to use browser implementation if provided?

@silvenon

This comment has been minimized.

Show comment Hide comment
@silvenon

silvenon Feb 12, 2016

Was wondering the same thing…

Was wondering the same thing…

@nickjanssen

This comment has been minimized.

Show comment Hide comment
@nickjanssen

nickjanssen Apr 26, 2016

babel-polyfill does not appear to include a fetch polyfill, so you'd still need to use this.

babel-polyfill does not appear to include a fetch polyfill, so you'd still need to use this.

@tarikjn

This comment has been minimized.

Show comment Hide comment
@tarikjn

tarikjn May 18, 2016

Wouldn't simply including it in entry be a better approach? There is actually a use case where the demonstrated method will fail, for example if you use Webpack server side with externals: [nodeExternals()], you would be forced to use entry anyways because external modules would not see fetch as being defined. So it seems it would be more consistent to use entry here as well.

tarikjn commented May 18, 2016

Wouldn't simply including it in entry be a better approach? There is actually a use case where the demonstrated method will fail, for example if you use Webpack server side with externals: [nodeExternals()], you would be forced to use entry anyways because external modules would not see fetch as being defined. So it seems it would be more consistent to use entry here as well.

@tarikjn

This comment has been minimized.

Show comment Hide comment
@tarikjn

tarikjn May 19, 2016

After double checking, this approach is actually incorrect and will not polyfill fetch properly, the correct approach is to configure whatwg-fetch on your Webpack entry configuration, like such:

module.exports = {
   entry: ['whatwg-fetch', './app/js']
};

This will fix issues where using a 3rd party that expect fetch will not find it with the approach of this gist. This is also consistent with the way babel documents using a polyfill with Webpack and does not require the extra loaders.

I have opened a PR with fetch so they stop referring to this solution: github/fetch#331

tarikjn commented May 19, 2016

After double checking, this approach is actually incorrect and will not polyfill fetch properly, the correct approach is to configure whatwg-fetch on your Webpack entry configuration, like such:

module.exports = {
   entry: ['whatwg-fetch', './app/js']
};

This will fix issues where using a 3rd party that expect fetch will not find it with the approach of this gist. This is also consistent with the way babel documents using a polyfill with Webpack and does not require the extra loaders.

I have opened a PR with fetch so they stop referring to this solution: github/fetch#331

@tarikjn

This comment has been minimized.

Show comment Hide comment
@tarikjn

tarikjn May 19, 2016

@Nosherwan @silvenon you should not include a Promise polyfill if you use babel-polyfill, the correct configuration would then be:

module.exports = {
   entry: ['babel-polyfill', 'whatwg-fetch', './app/js']
};

tarikjn commented May 19, 2016

@Nosherwan @silvenon you should not include a Promise polyfill if you use babel-polyfill, the correct configuration would then be:

module.exports = {
   entry: ['babel-polyfill', 'whatwg-fetch', './app/js']
};
@simonsmith

This comment has been minimized.

Show comment Hide comment
@simonsmith

simonsmith Jul 8, 2016

Has anyone had success adding this when entry is an object?

Has anyone had success adding this when entry is an object?

@xingwang

This comment has been minimized.

Show comment Hide comment
@xingwang

xingwang Jul 15, 2016

@simonsmith Does this not work for you?

entry: {
    demo: ['whatwg-fetch', './app']
  },

@simonsmith Does this not work for you?

entry: {
    demo: ['whatwg-fetch', './app']
  },
@strobox

This comment has been minimized.

Show comment Hide comment
@strobox

strobox Jul 19, 2016

@tarikjn
Works for me too, thanks!

strobox commented Jul 19, 2016

@tarikjn
Works for me too, thanks!

@pmcalmeida

This comment has been minimized.

Show comment Hide comment
@pmcalmeida

pmcalmeida Aug 18, 2016

@Couto ganda truta! 👍

@Couto ganda truta! 👍

@luiscvalmeida

This comment has been minimized.

Show comment Hide comment
@luiscvalmeida

luiscvalmeida Aug 18, 2016

🐟

🐟

@vikas5914

This comment has been minimized.

Show comment Hide comment
@vikas5914

vikas5914 Aug 31, 2016

@tarikjn i am getting a Exception thrown and not caught in IE11 when i use babel-polyfill instead of es6 promise .

entry: ['babel-polyfill', 'whatwg-fetch','./src/main.js'],

but this works perfect in IE11

entry: ['es6-promise', 'whatwg-fetch','./src/main.js']

vikas5914 commented Aug 31, 2016

@tarikjn i am getting a Exception thrown and not caught in IE11 when i use babel-polyfill instead of es6 promise .

entry: ['babel-polyfill', 'whatwg-fetch','./src/main.js'],

but this works perfect in IE11

entry: ['es6-promise', 'whatwg-fetch','./src/main.js']

@callmemagnus

This comment has been minimized.

Show comment Hide comment
@callmemagnus

callmemagnus Sep 10, 2016

Is there a way to have the polyfills loaded from a seperated chunk?

I wish that when a browser is lacking a feature it loads the predeclared polyfill (which would be put in a chunk by webpack). So that the final bundle size does not contain all the polyfills.

Is this possible somehow?

callmemagnus commented Sep 10, 2016

Is there a way to have the polyfills loaded from a seperated chunk?

I wish that when a browser is lacking a feature it loads the predeclared polyfill (which would be put in a chunk by webpack). So that the final bundle size does not contain all the polyfills.

Is this possible somehow?

@Zjaaspoer

This comment has been minimized.

Show comment Hide comment
@Zjaaspoer

Zjaaspoer Sep 14, 2016

@vikas5914, I'm getting the exact same. Did you solve this in the end, as I would rather keep using babel-polyfill

@vikas5914, I'm getting the exact same. Did you solve this in the end, as I would rather keep using babel-polyfill

@NicolasSiver

This comment has been minimized.

Show comment Hide comment
@NicolasSiver

NicolasSiver Oct 20, 2016

Adding polyfills through entry - defines them in global scope. If you own an application (SPA for example), I think It's Ok, but if you are working on the library, I don't recommend this approach.

Adding polyfills through entry - defines them in global scope. If you own an application (SPA for example), I think It's Ok, but if you are working on the library, I don't recommend this approach.

@karlAlnebratt

This comment has been minimized.

Show comment Hide comment
@karlAlnebratt

karlAlnebratt May 31, 2017

if you use webpack 2 you need to change import -> import-loader and export -> export-loader

new webpack.ProvidePlugin({
  fetch: 'imports-loader?this=>global!exports-loader?global.fetch!whatwg-fetch'
})

if you use webpack 2 you need to change import -> import-loader and export -> export-loader

new webpack.ProvidePlugin({
  fetch: 'imports-loader?this=>global!exports-loader?global.fetch!whatwg-fetch'
})
@samarpanda

This comment has been minimized.

Show comment Hide comment
@samarpanda

samarpanda Aug 2, 2017

Can we use webpack.ProvidePlugin to conditionally load polyfills? Essentially load polyfills if browser don't support natively.

Can we use webpack.ProvidePlugin to conditionally load polyfills? Essentially load polyfills if browser don't support natively.

@TheDolo

This comment has been minimized.

Show comment Hide comment
@TheDolo

TheDolo Aug 2, 2017

+1

TheDolo commented Aug 2, 2017

+1

@xgqfrms007

This comment has been minimized.

Show comment Hide comment
@xgqfrms007

xgqfrms007 Nov 29, 2017

🥇

good job!

const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const path = require('path');
const folders = {
    APP: path.resolve(__dirname, '../app'),
    BUILD: path.resolve(__dirname, '../build'),
    BOWER: path.resolve(__dirname, '../bower_components'),
    NPM: path.resolve(__dirname, '../node_modules')
};

const config = {
    entry: {
        app: [
            'webpack/hot/dev-server',
            "./js/app.js"
        ]
    },
    debug: true,
    resolve: {
        extensions: ['', '.js', '.jsx', '.scss'],
        alias: {
            //'es6-promise': path.join(folders.NPM, 'es6-promise', 'es6-promise.js'),
            //'fetch': path.join(folders.NPM, 'whatwg-fetch', 'fetch.js'),
        }
    },
    stats: {
        colors: true,
        reasons: true,
    },
    output: {
        path: __dirname + '/build',
        publicPath: '/',
        filename: '[name].[hash].js',
        chunkFilename: '[id].[hash].js'
    },
    module: {
        loaders: [
            {
                test: /\.s?css$/,
                exclude: /node_modules/,
                loaders: [
                    'style',
                    'css',
                    'autoprefixer?browsers=last 2 version',
                    'sass?' + ['outputStyle=nested'].join('&')
                ]
            },
            { test: /\.jsx?$/, loaders: ['react-hot', 'babel'], exclude: /node_modules/ },
            { test: /\.json$/, loader: 'json' },
        ]
    },
    plugins: [
        new webpack.HotModuleReplacementPlugin(),
        new webpack.ProvidePlugin({
            'Promise': 'es6-promise', // Thanks Aaron (https://gist.github.com/Couto/b29676dd1ab8714a818f#gistcomment-1584602)
            'fetch': 'imports?this=>global!exports?global.fetch!whatwg-fetch'
        }),
        //new webpack.optimize.CommonsChunkPlugin('app', null, false),
        new webpack.NoErrorsPlugin(),
        new HtmlWebpackPlugin({
            template: path.resolve('./', 'index.html'),
            webpackDevServer: '/webpack-dev-server.js'
        })
    ]
};

module.exports = config;

xgqfrms007 commented Nov 29, 2017

🥇

good job!

const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const path = require('path');
const folders = {
    APP: path.resolve(__dirname, '../app'),
    BUILD: path.resolve(__dirname, '../build'),
    BOWER: path.resolve(__dirname, '../bower_components'),
    NPM: path.resolve(__dirname, '../node_modules')
};

const config = {
    entry: {
        app: [
            'webpack/hot/dev-server',
            "./js/app.js"
        ]
    },
    debug: true,
    resolve: {
        extensions: ['', '.js', '.jsx', '.scss'],
        alias: {
            //'es6-promise': path.join(folders.NPM, 'es6-promise', 'es6-promise.js'),
            //'fetch': path.join(folders.NPM, 'whatwg-fetch', 'fetch.js'),
        }
    },
    stats: {
        colors: true,
        reasons: true,
    },
    output: {
        path: __dirname + '/build',
        publicPath: '/',
        filename: '[name].[hash].js',
        chunkFilename: '[id].[hash].js'
    },
    module: {
        loaders: [
            {
                test: /\.s?css$/,
                exclude: /node_modules/,
                loaders: [
                    'style',
                    'css',
                    'autoprefixer?browsers=last 2 version',
                    'sass?' + ['outputStyle=nested'].join('&')
                ]
            },
            { test: /\.jsx?$/, loaders: ['react-hot', 'babel'], exclude: /node_modules/ },
            { test: /\.json$/, loader: 'json' },
        ]
    },
    plugins: [
        new webpack.HotModuleReplacementPlugin(),
        new webpack.ProvidePlugin({
            'Promise': 'es6-promise', // Thanks Aaron (https://gist.github.com/Couto/b29676dd1ab8714a818f#gistcomment-1584602)
            'fetch': 'imports?this=>global!exports?global.fetch!whatwg-fetch'
        }),
        //new webpack.optimize.CommonsChunkPlugin('app', null, false),
        new webpack.NoErrorsPlugin(),
        new HtmlWebpackPlugin({
            template: path.resolve('./', 'index.html'),
            webpackDevServer: '/webpack-dev-server.js'
        })
    ]
};

module.exports = config;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment