lighthouse 11.7.0-dev.20240409 → 11.7.0-dev.20240411

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.
Files changed (134) hide show
  1. package/cli/cli-flags.js +3 -3
  2. package/cli/test/smokehouse/core-tests.js +0 -16
  3. package/cli/test/smokehouse/frontends/smokehouse-bin.js +2 -2
  4. package/core/audits/byte-efficiency/render-blocking-resources.js +25 -26
  5. package/core/audits/layout-shifts.js +1 -1
  6. package/core/computed/metrics/lantern-first-contentful-paint.js +3 -2
  7. package/core/computed/metrics/lantern-first-meaningful-paint.js +3 -2
  8. package/core/computed/metrics/lantern-interactive.js +3 -2
  9. package/core/computed/metrics/lantern-largest-contentful-paint.js +3 -2
  10. package/core/computed/metrics/lantern-max-potential-fid.js +3 -2
  11. package/core/computed/metrics/lantern-metric.d.ts +5 -0
  12. package/core/computed/metrics/lantern-metric.js +23 -1
  13. package/core/computed/metrics/lantern-speed-index.js +3 -2
  14. package/core/computed/navigation-insights.d.ts +36 -0
  15. package/core/computed/navigation-insights.js +35 -0
  16. package/core/computed/trace-engine-result.d.ts +16 -3
  17. package/core/computed/trace-engine-result.js +19 -14
  18. package/core/config/default-config.js +6 -51
  19. package/core/config/filters.js +0 -1
  20. package/core/gather/gatherers/root-causes.d.ts +2 -2
  21. package/core/gather/gatherers/root-causes.js +6 -5
  22. package/core/gather/gatherers/trace-elements.d.ts +2 -2
  23. package/core/gather/gatherers/trace-elements.js +2 -2
  24. package/core/lib/lantern/cpu-node.d.ts +7 -1
  25. package/core/lib/lantern/cpu-node.js +12 -2
  26. package/core/lib/lantern/lantern-error.d.ts +8 -0
  27. package/core/lib/lantern/lantern-error.js +9 -0
  28. package/core/lib/lantern/metrics/first-meaningful-paint.js +2 -4
  29. package/core/lib/lantern/metrics/interactive.js +1 -1
  30. package/core/lib/lantern/metrics/largest-contentful-paint.js +3 -3
  31. package/core/lib/lantern/network-node.js +28 -4
  32. package/core/lib/lantern/page-dependency-graph.js +14 -2
  33. package/core/lib/lantern/simulator/simulator.js +1 -1
  34. package/core/lib/lh-error.js +3 -3
  35. package/core/runner.js +0 -1
  36. package/dist/report/bundle.esm.js +11 -95
  37. package/dist/report/flow.js +16 -100
  38. package/dist/report/standalone.js +12 -96
  39. package/flow-report/src/help-dialog.tsx +0 -1
  40. package/flow-report/src/i18n/i18n.d.ts +0 -4
  41. package/flow-report/src/i18n/ui-strings.d.ts +0 -1
  42. package/flow-report/src/i18n/ui-strings.js +0 -2
  43. package/package.json +1 -1
  44. package/readme.md +2 -2
  45. package/report/assets/styles.css +1 -43
  46. package/report/assets/templates.html +0 -95
  47. package/report/renderer/components.d.ts +2 -2
  48. package/report/renderer/components.js +2 -103
  49. package/report/renderer/report-renderer.js +2 -12
  50. package/report/renderer/report-ui-features.js +1 -1
  51. package/report/renderer/report-utils.d.ts +0 -1
  52. package/report/renderer/report-utils.js +0 -3
  53. package/shared/localization/locales/ar-XB.json +0 -174
  54. package/shared/localization/locales/ar.json +0 -174
  55. package/shared/localization/locales/bg.json +0 -174
  56. package/shared/localization/locales/ca.json +0 -174
  57. package/shared/localization/locales/cs.json +0 -174
  58. package/shared/localization/locales/da.json +0 -174
  59. package/shared/localization/locales/de.json +0 -174
  60. package/shared/localization/locales/el.json +0 -174
  61. package/shared/localization/locales/en-GB.json +0 -174
  62. package/shared/localization/locales/en-US.json +0 -177
  63. package/shared/localization/locales/en-XA.json +0 -174
  64. package/shared/localization/locales/en-XL.json +0 -177
  65. package/shared/localization/locales/es-419.json +0 -174
  66. package/shared/localization/locales/es.json +0 -174
  67. package/shared/localization/locales/fi.json +0 -174
  68. package/shared/localization/locales/fil.json +0 -174
  69. package/shared/localization/locales/fr.json +0 -174
  70. package/shared/localization/locales/he.json +0 -174
  71. package/shared/localization/locales/hi.json +0 -174
  72. package/shared/localization/locales/hr.json +0 -174
  73. package/shared/localization/locales/hu.json +0 -174
  74. package/shared/localization/locales/id.json +0 -174
  75. package/shared/localization/locales/it.json +0 -174
  76. package/shared/localization/locales/ja.json +0 -174
  77. package/shared/localization/locales/ko.json +0 -174
  78. package/shared/localization/locales/lt.json +0 -174
  79. package/shared/localization/locales/lv.json +0 -174
  80. package/shared/localization/locales/nl.json +0 -174
  81. package/shared/localization/locales/no.json +0 -174
  82. package/shared/localization/locales/pl.json +0 -174
  83. package/shared/localization/locales/pt-PT.json +0 -174
  84. package/shared/localization/locales/pt.json +0 -174
  85. package/shared/localization/locales/ro.json +0 -174
  86. package/shared/localization/locales/ru.json +0 -174
  87. package/shared/localization/locales/sk.json +0 -174
  88. package/shared/localization/locales/sl.json +0 -174
  89. package/shared/localization/locales/sr-Latn.json +0 -174
  90. package/shared/localization/locales/sr.json +0 -174
  91. package/shared/localization/locales/sv.json +0 -174
  92. package/shared/localization/locales/ta.json +0 -174
  93. package/shared/localization/locales/te.json +0 -174
  94. package/shared/localization/locales/th.json +0 -174
  95. package/shared/localization/locales/tr.json +0 -174
  96. package/shared/localization/locales/uk.json +0 -174
  97. package/shared/localization/locales/vi.json +0 -174
  98. package/shared/localization/locales/zh-HK.json +0 -174
  99. package/shared/localization/locales/zh-TW.json +0 -174
  100. package/shared/localization/locales/zh.json +0 -174
  101. package/tsconfig.json +0 -1
  102. package/types/artifacts.d.ts +15 -90
  103. package/types/audit.d.ts +0 -10
  104. package/types/internal/smokehouse.d.ts +1 -1
  105. package/types/lhr/lhr.d.ts +0 -2
  106. package/core/audits/content-width.d.ts +0 -17
  107. package/core/audits/content-width.js +0 -76
  108. package/core/audits/installable-manifest.d.ts +0 -111
  109. package/core/audits/installable-manifest.js +0 -248
  110. package/core/audits/manual/pwa-cross-browser.d.ts +0 -12
  111. package/core/audits/manual/pwa-cross-browser.js +0 -40
  112. package/core/audits/manual/pwa-each-page-has-url.d.ts +0 -12
  113. package/core/audits/manual/pwa-each-page-has-url.js +0 -38
  114. package/core/audits/manual/pwa-page-transitions.d.ts +0 -12
  115. package/core/audits/manual/pwa-page-transitions.js +0 -38
  116. package/core/audits/maskable-icon.d.ts +0 -25
  117. package/core/audits/maskable-icon.js +0 -70
  118. package/core/audits/multi-check-audit.d.ts +0 -28
  119. package/core/audits/multi-check-audit.js +0 -82
  120. package/core/audits/splash-screen.d.ts +0 -37
  121. package/core/audits/splash-screen.js +0 -98
  122. package/core/audits/themed-omnibox.d.ts +0 -42
  123. package/core/audits/themed-omnibox.js +0 -102
  124. package/core/computed/manifest-values.d.ts +0 -24
  125. package/core/computed/manifest-values.js +0 -136
  126. package/core/gather/gatherers/dobetterweb/tags-blocking-first-paint.d.ts +0 -47
  127. package/core/gather/gatherers/dobetterweb/tags-blocking-first-paint.js +0 -233
  128. package/core/gather/gatherers/installability-errors.d.ts +0 -17
  129. package/core/gather/gatherers/installability-errors.js +0 -56
  130. package/core/gather/gatherers/web-app-manifest.d.ts +0 -32
  131. package/core/gather/gatherers/web-app-manifest.js +0 -106
  132. package/report/renderer/pwa-category-renderer.d.ts +0 -55
  133. package/report/renderer/pwa-category-renderer.js +0 -184
  134. package/third-party/chromium-synchronization/installability-errors-test.js +0 -79
@@ -1,98 +0,0 @@
1
- /**
2
- * @license
3
- * Copyright 2017 Google LLC
4
- * SPDX-License-Identifier: Apache-2.0
5
- */
6
-
7
- import MultiCheckAudit from './multi-check-audit.js';
8
- import {ManifestValues} from '../computed/manifest-values.js';
9
- import * as i18n from '../lib/i18n/i18n.js';
10
-
11
- const UIStrings = {
12
- /** Title of a Lighthouse audit that provides detail on splash screens. This descriptive title is shown to users when the site has a custom splash screen. */
13
- title: 'Configured for a custom splash screen',
14
- /** Title of a Lighthouse audit that provides detail on splash screens. This descriptive title is shown to users when the site does not have a custom splash screen. */
15
- failureTitle: 'Is not configured for a custom splash screen',
16
- /** Description of a Lighthouse audit that tells the user why they should configure a custom splash screen. This is displayed after a user expands the section to see more. No character length limits. The last sentence starting with 'Learn' becomes link text to additional documentation. */
17
- description: 'A themed splash screen ensures a high-quality experience when ' +
18
- 'users launch your app from their homescreens. ' +
19
- '[Learn more about splash screens](https://developer.chrome.com/docs/lighthouse/pwa/splash-screen/).',
20
- };
21
-
22
- const str_ = i18n.createIcuMessageFn(import.meta.url, UIStrings);
23
-
24
- /**
25
- * @fileoverview
26
- * Audits if a page is configured for a custom splash screen when launched
27
- * https://github.com/GoogleChrome/lighthouse/issues/24
28
- *
29
- * Requirements:
30
- * * manifest is not empty
31
- * * manifest has a valid name
32
- * * manifest has a valid background_color
33
- * * manifest has a valid theme_color
34
- * * manifest contains icon that's a png and size >= 512px
35
- */
36
-
37
- class SplashScreen extends MultiCheckAudit {
38
- /**
39
- * @return {LH.Audit.Meta}
40
- */
41
- static get meta() {
42
- return {
43
- id: 'splash-screen',
44
- title: str_(UIStrings.title),
45
- failureTitle: str_(UIStrings.failureTitle),
46
- description: str_(UIStrings.description),
47
- supportedModes: ['navigation'],
48
- requiredArtifacts: ['WebAppManifest', 'InstallabilityErrors'],
49
- };
50
- }
51
-
52
- /**
53
- * @param {LH.Artifacts.ManifestValues} manifestValues
54
- * @param {Array<string>} failures
55
- */
56
- static assessManifest(manifestValues, failures) {
57
- if (manifestValues.isParseFailure && manifestValues.parseFailureReason) {
58
- failures.push(manifestValues.parseFailureReason);
59
- return;
60
- }
61
-
62
- const splashScreenCheckIds = [
63
- 'hasName',
64
- 'hasBackgroundColor',
65
- 'hasThemeColor',
66
- 'hasIconsAtLeast512px',
67
- ];
68
-
69
- manifestValues.allChecks
70
- .filter(item => splashScreenCheckIds.includes(item.id))
71
- .forEach(item => {
72
- if (!item.passing) {
73
- failures.push(item.failureText);
74
- }
75
- });
76
- }
77
-
78
- /**
79
- * @param {LH.Artifacts} artifacts
80
- * @param {LH.Audit.Context} context
81
- * @return {Promise<{failures: Array<string>, manifestValues: LH.Artifacts.ManifestValues}>}
82
- */
83
- static async audit_(artifacts, context) {
84
- /** @type {Array<string>} */
85
- const failures = [];
86
-
87
- const manifestValues = await ManifestValues.request(artifacts, context);
88
- SplashScreen.assessManifest(manifestValues, failures);
89
-
90
- return {
91
- failures,
92
- manifestValues,
93
- };
94
- }
95
- }
96
-
97
- export default SplashScreen;
98
- export {UIStrings};
@@ -1,42 +0,0 @@
1
- export default ThemedOmnibox;
2
- /**
3
- * @fileoverview
4
- * Audits if a page is configured for a themed address bar
5
- *
6
- * Requirements:
7
- * * manifest is not empty
8
- * * manifest has a theme_color
9
- * * HTML has a theme-color meta
10
- *
11
- * Color validity is explicitly not checked.
12
- */
13
- declare class ThemedOmnibox extends MultiCheckAudit {
14
- /**
15
- * @param {LH.Artifacts.MetaElement|undefined} themeColorMeta
16
- * @param {Array<string>} failures
17
- */
18
- static assessMetaThemecolor(themeColorMeta: LH.Artifacts.MetaElement | undefined, failures: Array<string>): void;
19
- /**
20
- * @param {LH.Artifacts.ManifestValues} manifestValues
21
- * @param {Array<string>} failures
22
- */
23
- static assessManifest(manifestValues: LH.Artifacts.ManifestValues, failures: Array<string>): void;
24
- /**
25
- * @param {LH.Artifacts} artifacts
26
- * @param {LH.Audit.Context} context
27
- * @return {Promise<{failures: Array<string>, manifestValues: LH.Artifacts.ManifestValues, themeColor: ?string}>}
28
- */
29
- static audit_(artifacts: LH.Artifacts, context: LH.Audit.Context): Promise<{
30
- failures: Array<string>;
31
- manifestValues: LH.Artifacts.ManifestValues;
32
- themeColor: string | null;
33
- }>;
34
- }
35
- export namespace UIStrings {
36
- const title: string;
37
- const failureTitle: string;
38
- const description: string;
39
- }
40
- import MultiCheckAudit from './multi-check-audit.js';
41
- import { ManifestValues } from '../computed/manifest-values.js';
42
- //# sourceMappingURL=themed-omnibox.d.ts.map
@@ -1,102 +0,0 @@
1
- /**
2
- * @license
3
- * Copyright 2017 Google LLC
4
- * SPDX-License-Identifier: Apache-2.0
5
- */
6
-
7
- import MultiCheckAudit from './multi-check-audit.js';
8
- import {ManifestValues} from '../computed/manifest-values.js';
9
- import * as i18n from '../lib/i18n/i18n.js';
10
-
11
- const UIStrings = {
12
- /** Title of a Lighthouse audit that provides detail on the theme color the web page has set for the browser's address bar. This descriptive title is shown to users when an address-bar theme color has been set. */
13
- title: 'Sets a theme color for the address bar.',
14
- /** Title of a Lighthouse audit that provides detail on the theme color the web page has set for the browser's address bar. This descriptive title is shown to users when an address-bar theme color has not been set. */
15
- failureTitle: 'Does not set a theme color for the address bar.',
16
- /** Description of a Lighthouse audit that tells the user why they should set a theme color for the browser's address bar. This is displayed after a user expands the section to see more. No character length limits. The last sentence starting with 'Learn' becomes link text to additional documentation. */
17
- description: 'The browser address bar can be themed to match your site. ' +
18
- '[Learn more about theming the address bar](https://developer.chrome.com/docs/lighthouse/pwa/themed-omnibox/).',
19
- };
20
-
21
- const str_ = i18n.createIcuMessageFn(import.meta.url, UIStrings);
22
-
23
- /**
24
- * @fileoverview
25
- * Audits if a page is configured for a themed address bar
26
- *
27
- * Requirements:
28
- * * manifest is not empty
29
- * * manifest has a theme_color
30
- * * HTML has a theme-color meta
31
- *
32
- * Color validity is explicitly not checked.
33
- */
34
-
35
- class ThemedOmnibox extends MultiCheckAudit {
36
- /**
37
- * @return {LH.Audit.Meta}
38
- */
39
- static get meta() {
40
- return {
41
- id: 'themed-omnibox',
42
- title: str_(UIStrings.title),
43
- failureTitle: str_(UIStrings.failureTitle),
44
- description: str_(UIStrings.description),
45
- supportedModes: ['navigation'],
46
- requiredArtifacts: ['WebAppManifest', 'InstallabilityErrors', 'MetaElements'],
47
- };
48
- }
49
-
50
- /**
51
- * @param {LH.Artifacts.MetaElement|undefined} themeColorMeta
52
- * @param {Array<string>} failures
53
- */
54
- static assessMetaThemecolor(themeColorMeta, failures) {
55
- if (!themeColorMeta) {
56
- // TODO(#7238): i18n
57
- failures.push('No `<meta name="theme-color">` tag found');
58
- } else if (!themeColorMeta.content) {
59
- failures.push('The theme-color meta tag did not contain a content value');
60
- }
61
- }
62
-
63
- /**
64
- * @param {LH.Artifacts.ManifestValues} manifestValues
65
- * @param {Array<string>} failures
66
- */
67
- static assessManifest(manifestValues, failures) {
68
- if (manifestValues.isParseFailure && manifestValues.parseFailureReason) {
69
- failures.push(manifestValues.parseFailureReason);
70
- return;
71
- }
72
-
73
- const themeColorCheck = manifestValues.allChecks.find(i => i.id === 'hasThemeColor');
74
- if (themeColorCheck && !themeColorCheck.passing) {
75
- failures.push(themeColorCheck.failureText);
76
- }
77
- }
78
-
79
- /**
80
- * @param {LH.Artifacts} artifacts
81
- * @param {LH.Audit.Context} context
82
- * @return {Promise<{failures: Array<string>, manifestValues: LH.Artifacts.ManifestValues, themeColor: ?string}>}
83
- */
84
- static async audit_(artifacts, context) {
85
- /** @type {Array<string>} */
86
- const failures = [];
87
-
88
- const themeColorMeta = artifacts.MetaElements.find(meta => meta.name === 'theme-color');
89
- const manifestValues = await ManifestValues.request(artifacts, context);
90
- ThemedOmnibox.assessManifest(manifestValues, failures);
91
- ThemedOmnibox.assessMetaThemecolor(themeColorMeta, failures);
92
-
93
- return {
94
- failures,
95
- manifestValues,
96
- themeColor: themeColorMeta?.content || null,
97
- };
98
- }
99
- }
100
-
101
- export default ThemedOmnibox;
102
- export {UIStrings};
@@ -1,24 +0,0 @@
1
- export { ManifestValuesComputed as ManifestValues };
2
- declare const ManifestValuesComputed: typeof ManifestValues & {
3
- request: (dependencies: Pick<import("../index.js").Artifacts, "InstallabilityErrors" | "WebAppManifest">, context: import("../../types/utility-types.js").default.ImmutableObject<{
4
- computedCache: Map<string, import("../lib/arbitrary-equality-map.js").ArbitraryEqualityMap>;
5
- }>) => Promise<import("../index.js").Artifacts.ManifestValues>;
6
- };
7
- declare class ManifestValues {
8
- /** @typedef {(val: NonNullable<LH.Artifacts.Manifest['value']>, errors: LH.Artifacts.InstallabilityErrors['errors']) => boolean} Validator */
9
- /**
10
- * @return {Array<{id: LH.Artifacts.ManifestValueCheckID, failureText: string, validate: Validator}>}
11
- */
12
- static get manifestChecks(): {
13
- id: LH.Artifacts.ManifestValueCheckID;
14
- failureText: string;
15
- validate: (val: NonNullable<LH.Artifacts.Manifest['value']>, errors: LH.Artifacts.InstallabilityErrors['errors']) => boolean;
16
- }[];
17
- /**
18
- * Returns results of all manifest checks
19
- * @param {Pick<LH.Artifacts, 'WebAppManifest'|'InstallabilityErrors'>} Manifest
20
- * @return {Promise<LH.Artifacts.ManifestValues>}
21
- */
22
- static compute_({ WebAppManifest, InstallabilityErrors }: Pick<LH.Artifacts, 'WebAppManifest' | 'InstallabilityErrors'>): Promise<LH.Artifacts.ManifestValues>;
23
- }
24
- //# sourceMappingURL=manifest-values.d.ts.map
@@ -1,136 +0,0 @@
1
- /**
2
- * @license
3
- * Copyright 2016 Google LLC
4
- * SPDX-License-Identifier: Apache-2.0
5
- */
6
-
7
- import {makeComputedArtifact} from './computed-artifact.js';
8
- import * as icons from '../lib/icons.js';
9
-
10
- const PWA_DISPLAY_VALUES = ['minimal-ui', 'fullscreen', 'standalone'];
11
-
12
- // Historically, Chrome recommended 12 chars as the maximum short_name length to prevent truncation.
13
- // For more discussion, see https://github.com/GoogleChrome/lighthouse/issues/69 and https://developer.chrome.com/apps/manifest/name#short_name
14
- const SUGGESTED_SHORTNAME_LENGTH = 12;
15
-
16
- class ManifestValues {
17
- /** @typedef {(val: NonNullable<LH.Artifacts.Manifest['value']>, errors: LH.Artifacts.InstallabilityErrors['errors']) => boolean} Validator */
18
-
19
- /**
20
- * @return {Array<{id: LH.Artifacts.ManifestValueCheckID, failureText: string, validate: Validator}>}
21
- */
22
- static get manifestChecks() {
23
- return [
24
- {
25
- id: 'hasStartUrl',
26
- failureText: 'Manifest does not contain a `start_url`',
27
- validate: manifestValue => !!manifestValue.start_url.value,
28
- },
29
- {
30
- id: 'hasIconsAtLeast144px',
31
- failureText: 'Manifest does not have a PNG icon of at least 144px',
32
- validate: manifestValue => icons.doExist(manifestValue) &&
33
- icons.pngSizedAtLeast(144, manifestValue).length > 0,
34
- },
35
- {
36
- id: 'hasIconsAtLeast512px',
37
- failureText: 'Manifest does not have a PNG icon of at least 512px',
38
- validate: manifestValue => icons.doExist(manifestValue) &&
39
- icons.pngSizedAtLeast(512, manifestValue).length > 0,
40
- },
41
- {
42
- id: 'fetchesIcon',
43
- failureText: 'Manifest icon failed to be fetched',
44
- validate: (manifestValue, errors) => {
45
- const failedToFetchIconErrorIds = [
46
- 'cannot-download-icon',
47
- 'no-icon-available',
48
- ];
49
- return icons.doExist(manifestValue) &&
50
- !errors.some(error => failedToFetchIconErrorIds.includes(error.errorId));
51
- },
52
- },
53
- {
54
- id: 'hasPWADisplayValue',
55
- failureText: 'Manifest\'s `display` value is not one of: ' + PWA_DISPLAY_VALUES.join(' | '),
56
- validate: manifestValue => PWA_DISPLAY_VALUES.includes(manifestValue.display.value),
57
- },
58
- {
59
- id: 'hasBackgroundColor',
60
- failureText: 'Manifest does not have `background_color`',
61
- validate: manifestValue => !!manifestValue.background_color.value,
62
- },
63
- {
64
- id: 'hasThemeColor',
65
- failureText: 'Manifest does not have `theme_color`',
66
- validate: manifestValue => !!manifestValue.theme_color.value,
67
- },
68
- {
69
- id: 'hasShortName',
70
- failureText: 'Manifest does not have `short_name`',
71
- validate: manifestValue => !!manifestValue.short_name.value,
72
- },
73
- {
74
- id: 'shortNameLength',
75
- failureText: `Manifest's \`short_name\` is too long (>${SUGGESTED_SHORTNAME_LENGTH} ` +
76
- `characters) to be displayed on a homescreen without truncation`,
77
- // Pass if there's no short_name. Don't want to report a non-existent string is too long
78
- validate: manifestValue => !!manifestValue.short_name.value &&
79
- manifestValue.short_name.value.length <= SUGGESTED_SHORTNAME_LENGTH,
80
- },
81
- {
82
- id: 'hasName',
83
- failureText: 'Manifest does not have `name`',
84
- validate: manifestValue => !!manifestValue.name.value,
85
- },
86
- {
87
- id: 'hasMaskableIcon',
88
- failureText: 'Manifest does not have at least one icon that is maskable',
89
- validate: ManifestValue => icons.doExist(ManifestValue) &&
90
- icons.containsMaskableIcon(ManifestValue),
91
- },
92
- ];
93
- }
94
-
95
- /**
96
- * Returns results of all manifest checks
97
- * @param {Pick<LH.Artifacts, 'WebAppManifest'|'InstallabilityErrors'>} Manifest
98
- * @return {Promise<LH.Artifacts.ManifestValues>}
99
- */
100
- static async compute_({WebAppManifest, InstallabilityErrors}) {
101
- // if the manifest isn't there or is invalid json, we report that and bail
102
- if (WebAppManifest === null) {
103
- return {
104
- isParseFailure: true,
105
- parseFailureReason: 'No manifest was fetched',
106
- allChecks: [],
107
- };
108
- }
109
- const manifestValue = WebAppManifest.value;
110
- if (manifestValue === undefined) {
111
- return {
112
- isParseFailure: true,
113
- parseFailureReason: 'Manifest failed to parse as valid JSON',
114
- allChecks: [],
115
- };
116
- }
117
-
118
- // manifest is valid, so do the rest of the checks
119
- const remainingChecks = ManifestValues.manifestChecks.map(item => {
120
- return {
121
- id: item.id,
122
- failureText: item.failureText,
123
- passing: item.validate(manifestValue, InstallabilityErrors.errors),
124
- };
125
- });
126
-
127
- return {
128
- isParseFailure: false,
129
- allChecks: remainingChecks,
130
- };
131
- }
132
- }
133
-
134
- const ManifestValuesComputed =
135
- makeComputedArtifact(ManifestValues, ['InstallabilityErrors', 'WebAppManifest']);
136
- export {ManifestValuesComputed as ManifestValues};
@@ -1,47 +0,0 @@
1
- export default TagsBlockingFirstPaint;
2
- export type MediaChange = {
3
- href: string;
4
- media: string;
5
- msSinceHTMLEnd: number;
6
- matches: boolean;
7
- };
8
- export type LinkTag = {
9
- tagName: 'LINK';
10
- url: string;
11
- href: string;
12
- rel: string;
13
- media: string;
14
- disabled: boolean;
15
- mediaChanges: Array<MediaChange>;
16
- };
17
- export type ScriptTag = {
18
- tagName: 'SCRIPT';
19
- url: string;
20
- src: string;
21
- };
22
- declare class TagsBlockingFirstPaint extends BaseGatherer {
23
- /**
24
- * @param {Array<LH.Artifacts.NetworkRequest>} networkRecords
25
- * @return {Map<string, LH.Artifacts.NetworkRequest>}
26
- */
27
- static _filteredAndIndexedByUrl(networkRecords: Array<LH.Artifacts.NetworkRequest>): Map<string, LH.Artifacts.NetworkRequest>;
28
- /**
29
- * @param {LH.Gatherer.Driver} driver
30
- * @param {Array<LH.Artifacts.NetworkRequest>} networkRecords
31
- * @return {Promise<Array<LH.Artifacts.TagBlockingFirstPaint>>}
32
- */
33
- static findBlockingTags(driver: LH.Gatherer.Driver, networkRecords: Array<LH.Artifacts.NetworkRequest>): Promise<Array<LH.Artifacts.TagBlockingFirstPaint>>;
34
- /** @type {LH.Gatherer.GathererMeta<'DevtoolsLog'>} */
35
- meta: LH.Gatherer.GathererMeta<'DevtoolsLog'>;
36
- /**
37
- * @param {LH.Gatherer.Context} context
38
- */
39
- startSensitiveInstrumentation(context: LH.Gatherer.Context): Promise<void>;
40
- /**
41
- * @param {LH.Gatherer.Context<'DevtoolsLog'>} context
42
- * @return {Promise<LH.Artifacts['TagsBlockingFirstPaint']>}
43
- */
44
- getArtifact(context: LH.Gatherer.Context<'DevtoolsLog'>): Promise<LH.Artifacts['TagsBlockingFirstPaint']>;
45
- }
46
- import BaseGatherer from '../../base-gatherer.js';
47
- //# sourceMappingURL=tags-blocking-first-paint.d.ts.map
@@ -1,233 +0,0 @@
1
- /**
2
- * @license
3
- * Copyright 2016 Google LLC
4
- * SPDX-License-Identifier: Apache-2.0
5
- */
6
- /**
7
- * @fileoverview
8
- * Identifies stylesheets, HTML Imports, and scripts that potentially block
9
- * the first paint of the page by running several scripts in the page context.
10
- * Candidate blocking tags are collected by querying for all script tags in
11
- * the head of the page and all link tags that are either matching media
12
- * stylesheets or non-async HTML imports. These are then compared to the
13
- * network requests to ensure they were initiated by the parser and not
14
- * injected with script. To avoid false positives from strategies like
15
- * (http://filamentgroup.github.io/loadCSS/test/preload.html), a separate
16
- * script is run to flag all links that at one point were rel=preload.
17
- */
18
-
19
-
20
- import {NetworkRecords} from '../../../computed/network-records.js';
21
- import DevtoolsLog from '../devtools-log.js';
22
- import BaseGatherer from '../../base-gatherer.js';
23
-
24
- /* global document, window, HTMLLinkElement, SVGScriptElement */
25
-
26
- /** @typedef {{href: string, media: string, msSinceHTMLEnd: number, matches: boolean}} MediaChange */
27
- /** @typedef {{tagName: 'LINK', url: string, href: string, rel: string, media: string, disabled: boolean, mediaChanges: Array<MediaChange>}} LinkTag */
28
- /** @typedef {{tagName: 'SCRIPT', url: string, src: string}} ScriptTag */
29
-
30
- /* c8 ignore start */
31
- function installMediaListener() {
32
- // @ts-expect-error - inserted in page to track media changes.
33
- window.___linkMediaChanges = [];
34
- Object.defineProperty(HTMLLinkElement.prototype, 'media', {
35
- set: function(val) {
36
- /** @type {MediaChange} */
37
- const mediaChange = {
38
- href: this.href,
39
- media: val,
40
- msSinceHTMLEnd: Date.now() - performance.timing.responseEnd,
41
- matches: window.matchMedia(val).matches,
42
- };
43
- // @ts-expect-error - `___linkMediaChanges` created above.
44
- window.___linkMediaChanges.push(mediaChange);
45
-
46
- this.setAttribute('media', val);
47
- },
48
- });
49
- }
50
- /* c8 ignore stop */
51
-
52
- /**
53
- * @return {Promise<Array<LinkTag | ScriptTag>>}
54
- */
55
- /* c8 ignore start */
56
- async function collectTagsThatBlockFirstPaint() {
57
- /** @type {Array<MediaChange>} */
58
- // @ts-expect-error - `___linkMediaChanges` created in `installMediaListener`.
59
- const linkMediaChanges = window.___linkMediaChanges;
60
-
61
- try {
62
- /** @type {Array<LinkTag>} */
63
- const linkTags = [...document.querySelectorAll('link')]
64
- .filter(linkTag => {
65
- // Ignore malformed links with no href (e.g. `<link rel="stylesheet" href="">`)
66
- // The resolved `linkTag.href` will be the main document, but the main document
67
- // should never be render blocking.
68
- if (!linkTag.getAttribute('href')) return false;
69
-
70
- // Filter stylesheet/HTML imports that block rendering.
71
- // https://www.igvita.com/2012/06/14/debunking-responsive-css-performance-myths/
72
- // https://www.w3.org/TR/html-imports/#dfn-import-async-attribute
73
- const blockingStylesheet = linkTag.rel === 'stylesheet' &&
74
- window.matchMedia(linkTag.media).matches && !linkTag.disabled;
75
- const blockingImport = linkTag.rel === 'import' && !linkTag.hasAttribute('async');
76
- return blockingStylesheet || blockingImport;
77
- })
78
- .map(tag => {
79
- return {
80
- tagName: 'LINK',
81
- url: tag.href,
82
- href: tag.href,
83
- rel: tag.rel,
84
- media: tag.media,
85
- disabled: tag.disabled,
86
- mediaChanges: linkMediaChanges.filter(item => item.href === tag.href),
87
- };
88
- });
89
-
90
- /** @type {Array<ScriptTag>} */
91
- const scriptTags = [...document.querySelectorAll('head script[src]')]
92
- .filter(/** @return {scriptTag is HTMLScriptElement} */ scriptTag => {
93
- // SVGScriptElement can't appear in <head> (it'll be kicked to <body>), but keep tsc happy.
94
- // https://html.spec.whatwg.org/multipage/semantics.html#the-head-element
95
- if (scriptTag instanceof SVGScriptElement) return false;
96
-
97
- return (
98
- !scriptTag.hasAttribute('async') &&
99
- !scriptTag.hasAttribute('defer') &&
100
- !/^data:/.test(scriptTag.src) &&
101
- !/^blob:/.test(scriptTag.src) &&
102
- scriptTag.getAttribute('type') !== 'module'
103
- );
104
- })
105
- .map(tag => {
106
- return {
107
- tagName: 'SCRIPT',
108
- url: tag.src,
109
- src: tag.src,
110
- };
111
- });
112
-
113
- return [...linkTags, ...scriptTags];
114
- } catch (e) {
115
- const friendly = 'Unable to gather Scripts/Stylesheets/HTML Imports on the page';
116
- throw new Error(`${friendly}: ${e.message}`);
117
- }
118
- }
119
- /* c8 ignore stop */
120
-
121
- class TagsBlockingFirstPaint extends BaseGatherer {
122
- /** @type {LH.Gatherer.GathererMeta<'DevtoolsLog'>} */
123
- meta = {
124
- supportedModes: ['navigation'],
125
- dependencies: {DevtoolsLog: DevtoolsLog.symbol},
126
- };
127
-
128
- /**
129
- * @param {Array<LH.Artifacts.NetworkRequest>} networkRecords
130
- * @return {Map<string, LH.Artifacts.NetworkRequest>}
131
- */
132
- static _filteredAndIndexedByUrl(networkRecords) {
133
- /** @type {Map<string, LH.Artifacts.NetworkRequest>} */
134
- const result = new Map();
135
-
136
- for (const record of networkRecords) {
137
- if (!record.finished) continue;
138
-
139
- const isParserGenerated = record.initiator.type === 'parser';
140
- // A stylesheet only blocks script if it was initiated by the parser
141
- // https://html.spec.whatwg.org/multipage/semantics.html#interactions-of-styling-and-scripting
142
- const isParserScriptOrStyle = /(css|script)/.test(record.mimeType) && isParserGenerated;
143
- const isFailedRequest = record.failed;
144
- const isHtml = record.mimeType && record.mimeType.includes('html');
145
-
146
- // Filter stylesheet, javascript, and html import mimetypes.
147
- // Include 404 scripts/links generated by the parser because they are likely blocking.
148
- if (isHtml || isParserScriptOrStyle || (isFailedRequest && isParserGenerated)) {
149
- result.set(record.url, record);
150
- }
151
- }
152
-
153
- return result;
154
- }
155
-
156
- /**
157
- * @param {LH.Gatherer.Driver} driver
158
- * @param {Array<LH.Artifacts.NetworkRequest>} networkRecords
159
- * @return {Promise<Array<LH.Artifacts.TagBlockingFirstPaint>>}
160
- */
161
- static async findBlockingTags(driver, networkRecords) {
162
- const firstRequestEndTime = networkRecords.reduce(
163
- (min, record) => Math.min(min, record.networkEndTime),
164
- Infinity
165
- );
166
- const tags = await driver.executionContext.evaluate(collectTagsThatBlockFirstPaint, {args: []});
167
- const requests = TagsBlockingFirstPaint._filteredAndIndexedByUrl(networkRecords);
168
-
169
- /** @type {Array<LH.Artifacts.TagBlockingFirstPaint>} */
170
- const result = [];
171
- for (const tag of tags) {
172
- const request = requests.get(tag.url);
173
- if (!request || request.isLinkPreload) continue;
174
-
175
- let endTime = request.networkEndTime;
176
- let mediaChanges;
177
-
178
- if (tag.tagName === 'LINK') {
179
- // Even if the request was initially blocking or appeared to be blocking once the
180
- // page was loaded, the media attribute could have been changed during load, capping the
181
- // amount of time it was render blocking. See https://github.com/GoogleChrome/lighthouse/issues/2832.
182
- const timesResourceBecameNonBlocking = tag.mediaChanges
183
- .filter(change => !change.matches)
184
- .map(change => change.msSinceHTMLEnd);
185
- if (timesResourceBecameNonBlocking.length > 0) {
186
- const earliestNonBlockingTime = Math.min(...timesResourceBecameNonBlocking);
187
- const lastTimeResourceWasBlocking = Math.max(
188
- request.networkRequestTime,
189
- firstRequestEndTime + earliestNonBlockingTime / 1000
190
- );
191
- endTime = Math.min(endTime, lastTimeResourceWasBlocking);
192
- }
193
-
194
- mediaChanges = tag.mediaChanges;
195
- }
196
-
197
- const {tagName, url} = tag;
198
-
199
- result.push({
200
- tag: {tagName, url, mediaChanges},
201
- transferSize: request.transferSize,
202
- startTime: request.networkRequestTime,
203
- endTime,
204
- });
205
-
206
- // Prevent duplicates from showing up again
207
- requests.delete(tag.url);
208
- }
209
-
210
- return result;
211
- }
212
-
213
- /**
214
- * @param {LH.Gatherer.Context} context
215
- */
216
- async startSensitiveInstrumentation(context) {
217
- const {executionContext} = context.driver;
218
- // Don't return return value of `evaluateOnNewDocument`.
219
- await executionContext.evaluateOnNewDocument(installMediaListener, {args: []});
220
- }
221
-
222
- /**
223
- * @param {LH.Gatherer.Context<'DevtoolsLog'>} context
224
- * @return {Promise<LH.Artifacts['TagsBlockingFirstPaint']>}
225
- */
226
- async getArtifact(context) {
227
- const devtoolsLog = context.dependencies.DevtoolsLog;
228
- const networkRecords = await NetworkRecords.request(devtoolsLog, context);
229
- return TagsBlockingFirstPaint.findBlockingTags(context.driver, networkRecords);
230
- }
231
- }
232
-
233
- export default TagsBlockingFirstPaint;