lighthouse 8.5.1 → 8.6.0-dev.20211016
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/changelog.md +5490 -0
- package/dist/report/flow.js +35 -12
- package/dist/report/standalone.js +20 -21
- package/flow-report/.eslintrc.cjs +49 -0
- package/flow-report/assets/styles.css +272 -9
- package/flow-report/jest.config.js +1 -1
- package/flow-report/package.json +4 -0
- package/flow-report/src/app.tsx +31 -9
- package/flow-report/src/common.tsx +86 -7
- package/flow-report/src/header.tsx +88 -0
- package/flow-report/src/help-dialog.tsx +120 -0
- package/flow-report/src/i18n/i18n.tsx +52 -0
- package/flow-report/src/i18n/localized-strings.js +11 -0
- package/flow-report/src/i18n/ui-strings.js +134 -0
- package/flow-report/src/icons.tsx +20 -3
- package/flow-report/src/sidebar/sidebar.tsx +11 -16
- package/flow-report/src/summary/category.tsx +39 -24
- package/flow-report/src/summary/summary.tsx +35 -32
- package/flow-report/src/topbar.tsx +54 -4
- package/flow-report/src/util.ts +26 -1
- package/flow-report/src/wrappers/report-renderer.tsx +1 -1
- package/flow-report/src/wrappers/report.tsx +6 -21
- package/flow-report/test/app-test.tsx +17 -13
- package/flow-report/test/common-test.tsx +89 -0
- package/flow-report/test/header-test.tsx +55 -0
- package/flow-report/test/sample-flow.ts +17 -0
- package/flow-report/test/setup/env-setup.ts +1 -0
- package/flow-report/test/sidebar/flow-test.tsx +6 -12
- package/flow-report/test/sidebar/sidebar-test.tsx +7 -13
- package/flow-report/test/summary/category-test.tsx +83 -34
- package/flow-report/test/summary/summary-test.tsx +12 -18
- package/flow-report/test/topbar-test.tsx +68 -0
- package/flow-report/test/util-test.tsx +1 -12
- package/flow-report/tsconfig.json +2 -0
- package/flow-report/types/flow-report.d.ts +2 -1
- package/jest.config.js +1 -15
- package/lighthouse-cli/bin.js +5 -0
- package/lighthouse-cli/cli-flags.js +10 -3
- package/lighthouse-cli/commands/commands.js +1 -0
- package/lighthouse-cli/commands/list-locales.js +16 -0
- package/lighthouse-cli/run.js +2 -2
- package/lighthouse-cli/test/smokehouse/frontends/smokehouse-bin.js +0 -0
- package/lighthouse-cli/test/smokehouse/report-assert.js +20 -4
- package/lighthouse-core/audits/byte-efficiency/byte-efficiency-audit.js +2 -1
- package/lighthouse-core/audits/byte-efficiency/unused-javascript.js +1 -1
- package/lighthouse-core/audits/seo/crawlable-anchors.js +10 -12
- package/lighthouse-core/computed/js-bundles.js +1 -2
- package/lighthouse-core/computed/unused-javascript-summary.js +3 -3
- package/lighthouse-core/config/config-helpers.js +1 -0
- package/lighthouse-core/config/config.js +2 -2
- package/lighthouse-core/config/default-config.js +5 -0
- package/lighthouse-core/fraggle-rock/api.js +10 -0
- package/lighthouse-core/fraggle-rock/config/filters.js +22 -3
- package/lighthouse-core/fraggle-rock/gather/driver.js +4 -0
- package/lighthouse-core/fraggle-rock/gather/navigation-runner.js +2 -2
- package/lighthouse-core/fraggle-rock/user-flow.js +35 -6
- package/lighthouse-core/gather/driver/navigation.js +5 -0
- package/lighthouse-core/gather/driver/prepare.js +14 -0
- package/lighthouse-core/gather/driver/storage.js +5 -0
- package/lighthouse-core/gather/gather-runner.js +2 -2
- package/lighthouse-core/gather/gatherers/dobetterweb/response-compression.js +5 -1
- package/lighthouse-core/gather/gatherers/full-page-screenshot.js +30 -24
- package/lighthouse-core/lib/cdt/Platform.js +55 -0
- package/lighthouse-core/lib/cdt/SDK.js +3 -123
- package/lighthouse-core/lib/cdt/generated/SourceMap.js +148 -312
- package/lighthouse-core/lib/csp-evaluator.js +2 -1
- package/lighthouse-core/lib/i18n/README.md +6 -6
- package/lighthouse-core/lib/i18n/i18n.js +26 -348
- package/lighthouse-core/lib/page-functions.js +4 -4
- package/lighthouse-core/lib/stack-packs.js +1 -13
- package/lighthouse-core/runner.js +6 -6
- package/lighthouse-core/scripts/manual-chrome-launcher.js +4 -1
- package/lighthouse-core/scripts/package.json +4 -0
- package/lighthouse-core/util-commonjs.js +20 -4
- package/package.json +19 -16
- package/readme.md +1 -1
- package/report/assets/standalone-template.html +0 -1
- package/report/assets/styles.css +16 -41
- package/report/assets/templates.html +42 -4
- package/report/clients/psi.js +1 -0
- package/report/clients/standalone.js +1 -2
- package/report/generator/file-namer.js +17 -8
- package/report/generator/report-generator.js +0 -1
- package/report/renderer/category-renderer.js +4 -16
- package/report/renderer/components.js +107 -71
- package/report/renderer/dom.js +19 -0
- package/report/renderer/logger.js +35 -2
- package/report/renderer/performance-category-renderer.js +23 -21
- package/report/renderer/pwa-category-renderer.js +1 -2
- package/report/renderer/report-renderer.js +21 -0
- package/report/renderer/report-ui-features.js +7 -19
- package/report/renderer/swap-locale-feature.js +82 -0
- package/report/renderer/util.js +20 -4
- package/report/test/clients/psi-test.js +0 -4
- package/report/test/generator/file-namer-test.js +2 -2
- package/report/test/renderer/category-renderer-test.js +1 -1
- package/report/test/renderer/performance-category-renderer-test.js +12 -0
- package/report/test/renderer/report-renderer-axe-test.js +86 -0
- package/report/test/renderer/report-renderer-test.js +0 -55
- package/report/test/renderer/util-test.js +68 -8
- package/report/test-assets/faux-psi-template.html +0 -1
- package/report/tsconfig.json +2 -1
- package/report/types/html-renderer.d.ts +2 -1
- package/root.js +18 -0
- package/shared/localization/format.js +455 -0
- package/{lighthouse-core/lib/cdt/Common.js → shared/localization/i18n-module.js} +3 -8
- package/{lighthouse-core/lib/i18n → shared/localization}/locales/ar-XB.json +139 -4
- package/{lighthouse-core/lib/i18n → shared/localization}/locales/ar.json +141 -6
- package/{lighthouse-core/lib/i18n → shared/localization}/locales/bg.json +139 -4
- package/{lighthouse-core/lib/i18n → shared/localization}/locales/ca.json +140 -5
- package/{lighthouse-core/lib/i18n → shared/localization}/locales/cs.json +139 -4
- package/{lighthouse-core/lib/i18n → shared/localization}/locales/da.json +139 -4
- package/{lighthouse-core/lib/i18n → shared/localization}/locales/de.json +139 -4
- package/{lighthouse-core/lib/i18n → shared/localization}/locales/el.json +141 -6
- package/{lighthouse-core/lib/i18n → shared/localization}/locales/en-GB.json +139 -4
- package/{lighthouse-core/lib/i18n → shared/localization}/locales/en-US.json +129 -0
- package/{lighthouse-core/lib/i18n → shared/localization}/locales/en-XA.json +139 -4
- package/{lighthouse-core/lib/i18n → shared/localization}/locales/en-XL.json +129 -0
- package/{lighthouse-core/lib/i18n → shared/localization}/locales/es-419.json +139 -4
- package/{lighthouse-core/lib/i18n → shared/localization}/locales/es.json +139 -4
- package/{lighthouse-core/lib/i18n → shared/localization}/locales/fi.json +139 -4
- package/{lighthouse-core/lib/i18n → shared/localization}/locales/fil.json +139 -4
- package/{lighthouse-core/lib/i18n → shared/localization}/locales/fr.json +139 -4
- package/{lighthouse-core/lib/i18n → shared/localization}/locales/he.json +139 -4
- package/{lighthouse-core/lib/i18n → shared/localization}/locales/hi.json +142 -7
- package/{lighthouse-core/lib/i18n → shared/localization}/locales/hr.json +139 -4
- package/{lighthouse-core/lib/i18n → shared/localization}/locales/hu.json +139 -4
- package/{lighthouse-core/lib/i18n → shared/localization}/locales/id.json +139 -4
- package/{lighthouse-core/lib/i18n → shared/localization}/locales/it.json +139 -4
- package/{lighthouse-core/lib/i18n → shared/localization}/locales/ja.json +139 -4
- package/{lighthouse-core/lib/i18n → shared/localization}/locales/ko.json +139 -4
- package/{lighthouse-core/lib/i18n → shared/localization}/locales/lt.json +139 -4
- package/{lighthouse-core/lib/i18n → shared/localization}/locales/lv.json +139 -4
- package/{lighthouse-core/lib/i18n → shared/localization}/locales/nl.json +139 -4
- package/{lighthouse-core/lib/i18n → shared/localization}/locales/no.json +139 -4
- package/{lighthouse-core/lib/i18n → shared/localization}/locales/pl.json +139 -4
- package/{lighthouse-core/lib/i18n → shared/localization}/locales/pt-PT.json +139 -4
- package/{lighthouse-core/lib/i18n → shared/localization}/locales/pt.json +139 -4
- package/{lighthouse-core/lib/i18n → shared/localization}/locales/ro.json +139 -4
- package/{lighthouse-core/lib/i18n → shared/localization}/locales/ru.json +139 -4
- package/{lighthouse-core/lib/i18n → shared/localization}/locales/sk.json +139 -4
- package/{lighthouse-core/lib/i18n → shared/localization}/locales/sl.json +139 -4
- package/{lighthouse-core/lib/i18n → shared/localization}/locales/sr-Latn.json +139 -4
- package/{lighthouse-core/lib/i18n → shared/localization}/locales/sr.json +139 -4
- package/{lighthouse-core/lib/i18n → shared/localization}/locales/sv.json +139 -4
- package/{lighthouse-core/lib/i18n → shared/localization}/locales/ta.json +146 -11
- package/{lighthouse-core/lib/i18n → shared/localization}/locales/te.json +179 -44
- package/{lighthouse-core/lib/i18n → shared/localization}/locales/th.json +139 -4
- package/{lighthouse-core/lib/i18n → shared/localization}/locales/tr.json +139 -4
- package/{lighthouse-core/lib/i18n → shared/localization}/locales/uk.json +139 -4
- package/{lighthouse-core/lib/i18n → shared/localization}/locales/vi.json +139 -4
- package/{lighthouse-core/lib/i18n → shared/localization}/locales/zh-HK.json +139 -4
- package/{lighthouse-core/lib/i18n → shared/localization}/locales/zh-TW.json +139 -4
- package/{lighthouse-core/lib/i18n → shared/localization}/locales/zh.json +139 -4
- package/{lighthouse-core/lib/i18n → shared/localization}/locales.js +8 -1
- package/shared/localization/swap-flow-locale.js +20 -0
- package/{lighthouse-core/lib/i18n → shared/localization}/swap-locale.js +9 -7
- package/shared/test/localization/format-test.js +421 -0
- package/shared/test/localization/locales-test.js +47 -0
- package/shared/test/localization/swap-locale-test.js +140 -0
- package/shared/tsconfig.json +22 -0
- package/{lighthouse-core/lib → shared}/type-verifiers.js +0 -0
- package/shared/types/shared.d.ts +24 -0
- package/third-party/chromium-synchronization/inspector-issueAdded-types-test.js +5 -3
- package/third-party/chromium-synchronization/installability-errors-test.js +2 -1
- package/tsconfig-all.json +1 -0
- package/tsconfig-base.json +1 -1
- package/tsconfig.json +2 -3
- package/types/config.d.ts +1 -0
- package/types/es-main.d.ts +18 -0
- package/types/externs.d.ts +2 -0
- package/types/lhr/flow.d.ts +7 -0
- package/types/lhr/i18n.d.ts +0 -3
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
const defaultConfigPath = './default-config.js';
|
|
9
9
|
const defaultConfig = require('./default-config.js');
|
|
10
10
|
const constants = require('./constants.js');
|
|
11
|
-
const
|
|
11
|
+
const format = require('../../shared/localization/format.js');
|
|
12
12
|
const validation = require('./../fraggle-rock/config/validation.js');
|
|
13
13
|
|
|
14
14
|
const log = require('lighthouse-logger');
|
|
@@ -248,7 +248,7 @@ class Config {
|
|
|
248
248
|
}
|
|
249
249
|
|
|
250
250
|
// Printed config is more useful with localized strings.
|
|
251
|
-
|
|
251
|
+
format.replaceIcuMessages(jsonConfig, jsonConfig.settings.locale);
|
|
252
252
|
|
|
253
253
|
return JSON.stringify(jsonConfig, null, 2);
|
|
254
254
|
}
|
|
@@ -421,6 +421,7 @@ const defaultConfig = {
|
|
|
421
421
|
categories: {
|
|
422
422
|
'performance': {
|
|
423
423
|
title: str_(UIStrings.performanceCategoryTitle),
|
|
424
|
+
supportedModes: ['navigation', 'timespan', 'snapshot'],
|
|
424
425
|
auditRefs: [
|
|
425
426
|
{id: 'first-contentful-paint', weight: 10, group: 'metrics', acronym: 'FCP', relevantAudits: m2a.fcpRelevantAudits},
|
|
426
427
|
{id: 'speed-index', weight: 10, group: 'metrics', acronym: 'SI'},
|
|
@@ -490,6 +491,7 @@ const defaultConfig = {
|
|
|
490
491
|
title: str_(UIStrings.a11yCategoryTitle),
|
|
491
492
|
description: str_(UIStrings.a11yCategoryDescription),
|
|
492
493
|
manualDescription: str_(UIStrings.a11yCategoryManualDescription),
|
|
494
|
+
supportedModes: ['navigation', 'snapshot'],
|
|
493
495
|
// Audit weights are meant to match the aXe scoring system of
|
|
494
496
|
// minor, moderate, serious, and critical.
|
|
495
497
|
// See the audits listed at dequeuniversity.com/rules/axe/4.1.
|
|
@@ -554,6 +556,7 @@ const defaultConfig = {
|
|
|
554
556
|
},
|
|
555
557
|
'best-practices': {
|
|
556
558
|
title: str_(UIStrings.bestPracticesCategoryTitle),
|
|
559
|
+
supportedModes: ['navigation', 'timespan', 'snapshot'],
|
|
557
560
|
auditRefs: [
|
|
558
561
|
// Trust & Safety
|
|
559
562
|
{id: 'is-on-https', weight: 1, group: 'best-practices-trust-safety'},
|
|
@@ -584,6 +587,7 @@ const defaultConfig = {
|
|
|
584
587
|
title: str_(UIStrings.seoCategoryTitle),
|
|
585
588
|
description: str_(UIStrings.seoCategoryDescription),
|
|
586
589
|
manualDescription: str_(UIStrings.seoCategoryManualDescription),
|
|
590
|
+
supportedModes: ['navigation', 'snapshot'],
|
|
587
591
|
auditRefs: [
|
|
588
592
|
{id: 'viewport', weight: 1, group: 'seo-mobile'},
|
|
589
593
|
{id: 'document-title', weight: 1, group: 'seo-content'},
|
|
@@ -607,6 +611,7 @@ const defaultConfig = {
|
|
|
607
611
|
title: str_(UIStrings.pwaCategoryTitle),
|
|
608
612
|
description: str_(UIStrings.pwaCategoryDescription),
|
|
609
613
|
manualDescription: str_(UIStrings.pwaCategoryManualDescription),
|
|
614
|
+
supportedModes: ['navigation'],
|
|
610
615
|
auditRefs: [
|
|
611
616
|
// Installable
|
|
612
617
|
{id: 'installable-manifest', weight: 2, group: 'pwa-installable'},
|
|
@@ -8,9 +8,19 @@
|
|
|
8
8
|
const {snapshot} = require('./gather/snapshot-runner.js');
|
|
9
9
|
const {startTimespan} = require('./gather/timespan-runner.js');
|
|
10
10
|
const {navigation} = require('./gather/navigation-runner.js');
|
|
11
|
+
const UserFlow = require('./user-flow.js');
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* @param {import('puppeteer').Page} page
|
|
15
|
+
* @param {UserFlow.UserFlowOptions} [options]
|
|
16
|
+
*/
|
|
17
|
+
async function startFlow(page, options) {
|
|
18
|
+
return new UserFlow(page, options);
|
|
19
|
+
}
|
|
11
20
|
|
|
12
21
|
module.exports = {
|
|
13
22
|
snapshot,
|
|
14
23
|
startTimespan,
|
|
15
24
|
navigation,
|
|
25
|
+
startFlow,
|
|
16
26
|
};
|
|
@@ -162,6 +162,23 @@ function filterAuditsByGatherMode(audits, mode) {
|
|
|
162
162
|
});
|
|
163
163
|
}
|
|
164
164
|
|
|
165
|
+
/**
|
|
166
|
+
* Optional `supportedModes` property can explicitly exclude a category even if some audits are available.
|
|
167
|
+
*
|
|
168
|
+
* @param {LH.Config.Config['categories']} categories
|
|
169
|
+
* @param {LH.Gatherer.GatherMode} mode
|
|
170
|
+
* @return {LH.Config.Config['categories']}
|
|
171
|
+
*/
|
|
172
|
+
function filterCategoriesByGatherMode(categories, mode) {
|
|
173
|
+
if (!categories) return null;
|
|
174
|
+
|
|
175
|
+
const categoriesToKeep = Object.entries(categories)
|
|
176
|
+
.filter(([_, category]) => {
|
|
177
|
+
return !category.supportedModes || category.supportedModes.includes(mode);
|
|
178
|
+
});
|
|
179
|
+
return Object.fromEntries(categoriesToKeep);
|
|
180
|
+
}
|
|
181
|
+
|
|
165
182
|
/**
|
|
166
183
|
* Filters a categories object and their auditRefs down to the specified category ids.
|
|
167
184
|
*
|
|
@@ -225,9 +242,10 @@ function filterCategoriesByAvailableAudits(categories, availableAudits) {
|
|
|
225
242
|
*/
|
|
226
243
|
function filterConfigByGatherMode(config, mode) {
|
|
227
244
|
const artifacts = filterArtifactsByGatherMode(config.artifacts, mode);
|
|
228
|
-
const
|
|
229
|
-
const audits =
|
|
230
|
-
const
|
|
245
|
+
const supportedAudits = filterAuditsByGatherMode(config.audits, mode);
|
|
246
|
+
const audits = filterAuditsByAvailableArtifacts(supportedAudits, artifacts || []);
|
|
247
|
+
const supportedCategories = filterCategoriesByGatherMode(config.categories, mode);
|
|
248
|
+
const categories = filterCategoriesByAvailableAudits(supportedCategories, audits || []);
|
|
231
249
|
|
|
232
250
|
return {
|
|
233
251
|
...config,
|
|
@@ -291,4 +309,5 @@ module.exports = {
|
|
|
291
309
|
filterAuditsByGatherMode,
|
|
292
310
|
filterCategoriesByAvailableAudits,
|
|
293
311
|
filterCategoriesByExplicitFilters,
|
|
312
|
+
filterCategoriesByGatherMode,
|
|
294
313
|
};
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
'use strict';
|
|
7
7
|
|
|
8
|
+
const log = require('lighthouse-logger');
|
|
8
9
|
const ProtocolSession = require('./session.js');
|
|
9
10
|
const ExecutionContext = require('../../gather/driver/execution-context.js');
|
|
10
11
|
const Fetcher = require('../../gather/fetcher.js');
|
|
@@ -68,10 +69,13 @@ class Driver {
|
|
|
68
69
|
/** @return {Promise<void>} */
|
|
69
70
|
async connect() {
|
|
70
71
|
if (this._session) return;
|
|
72
|
+
const status = {msg: 'Connecting to browser', id: 'lh:driver:connect'};
|
|
73
|
+
log.time(status);
|
|
71
74
|
const session = await this._page.target().createCDPSession();
|
|
72
75
|
this._session = this.defaultSession = new ProtocolSession(session);
|
|
73
76
|
this._executionContext = new ExecutionContext(this._session);
|
|
74
77
|
this._fetcher = new Fetcher(this._session, this._executionContext);
|
|
78
|
+
log.timeEnd(status);
|
|
75
79
|
}
|
|
76
80
|
|
|
77
81
|
/** @return {Promise<void>} */
|
|
@@ -20,7 +20,7 @@ const emulation = require('../../lib/emulation.js');
|
|
|
20
20
|
const {defaultNavigationConfig} = require('../../config/constants.js');
|
|
21
21
|
const {initializeConfig} = require('../config/config.js');
|
|
22
22
|
const {getBaseArtifacts, finalizeArtifacts} = require('./base-artifacts.js');
|
|
23
|
-
const
|
|
23
|
+
const format = require('../../../shared/localization/format.js');
|
|
24
24
|
const LighthouseError = require('../../lib/lh-error.js');
|
|
25
25
|
const {getPageLoadError} = require('../../lib/navigation-error.js');
|
|
26
26
|
const Trace = require('../../gather/gatherers/trace.js');
|
|
@@ -165,7 +165,7 @@ async function _computeNavigationResult(
|
|
|
165
165
|
|
|
166
166
|
if (pageLoadError) {
|
|
167
167
|
const locale = navigationContext.config.settings.locale;
|
|
168
|
-
const localizedMessage =
|
|
168
|
+
const localizedMessage = format.getFormatted(pageLoadError.friendlyMessage, locale);
|
|
169
169
|
log.error('NavigationRunner', localizedMessage, navigationContext.requestedUrl);
|
|
170
170
|
|
|
171
171
|
/** @type {Partial<LH.GathererArtifacts>} */
|
|
@@ -6,11 +6,13 @@
|
|
|
6
6
|
'use strict';
|
|
7
7
|
|
|
8
8
|
const {generateFlowReportHtml} = require('../../report/generator/report-generator.js');
|
|
9
|
-
const {
|
|
9
|
+
const {snapshot} = require('./gather/snapshot-runner.js');
|
|
10
|
+
const {startTimespan} = require('./gather/timespan-runner.js');
|
|
11
|
+
const {navigation} = require('./gather/navigation-runner.js');
|
|
10
12
|
|
|
11
13
|
/** @typedef {Parameters<snapshot>[0]} FrOptions */
|
|
12
|
-
/** @typedef {Omit<FrOptions, 'page'>} UserFlowOptions */
|
|
13
|
-
/** @typedef {
|
|
14
|
+
/** @typedef {Omit<FrOptions, 'page'> & {name?: string}} UserFlowOptions */
|
|
15
|
+
/** @typedef {Omit<FrOptions, 'page'> & {stepName?: string}} StepOptions */
|
|
14
16
|
|
|
15
17
|
class UserFlow {
|
|
16
18
|
/**
|
|
@@ -20,6 +22,8 @@ class UserFlow {
|
|
|
20
22
|
constructor(page, options) {
|
|
21
23
|
/** @type {FrOptions} */
|
|
22
24
|
this.options = {page, ...options};
|
|
25
|
+
/** @type {string|undefined} */
|
|
26
|
+
this.name = options && options.name;
|
|
23
27
|
/** @type {LH.FlowResult.Step[]} */
|
|
24
28
|
this.steps = [];
|
|
25
29
|
}
|
|
@@ -49,6 +53,29 @@ class UserFlow {
|
|
|
49
53
|
}
|
|
50
54
|
}
|
|
51
55
|
|
|
56
|
+
/**
|
|
57
|
+
* @param {string} url
|
|
58
|
+
* @param {StepOptions=} stepOptions
|
|
59
|
+
*/
|
|
60
|
+
_getNextNavigationOptions(url, stepOptions) {
|
|
61
|
+
const options = {url, ...this.options, ...stepOptions};
|
|
62
|
+
|
|
63
|
+
// On repeat navigations, we want to disable storage reset by default (i.e. it's not a cold load).
|
|
64
|
+
const isSubsequentNavigation = this.steps.some(step => step.lhr.gatherMode === 'navigation');
|
|
65
|
+
if (isSubsequentNavigation) {
|
|
66
|
+
const configContext = {...options.configContext};
|
|
67
|
+
const settingsOverrides = {...configContext.settingsOverrides};
|
|
68
|
+
if (settingsOverrides.disableStorageReset === undefined) {
|
|
69
|
+
settingsOverrides.disableStorageReset = true;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
configContext.settingsOverrides = settingsOverrides;
|
|
73
|
+
options.configContext = configContext;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return options;
|
|
77
|
+
}
|
|
78
|
+
|
|
52
79
|
/**
|
|
53
80
|
* @param {string} url
|
|
54
81
|
* @param {StepOptions=} stepOptions
|
|
@@ -56,8 +83,7 @@ class UserFlow {
|
|
|
56
83
|
async navigate(url, stepOptions) {
|
|
57
84
|
if (this.currentTimespan) throw Error('Timespan already in progress');
|
|
58
85
|
|
|
59
|
-
const
|
|
60
|
-
const result = await navigation(options);
|
|
86
|
+
const result = await navigation(this._getNextNavigationOptions(url, stepOptions));
|
|
61
87
|
if (!result) throw Error('Navigation returned undefined');
|
|
62
88
|
|
|
63
89
|
const providedName = stepOptions && stepOptions.stepName;
|
|
@@ -120,7 +146,10 @@ class UserFlow {
|
|
|
120
146
|
* @return {LH.FlowResult}
|
|
121
147
|
*/
|
|
122
148
|
getFlowResult() {
|
|
123
|
-
|
|
149
|
+
if (!this.steps.length) throw Error('Need at least one step before getting the flow result');
|
|
150
|
+
const url = new URL(this.steps[0].lhr.finalUrl);
|
|
151
|
+
const name = this.name || `User flow (${url.hostname})`;
|
|
152
|
+
return {steps: this.steps, name};
|
|
124
153
|
}
|
|
125
154
|
|
|
126
155
|
/**
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
'use strict';
|
|
7
7
|
|
|
8
|
+
const log = require('lighthouse-logger');
|
|
8
9
|
const NetworkMonitor = require('./network-monitor.js');
|
|
9
10
|
const {waitForFullyLoaded, waitForFrameNavigated, waitForUserToContinue} = require('./wait-for-condition.js'); // eslint-disable-line max-len
|
|
10
11
|
const constants = require('../../config/constants.js');
|
|
@@ -82,6 +83,9 @@ function resolveWaitForFullyLoadedOptions(options) {
|
|
|
82
83
|
* @return {Promise<{finalUrl: string, warnings: Array<LH.IcuMessage>}>}
|
|
83
84
|
*/
|
|
84
85
|
async function gotoURL(driver, url, options) {
|
|
86
|
+
const status = {msg: `Navigating to ${url}`, id: 'lh:driver:navigate'};
|
|
87
|
+
log.time(status);
|
|
88
|
+
|
|
85
89
|
const session = driver.defaultSession;
|
|
86
90
|
const networkMonitor = new NetworkMonitor(driver.defaultSession);
|
|
87
91
|
|
|
@@ -125,6 +129,7 @@ async function gotoURL(driver, url, options) {
|
|
|
125
129
|
await waitForUserToContinue(driver);
|
|
126
130
|
}
|
|
127
131
|
|
|
132
|
+
log.timeEnd(status);
|
|
128
133
|
return {
|
|
129
134
|
finalUrl,
|
|
130
135
|
warnings: getNavigationWarnings({timedOut, finalUrl, requestedUrl: url}),
|
|
@@ -142,11 +142,16 @@ async function prepareDeviceEmulationAndAsyncStacks(driver, settings) {
|
|
|
142
142
|
* @param {LH.Config.Settings} settings
|
|
143
143
|
*/
|
|
144
144
|
async function prepareTargetForTimespanMode(driver, settings) {
|
|
145
|
+
const status = {msg: 'Preparing target for timespan mode', id: 'lh:prepare:timespanMode'};
|
|
146
|
+
log.time(status);
|
|
147
|
+
|
|
145
148
|
await prepareDeviceEmulationAndAsyncStacks(driver, settings);
|
|
146
149
|
await prepareThrottlingAndNetwork(driver.defaultSession, settings, {
|
|
147
150
|
disableThrottling: false,
|
|
148
151
|
blockedUrlPatterns: undefined,
|
|
149
152
|
});
|
|
153
|
+
|
|
154
|
+
log.timeEnd(status);
|
|
150
155
|
}
|
|
151
156
|
|
|
152
157
|
/**
|
|
@@ -159,6 +164,9 @@ async function prepareTargetForTimespanMode(driver, settings) {
|
|
|
159
164
|
* @param {LH.Config.Settings} settings
|
|
160
165
|
*/
|
|
161
166
|
async function prepareTargetForNavigationMode(driver, settings) {
|
|
167
|
+
const status = {msg: 'Preparing target for navigation mode', id: 'lh:prepare:navigationMode'};
|
|
168
|
+
log.time(status);
|
|
169
|
+
|
|
162
170
|
await prepareDeviceEmulationAndAsyncStacks(driver, settings);
|
|
163
171
|
|
|
164
172
|
// Automatically handle any JavaScript dialogs to prevent a hung renderer.
|
|
@@ -171,6 +179,8 @@ async function prepareTargetForNavigationMode(driver, settings) {
|
|
|
171
179
|
if (settings.throttlingMethod === 'simulate') {
|
|
172
180
|
await shimRequestIdleCallbackOnNewDocument(driver, settings);
|
|
173
181
|
}
|
|
182
|
+
|
|
183
|
+
log.timeEnd(status);
|
|
174
184
|
}
|
|
175
185
|
|
|
176
186
|
/**
|
|
@@ -184,6 +194,9 @@ async function prepareTargetForNavigationMode(driver, settings) {
|
|
|
184
194
|
* @return {Promise<{warnings: Array<LH.IcuMessage>}>}
|
|
185
195
|
*/
|
|
186
196
|
async function prepareTargetForIndividualNavigation(session, settings, navigation) {
|
|
197
|
+
const status = {msg: 'Preparing target for navigation', id: 'lh:prepare:navigation'};
|
|
198
|
+
log.time(status);
|
|
199
|
+
|
|
187
200
|
/** @type {Array<LH.IcuMessage>} */
|
|
188
201
|
const warnings = [];
|
|
189
202
|
|
|
@@ -195,6 +208,7 @@ async function prepareTargetForIndividualNavigation(session, settings, navigatio
|
|
|
195
208
|
|
|
196
209
|
await prepareThrottlingAndNetwork(session, settings, navigation);
|
|
197
210
|
|
|
211
|
+
log.timeEnd(status);
|
|
198
212
|
return {warnings};
|
|
199
213
|
}
|
|
200
214
|
|
|
@@ -33,6 +33,9 @@ const str_ = i18n.createMessageInstanceIdFn(__filename, UIStrings);
|
|
|
33
33
|
* @return {Promise<void>}
|
|
34
34
|
*/
|
|
35
35
|
async function clearDataForOrigin(session, url) {
|
|
36
|
+
const status = {msg: 'Cleaning origin data', id: 'lh:storage:clearDataForOrigin'};
|
|
37
|
+
log.time(status);
|
|
38
|
+
|
|
36
39
|
const origin = new URL(url).origin;
|
|
37
40
|
|
|
38
41
|
// Clear some types of storage.
|
|
@@ -63,6 +66,8 @@ async function clearDataForOrigin(session, url) {
|
|
|
63
66
|
} else {
|
|
64
67
|
throw err;
|
|
65
68
|
}
|
|
69
|
+
} finally {
|
|
70
|
+
log.timeEnd(status);
|
|
66
71
|
}
|
|
67
72
|
}
|
|
68
73
|
|
|
@@ -10,7 +10,7 @@ const NetworkRecords = require('../computed/network-records.js');
|
|
|
10
10
|
const {getPageLoadError} = require('../lib/navigation-error.js');
|
|
11
11
|
const emulation = require('../lib/emulation.js');
|
|
12
12
|
const constants = require('../config/constants.js');
|
|
13
|
-
const
|
|
13
|
+
const format = require('../../shared/localization/format.js');
|
|
14
14
|
const {getBenchmarkIndex, getEnvironmentWarnings} = require('./driver/environment.js');
|
|
15
15
|
const prepare = require('./driver/prepare.js');
|
|
16
16
|
const storage = require('./driver/storage.js');
|
|
@@ -574,7 +574,7 @@ class GatherRunner {
|
|
|
574
574
|
networkRecords: loadData.networkRecords,
|
|
575
575
|
});
|
|
576
576
|
if (pageLoadError) {
|
|
577
|
-
const localizedMessage =
|
|
577
|
+
const localizedMessage = format.getFormatted(pageLoadError.friendlyMessage,
|
|
578
578
|
passContext.settings.locale);
|
|
579
579
|
log.error('GatherRunner', localizedMessage, passContext.url);
|
|
580
580
|
|
|
@@ -20,7 +20,11 @@ const {fetchResponseBodyFromCache} = require('../../driver/network.js');
|
|
|
20
20
|
const NetworkRecords = require('../../../computed/network-records.js');
|
|
21
21
|
|
|
22
22
|
const CHROME_EXTENSION_PROTOCOL = 'chrome-extension:';
|
|
23
|
-
const compressionHeaders = [
|
|
23
|
+
const compressionHeaders = [
|
|
24
|
+
'content-encoding',
|
|
25
|
+
'x-original-content-encoding',
|
|
26
|
+
'x-content-encoding-over-network',
|
|
27
|
+
];
|
|
24
28
|
const compressionTypes = ['gzip', 'br', 'deflate'];
|
|
25
29
|
const binaryMimeTypes = ['image', 'audio', 'video'];
|
|
26
30
|
/** @type {LH.Crdp.Network.ResourceType[]} */
|
|
@@ -22,6 +22,26 @@ function snakeCaseToCamelCase(str) {
|
|
|
22
22
|
return str.replace(/(-\w)/g, m => m[1].toUpperCase());
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
+
/* c8 ignore start */
|
|
26
|
+
|
|
27
|
+
// eslint-disable-next-line no-inner-declarations
|
|
28
|
+
function getObservedDeviceMetrics() {
|
|
29
|
+
// Convert the Web API's snake case (landscape-primary) to camel case (landscapePrimary).
|
|
30
|
+
const screenOrientationType = /** @type {LH.Crdp.Emulation.ScreenOrientationType} */ (
|
|
31
|
+
snakeCaseToCamelCase(window.screen.orientation.type));
|
|
32
|
+
return {
|
|
33
|
+
width: document.documentElement.clientWidth,
|
|
34
|
+
height: document.documentElement.clientHeight,
|
|
35
|
+
screenOrientation: {
|
|
36
|
+
type: screenOrientationType,
|
|
37
|
+
angle: window.screen.orientation.angle,
|
|
38
|
+
},
|
|
39
|
+
deviceScaleFactor: window.devicePixelRatio,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/* c8 ignore stop */
|
|
44
|
+
|
|
25
45
|
class FullPageScreenshot extends FRGatherer {
|
|
26
46
|
/** @type {LH.Gatherer.GathererMeta} */
|
|
27
47
|
meta = {
|
|
@@ -140,9 +160,17 @@ class FullPageScreenshot extends FRGatherer {
|
|
|
140
160
|
const executionContext = context.driver.executionContext;
|
|
141
161
|
const settings = context.settings;
|
|
142
162
|
|
|
143
|
-
// In case some other program is controlling emulation,
|
|
163
|
+
// In case some other program is controlling emulation, remember what the device looks
|
|
144
164
|
// like now and reset after gatherer is done.
|
|
165
|
+
let observedDeviceMetrics;
|
|
145
166
|
const lighthouseControlsEmulation = !settings.screenEmulation.disabled;
|
|
167
|
+
if (!lighthouseControlsEmulation) {
|
|
168
|
+
observedDeviceMetrics = await executionContext.evaluate(getObservedDeviceMetrics, {
|
|
169
|
+
args: [],
|
|
170
|
+
useIsolation: true,
|
|
171
|
+
deps: [snakeCaseToCamelCase],
|
|
172
|
+
});
|
|
173
|
+
}
|
|
146
174
|
|
|
147
175
|
try {
|
|
148
176
|
return {
|
|
@@ -153,7 +181,7 @@ class FullPageScreenshot extends FRGatherer {
|
|
|
153
181
|
// Revert resized page.
|
|
154
182
|
if (lighthouseControlsEmulation) {
|
|
155
183
|
await emulation.emulate(session, settings);
|
|
156
|
-
} else {
|
|
184
|
+
} else if (observedDeviceMetrics) {
|
|
157
185
|
// Best effort to reset emulation to what it was.
|
|
158
186
|
// https://github.com/GoogleChrome/lighthouse/pull/10716#discussion_r428970681
|
|
159
187
|
// TODO: seems like this would be brittle. Should at least work for devtools, but what
|
|
@@ -161,28 +189,6 @@ class FullPageScreenshot extends FRGatherer {
|
|
|
161
189
|
// in the LH runner api, which for ex. puppeteer consumers would setup puppeteer emulation,
|
|
162
190
|
// and then just call that to reset?
|
|
163
191
|
// https://github.com/GoogleChrome/lighthouse/issues/11122
|
|
164
|
-
|
|
165
|
-
// eslint-disable-next-line no-inner-declarations
|
|
166
|
-
function getObservedDeviceMetrics() {
|
|
167
|
-
// Convert the Web API's snake case (landscape-primary) to camel case (landscapePrimary).
|
|
168
|
-
const screenOrientationType = /** @type {LH.Crdp.Emulation.ScreenOrientationType} */ (
|
|
169
|
-
snakeCaseToCamelCase(window.screen.orientation.type));
|
|
170
|
-
return {
|
|
171
|
-
width: document.documentElement.clientWidth,
|
|
172
|
-
height: document.documentElement.clientHeight,
|
|
173
|
-
screenOrientation: {
|
|
174
|
-
type: screenOrientationType,
|
|
175
|
-
angle: window.screen.orientation.angle,
|
|
176
|
-
},
|
|
177
|
-
deviceScaleFactor: window.devicePixelRatio,
|
|
178
|
-
};
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
const observedDeviceMetrics = await executionContext.evaluate(getObservedDeviceMetrics, {
|
|
182
|
-
args: [],
|
|
183
|
-
useIsolation: true,
|
|
184
|
-
deps: [snakeCaseToCamelCase],
|
|
185
|
-
});
|
|
186
192
|
await session.sendCommand('Emulation.setDeviceMetricsOverride', {
|
|
187
193
|
mobile: settings.formFactor === 'mobile',
|
|
188
194
|
...observedDeviceMetrics,
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
/**
|
|
3
|
+
* @license Copyright 2021 The Lighthouse Authors. All Rights Reserved.
|
|
4
|
+
* 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
|
|
5
|
+
* 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.
|
|
6
|
+
*/
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
// Functions manually copied from:
|
|
10
|
+
// https://github.com/ChromeDevTools/devtools-frontend/blob/master/front_end/core/platform/array-utilities.ts#L125
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* @param {any[]} array
|
|
14
|
+
* @param {any} needle
|
|
15
|
+
* @param {any} comparator
|
|
16
|
+
*/
|
|
17
|
+
function lowerBound(array, needle, comparator, left, right) {
|
|
18
|
+
let l = left || 0;
|
|
19
|
+
let r = right !== undefined ? right : array.length;
|
|
20
|
+
while (l < r) {
|
|
21
|
+
const m = (l + r) >> 1;
|
|
22
|
+
if (comparator(needle, array[m]) > 0) {
|
|
23
|
+
l = m + 1;
|
|
24
|
+
} else {
|
|
25
|
+
r = m;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return r;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* @param {any[]} array
|
|
33
|
+
* @param {any} needle
|
|
34
|
+
* @param {any} comparator
|
|
35
|
+
*/
|
|
36
|
+
function upperBound(array, needle, comparator, left, right) {
|
|
37
|
+
let l = left || 0;
|
|
38
|
+
let r = right !== undefined ? right : array.length;
|
|
39
|
+
while (l < r) {
|
|
40
|
+
const m = (l + r) >> 1;
|
|
41
|
+
if (comparator(needle, array[m]) >= 0) {
|
|
42
|
+
l = m + 1;
|
|
43
|
+
} else {
|
|
44
|
+
r = m;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return r;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
module.exports = {
|
|
51
|
+
ArrayUtilities: {
|
|
52
|
+
lowerBound,
|
|
53
|
+
upperBound,
|
|
54
|
+
},
|
|
55
|
+
};
|
|
@@ -4,145 +4,25 @@
|
|
|
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
|
'use strict';
|
|
7
|
-
|
|
8
7
|
const SDK = {
|
|
9
8
|
TextSourceMap: require('./generated/SourceMap.js'),
|
|
10
9
|
};
|
|
11
10
|
|
|
12
|
-
/**
|
|
13
|
-
* CDT pollutes Array.prototype w/ `lowerBound/upperBound`. SourceMap
|
|
14
|
-
* relies on this, but only for a couple method return values. To avoid global pollution,
|
|
15
|
-
* we explicitly set the extension functions on the return values.
|
|
16
|
-
*
|
|
17
|
-
* @param {unknown[]} array
|
|
18
|
-
*/
|
|
19
|
-
function extendArray(array) {
|
|
20
|
-
// @ts-expect-error
|
|
21
|
-
if (array.lowerBound) return;
|
|
22
|
-
|
|
23
|
-
// @ts-expect-error
|
|
24
|
-
array.lowerBound = lowerBound.bind(array);
|
|
25
|
-
// @ts-expect-error
|
|
26
|
-
array.upperBound = upperBound.bind(array);
|
|
27
|
-
|
|
28
|
-
array.slice = function(start, end) {
|
|
29
|
-
const retVal = Array.prototype.slice.call(array, start, end);
|
|
30
|
-
extendArray(retVal);
|
|
31
|
-
return retVal;
|
|
32
|
-
};
|
|
33
|
-
// @ts-expect-error
|
|
34
|
-
array.filter = function(fn) {
|
|
35
|
-
const retVal = Array.prototype.filter.call(array, fn);
|
|
36
|
-
extendArray(retVal);
|
|
37
|
-
return retVal;
|
|
38
|
-
};
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
const originalMappings = SDK.TextSourceMap.prototype.mappings;
|
|
42
|
-
SDK.TextSourceMap.prototype.mappings = function() {
|
|
43
|
-
const mappings = originalMappings.call(this);
|
|
44
|
-
extendArray(mappings);
|
|
45
|
-
return mappings;
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
const originalReversedMappings = SDK.TextSourceMap.prototype._reversedMappings;
|
|
49
|
-
/** @param {string} sourceURL */
|
|
50
|
-
SDK.TextSourceMap.prototype._reversedMappings = function(sourceURL) {
|
|
51
|
-
const mappings = originalReversedMappings.call(this, sourceURL);
|
|
52
|
-
extendArray(mappings);
|
|
53
|
-
return mappings;
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* `upperBound` and `lowerBound` are copied from CDT utilities.js.
|
|
58
|
-
* These are the only methods needed from that file.
|
|
59
|
-
*/
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* Return index of the leftmost element that is greater
|
|
63
|
-
* than the specimen object. If there's no such element (i.e. all
|
|
64
|
-
* elements are smaller or equal to the specimen) returns right bound.
|
|
65
|
-
* The function works for sorted array.
|
|
66
|
-
* When specified, |left| (inclusive) and |right| (exclusive) indices
|
|
67
|
-
* define the search window.
|
|
68
|
-
*
|
|
69
|
-
* @param {!T} object
|
|
70
|
-
* @param {function(!T,!S):number=} comparator
|
|
71
|
-
* @param {number=} left
|
|
72
|
-
* @param {number=} right
|
|
73
|
-
* @return {number}
|
|
74
|
-
* @this {Array.<!S>}
|
|
75
|
-
* @template T,S
|
|
76
|
-
*/
|
|
77
|
-
function upperBound(object, comparator, left, right) {
|
|
78
|
-
// @ts-expect-error
|
|
79
|
-
function defaultComparator(a, b) {
|
|
80
|
-
return a < b ? -1 : (a > b ? 1 : 0);
|
|
81
|
-
}
|
|
82
|
-
comparator = comparator || defaultComparator;
|
|
83
|
-
let l = left || 0;
|
|
84
|
-
let r = right !== undefined ? right : this.length;
|
|
85
|
-
while (l < r) {
|
|
86
|
-
const m = (l + r) >> 1;
|
|
87
|
-
if (comparator(object, this[m]) >= 0) {
|
|
88
|
-
l = m + 1;
|
|
89
|
-
} else {
|
|
90
|
-
r = m;
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
return r;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* Return index of the leftmost element that is equal or greater
|
|
98
|
-
* than the specimen object. If there's no such element (i.e. all
|
|
99
|
-
* elements are smaller than the specimen) returns right bound.
|
|
100
|
-
* The function works for sorted array.
|
|
101
|
-
* When specified, |left| (inclusive) and |right| (exclusive) indices
|
|
102
|
-
* define the search window.
|
|
103
|
-
*
|
|
104
|
-
* @param {!T} object
|
|
105
|
-
* @param {function(!T,!S):number=} comparator
|
|
106
|
-
* @param {number=} left
|
|
107
|
-
* @param {number=} right
|
|
108
|
-
* @return {number}
|
|
109
|
-
* @this {Array.<!S>}
|
|
110
|
-
* @template T,S
|
|
111
|
-
*/
|
|
112
|
-
function lowerBound(object, comparator, left, right) {
|
|
113
|
-
// @ts-expect-error
|
|
114
|
-
function defaultComparator(a, b) {
|
|
115
|
-
return a < b ? -1 : (a > b ? 1 : 0);
|
|
116
|
-
}
|
|
117
|
-
comparator = comparator || defaultComparator;
|
|
118
|
-
let l = left || 0;
|
|
119
|
-
let r = right !== undefined ? right : this.length;
|
|
120
|
-
while (l < r) {
|
|
121
|
-
const m = (l + r) >> 1;
|
|
122
|
-
if (comparator(object, this[m]) > 0) {
|
|
123
|
-
l = m + 1;
|
|
124
|
-
} else {
|
|
125
|
-
r = m;
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
return r;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
11
|
// Add `lastColumnNumber` to mappings. This will eventually be added to CDT.
|
|
132
12
|
// @ts-expect-error
|
|
133
13
|
SDK.TextSourceMap.prototype.computeLastGeneratedColumns = function() {
|
|
134
14
|
const mappings = this.mappings();
|
|
135
|
-
|
|
136
|
-
if (mappings.length && typeof mappings[0].lastColumnNumber !== 'undefined') return;
|
|
15
|
+
if (mappings.length && mappings[0].lastColumnNumber !== undefined) return;
|
|
137
16
|
|
|
138
17
|
for (let i = 0; i < mappings.length - 1; i++) {
|
|
139
18
|
const mapping = mappings[i];
|
|
140
19
|
const nextMapping = mappings[i + 1];
|
|
141
20
|
if (mapping.lineNumber === nextMapping.lineNumber) {
|
|
142
|
-
// @ts-expect-error: `lastColumnNumber` is not on types yet.
|
|
143
21
|
mapping.lastColumnNumber = nextMapping.columnNumber;
|
|
144
22
|
}
|
|
145
23
|
}
|
|
24
|
+
|
|
25
|
+
// Now, all but the last mapping on each line will have 'lastColumnNumber' set to a number.
|
|
146
26
|
};
|
|
147
27
|
|
|
148
28
|
module.exports = SDK;
|