Skip to content

Commit

Permalink
core(legacy-javascript): fix core-js 3 detection (#10852)
Browse files Browse the repository at this point in the history
  • Loading branch information
connorjclark committed May 27, 2020
1 parent e36fb5a commit f315cd2
Show file tree
Hide file tree
Showing 5 changed files with 638 additions and 593 deletions.
187 changes: 103 additions & 84 deletions lighthouse-core/audits/legacy-javascript.js
Expand Up @@ -148,9 +148,14 @@ class LegacyJavascript extends Audit {
// TODO: perhaps this is the wrong place to check for a CDN polyfill. Remove?
// expression += `|;e\\([^,]+,${qt(objectWithoutPrototype)},{${property}:`;

// Minified pattern.
// core-js@2 minified pattern.
// $export($export.S,"Date",{now:function
expression += `|\\$export\\([^,]+,${qt(objectWithoutPrototype)},{${property}:`;

// core-js@3 minified pattern.
// {target:"Array",proto:true},{fill:fill
// {target:"Array",proto:true,forced:!HAS_SPECIES_SUPPORT||!USES_TO_LENGTH},{filter:
expression += `|{target:${qt(objectWithoutPrototype)}\\S*},{${property}:`;
} else {
// WeakSet, etc.
expression += `|function ${property}\\(`;
Expand All @@ -162,88 +167,101 @@ class LegacyJavascript extends Audit {
static getPolyfillData() {
return [
/* eslint-disable max-len */
{module: 'es6.array.fill', name: 'Array.prototype.fill'},
{module: 'es6.array.filter', name: 'Array.prototype.filter'},
{module: 'es6.array.find', name: 'Array.prototype.find'},
{module: 'es6.array.find-index', name: 'Array.prototype.findIndex'},
{module: 'es6.array.for-each', name: 'Array.prototype.forEach'},
{module: 'es6.array.from', name: 'Array.from'},
{module: 'es6.array.is-array', name: 'Array.isArray'},
{module: 'es6.array.last-index-of', name: 'Array.prototype.lastIndexOf'},
{module: 'es6.array.map', name: 'Array.prototype.map'},
{module: 'es6.array.of', name: 'Array.of'},
{module: 'es6.array.reduce', name: 'Array.prototype.reduce'},
{module: 'es6.array.reduce-right', name: 'Array.prototype.reduceRight'},
{module: 'es6.array.some', name: 'Array.prototype.some'},
{module: 'es6.date.now', name: 'Date.now'},
{module: 'es6.date.to-iso-string', name: 'Date.prototype.toISOString'},
{module: 'es6.date.to-json', name: 'Date.prototype.toJSON'},
{module: 'es6.date.to-string', name: 'Date.prototype.toString'},
{module: 'es6.function.name', name: 'Function.prototype.name'},
{module: 'es6.map', name: 'Map'},
{module: 'es6.number.is-integer', name: 'Number.isInteger'},
{module: 'es6.number.is-safe-integer', name: 'Number.isSafeInteger'},
{module: 'es6.number.parse-float', name: 'Number.parseFloat'},
{module: 'es6.number.parse-int', name: 'Number.parseInt'},
{module: 'es6.object.assign', name: 'Object.assign'},
{module: 'es6.object.create', name: 'Object.create'},
{module: 'es6.object.define-properties', name: 'Object.defineProperties'},
{module: 'es6.object.define-property', name: 'Object.defineProperty'},
{module: 'es6.object.freeze', name: 'Object.freeze'},
{module: 'es6.object.get-own-property-descriptor', name: 'Object.getOwnPropertyDescriptor'},
{module: 'es6.object.get-own-property-names', name: 'Object.getOwnPropertyNames'},
{module: 'es6.object.get-prototype-of', name: 'Object.getPrototypeOf'},
{module: 'es6.object.is-extensible', name: 'Object.isExtensible'},
{module: 'es6.object.is-frozen', name: 'Object.isFrozen'},
{module: 'es6.object.is-sealed', name: 'Object.isSealed'},
{module: 'es6.object.keys', name: 'Object.keys'},
{module: 'es6.object.prevent-extensions', name: 'Object.preventExtensions'},
{module: 'es6.object.seal', name: 'Object.seal'},
{module: 'es6.object.set-prototype-of', name: 'Object.setPrototypeOf'},
{module: 'es6.promise', name: 'Promise'},
{module: 'es6.reflect.apply', name: 'Reflect.apply'},
{module: 'es6.reflect.construct', name: 'Reflect.construct'},
{module: 'es6.reflect.define-property', name: 'Reflect.defineProperty'},
{module: 'es6.reflect.delete-property', name: 'Reflect.deleteProperty'},
{module: 'es6.reflect.get', name: 'Reflect.get'},
{module: 'es6.reflect.get-own-property-descriptor', name: 'Reflect.getOwnPropertyDescriptor'},
{module: 'es6.reflect.get-prototype-of', name: 'Reflect.getPrototypeOf'},
{module: 'es6.reflect.has', name: 'Reflect.has'},
{module: 'es6.reflect.is-extensible', name: 'Reflect.isExtensible'},
{module: 'es6.reflect.own-keys', name: 'Reflect.ownKeys'},
{module: 'es6.reflect.prevent-extensions', name: 'Reflect.preventExtensions'},
{module: 'es6.reflect.set', name: 'Reflect.set'},
{module: 'es6.reflect.set-prototype-of', name: 'Reflect.setPrototypeOf'},
{module: 'es6.set', name: 'Set'},
{module: 'es6.string.code-point-at', name: 'String.prototype.codePointAt'},
{module: 'es6.string.ends-with', name: 'String.prototype.endsWith'},
{module: 'es6.string.from-code-point', name: 'String.fromCodePoint'},
{module: 'es6.string.includes', name: 'String.prototype.includes'},
{module: 'es6.string.raw', name: 'String.raw'},
{module: 'es6.string.repeat', name: 'String.prototype.repeat'},
{module: 'es6.string.starts-with', name: 'String.prototype.startsWith'},
{module: 'es6.string.trim', name: 'String.prototype.trim'},
{module: 'es6.typed.array-buffer', name: 'ArrayBuffer'},
{module: 'es6.typed.data-view', name: 'DataView'},
{module: 'es6.typed.float32-array', name: 'Float32Array'},
{module: 'es6.typed.float64-array', name: 'Float64Array'},
{module: 'es6.typed.int16-array', name: 'Int16Array'},
{module: 'es6.typed.int32-array', name: 'Int32Array'},
{module: 'es6.typed.int8-array', name: 'Int8Array'},
{module: 'es6.typed.uint16-array', name: 'Uint16Array'},
{module: 'es6.typed.uint32-array', name: 'Uint32Array'},
{module: 'es6.typed.uint8-array', name: 'Uint8Array'},
{module: 'es6.typed.uint8-clamped-array', name: 'Uint8ClampedArray'},
{module: 'es6.weak-map', name: 'WeakMap'},
{module: 'es6.weak-set', name: 'WeakSet'},
{module: 'es7.array.includes', name: 'Array.prototype.includes'},
{module: 'es7.object.entries', name: 'Object.entries'},
{module: 'es7.object.get-own-property-descriptors', name: 'Object.getOwnPropertyDescriptors'},
{module: 'es7.object.values', name: 'Object.values'},
{module: 'es7.string.pad-end', name: 'String.prototype.padEnd'},
{module: 'es7.string.pad-start', name: 'String.prototype.padStart'},
['Array.prototype.fill', 'es6.array.fill'],
['Array.prototype.filter', 'es6.array.filter'],
['Array.prototype.find', 'es6.array.find'],
['Array.prototype.findIndex', 'es6.array.find-index'],
['Array.prototype.forEach', 'es6.array.for-each'],
['Array.from', 'es6.array.from'],
['Array.isArray', 'es6.array.is-array'],
['Array.prototype.lastIndexOf', 'es6.array.last-index-of'],
['Array.prototype.map', 'es6.array.map'],
['Array.of', 'es6.array.of'],
['Array.prototype.reduce', 'es6.array.reduce'],
['Array.prototype.reduceRight', 'es6.array.reduce-right'],
['Array.prototype.some', 'es6.array.some'],
['Date.now', 'es6.date.now'],
['Date.prototype.toISOString', 'es6.date.to-iso-string'],
['Date.prototype.toJSON', 'es6.date.to-json'],
['Date.prototype.toString', 'es6.date.to-string'],
['Function.prototype.name', 'es6.function.name'],
['Map', 'es6.map'],
['Number.isInteger', 'es6.number.is-integer'],
['Number.isSafeInteger', 'es6.number.is-safe-integer'],
['Number.parseFloat', 'es6.number.parse-float'],
['Number.parseInt', 'es6.number.parse-int'],
['Object.assign', 'es6.object.assign'],
['Object.create', 'es6.object.create'],
['Object.defineProperties', 'es6.object.define-properties'],
['Object.defineProperty', 'es6.object.define-property'],
['Object.freeze', 'es6.object.freeze'],
['Object.getOwnPropertyDescriptor', 'es6.object.get-own-property-descriptor'],
['Object.getOwnPropertyNames', 'es6.object.get-own-property-names'],
['Object.getPrototypeOf', 'es6.object.get-prototype-of'],
['Object.isExtensible', 'es6.object.is-extensible'],
['Object.isFrozen', 'es6.object.is-frozen'],
['Object.isSealed', 'es6.object.is-sealed'],
['Object.keys', 'es6.object.keys'],
['Object.preventExtensions', 'es6.object.prevent-extensions'],
['Object.seal', 'es6.object.seal'],
['Object.setPrototypeOf', 'es6.object.set-prototype-of'],
['Promise', 'es6.promise'],
['Reflect.apply', 'es6.reflect.apply'],
['Reflect.construct', 'es6.reflect.construct'],
['Reflect.defineProperty', 'es6.reflect.define-property'],
['Reflect.deleteProperty', 'es6.reflect.delete-property'],
['Reflect.get', 'es6.reflect.get'],
['Reflect.getOwnPropertyDescriptor', 'es6.reflect.get-own-property-descriptor'],
['Reflect.getPrototypeOf', 'es6.reflect.get-prototype-of'],
['Reflect.has', 'es6.reflect.has'],
['Reflect.isExtensible', 'es6.reflect.is-extensible'],
['Reflect.ownKeys', 'es6.reflect.own-keys'],
['Reflect.preventExtensions', 'es6.reflect.prevent-extensions'],
['Reflect.set', 'es6.reflect.set'],
['Reflect.setPrototypeOf', 'es6.reflect.set-prototype-of'],
['Set', 'es6.set'],
['String.prototype.codePointAt', 'es6.string.code-point-at'],
['String.prototype.endsWith', 'es6.string.ends-with'],
['String.fromCodePoint', 'es6.string.from-code-point'],
['String.prototype.includes', 'es6.string.includes'],
['String.raw', 'es6.string.raw'],
['String.prototype.repeat', 'es6.string.repeat'],
['String.prototype.startsWith', 'es6.string.starts-with'],
['String.prototype.trim', 'es6.string.trim'],
// These break the coreJs2/coreJs3 naming pattern so are set explicitly.
{name: 'ArrayBuffer', coreJs2Module: 'es6.typed.array-buffer', coreJs3Module: 'es.array-buffer.constructor'},
{name: 'DataView', coreJs2Module: 'es6.typed.data-view', coreJs3Module: 'es.data-view'},
['Float32Array', 'es6.typed.float32-array'],
['Float64Array', 'es6.typed.float64-array'],
['Int16Array', 'es6.typed.int16-array'],
['Int32Array', 'es6.typed.int32-array'],
['Int8Array', 'es6.typed.int8-array'],
['Uint16Array', 'es6.typed.uint16-array'],
['Uint32Array', 'es6.typed.uint32-array'],
['Uint8Array', 'es6.typed.uint8-array'],
['Uint8ClampedArray', 'es6.typed.uint8-clamped-array'],
['WeakMap', 'es6.weak-map'],
['WeakSet', 'es6.weak-set'],
['Array.prototype.includes', 'es7.array.includes'],
['Object.entries', 'es7.object.entries'],
['Object.getOwnPropertyDescriptors', 'es7.object.get-own-property-descriptors'],
['Object.values', 'es7.object.values'],
['String.prototype.padEnd', 'es7.string.pad-end'],
['String.prototype.padStart', 'es7.string.pad-start'],
/* eslint-enable max-len */
];
].map(data => {
if (!Array.isArray(data)) return data;

const [name, coreJs2Module] = data;
return {
name,
coreJs2Module,
coreJs3Module: coreJs2Module
.replace('es6.', 'es.')
.replace('es7.', 'es.')
.replace('typed.', 'typed-array.'),
};
});
}

/**
Expand Down Expand Up @@ -306,11 +324,12 @@ class LegacyJavascript extends Audit {
// If it's a bundle with source maps, add in the polyfill modules by name too.
const bundle = bundles.find(b => b.script.src === networkRecord.url);
if (bundle) {
for (const {module, name} of polyfillData) {
for (const {coreJs2Module, coreJs3Module, name} of polyfillData) {
// Skip if the pattern matching found a match for this polyfill.
if (matches.some(m => m.name === name)) continue;

const source = bundle.rawMap.sources.find(source => source.endsWith(`${module}.js`));
const source = bundle.rawMap.sources.find(source =>
source.endsWith(`${coreJs2Module}.js`) || source.endsWith(`${coreJs3Module}.js`));
if (!source) continue;

const mapping = bundle.map.mappings().find(m => m.sourceURL === source);
Expand Down
40 changes: 32 additions & 8 deletions lighthouse-core/scripts/legacy-javascript/run.js
Expand Up @@ -25,6 +25,7 @@ const hash = crypto
.update(fs.readFileSync(`${__dirname}/yarn.lock`, 'utf8'))
.update(fs.readFileSync(`${__dirname}/run.js`, 'utf8'))
.update(fs.readFileSync(`${__dirname}/main.js`, 'utf8'))
.update(fs.readFileSync(require.resolve('../../audits/legacy-javascript.js'), 'utf8'))
.digest('hex');
const VARIANT_DIR = `${__dirname}/variants/${hash}`;

Expand All @@ -34,7 +35,7 @@ const STAGE = process.env.STAGE || 'all';
const mainCode = fs.readFileSync(`${__dirname}/main.js`, 'utf-8');

const plugins = LegacyJavascript.getTransformPatterns().map(pattern => pattern.name);
const polyfills = LegacyJavascript.getPolyfillData().map(d => d.module);
const polyfills = LegacyJavascript.getPolyfillData();

/**
* @param {string} command
Expand Down Expand Up @@ -95,6 +96,7 @@ async function createVariant(options) {
`${dir}/main.transpiled.js`,
'-o', `${dir}/main.bundle.js`,
'--debug', // source maps
'--full-paths=false',
]);

// Minify.
Expand Down Expand Up @@ -217,6 +219,13 @@ function createSummarySizes() {
fs.writeFileSync(`${__dirname}/summary-sizes.txt`, lines.join('\n'));
}

/**
* @param {string} module
*/
function makeRequireCodeForPolyfill(module) {
return `require("../../../../node_modules/core-js/modules/${module}")`;
}

async function main() {
for (const plugin of plugins) {
await createVariant({
Expand Down Expand Up @@ -254,12 +263,23 @@ async function main() {
}

for (const polyfill of polyfills) {
const module = coreJsVersion === 2 ? polyfill.coreJs2Module : polyfill.coreJs3Module;
await createVariant({
group: `core-js-${coreJsVersion}-only-polyfill`,
name: polyfill,
code: `require("core-js/modules/${polyfill}")`,
name: module,
code: makeRequireCodeForPolyfill(module),
});
}

const allPolyfillCode = polyfills.map(polyfill => {
const module = coreJsVersion === 2 ? polyfill.coreJs2Module : polyfill.coreJs3Module;
return makeRequireCodeForPolyfill(module);
}).join('\n');
await createVariant({
group: 'all-legacy-polyfills',
name: `all-legacy-polyfills-core-js-${coreJsVersion}`,
code: allPolyfillCode,
});
}

removeCoreJs();
Expand All @@ -269,15 +289,19 @@ async function main() {
// Summary of using source maps and pattern matching.
summary = makeSummary('legacy-javascript.json');
fs.writeFileSync(`${__dirname}/summary-signals.json`, JSON.stringify(summary, null, 2));
console.log({
totalSignals: summary.totalSignals,
variantsMissingSignals: summary.variantsMissingSignals,
});
console.table(summary.variants);

// Summary of using only pattern matching.
summary = makeSummary('legacy-javascript-nomaps.json');
fs.writeFileSync(`${__dirname}/summary-signals-nomaps.json`, JSON.stringify(summary, null, 2));
console.log({
totalSignals: summary.totalSignals,
variantsMissingSignals: summary.variantsMissingSignals,
});
console.table(summary.variants.filter(variant => {
// Too many signals, break layout.
if (variant.name.includes('all-legacy-polyfills')) return false;
return true;
}));

createSummarySizes();
}
Expand Down

0 comments on commit f315cd2

Please sign in to comment.