babel-plugin-which-builtins v0.0.2
#babel-plugin-which-builtins
This is a Babel plugin which attempts to determine which ECMAScript 2015/2016/2017
library built-ins are used by a codebase and only import polyfills for just the
built-ins which are actually used. It is a replacement for importing the
entirety of babel-polyfill.
##Instructions
First install the plugin to your project, along with core-js and regenerator-runtime:
npm install --save-dev babel-plugin-which-builtins core-js regenerator-runtimeNext add it to your Babel configuration after all the plugins that transform your
code. For example, in a .babelrc file:
{
"plugins": { "which-builtins" }
}##What does it do?
When this plugin encounters code that seems to use new built-in JavaScript global
objects, static methods, or instance methods, it adds an import declaration
for a polyfill that will add that built-in. For example, with this input:
const foo = Array.from([1,2,3]);the plugin will produce this output:
import "core-js/modules/es6.array.from"
import "core-js/modules/es6.array.iterator"
const foo = Array.from([1,2,3]);Note that this transformer does not insert the polyfill itself; it just adds an import statement for the polyfill. If you want to run this code in a browser, you will need to run it through a bundler like webpack, browserify, or rollup.
In additon to the ECMAScript polyfills of core-js, the plugin also looks for
and polyfills references to regeneratorRuntime, which are created by the standard
Babel plugin for transforming generators.
##When does the plugin fail?
Unfortunately, in a completely dynamic language like JavaScript, static analysis
can only go so far, and as a result babel-plugin-which-builtins cannot catch
every usage of new built-ins. There are two main known cases where the plugin
will fail.
The first case in which the plugin will fail is when code refers to one of the
built-in globals that has new methods (like Math or Array) using dynamic code
to refer to the global object. As an example:
// this case will work.
var a = Math.cos(90);
var { cos } = Math;
// this case will not work because Math doesn't have a direct
// property access in the code.
function getMath() { return Math; }
var b = getMath().cos(90);
var { cos } = getMath();The second case in which the plugin will fail is when code refers to a new built-in static or instance method in a dynamic way. For example:
// these cases will work.
var a = "foo".startsWith("f");
var b = "foo"["startsWith"]("f");
var { startsWith } = "foo";
var c = Math.cos(90);
var d = Math["cos"](90);
var { cos } = Math;
// these cases will not work.
var e = "foo"["starts" + "With"]("f");
var { ["starts" + "With"]: f } = "foo";
var g = "startsWith".forEach(method => "foo"[method]("f"))[0];
var h = Math["c" + "os"](90);
var { ["c" + "os"]: i } = Math;
function getCos() {
return "cos";
}
var j = Math[getCos()](90);
var { [getCos()]: k } = Math;##Instance methods may produce false positives
Some of the new built-ins in ES2015/2016/2017 are instance methods, like
Array.prototype.find or String.prototype.startsWith. It's very difficult
(bordering on impossible) to statically figure out the type of a variable
in JavaScript, so this plugin is conservative and assumes that any reference to
a property with the same name of a new instance method is a reference to that
instance method. For example:
// this triggers an import of String.prototype.startsWith.
var a = "foo".startsWith("f");
var { startsWith } = "foo";
// this also triggers an import of String.prototype.startsWith (false positive).
var b = null;
var c = b.startsWith("f");
var { startsWith } = b;
// this also triggers an import of String.prototype.startsWith (false positive).
var e = b["startsWith"];The theory here is that including an instance method polyfill when it's not needed is better than not including it when it is needed.
#What features are polyfilled?
- Generators
regeneratorRuntime- Global objects
DataViewInt8ArrayUint8ArrayUint8ClampedArrayInt16ArrayUint16ArrayInt32ArrayUint32ArrayFloat32ArrayFloat64ArrayMapSetWeakMapWeakSetPromiseSymbolReflect- Static methods
Array.fromArray.ofMath.acoshMath.asinhMath.atanhMath.cbrtMath.clz32Math.coshMath.expm1Math.froundMath.hypotMath.imulMath.log1pMath.log10Math.log2Math.signMath.sinhMath.tanhMath.truncNumber.isFiniteNumber.isIntegerNumber.isSafeIntegerNumber.isNaNNumber.EPSILONNumber.MIN_SAFE_INTEGERNumber.MAX_SAFE_INTEGERObject.assignObject.isObject.getOwnPropertySymbolsObject.setPrototypeOfObject.valuesObject.entriesObject.getOwnPropertyDescriptorsString.raw- Instance methods
Array.prototype.copyWithinArray.prototype.findArray.prototype.findIndexArray.prototype.fillArray.prototype.includesFunction.prototype.nameRegExp.prototype.flagsRegExp.prototype.matchRegExp.prototype.replaceRegExp.prototype.splitRegExp.prototype.searchString.prototype.codePointAtString.prototype.fromCodePointString.prototype.padStartString.prototype.padEndString.prototype.repeatString.prototype.startsWithString.prototype.endsWithString.prototype.includes