Both polyfill and transpilation enable you to use new features in old environments (think of old vs new browsers for example), the main difference is that a polyfill does that by implementing the feature in the old environment itself. So if we go by ES6 ES5 terminology, polyfill enables you to use a new feature by implementing it in ES5, however some new features can never be implemented in ES5, think for example about the arrow syntax introduced by ES6 - this is where transpilation is needed.
An example where transpilation is needed is arrow syntax (() => {}) because you can never implement that in ES5, and an example where a polyfill can be used is if you wanted to implement the String.prototype.includes
method. That can be implemented like this:
// taken from mdn
if (!String.prototype.includes) {
String.prototype.includes = function(search, start) {
'use strict';
if (search instanceof RegExp) {
throw TypeError('first argument must not be a RegExp');
}
if (start === undefined) { start = 0; }
return this.indexOf(search, start) !== -1;
};
}
Now another important distinction is that a polyfill will use the native feature if it exists, however transpilation will not. That leads to the conclusion that polyfills should be preferred over transpilation, because of security and performance reasons.
Last distinction is that transpilation is about language syntax, while polyfill is about language syntax and native browser APIs.
Long story made short: transpilation is at compile time, while polyfill is at runtime.
Now that the distinction has been made clear, we can see that to use new code and features Babel uses both strategies. The main guideline is to use polyfill when possible; otherwise, transpilation (transpilation will be needed for a new syntax that can never be implemented in the old ES5 environment, itself). So you need to import core-js to polyfill features that could have been enabled by transpilation, but its better to implement them using polyfill.
Let's take some code as an example, to differentiate between the two:
const f = (str) => {
return str.includes('fun')
}
what Babel will do is transpile that code into something like the following:
var f = function f() {
// notice that `.includes()` stays. Even though this is
// an ES6 feature, it stays because
// Babel does not transpile it.
//
// the following code will break if you don't import
// a polyfill using core-js or any other polyfill library
return str.includes('fun');
}
I have cut a lot of what Babel transpilation output would be, if you want to see it you can read here.
And now to your question if Babel uses something automatic to do that. The answer is yes, and that is @babel/preset-env
, which will configure the transpilation processes and polyfills needed for you automatically based on your target environment.