lighthouse 13.5.0-dev.20261021 → 13.5.0-dev.20261022
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 +2 -1
- package/core/computed/unused-css.js +15 -2
- package/core/config/lr-desktop-config.js +13 -0
- package/core/config/lr-mobile-config.js +13 -0
- package/core/gather/gatherers/trace.d.ts +6 -0
- package/core/gather/gatherers/trace.js +56 -16
- package/core/lib/network-request.js +3 -0
- package/package.json +1 -1
- package/readme.md +2 -1
package/cli/cli-flags.js
CHANGED
|
@@ -232,7 +232,8 @@ function getYargsParser(manualArgv) {
|
|
|
232
232
|
'output-path': {
|
|
233
233
|
type: 'string',
|
|
234
234
|
coerce: coerceOutputPath,
|
|
235
|
-
describe: `The file path to output the results. Use 'stdout' to write to stdout.
|
|
235
|
+
describe: `The file or directory path to output the results. Use 'stdout' to write to stdout.
|
|
236
|
+
Directories in the path must already exist. If a directory is specified, the file name will be based on the test URL and date.
|
|
236
237
|
If using JSON output, default is stdout.
|
|
237
238
|
If using HTML or CSV output, default is a file in the working directory with a name based on the test URL and date.
|
|
238
239
|
If using multiple outputs, --output-path is appended with the standard extension for each output type. "reports/my-run" -> "reports/my-run.report.html", "reports/my-run.report.json", etc.
|
|
@@ -66,8 +66,21 @@ class UnusedCSS {
|
|
|
66
66
|
let usedUncompressedBytes = 0;
|
|
67
67
|
const totalUncompressedBytes = stylesheetInfo.content.length;
|
|
68
68
|
|
|
69
|
-
|
|
70
|
-
|
|
69
|
+
// CSS rule usage tracking can return overlapping ranges (e.g. a nested rule inside its
|
|
70
|
+
// parent rule's range) or duplicate ranges (parent rules are emitted once per used child rule).
|
|
71
|
+
// Sort and merge overlapping ranges so used bytes are only counted once.
|
|
72
|
+
const usedRules = stylesheetInfo.usedRules
|
|
73
|
+
.slice()
|
|
74
|
+
.sort((a, b) => a.startOffset - b.startOffset);
|
|
75
|
+
|
|
76
|
+
let lastEndOffset = -1;
|
|
77
|
+
for (const usedRule of usedRules) {
|
|
78
|
+
if (usedRule.endOffset <= lastEndOffset) {
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
const startOffset = Math.max(usedRule.startOffset, lastEndOffset);
|
|
82
|
+
usedUncompressedBytes += usedRule.endOffset - startOffset;
|
|
83
|
+
lastEndOffset = usedRule.endOffset;
|
|
71
84
|
}
|
|
72
85
|
|
|
73
86
|
const compressedSize = estimateCompressedContentSize(
|
|
@@ -24,6 +24,19 @@ const config = {
|
|
|
24
24
|
'bf-cache',
|
|
25
25
|
],
|
|
26
26
|
},
|
|
27
|
+
audits: [
|
|
28
|
+
// Lightrider does not support WebSocket connections.
|
|
29
|
+
// See https://github.com/GoogleChrome/lighthouse/issues/12459
|
|
30
|
+
{
|
|
31
|
+
path: 'errors-in-console',
|
|
32
|
+
options: {
|
|
33
|
+
ignoredPatterns: [
|
|
34
|
+
'ERR_BLOCKED_BY_CLIENT.Inspector',
|
|
35
|
+
/^WebSocket connection to 'wss?:\/\/.*' failed:/,
|
|
36
|
+
],
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
],
|
|
27
40
|
};
|
|
28
41
|
|
|
29
42
|
export default config;
|
|
@@ -24,6 +24,19 @@ const config = {
|
|
|
24
24
|
'bf-cache',
|
|
25
25
|
],
|
|
26
26
|
},
|
|
27
|
+
audits: [
|
|
28
|
+
// Lightrider does not support WebSocket connections.
|
|
29
|
+
// See https://github.com/GoogleChrome/lighthouse/issues/12459
|
|
30
|
+
{
|
|
31
|
+
path: 'errors-in-console',
|
|
32
|
+
options: {
|
|
33
|
+
ignoredPatterns: [
|
|
34
|
+
'ERR_BLOCKED_BY_CLIENT.Inspector',
|
|
35
|
+
/^WebSocket connection to 'wss?:\/\/.*' failed:/,
|
|
36
|
+
],
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
],
|
|
27
40
|
};
|
|
28
41
|
|
|
29
42
|
export default config;
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
export default Trace;
|
|
2
2
|
declare class Trace extends BaseGatherer {
|
|
3
3
|
static getDefaultTraceCategories(): string[];
|
|
4
|
+
/**
|
|
5
|
+
* @param {LH.Gatherer.ProtocolSession} session
|
|
6
|
+
* @param {string} handle
|
|
7
|
+
* @return {Promise<LH.Trace>}
|
|
8
|
+
*/
|
|
9
|
+
static _readTraceFromStream(session: LH.Gatherer.ProtocolSession, handle: string): Promise<LH.Trace>;
|
|
4
10
|
/**
|
|
5
11
|
* @param {LH.Gatherer.ProtocolSession} session
|
|
6
12
|
* @return {Promise<LH.Trace>}
|
|
@@ -73,29 +73,68 @@ class Trace extends BaseGatherer {
|
|
|
73
73
|
|
|
74
74
|
/**
|
|
75
75
|
* @param {LH.Gatherer.ProtocolSession} session
|
|
76
|
+
* @param {string} handle
|
|
76
77
|
* @return {Promise<LH.Trace>}
|
|
77
78
|
*/
|
|
78
|
-
static async
|
|
79
|
+
static async _readTraceFromStream(session, handle) {
|
|
79
80
|
/** @type {Array<LH.TraceEvent>} */
|
|
80
81
|
const traceEvents = [];
|
|
82
|
+
const decoder = new TextDecoder();
|
|
83
|
+
let isEof = false;
|
|
84
|
+
let remainder = '';
|
|
85
|
+
|
|
86
|
+
try {
|
|
87
|
+
while (!isEof) {
|
|
88
|
+
const response = await session.sendCommand('IO.read', {handle});
|
|
89
|
+
isEof = Boolean(response.eof);
|
|
90
|
+
remainder += response.base64Encoded ?
|
|
91
|
+
decoder.decode(Buffer.from(response.data, 'base64'), {stream: !isEof}) :
|
|
92
|
+
response.data;
|
|
93
|
+
|
|
94
|
+
// Perfetto's `ExportJson` serializes each trace event and trailing `metadata`
|
|
95
|
+
// as compact single-line JSON (with string newlines escaped as `\\n`), emitting
|
|
96
|
+
// raw `,\n` only between top-level elements of `traceEvents`.
|
|
97
|
+
// Maintain the invariant that `remainder` always starts with `{"traceEvents":[`.
|
|
98
|
+
const lastEventSep = remainder.lastIndexOf('},\n');
|
|
99
|
+
if (lastEventSep !== -1) {
|
|
100
|
+
const {traceEvents: batch} = JSON.parse(`${remainder.slice(0, lastEventSep + 1)}]}`);
|
|
101
|
+
for (const event of batch) {
|
|
102
|
+
traceEvents.push(event);
|
|
103
|
+
}
|
|
104
|
+
remainder = `{"traceEvents":[${remainder.slice(lastEventSep + 3)}`;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
} finally {
|
|
108
|
+
await session.sendCommand('IO.close', {handle});
|
|
109
|
+
}
|
|
81
110
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
session.on('Tracing.dataCollected', dataListener);
|
|
90
|
-
|
|
91
|
-
return new Promise((resolve, reject) => {
|
|
92
|
-
session.once('Tracing.tracingComplete', _ => {
|
|
93
|
-
session.off('Tracing.dataCollected', dataListener);
|
|
94
|
-
resolve({traceEvents});
|
|
95
|
-
});
|
|
111
|
+
const traceJson = JSON.parse(remainder);
|
|
112
|
+
for (const event of traceJson.traceEvents) {
|
|
113
|
+
traceEvents.push(event);
|
|
114
|
+
}
|
|
115
|
+
traceJson.traceEvents = traceEvents;
|
|
116
|
+
return traceJson;
|
|
117
|
+
}
|
|
96
118
|
|
|
97
|
-
|
|
119
|
+
/**
|
|
120
|
+
* @param {LH.Gatherer.ProtocolSession} session
|
|
121
|
+
* @return {Promise<LH.Trace>}
|
|
122
|
+
*/
|
|
123
|
+
static async endTraceAndCollectEvents(session) {
|
|
124
|
+
/** @type {LH.Crdp.Tracing.TracingCompleteEvent} */
|
|
125
|
+
const completeEvent = await new Promise((resolve, reject) => {
|
|
126
|
+
session.once('Tracing.tracingComplete', resolve);
|
|
127
|
+
session.sendCommand('Tracing.end').catch(err => {
|
|
128
|
+
session.off('Tracing.tracingComplete', resolve);
|
|
129
|
+
reject(err);
|
|
130
|
+
});
|
|
98
131
|
});
|
|
132
|
+
|
|
133
|
+
if (!completeEvent.stream) {
|
|
134
|
+
throw new Error('Tracing.tracingComplete did not return a stream handle');
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return Trace._readTraceFromStream(session, completeEvent.stream);
|
|
99
138
|
}
|
|
100
139
|
|
|
101
140
|
static symbol = Symbol('Trace');
|
|
@@ -115,6 +154,7 @@ class Trace extends BaseGatherer {
|
|
|
115
154
|
await driver.defaultSession.sendCommand('Page.enable');
|
|
116
155
|
await driver.defaultSession.sendCommand('Tracing.start', {
|
|
117
156
|
categories: traceCategories.join(','),
|
|
157
|
+
transferMode: 'ReturnAsStream',
|
|
118
158
|
options: 'sampling-frequency=10000', // 1000 is default and too slow.
|
|
119
159
|
});
|
|
120
160
|
|
|
@@ -345,6 +345,9 @@ class NetworkRequest {
|
|
|
345
345
|
this.networkEndTime = data.timestamp * 1000;
|
|
346
346
|
|
|
347
347
|
this._updateResponseHeadersEndTimeIfNecessary();
|
|
348
|
+
this._updateProtocolForLightrider();
|
|
349
|
+
this._updateTransferSizeForLightrider();
|
|
350
|
+
this._updateTimingsForLightrider();
|
|
348
351
|
}
|
|
349
352
|
|
|
350
353
|
/**
|
package/package.json
CHANGED
package/readme.md
CHANGED
|
@@ -112,7 +112,8 @@ Configuration:
|
|
|
112
112
|
|
|
113
113
|
Output:
|
|
114
114
|
--output Reporter for the results, supports multiple values. choices: "json", "html", "csv" [array] [default: ["html"]]
|
|
115
|
-
--output-path The file path to output the results. Use 'stdout' to write to stdout.
|
|
115
|
+
--output-path The file or directory path to output the results. Use 'stdout' to write to stdout.
|
|
116
|
+
Directories in the path must already exist. If a directory is specified, the file name will be based on the test URL and date.
|
|
116
117
|
If using JSON output, default is stdout.
|
|
117
118
|
If using HTML or CSV output, default is a file in the working directory with a name based on the test URL and date.
|
|
118
119
|
If using multiple outputs, --output-path is appended with the standard extension for each output type. "reports/my-run" -> "reports/my-run.report.html", "reports/my-run.report.json", etc.
|