I'd like to better understand the differences between how promises are implemented in webpack. Normally, blissful ignorance was enough to get by as I mostly develop apps, but I am definately a little confused in how to properly develop a plugin/tool/lib.
webpack.config.js - using babel-polyfill as an entry point
module.exports = {
entry: {
foo: [
'core-js/fn/promise', <-- here
'./js/index.js'
]
},
module: {
rules: [
{
test: /\.js$/,
loader: 'babel-loader'
}
]
}
}
Q: In this approach, since it's a polyfill it modifies the global Promise?
webpack config - shimming using webpacks provide plugin
module.exports = {
entry: './js/index.js',
module: {
rules: [
{
test: /\.js$/,
loader: 'babel-loader'
}
]
},
plugins: [
new webpack.ProvidePlugin({
Promise: 'es6-promise' <-- here
})
]
};
Q: Does this mean that the Promise is a module only specific to the webpack bundling process? Does the transpiled ES5 code have a local copy or es6-promise? Does it patch the global Promise?
webpack.config.js - using babel-plugin-transform-runtime
module.exports = {
entry: {
foo: [
'./js/start.js'
]
},
module: {
rules: [
{
test: /\.js$/,
loader: 'babel-loader'
}
]
}
}
.babelrc
{
"presets": [ "es2015" ],
"plugins": ["transform-runtime"] <--here
}
start.js
require('babel-runtime/core-js/promise').default = require('es6-promise'); <--here
require('plugin');
Q: This aliases the es6-promise to the babel-runtime promise and is not global but only local to the tool?
entry: ['core-js/fn/promise', './index.js']
This has the same effect as if you imported it at the top of your entry point.
In this approach, since it's a polyfill it modifies the global Promise?
Yes, this polyfill changes the global Promise
. Calling it a polyfill usually means that it patches the global built-ins, although this is not strictly adhered to. If they don't change existing APIs but only provide the functionality, they are sometimes called Ponyfills.
ProvidePlugin
new webpack.ProvidePlugin({
Promise: 'es6-promise'
})
The ProvidePlugin
will import the configured module at the beginning of the module that uses it when the corresponding free variable is found. A free variable is an identifier that has not been declared in the current scope. Global variables are free variables in all local scopes.
Whenever a free Promise
is encountered, webpack will add the following to the beginning of the module:
var Promise = require('es6-promise');
Does this mean that the Promise is a module only specific to the webpack bundling process?
That is correct, because ProvidePlugin
is webpack specific and it's very unlikely that any other tool will respect any webpack settings.
Does the transpiled ES5 code have a local copy or es6-promise?
As with any other module, it is included once by webpack and all imports refer to that module.
Does it patch the global Promise?
It will only modify the global Promise
if the imported module does it explicitly. The es6-promise
you're using, does not patch the global by default as shown in Auto-polyfill.
{
"plugins": ["transform-runtime"]
}
The babel-plugin-transform-runtime
uses core-js
to provide missing functionalities like Promise
. As you will recall, I said that core-js
modifies the global Promise
. This is not true for this case, because babel uses the version that doesn't pollute the global namespace, which is in core-js/library
as mentioned in the core-js
README. For example:
const Promise = require('core-js/library/fn/promise');
Babel will import the core-js
Promise and replace Promise
with the imported variable. See also the example in babel-plugin-transform-runtime
- core-js
aliasing. This is essentially the same thing as webpack's ProvidePlugin
except that babel does not bundle up the modules, so it's just adding the import.
This aliases the es6-promise to the babel-runtime promise and is not global but only local to the tool?
It is not global because it's just a module. Babel takes your JavaScript and outputs some other JavaScript where the configured features are transpiled to ES5. You will run or bundle the resulting JavaScript and it's practically the same as if you had written ES5 in the first place.
require('babel-runtime/core-js/promise').default = require('es6-promise');
With that you modify the export and therefore the modules will use es6-promise
instead. But overwriting an export is not a good idea, especially since the imports of ES modules are immutable in the spec. Babel is currently not spec-compliant in that regard. For more details see Making transpiled ES modules more spec-compliant.
It depends on what you're doing. Apart from the difference of whether they change globals or not, you can choose whichever you prefer. For instance using babel's transform runtime allows you to use it with any tool that uses babel, not just webpack.
None.
Leave the polyfill to the application developer. But you might mention that it depends on a certain feature and when the user wants to use the library in an environment that doesn't support the feature, they have to polyfill it. It's also fairly reasonable to assume that Promises are widely supported and if an application targets older environments, they will very likely have polyfilled it already. Keep in mind that this doesn't mean that you shouldn't transpile new features / syntax. This is specifically for new functionality like Promise
or String.prototype.trimLeft
.
That also depends on your definition of a tool. Let's assume a tool is a piece of software that is used by developers (e.g. webpack, eslint, etc.). In that case it is exactly the same as for any app, at the end of the day it's just another app but only targeting developers. Specifically speaking about command line tools, you should decide what minimum Node version you want to support and include anything that is needed for that, you can specify that in your package.json
in the engines
field.
Plugin is a very broad term and can be anything between a library and an app. For example a webpack plugin or loader should work as is, whereas a jQuery plugin will be part of a web app and you should treat it as a library (they should probably be called library instead of plugin). Generally you want to match the guidelines of whatever you're extending. Have a look at it and see what they are targeting. For example webpack currently supports Node verions >=4.3.0, so your plugin should too.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With