lighthouse 9.5.0-dev.20221114 → 9.5.0-dev.20221116
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cli/cli-flags.js +1 -1
- package/cli/printer.js +1 -1
- package/cli/test/smokehouse/frontends/smokehouse-bin.js +1 -1
- package/core/audits/byte-efficiency/render-blocking-resources.js +1 -1
- package/core/audits/byte-efficiency/uses-long-cache-ttl.js +87 -88
- package/core/audits/critical-request-chains.js +41 -42
- package/core/audits/is-on-https.js +38 -39
- package/core/audits/metrics/first-contentful-paint-3g.js +1 -1
- package/core/audits/performance-budget.js +1 -1
- package/core/audits/seo/is-crawlable.js +51 -54
- package/core/audits/timing-budget.js +1 -1
- package/core/audits/user-timings.js +42 -43
- package/core/computed/computed-artifact.js +3 -3
- package/core/computed/load-simulator.js +1 -1
- package/core/computed/metrics/responsiveness.js +1 -1
- package/core/computed/metrics/timing-summary.js +2 -2
- package/core/computed/network-analysis.js +1 -1
- package/core/computed/resource-summary.js +2 -2
- package/core/config/budget.js +2 -2
- package/core/config/config-helpers.js +2 -2
- package/core/gather/gatherers/full-page-screenshot.js +3 -1
- package/core/index.js +81 -2
- package/core/lib/lantern-trace-saver.js +1 -1
- package/core/lib/network-request.js +1 -1
- package/package.json +1 -1
- package/report/generator/report-generator.js +19 -5
- package/report/test/generator/report-generator-test.js +18 -0
- package/types/artifacts.d.ts +5 -3
- package/types/audit.d.ts +2 -1
- package/types/externs.d.ts +0 -95
- package/types/global-lh.d.ts +3 -0
- package/types/node.d.ts +32 -0
- package/types/utility-types.d.ts +72 -0
- package/core/api.js +0 -80
|
@@ -12,9 +12,9 @@ import {ArbitraryEqualityMap} from '../lib/arbitrary-equality-map.js';
|
|
|
12
12
|
* Decorate computableArtifact with a caching `request()` method which will
|
|
13
13
|
* automatically call `computableArtifact.compute_()` under the hood.
|
|
14
14
|
* @template {{name: string, compute_(dependencies: unknown, context: LH.Artifacts.ComputedContext): Promise<unknown>}} C
|
|
15
|
-
* @template {Array<keyof FirstParamType<C['compute_']>>} K
|
|
15
|
+
* @template {Array<keyof LH.Util.FirstParamType<C['compute_']>>} K
|
|
16
16
|
* @param {C} computableArtifact
|
|
17
|
-
* @param {(K & ([keyof FirstParamType<C['compute_']>] extends [K[number]] ? unknown : never)) | null} keys List of properties of `dependencies` used by `compute_`; other properties are filtered out. Use `null` to allow all properties. Ensures that only required properties are used for caching result.
|
|
17
|
+
* @param {(K & ([keyof LH.Util.FirstParamType<C['compute_']>] extends [K[number]] ? unknown : never)) | null} keys List of properties of `dependencies` used by `compute_`; other properties are filtered out. Use `null` to allow all properties. Ensures that only required properties are used for caching result.
|
|
18
18
|
*/
|
|
19
19
|
function makeComputedArtifact(computableArtifact, keys) {
|
|
20
20
|
// tsc (3.1) has more difficulty with template inter-references in jsdoc, so
|
|
@@ -22,7 +22,7 @@ function makeComputedArtifact(computableArtifact, keys) {
|
|
|
22
22
|
// polymorphic-this behavior for C.
|
|
23
23
|
/**
|
|
24
24
|
* Return an automatically cached result from the computed artifact.
|
|
25
|
-
* @param {FirstParamType<C['compute_']>} dependencies
|
|
25
|
+
* @param {LH.Util.FirstParamType<C['compute_']>} dependencies
|
|
26
26
|
* @param {LH.Artifacts.ComputedContext} context
|
|
27
27
|
* @return {ReturnType<C['compute_']>}
|
|
28
28
|
*/
|
|
@@ -11,7 +11,7 @@ import {NetworkAnalysis} from './network-analysis.js';
|
|
|
11
11
|
|
|
12
12
|
class LoadSimulator {
|
|
13
13
|
/**
|
|
14
|
-
* @param {{devtoolsLog: LH.DevtoolsLog, settings:
|
|
14
|
+
* @param {{devtoolsLog: LH.DevtoolsLog, settings: LH.Audit.Context['settings']}} data
|
|
15
15
|
* @param {LH.Artifacts.ComputedContext} context
|
|
16
16
|
* @return {Promise<Simulator>}
|
|
17
17
|
*/
|
|
@@ -131,7 +131,7 @@ class Responsiveness {
|
|
|
131
131
|
}
|
|
132
132
|
|
|
133
133
|
/**
|
|
134
|
-
* @param {{trace: LH.Trace, settings:
|
|
134
|
+
* @param {{trace: LH.Trace, settings: LH.Audit.Context['settings']}} data
|
|
135
135
|
* @param {LH.Artifacts.ComputedContext} context
|
|
136
136
|
* @return {Promise<EventTimingEvent|FallbackTimingEvent|null>}
|
|
137
137
|
*/
|
|
@@ -24,7 +24,7 @@ class TimingSummary {
|
|
|
24
24
|
* @param {LH.Trace} trace
|
|
25
25
|
* @param {LH.DevtoolsLog} devtoolsLog
|
|
26
26
|
* @param {LH.Artifacts['GatherContext']} gatherContext
|
|
27
|
-
* @param {ImmutableObject<LH.Config.Settings>} settings
|
|
27
|
+
* @param {LH.Util.ImmutableObject<LH.Config.Settings>} settings
|
|
28
28
|
* @param {LH.Artifacts['URL']} URL
|
|
29
29
|
* @param {LH.Artifacts.ComputedContext} context
|
|
30
30
|
* @return {Promise<{metrics: LH.Artifacts.TimingSummary, debugInfo: Record<string,boolean>}>}
|
|
@@ -134,7 +134,7 @@ class TimingSummary {
|
|
|
134
134
|
return {metrics, debugInfo};
|
|
135
135
|
}
|
|
136
136
|
/**
|
|
137
|
-
* @param {{trace: LH.Trace, devtoolsLog: LH.DevtoolsLog, gatherContext: LH.Artifacts['GatherContext']; settings: ImmutableObject<LH.Config.Settings>, URL: LH.Artifacts['URL']}} data
|
|
137
|
+
* @param {{trace: LH.Trace, devtoolsLog: LH.DevtoolsLog, gatherContext: LH.Artifacts['GatherContext']; settings: LH.Util.ImmutableObject<LH.Config.Settings>, URL: LH.Artifacts['URL']}} data
|
|
138
138
|
* @param {LH.Artifacts.ComputedContext} context
|
|
139
139
|
* @return {Promise<{metrics: LH.Artifacts.TimingSummary, debugInfo: Record<string,boolean>}>}
|
|
140
140
|
*/
|
|
@@ -11,7 +11,7 @@ import {NetworkRecords} from './network-records.js';
|
|
|
11
11
|
class NetworkAnalysis {
|
|
12
12
|
/**
|
|
13
13
|
* @param {Array<LH.Artifacts.NetworkRequest>} records
|
|
14
|
-
* @return {StrictOmit<LH.Artifacts.NetworkAnalysis, 'throughput'>}
|
|
14
|
+
* @return {LH.Util.StrictOmit<LH.Artifacts.NetworkAnalysis, 'throughput'>}
|
|
15
15
|
*/
|
|
16
16
|
static computeRTTAndServerResponseTime(records) {
|
|
17
17
|
// First pass compute the estimated observed RTT to each origin's servers.
|
|
@@ -34,7 +34,7 @@ class ResourceSummary {
|
|
|
34
34
|
/**
|
|
35
35
|
* @param {Array<LH.Artifacts.NetworkRequest>} networkRecords
|
|
36
36
|
* @param {LH.Artifacts.URL} URLArtifact
|
|
37
|
-
* @param {ImmutableObject<LH.Budget[]|null>} budgets
|
|
37
|
+
* @param {LH.Util.ImmutableObject<LH.Budget[]|null>} budgets
|
|
38
38
|
* @return {Record<LH.Budget.ResourceType, ResourceEntry>}
|
|
39
39
|
*/
|
|
40
40
|
static summarize(networkRecords, URLArtifact, budgets) {
|
|
@@ -99,7 +99,7 @@ class ResourceSummary {
|
|
|
99
99
|
}
|
|
100
100
|
|
|
101
101
|
/**
|
|
102
|
-
* @param {{URL: LH.Artifacts['URL'], devtoolsLog: LH.DevtoolsLog, budgets: ImmutableObject<LH.Budget[]|null>}} data
|
|
102
|
+
* @param {{URL: LH.Artifacts['URL'], devtoolsLog: LH.DevtoolsLog, budgets: LH.Util.ImmutableObject<LH.Budget[]|null>}} data
|
|
103
103
|
* @param {LH.Artifacts.ComputedContext} context
|
|
104
104
|
* @return {Promise<Record<LH.Budget.ResourceType,ResourceEntry>>}
|
|
105
105
|
*/
|
package/core/config/budget.js
CHANGED
|
@@ -135,9 +135,9 @@ class Budget {
|
|
|
135
135
|
* Returns the budget that applies to a given URL.
|
|
136
136
|
* If multiple budgets match based on thier 'path' property,
|
|
137
137
|
* then the last-listed of those budgets is returned.
|
|
138
|
-
* @param {Immutable<Array<LH.Budget>>|null} budgets
|
|
138
|
+
* @param {LH.Util.Immutable<Array<LH.Budget>>|null} budgets
|
|
139
139
|
* @param {string|undefined} url
|
|
140
|
-
* @return {Immutable<LH.Budget> | undefined} budget
|
|
140
|
+
* @return {LH.Util.Immutable<LH.Budget> | undefined} budget
|
|
141
141
|
*/
|
|
142
142
|
static getMatchingBudget(budgets, url) {
|
|
143
143
|
if (budgets === null || url === undefined) return;
|
|
@@ -299,10 +299,10 @@ function requireAudit(auditPath, coreAuditList, configDir) {
|
|
|
299
299
|
* Creates a settings object from potential flags object by dropping all the properties
|
|
300
300
|
* that don't exist on Config.Settings.
|
|
301
301
|
* @param {Partial<LH.Flags>=} flags
|
|
302
|
-
* @return {RecursivePartial<LH.Config.Settings>}
|
|
302
|
+
* @return {LH.Util.RecursivePartial<LH.Config.Settings>}
|
|
303
303
|
*/
|
|
304
304
|
function cleanFlagsForSettings(flags = {}) {
|
|
305
|
-
/** @type {RecursivePartial<LH.Config.Settings>} */
|
|
305
|
+
/** @type {LH.Util.RecursivePartial<LH.Config.Settings>} */
|
|
306
306
|
const settings = {};
|
|
307
307
|
|
|
308
308
|
for (const key of Object.keys(flags)) {
|
|
@@ -25,7 +25,9 @@ const MAX_WEBP_SIZE = 16383;
|
|
|
25
25
|
* @param {S} str
|
|
26
26
|
*/
|
|
27
27
|
function kebabCaseToCamelCase(str) {
|
|
28
|
-
return /** @type {KebabToCamelCase<S>} */ (
|
|
28
|
+
return /** @type {LH.Util.KebabToCamelCase<S>} */ (
|
|
29
|
+
str.replace(/(-\w)/g, m => m[1].toUpperCase())
|
|
30
|
+
);
|
|
29
31
|
}
|
|
30
32
|
|
|
31
33
|
/* c8 ignore start */
|
package/core/index.js
CHANGED
|
@@ -10,9 +10,13 @@ import {Runner} from './runner.js';
|
|
|
10
10
|
import {CriConnection} from './legacy/gather/connections/cri.js';
|
|
11
11
|
import {Config} from './legacy/config/config.js';
|
|
12
12
|
import UrlUtils from './lib/url-utils.js';
|
|
13
|
-
import * as fraggleRock from './api.js';
|
|
14
13
|
import {Driver} from './legacy/gather/driver.js';
|
|
15
14
|
import {initializeConfig} from './config/config.js';
|
|
15
|
+
import {UserFlow, auditGatherSteps} from './user-flow.js';
|
|
16
|
+
import {ReportGenerator} from '../report/generator/report-generator.js';
|
|
17
|
+
import {startTimespanGather} from './gather/timespan-runner.js';
|
|
18
|
+
import {snapshotGather} from './gather/snapshot-runner.js';
|
|
19
|
+
import {navigationGather} from './gather/navigation-runner.js';
|
|
16
20
|
|
|
17
21
|
/** @typedef {import('./legacy/gather/connections/connection.js').Connection} Connection */
|
|
18
22
|
|
|
@@ -40,7 +44,7 @@ import {initializeConfig} from './config/config.js';
|
|
|
40
44
|
* @return {Promise<LH.RunnerResult|undefined>}
|
|
41
45
|
*/
|
|
42
46
|
async function lighthouse(url, flags = {}, configJSON, page) {
|
|
43
|
-
return
|
|
47
|
+
return navigation(page, url, {config: configJSON, flags});
|
|
44
48
|
}
|
|
45
49
|
|
|
46
50
|
/**
|
|
@@ -73,6 +77,75 @@ async function legacyNavigation(url, flags = {}, configJSON, userConnection) {
|
|
|
73
77
|
return Runner.audit(artifacts, options);
|
|
74
78
|
}
|
|
75
79
|
|
|
80
|
+
/**
|
|
81
|
+
* @param {LH.Puppeteer.Page} page
|
|
82
|
+
* @param {LH.UserFlow.Options} [options]
|
|
83
|
+
*/
|
|
84
|
+
async function startFlow(page, options) {
|
|
85
|
+
return new UserFlow(page, options);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* @param {LH.Puppeteer.Page|undefined} page
|
|
90
|
+
* @param {LH.NavigationRequestor|undefined} requestor
|
|
91
|
+
* @param {{config?: LH.Config.Json, flags?: LH.Flags}} [options]
|
|
92
|
+
* @return {Promise<LH.RunnerResult|undefined>}
|
|
93
|
+
*/
|
|
94
|
+
async function navigation(page, requestor, options) {
|
|
95
|
+
const gatherResult = await navigationGather(page, requestor, options);
|
|
96
|
+
return Runner.audit(gatherResult.artifacts, gatherResult.runnerOptions);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* @param {LH.Puppeteer.Page} page
|
|
101
|
+
* @param {{config?: LH.Config.Json, flags?: LH.Flags}} [options]
|
|
102
|
+
* @return {Promise<LH.RunnerResult|undefined>}
|
|
103
|
+
*/
|
|
104
|
+
async function snapshot(page, options) {
|
|
105
|
+
const gatherResult = await snapshotGather(page, options);
|
|
106
|
+
return Runner.audit(gatherResult.artifacts, gatherResult.runnerOptions);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* @param {LH.Puppeteer.Page} page
|
|
111
|
+
* @param {{config?: LH.Config.Json, flags?: LH.Flags}} [options]
|
|
112
|
+
* @return {Promise<{endTimespan: () => Promise<LH.RunnerResult|undefined>}>}
|
|
113
|
+
*/
|
|
114
|
+
async function startTimespan(page, options) {
|
|
115
|
+
const {endTimespanGather} = await startTimespanGather(page, options);
|
|
116
|
+
const endTimespan = async () => {
|
|
117
|
+
const gatherResult = await endTimespanGather();
|
|
118
|
+
return Runner.audit(gatherResult.artifacts, gatherResult.runnerOptions);
|
|
119
|
+
};
|
|
120
|
+
return {endTimespan};
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* @template {LH.Result|LH.FlowResult} R
|
|
125
|
+
* @param {R} result
|
|
126
|
+
* @param {[R] extends [LH.Result] ? LH.OutputMode : Exclude<LH.OutputMode, 'csv'>} [format]
|
|
127
|
+
* @return {string}
|
|
128
|
+
*/
|
|
129
|
+
function generateReport(result, format = 'html') {
|
|
130
|
+
const reportOutput = ReportGenerator.generateReport(result, format);
|
|
131
|
+
if (Array.isArray(reportOutput)) {
|
|
132
|
+
// In theory the output should never be an array.
|
|
133
|
+
// This is mostly for type checking.
|
|
134
|
+
return reportOutput[0];
|
|
135
|
+
} else {
|
|
136
|
+
return reportOutput;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* @param {LH.UserFlow.FlowArtifacts} flowArtifacts
|
|
142
|
+
* @param {LH.Config.Json} [config]
|
|
143
|
+
*/
|
|
144
|
+
async function auditFlowArtifacts(flowArtifacts, config) {
|
|
145
|
+
const {gatherSteps, name} = flowArtifacts;
|
|
146
|
+
return await auditGatherSteps(gatherSteps, {name, config});
|
|
147
|
+
}
|
|
148
|
+
|
|
76
149
|
/**
|
|
77
150
|
* Generate a Lighthouse Config.
|
|
78
151
|
* @param {LH.Config.Json=} configJson Configuration for the Lighthouse run. If
|
|
@@ -113,6 +186,12 @@ export {default as Gatherer} from './gather/base-gatherer.js';
|
|
|
113
186
|
export {NetworkRecords} from './computed/network-records.js';
|
|
114
187
|
export {
|
|
115
188
|
legacyNavigation,
|
|
189
|
+
startFlow,
|
|
190
|
+
navigation,
|
|
191
|
+
startTimespan,
|
|
192
|
+
snapshot,
|
|
193
|
+
generateReport,
|
|
194
|
+
auditFlowArtifacts,
|
|
116
195
|
generateConfig,
|
|
117
196
|
generateLegacyConfig,
|
|
118
197
|
getAuditList,
|
|
@@ -130,7 +130,7 @@ function convertNodeTimingsToTrace(nodeTimings) {
|
|
|
130
130
|
if (startTime === endTime) endTime += 0.3;
|
|
131
131
|
|
|
132
132
|
const requestData = {requestId: requestId.toString(), frame};
|
|
133
|
-
/** @type {StrictOmit<LH.TraceEvent, 'name'|'ts'|'args'>} */
|
|
133
|
+
/** @type {LH.Util.StrictOmit<LH.TraceEvent, 'name'|'ts'|'args'>} */
|
|
134
134
|
const baseRequestEvent = {...baseEvent, ph: 'I', s: 't', dur: 0};
|
|
135
135
|
|
|
136
136
|
const sendRequestData = {
|
|
@@ -89,7 +89,7 @@ const HEADER_PROTOCOL_IS_H2 = 'X-ProtocolIsH2';
|
|
|
89
89
|
* @property {number} responseMs
|
|
90
90
|
*/
|
|
91
91
|
|
|
92
|
-
/** @type {SelfMap<LH.Crdp.Network.ResourceType>} */
|
|
92
|
+
/** @type {LH.Util.SelfMap<LH.Crdp.Network.ResourceType>} */
|
|
93
93
|
const RESOURCE_TYPES = {
|
|
94
94
|
XHR: 'XHR',
|
|
95
95
|
Fetch: 'Fetch',
|
package/package.json
CHANGED
|
@@ -146,28 +146,42 @@ class ReportGenerator {
|
|
|
146
146
|
.join(CRLF);
|
|
147
147
|
}
|
|
148
148
|
|
|
149
|
+
/**
|
|
150
|
+
* @param {LHResult|FlowResult} result
|
|
151
|
+
* @return {result is FlowResult}
|
|
152
|
+
*/
|
|
153
|
+
static isFlowResult(result) {
|
|
154
|
+
return 'steps' in result;
|
|
155
|
+
}
|
|
156
|
+
|
|
149
157
|
/**
|
|
150
158
|
* Creates the results output in a format based on the `mode`.
|
|
151
|
-
* @param {LHResult}
|
|
159
|
+
* @param {LHResult|FlowResult} result
|
|
152
160
|
* @param {LHResult['configSettings']['output']} outputModes
|
|
153
161
|
* @return {string|string[]}
|
|
154
162
|
*/
|
|
155
|
-
static generateReport(
|
|
163
|
+
static generateReport(result, outputModes) {
|
|
156
164
|
const outputAsArray = Array.isArray(outputModes);
|
|
157
165
|
if (typeof outputModes === 'string') outputModes = [outputModes];
|
|
158
166
|
|
|
159
167
|
const output = outputModes.map(outputMode => {
|
|
160
168
|
// HTML report.
|
|
161
169
|
if (outputMode === 'html') {
|
|
162
|
-
|
|
170
|
+
if (ReportGenerator.isFlowResult(result)) {
|
|
171
|
+
return ReportGenerator.generateFlowReportHtml(result);
|
|
172
|
+
}
|
|
173
|
+
return ReportGenerator.generateReportHtml(result);
|
|
163
174
|
}
|
|
164
175
|
// CSV report.
|
|
165
176
|
if (outputMode === 'csv') {
|
|
166
|
-
|
|
177
|
+
if (ReportGenerator.isFlowResult(result)) {
|
|
178
|
+
throw new Error('CSV output is not support for user flows');
|
|
179
|
+
}
|
|
180
|
+
return ReportGenerator.generateReportCSV(result);
|
|
167
181
|
}
|
|
168
182
|
// JSON report.
|
|
169
183
|
if (outputMode === 'json') {
|
|
170
|
-
return JSON.stringify(
|
|
184
|
+
return JSON.stringify(result, null, 2);
|
|
171
185
|
}
|
|
172
186
|
|
|
173
187
|
throw new Error('Invalid output mode: ' + outputMode);
|
|
@@ -16,6 +16,7 @@ import {ReportGenerator} from '../../generator/report-generator.js';
|
|
|
16
16
|
import {readJson} from '../../../core/test/test-utils.js';
|
|
17
17
|
|
|
18
18
|
const sampleResults = readJson('core/test/results/sample_v2.json');
|
|
19
|
+
const sampleFlowResult = readJson('core/test/fixtures/fraggle-rock/reports/sample-flow-result.json');
|
|
19
20
|
|
|
20
21
|
describe('ReportGenerator', () => {
|
|
21
22
|
describe('#replaceStrings', () => {
|
|
@@ -75,12 +76,23 @@ describe('ReportGenerator', () => {
|
|
|
75
76
|
assert.doesNotThrow(_ => JSON.parse(jsonOutput));
|
|
76
77
|
});
|
|
77
78
|
|
|
79
|
+
it('creates JSON for flow result', () => {
|
|
80
|
+
const jsonOutput = ReportGenerator.generateReport(sampleFlowResult, 'json');
|
|
81
|
+
assert.doesNotThrow(_ => JSON.parse(jsonOutput));
|
|
82
|
+
});
|
|
83
|
+
|
|
78
84
|
it('creates HTML for results', () => {
|
|
79
85
|
const htmlOutput = ReportGenerator.generateReport(sampleResults, 'html');
|
|
80
86
|
assert.ok(/<!doctype/gim.test(htmlOutput));
|
|
81
87
|
assert.ok(/<html lang="en"/gim.test(htmlOutput));
|
|
82
88
|
});
|
|
83
89
|
|
|
90
|
+
it('creates HTML for flow result', () => {
|
|
91
|
+
const htmlOutput = ReportGenerator.generateReport(sampleFlowResult, 'html');
|
|
92
|
+
assert.ok(/<!doctype/gim.test(htmlOutput));
|
|
93
|
+
assert.ok(/<html lang="en"/gim.test(htmlOutput));
|
|
94
|
+
});
|
|
95
|
+
|
|
84
96
|
it('creates CSV for results', async () => {
|
|
85
97
|
const path = './.results-as-csv.csv';
|
|
86
98
|
|
|
@@ -126,6 +138,12 @@ category,audit,score,displayValue,description
|
|
|
126
138
|
expect(csvOutput).toContain('pwa');
|
|
127
139
|
});
|
|
128
140
|
|
|
141
|
+
it('throws when creating CSV for flow result', () => {
|
|
142
|
+
expect(() => {
|
|
143
|
+
ReportGenerator.generateReport(sampleFlowResult, 'csv');
|
|
144
|
+
}).toThrow('CSV output is not support for user flows');
|
|
145
|
+
});
|
|
146
|
+
|
|
129
147
|
it('writes extended info', () => {
|
|
130
148
|
const htmlOutput = ReportGenerator.generateReport(sampleResults, 'html');
|
|
131
149
|
const outputCheck = new RegExp('dobetterweb/dbw_tester.css', 'i');
|
package/types/artifacts.d.ts
CHANGED
|
@@ -18,10 +18,12 @@ import Gatherer from './gatherer';
|
|
|
18
18
|
import {IcuMessage} from './lhr/i18n';
|
|
19
19
|
import LHResult from './lhr/lhr'
|
|
20
20
|
import Protocol from './protocol';
|
|
21
|
+
import Util from './utility-types.js';
|
|
22
|
+
import Audit from './audit.js';
|
|
21
23
|
|
|
22
24
|
export interface Artifacts extends BaseArtifacts, GathererArtifacts {}
|
|
23
25
|
|
|
24
|
-
export type FRArtifacts = StrictOmit<Artifacts,
|
|
26
|
+
export type FRArtifacts = Util.StrictOmit<Artifacts,
|
|
25
27
|
| 'Fonts'
|
|
26
28
|
| 'Manifest'
|
|
27
29
|
| 'MixedContent'
|
|
@@ -179,7 +181,7 @@ export interface GathererArtifacts extends PublicGathererArtifacts,LegacyBaseArt
|
|
|
179
181
|
}
|
|
180
182
|
|
|
181
183
|
declare module Artifacts {
|
|
182
|
-
type ComputedContext = Immutable<{
|
|
184
|
+
type ComputedContext = Util.Immutable<{
|
|
183
185
|
computedCache: Map<string, ArbitraryEqualityMap>;
|
|
184
186
|
}>;
|
|
185
187
|
|
|
@@ -634,7 +636,7 @@ declare module Artifacts {
|
|
|
634
636
|
interface MetricComputationDataInput {
|
|
635
637
|
devtoolsLog: DevtoolsLog;
|
|
636
638
|
trace: Trace;
|
|
637
|
-
settings:
|
|
639
|
+
settings: Audit.Context['settings'];
|
|
638
640
|
gatherContext: Artifacts['GatherContext'];
|
|
639
641
|
simulator?: InstanceType<typeof Simulator>;
|
|
640
642
|
URL: Artifacts['URL'];
|
package/types/audit.d.ts
CHANGED
|
@@ -11,6 +11,7 @@ import Config from './config';
|
|
|
11
11
|
import Gatherer from './gatherer';
|
|
12
12
|
import {IcuMessage} from './lhr/i18n';
|
|
13
13
|
import * as AuditResult from './lhr/audit-result';
|
|
14
|
+
import Util from './utility-types.js';
|
|
14
15
|
|
|
15
16
|
declare module Audit {
|
|
16
17
|
export import Details = AuditDetails;
|
|
@@ -18,7 +19,7 @@ declare module Audit {
|
|
|
18
19
|
export type ScoreDisplayMode = AuditResult.ScoreDisplayMode;
|
|
19
20
|
export type ScoreDisplayModes = AuditResult.ScoreDisplayModes;
|
|
20
21
|
|
|
21
|
-
type Context = Immutable<{
|
|
22
|
+
type Context = Util.Immutable<{
|
|
22
23
|
/** audit options */
|
|
23
24
|
options: Record<string, any>;
|
|
24
25
|
settings: Config.Settings;
|
package/types/externs.d.ts
CHANGED
|
@@ -8,101 +8,6 @@ import {Artifacts} from './artifacts';
|
|
|
8
8
|
import LHResult from './lhr/lhr';
|
|
9
9
|
import {SharedFlagsSettings, OutputMode} from './lhr/settings';
|
|
10
10
|
|
|
11
|
-
declare global {
|
|
12
|
-
// Augment Intl to include
|
|
13
|
-
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/getCanonicalLocales
|
|
14
|
-
namespace Intl {
|
|
15
|
-
var getCanonicalLocales: (locales?: string | Array<string>) => Array<string>;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
interface Window {
|
|
19
|
-
// Cached native functions/objects for use in case the page overwrites them.
|
|
20
|
-
// See: `executionContext.cacheNativesOnNewDocument`.
|
|
21
|
-
__nativePromise: PromiseConstructor;
|
|
22
|
-
__nativePerformance: Performance;
|
|
23
|
-
__nativeFetch: typeof fetch,
|
|
24
|
-
__nativeURL: typeof URL;
|
|
25
|
-
__ElementMatches: Element['matches'];
|
|
26
|
-
__HTMLElementBoundingClientRect: HTMLElement['getBoundingClientRect'];
|
|
27
|
-
|
|
28
|
-
/** Used for monitoring long tasks in the test page. */
|
|
29
|
-
____lastLongTask?: number;
|
|
30
|
-
|
|
31
|
-
/** Used by FullPageScreenshot gatherer. */
|
|
32
|
-
__lighthouseNodesDontTouchOrAllVarianceGoesAway: Map<Element, string>;
|
|
33
|
-
__lighthouseExecutionContextUniqueIdentifier?: number;
|
|
34
|
-
|
|
35
|
-
/** Injected into the page when the `--debug` flag is used. */
|
|
36
|
-
continueLighthouseRun(): void;
|
|
37
|
-
|
|
38
|
-
// Not defined in tsc yet: https://github.com/microsoft/TypeScript/issues/40807
|
|
39
|
-
requestIdleCallback(callback: (deadline: {didTimeout: boolean, timeRemaining: () => DOMHighResTimeStamp}) => void, options?: {timeout: number}): number;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/** Make properties K in T optional. */
|
|
43
|
-
type MakeOptional<T, K extends keyof T> = {
|
|
44
|
-
[P in Exclude<keyof T, K>]: T[P]
|
|
45
|
-
} & {
|
|
46
|
-
[P in K]+?: T[P]
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/** An object with the keys in the union K mapped to themselves as values. */
|
|
50
|
-
type SelfMap<K extends string> = {
|
|
51
|
-
[P in K]: P;
|
|
52
|
-
};
|
|
53
|
-
|
|
54
|
-
/** Make optional all properties on T and any properties on object properties of T. */
|
|
55
|
-
type RecursivePartial<T> =
|
|
56
|
-
// Recurse into arrays and tuples: elements aren't (newly) optional, but any properties they have are.
|
|
57
|
-
T extends (infer U)[] ? RecursivePartial<U>[] :
|
|
58
|
-
// Recurse into objects: properties and any of their properties are optional.
|
|
59
|
-
T extends object ? {[P in keyof T]?: RecursivePartial<T[P]>} :
|
|
60
|
-
// Strings, numbers, etc. (terminal types) end here.
|
|
61
|
-
T;
|
|
62
|
-
|
|
63
|
-
/** Recursively makes all properties of T read-only. */
|
|
64
|
-
type Immutable<T> =
|
|
65
|
-
T extends Function ? T :
|
|
66
|
-
T extends Array<infer R> ? ImmutableArray<R> :
|
|
67
|
-
T extends Map<infer K, infer V> ? ImmutableMap<K, V> :
|
|
68
|
-
T extends Set<infer M> ? ImmutableSet<M> :
|
|
69
|
-
T extends object ? ImmutableObject<T> :
|
|
70
|
-
T
|
|
71
|
-
|
|
72
|
-
// Intermediate immutable types. Prefer e.g. Immutable<Set<T>> over direct use.
|
|
73
|
-
type ImmutableArray<T> = ReadonlyArray<Immutable<T>>;
|
|
74
|
-
type ImmutableMap<K, V> = ReadonlyMap<Immutable<K>, Immutable<V>>;
|
|
75
|
-
type ImmutableSet<T> = ReadonlySet<Immutable<T>>;
|
|
76
|
-
type ImmutableObject<T> = {
|
|
77
|
-
readonly [K in keyof T]: Immutable<T[K]>;
|
|
78
|
-
};
|
|
79
|
-
|
|
80
|
-
/**
|
|
81
|
-
* Exclude void from T
|
|
82
|
-
*/
|
|
83
|
-
type NonVoid<T> = T extends void ? never : T;
|
|
84
|
-
|
|
85
|
-
/** Remove properties K from T. */
|
|
86
|
-
type StrictOmit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
|
|
87
|
-
|
|
88
|
-
/** Obtain the type of the first parameter of a function. */
|
|
89
|
-
type FirstParamType<T extends (arg1: any, ...args: any[]) => any> =
|
|
90
|
-
T extends (arg1: infer P, ...args: any[]) => any ? P : never;
|
|
91
|
-
|
|
92
|
-
/**
|
|
93
|
-
* If `S` is a kebab-style string `S`, convert to camelCase.
|
|
94
|
-
*/
|
|
95
|
-
type KebabToCamelCase<S> =
|
|
96
|
-
S extends `${infer T}-${infer U}` ?
|
|
97
|
-
`${T}${Capitalize<KebabToCamelCase<U>>}` :
|
|
98
|
-
S
|
|
99
|
-
|
|
100
|
-
/** Returns T with any kebab-style property names rewritten as camelCase. */
|
|
101
|
-
type CamelCasify<T> = {
|
|
102
|
-
[K in keyof T as KebabToCamelCase<K>]: T[K];
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
|
|
106
11
|
/**
|
|
107
12
|
* Extends the flags in SharedFlagsSettings with flags used to configure the
|
|
108
13
|
* Lighthouse module but will not end up in the Config settings.
|
package/types/global-lh.d.ts
CHANGED
|
@@ -22,6 +22,7 @@ import * as Settings from './lhr/settings';
|
|
|
22
22
|
import Treemap_ from './lhr/treemap';
|
|
23
23
|
import UserFlow_ from './user-flow';
|
|
24
24
|
import Puppeteer_ from './puppeteer';
|
|
25
|
+
import Util_ from './utility-types.js';
|
|
25
26
|
|
|
26
27
|
// Construct hierarchy of global types under the LH namespace.
|
|
27
28
|
declare global {
|
|
@@ -29,6 +30,8 @@ declare global {
|
|
|
29
30
|
export type ArbitraryEqualityMap = ArbitraryEqualityMap_;
|
|
30
31
|
export type NavigationRequestor = string | (() => Promise<void> | void);
|
|
31
32
|
|
|
33
|
+
export import Util = Util_;
|
|
34
|
+
|
|
32
35
|
export import Puppeteer = Puppeteer_;
|
|
33
36
|
|
|
34
37
|
// artifacts.d.ts
|
package/types/node.d.ts
CHANGED
|
@@ -7,6 +7,38 @@
|
|
|
7
7
|
declare global {
|
|
8
8
|
var isDevtools: boolean | undefined;
|
|
9
9
|
var isLightrider: boolean | undefined;
|
|
10
|
+
|
|
11
|
+
// Augment Intl to include
|
|
12
|
+
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/getCanonicalLocales
|
|
13
|
+
namespace Intl {
|
|
14
|
+
var getCanonicalLocales: (locales?: string | Array<string>) => Array<string>;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// Some functions defined in node are stringified and run in the browser.
|
|
18
|
+
// Ensure those functions are working with the correct browser environment.
|
|
19
|
+
interface Window {
|
|
20
|
+
// Cached native functions/objects for use in case the page overwrites them.
|
|
21
|
+
// See: `executionContext.cacheNativesOnNewDocument`.
|
|
22
|
+
__nativePromise: PromiseConstructor;
|
|
23
|
+
__nativePerformance: Performance;
|
|
24
|
+
__nativeFetch: typeof fetch,
|
|
25
|
+
__nativeURL: typeof URL;
|
|
26
|
+
__ElementMatches: Element['matches'];
|
|
27
|
+
__HTMLElementBoundingClientRect: HTMLElement['getBoundingClientRect'];
|
|
28
|
+
|
|
29
|
+
/** Used for monitoring long tasks in the test page. */
|
|
30
|
+
____lastLongTask?: number;
|
|
31
|
+
|
|
32
|
+
/** Used by FullPageScreenshot gatherer. */
|
|
33
|
+
__lighthouseNodesDontTouchOrAllVarianceGoesAway: Map<Element, string>;
|
|
34
|
+
__lighthouseExecutionContextUniqueIdentifier?: number;
|
|
35
|
+
|
|
36
|
+
/** Injected into the page when the `--debug` flag is used. */
|
|
37
|
+
continueLighthouseRun(): void;
|
|
38
|
+
|
|
39
|
+
// Not defined in tsc yet: https://github.com/microsoft/TypeScript/issues/40807
|
|
40
|
+
requestIdleCallback(callback: (deadline: {didTimeout: boolean, timeRemaining: () => DOMHighResTimeStamp}) => void, options?: {timeout: number}): number;
|
|
41
|
+
}
|
|
10
42
|
}
|
|
11
43
|
|
|
12
44
|
export {};
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license Copyright 2022 The Lighthouse Authors. All Rights Reserved.
|
|
3
|
+
* 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
|
|
4
|
+
* 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.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
declare module Util {
|
|
8
|
+
/** Make properties K in T optional. */
|
|
9
|
+
type MakeOptional<T, K extends keyof T> = {
|
|
10
|
+
[P in Exclude<keyof T, K>]: T[P]
|
|
11
|
+
} & {
|
|
12
|
+
[P in K]+?: T[P]
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/** An object with the keys in the union K mapped to themselves as values. */
|
|
16
|
+
type SelfMap<K extends string> = {
|
|
17
|
+
[P in K]: P;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
/** Make optional all properties on T and any properties on object properties of T. */
|
|
21
|
+
type RecursivePartial<T> =
|
|
22
|
+
// Recurse into arrays and tuples: elements aren't (newly) optional, but any properties they have are.
|
|
23
|
+
T extends (infer U)[] ? RecursivePartial<U>[] :
|
|
24
|
+
// Recurse into objects: properties and any of their properties are optional.
|
|
25
|
+
T extends object ? {[P in keyof T]?: RecursivePartial<T[P]>} :
|
|
26
|
+
// Strings, numbers, etc. (terminal types) end here.
|
|
27
|
+
T;
|
|
28
|
+
|
|
29
|
+
/** Recursively makes all properties of T read-only. */
|
|
30
|
+
type Immutable<T> =
|
|
31
|
+
T extends Function ? T :
|
|
32
|
+
T extends Array<infer R> ? ImmutableArray<R> :
|
|
33
|
+
T extends Map<infer K, infer V> ? ImmutableMap<K, V> :
|
|
34
|
+
T extends Set<infer M> ? ImmutableSet<M> :
|
|
35
|
+
T extends object ? ImmutableObject<T> :
|
|
36
|
+
T
|
|
37
|
+
|
|
38
|
+
// Intermediate immutable types. Prefer e.g. Immutable<Set<T>> over direct use.
|
|
39
|
+
type ImmutableArray<T> = ReadonlyArray<Immutable<T>>;
|
|
40
|
+
type ImmutableMap<K, V> = ReadonlyMap<Immutable<K>, Immutable<V>>;
|
|
41
|
+
type ImmutableSet<T> = ReadonlySet<Immutable<T>>;
|
|
42
|
+
type ImmutableObject<T> = {
|
|
43
|
+
readonly [K in keyof T]: Immutable<T[K]>;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Exclude void from T
|
|
48
|
+
*/
|
|
49
|
+
type NonVoid<T> = T extends void ? never : T;
|
|
50
|
+
|
|
51
|
+
/** Remove properties K from T. */
|
|
52
|
+
type StrictOmit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
|
|
53
|
+
|
|
54
|
+
/** Obtain the type of the first parameter of a function. */
|
|
55
|
+
type FirstParamType<T extends (arg1: any, ...args: any[]) => any> =
|
|
56
|
+
T extends (arg1: infer P, ...args: any[]) => any ? P : never;
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* If `S` is a kebab-style string `S`, convert to camelCase.
|
|
60
|
+
*/
|
|
61
|
+
type KebabToCamelCase<S> =
|
|
62
|
+
S extends `${infer T}-${infer U}` ?
|
|
63
|
+
`${T}${Capitalize<KebabToCamelCase<U>>}` :
|
|
64
|
+
S
|
|
65
|
+
|
|
66
|
+
/** Returns T with any kebab-style property names rewritten as camelCase. */
|
|
67
|
+
type CamelCasify<T> = {
|
|
68
|
+
[K in keyof T as KebabToCamelCase<K>]: T[K];
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export default Util;
|