Oh boy :) cracks knuckles tl;dr no, also I overlooked so much, thanks for pointing this out.
The minified JS I linked to is every polyfill that @babel/present-env
would provide. The recommended distribution of these polyfills is not the module I linked (@babel/polyfill
), but instead via @babel/preset-env
. Regardless, they both use the polyfills as defined by core-js
. You can see that the polyfills module has a standard bundling and minifying script: https://github.com/babel/babel/blob/fced5cea430cc00e916876b663a8d2a84a5dad1f/packages/babel-polyfill/scripts/build-dist.sh, which is what I linked to before. The question is: does the pattern I'm looking for hold for these core-js
polyfills regardless of the minifier used? Additionally: where does this pattern come from? I'll focus on that first.
To further complicate, there's core-js@2
and core-js@3
, and both seem to be used (or at least, supported in @babel/preset-env
).
Let's start with core-js@2
.
There's a few different mechanisms for defining polyfills.
Here's a typical polyfill: https://github.com/zloirock/core-js/blob/6a3fe85136aaae0e3b099c96a05a5ceb1f515a50/modules/es6.array.fill.js
// 22.1.3.6 Array.prototype.fill(value, start = 0, end = this.length)
var $export = require('./_export');
$export($export.P, 'Array', { fill: require('./_array-fill') });
require('./_add-to-unscopables')('fill');
Here's another: https://github.com/zloirock/core-js/blob/6a3fe85136aaae0e3b099c96a05a5ceb1f515a50/modules/es6.array.find-index.js
'use strict';
// 22.1.3.9 Array.prototype.findIndex(predicate, thisArg = undefined)
var $export = require('./_export');
var $find = require('./_array-methods')(6);
var KEY = 'findIndex';
var forced = true;
// Shouldn't skip holes
if (KEY in []) Array(1)[KEY](function () { forced = false; });
$export($export.P + $export.F * forced, 'Array', {
findIndex: function findIndex(callbackfn /* , that = undefined */) {
return $find(this, callbackfn, arguments.length > 1 ? arguments[1] : undefined);
}
});
require('./_add-to-unscopables')(KEY);
$export
is a helper function to define an extension to the provided class. The first parameter is a bitmap.
// type bitmap
$export.F = 1; // forced
$export.G = 2; // global
$export.S = 4; // static
$export.P = 8; // proto
$export.B = 16; // bind
$export.W = 32; // wrap
$export.U = 64; // safe
$export.R = 128; // real proto method for `library`
So any number of these flags, but at least one, are passed in as the first parameter. I recall seeing this before, which explains why the regex has [^,]+
.
The second parameter the name of the class. The third is an object whose keys are properties/methods that will be added.
There's also a redefine
helper function, that looks pretty simple: https://github.com/zloirock/core-js/blob/6a3fe85136aaae0e3b099c96a05a5ceb1f515a50/modules/es6.regexp.to-string.js#L10
require('./_redefine')(RegExp.prototype, TO_STRING, fn, true);
That constant TO_STRING
is interesting - I'm seeing that some polyfills use constants for the method name, others don't. Should be the same code post-minification (it'd get inlined), but without minifying the difference is maybe something to consider.
Polyfills that create a new collection class uses _collection
.
'use strict';
var strong = require('./_collection-strong');
var validate = require('./_validate-collection');
var SET = 'Set';
// 23.2 Set Objects
module.exports = require('./_collection')(SET, function (get) {
return function Set() { return get(this, arguments.length > 0 ? arguments[0] : undefined); };
}, {
// 23.2.3.1 Set.prototype.add(value)
add: function add(value) {
return strong.def(validate(this, SET), value = value === 0 ? 0 : value, value);
}
}, strong);
require('./_typed-array')('Float32', 4, function (init) {
return function Float32Array(data, byteOffset, length) {
return init(this, data, byteOffset, length);
};
});
For these, we could combine each typed-array variant into one check by looking for code you'd find in require('./_typed-array')
.
Different patterns here.
var $ = require('../internals/export');
var fill = require('../internals/array-fill');
var addToUnscopables = require('../internals/add-to-unscopables');
// `Array.prototype.fill` method
// https://tc39.github.io/ecma262/#sec-array.prototype.fill
$({ target: 'Array', proto: true }, {
fill: fill
});
// https://tc39.github.io/ecma262/#sec-array.prototype-@@unscopables
addToUnscopables('fill');
No change really.
This command: npx browserify node_modules/core-js/modules/es6.array.find.js | npx uglify-js --mangle keep_fnames
will transform this:
$export($export.P + $export.F * forced, 'Array', {
find: function find(
to this:
e(e.P+e.F*a,"Array",{find:function find(
Drop the mangling, and you get:
$export($export.P+$export.F*forced,"Array",{find:function find(
I'm really curious how uglifyjs picks the short name it can use, and why it seems to commonly be e
. The answer lies somewhere in here: https://github.com/mishoo/UglifyJS2/blob/e8a2c0b5bf18659a3e1285b6038ea755a290220d/lib/scope.js
With this extra domain knowledge, I'd like to create the regex that'd match each of these few different patterns. And the pattern should survive typical minification. I'd want to verify these patterns too - could bundle each polyfill individually, minify, and apply the pattern.
Just a tip: the overwhelming majority of projects do not have property mangling enabled, which means any "tell-tale" properties in core-js or other polyfills can be used as relatively reliable indicators of their presence.
For example, core-js can be detected by looking for a few properties used in the _export helper mentioned above: