core(legacy-javascript): new audit (#10303)
This commit is contained in:
Родитель
6696ce4e4a
Коммит
fa70748c46
|
@ -12,3 +12,5 @@
|
|||
coverage/**
|
||||
|
||||
!.eslintrc.js
|
||||
|
||||
lighthouse-core/scripts/legacy-javascript/variants
|
||||
|
|
|
@ -32,6 +32,8 @@ last-run-results.html
|
|||
!lighthouse-core/test/fixtures/traces/**/*.trace.json
|
||||
!lighthouse-core/test/fixtures/traces/**/*.devtoolslog.json
|
||||
|
||||
lighthouse-core/scripts/legacy-javascript/variants
|
||||
|
||||
latest-run
|
||||
lantern-data
|
||||
timings-data
|
||||
|
|
|
@ -29,8 +29,6 @@ before_script:
|
|||
- google-chrome-stable --version
|
||||
- yarn build-all
|
||||
script:
|
||||
# Fail if any changes were written to source files (ex, from: build/build-cdt-lib.js).
|
||||
- git diff --exit-code
|
||||
- yarn bundlesize
|
||||
- yarn diff:sample-json
|
||||
- yarn lint
|
||||
|
@ -40,10 +38,14 @@ script:
|
|||
- yarn test-clients
|
||||
- yarn test-viewer
|
||||
- yarn test-lantern
|
||||
- yarn test-legacy-javascript
|
||||
- yarn test-bundle
|
||||
- yarn i18n:checks
|
||||
- yarn dogfood-lhci
|
||||
- yarn test-docs
|
||||
|
||||
# Fail if any changes were written to source files (ex, from: build/build-cdt-lib.js).
|
||||
- git diff --exit-code
|
||||
before_cache:
|
||||
# nyc, jest and other projects store files in here. They mess up the travis build cache.
|
||||
- rm -rf ./node_modules/.cache/
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
<!--
|
||||
* Copyright 2020 Google Inc. All Rights Reserved.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
|
||||
-->
|
||||
<html>
|
||||
<body>
|
||||
wanna cracker?
|
||||
<script src="/legacy-javascript.js"></script>
|
||||
<script>
|
||||
String.prototype.includes = function() {};
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,11 @@
|
|||
/**
|
||||
* @license Copyright 2020 Google Inc. All Rights Reserved.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
/*eslint-disable*/
|
||||
|
||||
// This is `./lighthouse-core/scripts/legacy-javascript/variants/core-js-3-preset-env-esmodules/false/main.bundle.min.js`.
|
||||
|
||||
(function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,(function(r){var n=e[i][1][r];return o(n||r)}),p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){"use strict";function _toConsumableArray(arr){return _arrayWithoutHoles(arr)||_iterableToArray(arr)||_nonIterableSpread()}function _nonIterableSpread(){throw new TypeError("Invalid attempt to spread non-iterable instance")}function _iterableToArray(iter){if(Symbol.iterator in Object(iter)||Object.prototype.toString.call(iter)==="[object Arguments]")return Array.from(iter)}function _arrayWithoutHoles(arr){if(Array.isArray(arr)){for(var i=0,arr2=new Array(arr.length);i<arr.length;i++){arr2[i]=arr[i]}return arr2}}function asyncGeneratorStep(gen,resolve,reject,_next,_throw,key,arg){try{var info=gen[key](arg);var value=info.value}catch(error){reject(error);return}if(info.done){resolve(value)}else{Promise.resolve(value).then(_next,_throw)}}function _asyncToGenerator(fn){return function(){var self=this,args=arguments;return new Promise((function(resolve,reject){var gen=fn.apply(self,args);function _next(value){asyncGeneratorStep(gen,resolve,reject,_next,_throw,"next",value)}function _throw(err){asyncGeneratorStep(gen,resolve,reject,_next,_throw,"throw",err)}_next(undefined)}))}}function _classCallCheck(instance,Constructor){if(!(instance instanceof Constructor)){throw new TypeError("Cannot call a class as a function")}}var MyTestClass=function MyTestClass(){_classCallCheck(this,MyTestClass)};function reg(){return _reg.apply(this,arguments)}function _reg(){_reg=_asyncToGenerator(regeneratorRuntime.mark((function _callee(){return regeneratorRuntime.wrap((function _callee$(_context){while(1){switch(_context.prev=_context.next){case 0:_context.next=2;return 1;case 2:case"end":return _context.stop()}}}),_callee)})));return _reg.apply(this,arguments)}var spread=[1,2,3].concat([3,2,1]);reg.apply(void 0,_toConsumableArray(spread))},{}]},{},[1]);
|
|
@ -70,6 +70,11 @@ const smokeTests = [{
|
|||
id: 'mixed-content',
|
||||
expectations: require('./mixed-content/mixed-content-expectations.js'),
|
||||
config: require('../../../../lighthouse-core/config/mixed-content-config.js'),
|
||||
},
|
||||
{
|
||||
id: 'legacy-javascript',
|
||||
expectations: require('./legacy-javascript/expectations.js'),
|
||||
config: require('./legacy-javascript/legacy-javascript-config.js'),
|
||||
}];
|
||||
|
||||
module.exports = smokeTests;
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
/**
|
||||
* @license Copyright 2020 Google Inc. All Rights Reserved.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Expected Lighthouse audit values for sites with polyfills.
|
||||
*/
|
||||
module.exports = [
|
||||
{
|
||||
lhr: {
|
||||
requestedUrl: 'http://localhost:10200/legacy-javascript.html',
|
||||
finalUrl: 'http://localhost:10200/legacy-javascript.html',
|
||||
audits: {
|
||||
'legacy-javascript': {
|
||||
details: {
|
||||
items: [
|
||||
{
|
||||
url: 'http://localhost:10200/legacy-javascript.js',
|
||||
signals: [
|
||||
'@babel/plugin-transform-classes',
|
||||
'@babel/plugin-transform-regenerator',
|
||||
'@babel/plugin-transform-spread',
|
||||
],
|
||||
},
|
||||
{
|
||||
url: 'http://localhost:10200/legacy-javascript.html',
|
||||
signals: [
|
||||
'String.prototype.includes',
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
|
@ -0,0 +1,23 @@
|
|||
/**
|
||||
* @license Copyright 2020 Google Inc. All Rights Reserved.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Config file for running PWA smokehouse audits.
|
||||
*/
|
||||
module.exports = {
|
||||
extends: 'lighthouse:default',
|
||||
settings: {
|
||||
onlyCategories: [
|
||||
'performance',
|
||||
],
|
||||
onlyAudits: [
|
||||
'legacy-javascript',
|
||||
],
|
||||
},
|
||||
// Not in default yet.
|
||||
audits: ['legacy-javascript'],
|
||||
};
|
|
@ -0,0 +1,372 @@
|
|||
/**
|
||||
* @license Copyright 2020 Google Inc. All Rights Reserved.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* @fileoverview Identifies polyfills and transforms that should not be present if using module/nomodule pattern.
|
||||
* @see https://docs.google.com/document/d/1ItjJwAd6e0Ts6yMbvh8TN3BBh_sAd58rYE1whnpuxaA/edit Design document
|
||||
* @see https://docs.google.com/spreadsheets/d/1z28Au8wo8-c2UsM2lDVEOJcI3jOkb2c951xEBqzBKCc/edit?usp=sharing Legacy babel transforms / polyfills
|
||||
* ./lighthouse-core/scripts/legacy-javascript - verification tool.
|
||||
*/
|
||||
|
||||
/** @typedef {{name: string, expression: string}} Pattern */
|
||||
/** @typedef {{name: string, line: number, column: number}} PatternMatchResult */
|
||||
|
||||
const Audit = require('./audit.js');
|
||||
const NetworkRecords = require('../computed/network-records.js');
|
||||
const MainResource = require('../computed/main-resource.js');
|
||||
const URL = require('../lib/url-shim.js');
|
||||
const i18n = require('../lib/i18n/i18n.js');
|
||||
|
||||
const UIStrings = {
|
||||
/** Title of a Lighthouse audit that tells the user about legacy polyfills and transforms used on the page. This is displayed in a list of audit titles that Lighthouse generates. */
|
||||
title: 'Legacy JavaScript',
|
||||
// eslint-disable-next-line max-len
|
||||
// TODO: web.dev article. this codelab is good starting place: https://web.dev/codelab-serve-modern-code/
|
||||
/** Description of a Lighthouse audit that tells the user about old JavaScript that is no longer needed. This is displayed after a user expands the section to see more. No character length limits. 'Learn More' becomes link text to additional documentation. */
|
||||
description: 'Polyfills and transforms enable legacy browsers to use new JavaScript features. However, many aren\'t necessary for modern browsers. For your bundled JavaScript, adopt a modern script deployment strategy using module/nomodule feature detection to reduce the amount of code shipped to modern browsers, while retaining support for legacy browsers. [Learn More](https://philipwalton.com/articles/deploying-es2015-code-in-production-today/)',
|
||||
};
|
||||
|
||||
const str_ = i18n.createMessageInstanceIdFn(__filename, UIStrings);
|
||||
|
||||
/**
|
||||
* Takes a list of patterns (consisting of a name identifier and a RegExp expression string)
|
||||
* and returns match results with line / column information for a given code input.
|
||||
*/
|
||||
class CodePatternMatcher {
|
||||
/**
|
||||
* @param {Pattern[]} patterns
|
||||
*/
|
||||
constructor(patterns) {
|
||||
const patternsExpression = patterns.map(pattern => `(${pattern.expression})`).join('|');
|
||||
this.re = new RegExp(`(^\r\n|\r|\n)|${patternsExpression}`, 'g');
|
||||
this.patterns = patterns;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} code
|
||||
* @return {PatternMatchResult[]}
|
||||
*/
|
||||
match(code) {
|
||||
// Reset RegExp state.
|
||||
this.re.lastIndex = 0;
|
||||
|
||||
const seen = new Set();
|
||||
/** @type {PatternMatchResult[]} */
|
||||
const matches = [];
|
||||
/** @type {RegExpExecArray | null} */
|
||||
let result;
|
||||
let line = 0;
|
||||
let lineBeginsAtIndex = 0;
|
||||
// Each pattern maps to one subgroup in the generated regex. For each iteration of RegExp.exec,
|
||||
// only one subgroup will be defined. Exec until no more matches.
|
||||
while ((result = this.re.exec(code)) !== null) {
|
||||
// Discard first value in `result` - it's just the entire match.
|
||||
const captureGroups = result.slice(1);
|
||||
// isNewline - truthy if matching a newline, used to track the line number.
|
||||
// `patternExpressionMatches` maps to each possible pattern in `this.patterns`.
|
||||
// Only one of [isNewline, ...patternExpressionMatches] is ever truthy.
|
||||
const [isNewline, ...patternExpressionMatches] = captureGroups;
|
||||
if (isNewline) {
|
||||
line++;
|
||||
lineBeginsAtIndex = result.index + 1;
|
||||
continue;
|
||||
}
|
||||
const pattern = this.patterns[patternExpressionMatches.findIndex(Boolean)];
|
||||
|
||||
// Don't report more than one instance of a pattern for this code.
|
||||
// Would result in multiple matches for the same pattern, ex: if both '='
|
||||
// and 'Object.defineProperty' are used conditionally based on feature detection.
|
||||
// Would also result in many matches for transform patterns.
|
||||
if (seen.has(pattern)) {
|
||||
continue;
|
||||
}
|
||||
seen.add(pattern);
|
||||
|
||||
matches.push({
|
||||
name: pattern.name,
|
||||
line,
|
||||
column: result.index - lineBeginsAtIndex,
|
||||
});
|
||||
}
|
||||
|
||||
return matches;
|
||||
}
|
||||
}
|
||||
|
||||
class LegacyJavascript extends Audit {
|
||||
/**
|
||||
* @return {LH.Audit.Meta}
|
||||
*/
|
||||
static get meta() {
|
||||
return {
|
||||
id: 'legacy-javascript',
|
||||
scoreDisplayMode: Audit.SCORING_MODES.INFORMATIVE,
|
||||
description: str_(UIStrings.description),
|
||||
title: str_(UIStrings.title),
|
||||
requiredArtifacts: ['devtoolsLogs', 'ScriptElements', 'URL'],
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string?} object
|
||||
* @param {string} property
|
||||
*/
|
||||
static buildPolyfillExpression(object, property) {
|
||||
const qt = (/** @type {string} */ token) =>
|
||||
`['"]${token}['"]`; // don't worry about matching string delims
|
||||
|
||||
let expression = '';
|
||||
|
||||
if (object) {
|
||||
// String.prototype.startsWith =
|
||||
expression += `${object}\\.${property}\\s?=[^=]`;
|
||||
} else {
|
||||
// Promise =
|
||||
// window.Promise =// Promise =Z
|
||||
// but not: SomePromise =
|
||||
expression += `(?:window\\.|[\\s;]+)${property}\\s?=[^=]`;
|
||||
}
|
||||
|
||||
// String.prototype['startsWith'] =
|
||||
if (object) {
|
||||
expression += `|${object}\\[${qt(property)}\\]\\s?=[^=]`;
|
||||
}
|
||||
|
||||
// Object.defineProperty(String.prototype, 'startsWith'
|
||||
expression += `|defineProperty\\(${object || 'window'},\\s?${qt(property)}`;
|
||||
|
||||
// core-js
|
||||
if (object) {
|
||||
const objectWithoutPrototype = object.replace('.prototype', '');
|
||||
// e(e.S,"Object",{values
|
||||
// Minified + mangled pattern found in CDN babel-polyfill.
|
||||
// see https://cdnjs.cloudflare.com/ajax/libs/babel-polyfill/7.2.5/polyfill.min.js
|
||||
// TODO: perhaps this is the wrong place to check for a CDN polyfill. Remove?
|
||||
// expression += `|;e\\([^,]+,${qt(objectWithoutPrototype)},{${property}:`;
|
||||
|
||||
// Minified pattern.
|
||||
// $export($export.S,"Date",{now:function
|
||||
expression += `|\\$export\\([^,]+,${qt(objectWithoutPrototype)},{${property}:`;
|
||||
} else {
|
||||
// WeakSet, etc.
|
||||
expression += `|function ${property}\\(`;
|
||||
}
|
||||
|
||||
return expression;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {Pattern[]}
|
||||
*/
|
||||
static getPolyfillPatterns() {
|
||||
return [
|
||||
'Array.fill',
|
||||
'Array.from',
|
||||
'Array.isArray',
|
||||
'Array.of',
|
||||
'Array.prototype.filter',
|
||||
'Array.prototype.find',
|
||||
'Array.prototype.findIndex',
|
||||
'Array.prototype.forEach',
|
||||
'Array.prototype.includes',
|
||||
'Array.prototype.lastIndexOf',
|
||||
'Array.prototype.map',
|
||||
'Array.prototype.reduce',
|
||||
'Array.prototype.reduceRight',
|
||||
'Array.prototype.some',
|
||||
'ArrayBuffer',
|
||||
'DataView',
|
||||
'Date.now',
|
||||
'Date.prototype.toISOString',
|
||||
'Date.prototype.toJSON',
|
||||
'Date.prototype.toString',
|
||||
'Float32Array',
|
||||
'Float64Array',
|
||||
'Function.prototype.name',
|
||||
'Int16Array',
|
||||
'Int32Array',
|
||||
'Int8Array',
|
||||
'Map',
|
||||
'Number.isInteger',
|
||||
'Number.isSafeInteger',
|
||||
'Number.parseFloat',
|
||||
'Number.parseInt',
|
||||
'Object.assign',
|
||||
'Object.create',
|
||||
'Object.defineProperties',
|
||||
'Object.defineProperty',
|
||||
'Object.entries',
|
||||
'Object.freeze',
|
||||
'Object.getOwnPropertyDescriptor',
|
||||
'Object.getOwnPropertyDescriptors',
|
||||
'Object.getOwnPropertyNames',
|
||||
'Object.getPrototypeOf',
|
||||
'Object.isExtensible',
|
||||
'Object.isFrozen',
|
||||
'Object.isSealed',
|
||||
'Object.keys',
|
||||
'Object.preventExtensions',
|
||||
'Object.seal',
|
||||
'Object.setPrototypeOf',
|
||||
'Object.values',
|
||||
'Promise',
|
||||
'Reflect.apply',
|
||||
'Reflect.construct',
|
||||
'Reflect.defineProperty',
|
||||
'Reflect.deleteProperty',
|
||||
'Reflect.get',
|
||||
'Reflect.getOwnPropertyDescriptor',
|
||||
'Reflect.getPrototypeOf',
|
||||
'Reflect.has',
|
||||
'Reflect.isExtensible',
|
||||
'Reflect.ownKeys',
|
||||
'Reflect.preventExtensions',
|
||||
'Reflect.set',
|
||||
'Reflect.setPrototypeOf',
|
||||
'Set',
|
||||
'String.fromCodePoint',
|
||||
'String.prototype.codePointAt',
|
||||
'String.prototype.endsWith',
|
||||
'String.prototype.includes',
|
||||
'String.prototype.padEnd',
|
||||
'String.prototype.padStart',
|
||||
'String.prototype.repeat',
|
||||
'String.prototype.startsWith',
|
||||
'String.prototype.trim',
|
||||
'String.raw',
|
||||
'Uint16Array',
|
||||
'Uint32Array',
|
||||
'Uint8Array',
|
||||
'Uint8ClampedArray',
|
||||
'WeakMap',
|
||||
'WeakSet',
|
||||
].map(polyfillName => {
|
||||
const parts = polyfillName.split('.');
|
||||
const object = parts.length > 1 ? parts.slice(0, parts.length - 1).join('.') : null;
|
||||
const property = parts[parts.length - 1];
|
||||
return {
|
||||
name: polyfillName,
|
||||
expression: this.buildPolyfillExpression(object, property),
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {Pattern[]}
|
||||
*/
|
||||
static getTransformPatterns() {
|
||||
return [
|
||||
{
|
||||
name: '@babel/plugin-transform-classes',
|
||||
expression: 'Cannot call a class as a function',
|
||||
},
|
||||
{
|
||||
name: '@babel/plugin-transform-regenerator',
|
||||
expression: /regeneratorRuntime\.a?wrap/.source,
|
||||
},
|
||||
{
|
||||
name: '@babel/plugin-transform-spread',
|
||||
expression: /\.apply\(void 0,\s?_toConsumableArray/.source,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a collection of match results grouped by script url and with a mapping
|
||||
* to determine the order in which the matches were discovered.
|
||||
*
|
||||
* @param {CodePatternMatcher} matcher
|
||||
* @param {LH.GathererArtifacts['ScriptElements']} scripts
|
||||
* @param {LH.Artifacts.NetworkRequest[]} networkRecords
|
||||
* @return {Map<string, PatternMatchResult[]>}
|
||||
*/
|
||||
static detectCodePatternsAcrossScripts(matcher, scripts, networkRecords) {
|
||||
/** @type {Map<string, PatternMatchResult[]>} */
|
||||
const urlToMatchResults = new Map();
|
||||
|
||||
for (const {requestId, content} of Object.values(scripts)) {
|
||||
if (!content) continue;
|
||||
const networkRecord = networkRecords.find(record => record.requestId === requestId);
|
||||
if (!networkRecord) continue;
|
||||
const matches = matcher.match(content);
|
||||
if (!matches.length) continue;
|
||||
urlToMatchResults.set(networkRecord.url, matches);
|
||||
}
|
||||
|
||||
return urlToMatchResults;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {LH.Artifacts} artifacts
|
||||
* @param {LH.Audit.Context} context
|
||||
* @return {Promise<LH.Audit.Product>}
|
||||
*/
|
||||
static async audit(artifacts, context) {
|
||||
const devtoolsLog = artifacts.devtoolsLogs[LegacyJavascript.DEFAULT_PASS];
|
||||
const networkRecords = await NetworkRecords.request(devtoolsLog, context);
|
||||
const mainResource = await MainResource.request({
|
||||
URL: artifacts.URL,
|
||||
devtoolsLog,
|
||||
}, context);
|
||||
|
||||
/** @type {Array<{url: string, signals: string[], locations: LH.Audit.Details.SourceLocationValue[]}>} */
|
||||
const tableRows = [];
|
||||
let signalCount = 0;
|
||||
|
||||
// TODO(cjamcl): Use SourceMaps, and only pattern match if maps are not available.
|
||||
const matcher = new CodePatternMatcher([
|
||||
...this.getPolyfillPatterns(),
|
||||
...this.getTransformPatterns(),
|
||||
]);
|
||||
|
||||
const urlToMatchResults =
|
||||
this.detectCodePatternsAcrossScripts(matcher, artifacts.ScriptElements, networkRecords);
|
||||
urlToMatchResults.forEach((matches, url) => {
|
||||
/** @type {typeof tableRows[number]} */
|
||||
const row = {url, signals: [], locations: []};
|
||||
for (const match of matches) {
|
||||
const {name, line, column} = match;
|
||||
row.signals.push(name);
|
||||
row.locations.push({
|
||||
type: 'source-location',
|
||||
url,
|
||||
line,
|
||||
column,
|
||||
urlProvider: 'network',
|
||||
});
|
||||
}
|
||||
tableRows.push(row);
|
||||
signalCount += row.signals.length;
|
||||
});
|
||||
|
||||
/** @type {LH.Audit.Details.Table['headings']} */
|
||||
const headings = [
|
||||
/* eslint-disable max-len */
|
||||
{key: 'url', itemType: 'url', subRows: {key: 'locations', itemType: 'source-location'}, text: str_(i18n.UIStrings.columnURL)},
|
||||
{key: null, itemType: 'code', subRows: {key: 'signals'}, text: ''},
|
||||
/* eslint-enable max-len */
|
||||
];
|
||||
const details = Audit.makeTableDetails(headings, tableRows);
|
||||
|
||||
// Only fail if first party code has legacy code.
|
||||
// TODO(cjamcl): Use third-party-web.
|
||||
const foundSignalInFirstPartyCode = tableRows.some(row => {
|
||||
return URL.rootDomainsMatch(row.url, mainResource.url);
|
||||
});
|
||||
return {
|
||||
score: foundSignalInFirstPartyCode ? 0 : 1,
|
||||
notApplicable: !foundSignalInFirstPartyCode,
|
||||
extendedInfo: {
|
||||
signalCount,
|
||||
},
|
||||
details,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = LegacyJavascript;
|
||||
module.exports.UIStrings = UIStrings;
|
|
@ -20,16 +20,14 @@ const config = {
|
|||
],
|
||||
}],
|
||||
audits: [
|
||||
// About to be added.
|
||||
// 'bundle-duplication',
|
||||
'legacy-javascript',
|
||||
],
|
||||
// @ts-ignore: `title` is required in CategoryJson. setting to the same value as the default
|
||||
// config is awkward - easier to omit the property here. Will defer to default config.
|
||||
categories: {
|
||||
'performance': {
|
||||
auditRefs: [
|
||||
// About to be added.
|
||||
// {id: 'bundle-duplication', weight: 0, group: 'load-opportunities'},
|
||||
{id: 'legacy-javascript', weight: 0, group: 'diagnostics'},
|
||||
],
|
||||
},
|
||||
},
|
||||
|
|
|
@ -788,6 +788,12 @@
|
|||
"lighthouse-core/audits/is-on-https.js | title": {
|
||||
"message": "Uses HTTPS"
|
||||
},
|
||||
"lighthouse-core/audits/legacy-javascript.js | description": {
|
||||
"message": "Polyfills and transforms enable legacy browsers to use new JavaScript features. However, many aren't necessary for modern browsers. For your bundled JavaScript, adopt a modern script deployment strategy using module/nomodule feature detection to reduce the amount of code shipped to modern browsers, while retaining support for legacy browsers. [Learn More](https://philipwalton.com/articles/deploying-es2015-code-in-production-today/)"
|
||||
},
|
||||
"lighthouse-core/audits/legacy-javascript.js | title": {
|
||||
"message": "Legacy JavaScript"
|
||||
},
|
||||
"lighthouse-core/audits/load-fast-enough-for-pwa.js | description": {
|
||||
"message": "A fast page load over a cellular network ensures a good mobile user experience. [Learn more](https://web.dev/load-fast-enough-for-pwa)."
|
||||
},
|
||||
|
|
|
@ -788,6 +788,12 @@
|
|||
"lighthouse-core/audits/is-on-https.js | title": {
|
||||
"message": "Ûśêś ĤT́T̂ṔŜ"
|
||||
},
|
||||
"lighthouse-core/audits/legacy-javascript.js | description": {
|
||||
"message": "P̂ól̂ýf̂íl̂ĺŝ án̂d́ t̂ŕâńŝf́ôŕm̂ś êńâb́l̂é l̂éĝáĉý b̂ŕôẃŝér̂ś t̂ó ûśê ńêẃ Ĵáv̂áŜćr̂íp̂t́ f̂éât́ûŕêś. Ĥóŵév̂ér̂, ḿâńŷ ár̂én̂'t́ n̂éĉéŝśâŕŷ f́ôŕ m̂ód̂ér̂ń b̂ŕôẃŝér̂ś. F̂ór̂ ýôúr̂ b́ûńd̂ĺêd́ Ĵáv̂áŜćr̂íp̂t́, âd́ôṕt̂ á m̂ód̂ér̂ń ŝćr̂íp̂t́ d̂ép̂ĺôým̂én̂t́ ŝt́r̂át̂éĝý ûśîńĝ ḿôd́ûĺê/ńôḿôd́ûĺê f́êát̂úr̂é d̂ét̂éĉt́îón̂ t́ô ŕêd́ûćê t́ĥé âḿôún̂t́ ôf́ ĉód̂é ŝh́îṕp̂éd̂ t́ô ḿôd́êŕn̂ b́r̂óŵśêŕŝ, ẃĥíl̂é r̂ét̂áîńîńĝ śûṕp̂ór̂t́ f̂ór̂ ĺêǵâćŷ b́r̂óŵśêŕŝ. [Ĺêár̂ń M̂ór̂é](https://philipwalton.com/articles/deploying-es2015-code-in-production-today/)"
|
||||
},
|
||||
"lighthouse-core/audits/legacy-javascript.js | title": {
|
||||
"message": "L̂éĝáĉý Ĵáv̂áŜćr̂íp̂t́"
|
||||
},
|
||||
"lighthouse-core/audits/load-fast-enough-for-pwa.js | description": {
|
||||
"message": "Â f́âśt̂ ṕâǵê ĺôád̂ óv̂ér̂ á ĉél̂ĺûĺâŕ n̂ét̂ẃôŕk̂ én̂śûŕêś â ǵôód̂ ḿôb́îĺê úŝér̂ éx̂ṕêŕîén̂ćê. [Ĺêár̂ń m̂ór̂é](https://web.dev/load-fast-enough-for-pwa)."
|
||||
},
|
||||
|
|
|
@ -319,6 +319,11 @@ class DetailsRenderer {
|
|||
if (heading.subRows) {
|
||||
// @ts-ignore: It's ok that there is no text.
|
||||
subRows = this._getCanonicalizedHeading(heading.subRows);
|
||||
if (!subRows.key) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn('key should not be null');
|
||||
}
|
||||
subRows = {...subRows, key: subRows.key || ''};
|
||||
}
|
||||
|
||||
return {
|
||||
|
@ -374,10 +379,21 @@ class DetailsRenderer {
|
|||
for (const heading of headings) {
|
||||
const valueFragment = this._dom.createFragment();
|
||||
|
||||
const value = row[heading.key];
|
||||
const valueElement =
|
||||
value !== undefined && !Array.isArray(value) && this._renderTableValue(value, heading);
|
||||
if (valueElement) valueFragment.appendChild(valueElement);
|
||||
if (heading.key === null && !heading.subRows) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn('A header with a null `key` should define `subRows`.');
|
||||
}
|
||||
|
||||
if (heading.key === null) {
|
||||
const emptyElement = this._dom.createElement('div');
|
||||
emptyElement.innerHTML = ' ';
|
||||
valueFragment.appendChild(emptyElement);
|
||||
} else {
|
||||
const value = row[heading.key];
|
||||
const valueElement =
|
||||
value !== undefined && !Array.isArray(value) && this._renderTableValue(value, heading);
|
||||
if (valueElement) valueFragment.appendChild(valueElement);
|
||||
}
|
||||
|
||||
if (heading.subRows) {
|
||||
const subRowsHeading = {
|
||||
|
|
|
@ -216,6 +216,11 @@ class ReportUIFeatures {
|
|||
// This audit deals explicitly with third party resources.
|
||||
'uses-rel-preconnect',
|
||||
];
|
||||
// Some audits should hide third party by default.
|
||||
const thirdPartyFilterAuditHideByDefault = [
|
||||
// Only first party resources are actionable.
|
||||
'legacy-javascript',
|
||||
];
|
||||
|
||||
// Get all tables with a text url column.
|
||||
/** @type {Array<HTMLTableElement>} */
|
||||
|
@ -230,8 +235,8 @@ class ReportUIFeatures {
|
|||
});
|
||||
|
||||
tablesWithUrls.forEach((tableEl, index) => {
|
||||
const urlItems = this._getUrlItems(tableEl);
|
||||
const thirdPartyRows = this._getThirdPartyRows(tableEl, urlItems, this.json.finalUrl);
|
||||
const rowEls = getTableRows(tableEl);
|
||||
const thirdPartyRows = this._getThirdPartyRows(rowEls, this.json.finalUrl);
|
||||
|
||||
// create input box
|
||||
const filterTemplate = this._dom.cloneTemplate('#tmpl-lh-3p-filter', this._templateContext);
|
||||
|
@ -262,43 +267,52 @@ class ReportUIFeatures {
|
|||
this._dom.find('.lh-3p-ui-string', filterTemplate).textContent =
|
||||
Util.i18n.strings.thirdPartyResourcesLabel;
|
||||
|
||||
const allThirdParty = thirdPartyRows.size === rowEls.length;
|
||||
const allFirstParty = !thirdPartyRows.size;
|
||||
|
||||
// If all or none of the rows are 3rd party, disable the checkbox.
|
||||
if (thirdPartyRows.size === urlItems.length || !thirdPartyRows.size) {
|
||||
if (allThirdParty || allFirstParty) {
|
||||
filterInput.disabled = true;
|
||||
filterInput.checked = thirdPartyRows.size === urlItems.length;
|
||||
filterInput.checked = allThirdParty;
|
||||
}
|
||||
|
||||
// Finally, add checkbox to the DOM.
|
||||
// Add checkbox to the DOM.
|
||||
if (!tableEl.parentNode) return; // Keep tsc happy.
|
||||
tableEl.parentNode.insertBefore(filterTemplate, tableEl);
|
||||
|
||||
// Hide third-party rows for some audits by default.
|
||||
const containingAudit = tableEl.closest('.lh-audit');
|
||||
if (!containingAudit) throw new Error('.lh-table not within audit');
|
||||
if (thirdPartyFilterAuditHideByDefault.includes(containingAudit.id) && !allThirdParty) {
|
||||
filterInput.click();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* From a table with URL entries, finds the rows containing third-party URLs
|
||||
* and returns a Map of those rows, mapping from row index to row Element.
|
||||
* @param {HTMLTableElement} el
|
||||
* @param {HTMLElement[]} rowEls
|
||||
* @param {string} finalUrl
|
||||
* @param {Array<HTMLElement>} urlItems
|
||||
* @return {Map<number, HTMLTableRowElement>}
|
||||
* @return {Map<number, HTMLElement>}
|
||||
*/
|
||||
_getThirdPartyRows(el, urlItems, finalUrl) {
|
||||
_getThirdPartyRows(rowEls, finalUrl) {
|
||||
/** @type {Map<number, HTMLElement>} */
|
||||
const thirdPartyRows = new Map();
|
||||
const finalUrlRootDomain = Util.getRootDomain(finalUrl);
|
||||
|
||||
/** @type {Map<number, HTMLTableRowElement>} */
|
||||
const thirdPartyRows = new Map();
|
||||
for (const urlItem of urlItems) {
|
||||
const datasetUrl = urlItem.dataset.url;
|
||||
if (!datasetUrl) continue;
|
||||
const isThirdParty = Util.getRootDomain(datasetUrl) !== finalUrlRootDomain;
|
||||
if (!isThirdParty) continue;
|
||||
rowEls.forEach((rowEl, rowPosition) => {
|
||||
/** @type {HTMLElement|null} */
|
||||
const urlItem = rowEl.querySelector('.lh-text__url');
|
||||
if (!urlItem) return;
|
||||
|
||||
const urlRowEl = urlItem.closest('tr');
|
||||
if (urlRowEl) {
|
||||
const rowPosition = getTableRows(el).indexOf(urlRowEl);
|
||||
thirdPartyRows.set(rowPosition, urlRowEl);
|
||||
}
|
||||
}
|
||||
const datasetUrl = urlItem.dataset.url;
|
||||
if (!datasetUrl) return;
|
||||
const isThirdParty = Util.getRootDomain(datasetUrl) !== finalUrlRootDomain;
|
||||
if (!isThirdParty) return;
|
||||
|
||||
thirdPartyRows.set(Number(rowPosition), rowEl);
|
||||
});
|
||||
|
||||
return thirdPartyRows;
|
||||
}
|
||||
|
|
|
@ -1410,6 +1410,9 @@
|
|||
margin-left: 20px;
|
||||
color: var(--color-gray-700);
|
||||
}
|
||||
.lh-sub-row {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
/* Chevron
|
||||
https://codepen.io/paulirish/pen/LmzEmK
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
# `legacy-javascript` Validation Tool
|
||||
|
||||
Run:
|
||||
|
||||
```sh
|
||||
yarn install
|
||||
node run.js
|
||||
# STAGE=build|audit|all to just build the audits or run LegacyJavascript on them. Defaults to both (`all`).
|
||||
```
|
||||
|
||||
`summary-signals.json` - summarizes the signals that LegacyJavascript finds for each variant. Variants in `variantsMissingSignals` (excluding `core-js-3-preset-env-esmodules/true`) signify a lack of detection for that variant. Full coverage isn't necessary.
|
||||
|
||||
`summary-sizes.json` - lists the size of each minified variant. Useful for understanding how many bytes each polyfill / transform adds.
|
||||
|
||||
## Future Work
|
||||
|
||||
* Use real apps to see how over-transpiling affects real code. Necessary for making an opprotunity.
|
||||
|
||||
## Notes
|
||||
|
||||
Digging into core-js: https://gist.github.com/connorjclark/cc583554ff07cba7cdc416c06721fd6a
|
|
@ -0,0 +1,103 @@
|
|||
/**
|
||||
* @license Copyright 2020 Google Inc. All Rights Reserved.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
/* eslint-disable no-console */
|
||||
|
||||
/**
|
||||
* @fileoverview - Used to manually examine the polyfills/transforms used on a page.
|
||||
*
|
||||
* USAGE:
|
||||
* 1. Run `yarn start <url to examine> -G
|
||||
* 2. Run `node ./lighthouse-core/scripts/legacy-javascript/examine-latest-run.js`
|
||||
* 3. Inspect output for fishy looking polyfills.
|
||||
*/
|
||||
|
||||
const path = require('path');
|
||||
// @ts-ignore - We don't really need types for this
|
||||
const colors = require('colors');
|
||||
const LegacyJavascript = require('../../audits/legacy-javascript.js');
|
||||
|
||||
const LH_ROOT_DIR = path.join(__dirname, '../../../');
|
||||
const LATEST_RUN_DIR = path.join(LH_ROOT_DIR, 'latest-run');
|
||||
|
||||
/** @param {LH.DevtoolsLog} log */
|
||||
function requestUrlToId(log) {
|
||||
return log.reduce(
|
||||
(map, entry) => {
|
||||
if (entry.method === 'Network.requestWillBeSent') {
|
||||
map[entry.params.request.url] = entry.params.requestId;
|
||||
}
|
||||
return map;
|
||||
},
|
||||
/** @type {Record<string, string>} */ ({})
|
||||
);
|
||||
}
|
||||
|
||||
async function main() {
|
||||
/** @type {LH.Artifacts} */
|
||||
const artifacts = require(`${LATEST_RUN_DIR}/artifacts.json`);
|
||||
const devtoolsLog = require(`${LATEST_RUN_DIR}/defaultPass.devtoolslog.json`);
|
||||
const scripts = artifacts.ScriptElements;
|
||||
const requestUrlMap = requestUrlToId(devtoolsLog);
|
||||
artifacts.devtoolsLogs = {defaultPass: devtoolsLog};
|
||||
|
||||
const auditResults = await LegacyJavascript.audit(artifacts, {
|
||||
computedCache: new Map(),
|
||||
options: {},
|
||||
settings: /** @type {any} */ ({}),
|
||||
LighthouseRunWarnings: [],
|
||||
});
|
||||
|
||||
const items =
|
||||
auditResults.details &&
|
||||
auditResults.details.type === 'table' &&
|
||||
auditResults.details.items;
|
||||
|
||||
if (!items) {
|
||||
console.log('No signals found!');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(colors.bold(`${items.length} signals found!`));
|
||||
for (const item of items) {
|
||||
if (typeof item.url !== 'string') continue;
|
||||
const requestId = requestUrlMap[item.url];
|
||||
const script = scripts.find(s => s.requestId === requestId);
|
||||
const signals = Array.isArray(item.signals) ? item.signals : [];
|
||||
const locations = Array.isArray(item.locations) ? item.locations : [];
|
||||
|
||||
console.log('---------------------------------');
|
||||
console.log(`URL: ${item.url}`);
|
||||
console.log(`Signals: ${signals.length}`);
|
||||
if (!script || !script.content) {
|
||||
console.log('\nFailed to find script content! :/');
|
||||
console.log('---------------------------------\n\n');
|
||||
continue;
|
||||
}
|
||||
|
||||
const lines = script.content.split('\n');
|
||||
for (let i = 0; i < signals.length; i++) {
|
||||
const signal = signals[i];
|
||||
const location = locations[i];
|
||||
if (typeof location !== 'object' || location.type !== 'source-location') {
|
||||
continue;
|
||||
}
|
||||
|
||||
const line = lines[location.line || 0] || '';
|
||||
const locationString = `at ${location.line}:${location.column}`;
|
||||
console.log('');
|
||||
console.log(`${signal} ${colors.dim(locationString)}`);
|
||||
const contentToShow = line.slice(location.column - 10, location.column + 80);
|
||||
const unimportant = contentToShow.split(signal.toString());
|
||||
console.log(unimportant.map(s => colors.dim(s)).join(signal.toString()));
|
||||
}
|
||||
|
||||
console.log('---------------------------------\n\n');
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
|
@ -0,0 +1,9 @@
|
|||
// @ts-nocheck
|
||||
/* eslint-disable */
|
||||
|
||||
class MyTestClass {};
|
||||
async function reg(...args) {
|
||||
await 1;
|
||||
}
|
||||
const spread = [...[1,2,3], 3, 2, 1];
|
||||
reg(...spread);
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"name": "legacy-javascript",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@babel/cli": "^7.8.4",
|
||||
"@babel/core": "^7.8.4",
|
||||
"@babel/preset-env": "^7.8.4",
|
||||
"browserify": "^16.5.0",
|
||||
"glob": "^7.1.6",
|
||||
"terser": "^4.6.3"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,321 @@
|
|||
/**
|
||||
* @license Copyright 2020 Google Inc. All Rights Reserved.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
/* eslint-disable no-console */
|
||||
|
||||
const fs = require('fs');
|
||||
const glob = require('glob');
|
||||
const {execFileSync} = require('child_process');
|
||||
const LegacyJavascript = require('../../audits/legacy-javascript.js');
|
||||
const networkRecordsToDevtoolsLog = require('../../test/network-records-to-devtools-log.js');
|
||||
const VARIANT_DIR = `${__dirname}/variants`;
|
||||
|
||||
// build, audit, all.
|
||||
const STAGE = process.env.STAGE || 'all';
|
||||
|
||||
const mainCode = fs.readFileSync(`${__dirname}/main.js`, 'utf-8');
|
||||
|
||||
const plugins = LegacyJavascript.getTransformPatterns().map(pattern => pattern.name);
|
||||
|
||||
// TODO(cjamcl): Get this from `LegacyJavascript`. Data isn't structured ideally yet, but should be
|
||||
// done when SourceMap support is in.
|
||||
const polyfills = [
|
||||
'es6.array.copy-within',
|
||||
'es6.array.every',
|
||||
'es6.array.fill',
|
||||
'es6.array.filter',
|
||||
'es6.array.find-index',
|
||||
'es6.array.find',
|
||||
'es6.array.for-each',
|
||||
'es6.array.from',
|
||||
'es6.array.index-of',
|
||||
'es6.array.is-array',
|
||||
'es6.array.iterator',
|
||||
'es6.array.last-index-of',
|
||||
'es6.array.map',
|
||||
'es6.array.of',
|
||||
'es6.array.reduce-right',
|
||||
'es6.array.reduce',
|
||||
'es6.array.some',
|
||||
'es6.array.species',
|
||||
'es6.date.now',
|
||||
'es6.date.to-iso-string',
|
||||
'es6.date.to-json',
|
||||
'es6.date.to-primitive',
|
||||
'es6.date.to-string',
|
||||
'es6.function.bind',
|
||||
'es6.function.has-instance',
|
||||
'es6.function.name',
|
||||
'es6.map',
|
||||
'es6.math.acosh',
|
||||
'es6.math.asinh',
|
||||
'es6.math.atanh',
|
||||
'es6.math.cbrt',
|
||||
'es6.math.clz32',
|
||||
'es6.math.cosh',
|
||||
'es6.math.expm1',
|
||||
'es6.math.fround',
|
||||
'es6.math.hypot',
|
||||
'es6.math.imul',
|
||||
'es6.math.log10',
|
||||
'es6.math.log1p',
|
||||
'es6.math.log2',
|
||||
'es6.math.sign',
|
||||
'es6.math.sinh',
|
||||
'es6.math.tanh',
|
||||
'es6.math.trunc',
|
||||
'es6.number.constructor',
|
||||
'es6.number.epsilon',
|
||||
'es6.number.is-integer',
|
||||
'es6.number.is-safe-integer',
|
||||
'es6.number.max-safe-integer',
|
||||
'es6.number.min-safe-integer',
|
||||
'es6.number.parse-float',
|
||||
'es6.number.parse-int',
|
||||
'es6.object.assign',
|
||||
'es6.object.create',
|
||||
'es6.object.define-properties',
|
||||
'es6.object.define-property',
|
||||
'es6.object.freeze',
|
||||
'es6.object.get-own-property-descriptor',
|
||||
'es6.object.get-own-property-names',
|
||||
'es6.object.get-prototype-of',
|
||||
'es6.object.is-extensible',
|
||||
'es6.object.is-frozen',
|
||||
'es6.object.is-sealed',
|
||||
'es6.object.keys',
|
||||
'es6.object.prevent-extensions',
|
||||
'es6.object.seal',
|
||||
'es6.object.set-prototype-of',
|
||||
'es6.object.to-string',
|
||||
'es6.promise',
|
||||
'es6.reflect.apply',
|
||||
'es6.reflect.construct',
|
||||
'es6.reflect.define-property',
|
||||
'es6.reflect.delete-property',
|
||||
'es6.reflect.get-own-property-descriptor',
|
||||
'es6.reflect.get-prototype-of',
|
||||
'es6.reflect.get',
|
||||
'es6.reflect.has',
|
||||
'es6.reflect.is-extensible',
|
||||
'es6.reflect.own-keys',
|
||||
'es6.reflect.prevent-extensions',
|
||||
'es6.reflect.set-prototype-of',
|
||||
'es6.reflect.set',
|
||||
'es6.set',
|
||||
'es6.string.code-point-at',
|
||||
'es6.string.ends-with',
|
||||
'es6.string.from-code-point',
|
||||
'es6.string.includes',
|
||||
'es6.string.iterator',
|
||||
'es6.string.raw',
|
||||
'es6.string.repeat',
|
||||
'es6.string.starts-with',
|
||||
'es6.string.trim',
|
||||
'es6.typed.array-buffer',
|
||||
'es6.typed.data-view',
|
||||
'es6.typed.float32-array',
|
||||
'es6.typed.float64-array',
|
||||
'es6.typed.int16-array',
|
||||
'es6.typed.int32-array',
|
||||
'es6.typed.int8-array',
|
||||
'es6.typed.uint16-array',
|
||||
'es6.typed.uint32-array',
|
||||
'es6.typed.uint8-array',
|
||||
'es6.typed.uint8-clamped-array',
|
||||
'es6.weak-map',
|
||||
'es6.weak-set',
|
||||
'es7.array.includes',
|
||||
'es7.object.entries',
|
||||
'es7.object.get-own-property-descriptors',
|
||||
'es7.object.values',
|
||||
'es7.string.pad-end',
|
||||
'es7.string.pad-start',
|
||||
];
|
||||
|
||||
/**
|
||||
* @param {string} command
|
||||
* @param {string[]} args
|
||||
*/
|
||||
function runCommand(command, args) {
|
||||
execFileSync(command, args, {cwd: __dirname});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number} version
|
||||
*/
|
||||
function installCoreJs(version) {
|
||||
runCommand('yarn', [
|
||||
'add',
|
||||
`core-js@${version}`,
|
||||
]);
|
||||
}
|
||||
|
||||
function removeCoreJs() {
|
||||
try {
|
||||
runCommand('yarn', [
|
||||
'remove',
|
||||
'core-js',
|
||||
]);
|
||||
} catch (e) { }
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {{group: string, name: string, code: string, babelrc?: *}} options
|
||||
*/
|
||||
async function createVariant(options) {
|
||||
const {group, name, code, babelrc} = options;
|
||||
const dir = `${VARIANT_DIR}/${group}/${name.replace(/[^a-zA-Z0-9]+/g, '-')}`;
|
||||
|
||||
if (!fs.existsSync(`${dir}/main.bundle.js`) && (STAGE === 'build' || STAGE === 'all')) {
|
||||
fs.mkdirSync(dir, {recursive: true});
|
||||
fs.writeFileSync(`${dir}/main.js`, code);
|
||||
fs.writeFileSync(`${dir}/.babelrc`, JSON.stringify(babelrc || {}, null, 2));
|
||||
// Not used in this script, but useful for running Lighthouse manually.
|
||||
// Just need to start a web server first.
|
||||
fs.writeFileSync(`${dir}/index.html`, `<title>${name}</title><script src=main.bundle.min.js>`);
|
||||
|
||||
// Note: No babelrc will make babel a glorified `cp`.
|
||||
runCommand('yarn', [
|
||||
'babel',
|
||||
`${dir}/main.js`,
|
||||
'--config-file', `${dir}/.babelrc`,
|
||||
'--ignore', 'node_modules/**/*.js',
|
||||
'-o', `${dir}/main.transpiled.js`,
|
||||
]);
|
||||
|
||||
// Transform any require statements (like for core-js) into a big bundle.
|
||||
runCommand('yarn', [
|
||||
'browserify',
|
||||
`${dir}/main.transpiled.js`,
|
||||
'-o', `${dir}/main.bundle.js`,
|
||||
]);
|
||||
|
||||
// Minify.
|
||||
runCommand('yarn', [
|
||||
'terser',
|
||||
`${dir}/main.bundle.js`,
|
||||
'-o', `${dir}/main.bundle.min.js`,
|
||||
]);
|
||||
}
|
||||
|
||||
if (STAGE === 'audit' || STAGE === 'all') {
|
||||
// Instead of running Lighthouse, use LegacyJavascript directly. Requires some setup.
|
||||
// Much faster than running Lighthouse.
|
||||
const documentUrl = 'http://localhost/index.html'; // These URLs don't matter.
|
||||
const scriptUrl = 'https://localhost/main.bundle.min.js';
|
||||
const networkRecords = [
|
||||
{url: documentUrl},
|
||||
{url: scriptUrl},
|
||||
];
|
||||
const devtoolsLogs = networkRecordsToDevtoolsLog(networkRecords);
|
||||
const jsRequestWillBeSentEvent = devtoolsLogs.find(e =>
|
||||
e.method === 'Network.requestWillBeSent' && e.params.request.url === scriptUrl);
|
||||
if (!jsRequestWillBeSentEvent) throw new Error('jsRequestWillBeSentEvent is undefined');
|
||||
// @ts-ignore - the log event is not narrowed to 'Network.requestWillBeSent' event from find
|
||||
const jsRequestId = jsRequestWillBeSentEvent.params.requestId;
|
||||
const code = fs.readFileSync(`${dir}/main.bundle.min.js`, 'utf-8').toString();
|
||||
/** @type {Pick<LH.Artifacts, 'devtoolsLogs'|'URL'|'ScriptElements'>} */
|
||||
const artifacts = {
|
||||
URL: {finalUrl: documentUrl, requestedUrl: documentUrl},
|
||||
devtoolsLogs: {
|
||||
[LegacyJavascript.DEFAULT_PASS]: devtoolsLogs,
|
||||
},
|
||||
ScriptElements: [
|
||||
// @ts-ignore - partial ScriptElement excluding unused DOM properties
|
||||
{requestId: jsRequestId, content: code},
|
||||
],
|
||||
};
|
||||
// @ts-ignore: partial Artifacts.
|
||||
const legacyJavascriptResults = await LegacyJavascript.audit(artifacts, {
|
||||
computedCache: new Map(),
|
||||
});
|
||||
fs.writeFileSync(`${dir}/legacy-javascript.json`,
|
||||
JSON.stringify(legacyJavascriptResults.details.items, null, 2));
|
||||
}
|
||||
}
|
||||
|
||||
function makeSummary() {
|
||||
let totalSignals = 0;
|
||||
const variants = [];
|
||||
for (const dir of glob.sync('*/*', {cwd: VARIANT_DIR})) {
|
||||
/** @type {Array<{signals: string[]}>} */
|
||||
const legacyJavascriptItems = require(`${VARIANT_DIR}/${dir}/legacy-javascript.json`);
|
||||
const signals = legacyJavascriptItems.reduce((acc, cur) => {
|
||||
totalSignals += cur.signals.length;
|
||||
return acc.concat(cur.signals);
|
||||
}, /** @type {string[]} */ ([])).join(', ');
|
||||
variants.push({name: dir, signals});
|
||||
}
|
||||
return {
|
||||
totalSignals,
|
||||
variantsMissingSignals: variants.filter(v => !v.signals).map(v => v.name),
|
||||
variants,
|
||||
};
|
||||
}
|
||||
|
||||
async function main() {
|
||||
for (const plugin of plugins) {
|
||||
await createVariant({
|
||||
group: 'only-plugin',
|
||||
name: plugin,
|
||||
code: mainCode,
|
||||
babelrc: {
|
||||
plugins: [plugin],
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
for (const coreJsVersion of [2, 3]) {
|
||||
removeCoreJs();
|
||||
installCoreJs(coreJsVersion);
|
||||
|
||||
for (const esmodules of [true, false]) {
|
||||
await createVariant({
|
||||
group: `core-js-${coreJsVersion}-preset-env-esmodules`,
|
||||
name: String(esmodules),
|
||||
code: mainCode,
|
||||
babelrc: {
|
||||
presets: [
|
||||
[
|
||||
'@babel/preset-env',
|
||||
{
|
||||
targets: {esmodules},
|
||||
useBuiltIns: 'entry',
|
||||
corejs: coreJsVersion,
|
||||
},
|
||||
],
|
||||
],
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
for (const polyfill of polyfills) {
|
||||
await createVariant({
|
||||
group: `core-js-${coreJsVersion}-only-polyfill`,
|
||||
name: polyfill,
|
||||
code: `require("core-js/modules/${polyfill}")`,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
removeCoreJs();
|
||||
|
||||
const summary = makeSummary();
|
||||
fs.writeFileSync(`${__dirname}/summary-signals.json`, JSON.stringify(summary, null, 2));
|
||||
console.log({
|
||||
totalSignals: summary.totalSignals,
|
||||
variantsMissingSignals: summary.variantsMissingSignals,
|
||||
});
|
||||
console.table(summary.variants);
|
||||
|
||||
runCommand('sh', [
|
||||
'update-sizes.sh',
|
||||
]);
|
||||
}
|
||||
|
||||
main();
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,244 @@
|
|||
variants/core-js-2-only-polyfill
|
||||
1482778 total
|
||||
47738 variants/core-js-2-only-polyfill/es6-typed-uint8-clamped-array/main.bundle.min.js
|
||||
47718 variants/core-js-2-only-polyfill/es6-typed-float64-array/main.bundle.min.js
|
||||
47718 variants/core-js-2-only-polyfill/es6-typed-float32-array/main.bundle.min.js
|
||||
47714 variants/core-js-2-only-polyfill/es6-typed-uint32-array/main.bundle.min.js
|
||||
47714 variants/core-js-2-only-polyfill/es6-typed-uint16-array/main.bundle.min.js
|
||||
47710 variants/core-js-2-only-polyfill/es6-typed-uint8-array/main.bundle.min.js
|
||||
47710 variants/core-js-2-only-polyfill/es6-typed-int32-array/main.bundle.min.js
|
||||
47710 variants/core-js-2-only-polyfill/es6-typed-int16-array/main.bundle.min.js
|
||||
47706 variants/core-js-2-only-polyfill/es6-typed-int8-array/main.bundle.min.js
|
||||
29514 variants/core-js-2-only-polyfill/es6-map/main.bundle.min.js
|
||||
29420 variants/core-js-2-only-polyfill/es6-set/main.bundle.min.js
|
||||
27277 variants/core-js-2-only-polyfill/es6-weak-map/main.bundle.min.js
|
||||
24645 variants/core-js-2-only-polyfill/es6-promise/main.bundle.min.js
|
||||
23127 variants/core-js-2-only-polyfill/es6-typed-array-buffer/main.bundle.min.js
|
||||
22881 variants/core-js-2-only-polyfill/es6-weak-set/main.bundle.min.js
|
||||
20885 variants/core-js-2-only-polyfill/es6-typed-data-view/main.bundle.min.js
|
||||
17640 variants/core-js-2-only-polyfill/es6-array-iterator/main.bundle.min.js
|
||||
17387 variants/core-js-2-only-polyfill/es6-number-constructor/main.bundle.min.js
|
||||
17329 variants/core-js-2-only-polyfill/es6-string-iterator/main.bundle.min.js
|
||||
14880 variants/core-js-2-only-polyfill/es6-reflect-construct/main.bundle.min.js
|
||||
12738 variants/core-js-2-only-polyfill/es7-object-get-own-property-descriptors/main.bundle.min.js
|
||||
12329 variants/core-js-2-only-polyfill/es6-object-create/main.bundle.min.js
|
||||
12184 variants/core-js-2-only-polyfill/es6-array-from/main.bundle.min.js
|
||||
11963 variants/core-js-2-only-polyfill/es6-object-assign/main.bundle.min.js
|
||||
11575 variants/core-js-2-only-polyfill/es6-object-get-own-property-names/main.bundle.min.js
|
||||
11412 variants/core-js-2-only-polyfill/es6-array-find-index/main.bundle.min.js
|
||||
11385 variants/core-js-2-only-polyfill/es6-array-find/main.bundle.min.js
|
||||
11295 variants/core-js-2-only-polyfill/es7-object-entries/main.bundle.min.js
|
||||
11290 variants/core-js-2-only-polyfill/es7-object-values/main.bundle.min.js
|
||||
11240 variants/core-js-2-only-polyfill/es6-array-for-each/main.bundle.min.js
|
||||
11213 variants/core-js-2-only-polyfill/es6-array-filter/main.bundle.min.js
|
||||
11206 variants/core-js-2-only-polyfill/es6-array-every/main.bundle.min.js
|
||||
11199 variants/core-js-2-only-polyfill/es6-array-some/main.bundle.min.js
|
||||
11192 variants/core-js-2-only-polyfill/es6-array-map/main.bundle.min.js
|
||||
11187 variants/core-js-2-only-polyfill/es6-object-keys/main.bundle.min.js
|
||||
11159 variants/core-js-2-only-polyfill/es6-reflect-own-keys/main.bundle.min.js
|
||||
11146 variants/core-js-2-only-polyfill/es6-object-define-properties/main.bundle.min.js
|
||||
10840 variants/core-js-2-only-polyfill/es6-reflect-set/main.bundle.min.js
|
||||
10362 variants/core-js-2-only-polyfill/es6-reflect-get/main.bundle.min.js
|
||||
10323 variants/core-js-2-only-polyfill/es7-array-includes/main.bundle.min.js
|
||||
9992 variants/core-js-2-only-polyfill/es6-array-index-of/main.bundle.min.js
|
||||
9974 variants/core-js-2-only-polyfill/es6-string-ends-with/main.bundle.min.js
|
||||
9928 variants/core-js-2-only-polyfill/es6-string-starts-with/main.bundle.min.js
|
||||
9916 variants/core-js-2-only-polyfill/es6-array-copy-within/main.bundle.min.js
|
||||
9840 variants/core-js-2-only-polyfill/es6-reflect-set-prototype-of/main.bundle.min.js
|
||||
9726 variants/core-js-2-only-polyfill/es6-array-fill/main.bundle.min.js
|
||||
9706 variants/core-js-2-only-polyfill/es6-array-reduce-right/main.bundle.min.js
|
||||
9682 variants/core-js-2-only-polyfill/es6-object-set-prototype-of/main.bundle.min.js
|
||||
9680 variants/core-js-2-only-polyfill/es6-array-reduce/main.bundle.min.js
|
||||
9561 variants/core-js-2-only-polyfill/es7-string-pad-start/main.bundle.min.js
|
||||
9554 variants/core-js-2-only-polyfill/es7-string-pad-end/main.bundle.min.js
|
||||
9532 variants/core-js-2-only-polyfill/es6-object-get-own-property-descriptor/main.bundle.min.js
|
||||
9458 variants/core-js-2-only-polyfill/es6-array-last-index-of/main.bundle.min.js
|
||||
9315 variants/core-js-2-only-polyfill/es6-string-includes/main.bundle.min.js
|
||||
9196 variants/core-js-2-only-polyfill/es6-reflect-delete-property/main.bundle.min.js
|
||||
9173 variants/core-js-2-only-polyfill/es6-reflect-get-own-property-descriptor/main.bundle.min.js
|
||||
9120 variants/core-js-2-only-polyfill/es6-object-prevent-extensions/main.bundle.min.js
|
||||
9093 variants/core-js-2-only-polyfill/es6-number-parse-int/main.bundle.min.js
|
||||
9087 variants/core-js-2-only-polyfill/es6-number-parse-float/main.bundle.min.js
|
||||
9041 variants/core-js-2-only-polyfill/es6-object-freeze/main.bundle.min.js
|
||||
9027 variants/core-js-2-only-polyfill/es6-object-seal/main.bundle.min.js
|
||||
9009 variants/core-js-2-only-polyfill/es6-object-get-prototype-of/main.bundle.min.js
|
||||
8843 variants/core-js-2-only-polyfill/es6-string-raw/main.bundle.min.js
|
||||
8792 variants/core-js-2-only-polyfill/es6-function-bind/main.bundle.min.js
|
||||
8667 variants/core-js-2-only-polyfill/es6-reflect-get-prototype-of/main.bundle.min.js
|
||||
8593 variants/core-js-2-only-polyfill/es6-string-trim/main.bundle.min.js
|
||||
8445 variants/core-js-2-only-polyfill/es6-string-from-code-point/main.bundle.min.js
|
||||
8423 variants/core-js-2-only-polyfill/es6-date-to-iso-string/main.bundle.min.js
|
||||
8413 variants/core-js-2-only-polyfill/es6-string-code-point-at/main.bundle.min.js
|
||||
8219 variants/core-js-2-only-polyfill/es6-math-fround/main.bundle.min.js
|
||||
8217 variants/core-js-2-only-polyfill/es6-string-repeat/main.bundle.min.js
|
||||
8193 variants/core-js-2-only-polyfill/es6-array-of/main.bundle.min.js
|
||||
8191 variants/core-js-2-only-polyfill/es6-date-to-json/main.bundle.min.js
|
||||
8004 variants/core-js-2-only-polyfill/es6-reflect-define-property/main.bundle.min.js
|
||||
7998 variants/core-js-2-only-polyfill/es6-object-is-extensible/main.bundle.min.js
|
||||
7984 variants/core-js-2-only-polyfill/es6-math-sinh/main.bundle.min.js
|
||||
7970 variants/core-js-2-only-polyfill/es6-object-is-sealed/main.bundle.min.js
|
||||
7970 variants/core-js-2-only-polyfill/es6-object-is-frozen/main.bundle.min.js
|
||||
7942 variants/core-js-2-only-polyfill/es6-reflect-apply/main.bundle.min.js
|
||||
7907 variants/core-js-2-only-polyfill/es6-math-tanh/main.bundle.min.js
|
||||
7906 variants/core-js-2-only-polyfill/es6-math-acosh/main.bundle.min.js
|
||||
7876 variants/core-js-2-only-polyfill/es6-number-is-safe-integer/main.bundle.min.js
|
||||
7821 variants/core-js-2-only-polyfill/es6-math-expm1/main.bundle.min.js
|
||||
7814 variants/core-js-2-only-polyfill/es6-array-is-array/main.bundle.min.js
|
||||
7786 variants/core-js-2-only-polyfill/es6-math-hypot/main.bundle.min.js
|
||||
7763 variants/core-js-2-only-polyfill/es6-reflect-prevent-extensions/main.bundle.min.js
|
||||
7752 variants/core-js-2-only-polyfill/es6-math-imul/main.bundle.min.js
|
||||
7743 variants/core-js-2-only-polyfill/es6-number-is-integer/main.bundle.min.js
|
||||
7702 variants/core-js-2-only-polyfill/es6-math-cbrt/main.bundle.min.js
|
||||
7693 variants/core-js-2-only-polyfill/es6-reflect-is-extensible/main.bundle.min.js
|
||||
7654 variants/core-js-2-only-polyfill/es6-math-log1p/main.bundle.min.js
|
||||
7631 variants/core-js-2-only-polyfill/es6-math-sign/main.bundle.min.js
|
||||
7628 variants/core-js-2-only-polyfill/es6-math-asinh/main.bundle.min.js
|
||||
7608 variants/core-js-2-only-polyfill/es6-object-define-property/main.bundle.min.js
|
||||
7592 variants/core-js-2-only-polyfill/es6-math-atanh/main.bundle.min.js
|
||||
7551 variants/core-js-2-only-polyfill/es6-math-clz32/main.bundle.min.js
|
||||
7539 variants/core-js-2-only-polyfill/es6-reflect-has/main.bundle.min.js
|
||||
7533 variants/core-js-2-only-polyfill/es6-math-cosh/main.bundle.min.js
|
||||
7531 variants/core-js-2-only-polyfill/es6-math-trunc/main.bundle.min.js
|
||||
7530 variants/core-js-2-only-polyfill/es6-number-min-safe-integer/main.bundle.min.js
|
||||
7529 variants/core-js-2-only-polyfill/es6-number-max-safe-integer/main.bundle.min.js
|
||||
7523 variants/core-js-2-only-polyfill/es6-math-log10/main.bundle.min.js
|
||||
7516 variants/core-js-2-only-polyfill/es6-math-log2/main.bundle.min.js
|
||||
7506 variants/core-js-2-only-polyfill/es6-date-now/main.bundle.min.js
|
||||
7501 variants/core-js-2-only-polyfill/es6-number-epsilon/main.bundle.min.js
|
||||
6915 variants/core-js-2-only-polyfill/es6-object-to-string/main.bundle.min.js
|
||||
5994 variants/core-js-2-only-polyfill/es6-function-has-instance/main.bundle.min.js
|
||||
5993 variants/core-js-2-only-polyfill/es6-date-to-string/main.bundle.min.js
|
||||
5422 variants/core-js-2-only-polyfill/es6-date-to-primitive/main.bundle.min.js
|
||||
4774 variants/core-js-2-only-polyfill/es6-array-species/main.bundle.min.js
|
||||
3410 variants/core-js-2-only-polyfill/es6-function-name/main.bundle.min.js
|
||||
|
||||
variants/core-js-2-preset-env-esmodules
|
||||
3567 total
|
||||
2259 variants/core-js-2-preset-env-esmodules/false/main.bundle.min.js
|
||||
1308 variants/core-js-2-preset-env-esmodules/true/main.bundle.min.js
|
||||
|
||||
variants/core-js-3-only-polyfill
|
||||
1482721 total
|
||||
47737 variants/core-js-3-only-polyfill/es6-typed-uint8-clamped-array/main.bundle.min.js
|
||||
47717 variants/core-js-3-only-polyfill/es6-typed-float64-array/main.bundle.min.js
|
||||
47717 variants/core-js-3-only-polyfill/es6-typed-float32-array/main.bundle.min.js
|
||||
47713 variants/core-js-3-only-polyfill/es6-typed-uint32-array/main.bundle.min.js
|
||||
47713 variants/core-js-3-only-polyfill/es6-typed-uint16-array/main.bundle.min.js
|
||||
47709 variants/core-js-3-only-polyfill/es6-typed-uint8-array/main.bundle.min.js
|
||||
47709 variants/core-js-3-only-polyfill/es6-typed-int32-array/main.bundle.min.js
|
||||
47709 variants/core-js-3-only-polyfill/es6-typed-int16-array/main.bundle.min.js
|
||||
47705 variants/core-js-3-only-polyfill/es6-typed-int8-array/main.bundle.min.js
|
||||
29516 variants/core-js-3-only-polyfill/es6-map/main.bundle.min.js
|
||||
29422 variants/core-js-3-only-polyfill/es6-set/main.bundle.min.js
|
||||
27278 variants/core-js-3-only-polyfill/es6-weak-map/main.bundle.min.js
|
||||
24645 variants/core-js-3-only-polyfill/es6-promise/main.bundle.min.js
|
||||
23127 variants/core-js-3-only-polyfill/es6-typed-array-buffer/main.bundle.min.js
|
||||
22880 variants/core-js-3-only-polyfill/es6-weak-set/main.bundle.min.js
|
||||
20885 variants/core-js-3-only-polyfill/es6-typed-data-view/main.bundle.min.js
|
||||
17642 variants/core-js-3-only-polyfill/es6-array-iterator/main.bundle.min.js
|
||||
17387 variants/core-js-3-only-polyfill/es6-number-constructor/main.bundle.min.js
|
||||
17329 variants/core-js-3-only-polyfill/es6-string-iterator/main.bundle.min.js
|
||||
14882 variants/core-js-3-only-polyfill/es6-reflect-construct/main.bundle.min.js
|
||||
12740 variants/core-js-3-only-polyfill/es7-object-get-own-property-descriptors/main.bundle.min.js
|
||||
12329 variants/core-js-3-only-polyfill/es6-object-create/main.bundle.min.js
|
||||
12185 variants/core-js-3-only-polyfill/es6-array-from/main.bundle.min.js
|
||||
11962 variants/core-js-3-only-polyfill/es6-object-assign/main.bundle.min.js
|
||||
11574 variants/core-js-3-only-polyfill/es6-object-get-own-property-names/main.bundle.min.js
|
||||
11412 variants/core-js-3-only-polyfill/es6-array-find-index/main.bundle.min.js
|
||||
11385 variants/core-js-3-only-polyfill/es6-array-find/main.bundle.min.js
|
||||
11294 variants/core-js-3-only-polyfill/es7-object-entries/main.bundle.min.js
|
||||
11289 variants/core-js-3-only-polyfill/es7-object-values/main.bundle.min.js
|
||||
11239 variants/core-js-3-only-polyfill/es6-array-for-each/main.bundle.min.js
|
||||
11212 variants/core-js-3-only-polyfill/es6-array-filter/main.bundle.min.js
|
||||
11205 variants/core-js-3-only-polyfill/es6-array-every/main.bundle.min.js
|
||||
11198 variants/core-js-3-only-polyfill/es6-array-some/main.bundle.min.js
|
||||
11191 variants/core-js-3-only-polyfill/es6-array-map/main.bundle.min.js
|
||||
11186 variants/core-js-3-only-polyfill/es6-object-keys/main.bundle.min.js
|
||||
11158 variants/core-js-3-only-polyfill/es6-reflect-own-keys/main.bundle.min.js
|
||||
11145 variants/core-js-3-only-polyfill/es6-object-define-properties/main.bundle.min.js
|
||||
10839 variants/core-js-3-only-polyfill/es6-reflect-set/main.bundle.min.js
|
||||
10361 variants/core-js-3-only-polyfill/es6-reflect-get/main.bundle.min.js
|
||||
10324 variants/core-js-3-only-polyfill/es7-array-includes/main.bundle.min.js
|
||||
9991 variants/core-js-3-only-polyfill/es6-array-index-of/main.bundle.min.js
|
||||
9973 variants/core-js-3-only-polyfill/es6-string-ends-with/main.bundle.min.js
|
||||
9927 variants/core-js-3-only-polyfill/es6-string-starts-with/main.bundle.min.js
|
||||
9915 variants/core-js-3-only-polyfill/es6-array-copy-within/main.bundle.min.js
|
||||
9839 variants/core-js-3-only-polyfill/es6-reflect-set-prototype-of/main.bundle.min.js
|
||||
9725 variants/core-js-3-only-polyfill/es6-array-fill/main.bundle.min.js
|
||||
9705 variants/core-js-3-only-polyfill/es6-array-reduce-right/main.bundle.min.js
|
||||
9681 variants/core-js-3-only-polyfill/es6-object-set-prototype-of/main.bundle.min.js
|
||||
9679 variants/core-js-3-only-polyfill/es6-array-reduce/main.bundle.min.js
|
||||
9561 variants/core-js-3-only-polyfill/es7-string-pad-start/main.bundle.min.js
|
||||
9554 variants/core-js-3-only-polyfill/es7-string-pad-end/main.bundle.min.js
|
||||
9531 variants/core-js-3-only-polyfill/es6-object-get-own-property-descriptor/main.bundle.min.js
|
||||
9457 variants/core-js-3-only-polyfill/es6-array-last-index-of/main.bundle.min.js
|
||||
9314 variants/core-js-3-only-polyfill/es6-string-includes/main.bundle.min.js
|
||||
9195 variants/core-js-3-only-polyfill/es6-reflect-delete-property/main.bundle.min.js
|
||||
9172 variants/core-js-3-only-polyfill/es6-reflect-get-own-property-descriptor/main.bundle.min.js
|
||||
9119 variants/core-js-3-only-polyfill/es6-object-prevent-extensions/main.bundle.min.js
|
||||
9094 variants/core-js-3-only-polyfill/es6-number-parse-int/main.bundle.min.js
|
||||
9088 variants/core-js-3-only-polyfill/es6-number-parse-float/main.bundle.min.js
|
||||
9040 variants/core-js-3-only-polyfill/es6-object-freeze/main.bundle.min.js
|
||||
9026 variants/core-js-3-only-polyfill/es6-object-seal/main.bundle.min.js
|
||||
9010 variants/core-js-3-only-polyfill/es6-object-get-prototype-of/main.bundle.min.js
|
||||
8842 variants/core-js-3-only-polyfill/es6-string-raw/main.bundle.min.js
|
||||
8792 variants/core-js-3-only-polyfill/es6-function-bind/main.bundle.min.js
|
||||
8667 variants/core-js-3-only-polyfill/es6-reflect-get-prototype-of/main.bundle.min.js
|
||||
8594 variants/core-js-3-only-polyfill/es6-string-trim/main.bundle.min.js
|
||||
8444 variants/core-js-3-only-polyfill/es6-string-from-code-point/main.bundle.min.js
|
||||
8424 variants/core-js-3-only-polyfill/es6-date-to-iso-string/main.bundle.min.js
|
||||
8413 variants/core-js-3-only-polyfill/es6-string-code-point-at/main.bundle.min.js
|
||||
8218 variants/core-js-3-only-polyfill/es6-math-fround/main.bundle.min.js
|
||||
8217 variants/core-js-3-only-polyfill/es6-string-repeat/main.bundle.min.js
|
||||
8194 variants/core-js-3-only-polyfill/es6-array-of/main.bundle.min.js
|
||||
8192 variants/core-js-3-only-polyfill/es6-date-to-json/main.bundle.min.js
|
||||
8003 variants/core-js-3-only-polyfill/es6-reflect-define-property/main.bundle.min.js
|
||||
7997 variants/core-js-3-only-polyfill/es6-object-is-extensible/main.bundle.min.js
|
||||
7983 variants/core-js-3-only-polyfill/es6-math-sinh/main.bundle.min.js
|
||||
7969 variants/core-js-3-only-polyfill/es6-object-is-sealed/main.bundle.min.js
|
||||
7969 variants/core-js-3-only-polyfill/es6-object-is-frozen/main.bundle.min.js
|
||||
7941 variants/core-js-3-only-polyfill/es6-reflect-apply/main.bundle.min.js
|
||||
7906 variants/core-js-3-only-polyfill/es6-math-tanh/main.bundle.min.js
|
||||
7905 variants/core-js-3-only-polyfill/es6-math-acosh/main.bundle.min.js
|
||||
7875 variants/core-js-3-only-polyfill/es6-number-is-safe-integer/main.bundle.min.js
|
||||
7820 variants/core-js-3-only-polyfill/es6-math-expm1/main.bundle.min.js
|
||||
7814 variants/core-js-3-only-polyfill/es6-array-is-array/main.bundle.min.js
|
||||
7785 variants/core-js-3-only-polyfill/es6-math-hypot/main.bundle.min.js
|
||||
7762 variants/core-js-3-only-polyfill/es6-reflect-prevent-extensions/main.bundle.min.js
|
||||
7751 variants/core-js-3-only-polyfill/es6-math-imul/main.bundle.min.js
|
||||
7742 variants/core-js-3-only-polyfill/es6-number-is-integer/main.bundle.min.js
|
||||
7701 variants/core-js-3-only-polyfill/es6-math-cbrt/main.bundle.min.js
|
||||
7692 variants/core-js-3-only-polyfill/es6-reflect-is-extensible/main.bundle.min.js
|
||||
7653 variants/core-js-3-only-polyfill/es6-math-log1p/main.bundle.min.js
|
||||
7630 variants/core-js-3-only-polyfill/es6-math-sign/main.bundle.min.js
|
||||
7627 variants/core-js-3-only-polyfill/es6-math-asinh/main.bundle.min.js
|
||||
7607 variants/core-js-3-only-polyfill/es6-object-define-property/main.bundle.min.js
|
||||
7591 variants/core-js-3-only-polyfill/es6-math-atanh/main.bundle.min.js
|
||||
7550 variants/core-js-3-only-polyfill/es6-math-clz32/main.bundle.min.js
|
||||
7538 variants/core-js-3-only-polyfill/es6-reflect-has/main.bundle.min.js
|
||||
7532 variants/core-js-3-only-polyfill/es6-math-cosh/main.bundle.min.js
|
||||
7530 variants/core-js-3-only-polyfill/es6-math-trunc/main.bundle.min.js
|
||||
7529 variants/core-js-3-only-polyfill/es6-number-min-safe-integer/main.bundle.min.js
|
||||
7528 variants/core-js-3-only-polyfill/es6-number-max-safe-integer/main.bundle.min.js
|
||||
7522 variants/core-js-3-only-polyfill/es6-math-log10/main.bundle.min.js
|
||||
7515 variants/core-js-3-only-polyfill/es6-math-log2/main.bundle.min.js
|
||||
7505 variants/core-js-3-only-polyfill/es6-date-now/main.bundle.min.js
|
||||
7500 variants/core-js-3-only-polyfill/es6-number-epsilon/main.bundle.min.js
|
||||
6917 variants/core-js-3-only-polyfill/es6-object-to-string/main.bundle.min.js
|
||||
5993 variants/core-js-3-only-polyfill/es6-function-has-instance/main.bundle.min.js
|
||||
5992 variants/core-js-3-only-polyfill/es6-date-to-string/main.bundle.min.js
|
||||
5421 variants/core-js-3-only-polyfill/es6-date-to-primitive/main.bundle.min.js
|
||||
4773 variants/core-js-3-only-polyfill/es6-array-species/main.bundle.min.js
|
||||
3410 variants/core-js-3-only-polyfill/es6-function-name/main.bundle.min.js
|
||||
|
||||
variants/core-js-3-preset-env-esmodules
|
||||
3567 total
|
||||
2259 variants/core-js-3-preset-env-esmodules/false/main.bundle.min.js
|
||||
1308 variants/core-js-3-preset-env-esmodules/true/main.bundle.min.js
|
||||
|
||||
variants/only-plugin
|
||||
2770 total
|
||||
1152 variants/only-plugin/-babel-plugin-transform-spread/main.bundle.min.js
|
||||
814 variants/only-plugin/-babel-plugin-transform-classes/main.bundle.min.js
|
||||
804 variants/only-plugin/-babel-plugin-transform-regenerator/main.bundle.min.js
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
for d in variants/* ; do
|
||||
echo "$d"
|
||||
wc -c "$d"/*/main.bundle.min.js | sort -hr
|
||||
printf "\n"
|
||||
done > summary-sizes.txt
|
|
@ -0,0 +1,27 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
DIRNAME="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||
LH_ROOT="$DIRNAME/../.."
|
||||
cd $LH_ROOT
|
||||
|
||||
set -e
|
||||
|
||||
# This test can be expensive, we'll only run the tests if we touched files that affect the simulations.
|
||||
CHANGED_FILES=""
|
||||
if [[ "$CI" ]]; then
|
||||
CHANGED_FILES=$(git --no-pager diff --name-only $TRAVIS_COMMIT_RANGE)
|
||||
else
|
||||
CHANGED_FILES=$(git --no-pager diff --name-only master)
|
||||
fi
|
||||
|
||||
printf "Determined the following files have been touched:\n\n$CHANGED_FILES\n\n"
|
||||
|
||||
if ! echo $CHANGED_FILES | grep -E 'legacy-javascript' > /dev/null; then
|
||||
echo "No legacy-javascript files affected, skipping test."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
printf "\n\nRunning test...\n"
|
||||
cd "$LH_ROOT/lighthouse-core/scripts/legacy-javascript"
|
||||
yarn
|
||||
node run.js
|
|
@ -0,0 +1,167 @@
|
|||
/**
|
||||
* @license Copyright 2020 Google Inc. All Rights Reserved.
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
const assert = require('assert');
|
||||
const LegacyJavascript = require('../../audits/legacy-javascript.js');
|
||||
const networkRecordsToDevtoolsLog = require('../network-records-to-devtools-log.js');
|
||||
|
||||
/**
|
||||
* @param {Array<{url: string, code: string}>} scripts
|
||||
* @return {LH.Artifacts}
|
||||
*/
|
||||
const createArtifacts = (scripts) => {
|
||||
const networkRecords = scripts.map(({url}, index) => ({
|
||||
requestId: String(index),
|
||||
url,
|
||||
}));
|
||||
return {
|
||||
URL: {finalUrl: '', requestedUrl: ''},
|
||||
devtoolsLogs: {defaultPass: networkRecordsToDevtoolsLog(networkRecords)},
|
||||
ScriptElements: scripts.reduce((acc, {code}, index) => {
|
||||
acc[String(index)] = {
|
||||
content: code,
|
||||
requestId: String(index),
|
||||
};
|
||||
return acc;
|
||||
}, {}),
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {string[]} codeSnippets
|
||||
* @return {string[]}
|
||||
*/
|
||||
const createVariants = (codeSnippets) => {
|
||||
const variants = [];
|
||||
|
||||
for (const codeSnippet of codeSnippets) {
|
||||
// Explicitly don't create a variant for just `codeSnippet`,
|
||||
// because making the patterns work with a starting anchor (^)
|
||||
// complicates the expressions more than its worth.
|
||||
variants.push(`;${codeSnippet}`);
|
||||
variants.push(` ${codeSnippet}`);
|
||||
}
|
||||
|
||||
return variants;
|
||||
};
|
||||
|
||||
/* eslint-env jest */
|
||||
describe('LegacyJavaScript audit', () => {
|
||||
it('passes code with no polyfills', async () => {
|
||||
const artifacts = createArtifacts([
|
||||
{
|
||||
code: 'var message = "hello world"; console.log(message);',
|
||||
url: 'https://www.example.com/a.js',
|
||||
},
|
||||
{
|
||||
code: 'SomeGlobal = function() {}',
|
||||
url: 'https://www.example.com/a.js',
|
||||
},
|
||||
{
|
||||
code: 'SomeClass.prototype.someFn = function() {}',
|
||||
url: 'https://www.example.com/a.js',
|
||||
},
|
||||
{
|
||||
code: 'Object.defineProperty(SomeClass.prototype, "someFn", function() {})',
|
||||
url: 'https://www.example.com/a.js',
|
||||
},
|
||||
]);
|
||||
const result = await LegacyJavascript.audit(artifacts, {computedCache: new Map()});
|
||||
assert.equal(result.score, 1);
|
||||
assert.equal(result.extendedInfo.signalCount, 0);
|
||||
});
|
||||
|
||||
it('fails code with a legacy polyfill', async () => {
|
||||
const artifacts = createArtifacts([
|
||||
{
|
||||
code: 'String.prototype.repeat = function() {}',
|
||||
url: 'https://www.example.com/a.js',
|
||||
},
|
||||
]);
|
||||
const result = await LegacyJavascript.audit(artifacts, {computedCache: new Map()});
|
||||
assert.equal(result.score, 0);
|
||||
assert.equal(result.extendedInfo.signalCount, 1);
|
||||
expect(result.details.items[0].signals).toEqual(['String.prototype.repeat']);
|
||||
});
|
||||
|
||||
it('fails code with multiple legacy polyfills', async () => {
|
||||
const artifacts = createArtifacts([
|
||||
{
|
||||
code: 'String.prototype.repeat = function() {}; String.prototype.includes = function() {}',
|
||||
url: 'https://www.example.com/a.js',
|
||||
},
|
||||
]);
|
||||
const result = await LegacyJavascript.audit(artifacts, {computedCache: new Map()});
|
||||
assert.equal(result.score, 0);
|
||||
assert.equal(result.extendedInfo.signalCount, 2);
|
||||
});
|
||||
|
||||
it('counts multiple of the same polyfill from the same script only once', async () => {
|
||||
const artifacts = createArtifacts([
|
||||
{
|
||||
code: (() => {
|
||||
// eslint-disable-next-line no-extend-native
|
||||
String.prototype.repeat = function() {};
|
||||
// eslint-disable-next-line no-extend-native
|
||||
Object.defineProperty(String.prototype, 'repeat', function() {});
|
||||
}),
|
||||
url: 'https://www.example.com/a.js',
|
||||
},
|
||||
]);
|
||||
const result = await LegacyJavascript.audit(artifacts, {computedCache: new Map()});
|
||||
assert.equal(result.score, 0);
|
||||
assert.equal(result.extendedInfo.signalCount, 1);
|
||||
});
|
||||
|
||||
it('should identify polyfills in multiple patterns', async () => {
|
||||
const codeSnippets = [
|
||||
'String.prototype.repeat = function() {}',
|
||||
'String.prototype["repeat"] = function() {}',
|
||||
'String.prototype[\'repeat\'] = function() {}',
|
||||
'Object.defineProperty(String.prototype, "repeat", function() {})',
|
||||
'Object.defineProperty(String.prototype, \'repeat\', function() {})',
|
||||
'Object.defineProperty(window, \'WeakMap\', function() {})',
|
||||
'$export($export.S,"Object",{values:function values(t){return i(t)}})',
|
||||
'WeakMap = function() {}',
|
||||
'window.WeakMap = function() {}',
|
||||
'function WeakMap() {}',
|
||||
'String.raw = function() {}',
|
||||
];
|
||||
const variants = createVariants(codeSnippets);
|
||||
const scripts = variants.map((code, i) => {
|
||||
return {code, url: `https://www.example.com/${i}.js`};
|
||||
});
|
||||
const getCodeForUrl = url => scripts.find(script => script.url === url).code;
|
||||
const artifacts = createArtifacts(scripts);
|
||||
|
||||
const result = await LegacyJavascript.audit(artifacts, {computedCache: new Map()});
|
||||
expect(result.details.items.map(item => getCodeForUrl(item.url)))
|
||||
.toEqual(scripts.map(script => getCodeForUrl(script.url)));
|
||||
assert.equal(result.score, 0);
|
||||
});
|
||||
|
||||
it('should not misidentify legacy code', async () => {
|
||||
const codeSnippets = [
|
||||
'i.prototype.toArrayBuffer = blah',
|
||||
'this.childListChangeMap=void 0',
|
||||
't.toPromise=u,t.makePromise=u,t.fromPromise=function(e){return new o.default',
|
||||
'var n=new Error(h.apply(void 0,[d].concat(f)));n.name="Invariant Violation";',
|
||||
'var b=typeof Map==="function"?new Map():void 0',
|
||||
'd.Promise=s;var y,g,v,b=function(n,o,t){if(function(t){if("function"!=typeof t)th',
|
||||
];
|
||||
const variants = createVariants(codeSnippets);
|
||||
const scripts = variants.map((code, i) => {
|
||||
return {code, url: `https://www.example.com/${i}.js`};
|
||||
});
|
||||
const getCodeForUrl = url => scripts.find(script => script.url === url).code;
|
||||
const artifacts = createArtifacts(scripts);
|
||||
|
||||
const result = await LegacyJavascript.audit(artifacts, {computedCache: new Map()});
|
||||
expect(result.details.items.map(item => getCodeForUrl(item.url))).toEqual([]);
|
||||
assert.equal(result.score, 1);
|
||||
});
|
||||
});
|
|
@ -32,6 +32,7 @@
|
|||
"test-clients": "jest \"clients/\"",
|
||||
"test-viewer": "yarn unit-viewer && jest lighthouse-viewer/test/viewer-test-pptr.js",
|
||||
"test-lantern": "bash lighthouse-core/scripts/test-lantern.sh",
|
||||
"test-legacy-javascript": "bash lighthouse-core/scripts/test-legacy-javascript.sh",
|
||||
"test-docs": "yarn --cwd docs/recipes/auth && jest docs/recipes/integration-test && yarn --cwd docs/recipes/custom-gatherer-puppeteer test",
|
||||
"unit-core": "jest \"lighthouse-core/\"",
|
||||
"unit-cli": "jest \"lighthouse-cli/\"",
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
"lighthouse-core/test/**/*.js",
|
||||
"clients/test/**/*.js",
|
||||
"lighthouse-cli/test/fixtures/**/*.js",
|
||||
"lighthouse-core/scripts/legacy-javascript/variants",
|
||||
],
|
||||
"files": [
|
||||
// Opt-in to typechecking for some core tests.
|
||||
|
|
|
@ -88,8 +88,11 @@ declare global {
|
|||
// TODO(bckenny): unify Table/Opportunity headings and items on next breaking change.
|
||||
|
||||
export interface TableColumnHeading {
|
||||
/** The name of the property within items being described. */
|
||||
key: string;
|
||||
/**
|
||||
* The name of the property within items being described.
|
||||
* If null, subRows must be defined, and the first sub-row will be empty.
|
||||
*/
|
||||
key: string|null;
|
||||
/** Readable text label of the field. */
|
||||
text: string;
|
||||
/**
|
||||
|
@ -114,8 +117,11 @@ declare global {
|
|||
}
|
||||
|
||||
export interface OpportunityColumnHeading {
|
||||
/** The name of the property within items being described. */
|
||||
key: string;
|
||||
/**
|
||||
* The name of the property within items being described.
|
||||
* If null, subRows must be defined, and the first sub-row will be empty.
|
||||
*/
|
||||
key: string|null;
|
||||
/** Readable text label of the field. */
|
||||
label: string;
|
||||
/**
|
||||
|
|
Загрузка…
Ссылка в новой задаче