extension(tsc): add type checking to extension entry points (#5346)

This commit is contained in:
Brendan Kenny 2018-05-29 14:05:24 -07:00 коммит произвёл Patrick Hulce
Родитель af7127715a
Коммит 92728c9725
14 изменённых файлов: 276 добавлений и 352 удалений

Просмотреть файл

@ -109,33 +109,3 @@ yarn deploy-viewer
# * Tell the world!!! *
echo "Complete the _Release publicity_ tasks documented above"
```
### Extension Canary release
```sh
# Pull latest in a clean non-dev clone.
yarn install-all
# Update manifest_canary.json w/ version bumps.
# branch and commit
git commmit -m "bump extension canary to 2.0.0.X"
npm version prerelease # this will commit
# overwrite extension's manifest w/ manifest_canary.
yarn build-all
cd lighthouse-extension/
gulp package
# upload zip to CWS and publish
# verify you build-all'd for the typescript compile
# ...
# publish to canary tag!
npm publish --tag canary
```

Просмотреть файл

@ -160,8 +160,13 @@ function runLighthouse(url, flags, config) {
const resultsP = chromeP.then(_ => {
return lighthouse(url, flags, config).then(runnerResult => {
return potentiallyKillChrome().then(_ => runnerResult);
}).then(runnerResult => {
return saveResults(runnerResult, flags).then(_ => runnerResult);
}).then(async runnerResult => {
// If in gatherMode only, there will be no runnerResult.
if (runnerResult) {
await saveResults(runnerResult, flags);
}
return runnerResult;
});
});

Просмотреть файл

@ -10,22 +10,11 @@ const Connection = require('./connection.js');
/* eslint-disable no-unused-vars */
/**
* @interface
* @typedef {object} Port
* @property {(eventName: 'message'|'close', cb: ((arg: string) => void) | (() => void)) => void} on
* @property {(message: string) => void} send
* @property {() => void} close
*/
class Port {
/**
* @param {'message' | 'close'} eventName
* @param {function(string)|function()} cb
*/
on(eventName, cb) { }
/**
* @param {string} message
*/
send(message) { }
close() { }
}
/* eslint-enable no-unused-vars */

Просмотреть файл

@ -3,7 +3,6 @@
* 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.
*/
// @ts-nocheck
'use strict';
const Runner = require('./runner');
@ -28,23 +27,25 @@ const Config = require('./config/config');
/**
* @param {string} url
* @param {LH.Flags} flags
* @param {LH.Flags=} flags
* @param {LH.Config.Json|undefined} configJSON
* @return {Promise<LH.RunnerResult>}
* @return {Promise<LH.RunnerResult|undefined>}
*/
function lighthouse(url, flags = {}, configJSON) {
return Promise.resolve().then(_ => {
// set logging preferences, assume quiet
flags.logLevel = flags.logLevel || 'error';
log.setLevel(flags.logLevel);
async function lighthouse(url, flags, configJSON) {
// TODO(bckenny): figure out Flags types.
flags = flags || /** @type {LH.Flags} */ ({});
// Use ConfigParser to generate a valid config file
const config = new Config(configJSON, flags);
const connection = new ChromeProtocol(flags.port, flags.hostname);
// set logging preferences, assume quiet
flags.logLevel = flags.logLevel || 'error';
log.setLevel(flags.logLevel);
// kick off a lighthouse run
return Runner.run(connection, {url, config});
});
// Use ConfigParser to generate a valid config file
// @ts-ignore - TODO(bckenny): type checking for Config
const config = /** @type {LH.Config} */ (new Config(configJSON, flags));
const connection = new ChromeProtocol(flags.port, flags.hostname);
// kick off a lighthouse run
return Runner.run(connection, {url, config});
}
lighthouse.getAuditList = Runner.getAuditList;

Просмотреть файл

@ -11,7 +11,6 @@
"default_locale": "en",
"background": {
"scripts": [
"scripts/chromereload.js",
"scripts/lighthouse-ext-background.js"
],
"persistent": false

Просмотреть файл

@ -1,33 +0,0 @@
{
"name": "__MSG_appName__",
"version": "2.0.0.7",
"version_name": "2.0.0-alpha.7 2017-05-16",
"minimum_chrome_version": "56",
"manifest_version": 2,
"description": "__MSG_appDescription__",
"icons": {
"16": "images/lh_logo_canary_bg.png",
"128": "images/lh_logo_canary_bg.png"
},
"default_locale": "en",
"background": {
"scripts": [
"scripts/chromereload.js",
"scripts/lighthouse-ext-background.js"
],
"persistent": false
},
"permissions": [
"activeTab",
"debugger",
"storage"
],
"browser_action": {
"default_icon": {
"38": "images/lh_logo_canary_icon.png"
},
"default_title": "Lighthouse",
"default_popup": "popup.html"
},
"content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'none'"
}

Просмотреть файл

@ -56,12 +56,6 @@ Unless required by applicable law or agreed to in writing, software distributed
<aside class="options subpage">
<h2 class="options__title" hidden>Settings</h2>
<div hidden>
<label>
<input type="checkbox" class="setting-disable-extensions" disabled
value="Disable other extensions while Lighthouse audits a page">Disable other extensions while Lighthouse audits a page.
</label>
</div>
<h2 class="options__title">Audit categories to include</h2>
<ul class="options__list">

Просмотреть файл

@ -1,29 +0,0 @@
/**
* @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';
/* eslint-disable no-console */
// Reload client for Chrome Apps & Extensions.
// The reload client has a compatibility with livereload.
// WARNING: only supports reload command.
const LIVERELOAD_HOST = 'localhost:';
const LIVERELOAD_PORT = 35729;
const connection = new WebSocket('ws://' + LIVERELOAD_HOST + LIVERELOAD_PORT + '/livereload');
connection.onerror = error => {
console.log('reload connection got error:', error);
};
connection.onmessage = e => {
if (e.data) {
const data = JSON.parse(e.data);
if (data && data.command === 'reload') {
chrome.runtime.reload();
}
}
};

Просмотреть файл

@ -11,20 +11,24 @@ const Config = require('../../../lighthouse-core/config/config');
const defaultConfig = require('../../../lighthouse-core/config/default-config.js');
const log = require('lighthouse-logger');
/** @typedef {import('../../../lighthouse-core/gather/connections/connection.js')} Connection */
/**
* @param {!Connection} connection
* @param {Connection} connection
* @param {string} url
* @param {!Object} options Lighthouse options.
* @param {!Array<string>} categoryIDs Name values of categories to include.
* @return {!Promise}
* @param {{flags: LH.Flags}} options Lighthouse options.
* @param {Array<string>} categoryIDs Name values of categories to include.
* @param {(url?: string) => void} updateBadgeFn
* @return {Promise<LH.RunnerResult|void>}
*/
window.runLighthouseForConnection = function(
connection, url, options, categoryIDs,
updateBadgeFn = function() { }) {
const config = new Config({
function runLighthouseForConnection(
connection, url, options, categoryIDs,
updateBadgeFn = function() { }) {
// @ts-ignore - TODO(bckenny): type checking for Config
const config = /** @type {LH.Config} */ (new Config({
extends: 'lighthouse:default',
settings: {onlyCategories: categoryIDs},
}, options.flags);
}, options.flags));
// Add url and config to fresh options object.
const runOptions = Object.assign({}, options, {url, config});
@ -39,30 +43,47 @@ window.runLighthouseForConnection = function(
updateBadgeFn();
throw err;
});
};
}
/**
* @param {!RawProtocol.Port} port
* @param {RawProtocol.Port} port
* @param {string} url
* @param {!Object} options Lighthouse options.
* @param {!Array<string>} categoryIDs Name values of categories to include.
* @return {!Promise}
* @param {{flags: LH.Flags}} options Lighthouse options.
* @param {Array<string>} categoryIDs Name values of categories to include.
* @return {Promise<LH.RunnerResult|void>}
*/
window.runLighthouseInWorker = function(port, url, options, categoryIDs) {
function runLighthouseInWorker(port, url, options, categoryIDs) {
// Default to 'info' logging level.
log.setLevel('info');
const connection = new RawProtocol(port);
return window.runLighthouseForConnection(connection, url, options, categoryIDs);
};
return runLighthouseForConnection(connection, url, options, categoryIDs);
}
/**
* Returns list of top-level categories from the default config.
* @return {!Array<{title: string, id: string}>}
* @return {Array<{title: string, id: string}>}
*/
window.getDefaultCategories = function() {
function getDefaultCategories() {
return Config.getCategories(defaultConfig);
};
}
window.listenForStatus = function(listenCallback) {
/** @param {(status: [string, string, string]) => void} listenCallback */
function listenForStatus(listenCallback) {
log.events.addListener('status', listenCallback);
};
}
if (typeof module !== 'undefined' && module.exports) {
// export for lighthouse-ext-background to require (via browserify).
module.exports = {
runLighthouseForConnection,
runLighthouseInWorker,
getDefaultCategories,
listenForStatus,
};
} else {
// If not require()d, expose on window for devtools, other consumers of file.
// @ts-ignore
window.runLighthouseInWorker = runLighthouseInWorker;
// @ts-ignore
window.listenForStatus = listenForStatus;
}

Просмотреть файл

@ -5,46 +5,20 @@
*/
'use strict';
require('./lighthouse-background');
const background = require('./lighthouse-background');
const ExtensionProtocol = require('../../../lighthouse-core/gather/connections/extension');
const log = require('lighthouse-logger');
const assetSaver = require('../../../lighthouse-core/lib/asset-saver.js');
/** @typedef {import('../../../lighthouse-core/gather/connections/connection.js')} Connection */
const STORAGE_KEY = 'lighthouse_audits';
const SETTINGS_KEY = 'lighthouse_settings';
// let installedExtensions = [];
let disableExtensionsDuringRun = false;
let lighthouseIsRunning = false;
let latestStatusLog = [];
// /**
// * Enables or disables all other installed chrome extensions. The initial list
// * of the user's extension is created when the background page is started.
// * @param {!boolean} enable If true, enables all other installed extensions.
// * False disables them.
// * @param {!Promise}
// */
// function enableOtherChromeExtensions(enable) {
// if (!disableExtensionsDuringRun) {
// return Promise.resolve();
// }
// const str = enable ? 'enabling' : 'disabling';
// log.log('Chrome', `${str} ${installedExtensions.length} extensions.`);
// return Promise.all(installedExtensions.map(info => {
// return new Promise((resolve, reject) => {
// chrome.management.setEnabled(info.id, enable, _ => {
// if (chrome.runtime.lastError) {
// reject(chrome.runtime.lastError);
// }
// resolve();
// });
// });
// }));
// }
/** @type {?[string, string, string]} */
let latestStatusLog = null;
/**
* Sets the extension badge text.
@ -53,11 +27,14 @@ let latestStatusLog = [];
*/
function updateBadgeUI(optUrl) {
lighthouseIsRunning = !!optUrl;
if (window.chrome && chrome.runtime) {
if ('chrome' in window && chrome.runtime) {
const manifest = chrome.runtime.getManifest();
if (!manifest.browser_action || !manifest.browser_action.default_icon) {
return;
}
let title = manifest.browser_action.default_title;
let path = manifest.browser_action.default_icon['38'];
let title = manifest.browser_action.default_title || '';
let path = manifest.browser_action.default_icon[38];
if (lighthouseIsRunning) {
title = `Testing ${optUrl}`;
@ -70,109 +47,95 @@ function updateBadgeUI(optUrl) {
}
/**
* @param {!Object} options Lighthouse options.
* @param {!Array<string>} categoryIDs Name values of categories to include.
* @return {!Promise}
* @param {{flags: LH.Flags}} options Lighthouse options.
* @param {Array<string>} categoryIDs Name values of categories to include.
* @return {Promise<LH.RunnerResult|void>}
*/
window.runLighthouseInExtension = function(options, categoryIDs) {
async function runLighthouseInExtension(options, categoryIDs) {
// Default to 'info' logging level.
log.setLevel('info');
const connection = new ExtensionProtocol();
options.flags = Object.assign({}, options.flags, {output: 'html'});
// return enableOtherChromeExtensions(false)
// .then(_ => connection.getCurrentTabURL())
return connection.getCurrentTabURL()
.then(url => window.runLighthouseForConnection(connection, url, options,
categoryIDs, updateBadgeUI))
.then(runnerResult => {
// return enableOtherChromeExtensions(true).then(_ => {
const blobURL = window.createReportPageAsBlob(runnerResult, 'extension');
return new Promise(resolve => chrome.windows.create({url: blobURL}, resolve));
// });
}).catch(err => {
// return enableOtherChromeExtensions(true).then(_ => {
throw err;
// });
});
};
const url = await connection.getCurrentTabURL();
const runnerResult = await background.runLighthouseForConnection(connection, url, options,
categoryIDs, updateBadgeUI);
if (!runnerResult) {
// For now, should always be a runnerResult as the extension can't do `gatherMode`
throw new Error('no runnerResult generated by Lighthouse');
}
const blobURL = createReportPageAsBlob(runnerResult);
await new Promise(resolve => chrome.windows.create({url: blobURL}, resolve));
}
/**
* Run lighthouse for connection and provide similar results as in CLI.
* @param {!Connection} connection
* @param {Connection} connection
* @param {string} url
* @param {!Object} options Lighthouse options.
* @param {{flags: LH.Flags} & {outputFormat: string, logAssets: boolean}} options Lighthouse options.
Specify outputFormat to change the output format.
* @param {!Array<string>} categoryIDs Name values of categories to include.
* @return {!Promise}
* @param {Array<string>} categoryIDs Name values of categories to include.
* @return {Promise<string|Array<string>|void>}
*/
window.runLighthouseAsInCLI = function(connection, url, options, categoryIDs) {
async function runLighthouseAsInCLI(connection, url, options, categoryIDs) {
log.setLevel('info');
const startTime = Date.now();
options.flags = Object.assign({}, options.flags, {output: options.outputFormat});
return window.runLighthouseForConnection(connection, url, options, categoryIDs)
.then(results => {
const endTime = Date.now();
results.timing = {total: endTime - startTime};
let promise = Promise.resolve();
if (options && options.logAssets) {
promise = promise.then(_ => assetSaver.logAssets(results.artifacts, results.lhr.audits));
}
return promise.then( _ => {
return results.report;
});
});
};
const results = await background.runLighthouseForConnection(connection, url, options,
categoryIDs);
if (results) {
if (options && options.logAssets) {
await assetSaver.logAssets(results.artifacts, results.lhr.audits);
}
return results.report;
}
}
/**
* @param {LH.RunnerResult} runnerResult Lighthouse results object
* @param {!string} reportContext Where the report is going
* @return {!string} Blob URL of the report (or error page) HTML
* @return {string} Blob URL of the report (or error page) HTML
*/
window.createReportPageAsBlob = function(runnerResult) {
function createReportPageAsBlob(runnerResult) {
performance.mark('report-start');
const html = runnerResult.report;
const blob = new Blob([html], {type: 'text/html'});
const blobURL = window.URL.createObjectURL(blob);
const blobURL = URL.createObjectURL(blob);
performance.mark('report-end');
performance.measure('generate report', 'report-start', 'report-end');
return blobURL;
};
}
/**
* Save currently selected set of category categories to local storage.
* @param {{selectedCategories: !Array<string>, disableExtensions: boolean, useDevTools: boolean}} settings
* @param {{selectedCategories: Array<string>, useDevTools: boolean}} settings
*/
window.saveSettings = function(settings) {
function saveSettings(settings) {
const storage = {
[STORAGE_KEY]: {},
[SETTINGS_KEY]: {},
};
// Stash selected categories.
window.getDefaultCategories().forEach(category => {
background.getDefaultCategories().forEach(category => {
storage[STORAGE_KEY][category.id] = settings.selectedCategories.includes(category.id);
});
// Stash disable extensions setting.
disableExtensionsDuringRun = settings.disableExtensions;
storage[SETTINGS_KEY].disableExtensions = disableExtensionsDuringRun;
// Stash throttling setting.
storage[SETTINGS_KEY].useDevTools = settings.useDevTools;
// Save object to chrome local storage.
chrome.storage.local.set(storage);
};
}
/**
* Load selected category categories from local storage.
* @return {!Promise<{selectedCategories: !Array<string>, disableExtensions: boolean, useDevTools: boolean}>}
* @return {Promise<{selectedCategories: Array<string>, useDevTools: boolean}>}
*/
window.loadSettings = function() {
function loadSettings() {
return new Promise(resolve => {
// Protip: debug what's in storage with:
// chrome.storage.local.get(['lighthouse_audits'], console.log)
@ -180,7 +143,7 @@ window.loadSettings = function() {
// Start with list of all default categories set to true so list is
// always up to date.
const defaultCategories = {};
window.getDefaultCategories().forEach(category => {
background.getDefaultCategories().forEach(category => {
defaultCategories[category.id] = true;
});
@ -190,49 +153,37 @@ window.loadSettings = function() {
const defaultSettings = {
useDevTools: false,
disableExtensions: disableExtensionsDuringRun,
};
const savedSettings = Object.assign(defaultSettings, result[SETTINGS_KEY]);
resolve({
useDevTools: savedSettings.useDevTools,
useDevTools: !!savedSettings.useDevTools,
selectedCategories: Object.keys(savedCategories).filter(cat => savedCategories[cat]),
disableExtensions: savedSettings.disableExtensions,
});
});
});
};
}
window.listenForStatus = function(callback) {
/** @param {(status: [string, string, string]) => void} listenCallback */
function listenForStatus(listenCallback) {
log.events.addListener('status', function(log) {
latestStatusLog = log;
callback(log);
listenCallback(log);
});
// Show latest saved status log to give immediate feedback
// when reopening the popup message when lighthouse is running
if (lighthouseIsRunning && latestStatusLog) {
callback(latestStatusLog);
listenCallback(latestStatusLog);
}
};
}
window.isRunning = function() {
function isRunning() {
return lighthouseIsRunning;
};
}
// Run when in extension context, but not in devtools.
if (window.chrome && chrome.runtime) {
// Get list of installed extensions that are enabled and can be disabled.
// Extensions are not allowed to be disabled if they are under an admin policy.
// chrome.management.getAll(installs => {
// chrome.management.getSelf(lighthouseCrxInfo => {
// installedExtensions = installs.filter(info => {
// return info.id !== lighthouseCrxInfo.id && info.type === 'extension' &&
// info.enabled && info.mayDisable;
// });
// });
// });
if ('chrome' in window && chrome.runtime) {
chrome.runtime.onInstalled.addListener(details => {
if (details.previousVersion) {
// eslint-disable-next-line no-console
@ -240,3 +191,30 @@ if (window.chrome && chrome.runtime) {
}
});
}
if (typeof module !== 'undefined' && module.exports) {
// Export for popup.js to import types. We don't want tsc to infer an index
// type, so use exports instead of module.exports.
exports.runLighthouseInExtension = runLighthouseInExtension;
exports.getDefaultCategories = background.getDefaultCategories;
exports.isRunning = isRunning;
exports.listenForStatus = listenForStatus;
exports.saveSettings = saveSettings;
exports.loadSettings = loadSettings;
}
// Expose on window for extension, other consumers of file.
// @ts-ignore
window.runLighthouseInExtension = runLighthouseInExtension;
// @ts-ignore
window.runLighthouseAsInCLI = runLighthouseAsInCLI;
// @ts-ignore
window.getDefaultCategories = background.getDefaultCategories;
// @ts-ignore
window.isRunning = isRunning;
// @ts-ignore
window.listenForStatus = listenForStatus;
// @ts-ignore
window.loadSettings = loadSettings;
// @ts-ignore
window.saveSettings = saveSettings;

Просмотреть файл

@ -5,6 +5,8 @@
*/
'use strict';
/** @typedef {typeof import('./lighthouse-ext-background.js') & {console: typeof console}} BackgroundPage */
/**
* Error strings that indicate a problem in how Lighthouse was run, not in
* Lighthouse itself, mapped to more useful strings to report to the user.
@ -30,10 +32,7 @@ const MAX_ISSUE_ERROR_LENGTH = 60;
const subpageVisibleClass = 'subpage--visible';
const getBackgroundPage = new Promise((resolve, reject) => {
chrome.runtime.getBackgroundPage(resolve);
});
/** @type {?URL} */
let siteURL = null;
function getLighthouseVersion() {
@ -45,17 +44,38 @@ function getLighthouseCommitHash() {
}
function getChromeVersion() {
// @ts-ignore
return /Chrome\/([0-9.]+)/.exec(navigator.userAgent)[1];
}
function showRunningSubpage() {
document.querySelector('.status').classList.add(subpageVisibleClass);
find('.status').classList.add(subpageVisibleClass);
}
function hideRunningSubpage() {
document.querySelector('.status').classList.remove(subpageVisibleClass);
find('.status').classList.remove(subpageVisibleClass);
}
/**
* Guaranteed context.querySelector. Always returns an element or throws if
* nothing matches query.
* @param {string} query
* @param {ParentNode=} context
* @return {HTMLElement}
*/
function find(query, context = document) {
/** @type {?HTMLElement} */
const result = context.querySelector(query);
if (result === null) {
throw new Error(`query ${query} not found`);
}
return result;
}
/**
* @param {Error} err
* @return {HTMLAnchorElement}
*/
function buildReportErrorLink(err) {
const issueBody = `
**Lighthouse Version**: ${getLighthouseVersion()}
@ -77,24 +97,33 @@ ${err.stack}
const reportErrorEl = document.createElement('a');
reportErrorEl.className = 'button button--report-error';
reportErrorEl.href = url;
reportErrorEl.href = url.href;
reportErrorEl.textContent = 'Report Error';
reportErrorEl.target = '_blank';
return reportErrorEl;
}
/**
* @param {[string, string, string]} status
*/
function logStatus([, message, details]) {
if (typeof details === 'string' && details.length > 110) {
// Grab 100 characters and up to the next comma, ellipsis for the rest
const hundredPlusChars = details.replace(/(.{100}.*?),.*/, '$1…');
details = hundredPlusChars;
}
document.querySelector('.status__msg').textContent = message;
const statusDetailsMessageEl = document.querySelector('.status__detailsmsg');
find('.status__msg').textContent = message;
const statusDetailsMessageEl = find('.status__detailsmsg');
statusDetailsMessageEl.textContent = details;
}
/**
* @param {string} text
* @param {string} id
* @param {boolean} isChecked
* @return {HTMLLIElement}
*/
function createOptionItem(text, id, isChecked) {
const input = document.createElement('input');
input.setAttribute('type', 'checkbox');
@ -114,22 +143,21 @@ function createOptionItem(text, id, isChecked) {
/**
* Click event handler for Generate Report button.
* @param {!Window} background Reference to the extension's background page.
* @param {{selectedCategories: !Object<boolean>, useDevTools: boolean}} settings
* @param {BackgroundPage} background Reference to the extension's background page.
* @param {{selectedCategories: Array<string>, useDevTools: boolean}} settings
*/
async function onGenerateReportButtonClick(background, settings) {
showRunningSubpage();
const feedbackEl = document.querySelector('.feedback');
const feedbackEl = find('.feedback');
feedbackEl.textContent = '';
const {selectedCategories, useDevTools} = settings;
// TODO(bckenny): make flags workable as a type.
const flags = /** @type {LH.Flags} */ ({throttlingMethod: useDevTools ? 'devtools' : 'simulate'});
try {
await background.runLighthouseInExtension({
restoreCleanState: true,
flags: {throttlingMethod: useDevTools ? 'devtools' : 'simulate'},
}, selectedCategories);
await background.runLighthouseInExtension({flags}, selectedCategories);
// Close popup once report is opened in a new tab
window.close();
@ -162,8 +190,8 @@ async function onGenerateReportButtonClick(background, settings) {
/**
* Generates a document fragment containing a list of checkboxes and labels
* for the categories.
* @param {!Window} background Reference to the extension's background page.
* @param {!Array<string>} selectedCategories
* @param {BackgroundPage} background Reference to the extension's background page.
* @param {Array<string>} selectedCategories
*/
function generateOptionsList(background, selectedCategories) {
const frag = document.createDocumentFragment();
@ -173,85 +201,88 @@ function generateOptionsList(background, selectedCategories) {
frag.appendChild(createOptionItem(category.title, category.id, isChecked));
});
const optionsList = document.querySelector('.options__list');
const optionsList = find('.options__list');
optionsList.appendChild(frag);
}
/**
* Initializes the popup's state and UI elements.
* @param {!Window} background Reference to the extension's background page.
*/
function initPopup() {
getBackgroundPage.then(background => {
// To prevent visual hiccups when opening the popup, we default the subpage
// to the "running" view and switch to the default view once we're sure
// Lighthouse is not already auditing the page. This change was necessary
// now that fetching the background event page is async.
if (background.isRunning()) {
showRunningSubpage();
} else {
hideRunningSubpage();
}
background.listenForStatus(logStatus);
// generate checkboxes from saved settings
background.loadSettings().then(settings => {
generateOptionsList(background, settings.selectedCategories);
document.querySelector('.setting-disable-extensions').checked = settings.disableExtensions;
document.querySelector('#lantern-checkbox').checked = !settings.useDevTools;
});
// bind throttling control button
const lanternCheckbox = document.getElementById('lantern-checkbox');
lanternCheckbox.addEventListener('change', async () => {
const settings = await background.loadSettings();
settings.useDevTools = !lanternCheckbox.checked;
background.saveSettings(settings);
});
// bind Generate Report button
const generateReportButton = document.getElementById('generate-report');
generateReportButton.addEventListener('click', () => {
background.loadSettings().then(settings => {
onGenerateReportButtonClick(background, settings);
});
});
// bind View Options button
const generateOptionsEl = document.getElementById('configure-options');
const optionsEl = document.querySelector('.options');
generateOptionsEl.addEventListener('click', () => {
optionsEl.classList.add(subpageVisibleClass);
});
// bind Save Options button
const okButton = document.getElementById('ok');
okButton.addEventListener('click', () => {
// Save settings when options page is closed.
const selectedCategories = Array.from(optionsEl.querySelectorAll(':checked'))
.map(input => input.value);
const disableExtensions = document.querySelector('.setting-disable-extensions').checked;
background.saveSettings({
useDevTools: !lanternCheckbox.checked,
selectedCategories,
disableExtensions,
});
optionsEl.classList.remove(subpageVisibleClass);
});
});
async function initPopup() {
chrome.tabs.query({active: true, lastFocusedWindow: true}, function(tabs) {
if (tabs.length === 0) {
return;
}
siteURL = new URL(tabs[0].url);
siteURL = new URL(tabs[0].url || '');
// Show the user what URL is going to be tested.
document.querySelector('header h2').textContent = siteURL.origin;
find('header h2').textContent = siteURL.origin;
});
/**
* Really the Window of the background page, but since we only want what's exposed
* on window in lighthouse-ext-background.js, use its module API as the type.
* @type {BackgroundPage}
*/
const background = await new Promise(resolve => chrome.runtime.getBackgroundPage(resolve));
// To prevent visual hiccups when opening the popup, we default the subpage
// to the "running" view and switch to the default view once we're sure
// Lighthouse is not already auditing the page. This change was necessary
// now that fetching the background event page is async.
if (background.isRunning()) {
showRunningSubpage();
} else {
hideRunningSubpage();
}
background.listenForStatus(logStatus);
// generate checkboxes from saved settings
background.loadSettings().then(settings => {
generateOptionsList(background, settings.selectedCategories);
const lanternCheck = /** @type {HTMLInputElement} */ (find('#lantern-checkbox'));
lanternCheck.checked = !settings.useDevTools;
});
// bind throttling control button
const lanternCheckbox = /** @type {HTMLInputElement} */ (find('lantern-checkbox'));
lanternCheckbox.addEventListener('change', async () => {
const settings = await background.loadSettings();
settings.useDevTools = !lanternCheckbox.checked;
background.saveSettings(settings);
});
// bind Generate Report button
const generateReportButton = find('generate-report');
generateReportButton.addEventListener('click', () => {
background.loadSettings().then(settings => {
onGenerateReportButtonClick(background, settings);
});
});
// bind View Options button
const generateOptionsEl = find('configure-options');
const optionsEl = find('.options');
generateOptionsEl.addEventListener('click', () => {
optionsEl.classList.add(subpageVisibleClass);
});
// bind Save Options button
const okButton = find('ok');
okButton.addEventListener('click', () => {
// Save settings when options page is closed.
const checkboxes = /** @type {NodeListOf<HTMLInputElement>} */
(optionsEl.querySelectorAll(':checked'));
const selectedCategories = Array.from(checkboxes).map(input => input.value);
background.saveSettings({
useDevTools: !lanternCheckbox.checked,
selectedCategories,
});
optionsEl.classList.remove(subpageVisibleClass);
});
}

Просмотреть файл

@ -83,9 +83,6 @@ gulp.task('chromeManifest', () => {
buildnumber: false,
background: {
target: 'scripts/lighthouse-ext-background.js',
exclude: [
'scripts/chromereload.js',
],
},
};
return gulp.src('app/manifest.json')
@ -111,7 +108,7 @@ gulp.task('browserify-lighthouse', () => {
let bundle = browserify(file.path); // , {debug: true}); // for sourcemaps
bundle = applyBrowserifyTransforms(bundle);
// lighthouse-background will need some additional transforms, ignores and requires…
// scripts will need some additional transforms, ignores and requires…
// Do the additional transform to convert references of devtools-timeline-model
// to the modified version internal to Lighthouse.
@ -157,7 +154,6 @@ gulp.task('browserify-lighthouse', () => {
gulp.task('browserify-other', () => {
return gulp.src([
'app/src/popup.js',
'app/src/chromereload.js',
], {read: false})
.pipe(tap(file => {
let bundle = browserify(file.path); // , {debug: true}); // for sourcemaps

Просмотреть файл

@ -20,6 +20,7 @@
"lighthouse-cli/**/*.js",
"lighthouse-core/**/*.js",
"lighthouse-extension/app/src/*.js",
"./typings/*.d.ts",
],
"exclude": [

1
typings/lighthouse-logger/index.d.ts поставляемый
Просмотреть файл

@ -12,4 +12,5 @@ declare module 'lighthouse-logger' {
export function error(title: string, ...args: any[]): void;
export function verbose(title: string, ...args: any[]): void;
export function reset(): string;
export var events: import('events').EventEmitter;
}