diff --git a/.travis.yml b/.travis.yml index 94fab7fddd78..be4e89c109d5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -35,7 +35,6 @@ script: - yarn lint - yarn unit:silentcoverage - yarn type-check - - yarn closure - yarn diff:sample-json - yarn smoke:silentcoverage - yarn test-extension diff --git a/lighthouse-core/closure/closure-type-checking.js b/lighthouse-core/closure/closure-type-checking.js deleted file mode 100755 index 1e1312f9d69e..000000000000 --- a/lighthouse-core/closure/closure-type-checking.js +++ /dev/null @@ -1,101 +0,0 @@ -#!/usr/bin/env node -/** - * @license Copyright 2016 Google Inc. All Rights Reserved. - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. - */ -'use strict'; - -const closureCompiler = require('google-closure-compiler').gulp(); -const gulp = require('gulp'); -const gutil = require('gulp-util'); -const replace = require('gulp-replace'); - -// Flags to generate additional debug information. -const PRINT_AST = false; -const PRINT_CODE = !PRINT_AST && false; -const OUTPUT_FILE = PRINT_AST ? '../closure-tree.txt' : 'closure-output.js'; - -/* eslint-disable camelcase */ -gulp.task('compile-report', () => { - return gulp.src([ - // externs - 'closure/third_party/commonjs.js', - 'closure/typedefs/viewer-externs.js', - 'closure/typedefs/devtools-externs.js', - 'closure/typedefs/element-externs.js', - - 'lib/file-namer.js', - 'report/html/renderer/*.js', - '../lighthouse-viewer/app/src/viewer-ui-features.js', - ]) - - // Ignore `module.exports` and `self.ClassName = ClassName` statements. - .pipe(replace(/^\s\smodule\.exports = \w+;$/gm, ';')) - .pipe(replace(/^\s\sself\.(\w+) = \1;$/gm, ';')) - - // Remove node-specific code from file-namer so it can be included in report. - .pipe(replace(/^\s\smodule\.exports = {\w+};$/gm, ';')) - .pipe(replace('require(\'./url-shim\');', 'null;')) - .pipe(replace('(URLConstructor || URL)', 'URL')) - - .pipe(closureCompiler({ - compilation_level: 'SIMPLE', - // new_type_inf: true, - language_in: 'ECMASCRIPT6_STRICT', - language_out: 'ECMASCRIPT5_STRICT', - warning_level: 'VERBOSE', - jscomp_error: [ - 'checkTypes', - 'missingProperties', - 'accessControls', - 'const', - 'visibility', - - // This is *very* strict, so we can move back to a warning if needed. - 'reportUnknownTypes', - ], - jscomp_warning: [ - // https://github.com/google/closure-compiler/wiki/Warnings - 'checkRegExp', - 'missingReturn', - 'strictModuleDepCheck', - 'typeInvalidation', - 'undefinedNames', - - 'checkDebuggerStatement', - 'externsValidation', - 'uselessCode', - 'ambiguousFunctionDecl', - 'es3', - 'es5Strict', - 'globalThis', - 'nonStandardJsDocs', - 'suspiciousCode', - 'unknownDefines', - - // nullable/undefined checker when new_type_inf enabled. - 'newCheckTypesAllChecks', - ], - conformance_configs: 'closure/conformance_config.textproto', - - // Debug output control. - checks_only: !PRINT_CODE, - print_tree: PRINT_AST, - js_output_file: OUTPUT_FILE, - formatting: 'PRETTY_PRINT', - preserve_type_annotations: true, - })) - .on('error', err => { - gutil.log(err.message); - return process.exit(1); - }) - .pipe(gulp.dest('../')) - .on('end', () => { - gutil.log('Closure compilation successful.'); - }); -}); - -/* eslint-enable */ - -gulp.start('compile-report'); diff --git a/lighthouse-core/closure/conformance_config.textproto b/lighthouse-core/closure/conformance_config.textproto deleted file mode 100644 index 42ee1b8df755..000000000000 --- a/lighthouse-core/closure/conformance_config.textproto +++ /dev/null @@ -1,31 +0,0 @@ -# Copyright 2016 Google Inc. All Rights Reserved. -# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 -# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. - -# This file defines the additional JS conformance tests run over lighthouse code -# when compiled with the Closure Compiler. For more, see https://github.com/google/closure-compiler/wiki/JS-Conformance-Framework - -# Better covered by newCheckTypesAllChecks under NTI -# requirement: { -# type: CUSTOM -# java_class: 'com.google.javascript.jscomp.ConformanceRules$BanNullDeref' -# error_message: 'Dereferencing `null` or `undefined` is usually an error. See https://github.com/google/closure-compiler/wiki/JS-Conformance-Framework#bannullderef' -# } - -requirement: { - type: CUSTOM - java_class: 'com.google.javascript.jscomp.ConformanceRules$BanUnknownThis' - error_message: 'References to `this` that are typed as `unknown` are not allowed. See https://github.com/google/closure-compiler/wiki/JS-Conformance-Framework#banunknownthis' -} - -requirement: { - type: CUSTOM - java_class: 'com.google.javascript.jscomp.ConformanceRules$BanUnknownTypedClassPropsReferences' - error_message: 'References to object props that are typed as `unknown` are discouraged. See https://github.com/google/closure-compiler/wiki/JS-Conformance-Framework#banunknowntypedclasspropsreferences' -} - -requirement: { - type: CUSTOM - java_class: 'com.google.javascript.jscomp.ConformanceRules$BanUnresolvedType' - error_message: 'Forward-declared types must be included in the compilation. See https://github.com/google/closure-compiler/wiki/JS-Conformance-Framework#banunresolvedtype' -} diff --git a/lighthouse-core/lib/dependency-graph/simulator/simulator.js b/lighthouse-core/lib/dependency-graph/simulator/simulator.js index c4fea57d50ef..1cb743b613ba 100644 --- a/lighthouse-core/lib/dependency-graph/simulator/simulator.js +++ b/lighthouse-core/lib/dependency-graph/simulator/simulator.js @@ -321,7 +321,7 @@ class Simulator { options = Object.assign({flexibleOrdering: false}, options); // initialize the necessary data containers - this._flexibleOrdering = options.flexibleOrdering; + this._flexibleOrdering = !!options.flexibleOrdering; this._initializeConnectionPool(graph); this._initializeAuxiliaryData(); diff --git a/lighthouse-core/report/html/renderer/category-renderer.js b/lighthouse-core/report/html/renderer/category-renderer.js index 84d1cbac90ad..889d184dddaa 100644 --- a/lighthouse-core/report/html/renderer/category-renderer.js +++ b/lighthouse-core/report/html/renderer/category-renderer.js @@ -7,26 +7,34 @@ /* globals self, Util */ +/** @typedef {import('./dom.js')} DOM */ +/** @typedef {import('./report-renderer.js')} ReportRenderer */ +/** @typedef {import('./report-renderer.js').AuditJSON} AuditJSON */ +/** @typedef {import('./report-renderer.js').CategoryJSON} CategoryJSON */ +/** @typedef {import('./report-renderer.js').GroupJSON} GroupJSON */ +/** @typedef {import('./details-renderer.js')} DetailsRenderer */ +/** @typedef {import('./util.js')} Util */ + class CategoryRenderer { /** - * @param {!DOM} dom - * @param {!DetailsRenderer} detailsRenderer + * @param {DOM} dom + * @param {DetailsRenderer} detailsRenderer */ constructor(dom, detailsRenderer) { - /** @protected {!DOM} */ + /** @type {DOM} */ this.dom = dom; - /** @protected {!DetailsRenderer} */ + /** @type {DetailsRenderer} */ this.detailsRenderer = detailsRenderer; - /** @protected {!Document|!Element} */ + /** @type {ParentNode} */ this.templateContext = this.dom.document(); this.detailsRenderer.setTemplateContext(this.templateContext); } /** - * @param {!ReportRenderer.AuditJSON} audit + * @param {AuditJSON} audit * @param {number} index - * @return {!Element} + * @return {Element} */ renderAudit(audit, index) { const tmpl = this.dom.cloneTemplate('#tmpl-lh-audit', this.templateContext); @@ -45,7 +53,7 @@ class CategoryRenderer { .appendChild(this.dom.convertMarkdownLinkSnippets(audit.result.description)); // Append audit details to header section so the entire audit is within a
. - const header = /** @type {!HTMLDetailsElement} */ (this.dom.find('details', auditEl)); + const header = /** @type {HTMLDetailsElement} */ (this.dom.find('details', auditEl)); if (audit.result.details && audit.result.details.type) { const elem = this.detailsRenderer.render(audit.result.details); elem.classList.add('lh-details'); @@ -73,10 +81,10 @@ class CategoryRenderer { } /** - * @param {!Element} element DOM node to populate with values. + * @param {Element} element DOM node to populate with values. * @param {number|null} score * @param {string} scoreDisplayMode - * @return {!Element} + * @return {Element} */ _setRatingClass(element, score, scoreDisplayMode) { const rating = Util.calculateRating(score, scoreDisplayMode); @@ -85,8 +93,8 @@ class CategoryRenderer { } /** - * @param {!ReportRenderer.CategoryJSON} category - * @return {!Element} + * @param {CategoryJSON} category + * @return {Element} */ renderCategoryHeader(category) { const tmpl = this.dom.cloneTemplate('#tmpl-lh-category-header', this.templateContext); @@ -102,16 +110,15 @@ class CategoryRenderer { this.dom.find('.lh-category-header__description', tmpl).appendChild(descEl); } - - return /** @type {!Element} */ (tmpl.firstElementChild); + return /** @type {Element} */ (tmpl.firstElementChild); } /** * Renders the group container for a group of audits. Individual audit elements can be added * directly to the returned element. - * @param {!ReportRenderer.GroupJSON} group - * @param {{expandable: boolean, itemCount: (number|undefined)}} opts - * @return {!Element} + * @param {GroupJSON} group + * @param {{expandable: boolean, itemCount?: number}} opts + * @return {Element} */ renderAuditGroup(group, opts) { const expandable = opts.expandable; @@ -140,7 +147,7 @@ class CategoryRenderer { /** * Find the total number of audits contained within a section. * Accounts for nested subsections like Accessibility. - * @param {!Array} elements + * @param {Array} elements * @return {number} */ _getTotalAuditsLength(elements) { @@ -158,8 +165,8 @@ class CategoryRenderer { } /** - * @param {!Array} elements - * @return {!Element} + * @param {Array} elements + * @return {Element} */ _renderFailedAuditsSection(elements) { const failedElem = this.dom.createElement('div'); @@ -169,8 +176,8 @@ class CategoryRenderer { } /** - * @param {!Array} elements - * @return {!Element} + * @param {Array} elements + * @return {Element} */ renderPassedAuditsSection(elements) { const passedElem = this.renderAuditGroup({ @@ -182,8 +189,8 @@ class CategoryRenderer { } /** - * @param {!Array} elements - * @return {!Element} + * @param {Array} elements + * @return {Element} */ _renderNotApplicableAuditsSection(elements) { const notApplicableElem = this.renderAuditGroup({ @@ -195,9 +202,9 @@ class CategoryRenderer { } /** - * @param {!Array} manualAudits + * @param {Array} manualAudits * @param {string} manualDescription - * @return {!Element} + * @return {Element} */ _renderManualAudits(manualAudits, manualDescription) { const group = {title: 'Additional items to manually check', description: manualDescription}; @@ -211,7 +218,7 @@ class CategoryRenderer { } /** - * @param {!Document|!Element} context + * @param {ParentNode} context */ setTemplateContext(context) { this.templateContext = context; @@ -219,12 +226,12 @@ class CategoryRenderer { } /** - * @param {!ReportRenderer.CategoryJSON} category - * @return {!DocumentFragment} + * @param {CategoryJSON} category + * @return {DocumentFragment} */ renderScoreGauge(category) { const tmpl = this.dom.cloneTemplate('#tmpl-lh-gauge', this.templateContext); - const wrapper = this.dom.find('.lh-gauge__wrapper', tmpl); + const wrapper = /** @type {HTMLAnchorElement} */ (this.dom.find('.lh-gauge__wrapper', tmpl)); wrapper.href = `#${category.id}`; wrapper.classList.add(`lh-gauge__wrapper--${Util.calculateRating(category.score)}`); @@ -234,11 +241,15 @@ class CategoryRenderer { // 329 is ~= 2 * Math.PI * gauge radius (53) // https://codepen.io/xgad/post/svg-radial-progress-meters // score of 50: `stroke-dasharray: 164.5 329`; - this.dom.find('.lh-gauge-arc', gauge).style.strokeDasharray = `${numericScore * 329} 329`; + /** @type {?SVGCircleElement} */ + const gaugeArc = gauge.querySelector('.lh-gauge-arc'); + if (gaugeArc) { + gaugeArc.style.strokeDasharray = `${numericScore * 329} 329`; + } const scoreOutOf100 = Math.round(numericScore * 100); const percentageEl = this.dom.find('.lh-gauge__percentage', tmpl); - percentageEl.textContent = scoreOutOf100; + percentageEl.textContent = scoreOutOf100.toString(); if (category.score === null) { percentageEl.textContent = '?'; percentageEl.title = 'Errors occurred while auditing'; @@ -249,9 +260,9 @@ class CategoryRenderer { } /** - * @param {!ReportRenderer.CategoryJSON} category - * @param {!Object} groupDefinitions - * @return {!Element} + * @param {CategoryJSON} category + * @param {Object} groupDefinitions + * @return {Element} */ render(category, groupDefinitions) { const element = this.dom.createElement('div', 'lh-category'); @@ -262,10 +273,8 @@ class CategoryRenderer { const manualAudits = auditRefs.filter(audit => audit.result.scoreDisplayMode === 'manual'); const nonManualAudits = auditRefs.filter(audit => !manualAudits.includes(audit)); - const auditsGroupedByGroup = /** @type {!Object, - failed: !Array, - notApplicable: !Array}>} */ ({}); + /** @type {Object, failed: Array, notApplicable: Array}>} */ + const auditsGroupedByGroup = {}; const auditsUngrouped = {passed: [], failed: [], notApplicable: []}; nonManualAudits.forEach(auditRef => { @@ -293,15 +302,15 @@ class CategoryRenderer { } }); - const failedElements = /** @type {!Array} */ ([]); - const passedElements = /** @type {!Array} */ ([]); - const notApplicableElements = /** @type {!Array} */ ([]); + const failedElements = /** @type {Array} */ ([]); + const passedElements = /** @type {Array} */ ([]); + const notApplicableElements = /** @type {Array} */ ([]); - auditsUngrouped.failed.forEach((/** @type {!ReportRenderer.AuditJSON} */ audit, i) => + auditsUngrouped.failed.forEach((/** @type {AuditJSON} */ audit, i) => failedElements.push(this.renderAudit(audit, i))); - auditsUngrouped.passed.forEach((/** @type {!ReportRenderer.AuditJSON} */ audit, i) => + auditsUngrouped.passed.forEach((/** @type {AuditJSON} */ audit, i) => passedElements.push(this.renderAudit(audit, i))); - auditsUngrouped.notApplicable.forEach((/** @type {!ReportRenderer.AuditJSON} */ audit, i) => + auditsUngrouped.notApplicable.forEach((/** @type {AuditJSON} */ audit, i) => notApplicableElements.push(this.renderAudit(audit, i))); Object.keys(auditsGroupedByGroup).forEach(groupId => { @@ -312,7 +321,6 @@ class CategoryRenderer { const auditGroupElem = this.renderAuditGroup(group, {expandable: false}); groups.failed.forEach((item, i) => auditGroupElem.appendChild(this.renderAudit(item, i))); auditGroupElem.classList.add('lh-audit-group--unadorned'); - auditGroupElem.open = true; failedElements.push(auditGroupElem); } @@ -357,7 +365,7 @@ class CategoryRenderer { /** * Create a non-semantic span used for hash navigation of categories - * @param {!Element} element + * @param {Element} element * @param {string} id */ createPermalinkSpan(element, id) { diff --git a/lighthouse-core/report/html/renderer/crc-details-renderer.js b/lighthouse-core/report/html/renderer/crc-details-renderer.js index 7960b46bf8c9..5c5fe7957421 100644 --- a/lighthouse-core/report/html/renderer/crc-details-renderer.js +++ b/lighthouse-core/report/html/renderer/crc-details-renderer.js @@ -12,11 +12,13 @@ /* globals self Util */ +/** @typedef {import('./dom.js')} DOM */ + class CriticalRequestChainRenderer { /** * Create render context for critical-request-chain tree display. - * @param {!Object} tree - * @return {{tree: !Object, startTime: number, transferSize: number}} + * @param {LH.Audit.SimpleCriticalRequestNode} tree + * @return {{tree: LH.Audit.SimpleCriticalRequestNode, startTime: number, transferSize: number}} */ static initTree(tree) { let startTime = 0; @@ -34,13 +36,13 @@ class CriticalRequestChainRenderer { * parent. Calculates if this node is the last child, whether it has any * children itself and what the tree looks like all the way back up to the root, * so the tree markers can be drawn correctly. - * @param {!Object} parent + * @param {LH.Audit.SimpleCriticalRequestNode} parent * @param {string} id * @param {number} startTime * @param {number} transferSize - * @param {!Array=} treeMarkers + * @param {Array=} treeMarkers * @param {boolean=} parentIsLastChild - * @return {!CriticalRequestChainRenderer.CRCSegment} + * @return {CRCSegment} */ static createSegment(parent, id, startTime, transferSize, treeMarkers, parentIsLastChild) { const node = parent[id]; @@ -68,10 +70,10 @@ class CriticalRequestChainRenderer { /** * Creates the DOM for a tree segment. - * @param {!DOM} dom - * @param {!DocumentFragment} tmpl - * @param {!CriticalRequestChainRenderer.CRCSegment} segment - * @return {!Node} + * @param {DOM} dom + * @param {DocumentFragment} tmpl + * @param {CRCSegment} segment + * @return {Node} */ static createChainNode(dom, tmpl, segment) { const chainsEl = dom.cloneTemplate('#tmpl-lh-crc__chains', tmpl); @@ -128,11 +130,11 @@ class CriticalRequestChainRenderer { /** * Recursively builds a tree from segments. - * @param {!DOM} dom - * @param {!DocumentFragment} tmpl - * @param {!CriticalRequestChainRenderer.CRCSegment} segment - * @param {!Element} elem Parent element. - * @param {!CriticalRequestChainRenderer.CRCDetailsJSON} details + * @param {DOM} dom + * @param {DocumentFragment} tmpl + * @param {CRCSegment} segment + * @param {Element} elem Parent element. + * @param {CRCDetailsJSON} details */ static buildTree(dom, tmpl, segment, elem, details) { elem.appendChild(CriticalRequestChainRenderer.createChainNode(dom, tmpl, segment)); @@ -145,10 +147,10 @@ class CriticalRequestChainRenderer { } /** - * @param {!DOM} dom - * @param {!Node} templateContext - * @param {!CriticalRequestChainRenderer.CRCDetailsJSON} details - * @return {!Element} + * @param {DOM} dom + * @param {ParentNode} templateContext + * @param {CRCDetailsJSON} details + * @return {Element} */ static render(dom, templateContext, details) { const tmpl = dom.cloneTemplate('#tmpl-lh-crc', templateContext); @@ -157,7 +159,7 @@ class CriticalRequestChainRenderer { // Fill in top summary. dom.find('.lh-crc__longest_duration', tmpl).textContent = Util.formatNumber(details.longestChain.duration) + 'ms'; - dom.find('.lh-crc__longest_length', tmpl).textContent = details.longestChain.length; + dom.find('.lh-crc__longest_length', tmpl).textContent = details.longestChain.length.toString(); dom.find('.lh-crc__longest_transfersize', tmpl).textContent = Util.formatBytesToKB(details.longestChain.transferSize); @@ -181,44 +183,19 @@ if (typeof module !== 'undefined' && module.exports) { } /** @typedef {{ - * type: string, - * header: {text: string}, - * longestChain: {duration: number, length: number, transferSize: number}, - * chains: !Object - * }} + type: string, + header: {text: string}, + longestChain: {duration: number, length: number, transferSize: number}, + chains: LH.Audit.SimpleCriticalRequestNode + }} CRCDetailsJSON */ -CriticalRequestChainRenderer.CRCDetailsJSON; // eslint-disable-line no-unused-expressions - -/** @typedef {{ - * endTime: number, - * responseReceivedTime: number, - * startTime: number, - * transferSize: number, - * url: string - * }} - */ -CriticalRequestChainRenderer.CRCRequest; // eslint-disable-line no-unused-expressions - -/** - * Record type so children can circularly have CRCNode values. - * @struct - * @record - */ -CriticalRequestChainRenderer.CRCNode = function() {}; - -/** @type {!Object} */ -CriticalRequestChainRenderer.CRCNode.prototype.children; // eslint-disable-line no-unused-expressions - -/** @type {!CriticalRequestChainRenderer.CRCRequest} */ -CriticalRequestChainRenderer.CRCNode.prototype.request; // eslint-disable-line no-unused-expressions /** @typedef {{ - * node: !CriticalRequestChainRenderer.CRCNode, - * isLastChild: boolean, - * hasChildren: boolean, - * startTime: number, - * transferSize: number, - * treeMarkers: !Array - * }} + node: LH.Audit.SimpleCriticalRequestNode[string], + isLastChild: boolean, + hasChildren: boolean, + startTime: number, + transferSize: number, + treeMarkers: Array + }} CRCSegment */ -CriticalRequestChainRenderer.CRCSegment; // eslint-disable-line no-unused-expressions diff --git a/lighthouse-core/report/html/renderer/details-renderer.js b/lighthouse-core/report/html/renderer/details-renderer.js index e141ec390080..b9082090c0d5 100644 --- a/lighthouse-core/report/html/renderer/details-renderer.js +++ b/lighthouse-core/report/html/renderer/details-renderer.js @@ -7,54 +7,61 @@ /* globals self CriticalRequestChainRenderer Util URL */ +/** @typedef {import('./dom.js')} DOM */ +/** @typedef {import('./crc-details-renderer.js')} CRCDetailsJSON */ + class DetailsRenderer { /** - * @param {!DOM} dom + * @param {DOM} dom */ constructor(dom) { - /** @private {!DOM} */ + /** @type {DOM} */ this._dom = dom; - /** @private {!Document|!Element} */ + /** @type {ParentNode} */ this._templateContext; // eslint-disable-line no-unused-expressions } /** - * @param {!Document|!Element} context + * @param {ParentNode} context */ setTemplateContext(context) { this._templateContext = context; } /** - * @param {!DetailsRenderer.DetailsJSON} details - * @return {!Element} + * @param {DetailsJSON} details + * @return {Element} */ render(details) { switch (details.type) { case 'text': - return this._renderText(/** @type {!DetailsRenderer.StringDetailsJSON} */ (details)); + return this._renderText(/** @type {StringDetailsJSON} */ (details)); case 'url': - return this._renderTextURL(/** @type {!DetailsRenderer.StringDetailsJSON} */ (details)); + return this._renderTextURL(/** @type {StringDetailsJSON} */ (details)); case 'bytes': - return this._renderBytes(/** @type {!DetailsRenderer.NumericUnitDetailsJSON} */ (details)); + return this._renderBytes(/** @type {NumericUnitDetailsJSON} */ (details)); case 'ms': // eslint-disable-next-line max-len - return this._renderMilliseconds(/** @type {!DetailsRenderer.NumericUnitDetailsJSON} */ (details)); + return this._renderMilliseconds(/** @type {NumericUnitDetailsJSON} */ (details)); case 'link': - return this._renderLink(/** @type {!DetailsRenderer.LinkDetailsJSON} */ (details)); + // @ts-ignore - TODO(bckenny): Fix type hierarchy + return this._renderLink(/** @type {LinkDetailsJSON} */ (details)); case 'thumbnail': - return this._renderThumbnail(/** @type {!DetailsRenderer.ThumbnailDetails} */ (details)); + return this._renderThumbnail(/** @type {ThumbnailDetails} */ (details)); case 'filmstrip': - return this._renderFilmstrip(/** @type {!DetailsRenderer.FilmstripDetails} */ (details)); + // @ts-ignore - TODO(bckenny): Fix type hierarchy + return this._renderFilmstrip(/** @type {FilmstripDetails} */ (details)); case 'table': - return this._renderTable(/** @type {!DetailsRenderer.TableDetailsJSON} */ (details)); + // @ts-ignore - TODO(bckenny): Fix type hierarchy + return this._renderTable(/** @type {TableDetailsJSON} */ (details)); case 'code': return this._renderCode(details); case 'node': - return this.renderNode(/** @type {!DetailsRenderer.NodeDetailsJSON} */(details)); + return this.renderNode(/** @type {NodeDetailsJSON} */(details)); case 'criticalrequestchain': return CriticalRequestChainRenderer.render(this._dom, this._templateContext, - /** @type {!CriticalRequestChainRenderer.CRCDetailsJSON} */ (details)); + // @ts-ignore - TODO(bckenny): Fix type hierarchy + /** @type {CRCDetailsJSON} */ (details)); default: { throw new Error(`Unknown type: ${details.type}`); } @@ -62,8 +69,8 @@ class DetailsRenderer { } /** - * @param {!DetailsRenderer.NumericUnitDetailsJSON} details - * @return {!Element} + * @param {NumericUnitDetailsJSON} details + * @return {Element} */ _renderBytes(details) { // TODO: handle displayUnit once we have something other than 'kb' @@ -72,8 +79,8 @@ class DetailsRenderer { } /** - * @param {!DetailsRenderer.NumericUnitDetailsJSON} details - * @return {!Element} + * @param {NumericUnitDetailsJSON} details + * @return {Element} */ _renderMilliseconds(details) { let value = Util.formatMilliseconds(details.value, details.granularity); @@ -85,8 +92,8 @@ class DetailsRenderer { } /** - * @param {!DetailsRenderer.StringDetailsJSON} text - * @return {!Element} + * @param {StringDetailsJSON} text + * @return {HTMLElement} */ _renderTextURL(text) { const url = text.value; @@ -106,7 +113,7 @@ class DetailsRenderer { displayedPath = url; } - const element = this._dom.createElement('div', 'lh-text__url'); + const element = /** @type {HTMLElement} */ (this._dom.createElement('div', 'lh-text__url')); element.appendChild(this._renderText({ value: displayedPath, type: 'text', @@ -126,8 +133,8 @@ class DetailsRenderer { } /** - * @param {!DetailsRenderer.LinkDetailsJSON} details - * @return {!Element} + * @param {LinkDetailsJSON} details + * @return {Element} */ _renderLink(details) { const allowedProtocols = ['https:', 'http:']; @@ -140,7 +147,7 @@ class DetailsRenderer { }); } - const a = /** @type {!HTMLAnchorElement} */ (this._dom.createElement('a')); + const a = /** @type {HTMLAnchorElement} */ (this._dom.createElement('a')); a.rel = 'noopener'; a.target = '_blank'; a.textContent = details.text; @@ -150,8 +157,8 @@ class DetailsRenderer { } /** - * @param {!DetailsRenderer.StringDetailsJSON} text - * @return {!Element} + * @param {StringDetailsJSON} text + * @return {Element} */ _renderText(text) { const element = this._dom.createElement('div', 'lh-text'); @@ -162,20 +169,23 @@ class DetailsRenderer { /** * Create small thumbnail with scaled down image asset. * If the supplied details doesn't have an image/* mimeType, then an empty span is returned. - * @param {!DetailsRenderer.ThumbnailDetails} details - * @return {!Element} + * @param {ThumbnailDetails} details + * @return {Element} */ _renderThumbnail(details) { - const element = this._dom.createElement('img', 'lh-thumbnail'); - element.src = details.value; - element.title = details.value; + const element = /** @type {HTMLImageElement}*/ (this._dom.createElement('img', 'lh-thumbnail')); + /** @type {string} */ + // @ts-ignore - type should have a value if we get here. + const strValue = details.value; + element.src = strValue; + element.title = strValue; element.alt = ''; return element; } /** - * @param {!DetailsRenderer.TableDetailsJSON} details - * @return {!Element} + * @param {TableDetailsJSON} details + * @return {Element} */ _renderTable(details) { if (!details.items.length) return this._dom.createElement('span'); @@ -197,15 +207,18 @@ class DetailsRenderer { for (const row of details.items) { const rowElem = this._dom.createChildOf(tbodyElem, 'tr'); for (const heading of details.headings) { - const value = /** @type {number|string|!DetailsRenderer.DetailsJSON} */ (row[heading.key]); + const key = /** @type {keyof DetailsJSON} */ (heading.key); + // TODO(bckenny): type should be naturally inferred here. + const value = /** @type {number|string|DetailsJSON|undefined} */ (row[key]); if (typeof value === 'undefined' || value === null) { this._dom.createChildOf(rowElem, 'td', 'lh-table-column--empty'); continue; } // handle nested types like code blocks in table rows. + // @ts-ignore - TODO(bckenny): narrow first if (value.type) { - const valueAsDetails = /** @type {!DetailsRenderer.DetailsJSON} */ (value); + const valueAsDetails = /** @type {DetailsJSON} */ (value); const classes = `lh-table-column--${valueAsDetails.type}`; this._dom.createChildOf(rowElem, 'td', classes).appendChild(this.render(valueAsDetails)); continue; @@ -218,7 +231,11 @@ class DetailsRenderer { displayUnit: heading.displayUnit, granularity: heading.granularity, }; - const classes = `lh-table-column--${value.type || heading.itemType}`; + + /** @type {string|undefined} */ + // @ts-ignore - TODO(bckenny): handle with refactoring above + const valueType = value.type; + const classes = `lh-table-column--${valueType || heading.itemType}`; this._dom.createChildOf(rowElem, 'td', classes).appendChild(this.render(item)); } } @@ -226,14 +243,18 @@ class DetailsRenderer { } /** - * @param {!DetailsRenderer.NodeDetailsJSON} item - * @return {!Element} + * @param {NodeDetailsJSON} item + * @return {Element} * @protected */ renderNode(item) { - const element = this._dom.createElement('span', 'lh-node'); - element.textContent = item.snippet; - element.title = item.selector; + const element = /** @type {HTMLSpanElement} */ (this._dom.createElement('span', 'lh-node')); + if (item.snippet) { + element.textContent = item.snippet; + } + if (item.selector) { + element.title = item.selector; + } if (item.path) element.setAttribute('data-path', item.path); if (item.selector) element.setAttribute('data-selector', item.selector); if (item.snippet) element.setAttribute('data-snippet', item.snippet); @@ -241,8 +262,8 @@ class DetailsRenderer { } /** - * @param {!DetailsRenderer.FilmstripDetails} details - * @return {!Element} + * @param {FilmstripDetails} details + * @return {Element} */ _renderFilmstrip(details) { const filmstripEl = this._dom.createElement('div', 'lh-filmstrip'); @@ -258,12 +279,12 @@ class DetailsRenderer { } /** - * @param {!DetailsRenderer.DetailsJSON} details - * @return {!Element} + * @param {DetailsJSON} details + * @return {Element} */ _renderCode(details) { const pre = this._dom.createElement('pre', 'lh-code'); - pre.textContent = details.value; + pre.textContent = /** @type {string} */ (details.value); return pre; } } @@ -277,92 +298,81 @@ if (typeof module !== 'undefined' && module.exports) { // TODO, what's the diff between DetailsJSON and NumericUnitDetailsJSON? /** * @typedef {{ - * type: string, - * value: (string|number|undefined), - * summary: (DetailsRenderer.OpportunitySummary|undefined), - * granularity: (number|undefined), - * displayUnit: (string|undefined) - * }} + type: string, + value: (string|number|undefined), + summary?: OpportunitySummary, + granularity?: number, + displayUnit?: string + }} DetailsJSON */ -DetailsRenderer.DetailsJSON; // eslint-disable-line no-unused-expressions /** * @typedef {{ - * type: string, - * value: string, - * granularity: (number|undefined), - * displayUnit: (string|undefined), - * }} + type: string, + value: string, + granularity?: number, + displayUnit?: string, + }} StringDetailsJSON */ -DetailsRenderer.StringDetailsJSON; // eslint-disable-line no-unused-expressions - /** * @typedef {{ - * type: string, - * value: number, - * granularity: (number|undefined), - * displayUnit: (string|undefined), - * }} + type: string, + value: number, + granularity?: number, + displayUnit?: string, + }} NumericUnitDetailsJSON */ -DetailsRenderer.NumericUnitDetailsJSON; // eslint-disable-line no-unused-expressions /** * @typedef {{ - * type: string, - * path: (string|undefined), - * selector: (string|undefined), - * snippet:(string|undefined) - * }} + type: string, + path?: string, + selector?: string, + snippet?: string + }} NodeDetailsJSON */ -DetailsRenderer.NodeDetailsJSON; // eslint-disable-line no-unused-expressions /** * @typedef {{ - * itemType: string, - * key: string, - * text: (string|undefined), - * granularity: (number|undefined), - * displayUnit: (string|undefined), - * }} + itemType: string, + key: string, + text?: string, + granularity?: number, + displayUnit?: string, + }} TableHeaderJSON */ -DetailsRenderer.TableHeaderJSON; // eslint-disable-line no-unused-expressions /** @typedef {{ - * type: string, - * items: !Array, - * headings: !Array - * }} + type: string, + items: Array, + headings: Array + }} TableDetailsJSON */ -DetailsRenderer.TableDetailsJSON; // eslint-disable-line no-unused-expressions /** @typedef {{ - * type: string, - * value: (string|undefined), - * }} + type: string, + value?: string, + }} ThumbnailDetails */ -DetailsRenderer.ThumbnailDetails; // eslint-disable-line no-unused-expressions /** @typedef {{ - * type: string, - * text: string, - * url: string - * }} + type: string, + text: string, + url: string + }} LinkDetailsJSON */ -DetailsRenderer.LinkDetailsJSON; // eslint-disable-line no-unused-expressions /** @typedef {{ - * type: string, - * scale: number, - * items: !Array<{timing: number, timestamp: number, data: string}>, - * }} + type: string, + scale: number, + items: Array<{timing: number, timestamp: number, data: string}>, + }} FilmstripDetails */ -DetailsRenderer.FilmstripDetails; // eslint-disable-line no-unused-expressions /** @typedef {{ - * wastedMs: (number|undefined), - * wastedBytes: (number|undefined), - * }} + wastedMs?: number, + wastedBytes?: number + }} OpportunitySummary */ -DetailsRenderer.OpportunitySummary; // eslint-disable-line no-unused-expressions diff --git a/lighthouse-core/report/html/renderer/dom.js b/lighthouse-core/report/html/renderer/dom.js index c60f57aeb437..f6905b2af8ec 100644 --- a/lighthouse-core/report/html/renderer/dom.js +++ b/lighthouse-core/report/html/renderer/dom.js @@ -9,20 +9,21 @@ class DOM { /** - * @param {!Document} document + * @param {Document} document */ constructor(document) { - /** @private {!Document} */ + /** @type {Document} */ this._document = document; } + // TODO(bckenny): can pass along `createElement`'s inferred type /** * @param {string} name * @param {string=} className - * @param {!Object=} attrs Attribute key/val pairs. + * @param {Object=} attrs Attribute key/val pairs. * Note: if an attribute key has an undefined value, this method does not * set the attribute on the node. - * @return {!Element} + * @return {Element} */ createElement(name, className, attrs = {}) { const element = this._document.createElement(name); @@ -39,20 +40,20 @@ class DOM { } /** - * @return {!DocumentFragment} + * @return {DocumentFragment} */ createFragment() { return this._document.createDocumentFragment(); } /** - * @param {!Element} parentElem + * @param {Element} parentElem * @param {string} elementName * @param {string=} className - * @param {!Object=} attrs Attribute key/val pairs. + * @param {Object=} attrs Attribute key/val pairs. * Note: if an attribute key has an undefined value, this method does not * set the attribute on the node. - * @return {!Element} + * @return {Element} */ createChildOf(parentElem, elementName, className, attrs) { const element = this.createElement(elementName, className, attrs); @@ -62,8 +63,8 @@ class DOM { /** * @param {string} selector - * @param {!Node} context - * @return {!DocumentFragment} A clone of the template content. + * @param {ParentNode} context + * @return {DocumentFragment} A clone of the template content. * @throws {Error} */ cloneTemplate(selector, context) { @@ -72,15 +73,14 @@ class DOM { throw new Error(`Template not found: template${selector}`); } - const clone = /** @type {!DocumentFragment} */ (this._document.importNode( - template.content, true)); + const clone = this._document.importNode(template.content, true); // Prevent duplicate styles in the DOM. After a template has been stamped // for the first time, remove the clone's styles so they're not re-added. if (template.hasAttribute('data-stamped')) { this.findAll('style', clone).forEach(style => style.remove()); } - template.setAttribute('data-stamped', true); + template.setAttribute('data-stamped', 'true'); return clone; } @@ -96,7 +96,7 @@ class DOM { /** * @param {string} text - * @return {!Element} + * @return {Element} */ convertMarkdownLinkSnippets(text) { const element = this.createElement('span'); @@ -111,7 +111,7 @@ class DOM { // Append link if there are any. if (linkText && linkHref) { - const a = /** @type {!HTMLAnchorElement} */ (this.createElement('a')); + const a = /** @type {HTMLAnchorElement} */ (this.createElement('a')); a.rel = 'noopener'; a.target = '_blank'; a.textContent = linkText; @@ -125,7 +125,7 @@ class DOM { /** * @param {string} text - * @return {!Element} + * @return {Element} */ convertMarkdownCodeSnippets(text) { const element = this.createElement('span'); @@ -136,7 +136,7 @@ class DOM { const [preambleText, codeText] = parts.splice(0, 2); element.appendChild(this._document.createTextNode(preambleText)); if (codeText) { - const pre = /** @type {!HTMLPreElement} */ (this.createElement('code')); + const pre = /** @type {HTMLPreElement} */ (this.createElement('code')); pre.textContent = codeText; element.appendChild(pre); } @@ -146,7 +146,7 @@ class DOM { } /** - * @return {!Document} + * @return {Document} */ document() { return this._document; @@ -156,10 +156,11 @@ class DOM { * Guaranteed context.querySelector. Always returns an element or throws if * nothing matches query. * @param {string} query - * @param {!Node} context - * @return {!Element} + * @param {ParentNode} context + * @return {HTMLElement} */ find(query, context) { + /** @type {?HTMLElement} */ const result = context.querySelector(query); if (result === null) { throw new Error(`query ${query} not found`); @@ -170,8 +171,8 @@ class DOM { /** * Helper for context.querySelectorAll. Returns an Array instead of a NodeList. * @param {string} query - * @param {!Node} context - * @return {!Array} + * @param {ParentNode} context + * @return {Array} */ findAll(query, context) { return Array.from(context.querySelectorAll(query)); diff --git a/lighthouse-core/report/html/renderer/logger.js b/lighthouse-core/report/html/renderer/logger.js index 745e2290c54c..88cbea90c98f 100644 --- a/lighthouse-core/report/html/renderer/logger.js +++ b/lighthouse-core/report/html/renderer/logger.js @@ -10,23 +10,22 @@ */ class Logger { /** - * @param {!Element} element + * @param {Element} element */ constructor(element) { - /** @type {!Element} */ + /** @type {Element} */ this.el = element; - /** @private {?number} */ - this._id = null; + this._id = undefined; } /** * Shows a butter bar. - * @param {!string} msg The message to show. + * @param {string} msg The message to show. * @param {boolean=} autoHide True to hide the message after a duration. * Default is true. */ log(msg, autoHide = true) { - clearTimeout(this._id); + this._id && clearTimeout(this._id); this.el.textContent = msg; this.el.classList.add('show'); @@ -61,7 +60,7 @@ class Logger { * Explicitly hides the butter bar. */ hide() { - clearTimeout(this._id); + this._id && clearTimeout(this._id); this.el.classList.remove('show'); } } diff --git a/lighthouse-core/report/html/renderer/performance-category-renderer.js b/lighthouse-core/report/html/renderer/performance-category-renderer.js index ad55a4677df4..443cb4d715a4 100644 --- a/lighthouse-core/report/html/renderer/performance-category-renderer.js +++ b/lighthouse-core/report/html/renderer/performance-category-renderer.js @@ -7,10 +7,17 @@ /* globals self, Util, CategoryRenderer */ +/** @typedef {import('./dom.js')} DOM */ +/** @typedef {import('./report-renderer.js').CategoryJSON} CategoryJSON */ +/** @typedef {import('./report-renderer.js').GroupJSON} GroupJSON */ +/** @typedef {import('./report-renderer.js').AuditJSON} AuditJSON */ +/** @typedef {import('./details-renderer.js').OpportunitySummary} OpportunitySummary */ +/** @typedef {import('./details-renderer.js').FilmstripDetails} FilmstripDetails */ + class PerformanceCategoryRenderer extends CategoryRenderer { /** - * @param {!ReportRenderer.AuditJSON} audit - * @return {!Element} + * @param {AuditJSON} audit + * @return {Element} */ _renderMetric(audit) { const tmpl = this.dom.cloneTemplate('#tmpl-lh-metric', this.templateContext); @@ -39,10 +46,10 @@ class PerformanceCategoryRenderer extends CategoryRenderer { } /** - * @param {!ReportRenderer.AuditJSON} audit + * @param {AuditJSON} audit * @param {number} index * @param {number} scale - * @return {!Element} + * @return {Element} */ _renderOpportunity(audit, index, scale) { const tmpl = this.dom.cloneTemplate('#tmpl-lh-opportunity', this.templateContext); @@ -56,13 +63,15 @@ class PerformanceCategoryRenderer extends CategoryRenderer { if (audit.result.errorMessage || audit.result.explanation) { const debugStrEl = this.dom.createChildOf(titleEl, 'div', 'lh-debug'); - debugStrEl.textContent = audit.result.errorMessage || audit.result.explanation; + debugStrEl.textContent = audit.result.errorMessage || audit.result.explanation || null; } if (audit.result.scoreDisplayMode === 'error') return element; const details = audit.result.details; - const summaryInfo = /** @type {!DetailsRenderer.OpportunitySummary} - */ (details && details.summary); + if (!details) { + return element; + } + const summaryInfo = /** @type {OpportunitySummary} */ (details.summary); if (!summaryInfo || !summaryInfo.wastedMs) { return element; } @@ -91,7 +100,7 @@ class PerformanceCategoryRenderer extends CategoryRenderer { * Get an audit's wastedMs to sort the opportunity by, and scale the sparkline width * Opportunties with an error won't have a summary object, so MIN_VALUE is returned to keep any * erroring opportunities last in sort order. - * @param {!ReportRenderer.AuditJSON} audit + * @param {AuditJSON} audit * @return {number} */ _getWastedMs(audit) { @@ -107,6 +116,9 @@ class PerformanceCategoryRenderer extends CategoryRenderer { } /** + * @param {CategoryJSON} category + * @param {Object} groups + * @return {Element} * @override */ render(category, groups) { @@ -135,7 +147,6 @@ class PerformanceCategoryRenderer extends CategoryRenderer { 'lh-metrics__disclaimer lh-metrics__disclaimer'); estValuesEl.textContent = 'Values are estimated and may vary.'; - metricAuditsEl.open = true; metricAuditsEl.classList.add('lh-audit-group--metrics'); element.appendChild(metricAuditsEl); @@ -145,9 +156,7 @@ class PerformanceCategoryRenderer extends CategoryRenderer { const thumbnailResult = thumbnailAudit && thumbnailAudit.result; if (thumbnailResult && thumbnailResult.details) { timelineEl.id = thumbnailResult.id; - const thumbnailDetails = /** @type {!DetailsRenderer.FilmstripDetails} */ - (thumbnailResult.details); - const filmstripEl = this.detailsRenderer.render(thumbnailDetails); + const filmstripEl = this.detailsRenderer.render(thumbnailResult.details); timelineEl.appendChild(filmstripEl); } @@ -168,7 +177,6 @@ class PerformanceCategoryRenderer extends CategoryRenderer { groupEl.appendChild(headerEl); opportunityAudits.forEach((item, i) => groupEl.appendChild(this._renderOpportunity(item, i, scale))); - groupEl.open = true; groupEl.classList.add('lh-audit-group--opportunities'); element.appendChild(groupEl); } @@ -185,7 +193,6 @@ class PerformanceCategoryRenderer extends CategoryRenderer { if (diagnosticAudits.length) { const groupEl = this.renderAuditGroup(groups['diagnostics'], {expandable: false}); diagnosticAudits.forEach((item, i) => groupEl.appendChild(this.renderAudit(item, i))); - groupEl.open = true; groupEl.classList.add('lh-audit-group--diagnostics'); element.appendChild(groupEl); } diff --git a/lighthouse-core/report/html/renderer/report-renderer.js b/lighthouse-core/report/html/renderer/report-renderer.js index a1db4f4a1127..8d3d935597f3 100644 --- a/lighthouse-core/report/html/renderer/report-renderer.js +++ b/lighthouse-core/report/html/renderer/report-renderer.js @@ -12,26 +12,29 @@ * Dummy text for ensuring report robustness: pre$`post %%LIGHTHOUSE_JSON%% */ +/** @typedef {import('./dom.js')} DOM */ +/** @typedef {import('./details-renderer.js').DetailsJSON} DetailsJSON */ + /* globals self, Util, DetailsRenderer, CategoryRenderer, PerformanceCategoryRenderer */ class ReportRenderer { /** - * @param {!DOM} dom + * @param {DOM} dom */ constructor(dom) { - /** @private {!DOM} */ + /** @type {DOM} */ this._dom = dom; - /** @private {!Document|!Element} */ + /** @type {ParentNode} */ this._templateContext = this._dom.document(); } /** - * @param {!ReportRenderer.ReportJSON} report - * @param {!Element} container Parent element to render the report into. + * @param {ReportJSON} report + * @param {Element} container Parent element to render the report into. */ renderReport(report, container) { // If any mutations happen to the report within the renderers, we want the original object untouched - const clone = /** @type {!ReportRenderer.ReportJSON} */ (JSON.parse(JSON.stringify(report))); + const clone = /** @type {ReportJSON} */ (JSON.parse(JSON.stringify(report))); // TODO(phulce): we all agree this is technical debt we should fix if (typeof clone.categories !== 'object') throw new Error('No categories provided.'); @@ -40,31 +43,31 @@ class ReportRenderer { container.textContent = ''; // Remove previous report. container.appendChild(this._renderReport(clone)); - return /** @type {!Element} **/ (container); + return /** @type {Element} **/ (container); } /** * Define a custom element for to be extracted from. For example: * this.setTemplateContext(new DOMParser().parseFromString(htmlStr, 'text/html')) - * @param {!Document|!Element} context + * @param {ParentNode} context */ setTemplateContext(context) { this._templateContext = context; } /** - * @param {!ReportRenderer.ReportJSON} report - * @return {!DocumentFragment} + * @param {ReportJSON} report + * @return {DocumentFragment} */ _renderReportHeader(report) { const header = this._dom.cloneTemplate('#tmpl-lh-heading', this._templateContext); this._dom.find('.lh-config__timestamp', header).textContent = Util.formatDateTime(report.fetchTime); this._dom.find('.lh-product-info__version', header).textContent = report.lighthouseVersion; - const url = this._dom.find('.lh-metadata__url', header); + const url = /** @type {HTMLAnchorElement} */ (this._dom.find('.lh-metadata__url', header)); url.href = report.finalUrl; url.textContent = report.finalUrl; - const toolbarUrl = this._dom.find('.lh-toolbar__url', header); + const toolbarUrl = /** @type {HTMLAnchorElement}*/ (this._dom.find('.lh-toolbar__url', header)); toolbarUrl.href = report.finalUrl; toolbarUrl.textContent = report.finalUrl; @@ -83,8 +86,8 @@ class ReportRenderer { } /** - * @param {!ReportRenderer.ReportJSON} report - * @return {!DocumentFragment} + * @param {ReportJSON} report + * @return {DocumentFragment} */ _renderReportFooter(report) { const footer = this._dom.cloneTemplate('#tmpl-lh-footer', this._templateContext); @@ -96,8 +99,8 @@ class ReportRenderer { /** * Returns a div with a list of top-level warnings, or an empty div if no warnings. - * @param {!ReportRenderer.ReportJSON} report - * @return {!Node} + * @param {ReportJSON} report + * @return {Node} */ _renderReportWarnings(report) { if (!report.runWarnings || report.runWarnings.length === 0) { @@ -115,8 +118,8 @@ class ReportRenderer { } /** - * @param {!ReportRenderer.ReportJSON} report - * @return {!DocumentFragment} + * @param {ReportJSON} report + * @return {DocumentFragment} */ _renderReport(report) { const headerStickyContainer = this._dom.createElement('div', 'lh-header-sticky'); @@ -172,8 +175,8 @@ class ReportRenderer { /** * Place the AuditResult into the auditDfn (which has just weight & group) - * @param {!Object} audits - * @param {!Array} reportCategories + * @param {Object} audits + * @param {Array} reportCategories */ static smooshAuditResultsIntoCategories(audits, reportCategories) { for (const category of reportCategories) { @@ -193,67 +196,62 @@ if (typeof module !== 'undefined' && module.exports) { /** * @typedef {{ - * rawValue: (number|boolean|undefined), - * id: string, - * title: string, - * description: string, - * explanation: (string|undefined), - * errorMessage: (string|undefined), - * displayValue: (string|Array|undefined), - * scoreDisplayMode: string, - * error: boolean, - * score: (number|null), - * details: (!DetailsRenderer.DetailsJSON|undefined), - * }} + rawValue: (number|boolean|undefined), + id: string, + title: string, + description: string, + explanation?: string, + errorMessage?: string, + displayValue?: string|Array, + scoreDisplayMode: string, + error: boolean, + score: (number|null), + details?: DetailsJSON, + }} AuditResultJSON */ -ReportRenderer.AuditResultJSON; // eslint-disable-line no-unused-expressions /** * @typedef {{ - * id: string, - * score: (number|null), - * weight: number, - * group: (string|undefined), - * result: ReportRenderer.AuditResultJSON - * }} + id: string, + score: (number|null), + weight: number, + group?: string, + result: AuditResultJSON + }} AuditJSON */ -ReportRenderer.AuditJSON; // eslint-disable-line no-unused-expressions /** * @typedef {{ - * title: string, - * id: string, - * score: (number|null), - * description: (string|undefined), - * manualDescription: string, - * auditRefs: !Array - * }} + title: string, + id: string, + score: (number|null), + description?: string, + manualDescription: string, + auditRefs: Array + }} CategoryJSON */ -ReportRenderer.CategoryJSON; // eslint-disable-line no-unused-expressions /** * @typedef {{ - * title: string, - * description: (string|undefined), - * }} + title: string, + description?: string, + }} GroupJSON */ -ReportRenderer.GroupJSON; // eslint-disable-line no-unused-expressions /** * @typedef {{ - * lighthouseVersion: string, - * userAgent: string, - * fetchTime: string, - * timing: {total: number}, - * requestedUrl: string, - * finalUrl: string, - * runWarnings: (!Array|undefined), - * artifacts: {traces: {defaultPass: {traceEvents: !Array}}}, - * audits: !Object, - * categories: !Object, - * reportCategories: !Array, - * categoryGroups: !Object, - * configSettings: !LH.Config.Settings, - * }} + lighthouseVersion: string, + userAgent: string, + fetchTime: string, + timing: {total: number}, + requestedUrl: string, + finalUrl: string, + runWarnings?: Array, + artifacts: {traces: {defaultPass: {traceEvents: Array}}}, + audits: Object, + categories: Object, + reportCategories: Array, + categoryGroups: Object, + configSettings: LH.Config.Settings, + }} ReportJSON */ -ReportRenderer.ReportJSON; // eslint-disable-line no-unused-expressions diff --git a/lighthouse-core/report/html/renderer/report-ui-features.js b/lighthouse-core/report/html/renderer/report-ui-features.js index a560b6dcd80e..2d3c8ee8e22e 100644 --- a/lighthouse-core/report/html/renderer/report-ui-features.js +++ b/lighthouse-core/report/html/renderer/report-ui-features.js @@ -12,40 +12,43 @@ /* globals self URL Blob CustomEvent getFilenamePrefix window */ +/** @typedef {import('./dom.js')} DOM */ +/** @typedef {import('./report-renderer.js').ReportJSON} ReportJSON */ + class ReportUIFeatures { /** - * @param {!DOM} dom + * @param {DOM} dom */ constructor(dom) { - /** @type {!ReportRenderer.ReportJSON} */ + /** @type {ReportJSON} */ this.json; // eslint-disable-line no-unused-expressions - /** @protected {!DOM} */ + /** @type {DOM} */ this._dom = dom; - /** @protected {!Document} */ + /** @type {Document} */ this._document = this._dom.document(); - /** @private {boolean} */ + /** @type {boolean} */ this._copyAttempt = false; - /** @type {!Element} */ + /** @type {HTMLElement} */ this.exportButton; // eslint-disable-line no-unused-expressions - /** @type {!Element} */ + /** @type {HTMLElement} */ this.headerSticky; // eslint-disable-line no-unused-expressions - /** @type {!Element} */ + /** @type {HTMLElement} */ this.headerBackground; // eslint-disable-line no-unused-expressions - /** @type {!Element} */ + /** @type {HTMLElement} */ this.lighthouseIcon; // eslint-disable-line no-unused-expressions - /** @type {!Element} */ + /** @type {HTMLElement} */ this.scoresShadowWrapper; // eslint-disable-line no-unused-expressions - /** @type {!Element} */ + /** @type {HTMLElement} */ this.productInfo; // eslint-disable-line no-unused-expressions - /** @type {!Element} */ + /** @type {HTMLElement} */ this.toolbar; // eslint-disable-line no-unused-expressions - /** @type {!Element} */ + /** @type {HTMLElement} */ this.toolbarMetadata; // eslint-disable-line no-unused-expressions - /** @type {!Element} */ + /** @type {HTMLElement} */ this.env; // eslint-disable-line no-unused-expressions - /** @type {!number} */ + /** @type {number} */ this.headerOverlap = 0; - /** @type {!number} */ + /** @type {number} */ this.headerHeight = 0; /** @type {number} */ this.latestKnownScrollY = 0; @@ -65,7 +68,7 @@ class ReportUIFeatures { /** * Adds export button, print, and other functionality to the report. The method * should be called whenever the report needs to be re-rendered. - * @param {!ReportRenderer.ReportJSON} report + * @param {ReportJSON} report */ initFeatures(report) { this.json = report; @@ -75,17 +78,18 @@ class ReportUIFeatures { this._setupHeaderAnimation(); this._resetUIState(); this._document.addEventListener('keydown', this.printShortCutDetect); + // @ts-ignore - tsc thinks document can't listen for `copy` this._document.addEventListener('copy', this.onCopy); } /** * Fires a custom DOM event on target. * @param {string} name Name of the event. - * @param {!Node=} target DOM node to fire the event on. + * @param {Node=} target DOM node to fire the event on. * @param {*=} detail Custom data to include. */ _fireEventOn(name, target = this._document, detail) { - const event = new CustomEvent(name, detail ? {detail} : null); + const event = new CustomEvent(name, detail ? {detail} : undefined); target.dispatchEvent(event); } @@ -98,7 +102,7 @@ class ReportUIFeatures { /** * Handle media query change events. - * @param {!MediaQueryList} mql + * @param {MediaQueryList} mql */ onMediaQueryChange(mql) { const root = this._dom.find('.lh-root', this._document); @@ -114,9 +118,9 @@ class ReportUIFeatures { } _setupHeaderAnimation() { - /** @type {!Element} **/ const scoresWrapper = this._dom.find('.lh-scores-wrapper', this._document); - this.headerOverlap = /** @type {!number} */ + this.headerOverlap = /** @type {number} */ + // @ts-ignore - TODO: move off CSSOM to support other browsers (scoresWrapper.computedStyleMap().get('margin-top').value); this.headerSticky = this._dom.find('.lh-header-sticky', this._document); @@ -128,6 +132,7 @@ class ReportUIFeatures { this.toolbarMetadata = this._dom.find('.lh-toolbar__metadata', this._document); this.env = this._dom.find('.lh-env', this._document); + // @ts-ignore - TODO: move off CSSOM to support other browsers this.headerHeight = this.headerBackground.computedStyleMap().get('height').value; this._document.addEventListener('scroll', this.onScroll, {passive: true}); @@ -138,7 +143,7 @@ class ReportUIFeatures { /** * Handle copy events. - * @param {!Event} e + * @param {ClipboardEvent} e */ onCopy(e) { // Only handle copy button presses (e.g. ignore the user copying page text). @@ -157,7 +162,6 @@ class ReportUIFeatures { /** * Copies the report JSON to the clipboard (if supported by the browser). - * @suppress {reportUnknownTypes} */ onCopyButtonClick() { this._fireEventOn('lh-analytics', this._document, { @@ -179,7 +183,7 @@ class ReportUIFeatures { }); } } - } catch (/** @type {!Error} */ e) { + } catch (/** @type {Error} */ e) { this._copyAttempt = false; this._fireEventOn('lh-log', this._document, {cmd: 'log', msg: e.message}); } @@ -200,7 +204,7 @@ class ReportUIFeatures { if (toggle.hasAttribute('open')) { toggle.removeAttribute('open'); } else { - toggle.setAttribute('open', true); + toggle.setAttribute('open', 'true'); } } @@ -220,26 +224,30 @@ class ReportUIFeatures { `translate3d(calc(var(--report-content-width) / 2),` + ` calc(-100% - ${animateScrollPercentage * this.headerOverlap * -1}px), 0) scale(${1 - animateScrollPercentage})`; - this.lighthouseIcon.style.opacity = Math.max(0, 1 - animateScrollPercentage); - this.scoresShadowWrapper.style.opacity = 1 - animateScrollPercentage; - const scoresContainer = this.scoresShadowWrapper.parentElement; + this.lighthouseIcon.style.opacity = Math.max(0, 1 - animateScrollPercentage).toString(); + this.scoresShadowWrapper.style.opacity = (1 - animateScrollPercentage).toString(); + const scoresContainer = /** @type {HTMLElement} */ (this.scoresShadowWrapper.parentElement); scoresContainer.style.borderRadius = (1 - animateScrollPercentage) * 8 + 'px'; scoresContainer.style.boxShadow = `0 4px 2px -2px rgba(0, 0, 0, ${animateScrollPercentage * 0.2})`; - const scoreScale = scoresContainer.querySelector('.lh-scorescale'); + const scoreScale = this._dom.find('.lh-scorescale', scoresContainer); scoreScale.style.opacity = `${1 - animateScrollPercentage}`; - const scoreHeader = scoresContainer.querySelector('.lh-scores-header'); + const scoreHeader = this._dom.find('.lh-scores-header', scoresContainer); const delta = 32 * animateScrollPercentage; scoreHeader.style.paddingBottom = `${32 - delta}px`; scoresContainer.style.marginBottom = `${delta}px`; this.toolbar.style.transform = `translateY(${headerTransitionHeightDiff * animateScrollPercentage}px)`; - this.exportButton.parentElement.style.transform = `translateY(${headerTransitionHeightDiff * - animateScrollPercentage}px)`; this.exportButton.style.transform = `scale(${1 - 0.2 * animateScrollPercentage})`; + // fix stacking context + const exportParent = this.exportButton.parentElement; + if (exportParent) { + exportParent.style.transform = `translateY(${headerTransitionHeightDiff * + animateScrollPercentage}px)`; + } // start showing the productinfo when we are at the 50% mark of our animation - this.productInfo.style.opacity = this.toolbarMetadata.style.opacity = - animateScrollPercentage < 0.5 ? 0 : (animateScrollPercentage - 0.5) * 2; + const opacity = animateScrollPercentage < 0.5 ? 0 : (animateScrollPercentage - 0.5) * 2; + this.productInfo.style.opacity = this.toolbarMetadata.style.opacity = opacity.toString(); this.env.style.transform = `translateY(${Math.max( 0, headerTransitionHeightDiff * animateScrollPercentage - 6 @@ -255,11 +263,11 @@ class ReportUIFeatures { /** * Click handler for export button. - * @param {!Event} e + * @param {Event} e */ onExportButtonClick(e) { e.preventDefault(); - const el = /** @type {!Element} */ (e.target); + const el = /** @type {Element} */ (e.target); el.classList.toggle('active'); this._document.addEventListener('keydown', this.onKeyDown); } @@ -276,14 +284,14 @@ class ReportUIFeatures { /** * Handler for "export as" button. - * @param {!Event} e + * @param {Event} e */ onExport(e) { e.preventDefault(); - const el = /** @type {!Element} */ (e.target); + const el = /** @type {?Element} */ (e.target); - if (!el.hasAttribute('data-action')) { + if (!el || el.hasAttribute('data-action')) { return; } @@ -310,7 +318,7 @@ class ReportUIFeatures { const htmlStr = this.getReportHtml(); try { this._saveFile(new Blob([htmlStr], {type: 'text/html'})); - } catch (/** @type {!Error} */ e) { + } catch (/** @type {Error} */ e) { this._fireEventOn('lh-log', this._document, { cmd: 'error', msg: 'Could not export as HTML. ' + e.message, }); @@ -334,7 +342,7 @@ class ReportUIFeatures { /** * Keydown handler for the document. - * @param {!Event} e + * @param {KeyboardEvent} e */ onKeyDown(e) { if (e.keyCode === 27) { // ESC @@ -345,9 +353,8 @@ class ReportUIFeatures { /** * Opens a new tab to the online viewer and sends the local page's JSON results * to the online viewer using postMessage. - * @param {!ReportRenderer.ReportJSON} reportJson + * @param {ReportJSON} reportJson * @param {string} viewerPath - * @suppress {reportUnknownTypes} * @protected */ static openTabAndSendJsonReport(reportJson, viewerPath) { @@ -357,26 +364,28 @@ class ReportUIFeatures { // load event, however it is cross-domain and won't fire. Instead, listen // for a message from the target app saying "I'm open". const json = reportJson; - window.addEventListener('message', function msgHandler(/** @type {!Event} */ e) { - const messageEvent = /** @type {!MessageEvent<{opened: boolean}>} */ (e); + window.addEventListener('message', function msgHandler(/** @type {Event} */ e) { + const messageEvent = /** @type {MessageEvent} */ (e); if (messageEvent.origin !== VIEWER_ORIGIN) { return; } - if (messageEvent.data.opened) { + if (popup && messageEvent.data.opened) { popup.postMessage({lhresults: json}, VIEWER_ORIGIN); window.removeEventListener('message', msgHandler); } }); // The popup's window.name is keyed by version+url+fetchTime, so we reuse/select tabs correctly - const fetchTime = json.fetchTime || json.generatedTime; + // @ts-ignore - If this is a v2 LHR, use old `generatedTime`. + const fallbackFetchTime = /** @type {string} */ (json.generatedTime); + const fetchTime = json.fetchTime || fallbackFetchTime; const windowName = `${json.lighthouseVersion}-${json.requestedUrl}-${fetchTime}`; - const popup = /** @type {!Window} */ (window.open(`${VIEWER_ORIGIN}${viewerPath}`, windowName)); + const popup = window.open(`${VIEWER_ORIGIN}${viewerPath}`, windowName); } /** * Expands audit details when user prints via keyboard shortcut. - * @param {!Event} e + * @param {KeyboardEvent} e */ printShortCutDetect(e) { if ((e.ctrlKey || e.metaKey) && e.keyCode === 80) { // Ctrl+P @@ -390,7 +399,8 @@ class ReportUIFeatures { * open a `
` element. */ expandAllDetails() { - const details = this._dom.findAll('.lh-categories details', this._document); + const details = /** @type {Array} */ (this._dom.findAll( + '.lh-categories details', this._document)); details.map(detail => detail.open = true); } @@ -399,7 +409,8 @@ class ReportUIFeatures { * open a `
` element. */ collapseAllDetails() { - const details = this._dom.findAll('.lh-categories details', this._document); + const details = /** @type {Array} */ (this._dom.findAll( + '.lh-categories details', this._document)); details.map(detail => detail.open = false); } @@ -412,9 +423,10 @@ class ReportUIFeatures { if ('onbeforeprint' in self) { self.addEventListener('afterprint', this.collapseAllDetails); } else { + const win = /** @type {Window} */ (self); // Note: FF implements both window.onbeforeprint and media listeners. However, // it doesn't matchMedia doesn't fire when matching 'print'. - self.matchMedia('print').addListener(mql => { + win.matchMedia('print').addListener(mql => { if (mql.matches) { this.expandAllDetails(); } else { @@ -444,7 +456,7 @@ class ReportUIFeatures { /** * Downloads a file (blob) using a[download]. - * @param {!Blob|!File} blob The file to save. + * @param {Blob|File} blob The file to save. * @private */ _saveFile(blob) { @@ -456,7 +468,7 @@ class ReportUIFeatures { const ext = blob.type.match('json') ? '.json' : '.html'; const href = URL.createObjectURL(blob); - const a = /** @type {!HTMLAnchorElement} */ (this._dom.createElement('a')); + const a = /** @type {HTMLAnchorElement} */ (this._dom.createElement('a')); a.download = `${filename}${ext}`; a.href = href; this._document.body.appendChild(a); // Firefox requires anchor to be in the DOM. diff --git a/lighthouse-core/report/html/renderer/util.js b/lighthouse-core/report/html/renderer/util.js index bd165d23b356..e9c13398c55a 100644 --- a/lighthouse-core/report/html/renderer/util.js +++ b/lighthouse-core/report/html/renderer/util.js @@ -18,10 +18,6 @@ const RATINGS = { ERROR: {label: 'error'}, }; -/** - * @fileoverview - * @suppress {reportUnknownTypes} see https://github.com/GoogleChrome/lighthouse/pull/4778#issuecomment-373549391 - */ class Util { static get PASS_THRESHOLD() { return PASS_THRESHOLD; @@ -195,9 +191,9 @@ class Util { return 'None'; } - /** @type {!Array} */ + /** @type {Array} */ const parts = []; - const unitLabels = /** @type {!Object} */ ({ + const unitLabels = /** @type {Object} */ ({ d: 60 * 60 * 24, h: 60 * 60, m: 60, @@ -217,8 +213,8 @@ class Util { } /** - * @param {!URL} parsedUrl - * @param {{numPathParts: (number|undefined), preserveQuery: (boolean|undefined), preserveHost: (boolean|undefined)}=} options + * @param {URL} parsedUrl + * @param {{numPathParts?: number, preserveQuery?: boolean, preserveHost?: boolean}=} options * @return {string} */ static getURLDisplayName(parsedUrl, options) { @@ -378,6 +374,5 @@ class Util { if (typeof module !== 'undefined' && module.exports) { module.exports = Util; } else { - // @ts-ignore self.Util = Util; } diff --git a/lighthouse-core/test/results/sample_v2.json b/lighthouse-core/test/results/sample_v2.json index 5625ff0bd0b3..6d058e6e7919 100644 --- a/lighthouse-core/test/results/sample_v2.json +++ b/lighthouse-core/test/results/sample_v2.json @@ -1,6 +1,6 @@ { "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3358.0 Safari/537.36", - "lighthouseVersion": "3.0.0-alpha.1", + "lighthouseVersion": "3.0.0-alpha.2", "fetchTime": "2018-03-13T00:55:45.840Z", "requestedUrl": "http://localhost/dobetterweb/dbw_tester.html", "finalUrl": "http://localhost:10200/dobetterweb/dbw_tester.html", diff --git a/package.json b/package.json index c2955ddcbfd9..c50c711ab064 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,7 @@ "debug": "node --inspect-brk ./lighthouse-cli/index.js", "start": "node ./lighthouse-cli/index.js", - "test": "yarn lint --quiet && yarn unit && yarn type-check && yarn closure && yarn diff:sample-json", + "test": "yarn lint --quiet && yarn unit && yarn type-check && yarn diff:sample-json", "test-extension": "cd lighthouse-extension && yarn test", "test-viewer": "cd lighthouse-viewer && yarn pptr-test", @@ -48,7 +48,6 @@ "coveralls": "cat unit-coverage.lcov | coveralls", "codecov": "codecov -f unit-coverage.lcov -F unit && codecov -f smoke-coverage.lcov -F smoke", - "closure": "cd lighthouse-core && node closure/closure-type-checking.js", "devtools": "bash lighthouse-core/scripts/roll-to-devtools.sh", "compile-devtools": "bash lighthouse-core/scripts/compile-against-devtools.sh", "chrome": "node lighthouse-core/scripts/manual-chrome-launcher.js", @@ -88,7 +87,6 @@ "cz-customizable": "^5.2.0", "eslint": "^4.19.1", "eslint-config-google": "^0.9.1", - "google-closure-compiler": "^20170521.0.0", "gulp": "^3.9.1", "gulp-replace": "^0.5.4", "gulp-util": "^3.0.7", @@ -99,7 +97,7 @@ "postinstall-prepare": "^1.0.1", "puppeteer": "1.4.0", "sinon": "^2.3.5", - "typescript": "2.9.0-dev.20180323", + "typescript": "2.9.1-insiders.20180516", "vscode-chrome-debug-core": "^3.23.8", "zone.js": "^0.7.3" }, diff --git a/tsconfig.json b/tsconfig.json index 9806892ea1b3..db61aa693b0e 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -15,15 +15,18 @@ "diagnostics": true }, "include": [ + // TODO(bckenny): unnecessary workaround until https://github.com/Microsoft/TypeScript/issues/24062 + "node_modules/@types/node/index.d.ts", + "lighthouse-cli/**/*.js", "lighthouse-core/**/*.js", - "./typings/*.d.ts" + "./typings/*.d.ts", + ], "exclude": [ "lighthouse-cli/test/**/*.js", "lighthouse-core/test/**/*.js", "lighthouse-core/closure/**/*.js", "lighthouse-core/third_party/src/**/*.js", - "lighthouse-core/report/html/renderer/**/*.js", ] } diff --git a/typings/artifacts.d.ts b/typings/artifacts.d.ts index ccbe6e204f55..53035b0813b4 100644 --- a/typings/artifacts.d.ts +++ b/typings/artifacts.d.ts @@ -4,8 +4,8 @@ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ -import * as parseManifest from '../lighthouse-core/lib/manifest-parser.js'; -import * as _LanternSimulator from '../lighthouse-core/lib/dependency-graph/simulator/simulator.js'; +import parseManifest = require('../lighthouse-core/lib/manifest-parser.js'); +import _LanternSimulator = require('../lighthouse-core/lib/dependency-graph/simulator/simulator.js'); import speedline = require('speedline'); type LanternSimulator = InstanceType; diff --git a/typings/config.d.ts b/typings/config.d.ts index 2b7a3331f44f..7f4b7b6dd1d6 100644 --- a/typings/config.d.ts +++ b/typings/config.d.ts @@ -4,8 +4,9 @@ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ -import * as Gatherer from '../lighthouse-core/gather/gatherers/gatherer.js'; -import * as Audit from '../lighthouse-core/audits/audit.js'; +import Gatherer = require('../lighthouse-core/gather/gatherers/gatherer.js'); +import Audit = require('../lighthouse-core/audits/audit.js'); + declare global { module LH { diff --git a/typings/gatherer.d.ts b/typings/gatherer.d.ts index ff9827cc37d7..8c4e5a30f9f0 100644 --- a/typings/gatherer.d.ts +++ b/typings/gatherer.d.ts @@ -4,11 +4,11 @@ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ -import * as _Node from '../lighthouse-core/lib/dependency-graph/node'; -import * as _NetworkNode from '../lighthouse-core/lib/dependency-graph/network-node'; -import * as _CPUNode from '../lighthouse-core/lib/dependency-graph/cpu-node'; -import * as _Simulator from '../lighthouse-core/lib/dependency-graph/simulator/simulator'; -import * as Driver from '../lighthouse-core/gather/driver'; +import _Node = require('../lighthouse-core/lib/dependency-graph/node'); +import _NetworkNode = require('../lighthouse-core/lib/dependency-graph/network-node'); +import _CPUNode = require('../lighthouse-core/lib/dependency-graph/cpu-node'); +import _Simulator = require('../lighthouse-core/lib/dependency-graph/simulator/simulator'); +import Driver = require('../lighthouse-core/gather/driver'); declare global { module LH.Gatherer { diff --git a/typings/html-renderer.d.ts b/typings/html-renderer.d.ts new file mode 100644 index 000000000000..a137a660dbfa --- /dev/null +++ b/typings/html-renderer.d.ts @@ -0,0 +1,41 @@ +/** + * @license Copyright 2018 Google Inc. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. + */ + +import _CategoryRenderer = require('../lighthouse-core/report/html/renderer/category-renderer.js'); +import _CriticalRequestChainRenderer = require('../lighthouse-core/report/html/renderer/crc-details-renderer.js'); +import _DetailsRenderer = require('../lighthouse-core/report/html/renderer/details-renderer.js'); +import _DOM = require('../lighthouse-core/report/html/renderer/dom.js'); +import _PerformanceCategoryRenderer = require('../lighthouse-core/report/html/renderer/performance-category-renderer.js'); +import _ReportRenderer = require('../lighthouse-core/report/html/renderer/report-renderer.js'); +import _ReportUIFeatures = require('../lighthouse-core/report/html/renderer/report-ui-features.js'); +import _Util = require('../lighthouse-core/report/html/renderer/util.js'); +import _FileNamer = require('../lighthouse-core/lib/file-namer.js'); + +declare global { + var CategoryRenderer: typeof _CategoryRenderer; + var CriticalRequestChainRenderer: typeof _CriticalRequestChainRenderer; + var DetailsRenderer: typeof _DetailsRenderer; + var DOM: typeof _DOM; + var getFilenamePrefix: typeof _FileNamer.getFilenamePrefix; + var PerformanceCategoryRenderer: typeof _PerformanceCategoryRenderer; + var ReportRenderer: typeof _ReportRenderer; + var ReportUIFeatures: typeof _ReportUIFeatures; + var Util: typeof _Util; + + interface Window { + CategoryRenderer: typeof _CategoryRenderer; + CriticalRequestChainRenderer: typeof _CriticalRequestChainRenderer; + DetailsRenderer: typeof _DetailsRenderer; + DOM: typeof _DOM; + PerformanceCategoryRenderer: typeof _PerformanceCategoryRenderer; + ReportRenderer: typeof _ReportRenderer; + ReportUIFeatures: typeof _ReportUIFeatures; + Util: typeof _Util; + } +} + +// empty export to keep file a module +export {} diff --git a/yarn.lock b/yarn.lock index 883215045c28..60d56fe7bfd6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -925,18 +925,10 @@ cliui@^4.0.0: strip-ansi "^4.0.0" wrap-ansi "^2.0.0" -clone-buffer@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/clone-buffer/-/clone-buffer-1.0.0.tgz#e3e25b207ac4e701af721e2cb5a16792cac3dc58" - clone-stats@^0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/clone-stats/-/clone-stats-0.0.1.tgz#b88f94a82cf38b8791d58046ea4029ad88ca99d1" -clone-stats@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/clone-stats/-/clone-stats-1.0.0.tgz#b3782dff8bb5474e18b9b6bf0fdfe782f8777680" - clone@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/clone/-/clone-0.2.0.tgz#c6126a90ad4f72dbf5acdb243cc37724fe93fc1f" @@ -945,14 +937,6 @@ clone@^1.0.0, clone@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.2.tgz#260b7a99ebb1edfe247538175f783243cb19d149" -cloneable-readable@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/cloneable-readable/-/cloneable-readable-1.0.0.tgz#a6290d413f217a61232f95e458ff38418cfb0117" - dependencies: - inherits "^2.0.1" - process-nextick-args "^1.0.6" - through2 "^2.0.1" - co@^4.6.0: version "4.6.0" resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" @@ -2257,14 +2241,6 @@ glogg@^1.0.0: dependencies: sparkles "^1.0.0" -google-closure-compiler@^20170521.0.0: - version "20170521.0.0" - resolved "https://registry.yarnpkg.com/google-closure-compiler/-/google-closure-compiler-20170521.0.0.tgz#4671caf70bd182e4c041ddb0c95f48f77695baa9" - dependencies: - chalk "^1.0.0" - vinyl "^2.0.1" - vinyl-sourcemaps-apply "^0.2.0" - got@^6.7.1: version "6.7.1" resolved "https://registry.yarnpkg.com/got/-/got-6.7.1.tgz#240cd05785a9a18e561dc1b44b41c763ef1e8db0" @@ -2520,7 +2496,7 @@ inherits@1: version "1.0.2" resolved "https://registry.yarnpkg.com/inherits/-/inherits-1.0.2.tgz#ca4309dadee6b54cc0b8d247e8d7c7a0975bdc9b" -inherits@2, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.1, inherits@~2.0.3: +inherits@2, inherits@^2.0.3, inherits@~2.0.1, inherits@~2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" @@ -4023,7 +3999,7 @@ private@^0.1.7: version "0.1.8" resolved "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff" -process-nextick-args@^1.0.6, process-nextick-args@~1.0.6: +process-nextick-args@~1.0.6: version "1.0.7" resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-1.0.7.tgz#150e20b756590ad3f91093f25a4f2ad8bff30ba3" @@ -4228,10 +4204,6 @@ registry-url@^3.0.3: dependencies: rc "^1.0.1" -remove-trailing-separator@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.0.1.tgz#615ebb96af559552d4bf4057c8436d486ab63cc4" - repeat-element@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.2.tgz#ef089a178d1483baae4d93eb98b4f9e4e11d990a" @@ -4254,10 +4226,6 @@ replace-ext@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/replace-ext/-/replace-ext-0.0.1.tgz#29bbd92078a739f0bcce2b4ee41e837953522924" -replace-ext@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/replace-ext/-/replace-ext-1.0.0.tgz#de63128373fcbf7c3ccfa4de5a480c45a67958eb" - replacestream@^4.0.0: version "4.0.2" resolved "https://registry.yarnpkg.com/replacestream/-/replacestream-4.0.2.tgz#0c4140707e4f0323f50de044851708cf58bc37bd" @@ -4601,7 +4569,7 @@ source-map@^0.4.4: dependencies: amdefine ">=0.0.4" -source-map@^0.5.1, source-map@^0.5.3, source-map@~0.5.1: +source-map@^0.5.3, source-map@~0.5.1: version "0.5.6" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.6.tgz#75ce38f52bf0733c5a7f0c118d81334a2bb5f412" @@ -4891,7 +4859,7 @@ through2@^0.6.1: readable-stream ">=1.0.33-1 <1.1.0-0" xtend ">=4.0.0 <4.1.0-0" -through2@^2.0.0, through2@^2.0.1: +through2@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.1.tgz#384e75314d49f32de12eebb8136b8eb6b5d59da9" dependencies: @@ -5013,9 +4981,9 @@ typedarray@^0.0.6: version "0.0.6" resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" -typescript@2.9.0-dev.20180323: - version "2.9.0-dev.20180323" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.9.0-dev.20180323.tgz#9b33b1366a2b9af88e5e4de9a8e502f1df8b5aad" +typescript@2.9.1-insiders.20180516: + version "2.9.1-insiders.20180516" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.9.1-insiders.20180516.tgz#aab5261edb2c162c2d0c1754bb3092d4ff6efed0" uglify-js@^2.6: version "2.7.3" @@ -5153,12 +5121,6 @@ vinyl-fs@^0.3.0: through2 "^0.6.1" vinyl "^0.4.0" -vinyl-sourcemaps-apply@^0.2.0: - version "0.2.1" - resolved "https://registry.yarnpkg.com/vinyl-sourcemaps-apply/-/vinyl-sourcemaps-apply-0.2.1.tgz#ab6549d61d172c2b1b87be5c508d239c8ef87705" - dependencies: - source-map "^0.5.1" - vinyl@^0.4.0: version "0.4.6" resolved "https://registry.yarnpkg.com/vinyl/-/vinyl-0.4.6.tgz#2f356c87a550a255461f36bbeb2a5ba8bf784847" @@ -5174,18 +5136,6 @@ vinyl@^0.5.0: clone-stats "^0.0.1" replace-ext "0.0.1" -vinyl@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/vinyl/-/vinyl-2.0.1.tgz#1c3b4931e7ac4c1efee743f3b91a74c094407bb6" - dependencies: - clone "^1.0.0" - clone-buffer "^1.0.0" - clone-stats "^1.0.0" - cloneable-readable "^1.0.0" - is-stream "^1.1.0" - remove-trailing-separator "^1.0.1" - replace-ext "^1.0.0" - vscode-chrome-debug-core@^3.23.8: version "3.23.8" resolved "https://registry.yarnpkg.com/vscode-chrome-debug-core/-/vscode-chrome-debug-core-3.23.8.tgz#f0fd1582b6d7653d327171104b9c8e2eebb6bf41"