lighthouse 10.4.0-dev.20230719 → 10.4.0-dev.20230721
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/test/smokehouse/lighthouse-runners/bundle.js +1 -1
- package/cli/test/smokehouse/lighthouse-runners/cli.d.ts +0 -1
- package/cli/test/smokehouse/report-assert.d.ts +0 -1
- package/core/computed/processed-navigation.d.ts +1 -1
- package/core/config/constants.js +1 -1
- package/core/gather/driver/execution-context.js +12 -5
- package/core/gather/driver/navigation.js +1 -4
- package/core/gather/driver/network-monitor.d.ts +1 -0
- package/core/gather/driver/network-monitor.js +1 -0
- package/core/gather/driver.d.ts +5 -2
- package/core/gather/driver.js +11 -1
- package/core/gather/gatherers/full-page-screenshot.js +1 -4
- package/core/gather/gatherers/scripts.d.ts +1 -0
- package/core/gather/gatherers/scripts.js +3 -5
- package/core/gather/gatherers/source-maps.d.ts +6 -18
- package/core/gather/gatherers/source-maps.js +18 -49
- package/core/gather/gatherers/trace-elements.js +7 -3
- package/core/lib/network-recorder.d.ts +2 -1
- package/core/lib/network-recorder.js +1 -0
- package/core/lib/page-functions.d.ts +10 -4
- package/core/lib/page-functions.js +78 -10
- package/package.json +3 -3
- package/readme.md +2 -0
- package/types/gatherer.d.ts +2 -0
- package/types/internal/lighthouse-logger.d.ts +0 -34
|
@@ -17,7 +17,7 @@ import {Worker, isMainThread, parentPort, workerData} from 'worker_threads';
|
|
|
17
17
|
import {once} from 'events';
|
|
18
18
|
|
|
19
19
|
import puppeteer from 'puppeteer-core';
|
|
20
|
-
import ChromeLauncher from 'chrome-launcher';
|
|
20
|
+
import * as ChromeLauncher from 'chrome-launcher';
|
|
21
21
|
|
|
22
22
|
import {LH_ROOT} from '../../../../root.js';
|
|
23
23
|
import {loadArtifacts, saveArtifacts} from '../../../../core/lib/asset-saver.js';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export { ProcessedNavigationComputed as ProcessedNavigation };
|
|
2
2
|
declare const ProcessedNavigationComputed: typeof ProcessedNavigation & {
|
|
3
|
-
request: (dependencies: import("../index.js").
|
|
3
|
+
request: (dependencies: import("../index.js").Artifacts.ProcessedTrace | import("../index.js").Trace, context: import("../../types/utility-types.js").default.ImmutableObject<{
|
|
4
4
|
computedCache: Map<string, import("../lib/arbitrary-equality-map.js").ArbitraryEqualityMap>;
|
|
5
5
|
}>) => Promise<import("../index.js").Artifacts.ProcessedNavigation>;
|
|
6
6
|
};
|
package/core/config/constants.js
CHANGED
|
@@ -17,7 +17,7 @@ const throttling = {
|
|
|
17
17
|
DEVTOOLS_RTT_ADJUSTMENT_FACTOR,
|
|
18
18
|
DEVTOOLS_THROUGHPUT_ADJUSTMENT_FACTOR,
|
|
19
19
|
// These values align with WebPageTest's definition of "Fast 3G"
|
|
20
|
-
// But offer similar
|
|
20
|
+
// But offer similar characteristics to roughly the 75th percentile of 4G connections.
|
|
21
21
|
mobileSlow4G: {
|
|
22
22
|
rttMs: 150,
|
|
23
23
|
throughputKbps: 1.6 * 1024,
|
|
@@ -108,7 +108,7 @@ class ExecutionContext {
|
|
|
108
108
|
${ExecutionContext._cachedNativesPreamble};
|
|
109
109
|
globalThis.__lighthouseExecutionContextUniqueIdentifier =
|
|
110
110
|
${uniqueExecutionContextIdentifier};
|
|
111
|
-
${pageFunctions.
|
|
111
|
+
${pageFunctions.esbuildFunctionWrapperString}
|
|
112
112
|
return new Promise(function (resolve) {
|
|
113
113
|
return Promise.resolve()
|
|
114
114
|
.then(_ => ${expression})
|
|
@@ -277,13 +277,20 @@ class ExecutionContext {
|
|
|
277
277
|
* @return {string}
|
|
278
278
|
*/
|
|
279
279
|
static serializeDeps(deps) {
|
|
280
|
-
deps = [pageFunctions.
|
|
280
|
+
deps = [pageFunctions.esbuildFunctionWrapperString, ...deps || []];
|
|
281
281
|
return deps.map(dep => {
|
|
282
282
|
if (typeof dep === 'function') {
|
|
283
283
|
// esbuild will change the actual function name (ie. function actualName() {})
|
|
284
|
-
// always,
|
|
285
|
-
//
|
|
286
|
-
|
|
284
|
+
// always, and preserve the real name in `actualName.name`.
|
|
285
|
+
// See esbuildFunctionWrapperString.
|
|
286
|
+
const output = dep.toString();
|
|
287
|
+
const runtimeName = pageFunctions.getRuntimeFunctionName(dep);
|
|
288
|
+
if (runtimeName !== dep.name) {
|
|
289
|
+
// In addition to exposing the mangled name, also expose the original as an alias.
|
|
290
|
+
return `${output}; const ${dep.name} = ${runtimeName};`;
|
|
291
|
+
} else {
|
|
292
|
+
return output;
|
|
293
|
+
}
|
|
287
294
|
} else {
|
|
288
295
|
return dep;
|
|
289
296
|
}
|
|
@@ -6,7 +6,6 @@
|
|
|
6
6
|
|
|
7
7
|
import log from 'lighthouse-logger';
|
|
8
8
|
|
|
9
|
-
import {NetworkMonitor} from './network-monitor.js';
|
|
10
9
|
import {waitForFullyLoaded, waitForFrameNavigated, waitForUserToContinue} from './wait-for-condition.js'; // eslint-disable-line max-len
|
|
11
10
|
import * as constants from '../../config/constants.js';
|
|
12
11
|
import * as i18n from '../../lib/i18n/i18n.js';
|
|
@@ -89,10 +88,9 @@ async function gotoURL(driver, requestor, options) {
|
|
|
89
88
|
log.time(status);
|
|
90
89
|
|
|
91
90
|
const session = driver.defaultSession;
|
|
92
|
-
const networkMonitor =
|
|
91
|
+
const networkMonitor = driver.networkMonitor;
|
|
93
92
|
|
|
94
93
|
// Enable the events and network monitor needed to track navigation progress.
|
|
95
|
-
await networkMonitor.enable();
|
|
96
94
|
await session.sendCommand('Page.enable');
|
|
97
95
|
await session.sendCommand('Page.setLifecycleEventsEnabled', {enabled: true});
|
|
98
96
|
|
|
@@ -144,7 +142,6 @@ async function gotoURL(driver, requestor, options) {
|
|
|
144
142
|
|
|
145
143
|
// Bring `Page.navigate` errors back into the promise chain. See https://github.com/GoogleChrome/lighthouse/pull/6739.
|
|
146
144
|
await waitForNavigationTriggered;
|
|
147
|
-
await networkMonitor.disable();
|
|
148
145
|
|
|
149
146
|
if (options.debugNavigation) {
|
|
150
147
|
await waitForUserToContinue(driver);
|
|
@@ -76,6 +76,7 @@ export class NetworkMonitor extends NetworkMonitor_base {
|
|
|
76
76
|
*/
|
|
77
77
|
_emitNetworkStatus(): void;
|
|
78
78
|
}
|
|
79
|
+
import * as LH from '../../../types/lh.js';
|
|
79
80
|
import { NetworkRecorder } from '../../lib/network-recorder.js';
|
|
80
81
|
import { NetworkRequest } from '../../lib/network-request.js';
|
|
81
82
|
export {};
|
|
@@ -13,6 +13,7 @@ import {EventEmitter} from 'events';
|
|
|
13
13
|
|
|
14
14
|
import log from 'lighthouse-logger';
|
|
15
15
|
|
|
16
|
+
import * as LH from '../../../types/lh.js';
|
|
16
17
|
import {NetworkRecorder} from '../../lib/network-recorder.js';
|
|
17
18
|
import {NetworkRequest} from '../../lib/network-request.js';
|
|
18
19
|
import UrlUtils from '../../lib/url-utils.js';
|
package/core/gather/driver.d.ts
CHANGED
|
@@ -7,6 +7,8 @@ export class Driver implements LH.Gatherer.Driver {
|
|
|
7
7
|
_page: import("../../types/puppeteer.js").default.Page;
|
|
8
8
|
/** @type {TargetManager|undefined} */
|
|
9
9
|
_targetManager: TargetManager | undefined;
|
|
10
|
+
/** @type {NetworkMonitor|undefined} */
|
|
11
|
+
_networkMonitor: NetworkMonitor | undefined;
|
|
10
12
|
/** @type {ExecutionContext|undefined} */
|
|
11
13
|
_executionContext: ExecutionContext | undefined;
|
|
12
14
|
/** @type {Fetcher|undefined} */
|
|
@@ -14,9 +16,9 @@ export class Driver implements LH.Gatherer.Driver {
|
|
|
14
16
|
defaultSession: import("../../types/gatherer.js").default.ProtocolSession;
|
|
15
17
|
/** @return {LH.Gatherer.Driver['executionContext']} */
|
|
16
18
|
get executionContext(): ExecutionContext;
|
|
17
|
-
|
|
18
|
-
get fetcher(): Fetcher;
|
|
19
|
+
get fetcher(): any;
|
|
19
20
|
get targetManager(): any;
|
|
21
|
+
get networkMonitor(): any;
|
|
20
22
|
/** @return {Promise<string>} */
|
|
21
23
|
url(): Promise<string>;
|
|
22
24
|
/** @return {Promise<void>} */
|
|
@@ -25,6 +27,7 @@ export class Driver implements LH.Gatherer.Driver {
|
|
|
25
27
|
disconnect(): Promise<void>;
|
|
26
28
|
}
|
|
27
29
|
import { TargetManager } from './driver/target-manager.js';
|
|
30
|
+
import { NetworkMonitor } from './driver/network-monitor.js';
|
|
28
31
|
import { ExecutionContext } from './driver/execution-context.js';
|
|
29
32
|
import { Fetcher } from './fetcher.js';
|
|
30
33
|
//# sourceMappingURL=driver.d.ts.map
|
package/core/gather/driver.js
CHANGED
|
@@ -9,6 +9,7 @@ import log from 'lighthouse-logger';
|
|
|
9
9
|
import {ExecutionContext} from './driver/execution-context.js';
|
|
10
10
|
import {TargetManager} from './driver/target-manager.js';
|
|
11
11
|
import {Fetcher} from './fetcher.js';
|
|
12
|
+
import {NetworkMonitor} from './driver/network-monitor.js';
|
|
12
13
|
|
|
13
14
|
/** @return {*} */
|
|
14
15
|
const throwNotConnectedFn = () => {
|
|
@@ -37,6 +38,8 @@ class Driver {
|
|
|
37
38
|
this._page = page;
|
|
38
39
|
/** @type {TargetManager|undefined} */
|
|
39
40
|
this._targetManager = undefined;
|
|
41
|
+
/** @type {NetworkMonitor|undefined} */
|
|
42
|
+
this._networkMonitor = undefined;
|
|
40
43
|
/** @type {ExecutionContext|undefined} */
|
|
41
44
|
this._executionContext = undefined;
|
|
42
45
|
/** @type {Fetcher|undefined} */
|
|
@@ -51,7 +54,6 @@ class Driver {
|
|
|
51
54
|
return this._executionContext;
|
|
52
55
|
}
|
|
53
56
|
|
|
54
|
-
/** @return {Fetcher} */
|
|
55
57
|
get fetcher() {
|
|
56
58
|
if (!this._fetcher) return throwNotConnectedFn();
|
|
57
59
|
return this._fetcher;
|
|
@@ -62,6 +64,11 @@ class Driver {
|
|
|
62
64
|
return this._targetManager;
|
|
63
65
|
}
|
|
64
66
|
|
|
67
|
+
get networkMonitor() {
|
|
68
|
+
if (!this._networkMonitor) return throwNotConnectedFn();
|
|
69
|
+
return this._networkMonitor;
|
|
70
|
+
}
|
|
71
|
+
|
|
65
72
|
/** @return {Promise<string>} */
|
|
66
73
|
async url() {
|
|
67
74
|
return this._page.url();
|
|
@@ -75,6 +82,8 @@ class Driver {
|
|
|
75
82
|
const cdpSession = await this._page.target().createCDPSession();
|
|
76
83
|
this._targetManager = new TargetManager(cdpSession);
|
|
77
84
|
await this._targetManager.enable();
|
|
85
|
+
this._networkMonitor = new NetworkMonitor(this._targetManager);
|
|
86
|
+
await this._networkMonitor.enable();
|
|
78
87
|
this.defaultSession = this._targetManager.rootSession();
|
|
79
88
|
this._executionContext = new ExecutionContext(this.defaultSession);
|
|
80
89
|
this._fetcher = new Fetcher(this.defaultSession);
|
|
@@ -85,6 +94,7 @@ class Driver {
|
|
|
85
94
|
async disconnect() {
|
|
86
95
|
if (this.defaultSession === throwingSession) return;
|
|
87
96
|
await this._targetManager?.disable();
|
|
97
|
+
await this._networkMonitor?.disable();
|
|
88
98
|
await this.defaultSession.dispose();
|
|
89
99
|
}
|
|
90
100
|
}
|
|
@@ -9,7 +9,6 @@
|
|
|
9
9
|
import BaseGatherer from '../base-gatherer.js';
|
|
10
10
|
import * as emulation from '../../lib/emulation.js';
|
|
11
11
|
import {pageFunctions} from '../../lib/page-functions.js';
|
|
12
|
-
import {NetworkMonitor} from '../driver/network-monitor.js';
|
|
13
12
|
import {waitForNetworkIdle} from '../driver/wait-for-condition.js';
|
|
14
13
|
|
|
15
14
|
// JPEG quality setting
|
|
@@ -89,7 +88,7 @@ class FullPageScreenshot extends BaseGatherer {
|
|
|
89
88
|
const height = Math.min(fullHeight, MAX_WEBP_SIZE);
|
|
90
89
|
|
|
91
90
|
// Setup network monitor before we change the viewport.
|
|
92
|
-
const networkMonitor =
|
|
91
|
+
const networkMonitor = context.driver.networkMonitor;
|
|
93
92
|
const waitForNetworkIdleResult = waitForNetworkIdle(session, networkMonitor, {
|
|
94
93
|
pretendDCLAlreadyFired: true,
|
|
95
94
|
networkQuietThresholdMs: 1000,
|
|
@@ -97,7 +96,6 @@ class FullPageScreenshot extends BaseGatherer {
|
|
|
97
96
|
idleEvent: 'network-critical-idle',
|
|
98
97
|
isIdle: recorder => recorder.isCriticalIdle(),
|
|
99
98
|
});
|
|
100
|
-
await networkMonitor.enable();
|
|
101
99
|
|
|
102
100
|
await session.sendCommand('Emulation.setDeviceMetricsOverride', {
|
|
103
101
|
mobile: deviceMetrics.mobile,
|
|
@@ -113,7 +111,6 @@ class FullPageScreenshot extends BaseGatherer {
|
|
|
113
111
|
waitForNetworkIdleResult.promise,
|
|
114
112
|
]);
|
|
115
113
|
waitForNetworkIdleResult.cancel();
|
|
116
|
-
await networkMonitor.disable();
|
|
117
114
|
|
|
118
115
|
// Now that new resources are (probably) fetched, wait long enough for a layout.
|
|
119
116
|
await context.driver.executionContext.evaluate(waitForDoubleRaf, {args: []});
|
|
@@ -3,6 +3,7 @@ export default Scripts;
|
|
|
3
3
|
* @fileoverview Gets JavaScript file contents.
|
|
4
4
|
*/
|
|
5
5
|
declare class Scripts extends BaseGatherer {
|
|
6
|
+
static symbol: symbol;
|
|
6
7
|
/** @type {LH.Crdp.Debugger.ScriptParsedEvent[]} */
|
|
7
8
|
_scriptParsedEvents: LH.Crdp.Debugger.ScriptParsedEvent[];
|
|
8
9
|
/** @type {Array<string | undefined>} */
|
|
@@ -48,8 +48,11 @@ function isLighthouseRuntimeEvaluateScript(script) {
|
|
|
48
48
|
* @fileoverview Gets JavaScript file contents.
|
|
49
49
|
*/
|
|
50
50
|
class Scripts extends BaseGatherer {
|
|
51
|
+
static symbol = Symbol('Scripts');
|
|
52
|
+
|
|
51
53
|
/** @type {LH.Gatherer.GathererMeta} */
|
|
52
54
|
meta = {
|
|
55
|
+
symbol: Scripts.symbol,
|
|
53
56
|
supportedModes: ['timespan', 'navigation'],
|
|
54
57
|
};
|
|
55
58
|
|
|
@@ -91,11 +94,6 @@ class Scripts extends BaseGatherer {
|
|
|
91
94
|
|
|
92
95
|
session.off('Debugger.scriptParsed', this.onScriptParsed);
|
|
93
96
|
|
|
94
|
-
// Without this line the Debugger domain will be off in FR runner,
|
|
95
|
-
// because only the legacy gatherer has special handling for multiple,
|
|
96
|
-
// overlapped enabled/disable calls.
|
|
97
|
-
await session.sendCommand('Debugger.enable');
|
|
98
|
-
|
|
99
97
|
// If run on a mobile device, be sensitive to memory limitations and only
|
|
100
98
|
// request one at a time.
|
|
101
99
|
this._scriptContents = await runInSeriesOrParallel(
|
|
@@ -3,12 +3,8 @@ export default SourceMaps;
|
|
|
3
3
|
* @fileoverview Gets JavaScript source maps.
|
|
4
4
|
*/
|
|
5
5
|
declare class SourceMaps extends BaseGatherer {
|
|
6
|
-
/** @type {LH.
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* @param {LH.Crdp.Debugger.ScriptParsedEvent} event
|
|
10
|
-
*/
|
|
11
|
-
onScriptParsed(event: LH.Crdp.Debugger.ScriptParsedEvent): void;
|
|
6
|
+
/** @type {LH.Gatherer.GathererMeta<'Scripts'>} */
|
|
7
|
+
meta: LH.Gatherer.GathererMeta<'Scripts'>;
|
|
12
8
|
/**
|
|
13
9
|
* @param {LH.Gatherer.Driver} driver
|
|
14
10
|
* @param {string} sourceMapUrl
|
|
@@ -28,23 +24,15 @@ declare class SourceMaps extends BaseGatherer {
|
|
|
28
24
|
_resolveUrl(url: string, base: string): string | undefined;
|
|
29
25
|
/**
|
|
30
26
|
* @param {LH.Gatherer.Driver} driver
|
|
31
|
-
* @param {LH.
|
|
27
|
+
* @param {LH.Artifacts.Script} script
|
|
32
28
|
* @return {Promise<LH.Artifacts.SourceMap>}
|
|
33
29
|
*/
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* @param {LH.Gatherer.Context} context
|
|
37
|
-
*/
|
|
38
|
-
startSensitiveInstrumentation(context: LH.Gatherer.Context): Promise<void>;
|
|
39
|
-
/**
|
|
40
|
-
* @param {LH.Gatherer.Context} context
|
|
41
|
-
*/
|
|
42
|
-
stopSensitiveInstrumentation(context: LH.Gatherer.Context): Promise<void>;
|
|
30
|
+
_retrieveMapFromScript(driver: LH.Gatherer.Driver, script: LH.Artifacts.Script): Promise<LH.Artifacts.SourceMap>;
|
|
43
31
|
/**
|
|
44
|
-
* @param {LH.Gatherer.Context} context
|
|
32
|
+
* @param {LH.Gatherer.Context<'Scripts'>} context
|
|
45
33
|
* @return {Promise<LH.Artifacts['SourceMaps']>}
|
|
46
34
|
*/
|
|
47
|
-
getArtifact(context: LH.Gatherer.Context): Promise<LH.Artifacts['SourceMaps']>;
|
|
35
|
+
getArtifact(context: LH.Gatherer.Context<'Scripts'>): Promise<LH.Artifacts['SourceMaps']>;
|
|
48
36
|
}
|
|
49
37
|
import BaseGatherer from '../base-gatherer.js';
|
|
50
38
|
//# sourceMappingURL=source-maps.d.ts.map
|
|
@@ -6,23 +6,18 @@
|
|
|
6
6
|
|
|
7
7
|
import SDK from '../../lib/cdt/SDK.js';
|
|
8
8
|
import BaseGatherer from '../base-gatherer.js';
|
|
9
|
+
import Scripts from './scripts.js';
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
12
|
* @fileoverview Gets JavaScript source maps.
|
|
12
13
|
*/
|
|
13
14
|
class SourceMaps extends BaseGatherer {
|
|
14
|
-
/** @type {LH.Gatherer.GathererMeta} */
|
|
15
|
+
/** @type {LH.Gatherer.GathererMeta<'Scripts'>} */
|
|
15
16
|
meta = {
|
|
16
17
|
supportedModes: ['timespan', 'navigation'],
|
|
18
|
+
dependencies: {Scripts: Scripts.symbol},
|
|
17
19
|
};
|
|
18
20
|
|
|
19
|
-
constructor() {
|
|
20
|
-
super();
|
|
21
|
-
/** @type {LH.Crdp.Debugger.ScriptParsedEvent[]} */
|
|
22
|
-
this._scriptParsedEvents = [];
|
|
23
|
-
this.onScriptParsed = this.onScriptParsed.bind(this);
|
|
24
|
-
}
|
|
25
|
-
|
|
26
21
|
/**
|
|
27
22
|
* @param {LH.Gatherer.Driver} driver
|
|
28
23
|
* @param {string} sourceMapUrl
|
|
@@ -45,15 +40,6 @@ class SourceMaps extends BaseGatherer {
|
|
|
45
40
|
return SDK.SourceMap.parseSourceMap(buffer.toString());
|
|
46
41
|
}
|
|
47
42
|
|
|
48
|
-
/**
|
|
49
|
-
* @param {LH.Crdp.Debugger.ScriptParsedEvent} event
|
|
50
|
-
*/
|
|
51
|
-
onScriptParsed(event) {
|
|
52
|
-
if (event.sourceMapURL) {
|
|
53
|
-
this._scriptParsedEvents.push(event);
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
43
|
/**
|
|
58
44
|
* @param {string} url
|
|
59
45
|
* @param {string} base
|
|
@@ -69,27 +55,27 @@ class SourceMaps extends BaseGatherer {
|
|
|
69
55
|
|
|
70
56
|
/**
|
|
71
57
|
* @param {LH.Gatherer.Driver} driver
|
|
72
|
-
* @param {LH.
|
|
58
|
+
* @param {LH.Artifacts.Script} script
|
|
73
59
|
* @return {Promise<LH.Artifacts.SourceMap>}
|
|
74
60
|
*/
|
|
75
|
-
async
|
|
76
|
-
if (!
|
|
61
|
+
async _retrieveMapFromScript(driver, script) {
|
|
62
|
+
if (!script.sourceMapURL) {
|
|
77
63
|
throw new Error('precondition failed: event.sourceMapURL should exist');
|
|
78
64
|
}
|
|
79
65
|
|
|
80
66
|
// `sourceMapURL` is simply the URL found in either a magic comment or an x-sourcemap header.
|
|
81
67
|
// It has not been resolved to a base url.
|
|
82
|
-
const isSourceMapADataUri =
|
|
83
|
-
const scriptUrl =
|
|
68
|
+
const isSourceMapADataUri = script.sourceMapURL.startsWith('data:');
|
|
69
|
+
const scriptUrl = script.name;
|
|
84
70
|
const rawSourceMapUrl = isSourceMapADataUri ?
|
|
85
|
-
|
|
86
|
-
this._resolveUrl(
|
|
71
|
+
script.sourceMapURL :
|
|
72
|
+
this._resolveUrl(script.sourceMapURL, script.name);
|
|
87
73
|
|
|
88
74
|
if (!rawSourceMapUrl) {
|
|
89
75
|
return {
|
|
90
|
-
scriptId:
|
|
76
|
+
scriptId: script.scriptId,
|
|
91
77
|
scriptUrl,
|
|
92
|
-
errorMessage: `Could not resolve map url: ${
|
|
78
|
+
errorMessage: `Could not resolve map url: ${script.sourceMapURL}`,
|
|
93
79
|
};
|
|
94
80
|
}
|
|
95
81
|
|
|
@@ -109,14 +95,14 @@ class SourceMaps extends BaseGatherer {
|
|
|
109
95
|
map.sections = map.sections.filter(section => section.map);
|
|
110
96
|
}
|
|
111
97
|
return {
|
|
112
|
-
scriptId:
|
|
98
|
+
scriptId: script.scriptId,
|
|
113
99
|
scriptUrl,
|
|
114
100
|
sourceMapUrl,
|
|
115
101
|
map,
|
|
116
102
|
};
|
|
117
103
|
} catch (err) {
|
|
118
104
|
return {
|
|
119
|
-
scriptId:
|
|
105
|
+
scriptId: script.scriptId,
|
|
120
106
|
scriptUrl,
|
|
121
107
|
sourceMapUrl,
|
|
122
108
|
errorMessage: err.toString(),
|
|
@@ -125,30 +111,13 @@ class SourceMaps extends BaseGatherer {
|
|
|
125
111
|
}
|
|
126
112
|
|
|
127
113
|
/**
|
|
128
|
-
* @param {LH.Gatherer.Context} context
|
|
129
|
-
*/
|
|
130
|
-
async startSensitiveInstrumentation(context) {
|
|
131
|
-
const session = context.driver.defaultSession;
|
|
132
|
-
session.on('Debugger.scriptParsed', this.onScriptParsed);
|
|
133
|
-
await session.sendCommand('Debugger.enable');
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
/**
|
|
137
|
-
* @param {LH.Gatherer.Context} context
|
|
138
|
-
*/
|
|
139
|
-
async stopSensitiveInstrumentation(context) {
|
|
140
|
-
const session = context.driver.defaultSession;
|
|
141
|
-
await session.sendCommand('Debugger.disable');
|
|
142
|
-
session.off('Debugger.scriptParsed', this.onScriptParsed);
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
/**
|
|
146
|
-
* @param {LH.Gatherer.Context} context
|
|
114
|
+
* @param {LH.Gatherer.Context<'Scripts'>} context
|
|
147
115
|
* @return {Promise<LH.Artifacts['SourceMaps']>}
|
|
148
116
|
*/
|
|
149
117
|
async getArtifact(context) {
|
|
150
|
-
const eventProcessPromises =
|
|
151
|
-
.
|
|
118
|
+
const eventProcessPromises = context.dependencies.Scripts
|
|
119
|
+
.filter(script => script.sourceMapURL)
|
|
120
|
+
.map(script => this._retrieveMapFromScript(context.driver, script));
|
|
152
121
|
return Promise.all(eventProcessPromises);
|
|
153
122
|
}
|
|
154
123
|
}
|
|
@@ -23,6 +23,7 @@ import {ProcessedNavigation} from '../../computed/processed-navigation.js';
|
|
|
23
23
|
import {LighthouseError} from '../../lib/lh-error.js';
|
|
24
24
|
import {Responsiveness} from '../../computed/metrics/responsiveness.js';
|
|
25
25
|
import {CumulativeLayoutShift} from '../../computed/metrics/cumulative-layout-shift.js';
|
|
26
|
+
import {ExecutionContext} from '../driver/execution-context.js';
|
|
26
27
|
|
|
27
28
|
/** @typedef {{nodeId: number, score?: number, animations?: {name?: string, failureReasonsMask?: number, unsupportedProperties?: string[]}[], type?: string}} TraceElementData */
|
|
28
29
|
|
|
@@ -284,12 +285,15 @@ class TraceElements extends BaseGatherer {
|
|
|
284
285
|
try {
|
|
285
286
|
const objectId = await resolveNodeIdToObjectId(session, backendNodeId);
|
|
286
287
|
if (!objectId) continue;
|
|
288
|
+
|
|
289
|
+
const deps = ExecutionContext.serializeDeps([
|
|
290
|
+
pageFunctions.getNodeDetails,
|
|
291
|
+
getNodeDetailsData,
|
|
292
|
+
]);
|
|
287
293
|
response = await session.sendCommand('Runtime.callFunctionOn', {
|
|
288
294
|
objectId,
|
|
289
295
|
functionDeclaration: `function () {
|
|
290
|
-
${
|
|
291
|
-
${getNodeDetailsData.toString()};
|
|
292
|
-
${pageFunctions.getNodeDetails};
|
|
296
|
+
${deps}
|
|
293
297
|
return getNodeDetailsData.call(this);
|
|
294
298
|
}`,
|
|
295
299
|
returnByValue: true,
|
|
@@ -17,7 +17,7 @@ export class NetworkRecorder extends NetworkRecorder_base {
|
|
|
17
17
|
* @param {LH.DevtoolsLog} devtoolsLog
|
|
18
18
|
* @return {Array<LH.Artifacts.NetworkRequest>}
|
|
19
19
|
*/
|
|
20
|
-
static recordsFromLogs(devtoolsLog:
|
|
20
|
+
static recordsFromLogs(devtoolsLog: LH.DevtoolsLog): Array<LH.Artifacts.NetworkRequest>;
|
|
21
21
|
/** @type {NetworkRequest[]} */
|
|
22
22
|
_records: NetworkRequest[];
|
|
23
23
|
/** @type {Map<string, NetworkRequest>} */
|
|
@@ -117,5 +117,6 @@ export class NetworkRecorder extends NetworkRecorder_base {
|
|
|
117
117
|
_findRealRequestAndSetSession(requestId: string, targetType: LH.Protocol.TargetType, sessionId: string | undefined): NetworkRequest | undefined;
|
|
118
118
|
}
|
|
119
119
|
import { NetworkRequest } from './network-request.js';
|
|
120
|
+
import * as LH from '../../types/lh.js';
|
|
120
121
|
export {};
|
|
121
122
|
//# sourceMappingURL=network-recorder.d.ts.map
|
|
@@ -11,7 +11,8 @@ export namespace pageFunctions {
|
|
|
11
11
|
export { wrapRequestIdleCallback };
|
|
12
12
|
export { getBoundingClientRect };
|
|
13
13
|
export { truncate };
|
|
14
|
-
export {
|
|
14
|
+
export { esbuildFunctionWrapperString };
|
|
15
|
+
export { getRuntimeFunctionName };
|
|
15
16
|
}
|
|
16
17
|
/**
|
|
17
18
|
* `typed-query-selector`'s CSS selector parser.
|
|
@@ -50,11 +51,11 @@ declare function wrapRuntimeEvalErrorInBrowser(err?: string | Error | undefined)
|
|
|
50
51
|
};
|
|
51
52
|
/**
|
|
52
53
|
* @template {string} T
|
|
53
|
-
* @param {T} selector Optional simple CSS selector to filter nodes on.
|
|
54
|
+
* @param {T=} selector Optional simple CSS selector to filter nodes on.
|
|
54
55
|
* Combinators are not supported.
|
|
55
56
|
* @return {Array<ParseSelector<T>>}
|
|
56
57
|
*/
|
|
57
|
-
declare function getElementsInDocument<T extends string>(selector
|
|
58
|
+
declare function getElementsInDocument<T extends string>(selector?: T | undefined): import("typed-query-selector/parser").ParseSelector<T, Element>[];
|
|
58
59
|
/**
|
|
59
60
|
* Gets the opening tag text of the given node.
|
|
60
61
|
* @param {Element|ShadowRoot} element
|
|
@@ -158,6 +159,11 @@ declare function truncate(string: string, characterLimit: number): string;
|
|
|
158
159
|
declare namespace truncate {
|
|
159
160
|
function toString(): string;
|
|
160
161
|
}
|
|
161
|
-
declare const
|
|
162
|
+
declare const esbuildFunctionWrapperString: string;
|
|
163
|
+
/**
|
|
164
|
+
* @param {Function} fn
|
|
165
|
+
* @return {string}
|
|
166
|
+
*/
|
|
167
|
+
declare function getRuntimeFunctionName(fn: Function): string;
|
|
162
168
|
export {};
|
|
163
169
|
//# sourceMappingURL=page-functions.d.ts.map
|
|
@@ -4,6 +4,8 @@
|
|
|
4
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
5
|
*/
|
|
6
6
|
|
|
7
|
+
import {createRequire} from 'module';
|
|
8
|
+
|
|
7
9
|
import {Util} from '../../shared/util.js';
|
|
8
10
|
|
|
9
11
|
/**
|
|
@@ -50,7 +52,7 @@ function wrapRuntimeEvalErrorInBrowser(err) {
|
|
|
50
52
|
|
|
51
53
|
/**
|
|
52
54
|
* @template {string} T
|
|
53
|
-
* @param {T} selector Optional simple CSS selector to filter nodes on.
|
|
55
|
+
* @param {T=} selector Optional simple CSS selector to filter nodes on.
|
|
54
56
|
* Combinators are not supported.
|
|
55
57
|
* @return {Array<ParseSelector<T>>}
|
|
56
58
|
*/
|
|
@@ -513,24 +515,89 @@ function truncate(string, characterLimit) {
|
|
|
513
515
|
return Util.truncate(string, characterLimit);
|
|
514
516
|
}
|
|
515
517
|
|
|
518
|
+
function isBundledEnvironment() {
|
|
519
|
+
// If we're in DevTools or LightRider, we are definitely bundled.
|
|
520
|
+
// TODO: refactor and delete `global.isDevtools`.
|
|
521
|
+
if (global.isDevtools || global.isLightrider) return true;
|
|
522
|
+
|
|
523
|
+
const require = createRequire(import.meta.url);
|
|
524
|
+
|
|
525
|
+
try {
|
|
526
|
+
// Not foolproof, but `lighthouse-logger` is a dependency of lighthouse that should always be resolvable.
|
|
527
|
+
// `require.resolve` will only throw in atypical/bundled environments.
|
|
528
|
+
require.resolve('lighthouse-logger');
|
|
529
|
+
return false;
|
|
530
|
+
} catch (err) {
|
|
531
|
+
return true;
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
|
|
516
535
|
// This is to support bundled lighthouse.
|
|
517
536
|
// esbuild calls every function with a builtin `__name` (since we set keepNames: true),
|
|
518
537
|
// whose purpose is to store the real name of the function so that esbuild can rename it to avoid
|
|
519
538
|
// collisions. Anywhere we inject dynamically generated code at runtime for the browser to process,
|
|
520
539
|
// we must manually include this function (because esbuild only does so once at the top scope of
|
|
521
540
|
// the bundle, which is irrelevant for code executed in the browser).
|
|
522
|
-
|
|
541
|
+
// When minified, esbuild will mangle the name of this wrapper function, so we need to determine what it
|
|
542
|
+
// is at runtime in order to recreate it within the page.
|
|
543
|
+
const esbuildFunctionWrapperString = createEsbuildFunctionWrapper();
|
|
523
544
|
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
545
|
+
function createEsbuildFunctionWrapper() {
|
|
546
|
+
if (!isBundledEnvironment()) {
|
|
547
|
+
return '';
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
const functionAsString = (()=>{
|
|
551
|
+
// eslint-disable-next-line no-unused-vars
|
|
552
|
+
const a = ()=>{};
|
|
553
|
+
}).toString()
|
|
554
|
+
// When not minified, esbuild annotates the call to this function wrapper with PURE.
|
|
555
|
+
// We know further that the name of the wrapper function should be `__name`, but let's not
|
|
556
|
+
// hardcode that. Remove the PURE annotation to simplify the regex.
|
|
557
|
+
.replace('/* @__PURE__ */', '');
|
|
558
|
+
const functionStringMatch = functionAsString.match(/=\s*([\w_]+)\(/);
|
|
559
|
+
if (!functionStringMatch) {
|
|
560
|
+
throw new Error('Could not determine esbuild function wrapper name');
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
/**
|
|
564
|
+
* @param {Function} fn
|
|
565
|
+
* @param {string} value
|
|
566
|
+
*/
|
|
567
|
+
const esbuildFunctionWrapper = (fn, value) =>
|
|
568
|
+
Object.defineProperty(fn, 'name', {value, configurable: true});
|
|
569
|
+
const wrapperFnName = functionStringMatch[1];
|
|
570
|
+
return `let ${wrapperFnName}=${esbuildFunctionWrapper}`;
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
/**
|
|
574
|
+
* @param {Function} fn
|
|
575
|
+
* @return {string}
|
|
576
|
+
*/
|
|
577
|
+
function getRuntimeFunctionName(fn) {
|
|
578
|
+
const match = fn.toString().match(/function ([\w$]+)/);
|
|
579
|
+
if (!match) throw new Error(`could not find function name for: ${fn}`);
|
|
580
|
+
return match[1];
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
// We setup a number of our page functions to automatically include their dependencies.
|
|
584
|
+
// Because of esbuild bundling, we must refer to the actual (mangled) runtime function name.
|
|
585
|
+
/** @type {Record<string, string>} */
|
|
586
|
+
const names = {
|
|
587
|
+
truncate: getRuntimeFunctionName(truncate),
|
|
588
|
+
getNodeLabel: getRuntimeFunctionName(getNodeLabel),
|
|
589
|
+
getOuterHTMLSnippet: getRuntimeFunctionName(getOuterHTMLSnippet),
|
|
590
|
+
getNodeDetails: getRuntimeFunctionName(getNodeDetails),
|
|
591
|
+
};
|
|
592
|
+
|
|
593
|
+
truncate.toString = () => `function ${names.truncate}(string, characterLimit) {
|
|
527
594
|
const Util = { ${Util.truncate} };
|
|
528
|
-
return (
|
|
595
|
+
return Util.truncate(string, characterLimit);
|
|
529
596
|
}`;
|
|
530
597
|
|
|
531
598
|
/** @type {string} */
|
|
532
599
|
const getNodeLabelRawString = getNodeLabel.toString();
|
|
533
|
-
getNodeLabel.toString = () => `function getNodeLabel(element) {
|
|
600
|
+
getNodeLabel.toString = () => `function ${names.getNodeLabel}(element) {
|
|
534
601
|
${truncate};
|
|
535
602
|
return (${getNodeLabelRawString})(element);
|
|
536
603
|
}`;
|
|
@@ -538,14 +605,14 @@ getNodeLabel.toString = () => `function getNodeLabel(element) {
|
|
|
538
605
|
/** @type {string} */
|
|
539
606
|
const getOuterHTMLSnippetRawString = getOuterHTMLSnippet.toString();
|
|
540
607
|
// eslint-disable-next-line max-len
|
|
541
|
-
getOuterHTMLSnippet.toString = () => `function getOuterHTMLSnippet(element, ignoreAttrs = [], snippetCharacterLimit = 500) {
|
|
608
|
+
getOuterHTMLSnippet.toString = () => `function ${names.getOuterHTMLSnippet}(element, ignoreAttrs = [], snippetCharacterLimit = 500) {
|
|
542
609
|
${truncate};
|
|
543
610
|
return (${getOuterHTMLSnippetRawString})(element, ignoreAttrs, snippetCharacterLimit);
|
|
544
611
|
}`;
|
|
545
612
|
|
|
546
613
|
/** @type {string} */
|
|
547
614
|
const getNodeDetailsRawString = getNodeDetails.toString();
|
|
548
|
-
getNodeDetails.toString = () => `function getNodeDetails(element) {
|
|
615
|
+
getNodeDetails.toString = () => `function ${names.getNodeDetails}(element) {
|
|
549
616
|
${truncate};
|
|
550
617
|
${getNodePath};
|
|
551
618
|
${getNodeSelector};
|
|
@@ -568,5 +635,6 @@ export const pageFunctions = {
|
|
|
568
635
|
wrapRequestIdleCallback,
|
|
569
636
|
getBoundingClientRect,
|
|
570
637
|
truncate,
|
|
571
|
-
|
|
638
|
+
esbuildFunctionWrapperString,
|
|
639
|
+
getRuntimeFunctionName,
|
|
572
640
|
};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lighthouse",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "10.4.0-dev.
|
|
4
|
+
"version": "10.4.0-dev.20230721",
|
|
5
5
|
"description": "Automated auditing, performance metrics, and best practices for the web.",
|
|
6
6
|
"main": "./core/index.js",
|
|
7
7
|
"bin": {
|
|
@@ -181,7 +181,7 @@
|
|
|
181
181
|
"dependencies": {
|
|
182
182
|
"@sentry/node": "^6.17.4",
|
|
183
183
|
"axe-core": "4.7.2",
|
|
184
|
-
"chrome-launcher": "^0.
|
|
184
|
+
"chrome-launcher": "^1.0.0",
|
|
185
185
|
"configstore": "^5.0.1",
|
|
186
186
|
"csp_evaluator": "1.1.1",
|
|
187
187
|
"devtools-protocol": "0.0.1155343",
|
|
@@ -190,7 +190,7 @@
|
|
|
190
190
|
"intl-messageformat": "^4.4.0",
|
|
191
191
|
"jpeg-js": "^0.4.4",
|
|
192
192
|
"js-library-detector": "^6.6.0",
|
|
193
|
-
"lighthouse-logger": "^
|
|
193
|
+
"lighthouse-logger": "^2.0.1",
|
|
194
194
|
"lighthouse-stack-packs": "1.11.0",
|
|
195
195
|
"lodash": "^4.17.21",
|
|
196
196
|
"lookup-closest-locale": "6.2.0",
|
package/readme.md
CHANGED
|
@@ -337,6 +337,8 @@ This section details services that have integrated Lighthouse data. If you're wo
|
|
|
337
337
|
|
|
338
338
|
* **[Treo](https://treo.sh)** - Treo is Lighthouse as a Service. It provides regression testing, geographical regions, custom networks, and integrations with GitHub & Slack. Treo is a paid product with plans for solo-developers and teams.
|
|
339
339
|
|
|
340
|
+
* **[PageVitals](https://pagevitals.com)** - PageVitals combines Lighthouse, CrUX and real-user monitoring data to monitor the performance of websites. See how your website performs over time and get alerted if it gets too slow. Drill down and find the real cause of any performance issue. PageVitals is a paid product with a free 14-day trial.
|
|
341
|
+
|
|
340
342
|
* **[Alertdesk](https://www.alertdesk.com/)** - Alertdesk is based on Lighthouse and helps you to keep track of your site’s quality & performance. Run daily quality & performance tests from both Mobile and Desktop and dive into the powerful & intuitive reports. You can also monitor your uptime (every minute - 24/7) & domain health. Alertdesk is a paid product with a free 14-day trial.
|
|
341
343
|
|
|
342
344
|
* **[Screpy](https://screpy.com)** - Screpy is a web analysis tool that can analyze all pages of your websites in one dashboard and monitor them with your team. It's powered by Lighthouse and it also includes some different analysis tools (SERP, W3C, Uptime, etc). Screpy has free and paid plans.
|
package/types/gatherer.d.ts
CHANGED
|
@@ -11,6 +11,7 @@ import {NetworkNode as _NetworkNode} from '../core/lib/dependency-graph/network-
|
|
|
11
11
|
import {CPUNode as _CPUNode} from '../core/lib/dependency-graph/cpu-node.js';
|
|
12
12
|
import {Simulator as _Simulator} from '../core/lib/dependency-graph/simulator/simulator.js';
|
|
13
13
|
import {ExecutionContext} from '../core/gather/driver/execution-context.js';
|
|
14
|
+
import {NetworkMonitor} from '../core/gather/driver/network-monitor.js';
|
|
14
15
|
import {Fetcher} from '../core/gather/fetcher.js';
|
|
15
16
|
import {ArbitraryEqualityMap} from '../core/lib/arbitrary-equality-map.js';
|
|
16
17
|
|
|
@@ -48,6 +49,7 @@ declare module Gatherer {
|
|
|
48
49
|
on(event: 'protocolevent', callback: (payload: Protocol.RawEventMessage) => void): void
|
|
49
50
|
off(event: 'protocolevent', callback: (payload: Protocol.RawEventMessage) => void): void
|
|
50
51
|
};
|
|
52
|
+
networkMonitor: NetworkMonitor;
|
|
51
53
|
}
|
|
52
54
|
|
|
53
55
|
interface Context<TDependencies extends DependencyKey = DefaultDependenciesKey> {
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @license Copyright 2017 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 'lighthouse-logger' {
|
|
8
|
-
interface Status {
|
|
9
|
-
msg: string;
|
|
10
|
-
id: string;
|
|
11
|
-
args?: any[];
|
|
12
|
-
}
|
|
13
|
-
export function setLevel(level: string): void;
|
|
14
|
-
export function isVerbose(): boolean;
|
|
15
|
-
export function formatProtocol(prefix: string, data: Object, level?: string): void;
|
|
16
|
-
export function log(title: string, ...args: any[]): void;
|
|
17
|
-
export function warn(title: string, ...args: any[]): void;
|
|
18
|
-
export function error(title: string, ...args: any[]): void;
|
|
19
|
-
export function verbose(title: string, ...args: any[]): void;
|
|
20
|
-
export function time(status: Status, level?: string): void;
|
|
21
|
-
export function timeEnd(status: Status, level?: string): void;
|
|
22
|
-
export function reset(): string;
|
|
23
|
-
export const cross: string;
|
|
24
|
-
export const dim: string;
|
|
25
|
-
export const tick: string;
|
|
26
|
-
export const bold: string;
|
|
27
|
-
export const purple: string;
|
|
28
|
-
export function redify(message: any): string;
|
|
29
|
-
export function greenify(message: any): string;
|
|
30
|
-
/** Retrieves and clears all stored time entries */
|
|
31
|
-
export function takeTimeEntries(): PerformanceEntry[];
|
|
32
|
-
export function getTimeEntries(): PerformanceEntry[];
|
|
33
|
-
export const events: import('events').EventEmitter;
|
|
34
|
-
}
|