diff --git a/lighthouse-core/audits/byte-efficiency/byte-efficiency-audit.js b/lighthouse-core/audits/byte-efficiency/byte-efficiency-audit.js index 6e0d59c683..e7c32eba4a 100644 --- a/lighthouse-core/audits/byte-efficiency/byte-efficiency-audit.js +++ b/lighthouse-core/audits/byte-efficiency/byte-efficiency-audit.js @@ -8,6 +8,9 @@ const Audit = require('../audit'); const Interactive = require('../../gather/computed/metrics/lantern-interactive'); // eslint-disable-line max-len const Simulator = require('../../lib/dependency-graph/simulator/simulator'); // eslint-disable-line no-unused-vars +const Node = require('../../lib/dependency-graph/node.js'); // eslint-disable-line no-unused-vars + +const NetworkNode = require('../../lib/dependency-graph/network-node.js'); // eslint-disable-line no-unused-vars const KB_IN_BYTES = 1024; @@ -33,7 +36,7 @@ class UnusedBytes extends Audit { /** * @param {number} bytes * @param {number} networkThroughput measured in bytes/second - * @return {string} + * @return {number} */ static bytesToMs(bytes, networkThroughput) { const milliseconds = bytes / networkThroughput * 1000; @@ -42,9 +45,9 @@ class UnusedBytes extends Audit { /** * Estimates the number of bytes this network record would have consumed on the network based on the - * uncompressed size (totalBytes), uses the actual transfer size from the network record if applicable. + * uncompressed size (totalBytes). Uses the actual transfer size from the network record if applicable. * - * @param {?LH.WebInspector.NetworkRequest} networkRecord + * @param {LH.WebInspector.NetworkRequest=} networkRecord * @param {number} totalBytes Uncompressed size of the resource * @param {string=} resourceType * @param {number=} compressionRatio @@ -58,19 +61,21 @@ class UnusedBytes extends Audit { return Math.round(totalBytes * compressionRatio); } else if (networkRecord._resourceType && networkRecord._resourceType._name === resourceType) { // This was a regular standalone asset, just use the transfer size. - return networkRecord._transferSize; + return networkRecord._transferSize || 0; } else { // This was an asset that was inlined in a different resource type (e.g. HTML document). // Use the compression ratio of the resource to estimate the total transferred bytes. - const compressionRatio = networkRecord._transferSize / networkRecord._resourceSize || 1; + const transferSize = networkRecord._transferSize || 0; + const resourceSize = networkRecord._resourceSize; + const compressionRatio = resourceSize !== undefined ? (transferSize / resourceSize) : 1; return Math.round(totalBytes * compressionRatio); } } /** - * @param {Artifacts} artifacts - * @param {LH.Audit.Context=} context - * @return {Promise} + * @param {LH.Artifacts} artifacts + * @param {LH.Audit.Context} context + * @return {Promise} */ static audit(artifacts, context) { const trace = artifacts.traces[Audit.DEFAULT_PASS]; @@ -90,42 +95,51 @@ class UnusedBytes extends Audit { artifacts.requestLoadSimulator(simulatorOptions), ]) ) - .then(([result, graph, simulator]) => this.createAuditResult(result, graph, simulator)); + .then(([result, graph, simulator]) => this.createAuditProduct(result, graph, simulator)); } /** * Computes the estimated effect of all the byte savings on the last long task * in the provided graph. * - * @param {Array<{url: string, wastedBytes: number}>} results The array of byte savings results per resource + * @param {Array} results The array of byte savings results per resource * @param {Node} graph * @param {Simulator} simulator * @return {number} */ static computeWasteWithTTIGraph(results, graph, simulator) { const simulationBeforeChanges = simulator.simulate(graph); + /** @type {Map} */ const resultsByUrl = new Map(); for (const result of results) { resultsByUrl.set(result.url, result); } // Update all the transfer sizes to reflect implementing our recommendations + /** @type {Map} */ + const originalTransferSizes = new Map(); graph.traverse(node => { if (node.type !== 'network') return; - if (!resultsByUrl.has(node.record.url)) return; - const original = node.record.transferSize; - const wastedBytes = resultsByUrl.get(node.record.url).wastedBytes; + const networkNode = /** @type {NetworkNode} */ (node); + const result = resultsByUrl.get(networkNode.record.url); + if (!result) return; + const original = networkNode.record.transferSize; // cloning NetworkRequest objects is difficult, so just stash the original transfer size - node.record._originalTransferSize = original; - node.record._transferSize = Math.max(original - wastedBytes, 0); + originalTransferSizes.set(networkNode.record.requestId, original); + + const wastedBytes = result.wastedBytes; + networkNode.record._transferSize = Math.max(original - wastedBytes, 0); }); const simulationAfterChanges = simulator.simulate(graph); + // Restore the original transfer size after we've done our simulation graph.traverse(node => { if (node.type !== 'network') return; - if (!node.record._originalTransferSize) return; - node.record._transferSize = node.record._originalTransferSize; + const networkNode = /** @type {NetworkNode} */ (node); + const originalTransferSize = originalTransferSizes.get(networkNode.record.requestId); + if (originalTransferSize === undefined) return; + networkNode.record._transferSize = originalTransferSize; }); const savingsOnTTI = Math.max( @@ -139,12 +153,12 @@ class UnusedBytes extends Audit { } /** - * @param {Audit.HeadingsResult} result + * @param {LH.Audit.ByteEfficiencyProduct} result * @param {Node} graph * @param {Simulator} simulator - * @return {AuditResult} + * @return {LH.Audit.Product} */ - static createAuditResult(result, graph, simulator) { + static createAuditProduct(result, graph, simulator) { const debugString = result.debugString; const results = result.results.sort((itemA, itemB) => itemB.wastedBytes - itemA.wastedBytes); @@ -161,6 +175,8 @@ class UnusedBytes extends Audit { wastedMs, wastedBytes, }; + + // @ts-ignore - TODO(bckenny): unify details types. items shouldn't be an indexed type. const details = Audit.makeTableDetails(result.headings, results, summary); return { @@ -179,13 +195,19 @@ class UnusedBytes extends Audit { }; } + /* eslint-disable no-unused-vars */ + /** - * @param {!Artifacts} artifacts - * @return {!Audit.HeadingsResult} + * @param {LH.Artifacts} artifacts + * @param {Array} networkRecords + * @param {LH.Audit.Context} context + * @return {LH.Audit.ByteEfficiencyProduct|Promise} */ - static audit_() { + static audit_(artifacts, networkRecords, context) { throw new Error('audit_ unimplemented'); } + + /* eslint-enable no-unused-vars */ } module.exports = UnusedBytes; diff --git a/lighthouse-core/audits/byte-efficiency/efficient-animated-content.js b/lighthouse-core/audits/byte-efficiency/efficient-animated-content.js index a5555e48cd..18e9d736fe 100644 --- a/lighthouse-core/audits/byte-efficiency/efficient-animated-content.js +++ b/lighthouse-core/audits/byte-efficiency/efficient-animated-content.js @@ -42,26 +42,25 @@ class EfficientAnimatedContent extends ByteEfficiencyAudit { } /** - * @param {!LH.Artifacts} artifacts - * @return {Promise} + * @param {LH.Artifacts} artifacts + * @param {Array} networkRecords + * @return {LH.Audit.ByteEfficiencyProduct} */ - static async audit_(artifacts) { - const devtoolsLogs = artifacts.devtoolsLogs[EfficientAnimatedContent.DEFAULT_PASS]; - - const networkRecords = await artifacts.requestNetworkRecords(devtoolsLogs); + static audit_(artifacts, networkRecords) { const unoptimizedContent = networkRecords.filter( - record => record.mimeType === 'image/gif' && + record => record._mimeType === 'image/gif' && record._resourceType === WebInspector.resourceTypes.Image && - record.resourceSize > GIF_BYTE_THRESHOLD + (record._resourceSize || 0) > GIF_BYTE_THRESHOLD ); /** @type {Array<{url: string, totalBytes: number, wastedBytes: number}>}*/ const results = unoptimizedContent.map(record => { + const resourceSize = record._resourceSize || 0; return { url: record.url, - totalBytes: record.resourceSize, - wastedBytes: Math.round(record.resourceSize * - EfficientAnimatedContent.getPercentSavings(record.resourceSize)), + totalBytes: resourceSize, + wastedBytes: Math.round(resourceSize * + EfficientAnimatedContent.getPercentSavings(resourceSize)), }; }); diff --git a/lighthouse-core/audits/byte-efficiency/offscreen-images.js b/lighthouse-core/audits/byte-efficiency/offscreen-images.js index bcd22971d4..ce3140a35e 100644 --- a/lighthouse-core/audits/byte-efficiency/offscreen-images.js +++ b/lighthouse-core/audits/byte-efficiency/offscreen-images.js @@ -21,7 +21,7 @@ const IGNORE_THRESHOLD_IN_PERCENT = 75; class OffscreenImages extends ByteEfficiencyAudit { /** - * @return {!AuditMeta} + * @return {LH.Audit.Meta} */ static get meta() { return { @@ -38,7 +38,7 @@ class OffscreenImages extends ByteEfficiencyAudit { } /** - * @param {!ClientRect} imageRect + * @param {ClientRect} imageRect * @param {{innerWidth: number, innerHeight: number}} viewportDimensions * @return {number} */ @@ -82,8 +82,10 @@ class OffscreenImages extends ByteEfficiencyAudit { } /** - * @param {!Artifacts} artifacts - * @return {!Audit.HeadingsResult} + * @param {LH.Artifacts} artifacts + * @param {Array} networkRecords + * @param {LH.Audit.Context} context + * @return {Promise} */ static audit_(artifacts, networkRecords, context) { const images = artifacts.ImageUsage; @@ -91,6 +93,7 @@ class OffscreenImages extends ByteEfficiencyAudit { const trace = artifacts.traces[ByteEfficiencyAudit.DEFAULT_PASS]; const devtoolsLog = artifacts.devtoolsLogs[ByteEfficiencyAudit.DEFAULT_PASS]; + /** @type {string|undefined} */ let debugString; const resultsMap = images.reduce((results, image) => { if (!image.networkRecord) { @@ -100,6 +103,7 @@ class OffscreenImages extends ByteEfficiencyAudit { const processed = OffscreenImages.computeWaste(image, viewportDimensions); if (processed instanceof Error) { debugString = processed.message; + // @ts-ignore TODO(bckenny): Sentry type checking Sentry.captureException(processed, {tags: {audit: this.meta.name}, level: 'warning'}); return results; } @@ -116,6 +120,7 @@ class OffscreenImages extends ByteEfficiencyAudit { // TODO(phulce): move this to always use lantern const settings = context.settings; return artifacts.requestFirstCPUIdle({trace, devtoolsLog, settings}).then(firstInteractive => { + // @ts-ignore - see above TODO. const ttiTimestamp = firstInteractive.timestamp / 1000000; const results = Array.from(resultsMap.values()).filter(item => { const isWasteful = diff --git a/lighthouse-core/audits/byte-efficiency/render-blocking-resources.js b/lighthouse-core/audits/byte-efficiency/render-blocking-resources.js index 9f1fb7e427..647e6afd0a 100644 --- a/lighthouse-core/audits/byte-efficiency/render-blocking-resources.js +++ b/lighthouse-core/audits/byte-efficiency/render-blocking-resources.js @@ -7,7 +7,6 @@ /** * @fileoverview Audit a page to see if it does have resources that are blocking first paint */ - 'use strict'; const Audit = require('../audit'); @@ -16,6 +15,9 @@ const ByteEfficiencyAudit = require('./byte-efficiency-audit'); const UnusedCSS = require('./unused-css-rules'); const WebInspector = require('../../lib/web-inspector'); +const Simulator = require('../../lib/dependency-graph/simulator/simulator'); // eslint-disable-line no-unused-vars +const NetworkNode = require('../../lib/dependency-graph/network-node.js'); // eslint-disable-line no-unused-vars + // Because of the way we detect blocking stylesheets, asynchronously loaded // CSS with link[rel=preload] and an onload handler (see https://github.com/filamentgroup/loadCSS) // can be falsely flagged as blocking. Therefore, ignore stylesheets that loaded fast enough @@ -27,17 +29,25 @@ const MINIMUM_WASTED_MS = 50; * @param {LH.Gatherer.Simulation.Result['nodeTimings']} nodeTimings * @return {Object} */ -const getNodesAndTimingByUrl = nodeTimings => { +function getNodesAndTimingByUrl(nodeTimings) { + /** @type {Object} */ + const urlMap = {}; const nodes = Array.from(nodeTimings.keys()); - return nodes.reduce((map, node) => { - map[node.record && node.record.url] = {node, nodeTiming: nodeTimings.get(node)}; - return map; - }, {}); -}; + nodes.forEach(node => { + if (node.type !== 'network') return; + const networkNode = /** @type {NetworkNode} */ (node); + const nodeTiming = nodeTimings.get(node); + if (!nodeTiming) return; + + urlMap[networkNode.record.url] = {node, nodeTiming}; + }); + + return urlMap; +} class RenderBlockingResources extends Audit { /** - * @return {!AuditMeta} + * @return {LH.Audit.Meta} */ static get meta() { return { @@ -57,8 +67,9 @@ class RenderBlockingResources extends Audit { } /** - * @param {Artifacts} artifacts + * @param {LH.Artifacts} artifacts * @param {LH.Audit.Context} context + * @return {Promise<{wastedMs: number, results: Array<{url: string, totalBytes: number, wastedMs: number}>}>} */ static async computeResults(artifacts, context) { const trace = artifacts.traces[Audit.DEFAULT_PASS]; @@ -70,6 +81,7 @@ class RenderBlockingResources extends Audit { const metricSettings = {throttlingMethod: 'simulate'}; const metricComputationData = {trace, devtoolsLog, simulator, settings: metricSettings}; + // @ts-ignore - TODO(bckenny): allow optional `throttling` settings const fcpSimulation = await artifacts.requestFirstContentfulPaint(metricComputationData); const fcpTsInMs = traceOfTab.timestamps.firstContentfulPaint / 1000; @@ -91,6 +103,7 @@ class RenderBlockingResources extends Audit { node.traverse(node => deferredNodeIds.add(node.id)); // "wastedMs" is the download time of the network request, responseReceived - requestSent + // @ts-ignore - TODO(phulce): nodeTiming.startTime/endTime shouldn't be optional by this point? const wastedMs = Math.round(nodeTiming.endTime - nodeTiming.startTime); if (wastedMs < MINIMUM_WASTED_MS) continue; @@ -135,38 +148,43 @@ class RenderBlockingResources extends Audit { const originalEstimate = simulator.simulate(fcpGraph).timeInMs; let totalChildNetworkBytes = 0; - const minimalFCPGraph = fcpGraph.cloneWithRelationships(node => { + const minimalFCPGraph = /** @type {NetworkNode} */ (fcpGraph.cloneWithRelationships(node => { + // If a node can be deferred, exclude it from the new FCP graph const canDeferRequest = deferredIds.has(node.id); + if (node.type !== Node.TYPES.NETWORK) return !canDeferRequest; + + const networkNode = /** @type {NetworkNode} */ (node); + const isStylesheet = - node.type === Node.TYPES.NETWORK && - node.record._resourceType === WebInspector.resourceTypes.Stylesheet; + networkNode.record._resourceType === WebInspector.resourceTypes.Stylesheet; if (canDeferRequest && isStylesheet) { // We'll inline the used bytes of the stylesheet and assume the rest can be deferred - const wastedBytes = wastedCssBytesByUrl.get(node.record.url) || 0; - totalChildNetworkBytes += node.record._transferSize - wastedBytes; + const wastedBytes = wastedCssBytesByUrl.get(networkNode.record.url) || 0; + totalChildNetworkBytes += (networkNode.record._transferSize || 0) - wastedBytes; } - - // If a node can be deferred, exclude it from the new FCP graph return !canDeferRequest; - }); + })); // Add the inlined bytes to the HTML response - minimalFCPGraph.record._transferSize += totalChildNetworkBytes; + const originalTransferSize = minimalFCPGraph.record._transferSize; + const safeTransferSize = originalTransferSize || 0; + minimalFCPGraph.record._transferSize = safeTransferSize + totalChildNetworkBytes; const estimateAfterInline = simulator.simulate(minimalFCPGraph).timeInMs; - minimalFCPGraph.record._transferSize -= totalChildNetworkBytes; + minimalFCPGraph.record._transferSize = originalTransferSize; return Math.round(Math.max(originalEstimate - estimateAfterInline, 0)); } /** - * @param {!Artifacts} artifacts + * @param {LH.Artifacts} artifacts * @param {LH.Audit.Context} context - * @return {Map} + * @return {Promise>} */ static async computeWastedCSSBytes(artifacts, context) { const wastedBytesByUrl = new Map(); try { // TODO(phulce): pull this out into computed artifact const results = await UnusedCSS.audit(artifacts, context); + // @ts-ignore - TODO(bckenny): details types. for (const item of results.details.items) { wastedBytesByUrl.set(item.url, item.wastedBytes); } @@ -176,9 +194,9 @@ class RenderBlockingResources extends Audit { } /** - * @param {!Artifacts} artifacts + * @param {LH.Artifacts} artifacts * @param {LH.Audit.Context} context - * @return {AuditResult} + * @return {Promise} */ static async audit(artifacts, context) { const {results, wastedMs} = await RenderBlockingResources.computeResults(artifacts, context); diff --git a/lighthouse-core/audits/byte-efficiency/total-byte-weight.js b/lighthouse-core/audits/byte-efficiency/total-byte-weight.js index 816108aced..01b0e1bc94 100644 --- a/lighthouse-core/audits/byte-efficiency/total-byte-weight.js +++ b/lighthouse-core/audits/byte-efficiency/total-byte-weight.js @@ -10,7 +10,7 @@ const Util = require('../../report/html/renderer/util'); class TotalByteWeight extends ByteEfficiencyAudit { /** - * @return {!AuditMeta} + * @return {LH.Audit.Meta} */ static get meta() { return { @@ -39,9 +39,9 @@ class TotalByteWeight extends ByteEfficiencyAudit { } /** - * @param {!Artifacts} artifacts + * @param {LH.Artifacts} artifacts * @param {LH.Audit.Context} context - * @return {!Promise} + * @return {Promise} */ static audit(artifacts, context) { const devtoolsLogs = artifacts.devtoolsLogs[ByteEfficiencyAudit.DEFAULT_PASS]; @@ -50,11 +50,12 @@ class TotalByteWeight extends ByteEfficiencyAudit { artifacts.requestNetworkThroughput(devtoolsLogs), ]).then(([networkRecords, networkThroughput]) => { let totalBytes = 0; + /** @type {Array<{url: string, totalBytes: number, totalMs: number}>} */ let results = []; networkRecords.forEach(record => { // exclude data URIs since their size is reflected in other resources // exclude unfinished requests since they won't have transfer size information - if (record.scheme === 'data' || !record.finished) return; + if (record.parsedURL.scheme === 'data' || !record.finished) return; const result = { url: record.url, diff --git a/lighthouse-core/audits/byte-efficiency/unminified-css.js b/lighthouse-core/audits/byte-efficiency/unminified-css.js index 873256205e..7c553f4ba8 100644 --- a/lighthouse-core/audits/byte-efficiency/unminified-css.js +++ b/lighthouse-core/audits/byte-efficiency/unminified-css.js @@ -16,7 +16,7 @@ const IGNORE_THRESHOLD_IN_BYTES = 2048; */ class UnminifiedCSS extends ByteEfficiencyAudit { /** - * @return {!AuditMeta} + * @return {LH.Audit.Meta} */ static get meta() { return { @@ -92,15 +92,16 @@ class UnminifiedCSS extends ByteEfficiencyAudit { } /** - * @param {{content: string, header: {sourceURL: string}}} stylesheet - * @param {?LH.WebInspector.NetworkRequest} networkRecord + * @param {LH.Artifacts.CSSStyleSheetInfo} stylesheet + * @param {LH.WebInspector.NetworkRequest=} networkRecord * @param {string} pageUrl - * @return {{minifiedLength: number, contentLength: number}} + * @return {{url: string|LH.Audit.DetailsRendererCodeDetailJSON, totalBytes: number, wastedBytes: number, wastedPercent: number}} */ static computeWaste(stylesheet, networkRecord, pageUrl) { const content = stylesheet.content; const totalTokenLength = UnminifiedCSS.computeTokenLength(content); + /** @type {LH.Audit.ByteEfficiencyResult['url']} */ let url = stylesheet.header.sourceURL; if (!url || url === pageUrl) { const contentPreview = UnusedCSSRules.determineContentPreview(stylesheet.content); @@ -121,8 +122,9 @@ class UnminifiedCSS extends ByteEfficiencyAudit { } /** - * @param {!Artifacts} artifacts - * @return {!Audit.HeadingsResult} + * @param {LH.Artifacts} artifacts + * @param {Array} networkRecords + * @return {LH.Audit.ByteEfficiencyProduct} */ static audit_(artifacts, networkRecords) { const pageUrl = artifacts.URL.finalUrl; diff --git a/lighthouse-core/audits/byte-efficiency/unminified-javascript.js b/lighthouse-core/audits/byte-efficiency/unminified-javascript.js index a630aac4fe..ffc717818f 100644 --- a/lighthouse-core/audits/byte-efficiency/unminified-javascript.js +++ b/lighthouse-core/audits/byte-efficiency/unminified-javascript.js @@ -6,6 +6,7 @@ 'use strict'; const ByteEfficiencyAudit = require('./byte-efficiency-audit'); +// @ts-ignore - TODO: https://github.com/DefinitelyTyped/DefinitelyTyped/pull/25410 const esprima = require('esprima'); const IGNORE_THRESHOLD_IN_PERCENT = 10; @@ -23,7 +24,7 @@ const IGNORE_THRESHOLD_IN_BYTES = 2048; */ class UnminifiedJavaScript extends ByteEfficiencyAudit { /** - * @return {!AuditMeta} + * @return {LH.Audit.Meta} */ static get meta() { return { @@ -39,7 +40,8 @@ class UnminifiedJavaScript extends ByteEfficiencyAudit { /** * @param {string} scriptContent - * @return {{minifiedLength: number, contentLength: number}} + * @param {LH.WebInspector.NetworkRequest} networkRecord + * @return {{url: string, totalBytes: number, wastedBytes: number, wastedPercent: number}} */ static computeWaste(scriptContent, networkRecord) { const contentLength = scriptContent.length; @@ -68,10 +70,12 @@ class UnminifiedJavaScript extends ByteEfficiencyAudit { } /** - * @param {!Artifacts} artifacts - * @return {!Audit.HeadingsResult} + * @param {LH.Artifacts} artifacts + * @param {Array} networkRecords + * @return {LH.Audit.ByteEfficiencyProduct} */ static audit_(artifacts, networkRecords) { + /** @type {Array} */ const results = []; let debugString; for (const requestId of Object.keys(artifacts.Scripts)) { diff --git a/lighthouse-core/audits/byte-efficiency/unused-css-rules.js b/lighthouse-core/audits/byte-efficiency/unused-css-rules.js index 6cf31fbf60..8106887a48 100644 --- a/lighthouse-core/audits/byte-efficiency/unused-css-rules.js +++ b/lighthouse-core/audits/byte-efficiency/unused-css-rules.js @@ -10,9 +10,11 @@ const ByteEfficiencyAudit = require('./byte-efficiency-audit'); const IGNORE_THRESHOLD_IN_BYTES = 2048; const PREVIEW_LENGTH = 100; +/** @typedef {LH.Artifacts.CSSStyleSheetInfo & {networkRecord: LH.WebInspector.NetworkRequest, usedRules: Array}} StyleSheetInfo */ + class UnusedCSSRules extends ByteEfficiencyAudit { /** - * @return {!AuditMeta} + * @return {LH.Audit.Meta} */ static get meta() { return { @@ -28,16 +30,16 @@ class UnusedCSSRules extends ByteEfficiencyAudit { } /** - * @param {!Array.<{header: {styleSheetId: string}}>} styles The output of the Styles gatherer. + * @param {Array} styles The output of the Styles gatherer. * @param {Array} networkRecords - * @return {!Object} A map of styleSheetId to stylesheet information. + * @return {Object} A map of styleSheetId to stylesheet information. */ static indexStylesheetsById(styles, networkRecords) { const indexedNetworkRecords = networkRecords .reduce((indexed, record) => { indexed[record.url] = record; return indexed; - }, {}); + }, /** @type {Object} */ ({})); return styles.reduce((indexed, stylesheet) => { indexed[stylesheet.header.styleSheetId] = Object.assign({ @@ -45,13 +47,13 @@ class UnusedCSSRules extends ByteEfficiencyAudit { networkRecord: indexedNetworkRecords[stylesheet.header.sourceURL], }, stylesheet); return indexed; - }, {}); + }, /** @type {Object} */ ({})); } /** * Adds used rules to their corresponding stylesheet. - * @param {!Array.<{styleSheetId: string, used: boolean}>} rules The output of the CSSUsage gatherer. - * @param {!Object} indexedStylesheets Stylesheet information indexed by id. + * @param {Array} rules The output of the CSSUsage gatherer. + * @param {Object} indexedStylesheets Stylesheet information indexed by id. */ static indexUsedRules(rules, indexedStylesheets) { rules.forEach(rule => { @@ -68,7 +70,7 @@ class UnusedCSSRules extends ByteEfficiencyAudit { } /** - * @param {!Object} stylesheetInfo + * @param {StyleSheetInfo} stylesheetInfo * @return {{wastedBytes: number, totalBytes: number, wastedPercent: number}} */ static computeUsage(stylesheetInfo) { @@ -93,7 +95,7 @@ class UnusedCSSRules extends ByteEfficiencyAudit { /** * Trims stylesheet content down to the first rule-set definition. - * @param {?string} content + * @param {string=} content * @return {string} */ static determineContentPreview(content) { @@ -128,15 +130,12 @@ class UnusedCSSRules extends ByteEfficiencyAudit { } /** - * @param {!Object} stylesheetInfo The stylesheetInfo object. + * @param {StyleSheetInfo} stylesheetInfo The stylesheetInfo object. * @param {string} pageUrl The URL of the page, used to identify inline styles. - * @return {?{url: string, wastedBytes: number, totalBytes: number}} + * @return {LH.Audit.ByteEfficiencyResult} */ static mapSheetToResult(stylesheetInfo, pageUrl) { - if (stylesheetInfo.isDuplicate) { - return null; - } - + /** @type {LH.Audit.ByteEfficiencyResult['url']} */ let url = stylesheetInfo.header.sourceURL; if (!url || url === pageUrl) { const contentPreview = UnusedCSSRules.determineContentPreview(stylesheetInfo.content); @@ -148,8 +147,8 @@ class UnusedCSSRules extends ByteEfficiencyAudit { } /** - * @param {!Artifacts} artifacts - * @return {!Audit.HeadingsResult} + * @param {LH.Artifacts} artifacts + * @return {Promise} */ static audit_(artifacts) { const styles = artifacts.CSSUsage.stylesheets; diff --git a/lighthouse-core/audits/byte-efficiency/unused-javascript.js b/lighthouse-core/audits/byte-efficiency/unused-javascript.js index 8db4c722f2..e645a760f5 100644 --- a/lighthouse-core/audits/byte-efficiency/unused-javascript.js +++ b/lighthouse-core/audits/byte-efficiency/unused-javascript.js @@ -11,7 +11,7 @@ const IGNORE_THRESHOLD_IN_BYTES = 2048; class UnusedJavaScript extends ByteEfficiencyAudit { /** - * @return {!AuditMeta} + * @return {LH.Audit.Meta} */ static get meta() { return { @@ -25,7 +25,7 @@ class UnusedJavaScript extends ByteEfficiencyAudit { } /** - * @param {!JsUsageArtifact} script + * @param {LH.Crdp.Profiler.ScriptCoverage} script * @return {{unusedLength: number, contentLength: number}} */ static computeWaste(script) { @@ -61,9 +61,9 @@ class UnusedJavaScript extends ByteEfficiencyAudit { } /** - * @param {!Array<{unusedLength: number, contentLength: number}>} wasteData + * @param {Array<{unusedLength: number, contentLength: number}>} wasteData * @param {LH.WebInspector.NetworkRequest} networkRecord - * @return {{url: string, totalBytes: number, wastedBytes: number, wastedPercent: number}} + * @return {LH.Audit.ByteEfficiencyResult} */ static mergeWaste(wasteData, networkRecord) { let unusedLength = 0; @@ -87,10 +87,12 @@ class UnusedJavaScript extends ByteEfficiencyAudit { } /** - * @param {!Artifacts} artifacts - * @return {!Audit.HeadingsResult} + * @param {LH.Artifacts} artifacts + * @param {Array} networkRecords + * @return {LH.Audit.ByteEfficiencyProduct} */ static audit_(artifacts, networkRecords) { + /** @type {Map>} */ const scriptsByUrl = new Map(); for (const script of artifacts.JsUsage) { const scripts = scriptsByUrl.get(script.url) || []; @@ -98,6 +100,7 @@ class UnusedJavaScript extends ByteEfficiencyAudit { scriptsByUrl.set(script.url, scripts); } + /** @type {Array} */ const results = []; for (const [url, scripts] of scriptsByUrl.entries()) { const networkRecord = networkRecords.find(record => record.url === url); diff --git a/lighthouse-core/audits/byte-efficiency/uses-long-cache-ttl.js b/lighthouse-core/audits/byte-efficiency/uses-long-cache-ttl.js index 121d090db4..9d50cf7862 100644 --- a/lighthouse-core/audits/byte-efficiency/uses-long-cache-ttl.js +++ b/lighthouse-core/audits/byte-efficiency/uses-long-cache-ttl.js @@ -6,6 +6,7 @@ 'use strict'; const assert = require('assert'); +// @ts-ignore - typed where used. const parseCacheControl = require('parse-cache-control'); const Audit = require('../audit'); const WebInspector = require('../../lib/web-inspector'); @@ -16,11 +17,10 @@ const IGNORE_THRESHOLD_IN_PERCENT = 0.925; class CacheHeaders extends Audit { /** - * @return {!AuditMeta} + * @return {LH.Audit.Meta} */ static get meta() { return { - category: 'Caching', name: 'uses-long-cache-ttl', description: 'Uses efficient cache policy on static assets', failureDescription: 'Uses inefficient cache policy on static assets', @@ -103,22 +103,24 @@ class CacheHeaders extends Audit { * Computes the user-specified cache lifetime, 0 if explicit no-cache policy is in effect, and null if not * user-specified. See https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html * - * @param {!Map} headers - * @param {!Object} cacheControl Follows the potential settings of cache-control, see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control + * @param {Map} headers + * @param {{'no-cache'?: boolean,'no-store'?: boolean, 'max-age'?: number}} cacheControl Follows the potential settings of cache-control, see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control * @return {?number} */ static computeCacheLifetimeInSeconds(headers, cacheControl) { if (cacheControl) { // Cache-Control takes precendence over expires if (cacheControl['no-cache'] || cacheControl['no-store']) return 0; - if (Number.isFinite(cacheControl['max-age'])) return Math.max(cacheControl['max-age'], 0); + const maxAge = cacheControl['max-age']; + if (maxAge !== undefined && Number.isFinite(maxAge)) return Math.max(maxAge, 0); } else if ((headers.get('pragma') || '').includes('no-cache')) { // The HTTP/1.0 Pragma header can disable caching if cache-control is not set, see https://tools.ietf.org/html/rfc7234#section-5.4 return 0; } - if (headers.has('expires')) { - const expires = new Date(headers.get('expires')).getTime(); + const expiresHeaders = headers.get('expires'); + if (expiresHeaders) { + const expires = new Date(expiresHeaders).getTime(); // Invalid expires values MUST be treated as already expired if (!expires) return 0; return Math.max(0, Math.ceil((expires - Date.now()) / 1000)); @@ -162,9 +164,9 @@ class CacheHeaders extends Audit { } /** - * @param {!Artifacts} artifacts + * @param {LH.Artifacts} artifacts * @param {LH.Audit.Context} context - * @return {!AuditResult} + * @return {Promise} */ static audit(artifacts, context) { const devtoolsLogs = artifacts.devtoolsLogs[Audit.DEFAULT_PASS]; @@ -176,8 +178,9 @@ class CacheHeaders extends Audit { for (const record of records) { if (!CacheHeaders.isCacheableAsset(record)) continue; + /** @type {Map} */ const headers = new Map(); - for (const header of record._responseHeaders) { + for (const header of record._responseHeaders || []) { headers.set(header.name.toLowerCase(), header.value); } @@ -195,7 +198,7 @@ class CacheHeaders extends Audit { if (cacheHitProbability > IGNORE_THRESHOLD_IN_PERCENT) continue; const url = URL.elideDataURI(record._url); - const totalBytes = record._transferSize; + const totalBytes = record._transferSize || 0; const wastedBytes = (1 - cacheHitProbability) * totalBytes; totalWastedBytes += wastedBytes; diff --git a/lighthouse-core/audits/byte-efficiency/uses-optimized-images.js b/lighthouse-core/audits/byte-efficiency/uses-optimized-images.js index 4c2c5d4d37..bcc5fe7d60 100644 --- a/lighthouse-core/audits/byte-efficiency/uses-optimized-images.js +++ b/lighthouse-core/audits/byte-efficiency/uses-optimized-images.js @@ -7,6 +7,7 @@ * @fileoverview This audit determines if the images used are sufficiently larger * than JPEG compressed images without metadata at quality 85. */ +// @ts-nocheck - TODO(bckenny) 'use strict'; const ByteEfficiencyAudit = require('./byte-efficiency-audit'); @@ -16,7 +17,7 @@ const IGNORE_THRESHOLD_IN_BYTES = 4096; class UsesOptimizedImages extends ByteEfficiencyAudit { /** - * @return {!AuditMeta} + * @return {LH.Audit.Meta} */ static get meta() { return { @@ -31,19 +32,18 @@ class UsesOptimizedImages extends ByteEfficiencyAudit { } /** - * @param {{originalSize: number, webpSize: number, jpegSize: number}} image - * @param {string} type + * @param {{originalSize: number, jpegSize: number}} image * @return {{bytes: number, percent: number}} */ - static computeSavings(image, type) { - const bytes = image.originalSize - image[type + 'Size']; + static computeSavings(image) { + const bytes = image.originalSize - image.jpegSize; const percent = 100 * bytes / image.originalSize; return {bytes, percent}; } /** - * @param {!Artifacts} artifacts - * @return {!Audit.HeadingsResult} + * @param {LH.Artifacts} artifacts + * @return {LH.Audit.ByteEfficiencyProduct} */ static audit_(artifacts) { const images = artifacts.OptimizedImages; @@ -60,7 +60,7 @@ class UsesOptimizedImages extends ByteEfficiencyAudit { } const url = URL.elideDataURI(image.url); - const jpegSavings = UsesOptimizedImages.computeSavings(image, 'jpeg'); + const jpegSavings = UsesOptimizedImages.computeSavings(image); results.push({ url, diff --git a/lighthouse-core/audits/byte-efficiency/uses-responsive-images.js b/lighthouse-core/audits/byte-efficiency/uses-responsive-images.js index bbe4ff711e..28d701456f 100644 --- a/lighthouse-core/audits/byte-efficiency/uses-responsive-images.js +++ b/lighthouse-core/audits/byte-efficiency/uses-responsive-images.js @@ -18,11 +18,10 @@ const Sentry = require('../../lib/sentry'); const URL = require('../../lib/url-shim'); const IGNORE_THRESHOLD_IN_BYTES = 2048; -const WASTEFUL_THRESHOLD_IN_BYTES = 25 * 1024; class UsesResponsiveImages extends ByteEfficiencyAudit { /** - * @return {!AuditMeta} + * @return {LH.Audit.Meta} */ static get meta() { return { @@ -39,9 +38,9 @@ class UsesResponsiveImages extends ByteEfficiencyAudit { } /** - * @param {!Object} image + * @param {LH.Artifacts.SingleImageUsage} image * @param {number} DPR devicePixelRatio - * @return {?Object} + * @return {null|Error|LH.Audit.ByteEfficiencyResult}; */ static computeWaste(image, DPR) { const url = URL.elideDataURI(image.src); @@ -66,19 +65,19 @@ class UsesResponsiveImages extends ByteEfficiencyAudit { totalBytes, wastedBytes, wastedPercent: 100 * wastedRatio, - isWasteful: wastedBytes > WASTEFUL_THRESHOLD_IN_BYTES, }; } /** - * @param {!Artifacts} artifacts - * @return {!Audit.HeadingsResult} + * @param {LH.Artifacts} artifacts + * @return {LH.Audit.ByteEfficiencyProduct} */ static audit_(artifacts) { const images = artifacts.ImageUsage; const DPR = artifacts.ViewportDimensions.devicePixelRatio; let debugString; + /** @type {Map} */ const resultsMap = new Map(); images.forEach(image => { // TODO: give SVG a free pass until a detail per pixel metric is available @@ -91,6 +90,7 @@ class UsesResponsiveImages extends ByteEfficiencyAudit { if (processed instanceof Error) { debugString = processed.message; + // @ts-ignore TODO(bckenny): Sentry type checking Sentry.captureException(processed, {tags: {audit: this.meta.name}, level: 'warning'}); return; } diff --git a/lighthouse-core/audits/byte-efficiency/uses-text-compression.js b/lighthouse-core/audits/byte-efficiency/uses-text-compression.js index bfa257e9f2..60fbbe062b 100644 --- a/lighthouse-core/audits/byte-efficiency/uses-text-compression.js +++ b/lighthouse-core/audits/byte-efficiency/uses-text-compression.js @@ -17,7 +17,7 @@ const IGNORE_THRESHOLD_IN_PERCENT = 0.1; class ResponsesAreCompressed extends ByteEfficiencyAudit { /** - * @return {!AuditMeta} + * @return {LH.Audit.Meta} */ static get meta() { return { @@ -33,13 +33,13 @@ class ResponsesAreCompressed extends ByteEfficiencyAudit { } /** - * @param {!Artifacts} artifacts - * @param {number} networkThroughput - * @return {!Audit.HeadingsResult} + * @param {LH.Artifacts} artifacts + * @return {LH.Audit.ByteEfficiencyProduct} */ static audit_(artifacts) { const uncompressedResponses = artifacts.ResponseCompression; + /** @type {Array} */ const results = []; uncompressedResponses.forEach(record => { const originalSize = record.resourceSize; diff --git a/lighthouse-core/audits/byte-efficiency/uses-webp-images.js b/lighthouse-core/audits/byte-efficiency/uses-webp-images.js index 00ca938a9c..34541920b4 100644 --- a/lighthouse-core/audits/byte-efficiency/uses-webp-images.js +++ b/lighthouse-core/audits/byte-efficiency/uses-webp-images.js @@ -6,17 +6,17 @@ /* * @fileoverview This audit determines if the images could be smaller when compressed with WebP. */ +// @ts-nocheck - TODO(bckenny) 'use strict'; const ByteEfficiencyAudit = require('./byte-efficiency-audit'); -const OptimizedImages = require('./uses-optimized-images'); const URL = require('../../lib/url-shim'); const IGNORE_THRESHOLD_IN_BYTES = 8192; class UsesWebPImages extends ByteEfficiencyAudit { /** - * @return {!AuditMeta} + * @return {LH.Audit.Meta} */ static get meta() { return { @@ -32,8 +32,18 @@ class UsesWebPImages extends ByteEfficiencyAudit { } /** - * @param {!Artifacts} artifacts - * @return {!Audit.HeadingsResult} + * @param {{originalSize: number, webpSize: number}} image + * @return {{bytes: number, percent: number}} + */ + static computeSavings(image) { + const bytes = image.originalSize - image.webpSize; + const percent = 100 * bytes / image.originalSize; + return {bytes, percent}; + } + + /** + * @param {LH.Artifacts} artifacts + * @return {LH.Audit.ByteEfficiencyProduct} */ static audit_(artifacts) { const images = artifacts.OptimizedImages; @@ -49,7 +59,7 @@ class UsesWebPImages extends ByteEfficiencyAudit { } const url = URL.elideDataURI(image.url); - const webpSavings = OptimizedImages.computeSavings(image, 'webp'); + const webpSavings = UsesWebPImages.computeSavings(image); results.push({ url, diff --git a/lighthouse-core/lib/dependency-graph/node.js b/lighthouse-core/lib/dependency-graph/node.js index 877882e77c..ed2f444402 100644 --- a/lighthouse-core/lib/dependency-graph/node.js +++ b/lighthouse-core/lib/dependency-graph/node.js @@ -19,7 +19,7 @@ */ class Node { /** - * @param {string|number} id + * @param {string} id */ constructor(id) { this._id = id; @@ -30,7 +30,7 @@ class Node { } /** - * @return {string|number} + * @return {string} */ get id() { return this._id; diff --git a/lighthouse-core/test/audits/byte-efficiency/byte-efficiency-audit-test.js b/lighthouse-core/test/audits/byte-efficiency/byte-efficiency-audit-test.js index 223e57a732..a1c1fba64d 100644 --- a/lighthouse-core/test/audits/byte-efficiency/byte-efficiency-audit-test.js +++ b/lighthouse-core/test/audits/byte-efficiency/byte-efficiency-audit-test.js @@ -76,7 +76,7 @@ describe('Byte efficiency base audit', () => { }); it('should format details', () => { - const result = ByteEfficiencyAudit.createAuditResult({ + const result = ByteEfficiencyAudit.createAuditProduct({ headings: baseHeadings, results: [], }, graph, simulator); @@ -85,7 +85,7 @@ describe('Byte efficiency base audit', () => { }); it('should set the rawValue', () => { - const result = ByteEfficiencyAudit.createAuditResult( + const result = ByteEfficiencyAudit.createAuditProduct( { headings: baseHeadings, results: [ @@ -101,22 +101,22 @@ describe('Byte efficiency base audit', () => { }); it('should score the wastedMs', () => { - const perfectResult = ByteEfficiencyAudit.createAuditResult({ + const perfectResult = ByteEfficiencyAudit.createAuditProduct({ headings: baseHeadings, results: [{url: 'http://example.com/', wastedBytes: 1 * 1000}], }, graph, simulator); - const goodResult = ByteEfficiencyAudit.createAuditResult({ + const goodResult = ByteEfficiencyAudit.createAuditProduct({ headings: baseHeadings, results: [{url: 'http://example.com/', wastedBytes: 20 * 1000}], }, graph, simulator); - const averageResult = ByteEfficiencyAudit.createAuditResult({ + const averageResult = ByteEfficiencyAudit.createAuditProduct({ headings: baseHeadings, results: [{url: 'http://example.com/', wastedBytes: 100 * 1000}], }, graph, simulator); - const failingResult = ByteEfficiencyAudit.createAuditResult({ + const failingResult = ByteEfficiencyAudit.createAuditProduct({ headings: baseHeadings, results: [{url: 'http://example.com/', wastedBytes: 400 * 1000}], }, graph, simulator); @@ -129,7 +129,7 @@ describe('Byte efficiency base audit', () => { it('should throw on invalid graph', () => { assert.throws(() => { - ByteEfficiencyAudit.createAuditResult({ + ByteEfficiencyAudit.createAuditProduct({ headings: baseHeadings, results: [{wastedBytes: 350, totalBytes: 700, wastedPercent: 50}], }, null); @@ -137,7 +137,7 @@ describe('Byte efficiency base audit', () => { }); it('should populate KB', () => { - const result = ByteEfficiencyAudit.createAuditResult({ + const result = ByteEfficiencyAudit.createAuditProduct({ headings: baseHeadings, results: [ {wastedBytes: 2048, totalBytes: 4096, wastedPercent: 50}, @@ -152,7 +152,7 @@ describe('Byte efficiency base audit', () => { }); it('should sort on wastedBytes', () => { - const result = ByteEfficiencyAudit.createAuditResult({ + const result = ByteEfficiencyAudit.createAuditProduct({ headings: baseHeadings, results: [ {wastedBytes: 350, totalBytes: 700, wastedPercent: 50}, @@ -167,7 +167,7 @@ describe('Byte efficiency base audit', () => { }); it('should create a display value', () => { - const result = ByteEfficiencyAudit.createAuditResult({ + const result = ByteEfficiencyAudit.createAuditProduct({ headings: baseHeadings, results: [ {wastedBytes: 512, totalBytes: 700, wastedPercent: 50}, @@ -185,7 +185,7 @@ describe('Byte efficiency base audit', () => { const artifacts = Runner.instantiateComputedArtifacts(); const graph = await artifacts.requestPageDependencyGraph({trace, devtoolsLog}); const simulator = await artifacts.requestLoadSimulator({devtoolsLog, settings}); - const result = ByteEfficiencyAudit.createAuditResult( + const result = ByteEfficiencyAudit.createAuditProduct( { headings: [{key: 'value', text: 'Label'}], results: [ diff --git a/lighthouse-core/test/audits/byte-efficiency/efficient-animated-content-test.js b/lighthouse-core/test/audits/byte-efficiency/efficient-animated-content-test.js index 0dc7ecca12..fe0bd9a276 100644 --- a/lighthouse-core/test/audits/byte-efficiency/efficient-animated-content-test.js +++ b/lighthouse-core/test/audits/byte-efficiency/efficient-animated-content-test.js @@ -16,23 +16,22 @@ describe('Page uses videos for animated GIFs', () => { const networkRecords = [ { _resourceType: WebInspector.resourceTypes.Image, - mimeType: 'image/gif', - resourceSize: 100240, + _mimeType: 'image/gif', + _resourceSize: 100240, url: 'https://example.com/example.gif', }, { _resourceType: WebInspector.resourceTypes.Image, - mimeType: 'image/gif', - resourceSize: 110000, + _mimeType: 'image/gif', + _resourceSize: 110000, url: 'https://example.com/example2.gif', }, ]; const artifacts = { devtoolsLogs: {[EfficientAnimatedContent.DEFAULT_PASS]: []}, - requestNetworkRecords: () => Promise.resolve(networkRecords), }; - const {results} = await EfficientAnimatedContent.audit_(artifacts); + const {results} = await EfficientAnimatedContent.audit_(artifacts, networkRecords); assert.equal(results.length, 1); assert.equal(results[0].url, 'https://example.com/example2.gif'); assert.equal(results[0].totalBytes, 110000); @@ -49,10 +48,9 @@ describe('Page uses videos for animated GIFs', () => { ]; const artifacts = { devtoolsLogs: {[EfficientAnimatedContent.DEFAULT_PASS]: []}, - requestNetworkRecords: () => Promise.resolve(networkRecords), }; - const {results} = await EfficientAnimatedContent.audit_(artifacts); + const {results} = await EfficientAnimatedContent.audit_(artifacts, networkRecords); assert.equal(results.length, 0); }); @@ -71,10 +69,9 @@ describe('Page uses videos for animated GIFs', () => { ]; const artifacts = { devtoolsLogs: {[EfficientAnimatedContent.DEFAULT_PASS]: []}, - requestNetworkRecords: () => Promise.resolve(networkRecords), }; - const {results} = await EfficientAnimatedContent.audit_(artifacts); + const {results} = await EfficientAnimatedContent.audit_(artifacts, networkRecords); assert.equal(results.length, 0); }); }); diff --git a/lighthouse-core/test/audits/byte-efficiency/total-byte-weight-test.js b/lighthouse-core/test/audits/byte-efficiency/total-byte-weight-test.js index 5e5ee5c191..4bba4e9af6 100644 --- a/lighthouse-core/test/audits/byte-efficiency/total-byte-weight-test.js +++ b/lighthouse-core/test/audits/byte-efficiency/total-byte-weight-test.js @@ -7,17 +7,23 @@ const TotalByteWeight = require('../../../audits/byte-efficiency/total-byte-weight.js'); const assert = require('assert'); +const URL = require('url').URL; const options = TotalByteWeight.defaultOptions; /* eslint-env mocha */ function generateRequest(url, size, baseUrl = 'http://google.com/') { + const parsedUrl = new URL(url, baseUrl); + const scheme = parsedUrl.protocol.slice(0, -1); return { - url: `${baseUrl}${url}`, + url: parsedUrl.href, finished: true, transferSize: size * 1024, responseReceivedTime: 1000, endTime: 2000, + parsedURL: { + scheme, + }, }; } @@ -25,6 +31,7 @@ function generateArtifacts(records) { if (records[0] && records[0].length > 1) { records = records.map(args => generateRequest(...args)); } + return { devtoolsLogs: {defaultPass: []}, requestNetworkRecords: () => Promise.resolve(records), diff --git a/tsconfig.json b/tsconfig.json index 85cf0b3616..fcfe3afb57 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -19,6 +19,7 @@ "lighthouse-core/audits/audit.js", "lighthouse-core/audits/accessibility/**/*.js", "lighthouse-core/audits/dobetterweb/**/*.js", + "lighthouse-core/audits/byte-efficiency/**/*.js", "lighthouse-core/audits/manual/**/*.js", "lighthouse-core/audits/seo/manual/*.js", "lighthouse-core/audits/seo/robots-txt.js", diff --git a/typings/audit.d.ts b/typings/audit.d.ts index dd52ca4677..45eefb44c4 100644 --- a/typings/audit.d.ts +++ b/typings/audit.d.ts @@ -38,15 +38,24 @@ declare global { key: string; itemType: string; text: string; + displayUnit?: string; + granularity?: number; } - export interface HeadingsResult { - results: number; + export interface ByteEfficiencyProduct { + results: Array; headings: Array; - passes: boolean; + displayValue?: string; debugString?: string; } + export interface ByteEfficiencyResult { + url: string | DetailsRendererCodeDetailJSON; + wastedBytes: number; + totalBytes: number; + wastedPercent?: number; + } + // TODO: placeholder typedefs until Details are typed export interface DetailsRendererDetailsSummary { wastedMs?: number; @@ -61,8 +70,13 @@ declare global { summary?: DetailsRendererDetailsSummary; } + export interface DetailsRendererCodeDetailJSON { + type: 'code', + value: string; + } + export type DetailsItem = string | number | DetailsRendererNodeDetailsJSON | - DetailsRendererLinkDetailsJSON; + DetailsRendererLinkDetailsJSON | DetailsRendererCodeDetailJSON; export interface DetailsRendererNodeDetailsJSON { type: 'node'; diff --git a/typings/web-inspector.d.ts b/typings/web-inspector.d.ts index ca619dc5b8..7a7ccc64bd 100644 --- a/typings/web-inspector.d.ts +++ b/typings/web-inspector.d.ts @@ -26,7 +26,9 @@ declare global { _responseReceivedTime: number; transferSize: number; + /** Should use a default of 0 if not defined */ _transferSize?: number; + /** Should use a default of 0 if not defined */ _resourceSize?: number; finished: boolean;