@dcloudio/uni-debugger
Version:
uni-app debugger
2,309 lines (1,111 loc) • 1.45 MB
JavaScript
// lighthouse, browserified. 3.0.3 (d1cae24fda4182406e02d3f4df6309d48878fc50)
require=function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a;}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r);},p,p.exports,r,e,n,t);}return n[i].exports;}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o;}return r;}()({"../audits/accessibility/accesskeys":[function(require,module,exports){
'use strict';
const AxeAudit=require('./axe-audit');
class Accesskeys extends AxeAudit{
static get meta(){
return{
id:'accesskeys',
title:'`[accesskey]` values are unique',
failureTitle:'`[accesskey]` values are not unique',
description:'Access keys let users quickly focus a part of the page. For proper '+
'navigation, each access key must be unique. '+
'[Learn more](https://dequeuniversity.com/rules/axe/2.2/accesskeys?application=lighthouse).',
requiredArtifacts:['Accessibility']};
}}
module.exports=Accesskeys;
},{"./axe-audit":1}],"../audits/accessibility/aria-allowed-attr":[function(require,module,exports){
'use strict';
const AxeAudit=require('./axe-audit');
class ARIAAllowedAttr extends AxeAudit{
static get meta(){
return{
id:'aria-allowed-attr',
title:'`[aria-*]` attributes match their roles',
failureTitle:'`[aria-*]` attributes do not match their roles',
description:'Each ARIA `role` supports a specific subset of `aria-*` attributes. '+
'Mismatching these invalidates the `aria-*` attributes. [Learn '+
'more](https://dequeuniversity.com/rules/axe/2.2/aria-allowed-attr?application=lighthouse).',
requiredArtifacts:['Accessibility']};
}}
module.exports=ARIAAllowedAttr;
},{"./axe-audit":1}],"../audits/accessibility/aria-required-attr":[function(require,module,exports){
'use strict';
const AxeAudit=require('./axe-audit');
class ARIARequiredAttr extends AxeAudit{
static get meta(){
return{
id:'aria-required-attr',
title:'`[role]`s have all required `[aria-*]` attributes',
failureTitle:'`[role]`s do not have all required `[aria-*]` attributes',
description:'Some ARIA roles have required attributes that describe the state '+
'of the element to screen readers. [Learn more](https://dequeuniversity.com/rules/axe/2.2/aria-required-attr?application=lighthouse).',
requiredArtifacts:['Accessibility']};
}}
module.exports=ARIARequiredAttr;
},{"./axe-audit":1}],"../audits/accessibility/aria-required-children":[function(require,module,exports){
'use strict';
const AxeAudit=require('./axe-audit');
class AriaRequiredChildren extends AxeAudit{
static get meta(){
return{
id:'aria-required-children',
title:'Elements with `[role]` that require specific children `[role]`s, are present',
failureTitle:'Elements with `[role]` that require specific children `[role]`s, '+
'are missing.',
description:'Some ARIA parent roles must contain specific child roles to perform '+
'their intended accessibility functions. '+
'[Learn more](https://dequeuniversity.com/rules/axe/2.2/aria-required-children?application=lighthouse).',
requiredArtifacts:['Accessibility']};
}}
module.exports=AriaRequiredChildren;
},{"./axe-audit":1}],"../audits/accessibility/aria-required-parent":[function(require,module,exports){
'use strict';
const AxeAudit=require('./axe-audit');
class AriaRequiredParent extends AxeAudit{
static get meta(){
return{
id:'aria-required-parent',
title:'`[role]`s are contained by their required parent element',
failureTitle:'`[role]`s are not contained by their required parent element',
description:'Some ARIA child roles must be contained by specific parent roles to '+
'properly perform their intended accessibility functions. '+
'[Learn more](https://dequeuniversity.com/rules/axe/2.2/aria-required-parent?application=lighthouse).',
requiredArtifacts:['Accessibility']};
}}
module.exports=AriaRequiredParent;
},{"./axe-audit":1}],"../audits/accessibility/aria-roles":[function(require,module,exports){
'use strict';
const AxeAudit=require('./axe-audit');
class AriaRoles extends AxeAudit{
static get meta(){
return{
id:'aria-roles',
title:'`[role]` values are valid',
failureTitle:'`[role]` values are not valid',
description:'ARIA roles must have valid values in order to perform their '+
'intended accessibility functions. '+
'[Learn more](https://dequeuniversity.com/rules/axe/2.2/aria-roles?application=lighthouse).',
requiredArtifacts:['Accessibility']};
}}
module.exports=AriaRoles;
},{"./axe-audit":1}],"../audits/accessibility/aria-valid-attr-value":[function(require,module,exports){
'use strict';
const AxeAudit=require('./axe-audit');
class ARIAValidAttr extends AxeAudit{
static get meta(){
return{
id:'aria-valid-attr-value',
title:'`[aria-*]` attributes have valid values',
failureTitle:'`[aria-*]` attributes do not have valid values',
description:'Assistive technologies, like screen readers, can\'t interpret ARIA '+
'attributes with invalid values. [Learn '+
'more](https://dequeuniversity.com/rules/axe/2.2/aria-valid-attr-value?application=lighthouse).',
requiredArtifacts:['Accessibility']};
}}
module.exports=ARIAValidAttr;
},{"./axe-audit":1}],"../audits/accessibility/aria-valid-attr":[function(require,module,exports){
'use strict';
const AxeAudit=require('./axe-audit');
class ARIAValidAttr extends AxeAudit{
static get meta(){
return{
id:'aria-valid-attr',
title:'`[aria-*]` attributes are valid and not misspelled',
failureTitle:'`[aria-*]` attributes are not valid or misspelled',
description:'Assistive technologies, like screen readers, can\'t interpret ARIA '+
'attributes with invalid names. [Learn '+
'more](https://dequeuniversity.com/rules/axe/2.2/aria-valid-attr?application=lighthouse).',
requiredArtifacts:['Accessibility']};
}}
module.exports=ARIAValidAttr;
},{"./axe-audit":1}],"../audits/accessibility/audio-caption":[function(require,module,exports){
'use strict';
const AxeAudit=require('./axe-audit');
class AudioCaption extends AxeAudit{
static get meta(){
return{
id:'audio-caption',
title:'`<audio>` elements contain a `<track>` element with `[kind="captions"]`',
failureTitle:'`<audio>` elements are missing a `<track>` element with '+
'`[kind="captions"]`.',
description:'Captions make audio elements usable for deaf or hearing-impaired users, '+
'providing critical information such as who is talking, what they\'re saying, '+
'and other non-speech information. '+
'[Learn more](https://dequeuniversity.com/rules/axe/2.2/audio-caption?application=lighthouse).',
requiredArtifacts:['Accessibility']};
}}
module.exports=AudioCaption;
},{"./axe-audit":1}],"../audits/accessibility/button-name":[function(require,module,exports){
'use strict';
const AxeAudit=require('./axe-audit');
class ButtonName extends AxeAudit{
static get meta(){
return{
id:'button-name',
title:'Buttons have an accessible name',
failureTitle:'Buttons do not have an accessible name',
description:'When a button doesn\'t have an accessible name, screen readers announce it '+
'as "button", making it unusable for users who rely on screen readers. '+
'[Learn more](https://dequeuniversity.com/rules/axe/2.2/button-name?application=lighthouse).',
requiredArtifacts:['Accessibility']};
}}
module.exports=ButtonName;
},{"./axe-audit":1}],"../audits/accessibility/bypass":[function(require,module,exports){
'use strict';
const AxeAudit=require('./axe-audit');
class Bypass extends AxeAudit{
static get meta(){
return{
id:'bypass',
title:'The page contains a heading, skip link, or landmark region',
failureTitle:'The page does not contain a heading, skip link, or landmark region',
description:'Adding ways to bypass repetitive content lets keyboard users navigate the '+
'page more efficiently. '+
'[Learn more](https://dequeuniversity.com/rules/axe/2.2/bypass?application=lighthouse).',
requiredArtifacts:['Accessibility']};
}}
module.exports=Bypass;
},{"./axe-audit":1}],"../audits/accessibility/color-contrast":[function(require,module,exports){
'use strict';
const AxeAudit=require('./axe-audit');
class ColorContrast extends AxeAudit{
static get meta(){
return{
id:'color-contrast',
title:'Background and foreground colors have a sufficient contrast ratio',
failureTitle:'Background and foreground colors do not have a '+
'sufficient contrast ratio.',
description:'Low-contrast text is difficult or impossible for many users to read. '+
'[Learn more](https://dequeuniversity.com/rules/axe/2.2/color-contrast?application=lighthouse).',
requiredArtifacts:['Accessibility']};
}}
module.exports=ColorContrast;
},{"./axe-audit":1}],"../audits/accessibility/definition-list":[function(require,module,exports){
'use strict';
const AxeAudit=require('./axe-audit');
class DefinitionList extends AxeAudit{
static get meta(){
return{
id:'definition-list',
title:'`<dl>`\'s contain only properly-ordered `<dt>` and `<dd>` groups, `<script>` '+
'or `<template>` elements.',
failureTitle:'`<dl>`\'s do not contain only properly-ordered `<dt>` and `<dd>` '+
'groups, `<script>` or `<template>` elements.',
description:'When definition lists are not properly marked up, screen readers may produce '+
'confusing or inaccurate output. '+
'[Learn more](https://dequeuniversity.com/rules/axe/2.2/definition-list?application=lighthouse).',
requiredArtifacts:['Accessibility']};
}}
module.exports=DefinitionList;
},{"./axe-audit":1}],"../audits/accessibility/dlitem":[function(require,module,exports){
'use strict';
const AxeAudit=require('./axe-audit');
class DLItem extends AxeAudit{
static get meta(){
return{
id:'dlitem',
title:'Definition list items are wrapped in `<dl>` elements',
failureTitle:'Definition list items are not wrapped in `<dl>` elements',
description:'Definition list items (`<dt>` and `<dd>`) must be wrapped in a '+
'parent `<dl>` element to ensure that screen readers can properly announce them. '+
'[Learn more](https://dequeuniversity.com/rules/axe/2.2/dlitem?application=lighthouse).',
requiredArtifacts:['Accessibility']};
}}
module.exports=DLItem;
},{"./axe-audit":1}],"../audits/accessibility/document-title":[function(require,module,exports){
'use strict';
const AxeAudit=require('./axe-audit');
class DocumentTitle extends AxeAudit{
static get meta(){
return{
id:'document-title',
title:'Document has a `<title>` element',
failureTitle:'Document doesn\'t have a `<title>` element',
description:'The title gives screen reader users an overview of the page, and search '+
'engine users rely on it heavily to determine if a page is relevant to their search. '+
'[Learn more](https://developers.google.com/web/tools/lighthouse/audits/title).',
requiredArtifacts:['Accessibility']};
}}
module.exports=DocumentTitle;
},{"./axe-audit":1}],"../audits/accessibility/duplicate-id":[function(require,module,exports){
'use strict';
const AxeAudit=require('./axe-audit');
class DuplicateId extends AxeAudit{
static get meta(){
return{
id:'duplicate-id',
title:'`[id]` attributes on the page are unique',
failureTitle:'`[id]` attributes on the page are not unique',
description:'The value of an id attribute must be unique to prevent '+
'other instances from being overlooked by assistive technologies. '+
'[Learn more](https://dequeuniversity.com/rules/axe/2.2/duplicate-id?application=lighthouse).',
requiredArtifacts:['Accessibility']};
}}
module.exports=DuplicateId;
},{"./axe-audit":1}],"../audits/accessibility/frame-title":[function(require,module,exports){
'use strict';
const AxeAudit=require('./axe-audit');
class FrameTitle extends AxeAudit{
static get meta(){
return{
id:'frame-title',
title:'`<frame>` or `<iframe>` elements have a title',
failureTitle:'`<frame>` or `<iframe>` elements do not have a title',
description:'Screen reader users rely on frame titles to describe the contents of frames. '+
'[Learn more](https://dequeuniversity.com/rules/axe/2.2/frame-title?application=lighthouse).',
requiredArtifacts:['Accessibility']};
}}
module.exports=FrameTitle;
},{"./axe-audit":1}],"../audits/accessibility/html-has-lang":[function(require,module,exports){
'use strict';
const AxeAudit=require('./axe-audit');
class HTMLHasLang extends AxeAudit{
static get meta(){
return{
id:'html-has-lang',
title:'`<html>` element has a `[lang]` attribute',
failureTitle:'`<html>` element does not have a `[lang]` attribute',
description:'If a page doesn\'t specify a lang attribute, a screen reader assumes '+
'that the page is in the default language that the user chose when setting up the '+
'screen reader. If the page isn\'t actually in the default language, then the screen '+
'reader might not announce the page\'s text correctly. '+
'[Learn more](https://dequeuniversity.com/rules/axe/2.2/html-lang?application=lighthouse).',
requiredArtifacts:['Accessibility']};
}}
module.exports=HTMLHasLang;
},{"./axe-audit":1}],"../audits/accessibility/html-lang-valid":[function(require,module,exports){
'use strict';
const AxeAudit=require('./axe-audit');
class HTMLLangValid extends AxeAudit{
static get meta(){
return{
id:'html-lang-valid',
title:'`<html>` element has a valid value for its `[lang]` attribute',
failureTitle:'`<html>` element does not have a valid value for '+
'its `[lang]` attribute.',
description:'Specifying a valid [BCP 47 language](https://www.w3.org/International/questions/qa-choosing-language-tags#question) '+
'helps screen readers announce text properly. '+
'[Learn more](https://dequeuniversity.com/rules/axe/2.2/valid-lang?application=lighthouse).',
requiredArtifacts:['Accessibility']};
}}
module.exports=HTMLLangValid;
},{"./axe-audit":1}],"../audits/accessibility/image-alt":[function(require,module,exports){
'use strict';
const AxeAudit=require('./axe-audit');
class ImageAlt extends AxeAudit{
static get meta(){
return{
id:'image-alt',
title:'Image elements have `[alt]` attributes',
failureTitle:'Image elements do not have `[alt]` attributes',
description:'Informative elements should aim for short, descriptive alternate text. '+
'Decorative elements can be ignored with an empty alt attribute. '+
'[Learn more](https://dequeuniversity.com/rules/axe/2.2/image-alt?application=lighthouse).',
requiredArtifacts:['Accessibility']};
}}
module.exports=ImageAlt;
},{"./axe-audit":1}],"../audits/accessibility/input-image-alt":[function(require,module,exports){
'use strict';
const AxeAudit=require('./axe-audit');
class InputImageAlt extends AxeAudit{
static get meta(){
return{
id:'input-image-alt',
title:'`<input type="image">` elements have `[alt]` text',
failureTitle:'`<input type="image">` elements do not have `[alt]` text',
description:'When an image is being used as an `<input>` button, providing alternative '+
'text can help screen reader users understand the purpose of the button. '+
'[Learn more](https://dequeuniversity.com/rules/axe/2.2/input-image-alt?application=lighthouse).',
requiredArtifacts:['Accessibility']};
}}
module.exports=InputImageAlt;
},{"./axe-audit":1}],"../audits/accessibility/label":[function(require,module,exports){
'use strict';
const AxeAudit=require('./axe-audit');
class Label extends AxeAudit{
static get meta(){
return{
id:'label',
title:'Form elements have associated labels',
failureTitle:'Form elements do not have associated labels',
description:'Labels ensure that form controls are announced properly by assistive '+
'technologies, like screen readers. [Learn '+
'more](https://dequeuniversity.com/rules/axe/2.2/label?application=lighthouse).',
requiredArtifacts:['Accessibility']};
}}
module.exports=Label;
},{"./axe-audit":1}],"../audits/accessibility/layout-table":[function(require,module,exports){
'use strict';
const AxeAudit=require('./axe-audit');
class LayoutTable extends AxeAudit{
static get meta(){
return{
id:'layout-table',
title:'Presentational `<table>` elements avoid using `<th>`, `<caption>` or the '+
'`[summary]` attribute.',
failureTitle:'Presentational `<table>` elements do not avoid using `<th>`, '+
'`<caption>` or the `[summary]` attribute.',
description:'A table being used for layout purposes should not include data elements, '+
'such as the th or caption elements or the summary attribute, because this can '+
'create a confusing experience for screen reader users. '+
'[Learn more](https://dequeuniversity.com/rules/axe/2.2/layout-table?application=lighthouse).',
requiredArtifacts:['Accessibility']};
}}
module.exports=LayoutTable;
},{"./axe-audit":1}],"../audits/accessibility/link-name":[function(require,module,exports){
'use strict';
const AxeAudit=require('./axe-audit');
class LinkName extends AxeAudit{
static get meta(){
return{
id:'link-name',
title:'Links have a discernible name',
failureTitle:'Links do not have a discernible name',
description:'Link text (and alternate text for images, when used as links) that is '+
'discernible, unique, and focusable improves the navigation experience for '+
'screen reader users. '+
'[Learn more](https://dequeuniversity.com/rules/axe/2.2/link-name?application=lighthouse).',
requiredArtifacts:['Accessibility']};
}}
module.exports=LinkName;
},{"./axe-audit":1}],"../audits/accessibility/listitem":[function(require,module,exports){
'use strict';
const AxeAudit=require('./axe-audit');
class ListItem extends AxeAudit{
static get meta(){
return{
id:'listitem',
title:'List items (`<li>`) are contained within `<ul>` or `<ol>` parent elements',
failureTitle:'List items (`<li>`) are not contained within `<ul>` '+
'or `<ol>` parent elements.',
description:'Screen readers require list items (`<li>`) to be contained within a '+
'parent `<ul>` or `<ol>` to be announced properly. '+
'[Learn more](https://dequeuniversity.com/rules/axe/2.2/listitem?application=lighthouse).',
requiredArtifacts:['Accessibility']};
}}
module.exports=ListItem;
},{"./axe-audit":1}],"../audits/accessibility/list":[function(require,module,exports){
'use strict';
const AxeAudit=require('./axe-audit');
class List extends AxeAudit{
static get meta(){
return{
id:'list',
title:'Lists contain only `<li>` elements and script supporting elements '+
'(`<script>` and `<template>`).',
failureTitle:'Lists do not contain only `<li>` elements and script '+
'supporting elements (`<script>` and `<template>`).',
description:'Screen readers have a specific way of announcing lists. Ensuring proper list '+
'structure aids screen reader output. '+
'[Learn more](https://dequeuniversity.com/rules/axe/2.2/list?application=lighthouse).',
requiredArtifacts:['Accessibility']};
}}
module.exports=List;
},{"./axe-audit":1}],"../audits/accessibility/manual/custom-controls-labels":[function(require,module,exports){
'use strict';
const ManualAudit=require('../../manual/manual-audit');
class CustomControlsLabels extends ManualAudit{
static get meta(){
return Object.assign({
id:'custom-controls-labels',
description:'Custom interactive controls have associated labels, provided by aria-label or aria-labelledby. [Learn more](https://developers.google.com/web/fundamentals/accessibility/how-to-review#try_it_with_a_screen_reader).',
title:'Custom controls have associated labels'},
super.partialMeta);
}}
module.exports=CustomControlsLabels;
},{"../../manual/manual-audit":4}],"../audits/accessibility/manual/custom-controls-roles":[function(require,module,exports){
'use strict';
const ManualAudit=require('../../manual/manual-audit');
class CustomControlsRoles extends ManualAudit{
static get meta(){
return Object.assign({
id:'custom-controls-roles',
description:'Custom interactive controls have appropriate ARIA roles. [Learn more](https://developers.google.com/web/fundamentals/accessibility/how-to-review#try_it_with_a_screen_reader).',
title:'Custom controls have ARIA roles'},
super.partialMeta);
}}
module.exports=CustomControlsRoles;
},{"../../manual/manual-audit":4}],"../audits/accessibility/manual/focus-traps":[function(require,module,exports){
'use strict';
const ManualAudit=require('../../manual/manual-audit');
class FocusTraps extends ManualAudit{
static get meta(){
return Object.assign({
id:'focus-traps',
description:'A user can tab into and out of any control or region without accidentally trapping their focus. [Learn more](https://developers.google.com/web/fundamentals/accessibility/how-to-review#start_with_the_keyboard).',
title:'User focus is not accidentally trapped in a region'},
super.partialMeta);
}}
module.exports=FocusTraps;
},{"../../manual/manual-audit":4}],"../audits/accessibility/manual/focusable-controls":[function(require,module,exports){
'use strict';
const ManualAudit=require('../../manual/manual-audit');
class FocusableControls extends ManualAudit{
static get meta(){
return Object.assign({
id:'focusable-controls',
description:'Custom interactive controls are keyboard focusable and display a focus indicator. [Learn more](https://developers.google.com/web/fundamentals/accessibility/how-to-review#start_with_the_keyboard).',
title:'Interactive controls are keyboard focusable'},
super.partialMeta);
}}
module.exports=FocusableControls;
},{"../../manual/manual-audit":4}],"../audits/accessibility/manual/heading-levels":[function(require,module,exports){
'use strict';
const ManualAudit=require('../../manual/manual-audit');
class HeadingLevels extends ManualAudit{
static get meta(){
return Object.assign({
id:'heading-levels',
description:'Headings are used to create an outline for the page and heading levels are not skipped. [Learn more](https://developers.google.com/web/fundamentals/accessibility/how-to-review#take_advantage_of_headings_and_landmarks).',
title:'Headings don\'t skip levels'},
super.partialMeta);
}}
module.exports=HeadingLevels;
},{"../../manual/manual-audit":4}],"../audits/accessibility/manual/logical-tab-order":[function(require,module,exports){
'use strict';
const ManualAudit=require('../../manual/manual-audit');
class LogicalTabOrder extends ManualAudit{
static get meta(){
return Object.assign({
id:'logical-tab-order',
description:'Tabbing through the page follows the visual layout. Users cannot focus elements that are offscreen. [Learn more](https://developers.google.com/web/fundamentals/accessibility/how-to-review#start_with_the_keyboard).',
title:'The page has a logical tab order'},
super.partialMeta);
}}
module.exports=LogicalTabOrder;
},{"../../manual/manual-audit":4}],"../audits/accessibility/manual/managed-focus":[function(require,module,exports){
'use strict';
const ManualAudit=require('../../manual/manual-audit');
class ManagedFocus extends ManualAudit{
static get meta(){
return Object.assign({
id:'managed-focus',
description:'If new content, such as a dialog, is added to the page, the user\'s focus is directed to it. [Learn more](https://developers.google.com/web/fundamentals/accessibility/how-to-review#start_with_the_keyboard).',
title:'The user\'s focus is directed to new content added to the page'},
super.partialMeta);
}}
module.exports=ManagedFocus;
},{"../../manual/manual-audit":4}],"../audits/accessibility/manual/offscreen-content-hidden":[function(require,module,exports){
'use strict';
const ManualAudit=require('../../manual/manual-audit');
class OffscreenContentHidden extends ManualAudit{
static get meta(){
return Object.assign({
id:'offscreen-content-hidden',
description:'Offscreen content is hidden with display: none or aria-hidden=true. [Learn more](https://developers.google.com/web/fundamentals/accessibility/how-to-review#try_it_with_a_screen_reader).',
title:'Offscreen content is hidden from assistive technology'},
super.partialMeta);
}}
module.exports=OffscreenContentHidden;
},{"../../manual/manual-audit":4}],"../audits/accessibility/manual/use-landmarks":[function(require,module,exports){
'use strict';
const ManualAudit=require('../../manual/manual-audit');
class UseLandmarks extends ManualAudit{
static get meta(){
return Object.assign({
id:'use-landmarks',
description:'Landmark elements (<main>, <nav>, etc.) are used to improve the keyboard navigation of the page for assistive technology. [Learn more](https://developers.google.com/web/fundamentals/accessibility/how-to-review#take_advantage_of_headings_and_landmarks).',
title:'HTML5 landmark elements are used to improve navigation'},
super.partialMeta);
}}
module.exports=UseLandmarks;
},{"../../manual/manual-audit":4}],"../audits/accessibility/manual/visual-order-follows-dom":[function(require,module,exports){
'use strict';
const ManualAudit=require('../../manual/manual-audit');
class VisualOrderFollowsDOM extends ManualAudit{
static get meta(){
return Object.assign({
id:'visual-order-follows-dom',
description:'DOM order matches the visual order, improving navigation for assistive technology. [Learn more](https://developers.google.com/web/fundamentals/accessibility/how-to-review#try_it_with_a_screen_reader).',
title:'Visual order on the page follows DOM order'},
super.partialMeta);
}}
module.exports=VisualOrderFollowsDOM;
},{"../../manual/manual-audit":4}],"../audits/accessibility/meta-refresh":[function(require,module,exports){
'use strict';
const AxeAudit=require('./axe-audit');
class MetaRefresh extends AxeAudit{
static get meta(){
return{
id:'meta-refresh',
title:'The document does not use `<meta http-equiv="refresh">`',
failureTitle:'The document uses `<meta http-equiv="refresh">`',
description:'Users do not expect a page to refresh automatically, and doing so will move '+
'focus back to the top of the page. This may create a frustrating or '+
'confusing experience. '+
'[Learn more](https://dequeuniversity.com/rules/axe/2.2/meta-refresh?application=lighthouse).',
requiredArtifacts:['Accessibility']};
}}
module.exports=MetaRefresh;
},{"./axe-audit":1}],"../audits/accessibility/meta-viewport":[function(require,module,exports){
'use strict';
const AxeAudit=require('./axe-audit');
class MetaViewport extends AxeAudit{
static get meta(){
return{
id:'meta-viewport',
title:'`[user-scalable="no"]` is not used in the `<meta name="viewport">` '+
'element and the `[maximum-scale]` attribute is not less than 5.',
failureTitle:'`[user-scalable="no"]` is used in the `<meta name="viewport">` '+
'element or the `[maximum-scale]` attribute is less than 5.',
description:'Disabling zooming is problematic for users with low vision who rely on '+
'screen magnification to properly see the contents of a web page. '+
'[Learn more](https://dequeuniversity.com/rules/axe/2.2/meta-viewport?application=lighthouse).',
requiredArtifacts:['Accessibility']};
}}
module.exports=MetaViewport;
},{"./axe-audit":1}],"../audits/accessibility/object-alt":[function(require,module,exports){
'use strict';
const AxeAudit=require('./axe-audit');
class ObjectAlt extends AxeAudit{
static get meta(){
return{
id:'object-alt',
title:'`<object>` elements have `[alt]` text',
failureTitle:'`<object>` elements do not have `[alt]` text',
description:'Screen readers cannot translate non-text content. Adding alt text to '+
'`<object>` elements helps screen readers convey meaning to users. '+
'[Learn more](https://dequeuniversity.com/rules/axe/2.2/object-alt?application=lighthouse).',
requiredArtifacts:['Accessibility']};
}}
module.exports=ObjectAlt;
},{"./axe-audit":1}],"../audits/accessibility/tabindex":[function(require,module,exports){
'use strict';
const AxeAudit=require('./axe-audit');
class TabIndex extends AxeAudit{
static get meta(){
return{
id:'tabindex',
title:'No element has a `[tabindex]` value greater than 0',
failureTitle:'Some elements have a `[tabindex]` value greater than 0',
description:'A value greater than 0 implies an explicit navigation ordering. '+
'Although technically valid, this often creates frustrating experiences '+
'for users who rely on assistive technologies. [Learn more](https://dequeuniversity.com/rules/axe/2.2/tabindex?application=lighthouse).',
requiredArtifacts:['Accessibility']};
}}
module.exports=TabIndex;
},{"./axe-audit":1}],"../audits/accessibility/td-headers-attr":[function(require,module,exports){
'use strict';
const AxeAudit=require('./axe-audit');
class TDHeadersAttr extends AxeAudit{
static get meta(){
return{
id:'td-headers-attr',
title:'Cells in a `<table>` element that use the `[headers]` attribute only refer '+
'to other cells of that same table.',
failureTitle:'Cells in a `<table>` element that use the `[headers]` '+
'attribute refers to other cells of that same table.',
description:'Screen readers have features to make navigating tables easier. Ensuring '+
'`<td>` cells using the `[headers]` attribute only refer to other cells in the same '+
'table may improve the experience for screen reader users. '+
'[Learn more](https://dequeuniversity.com/rules/axe/2.2/td-headers-attr?application=lighthouse).',
requiredArtifacts:['Accessibility']};
}}
module.exports=TDHeadersAttr;
},{"./axe-audit":1}],"../audits/accessibility/th-has-data-cells":[function(require,module,exports){
'use strict';
const AxeAudit=require('./axe-audit');
class THHasDataCells extends AxeAudit{
static get meta(){
return{
id:'th-has-data-cells',
title:'`<th>` elements and elements with `[role="columnheader"/"rowheader"]` have '+
'data cells they describe.',
failureTitle:'`<th>` elements and elements with '+
'`[role="columnheader"/"rowheader"]` do not have data cells they describe.',
description:'Screen readers have features to make navigating tables easier. Ensuring '+
'table headers always refer to some set of cells may improve the experience for screen '+
'reader users. '+
'[Learn more](https://dequeuniversity.com/rules/axe/2.2/th-has-data-cells?application=lighthouse).',
requiredArtifacts:['Accessibility']};
}}
module.exports=THHasDataCells;
},{"./axe-audit":1}],"../audits/accessibility/valid-lang":[function(require,module,exports){
'use strict';
const AxeAudit=require('./axe-audit');
class ValidLang extends AxeAudit{
static get meta(){
return{
id:'valid-lang',
title:'`[lang]` attributes have a valid value',
failureTitle:'`[lang]` attributes do not have a valid value',
description:'Specifying a valid [BCP 47 language](https://www.w3.org/International/questions/qa-choosing-language-tags#question) '+
'on elements helps ensure that text is pronounced correctly by a screen reader. '+
'[Learn more](https://dequeuniversity.com/rules/axe/2.2/valid-lang?application=lighthouse).',
requiredArtifacts:['Accessibility']};
}}
module.exports=ValidLang;
},{"./axe-audit":1}],"../audits/accessibility/video-caption":[function(require,module,exports){
'use strict';
const AxeAudit=require('./axe-audit');
class VideoCaption extends AxeAudit{
static get meta(){
return{
id:'video-caption',
title:'`<video>` elements contain a `<track>` element with `[kind="captions"]`',
failureTitle:'`<video>` elements do not contain a `<track>` element '+
'with `[kind="captions"]`.',
description:'When a video provides a caption it is easier for deaf and hearing impaired '+
'users to access its information. '+
'[Learn more](https://dequeuniversity.com/rules/axe/2.2/video-caption?application=lighthouse).',
requiredArtifacts:['Accessibility']};
}}
module.exports=VideoCaption;
},{"./axe-audit":1}],"../audits/accessibility/video-description":[function(require,module,exports){
'use strict';
const AxeAudit=require('./axe-audit');
class VideoDescription extends AxeAudit{
static get meta(){
return{
id:'video-description',
title:'`<video>` elements contain a `<track>` element with `[kind="description"]`',
failureTitle:'`<video>` elements do not contain a `<track>` element with '+
'`[kind="description"]`.',
description:'Audio descriptions provide relevant information for videos that dialogue '+
'cannot, such as facial expressions and scenes. '+
'[Learn more](https://dequeuniversity.com/rules/axe/2.2/video-description?application=lighthouse).',
requiredArtifacts:['Accessibility']};
}}
module.exports=VideoDescription;
},{"./axe-audit":1}],"../audits/bootup-time":[function(require,module,exports){
'use strict';
const Audit=require('./audit');
const Util=require('../report/html/renderer/util');
const NetworkRequest=require('../lib/network-request');
const{taskGroups}=require('../lib/task-groups');
class BootupTime extends Audit{
static get meta(){
return{
id:'bootup-time',
title:'JavaScript boot-up time',
failureTitle:'JavaScript boot-up time is too high',
scoreDisplayMode:Audit.SCORING_MODES.NUMERIC,
description:'Consider reducing the time spent parsing, compiling, and executing JS. '+
'You may find delivering smaller JS payloads helps with this. [Learn '+
'more](https://developers.google.com/web/tools/lighthouse/audits/bootup).',
requiredArtifacts:['traces']};
}
static get defaultOptions(){
return{
scorePODR:600,
scoreMedian:3500,
thresholdInMs:50};
}
static getJavaScriptURLs(records){
const urls=new Set();
for(const record of records){
if(record.resourceType===NetworkRequest.TYPES.Script){
urls.add(record.url);
}
}
return urls;
}
static getExecutionTimingsByURL(tasks,jsURLs){
const result=new Map();
for(const task of tasks){
const jsURL=task.attributableURLs.find(url=>jsURLs.has(url));
const fallbackURL=task.attributableURLs[0];
const attributableURL=jsURL||fallbackURL;
if(!attributableURL||attributableURL==='about:blank')continue;
const timingByGroupId=result.get(attributableURL)||{};
const originalTime=timingByGroupId[task.group.id]||0;
timingByGroupId[task.group.id]=originalTime+task.selfTime;
result.set(attributableURL,timingByGroupId);
}
return result;
}
static async audit(artifacts,context){
const settings=context.settings||{};
const trace=artifacts.traces[BootupTime.DEFAULT_PASS];
const devtoolsLog=artifacts.devtoolsLogs[BootupTime.DEFAULT_PASS];
const networkRecords=await artifacts.requestNetworkRecords(devtoolsLog);
const tasks=await artifacts.requestMainThreadTasks(trace);
const multiplier=settings.throttlingMethod==='simulate'?
settings.throttling.cpuSlowdownMultiplier:1;
const jsURLs=BootupTime.getJavaScriptURLs(networkRecords);
const executionTimings=BootupTime.getExecutionTimingsByURL(tasks,jsURLs);
let totalBootupTime=0;
const results=Array.from(executionTimings).
map(([url,timingByGroupId])=>{
let bootupTimeForURL=0;
for(const[groupId,timespanMs]of Object.entries(timingByGroupId)){
timingByGroupId[groupId]=timespanMs*multiplier;
bootupTimeForURL+=timespanMs*multiplier;
}
if(bootupTimeForURL>=context.options.thresholdInMs){
totalBootupTime+=bootupTimeForURL;
}
const scriptingTotal=timingByGroupId[taskGroups.scriptEvaluation.id]||0;
const parseCompileTotal=timingByGroupId[taskGroups.scriptParseCompile.id]||0;
return{
url:url,
total:bootupTimeForURL,
scripting:scriptingTotal,
scriptParseCompile:parseCompileTotal};
}).
filter(result=>result.total>=context.options.thresholdInMs).
sort((a,b)=>b.total-a.total);
const summary={wastedMs:totalBootupTime};
const headings=[
{key:'url',itemType:'url',text:'URL'},
{key:'total',granularity:1,itemType:'ms',text:'Total'},
{key:'scripting',granularity:1,itemType:'ms',text:taskGroups.scriptEvaluation.label},
{key:'scriptParseCompile',granularity:1,itemType:'ms',
text:taskGroups.scriptParseCompile.label}];
const details=BootupTime.makeTableDetails(headings,results,summary);
const score=Audit.computeLogNormalScore(
totalBootupTime,
context.options.scorePODR,
context.options.scoreMedian);
return{
score,
rawValue:totalBootupTime,
displayValue:[Util.MS_DISPLAY_VALUE,totalBootupTime],
details};
}}
module.exports=BootupTime;
},{"../lib/network-request":38,"../lib/task-groups":43,"../report/html/renderer/util":48,"./audit":2}],"../audits/byte-efficiency/efficient-animated-content":[function(require,module,exports){
'use strict';
const NetworkRequest=require('../../lib/network-request');
const ByteEfficiencyAudit=require('./byte-efficiency-audit');
const GIF_BYTE_THRESHOLD=100*1024;
class EfficientAnimatedContent extends ByteEfficiencyAudit{
static get meta(){
return{
id:'efficient-animated-content',
scoreDisplayMode:ByteEfficiencyAudit.SCORING_MODES.NUMERIC,
title:'Use video formats for animated content',
description:'Large GIFs are inefficient for delivering animated content. Consider using '+
'MPEG4/WebM videos for animations and PNG/WebP for static images instead of GIF to save '+
'network bytes. [Learn more](https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/replace-animated-gifs-with-video/)',
requiredArtifacts:['devtoolsLogs']};
}
static getPercentSavings(bytes){
return Math.round(29.1*Math.log10(bytes)-100.7)/100;
}
static audit_(artifacts,networkRecords){
const unoptimizedContent=networkRecords.filter(
record=>record.mimeType==='image/gif'&&
record.resourceType===NetworkRequest.TYPES.Image&&
(record.resourceSize||0)>GIF_BYTE_THRESHOLD);
const items=unoptimizedContent.map(record=>{
const resourceSize=record.resourceSize||0;
return{
url:record.url,
totalBytes:resourceSize,
wastedBytes:Math.round(resourceSize*
EfficientAnimatedContent.getPercentSavings(resourceSize))};
});
const headings=[
{key:'url',valueType:'url',label:'URL'},
{key:'totalBytes',valueType:'bytes',label:'Transfer Size'},
{key:'wastedBytes',valueType:'bytes',label:'Byte Savings'}];
return{
items,
headings};
}}
module.exports=EfficientAnimatedContent;
},{"../../lib/network-request":38,"./byte-efficiency-audit":3}],"../audits/byte-efficiency/offscreen-images":[function(require,module,exports){
'use strict';
const ByteEfficiencyAudit=require('./byte-efficiency-audit');
const Sentry=require('../../lib/sentry');
const URL=require('../../lib/url-shim');
const ALLOWABLE_OFFSCREEN_X=100;
const ALLOWABLE_OFFSCREEN_Y=200;
const IGNORE_THRESHOLD_IN_BYTES=2048;
const IGNORE_THRESHOLD_IN_PERCENT=75;
const IGNORE_THRESHOLD_IN_MS=50;
class OffscreenImages extends ByteEfficiencyAudit{
static get meta(){
return{
id:'offscreen-images',
title:'Defer offscreen images',
scoreDisplayMode:ByteEfficiencyAudit.SCORING_MODES.NUMERIC,
description:
'Consider lazy-loading offscreen and hidden images after all critical resources have '+
'finished loading to lower time to interactive. '+
'[Learn more](https://developers.google.com/web/tools/lighthouse/audits/offscreen-images).',
requiredArtifacts:['ImageUsage','ViewportDimensions','traces','devtoolsLogs']};
}
static computeVisiblePixels(imageRect,viewportDimensions){
const innerWidth=viewportDimensions.innerWidth;
const innerHeight=viewportDimensions.innerHeight;
const top=Math.max(imageRect.top,-1*ALLOWABLE_OFFSCREEN_Y);
const right=Math.min(imageRect.right,innerWidth+ALLOWABLE_OFFSCREEN_X);
const bottom=Math.min(imageRect.bottom,innerHeight+ALLOWABLE_OFFSCREEN_Y);
const left=Math.max(imageRect.left,-1*ALLOWABLE_OFFSCREEN_X);
return Math.max(right-left,0)*Math.max(bottom-top,0);
}
static computeWaste(image,viewportDimensions){
if(!image.networkRecord){
return null;
}
const url=URL.elideDataURI(image.src);
const totalPixels=image.clientWidth*image.clientHeight;
const visiblePixels=this.computeVisiblePixels(image.clientRect,viewportDimensions);
const wastedRatio=totalPixels===0?1:1-visiblePixels/totalPixels;
const totalBytes=image.networkRecord.resourceSize;
const wastedBytes=Math.round(totalBytes*wastedRatio);
if(!Number.isFinite(wastedRatio)){
return new Error(`Invalid image sizing information ${url}`);
}
return{
url,
requestStartTime:image.networkRecord.startTime,
totalBytes,
wastedBytes,
wastedPercent:100*wastedRatio};
}
static filterLanternResults(images,lanternMetricData){
const nodeTimings=lanternMetricData.pessimisticEstimate.nodeTimings;
let lastLongTaskStartTime=0;
const startTimesByURL=new Map();
for(const[node,timing]of nodeTimings){
if(node.type==='cpu'&&timing.duration>=50){
lastLongTaskStartTime=Math.max(lastLongTaskStartTime,timing.startTime);
}else if(node.type==='network'){
const networkNode=node;
startTimesByURL.set(networkNode.record.url,timing.startTime);
}
}
return images.filter(image=>{
if(image.wastedBytes<IGNORE_THRESHOLD_IN_BYTES)return false;
if(image.wastedPercent<IGNORE_THRESHOLD_IN_PERCENT)return false;
const imageRequestStartTime=startTimesByURL.get(image.url)||0;
return imageRequestStartTime<lastLongTaskStartTime-IGNORE_THRESHOLD_IN_MS;
});
}
static filterObservedResults(images,interactiveTimestamp){
return images.filter(image=>{
if(image.wastedBytes<IGNORE_THRESHOLD_IN_BYTES)return false;
if(image.wastedPercent<IGNORE_THRESHOLD_IN_PERCENT)return false;
return image.requestStartTime<interactiveTimestamp/1e6-IGNORE_THRESHOLD_IN_MS/1000;
});
}
static computeWasteWithTTIGraph(results,graph,simulator){
return super.computeWasteWithTTIGraph(results,graph,simulator,
{includeLoad:false});
}
static audit_(artifacts,networkRecords,context){
const images=artifacts.ImageUsage;
const viewportDimensions=artifacts.ViewportDimensions;
const trace=artifacts.traces[ByteEfficiencyAudit.DEFAULT_PASS];
const devtoolsLog=artifacts.devtoolsLogs[ByteEfficiencyAudit.DEFAULT_PASS];
const warnings=[];
const resultsMap=images.reduce((results,image)=>{
const processed=OffscreenImages.computeWaste(image,viewportDimensions);
if(processed===null){
return results;
}
if(processed instanceof Error){
warnings.push(processed.message);
Sentry.captureException(processed,{tags:{audit:this.meta.id},level:'warning'});
return results;
}
const existing=results.get(processed.url);
if(!existing||existing.wastedBytes>processed.wastedBytes){
results.set(processed.url,processed);
}
return results;
},new Map());
const settings=context.settings;
return artifacts.requestInteractive({trace,devtoolsLog,settings}).then(interactive=>{
const unfilteredResults=Array.from(resultsMap.values());
const lanternInteractive=interactive;
const items=context.settings.throttlingMethod==='simulate'?
OffscreenImages.filterLanternResults(unfilteredResults,lanternInteractive):
OffscreenImages.filterObservedResults(unfilteredResults,interactive.timestamp);
const headings=[
{key:'url',valueType:'thumbnail',label:''},
{key:'url',valueType:'url',label:'URL'},
{key:'totalBytes',valueType:'bytes',label:'Original'},
{key:'wastedBytes',valueType:'bytes',label:'Potential Savings'}];
return{
warnings,
items,
headings};
});
}}
module.exports=OffscreenImages;
},{"../../lib/sentry":40,"../../lib/url-shim":"url","./byte-efficiency-audit":3}],"../audits/byte-efficiency/render-blocking-resources":[function(require,module,exports){
'use strict';
const Audit=require('../audit');
const BaseNode=require('../../lib/dependency-graph/base-node');
const ByteEfficiencyAudit=require('./byte-efficiency-audit');
const UnusedCSS=require('./unused-css-rules');
const NetworkRequest=require('../../lib/network-request');
const MINIMUM_WASTED_MS=50;
function getNodesAndTimingByUrl(nodeTimings){
const urlMap={};
const nodes=Array.from(nodeTimings.keys());
nodes.forEach(node=>{
if(node.type!=='network')return;
const nodeTiming=nodeTimings.get(node);
if(!nodeTiming)return;
urlMap[node.record.url]={node,nodeTiming};
});
return urlMap;
}
class RenderBlockingResources extends Audit{
static get meta(){
return{
id:'render-blocking-resources',
title:'Eliminate render-blocking resources',
scoreDisplayMode:Audit.SCORING_MODES.NUMERIC,
description:
'Resources are blocking the first paint of your page. Consider '+
'delivering critical JS/CSS inline and deferring all non-critical '+
'JS/styles. [Learn more](https://developers.google.com/web/tools/lighthouse/audits/blocking-resources).',
requiredArtifacts:['URL','TagsBlockingFirstPaint','traces']};
}
static async computeResults(artifacts,context){
const trace=artifacts.traces[Audit.DEFAULT_PASS];
const devtoolsLog=artifacts.devtoolsLogs[Audit.DEFAULT_PASS];
const simulatorData={devtoolsLog,settings:context.settings};
const traceOfTab=await artifacts.requestTraceOfTab(trace);
const simulator=await artifacts.requestLoadSimulator(simulatorData);
const wastedCssBytes=await RenderBlockingResources.computeWastedCSSBytes(artifacts,context);
const metricSettings={throttlingMethod:'simulate'};
const metricComputationData={trace,devtoolsLog,simulator,settings:metricSettings};
const fcpSimulation=await artifacts.requestFirstContentfulPaint(metricComputationData);
const fcpTsInMs=traceOfTab.timestamps.firstContentfulPaint/1000;
const nodesByUrl=getNodesAndTimingByUrl(fcpSimulation.optimisticEstimate.nodeTimings);
const results=[];
const deferredNodeIds=new Set();
for(const resource of artifacts.TagsBlockingFirstPaint){
if(resource.endTime*1000>fcpTsInMs)continue;
if(!nodesByUrl[resource.tag.url])continue;
const{node,nodeTiming}=nodesByUrl[resource.tag.url];
node.traverse(node=>deferredNodeIds.add(node.id));
const wastedMs=Math.round(nodeTiming.duration);
if(wastedMs<MINIMUM_WASTED_MS)continue;
results.push({
url:resource.tag.url,
totalBytes:resource.transferSize,
wastedMs});
}
if(!results.length){
return{results,wastedMs:0};
}
const wastedMs=RenderBlockingResources.estimateSavingsWithGraphs(
simulator,
fcpSimulation.optimisticGraph,
deferredNodeIds,
wastedCssBytes);
return{results,wastedMs};
}
static estimateSavingsWithGraphs(simulator,fcpGraph,deferredIds,wastedCssBytesByUrl){
const originalEstimate=simulator.simulate(fcpGraph).timeInMs;
let totalChildNetworkBytes=0;
const minimalFCPGraph=fcpGraph.cloneWithRelationships(node=>{
const canDeferRequest=deferredIds.has(node.id);
if(node.type!==BaseNode.TYPES.NETWORK)return!canDeferRequest;
const isStylesheet=
node.record.resourceType===NetworkRequest.TYPES.Stylesheet;
if(canDeferRequest&&isStylesheet){
const wastedBytes=wastedCssBytesByUrl.get(node.record.url)||0;
totalChildNetworkBytes+=(node.record.transferSize||0)-wastedBytes;
}
return!canDeferRequest;
});
const originalTransferSize=minimalFCPGraph.record.transferSize;
const safeTransferSize=originalTransferSize||0;
minimalFCPGraph.record.transferSize=safeTransferSize+totalChildNetworkBytes;
const estimateAfterInline=simulator.simulate(minimalFCPGraph).timeInMs;
minimalFCPGraph.record.transferSize=originalTransferSize;
return Math.round(Math.max(originalEstimate-estimateAfterInline,0));
}
static async computeWastedCSSBytes(artifacts,context){
const wastedBytesByUrl=new Map();
try{
const results=await UnusedCSS.audit(artifacts,context);
for(const item of results.details.items){
wastedBytesByUrl.set(item.url,item.wastedBytes);
}
}catch(_){}
return wastedBytesByUrl;
}
static async audit(artifacts,context){
const{results,wastedMs}=await RenderBlockingResources.computeResults(artifacts,context);
let displayValue='';
if(results.length>1){
displayValue=`${results.length} resources delayed first paint by ${wastedMs}ms`;
}else if(results.length===1){
displayValue=`${results.length} resource delayed first paint by ${wastedMs}ms`;
}
const headings=[
{key:'url',valueType:'url',label:'URL'},
{key:'totalBytes',valueType:'bytes',label:'Size (KB)'},
{key:'wastedMs',valueType:'timespanMs',label:'Download Time (ms)'}];
const details=Audit.makeOpportunityDetails(headings,results,wastedMs);
return{
displayValue,
score:ByteEfficiencyAudit.scoreForWastedMs(wastedMs),
rawValue:wastedMs,
details};
}}
module.exports=RenderBlockingResources;
},{"../../lib/dependency-graph/base-node":22,"../../lib/network-request":38,"../audit":2,"./byte-efficiency-audit":3,"./unused-css-rules":"../audits/byte-efficiency/unused-css-rules"}],"../audits/byte-efficiency/total-byte-weight":[function(require,module,exports){
'use strict';
const ByteEfficiencyAudit=require('./byte-efficiency-audit');
class TotalByteWeight extends ByteEfficiencyAudit{
static get meta(){
return{
id:'total-byte-weight',
title:'Avoids enormous network payloads',
failureTitle:'Has enormous network payloads',
scoreDisplayMode:ByteEfficiencyAudit.SCORING_MODES.NUMERIC,
description:
'Large network payloads cost users real money and are highly correlated with '+
'long load times. [Learn '+
'more](https://developers.google.com/web/tools/lighthouse/audits/network-payloads).',
requiredArtifacts:['devtoolsLogs']};
}
static get defaultOptions(){
return{
scorePODR:2500*1024,
scoreMedian:4000*1024};
}
static async audit(artifacts,context){
const devtoolsLogs=artifacts.devtoolsLogs[ByteEfficiencyAudit.DEFAULT_PASS];
const[networkRecords,networkThroughput]=await Promise.all([
artifacts.requestNetworkRecords(devtoolsLogs),
artifacts.requestNetworkThroughput(devtoolsLogs)]);
let totalBytes=0;
let results=[];
networkRecords.forEach(record=>{
if(record.parsedURL.scheme==='data'||!record.finished)return;
const result={
url:record.url,
totalBytes:record.transferSize,
totalMs:ByteEfficiencyAudit.bytesToMs(record.transferSize,networkThroughput)};
totalBytes+=result.totalBytes;
results.push(result);
});
const totalCompletedRequests=results.length;
results=results.sort((itemA,itemB)=>itemB.totalBytes-itemA.totalBytes).slice(0,10);
const score=ByteEfficiencyAudit.computeLogNormalScore(
totalBytes,
context.options.scorePODR,
context.options.scoreMedian);
const headings=[
{key:'url',itemType:'url',text:'URL'},
{
key:'totalBytes',
itemType:'bytes',
displayUnit:'kb',
granularity:1,
text:'Total Size'},
{key:'totalMs',itemType:'ms',text:'Transfer Time'}];
const tableDetails=ByteEfficiencyAudit.makeTableDetails(headings,results);
return{
score,
rawValue:totalBytes,
displayValue:[
'Total size was %d\xa0KB',
totalBytes/1024],
extendedInfo:{
value:{
results,
totalCompletedRequests