core(runner): make LH.Audit.Context immutable (#10555)
This commit is contained in:
Родитель
159fd93e48
Коммит
3e5c664c3f
|
@ -181,8 +181,9 @@ class BootupTime extends Audit {
|
|||
|
||||
|
||||
// TODO: consider moving this to core gathering so you don't need to run the audit for warning
|
||||
let runWarnings;
|
||||
if (hadExcessiveChromeExtension) {
|
||||
context.LighthouseRunWarnings.push(str_(UIStrings.chromeExtensionsWarning));
|
||||
runWarnings = [str_(UIStrings.chromeExtensionsWarning)];
|
||||
}
|
||||
|
||||
const summary = {wastedMs: totalBootupTime};
|
||||
|
@ -211,6 +212,7 @@ class BootupTime extends Audit {
|
|||
displayValue: totalBootupTime > 0 ?
|
||||
str_(i18n.UIStrings.seconds, {timeInMs: totalBootupTime}) : '',
|
||||
details,
|
||||
runWarnings,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,7 +44,7 @@ class FirstContentfulPaint3G extends Audit {
|
|||
static async audit(artifacts, context) {
|
||||
const trace = artifacts.traces[Audit.DEFAULT_PASS];
|
||||
const devtoolsLog = artifacts.devtoolsLogs[Audit.DEFAULT_PASS];
|
||||
/** @type {LH.Config.Settings} */
|
||||
/** @type {Immutable<LH.Config.Settings>} */
|
||||
const settings = {...context.settings, throttlingMethod: 'simulate', throttling: regular3G};
|
||||
const metricComputationData = {trace, devtoolsLog, settings};
|
||||
const metricResult = await ComputedFcp.request(metricComputationData, context);
|
||||
|
|
|
@ -64,7 +64,7 @@ class ResourceBudget extends Audit {
|
|||
}
|
||||
|
||||
/**
|
||||
* @param {LH.Budget} budget
|
||||
* @param {Immutable<LH.Budget>} budget
|
||||
* @param {Record<LH.Budget.ResourceType,ResourceEntry>} summary
|
||||
* @return {Array<BudgetItem>}
|
||||
*/
|
||||
|
|
|
@ -80,7 +80,7 @@ class TimingBudget extends Audit {
|
|||
}
|
||||
|
||||
/**
|
||||
* @param {LH.Budget} budget
|
||||
* @param {Immutable<LH.Budget>} budget
|
||||
* @param {LH.Artifacts.TimingSummary} summary
|
||||
* @return {Array<BudgetItem>}
|
||||
*/
|
||||
|
|
|
@ -25,7 +25,8 @@ function makeComputedArtifact(computableArtifact) {
|
|||
* @return {ReturnType<C['compute_']>}
|
||||
*/
|
||||
const request = (artifacts, context) => {
|
||||
const computedCache = context.computedCache;
|
||||
// NOTE: break immutability solely for this caching-controller function.
|
||||
const computedCache = /** @type {Map<string, ArbitraryEqualityMap>} */ (context.computedCache);
|
||||
const computedName = computableArtifact.name;
|
||||
|
||||
const cache = computedCache.get(computedName) || new ArbitraryEqualityMap();
|
||||
|
|
|
@ -12,7 +12,7 @@ const NetworkAnalysis = require('./network-analysis.js');
|
|||
|
||||
class LoadSimulator {
|
||||
/**
|
||||
* @param {{devtoolsLog: LH.DevtoolsLog, settings: LH.Config.Settings}} data
|
||||
* @param {{devtoolsLog: LH.DevtoolsLog, settings: Immutable<LH.Config.Settings>}} data
|
||||
* @param {LH.Audit.Context} context
|
||||
* @return {Promise<Simulator>}
|
||||
*/
|
||||
|
|
|
@ -51,7 +51,8 @@ class ResourceSummary {
|
|||
'third-party': {count: 0, size: 0},
|
||||
};
|
||||
const budget = Budget.getMatchingBudget(context.settings.budgets, mainResourceURL);
|
||||
let firstPartyHosts = /** @type {Array<string>} */ ([]);
|
||||
/** @type {ReadonlyArray<string>} */
|
||||
let firstPartyHosts = [];
|
||||
if (budget && budget.options && budget.options.firstPartyHostnames) {
|
||||
firstPartyHosts = budget.options.firstPartyHostnames;
|
||||
} else {
|
||||
|
|
|
@ -136,9 +136,9 @@ class Budget {
|
|||
* Returns the budget that applies to a given URL.
|
||||
* If multiple budgets match based on thier 'path' property,
|
||||
* then the last-listed of those budgets is returned.
|
||||
* @param {Array<LH.Budget>|null} budgets
|
||||
* @param {Immutable<Array<LH.Budget>>|null} budgets
|
||||
* @param {string} url
|
||||
* @return {LH.Budget | undefined} budget
|
||||
* @return {Immutable<LH.Budget> | undefined} budget
|
||||
*/
|
||||
static getMatchingBudget(budgets, url) {
|
||||
if (budgets === null) return;
|
||||
|
|
|
@ -273,7 +273,9 @@ async function saveAssets(artifacts, audits, pathWithBasename) {
|
|||
* @return {Promise<void>}
|
||||
*/
|
||||
async function saveLanternNetworkData(devtoolsLog, outputPath) {
|
||||
const context = /** @type {LH.Audit.Context} */ ({computedCache: new Map()});
|
||||
/** @type {LH.Audit.Context} */
|
||||
// @ts-ignore - the full audit context isn't needed for analysis.
|
||||
const context = {computedCache: new Map()};
|
||||
const networkAnalysis = await NetworkAnalysisComputed.request(devtoolsLog, context);
|
||||
const lanternData = LoadSimulatorComputed.convertAnalysisToSaveableLanternData(networkAnalysis);
|
||||
|
||||
|
|
|
@ -248,14 +248,14 @@ class Runner {
|
|||
// Members of LH.Audit.Context that are shared across all audits.
|
||||
const sharedAuditContext = {
|
||||
settings,
|
||||
LighthouseRunWarnings: runWarnings,
|
||||
computedCache: new Map(),
|
||||
};
|
||||
|
||||
// Run each audit sequentially
|
||||
const auditResults = [];
|
||||
for (const auditDefn of audits) {
|
||||
const auditResult = await Runner._runAudit(auditDefn, artifacts, sharedAuditContext);
|
||||
const auditResult = await Runner._runAudit(auditDefn, artifacts, sharedAuditContext,
|
||||
runWarnings);
|
||||
auditResults.push(auditResult);
|
||||
}
|
||||
|
||||
|
@ -268,11 +268,12 @@ class Runner {
|
|||
* Otherwise returns error audit result.
|
||||
* @param {LH.Config.AuditDefn} auditDefn
|
||||
* @param {LH.Artifacts} artifacts
|
||||
* @param {Pick<LH.Audit.Context, 'settings'|'LighthouseRunWarnings'|'computedCache'>} sharedAuditContext
|
||||
* @param {Pick<LH.Audit.Context, 'settings'|'computedCache'>} sharedAuditContext
|
||||
* @param {Array<string>} runWarnings
|
||||
* @return {Promise<LH.Audit.Result>}
|
||||
* @private
|
||||
*/
|
||||
static async _runAudit(auditDefn, artifacts, sharedAuditContext) {
|
||||
static async _runAudit(auditDefn, artifacts, sharedAuditContext, runWarnings) {
|
||||
const audit = auditDefn.implementation;
|
||||
const status = {
|
||||
msg: `Auditing: ${i18n.getFormatted(audit.meta.title, 'en-US')}`,
|
||||
|
@ -341,6 +342,8 @@ class Runner {
|
|||
return narrowedArtifacts;
|
||||
}, /** @type {LH.Artifacts} */ ({}));
|
||||
const product = await audit.audit(narrowedArtifacts, auditContext);
|
||||
runWarnings.push(...product.runWarnings || []);
|
||||
|
||||
auditResult = Audit.generateAuditResult(audit, product);
|
||||
} catch (err) {
|
||||
// Log error if it hasn't already been logged above.
|
||||
|
|
|
@ -49,7 +49,6 @@ async function main() {
|
|||
computedCache: new Map(),
|
||||
options: {},
|
||||
settings: /** @type {any} */ ({}),
|
||||
LighthouseRunWarnings: [],
|
||||
});
|
||||
|
||||
const items =
|
||||
|
|
|
@ -113,8 +113,8 @@ variants/core-js-2-only-polyfill
|
|||
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
|
||||
4121 total
|
||||
2813 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
|
||||
|
@ -232,13 +232,13 @@ variants/core-js-3-only-polyfill
|
|||
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
|
||||
4121 total
|
||||
2813 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
|
||||
2793 total
|
||||
1152 variants/only-plugin/-babel-plugin-transform-spread/main.bundle.min.js
|
||||
3347 total
|
||||
1706 variants/only-plugin/-babel-plugin-transform-spread/main.bundle.min.js
|
||||
827 variants/only-plugin/-babel-plugin-transform-regenerator/main.bundle.min.js
|
||||
814 variants/only-plugin/-babel-plugin-transform-classes/main.bundle.min.js
|
||||
|
||||
|
|
|
@ -654,11 +654,11 @@ describe('Runner', () => {
|
|||
static get meta() {
|
||||
return basicAuditMeta;
|
||||
}
|
||||
static audit(artifacts, context) {
|
||||
context.LighthouseRunWarnings.push(warningString);
|
||||
static audit() {
|
||||
return {
|
||||
numericValue: 5,
|
||||
score: 1,
|
||||
runWarnings: [warningString],
|
||||
};
|
||||
}
|
||||
},
|
||||
|
@ -670,31 +670,6 @@ describe('Runner', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('includes any LighthouseRunWarnings from errored audits in LHR', () => {
|
||||
const warningString = 'Audit warning just before a terrible error!';
|
||||
|
||||
const config = new Config({
|
||||
settings: {
|
||||
auditMode: __dirname + '/fixtures/artifacts/empty-artifacts/',
|
||||
},
|
||||
audits: [
|
||||
class WarningAudit extends Audit {
|
||||
static get meta() {
|
||||
return basicAuditMeta;
|
||||
}
|
||||
static audit(artifacts, context) {
|
||||
context.LighthouseRunWarnings.push(warningString);
|
||||
throw new Error('Terrible.');
|
||||
}
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
return Runner.run(null, {config, driverMock}).then(results => {
|
||||
assert.deepStrictEqual(results.lhr.runWarnings, [warningString]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('lhr.runtimeError', () => {
|
||||
const NO_FCP = LHError.errors.NO_FCP;
|
||||
class RuntimeErrorGatherer extends Gatherer {
|
||||
|
|
|
@ -509,7 +509,7 @@ declare global {
|
|||
export interface MetricComputationDataInput {
|
||||
devtoolsLog: DevtoolsLog;
|
||||
trace: Trace;
|
||||
settings: Config.Settings;
|
||||
settings: Immutable<Config.Settings>;
|
||||
simulator?: LanternSimulator;
|
||||
}
|
||||
|
||||
|
|
|
@ -8,19 +8,17 @@ import ArbitraryEqualityMap = require('../lighthouse-core/lib/arbitrary-equality
|
|||
|
||||
declare global {
|
||||
module LH.Audit {
|
||||
export interface Context {
|
||||
export type Context = Immutable<{
|
||||
/** audit options */
|
||||
options: Record<string, any>;
|
||||
settings: Config.Settings;
|
||||
/** Push to this array to add top-level warnings to the LHR. */
|
||||
LighthouseRunWarnings: Array<string>;
|
||||
/**
|
||||
* Nested cache for already-computed computed artifacts. Keyed first on
|
||||
* the computed artifact's `name` property, then on input artifact(s).
|
||||
* Values are Promises resolving to the computedArtifact result.
|
||||
*/
|
||||
computedCache: Map<string, ArbitraryEqualityMap>;
|
||||
}
|
||||
}>;
|
||||
|
||||
export interface ScoreOptions {
|
||||
scorePODR: number;
|
||||
|
@ -85,6 +83,8 @@ declare global {
|
|||
notApplicable?: boolean;
|
||||
/** Extra information about the page provided by some types of audits, in one of several possible forms that can be rendered in the HTML report. */
|
||||
details?: Audit.Details;
|
||||
/** If an audit encounters unusual execution circumstances, strings can be put in this optional array to add top-level warnings to the LHR. */
|
||||
runWarnings?: Array<string>;
|
||||
}
|
||||
|
||||
/** The Audit.Product type for audits that do not return a `numericValue`. */
|
||||
|
|
|
@ -76,6 +76,23 @@ declare global {
|
|||
T[P];
|
||||
};
|
||||
|
||||
/** Recursively makes all properties of T read-only. */
|
||||
export type Immutable<T> =
|
||||
T extends Function ? T :
|
||||
T extends Array<infer R> ? ImmutableArray<R> :
|
||||
T extends Map<infer K, infer V> ? ImmutableMap<K, V> :
|
||||
T extends Set<infer M> ? ImmutableSet<M> :
|
||||
T extends object ? ImmutableObject<T> :
|
||||
T
|
||||
|
||||
// Intermediate immutable types. Prefer e.g. Immutable<Set<T>> over direct use.
|
||||
type ImmutableArray<T> = ReadonlyArray<Immutable<T>>;
|
||||
type ImmutableMap<K, V> = ReadonlyMap<Immutable<K>, Immutable<V>>;
|
||||
type ImmutableSet<T> = ReadonlySet<Immutable<T>>;
|
||||
type ImmutableObject<T> = {
|
||||
readonly [K in keyof T]: Immutable<T[K]>;
|
||||
};
|
||||
|
||||
/**
|
||||
* Exclude void from T
|
||||
*/
|
||||
|
|
Загрузка…
Ссылка в новой задаче