lighthouse 12.0.0 → 12.1.0-dev.20240618
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/run.js +5 -38
- package/cli/test/smokehouse/lighthouse-runners/bundle.js +1 -1
- package/cli/test/smokehouse/smokehouse.js +11 -1
- package/core/audits/accessibility/aria-allowed-role.js +7 -9
- package/core/audits/byte-efficiency/byte-efficiency-audit.d.ts +2 -2
- package/core/audits/byte-efficiency/byte-efficiency-audit.js +3 -3
- package/core/audits/byte-efficiency/offscreen-images.js +1 -1
- package/core/audits/byte-efficiency/render-blocking-resources.d.ts +3 -3
- package/core/audits/byte-efficiency/render-blocking-resources.js +10 -10
- package/core/audits/dobetterweb/uses-http2.d.ts +2 -2
- package/core/audits/dobetterweb/uses-http2.js +5 -5
- package/core/audits/long-tasks.d.ts +7 -6
- package/core/audits/long-tasks.js +5 -4
- package/core/audits/metrics/first-meaningful-paint.d.ts +1 -17
- package/core/audits/metrics/first-meaningful-paint.js +5 -47
- package/core/audits/metrics/interactive.d.ts +1 -1
- package/core/audits/metrics/interactive.js +1 -1
- package/core/audits/predictive-perf.js +0 -6
- package/core/audits/prioritize-lcp-image.d.ts +1 -1
- package/core/audits/prioritize-lcp-image.js +2 -2
- package/core/audits/redirects.js +2 -3
- package/core/audits/third-party-facades.js +1 -1
- package/core/audits/third-party-summary.js +1 -1
- package/core/audits/uses-rel-preconnect.js +2 -2
- package/core/audits/uses-rel-preload.js +9 -9
- package/core/computed/critical-request-chains.js +3 -3
- package/core/computed/document-urls.js +3 -2
- package/core/computed/load-simulator.d.ts +4 -4
- package/core/computed/load-simulator.js +3 -54
- package/core/computed/main-resource.js +3 -2
- package/core/computed/metrics/interactive.d.ts +6 -0
- package/core/computed/metrics/interactive.js +2 -2
- package/core/computed/metrics/lantern-first-contentful-paint.d.ts +4 -4
- package/core/computed/metrics/lantern-first-contentful-paint.js +3 -3
- package/core/computed/metrics/lantern-interactive.d.ts +4 -4
- package/core/computed/metrics/lantern-interactive.js +6 -6
- package/core/computed/metrics/lantern-largest-contentful-paint.d.ts +4 -4
- package/core/computed/metrics/lantern-largest-contentful-paint.js +3 -3
- package/core/computed/metrics/lantern-max-potential-fid.d.ts +4 -4
- package/core/computed/metrics/lantern-max-potential-fid.js +3 -3
- package/core/computed/metrics/lantern-metric.d.ts +21 -2
- package/core/computed/metrics/lantern-metric.js +39 -4
- package/core/computed/metrics/lantern-speed-index.d.ts +4 -4
- package/core/computed/metrics/lantern-speed-index.js +4 -4
- package/core/computed/metrics/lantern-total-blocking-time.d.ts +4 -4
- package/core/computed/metrics/lantern-total-blocking-time.js +3 -3
- package/core/computed/metrics/timing-summary.js +0 -6
- package/core/computed/metrics/total-blocking-time.js +3 -1
- package/core/computed/navigation-insights.d.ts +2 -20
- package/core/computed/network-analysis.d.ts +0 -5
- package/core/computed/network-analysis.js +2 -42
- package/core/computed/page-dependency-graph.d.ts +5 -3
- package/core/computed/page-dependency-graph.js +17 -7
- package/core/computed/processed-navigation.d.ts +1 -1
- package/core/computed/tbt-impact-tasks.js +4 -2
- package/core/computed/trace-engine-result.d.ts +0 -11
- package/core/computed/trace-engine-result.js +5 -19
- package/core/config/constants.d.ts +28 -53
- package/core/config/constants.js +2 -43
- package/core/gather/driver/navigation.js +1 -1
- package/core/gather/driver/network-monitor.d.ts +2 -1
- package/core/gather/driver/network-monitor.js +4 -4
- package/core/gather/driver/target-manager.d.ts +1 -1
- package/core/gather/driver.d.ts +0 -12
- package/core/gather/driver.js +1 -26
- package/core/gather/gatherers/inspector-issues.js +1 -0
- package/core/gather/gatherers/root-causes.js +0 -1
- package/core/gather/session.d.ts +5 -3
- package/core/gather/session.js +22 -1
- package/core/lib/asset-saver.d.ts +13 -9
- package/core/lib/asset-saver.js +77 -30
- package/core/lib/bf-cache-strings.js +2 -2
- package/core/lib/lantern/{base-node.d.ts → BaseNode.d.ts} +7 -9
- package/core/lib/lantern/{base-node.js → BaseNode.js} +6 -6
- package/core/lib/lantern/BaseNode.test.js +385 -0
- package/core/lib/lantern/CpuNode.d.ts +43 -0
- package/core/lib/lantern/{cpu-node.js → CpuNode.js} +8 -9
- package/core/lib/lantern/{lantern-error.d.ts → LanternError.d.ts} +1 -1
- package/core/lib/lantern/{metric.d.ts → Metric.d.ts} +23 -25
- package/core/lib/lantern/{metric.js → Metric.js} +14 -16
- package/core/lib/lantern/{network-node.d.ts → NetworkNode.d.ts} +6 -7
- package/core/lib/lantern/{network-node.js → NetworkNode.js} +7 -9
- package/core/lib/lantern/PageDependencyGraph.d.ts +82 -0
- package/core/lib/lantern/{page-dependency-graph.js → PageDependencyGraph.js} +185 -50
- package/core/lib/lantern/PageDependencyGraph.test.js +654 -0
- package/core/{computed/metrics/tbt-utils.d.ts → lib/lantern/TBTUtils.d.ts} +1 -1
- package/core/lib/lantern/TBTUtils.test.d.ts +2 -0
- package/core/lib/lantern/TBTUtils.test.js +130 -0
- package/core/lib/lantern/TraceEngineComputationData.d.ts +25 -0
- package/core/lib/lantern/TraceEngineComputationData.js +466 -0
- package/core/lib/lantern/lantern.d.ts +19 -7
- package/core/lib/lantern/lantern.js +21 -1
- package/core/lib/lantern/metrics/{first-contentful-paint.d.ts → FirstContentfulPaint.d.ts} +11 -11
- package/core/lib/lantern/metrics/{first-contentful-paint.js → FirstContentfulPaint.js} +11 -13
- package/core/lib/lantern/metrics/FirstContentfulPaint.test.js +54 -0
- package/core/lib/lantern/metrics/Interactive.d.ts +20 -0
- package/core/lib/lantern/metrics/{interactive.js → Interactive.js} +23 -26
- package/core/lib/lantern/metrics/Interactive.test.js +56 -0
- package/core/lib/lantern/metrics/LargestContentfulPaint.d.ts +19 -0
- package/core/lib/lantern/metrics/{largest-contentful-paint.js → LargestContentfulPaint.js} +16 -19
- package/core/lib/lantern/metrics/LargestContentfulPaint.test.js +42 -0
- package/core/lib/lantern/metrics/MaxPotentialFID.d.ts +24 -0
- package/core/lib/lantern/metrics/{max-potential-fid.js → MaxPotentialFID.js} +12 -13
- package/core/lib/lantern/metrics/MetricTestUtils.d.ts +19 -0
- package/core/lib/lantern/metrics/MetricTestUtils.js +48 -0
- package/core/lib/lantern/metrics/{speed-index.d.ts → SpeedIndex.d.ts} +7 -13
- package/core/lib/lantern/metrics/{speed-index.js → SpeedIndex.js} +14 -16
- package/core/lib/lantern/metrics/SpeedIndex.test.js +83 -0
- package/core/lib/lantern/metrics/TotalBlockingTime.d.ts +25 -0
- package/core/lib/lantern/metrics/{total-blocking-time.js → TotalBlockingTime.js} +13 -16
- package/core/lib/lantern/metrics/__snapshots__/FirstContentfulPaint.test.js.snap +11 -0
- package/core/lib/lantern/metrics/__snapshots__/Interactive.test.js.snap +17 -0
- package/core/lib/lantern/metrics/metrics.d.ts +9 -0
- package/core/lib/lantern/metrics/metrics.js +16 -0
- package/core/lib/lantern/{simulator/connection-pool.d.ts → simulation/ConnectionPool.d.ts} +15 -15
- package/core/lib/lantern/{simulator/connection-pool.js → simulation/ConnectionPool.js} +25 -25
- package/core/lib/lantern/simulation/ConnectionPool.test.js +195 -0
- package/core/lib/lantern/simulation/Constants.d.ts +52 -0
- package/core/lib/lantern/simulation/Constants.js +48 -0
- package/core/lib/lantern/{simulator/dns-cache.d.ts → simulation/DNSCache.d.ts} +3 -3
- package/core/lib/lantern/{simulator/dns-cache.js → simulation/DNSCache.js} +2 -2
- package/core/lib/lantern/simulation/DNSCache.test.js +72 -0
- package/core/lib/lantern/{simulator/network-analyzer.d.ts → simulation/NetworkAnalyzer.d.ts} +34 -21
- package/core/lib/lantern/{simulator/network-analyzer.js → simulation/NetworkAnalyzer.js} +106 -57
- package/core/lib/lantern/simulation/NetworkAnalyzer.test.js +475 -0
- package/core/lib/lantern/{simulator/simulator-timing-map.d.ts → simulation/SimulationTimingMap.d.ts} +7 -7
- package/core/lib/lantern/{simulator/simulator-timing-map.js → simulation/SimulationTimingMap.js} +5 -6
- package/core/lib/lantern/{simulator/simulator.d.ts → simulation/Simulator.d.ts} +21 -19
- package/core/lib/lantern/{simulator/simulator.js → simulation/Simulator.js} +100 -45
- package/core/lib/lantern/simulation/Simulator.test.js +434 -0
- package/core/lib/lantern/simulation/TCPConnection.test.d.ts +2 -0
- package/core/lib/lantern/simulation/TCPConnection.test.js +374 -0
- package/core/lib/lantern/{simulator/tcp-connection.d.ts → simulation/TcpConnection.d.ts} +2 -2
- package/core/lib/lantern/{simulator/tcp-connection.js → simulation/TcpConnection.js} +1 -1
- package/core/lib/lantern/simulation/simulation.d.ts +21 -0
- package/core/lib/lantern/simulation/simulation.js +28 -0
- package/core/lib/lantern/types/lantern.d.ts +145 -20
- package/core/lib/lantern-trace-saver.d.ts +4 -4
- package/core/lib/lantern-trace-saver.js +4 -4
- package/core/lib/navigation-error.js +3 -3
- package/core/lib/network-recorder.js +2 -2
- package/core/lib/network-request.d.ts +29 -3
- package/core/lib/network-request.js +20 -13
- package/core/lib/tracehouse/trace-processor.d.ts +0 -2
- package/core/lib/tracehouse/trace-processor.js +1 -31
- package/core/lib/traces/metric-trace-events.js +0 -5
- package/dist/report/bundle.esm.js +1 -1
- package/dist/report/flow.js +1 -1
- package/dist/report/standalone.js +1 -1
- package/package.json +14 -13
- package/report/renderer/performance-category-renderer.js +1 -1
- package/shared/localization/locales/ar-XB.json +43 -4
- package/shared/localization/locales/ar.json +43 -4
- package/shared/localization/locales/bg.json +43 -4
- package/shared/localization/locales/ca.json +44 -5
- package/shared/localization/locales/cs.json +43 -4
- package/shared/localization/locales/da.json +43 -4
- package/shared/localization/locales/de.json +43 -4
- package/shared/localization/locales/el.json +43 -4
- package/shared/localization/locales/en-GB.json +42 -3
- package/shared/localization/locales/en-US.json +4 -4
- package/shared/localization/locales/en-XA.json +43 -4
- package/shared/localization/locales/en-XL.json +4 -4
- package/shared/localization/locales/es-419.json +43 -4
- package/shared/localization/locales/es.json +42 -3
- package/shared/localization/locales/fi.json +43 -4
- package/shared/localization/locales/fil.json +42 -3
- package/shared/localization/locales/fr.json +42 -3
- package/shared/localization/locales/he.json +48 -9
- package/shared/localization/locales/hi.json +43 -4
- package/shared/localization/locales/hr.json +43 -4
- package/shared/localization/locales/hu.json +42 -3
- package/shared/localization/locales/id.json +42 -3
- package/shared/localization/locales/it.json +43 -4
- package/shared/localization/locales/ja.json +42 -3
- package/shared/localization/locales/ko.json +43 -4
- package/shared/localization/locales/lt.json +42 -3
- package/shared/localization/locales/lv.json +43 -4
- package/shared/localization/locales/nl.json +44 -5
- package/shared/localization/locales/no.json +42 -3
- package/shared/localization/locales/pl.json +43 -4
- package/shared/localization/locales/pt-PT.json +43 -4
- package/shared/localization/locales/pt.json +43 -4
- package/shared/localization/locales/ro.json +43 -4
- package/shared/localization/locales/ru.json +43 -4
- package/shared/localization/locales/sk.json +43 -4
- package/shared/localization/locales/sl.json +42 -3
- package/shared/localization/locales/sr-Latn.json +43 -4
- package/shared/localization/locales/sr.json +43 -4
- package/shared/localization/locales/sv.json +42 -3
- package/shared/localization/locales/ta.json +42 -3
- package/shared/localization/locales/te.json +43 -4
- package/shared/localization/locales/th.json +44 -5
- package/shared/localization/locales/tr.json +43 -4
- package/shared/localization/locales/uk.json +43 -4
- package/shared/localization/locales/vi.json +43 -4
- package/shared/localization/locales/zh-HK.json +43 -4
- package/shared/localization/locales/zh-TW.json +43 -4
- package/shared/localization/locales/zh.json +43 -4
- package/third-party/chromium-synchronization/inspector-issueAdded-types-test.js +1 -0
- package/tsconfig-all.json +1 -0
- package/tsconfig.json +11 -7
- package/types/artifacts.d.ts +23 -25
- package/types/gatherer.d.ts +2 -3
- package/types/lhr/lhr.d.ts +2 -0
- package/core/computed/metrics/first-meaningful-paint.d.ts +0 -21
- package/core/computed/metrics/first-meaningful-paint.js +0 -44
- package/core/computed/metrics/lantern-first-meaningful-paint.d.ts +0 -25
- package/core/computed/metrics/lantern-first-meaningful-paint.js +0 -43
- package/core/lib/lantern/cpu-node.d.ts +0 -44
- package/core/lib/lantern/metrics/first-meaningful-paint.d.ts +0 -6
- package/core/lib/lantern/metrics/first-meaningful-paint.js +0 -64
- package/core/lib/lantern/metrics/interactive.d.ts +0 -26
- package/core/lib/lantern/metrics/largest-contentful-paint.d.ts +0 -19
- package/core/lib/lantern/metrics/max-potential-fid.d.ts +0 -30
- package/core/lib/lantern/metrics/total-blocking-time.d.ts +0 -31
- package/core/lib/lantern/page-dependency-graph.d.ts +0 -58
- /package/core/lib/lantern/{lantern-error.js → LanternError.js} +0 -0
- /package/core/{computed/metrics/tbt-utils.js → lib/lantern/TBTUtils.js} +0 -0
|
@@ -114,7 +114,6 @@ class RootCauses extends BaseGatherer {
|
|
|
114
114
|
const rootCausesEngine = new TraceEngine.RootCauses(protocolInterface);
|
|
115
115
|
const layoutShiftEvents = traceParsedData.LayoutShifts.clusters.flatMap(c => c.events);
|
|
116
116
|
for (const event of layoutShiftEvents) {
|
|
117
|
-
// @ts-expect-error The data we do get in the trace processor is still enough here
|
|
118
117
|
const r = await rootCausesEngine.layoutShifts.rootCausesForEvent(traceParsedData, event);
|
|
119
118
|
if (!r) continue;
|
|
120
119
|
|
package/core/gather/session.d.ts
CHANGED
|
@@ -17,7 +17,8 @@ export class ProtocolSession extends ProtocolSession_base implements LH.Gatherer
|
|
|
17
17
|
* @param {E} method
|
|
18
18
|
* @param {LH.CrdpEvents[E]} params
|
|
19
19
|
*/
|
|
20
|
-
_handleProtocolEvent<E extends keyof import("
|
|
20
|
+
_handleProtocolEvent<E extends keyof import("devtools-protocol/types/protocol-mapping.js").ProtocolMapping.Events>(method: E, ...params: import("devtools-protocol/types/protocol-mapping.js").ProtocolMapping.Events[E]): void;
|
|
21
|
+
_targetCrashedPromise: Promise<never>;
|
|
21
22
|
id(): string;
|
|
22
23
|
/** @param {LH.Crdp.Target.TargetInfo} targetInfo */
|
|
23
24
|
setTargetInfo(targetInfo: LH.Crdp.Target.TargetInfo): void;
|
|
@@ -39,7 +40,7 @@ export class ProtocolSession extends ProtocolSession_base implements LH.Gatherer
|
|
|
39
40
|
* @param {LH.CrdpCommands[C]['paramsType']} params
|
|
40
41
|
* @return {Promise<LH.CrdpCommands[C]['returnType']>}
|
|
41
42
|
*/
|
|
42
|
-
sendCommand<C extends keyof import("
|
|
43
|
+
sendCommand<C extends keyof import("devtools-protocol/types/protocol-mapping.js").ProtocolMapping.Commands>(method: C, ...params: import("devtools-protocol/types/protocol-mapping.js").ProtocolMapping.Commands[C]["paramsType"]): Promise<import("devtools-protocol/types/protocol-mapping.js").ProtocolMapping.Commands[C]["returnType"]>;
|
|
43
44
|
/**
|
|
44
45
|
* Send and if there's an error response, do not reject.
|
|
45
46
|
* @template {keyof LH.CrdpCommands} C
|
|
@@ -47,12 +48,13 @@ export class ProtocolSession extends ProtocolSession_base implements LH.Gatherer
|
|
|
47
48
|
* @param {LH.CrdpCommands[C]['paramsType']} params
|
|
48
49
|
* @return {Promise<void>}
|
|
49
50
|
*/
|
|
50
|
-
sendCommandAndIgnore<C_1 extends keyof import("
|
|
51
|
+
sendCommandAndIgnore<C_1 extends keyof import("devtools-protocol/types/protocol-mapping.js").ProtocolMapping.Commands>(method: C_1, ...params: import("devtools-protocol/types/protocol-mapping.js").ProtocolMapping.Commands[C_1]["paramsType"]): Promise<void>;
|
|
51
52
|
/**
|
|
52
53
|
* Disposes of a session so that it can no longer talk to Chrome.
|
|
53
54
|
* @return {Promise<void>}
|
|
54
55
|
*/
|
|
55
56
|
dispose(): Promise<void>;
|
|
57
|
+
onCrashPromise(): Promise<never>;
|
|
56
58
|
}
|
|
57
59
|
export {};
|
|
58
60
|
//# sourceMappingURL=session.d.ts.map
|
package/core/gather/session.js
CHANGED
|
@@ -44,6 +44,22 @@ class ProtocolSession extends CrdpEventEmitter {
|
|
|
44
44
|
this._handleProtocolEvent = this._handleProtocolEvent.bind(this);
|
|
45
45
|
// @ts-expect-error Puppeteer expects the handler params to be type `unknown`
|
|
46
46
|
this._cdpSession.on('*', this._handleProtocolEvent);
|
|
47
|
+
|
|
48
|
+
// If the target crashes, we can't continue gathering.
|
|
49
|
+
// FWIW, if the target unexpectedly detaches (eg the user closed the tab), pptr will
|
|
50
|
+
// catch that and reject in this._cdpSession.send, which is caught by us.
|
|
51
|
+
/** @param {Error} _ */
|
|
52
|
+
let rej = _ => {}; // Poor man's Promise.withResolvers()
|
|
53
|
+
this._targetCrashedPromise = /** @type {Promise<never>} */ (
|
|
54
|
+
new Promise((_, theRej) => rej = theRej));
|
|
55
|
+
this.on('Inspector.targetCrashed', async () => {
|
|
56
|
+
log.error('TargetManager', 'Inspector.targetCrashed');
|
|
57
|
+
// Manually detach so no more CDP traffic is attempted.
|
|
58
|
+
// Don't await, else our rejection will be a 'Target closed' protocol error on cross-talk
|
|
59
|
+
// CDP calls.
|
|
60
|
+
void this.dispose();
|
|
61
|
+
rej(new LighthouseError(LighthouseError.errors.TARGET_CRASHED));
|
|
62
|
+
});
|
|
47
63
|
}
|
|
48
64
|
|
|
49
65
|
id() {
|
|
@@ -114,7 +130,8 @@ class ProtocolSession extends CrdpEventEmitter {
|
|
|
114
130
|
log.formatProtocol('method <= browser ERR', {method}, 'error');
|
|
115
131
|
throw LighthouseError.fromProtocolMessage(method, error);
|
|
116
132
|
});
|
|
117
|
-
const resultWithTimeoutPromise =
|
|
133
|
+
const resultWithTimeoutPromise =
|
|
134
|
+
Promise.race([resultPromise, timeoutPromise, this._targetCrashedPromise]);
|
|
118
135
|
|
|
119
136
|
return resultWithTimeoutPromise.finally(() => {
|
|
120
137
|
if (timeout) clearTimeout(timeout);
|
|
@@ -142,6 +159,10 @@ class ProtocolSession extends CrdpEventEmitter {
|
|
|
142
159
|
this._cdpSession.off('*', this._handleProtocolEvent);
|
|
143
160
|
await this._cdpSession.detach().catch(e => log.verbose('session', 'detach failed', e.message));
|
|
144
161
|
}
|
|
162
|
+
|
|
163
|
+
onCrashPromise() {
|
|
164
|
+
return this._targetCrashedPromise;
|
|
165
|
+
}
|
|
145
166
|
}
|
|
146
167
|
|
|
147
168
|
export {ProtocolSession};
|
|
@@ -4,12 +4,15 @@ export type PreparedAssets = {
|
|
|
4
4
|
};
|
|
5
5
|
/**
|
|
6
6
|
* Save artifacts object mostly to single file located at basePath/artifacts.json.
|
|
7
|
-
* Also save the traces & devtoolsLogs to their own files
|
|
7
|
+
* Also save the traces & devtoolsLogs to their own files, with optional compression.
|
|
8
8
|
* @param {LH.Artifacts} artifacts
|
|
9
9
|
* @param {string} basePath
|
|
10
|
+
* @param {{gzip?: boolean}} options
|
|
10
11
|
* @return {Promise<void>}
|
|
11
12
|
*/
|
|
12
|
-
export function saveArtifacts(artifacts: LH.Artifacts, basePath: string
|
|
13
|
+
export function saveArtifacts(artifacts: LH.Artifacts, basePath: string, options?: {
|
|
14
|
+
gzip?: boolean;
|
|
15
|
+
}): Promise<void>;
|
|
13
16
|
/**
|
|
14
17
|
* Saves flow artifacts with the following file structure:
|
|
15
18
|
* flow/ -- Directory specified by `basePath`.
|
|
@@ -32,11 +35,6 @@ export function saveFlowArtifacts(flowArtifacts: LH.UserFlow.FlowArtifacts, base
|
|
|
32
35
|
* @param {string} basePath
|
|
33
36
|
*/
|
|
34
37
|
export function saveLhr(lhr: LH.Result, basePath: string): void;
|
|
35
|
-
/**
|
|
36
|
-
* @typedef {object} PreparedAssets
|
|
37
|
-
* @property {LH.Trace} [traceData]
|
|
38
|
-
* @property {LH.DevtoolsLog} [devtoolsLog]
|
|
39
|
-
*/
|
|
40
38
|
/**
|
|
41
39
|
* Load artifacts object from files located within basePath
|
|
42
40
|
* Also save the traces to their own files
|
|
@@ -67,16 +65,22 @@ export function prepareAssets(artifacts: LH.Artifacts, audits?: Record<string, i
|
|
|
67
65
|
* Save a trace as JSON by streaming to disk at traceFilename.
|
|
68
66
|
* @param {LH.Trace} traceData
|
|
69
67
|
* @param {string} traceFilename
|
|
68
|
+
* @param {{gzip?: boolean}=} options
|
|
70
69
|
* @return {Promise<void>}
|
|
71
70
|
*/
|
|
72
|
-
export function saveTrace(traceData: LH.Trace, traceFilename: string
|
|
71
|
+
export function saveTrace(traceData: LH.Trace, traceFilename: string, options?: {
|
|
72
|
+
gzip?: boolean;
|
|
73
|
+
} | undefined): Promise<void>;
|
|
73
74
|
/**
|
|
74
75
|
* Save a devtoolsLog as JSON by streaming to disk at devtoolLogFilename.
|
|
75
76
|
* @param {LH.DevtoolsLog} devtoolsLog
|
|
76
77
|
* @param {string} devtoolLogFilename
|
|
78
|
+
* @param {{gzip?: boolean}=} options
|
|
77
79
|
* @return {Promise<void>}
|
|
78
80
|
*/
|
|
79
|
-
export function saveDevtoolsLog(devtoolsLog: import("../index.js").DevtoolsLog, devtoolLogFilename: string
|
|
81
|
+
export function saveDevtoolsLog(devtoolsLog: import("../index.js").DevtoolsLog, devtoolLogFilename: string, options?: {
|
|
82
|
+
gzip?: boolean;
|
|
83
|
+
} | undefined): Promise<void>;
|
|
80
84
|
/**
|
|
81
85
|
* @param {LH.DevtoolsLog} devtoolsLog
|
|
82
86
|
* @param {string} outputPath
|
package/core/lib/asset-saver.js
CHANGED
|
@@ -8,10 +8,11 @@ import fs from 'fs';
|
|
|
8
8
|
import path from 'path';
|
|
9
9
|
import stream from 'stream';
|
|
10
10
|
import url from 'url';
|
|
11
|
+
import {createGzip, gunzipSync} from 'zlib';
|
|
11
12
|
|
|
12
13
|
import log from 'lighthouse-logger';
|
|
13
14
|
|
|
14
|
-
import
|
|
15
|
+
import * as Lantern from './lantern/lantern.js';
|
|
15
16
|
import lanternTraceSaver from './lantern-trace-saver.js';
|
|
16
17
|
import {MetricTraceEvents} from './traces/metric-trace-events.js';
|
|
17
18
|
import {NetworkAnalysis} from '../computed/network-analysis.js';
|
|
@@ -33,6 +34,46 @@ const stepDirectoryRegex = /^step(\d+)$/;
|
|
|
33
34
|
* @property {LH.DevtoolsLog} [devtoolsLog]
|
|
34
35
|
*/
|
|
35
36
|
|
|
37
|
+
/**
|
|
38
|
+
* @param {import('stream').PipelineSource<any>} contents
|
|
39
|
+
* @param {string} path
|
|
40
|
+
* @param {boolean} gzip
|
|
41
|
+
*/
|
|
42
|
+
async function writeJson(contents, path, gzip) {
|
|
43
|
+
const writeStream = fs.createWriteStream(gzip ? path + '.gz' : path);
|
|
44
|
+
if (gzip) {
|
|
45
|
+
await stream.promises.pipeline(contents, createGzip(), writeStream);
|
|
46
|
+
} else {
|
|
47
|
+
await stream.promises.pipeline(contents, writeStream);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Prefers reading a gzipped file (.gz) if present.
|
|
53
|
+
* @param {string} filename
|
|
54
|
+
* @param {(this: any, key: string, value: any) => any=} reviver
|
|
55
|
+
*/
|
|
56
|
+
function readJson(filename, reviver) {
|
|
57
|
+
if (fs.existsSync(filename + '.gz')) {
|
|
58
|
+
filename = filename + '.gz';
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (!filename.endsWith('.json.gz')) {
|
|
62
|
+
return JSON.parse(fs.readFileSync(filename, 'utf8'), reviver);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const buffer = gunzipSync(fs.readFileSync(filename));
|
|
66
|
+
return JSON.parse(buffer.toString('utf8'), reviver);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* @param {string} filename
|
|
71
|
+
* @param {string} suffix
|
|
72
|
+
* @returns
|
|
73
|
+
*/
|
|
74
|
+
function endsWithSuffix(filename, suffix) {
|
|
75
|
+
return filename.endsWith(suffix) || filename.endsWith(suffix + '.gz');
|
|
76
|
+
}
|
|
36
77
|
|
|
37
78
|
/**
|
|
38
79
|
* Load artifacts object from files located within basePath
|
|
@@ -48,16 +89,15 @@ function loadArtifacts(basePath) {
|
|
|
48
89
|
}
|
|
49
90
|
|
|
50
91
|
// load artifacts.json using a reviver to deserialize any LighthouseErrors in artifacts.
|
|
51
|
-
const artifactsStr = fs.readFileSync(path.join(basePath, artifactsFilename), 'utf8');
|
|
52
92
|
/** @type {LH.Artifacts} */
|
|
53
|
-
const artifacts =
|
|
93
|
+
const artifacts = readJson(path.join(basePath, artifactsFilename), LighthouseError.parseReviver);
|
|
54
94
|
|
|
55
95
|
const filenames = fs.readdirSync(basePath);
|
|
56
96
|
|
|
57
|
-
filenames.filter(f => f
|
|
97
|
+
filenames.filter(f => endsWithSuffix(f, devtoolsLogSuffix)).forEach(filename => {
|
|
58
98
|
if (!artifacts.devtoolsLogs) artifacts.devtoolsLogs = {};
|
|
59
|
-
const prefix = filename.replace(devtoolsLogSuffix, '');
|
|
60
|
-
const devtoolsLog =
|
|
99
|
+
const prefix = filename.replace(devtoolsLogSuffix + '.gz', '').replace(devtoolsLogSuffix, '');
|
|
100
|
+
const devtoolsLog = readJson(path.join(basePath, filename));
|
|
61
101
|
artifacts.devtoolsLogs[prefix] = devtoolsLog;
|
|
62
102
|
if (prefix === defaultPrefix) {
|
|
63
103
|
artifacts.DevtoolsLog = devtoolsLog;
|
|
@@ -67,11 +107,10 @@ function loadArtifacts(basePath) {
|
|
|
67
107
|
}
|
|
68
108
|
});
|
|
69
109
|
|
|
70
|
-
filenames.filter(f => f
|
|
110
|
+
filenames.filter(f => endsWithSuffix(f, traceSuffix)).forEach(filename => {
|
|
71
111
|
if (!artifacts.traces) artifacts.traces = {};
|
|
72
|
-
const
|
|
73
|
-
const
|
|
74
|
-
const prefix = filename.replace(traceSuffix, '');
|
|
112
|
+
const trace = readJson(path.join(basePath, filename));
|
|
113
|
+
const prefix = filename.replace(traceSuffix + '.gz', '').replace(traceSuffix, '');
|
|
75
114
|
artifacts.traces[prefix] = Array.isArray(trace) ? {traceEvents: trace} : trace;
|
|
76
115
|
if (prefix === defaultPrefix) {
|
|
77
116
|
artifacts.Trace = artifacts.traces[prefix];
|
|
@@ -200,12 +239,13 @@ async function saveFlowArtifacts(flowArtifacts, basePath) {
|
|
|
200
239
|
|
|
201
240
|
/**
|
|
202
241
|
* Save artifacts object mostly to single file located at basePath/artifacts.json.
|
|
203
|
-
* Also save the traces & devtoolsLogs to their own files
|
|
242
|
+
* Also save the traces & devtoolsLogs to their own files, with optional compression.
|
|
204
243
|
* @param {LH.Artifacts} artifacts
|
|
205
244
|
* @param {string} basePath
|
|
245
|
+
* @param {{gzip?: boolean}} options
|
|
206
246
|
* @return {Promise<void>}
|
|
207
247
|
*/
|
|
208
|
-
async function saveArtifacts(artifacts, basePath) {
|
|
248
|
+
async function saveArtifacts(artifacts, basePath, options = {}) {
|
|
209
249
|
const status = {msg: 'Saving artifacts', id: 'lh:assetSaver:saveArtifacts'};
|
|
210
250
|
log.time(status);
|
|
211
251
|
fs.mkdirSync(basePath, {recursive: true});
|
|
@@ -213,8 +253,11 @@ async function saveArtifacts(artifacts, basePath) {
|
|
|
213
253
|
// Delete any previous artifacts in this directory.
|
|
214
254
|
const filenames = fs.readdirSync(basePath);
|
|
215
255
|
for (const filename of filenames) {
|
|
216
|
-
|
|
217
|
-
|
|
256
|
+
const isPreviousFile =
|
|
257
|
+
filename.endsWith(traceSuffix) || filename.endsWith(devtoolsLogSuffix) ||
|
|
258
|
+
filename.endsWith(traceSuffix + '.gz') || filename.endsWith(devtoolsLogSuffix + '.gz') ||
|
|
259
|
+
filename === artifactsFilename || filename === artifactsFilename + '.gz';
|
|
260
|
+
if (isPreviousFile) {
|
|
218
261
|
fs.unlinkSync(`${basePath}/${filename}`);
|
|
219
262
|
}
|
|
220
263
|
}
|
|
@@ -234,24 +277,30 @@ async function saveArtifacts(artifacts, basePath) {
|
|
|
234
277
|
} = artifacts;
|
|
235
278
|
|
|
236
279
|
if (Trace) {
|
|
237
|
-
await saveTrace(Trace, `${basePath}/${defaultPrefix}${traceSuffix}
|
|
280
|
+
await saveTrace(Trace, `${basePath}/${defaultPrefix}${traceSuffix}`, options);
|
|
238
281
|
}
|
|
239
282
|
|
|
240
283
|
if (TraceError) {
|
|
241
|
-
await saveTrace(TraceError, `${basePath}/${errorPrefix}${traceSuffix}
|
|
284
|
+
await saveTrace(TraceError, `${basePath}/${errorPrefix}${traceSuffix}`, options);
|
|
242
285
|
}
|
|
243
286
|
|
|
244
287
|
if (DevtoolsLog) {
|
|
245
|
-
await saveDevtoolsLog(
|
|
288
|
+
await saveDevtoolsLog(
|
|
289
|
+
DevtoolsLog, `${basePath}/${defaultPrefix}${devtoolsLogSuffix}`, options);
|
|
246
290
|
}
|
|
247
291
|
|
|
248
292
|
if (DevtoolsLogError) {
|
|
249
|
-
await saveDevtoolsLog(
|
|
293
|
+
await saveDevtoolsLog(
|
|
294
|
+
DevtoolsLogError, `${basePath}/${errorPrefix}${devtoolsLogSuffix}`, options);
|
|
250
295
|
}
|
|
251
296
|
|
|
252
297
|
// save everything else, using a replacer to serialize LighthouseErrors in the artifacts.
|
|
253
|
-
const restArtifactsString = JSON.stringify(restArtifacts, stringifyReplacer, 2)
|
|
254
|
-
|
|
298
|
+
const restArtifactsString = JSON.stringify(restArtifacts, stringifyReplacer, 2);
|
|
299
|
+
await writeJson(function* () {
|
|
300
|
+
yield restArtifactsString;
|
|
301
|
+
yield '\n';
|
|
302
|
+
}, `${basePath}/${artifactsFilename}`, !!options.gzip);
|
|
303
|
+
|
|
255
304
|
log.log('Artifacts saved to disk in folder:', basePath);
|
|
256
305
|
log.timeEnd(status);
|
|
257
306
|
}
|
|
@@ -371,28 +420,26 @@ function* traceJsonGenerator(traceData) {
|
|
|
371
420
|
* Save a trace as JSON by streaming to disk at traceFilename.
|
|
372
421
|
* @param {LH.Trace} traceData
|
|
373
422
|
* @param {string} traceFilename
|
|
423
|
+
* @param {{gzip?: boolean}=} options
|
|
374
424
|
* @return {Promise<void>}
|
|
375
425
|
*/
|
|
376
|
-
|
|
426
|
+
function saveTrace(traceData, traceFilename, options = {}) {
|
|
377
427
|
const traceIter = traceJsonGenerator(traceData);
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
return stream.promises.pipeline(traceIter, writeStream);
|
|
428
|
+
return writeJson(traceIter, traceFilename, !!options.gzip);
|
|
381
429
|
}
|
|
382
430
|
|
|
383
431
|
/**
|
|
384
432
|
* Save a devtoolsLog as JSON by streaming to disk at devtoolLogFilename.
|
|
385
433
|
* @param {LH.DevtoolsLog} devtoolsLog
|
|
386
434
|
* @param {string} devtoolLogFilename
|
|
435
|
+
* @param {{gzip?: boolean}=} options
|
|
387
436
|
* @return {Promise<void>}
|
|
388
437
|
*/
|
|
389
|
-
function saveDevtoolsLog(devtoolsLog, devtoolLogFilename) {
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
return stream.promises.pipeline(function* () {
|
|
438
|
+
function saveDevtoolsLog(devtoolsLog, devtoolLogFilename, options = {}) {
|
|
439
|
+
return writeJson(function* () {
|
|
393
440
|
yield* arrayOfObjectsJsonGenerator(devtoolsLog);
|
|
394
441
|
yield '\n';
|
|
395
|
-
},
|
|
442
|
+
}, devtoolLogFilename, !!options.gzip);
|
|
396
443
|
}
|
|
397
444
|
|
|
398
445
|
/**
|
|
@@ -402,7 +449,7 @@ function saveDevtoolsLog(devtoolsLog, devtoolLogFilename) {
|
|
|
402
449
|
async function saveLanternDebugTraces(pathWithBasename) {
|
|
403
450
|
if (!process.env.LANTERN_DEBUG) return;
|
|
404
451
|
|
|
405
|
-
for (const [label, nodeTimings] of Simulator.ALL_NODE_TIMINGS) {
|
|
452
|
+
for (const [label, nodeTimings] of Lantern.Simulation.Simulator.ALL_NODE_TIMINGS) {
|
|
406
453
|
if (lanternTraceSaver.simulationNamesToIgnore.includes(label)) continue;
|
|
407
454
|
|
|
408
455
|
const traceFilename = `${pathWithBasename}-${label}${traceSuffix}`;
|
|
@@ -380,7 +380,7 @@ const UIStrings = {
|
|
|
380
380
|
/**
|
|
381
381
|
* @description Description text for not restored reason NotMainFrame.
|
|
382
382
|
*/
|
|
383
|
-
contentWebAuthenticationAPI: 'Pages that use
|
|
383
|
+
contentWebAuthenticationAPI: 'Pages that use WebAuthentication API are not eligible for back/forward cache.',
|
|
384
384
|
/**
|
|
385
385
|
* @description Description text for not restored reason NotMainFrame.
|
|
386
386
|
*/
|
|
@@ -674,4 +674,4 @@ const NotRestoredReasonDescription = {
|
|
|
674
674
|
export {
|
|
675
675
|
NotRestoredReasonDescription,
|
|
676
676
|
UIStrings,
|
|
677
|
-
};
|
|
677
|
+
};
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* A union of all types derived from BaseNode, allowing type check discrimination
|
|
3
3
|
* based on `node.type`. If a new node type is created, it should be added here.
|
|
4
4
|
*/
|
|
5
|
-
export type Node<T = any> = import('./
|
|
5
|
+
export type Node<T = any> = import('./CpuNode.js').CPUNode<T> | import('./NetworkNode.js').NetworkNode<T>;
|
|
6
6
|
/**
|
|
7
7
|
* @license
|
|
8
8
|
* Copyright 2017 Google LLC
|
|
@@ -12,7 +12,7 @@ export type Node<T = any> = import('./cpu-node.js').CPUNode<T> | import('./netwo
|
|
|
12
12
|
* A union of all types derived from BaseNode, allowing type check discrimination
|
|
13
13
|
* based on `node.type`. If a new node type is created, it should be added here.
|
|
14
14
|
* @template [T=any]
|
|
15
|
-
* @typedef {import('./
|
|
15
|
+
* @typedef {import('./CpuNode.js').CPUNode<T> | import('./NetworkNode.js').NetworkNode<T>} Node
|
|
16
16
|
*/
|
|
17
17
|
/**
|
|
18
18
|
* @fileoverview This class encapsulates logic for handling resources and tasks used to model the
|
|
@@ -30,6 +30,10 @@ export type Node<T = any> = import('./cpu-node.js').CPUNode<T> | import('./netwo
|
|
|
30
30
|
* @template [T=any]
|
|
31
31
|
*/
|
|
32
32
|
export class BaseNode<T = any> {
|
|
33
|
+
static TYPES: {
|
|
34
|
+
NETWORK: 'network';
|
|
35
|
+
CPU: 'cpu';
|
|
36
|
+
};
|
|
33
37
|
/**
|
|
34
38
|
* Returns whether the given node has a cycle in its dependent graph by performing a DFS.
|
|
35
39
|
* @param {Node} node
|
|
@@ -156,10 +160,4 @@ export class BaseNode<T = any> {
|
|
|
156
160
|
*/
|
|
157
161
|
canDependOn(node: Node): boolean;
|
|
158
162
|
}
|
|
159
|
-
|
|
160
|
-
namespace TYPES {
|
|
161
|
-
const NETWORK: 'network';
|
|
162
|
-
const CPU: 'cpu';
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
//# sourceMappingURL=base-node.d.ts.map
|
|
163
|
+
//# sourceMappingURL=BaseNode.d.ts.map
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* A union of all types derived from BaseNode, allowing type check discrimination
|
|
9
9
|
* based on `node.type`. If a new node type is created, it should be added here.
|
|
10
10
|
* @template [T=any]
|
|
11
|
-
* @typedef {import('./
|
|
11
|
+
* @typedef {import('./CpuNode.js').CPUNode<T> | import('./NetworkNode.js').NetworkNode<T>} Node
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
14
|
/**
|
|
@@ -28,6 +28,11 @@
|
|
|
28
28
|
* @template [T=any]
|
|
29
29
|
*/
|
|
30
30
|
class BaseNode {
|
|
31
|
+
static TYPES = /** @type {{NETWORK: 'network', CPU: 'cpu'}} */({
|
|
32
|
+
NETWORK: 'network',
|
|
33
|
+
CPU: 'cpu',
|
|
34
|
+
});
|
|
35
|
+
|
|
31
36
|
/**
|
|
32
37
|
* @param {string} id
|
|
33
38
|
*/
|
|
@@ -361,9 +366,4 @@ class BaseNode {
|
|
|
361
366
|
}
|
|
362
367
|
}
|
|
363
368
|
|
|
364
|
-
BaseNode.TYPES = /** @type {{NETWORK: 'network', CPU: 'cpu'}} */({
|
|
365
|
-
NETWORK: 'network',
|
|
366
|
-
CPU: 'cpu',
|
|
367
|
-
});
|
|
368
|
-
|
|
369
369
|
export {BaseNode};
|