diff --git a/.gitignore b/.gitignore
index 3e1a0ede2223..4ac181580954 100644
--- a/.gitignore
+++ b/.gitignore
@@ -20,3 +20,4 @@ closure-error.log
typings
lighthouse-cli/out
+outagain
diff --git a/jsconfig.json b/jsconfig.json
deleted file mode 100644
index d62db7ce4c1f..000000000000
--- a/jsconfig.json
+++ /dev/null
@@ -1,20 +0,0 @@
-{
- "//": "to run: tsc -p ./jsconfig.json ",
-
- "//": "good docs here: https://github.com/Microsoft/vscode-docs/blob/vnext/docs/languages/javascript.md#javascript-projects-jsconfigjson ",
-
- "compilerOptions": {
- "target": "ES6",
- "outDir": "./dist",
- "diagnostics": true
- },
- "exclude": [
- "node_modules",
- "lighthouse-extension",
- "lighthouse-core/closure",
-
- "third_party",
- "lighthouse-core/third_party",
- "dist"
- ]
-}
diff --git a/lighthouse-cli/ask.ts b/lighthouse-cli/ask.ts
deleted file mode 100644
index 439277f8a1cb..000000000000
--- a/lighthouse-cli/ask.ts
+++ /dev/null
@@ -1,49 +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';
-
-const readline = require('readline');
-
-function ask(question: string, options: string[]): Promise {
- return new Promise((resolve, reject) => {
- const iface = readline.createInterface(process.stdin, process.stdout);
- const optionsStr = options.map((o, i) => i + 1 + '. ' + o).join('\r\n');
-
- iface.setPrompt(question + '\r\n' + optionsStr + '\r\nChoice: ');
- iface.prompt();
-
- iface.on('line', _answer => {
- const answer = toInt(_answer);
- if (answer > 0 && answer <= options.length) {
- iface.close();
- resolve(options[answer - 1]);
- } else {
- iface.prompt();
- }
- });
- });
-}
-
-function toInt(n: string): number {
- const result = parseInt(n, 10);
- return isNaN(result) ? 0 : result;
-}
-
-export {ask};
diff --git a/lighthouse-cli/bin.js b/lighthouse-cli/bin.js
index 5032b84e1930..0a2122338883 100755
--- a/lighthouse-cli/bin.js
+++ b/lighthouse-cli/bin.js
@@ -2,4 +2,4 @@
'use strict';
-require('./out/index.js');
+require('./js/index.js');
diff --git a/lighthouse-cli/chrome-finder.ts b/lighthouse-cli/chrome-finder.ts
deleted file mode 100644
index 1827c0a3489c..000000000000
--- a/lighthouse-cli/chrome-finder.ts
+++ /dev/null
@@ -1,130 +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';
-
-const fs = require('fs');
-const path = require('path');
-const execSync = require('child_process').execSync;
-
-export function darwin() {
- const suffix = '/Contents/MacOS/Google Chrome Canary';
-
- const LSREGISTER =
- '/System/Library/Frameworks/CoreServices.framework' +
- '/Versions/A/Frameworks/LaunchServices.framework' +
- '/Versions/A/Support/lsregister';
-
- const installations = [];
-
- execSync(
- `${LSREGISTER} -dump` +
- ' | grep -i \'google chrome canary.app$\'' +
- ' | awk \'{$1=""; print $0}\''
- ).toString()
- .split(/\r?\n/)
- .forEach(inst => {
- const execPath = path.join(inst.trim(), suffix);
- if (canAccess(execPath)) {
- installations.push(execPath);
- }
- });
-
- /* https://github.com/Microsoft/TypeScript/issues/6574
- const priorities = new Map([
- [/^\/Volumes\//, -1],
- [/^\/Applications\//, 100],
- [new RegExp(`^${process.env.HOME}/Applications/`), 50]
- ]);
- */
- const priorities: {regex: RegExp, weight: number}[] = [{
- regex: /^\/Volumes\//,
- weight: -1
- }, {
- regex: /^\/Applications\//,
- weight: 100
- },
- {
- regex: new RegExp(`^${process.env.HOME}/Applications/`),
- weight: 50
- }
- ];
-
- return sort(installations, priorities);
-}
-
-export function linux() {
- const execPath = process.env.LIGHTHOUSE_CHROMIUM_PATH;
- if (execPath && canAccess(execPath)) {
- return [execPath];
- }
- throw new Error(
- 'The environment variable LIGHTHOUSE_CHROMIUM_PATH must be set to ' +
- 'executable of a build of Chromium version 52.0 or later.'
- );
-}
-
-export function win32() {
- const installations = [];
- const suffixes = [
- '\\Google\\Chrome SxS\\Application\\chrome.exe',
- '\\Google\\Chrome\\Application\\chrome.exe'
- ];
- let prefixes = [
- process.env.LOCALAPPDATA,
- process.env.PROGRAMFILES,
- process.env['PROGRAMFILES(X86)']
- ];
- prefixes.forEach(prefix =>
- suffixes.forEach(suffix => {
- const chromePath = path.join(prefix, suffix);
- if (canAccess(chromePath)) {
- installations.push(chromePath);
- }
- })
- );
- return installations;
-}
-
-function sort(installations, priorities) {
- const defaultPriority = 10;
- return installations
- // assign priorities
- .map(inst => {
- for (let pair of priorities) {
- if (pair.regex.test(inst)) {
- return [inst, pair.weight];
- }
- }
- return [inst, defaultPriority];
- })
- // sort based on priorities
- .sort((a, b) => b[1] - a[1])
- // remove priority flag
- .map(pair => pair[0]);
-}
-
-function canAccess(file: string): Boolean {
- try {
- fs.accessSync(file);
- return true;
- } catch (e) {
- return false;
- }
-}
diff --git a/lighthouse-cli/chrome-launcher.ts b/lighthouse-cli/chrome-launcher.ts
deleted file mode 100644
index 9c1ce6aaf9f6..000000000000
--- a/lighthouse-cli/chrome-launcher.ts
+++ /dev/null
@@ -1,243 +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';
-
-import * as childProcess from 'child_process';
-import * as fs from 'fs';
-import * as path from 'path';
-import * as chromeFinder from './chrome-finder';
-import {ask} from './ask';
-
-const mkdirp = require('mkdirp');
-const net = require('net');
-const rimraf = require('rimraf');
-
-const spawn = childProcess.spawn;
-const execSync = childProcess.execSync;
-const spawnSync = childProcess.spawnSync;
-
-class ChromeLauncher {
- prepared: Boolean = false
- pollInterval: number = 500
- autoSelectChrome: Boolean
- TMP_PROFILE_DIR: string
- outFile: number
- errFile: number
- pidFile: string
- chrome: childProcess.ChildProcess
-
- // We can not use default args here due to support node pre 6.
- constructor(opts?: {autoSelectChrome?: Boolean}) {
- opts = opts || {};
-
- // choose the first one (default)
- this.autoSelectChrome = defaults(opts.autoSelectChrome, true);
- }
-
- flags() {
- const flags = [
- '--remote-debugging-port=9222',
- '--no-first-run',
- `--user-data-dir=${this.TMP_PROFILE_DIR}`
- ];
-
- if (process.platform === 'linux') {
- flags.push('--disable-setuid-sandbox');
- }
-
- return flags;
- }
-
- prepare() {
- switch (process.platform) {
- case 'darwin':
- case 'linux':
- this.TMP_PROFILE_DIR = unixTmpDir();
- break;
-
- case 'win32':
- this.TMP_PROFILE_DIR = win32TmpDir();
- break;
-
- default:
- throw new Error('Platform ' + process.platform + ' is not supported');
- }
-
- this.outFile = fs.openSync(`${this.TMP_PROFILE_DIR}/chrome-out.log`, 'a');
- this.errFile = fs.openSync(`${this.TMP_PROFILE_DIR}/chrome-err.log`, 'a');
-
- // fix for Node4
- // you can't pass a fd to fs.writeFileSync
- this.pidFile = `${this.TMP_PROFILE_DIR}/chrome.pid`;
-
- console.log(`created ${this.TMP_PROFILE_DIR}`);
-
- this.prepared = true;
- }
-
- run() {
- if (!this.prepared) {
- this.prepare();
- }
-
- return Promise.resolve()
- .then(() => {
- const installations = chromeFinder[process.platform]();
- if (installations.length < 1) {
- return Promise.reject(new Error('No Chrome Installations Found'));
- } else if (installations.length === 1 || this.autoSelectChrome) {
- return installations[0];
- }
-
- return ask('Choose a Chrome installation to use with Lighthouse', installations);
- })
- .then(execPath => this.spawn(execPath));
- }
-
- spawn(execPath: string): Promise {
- return new Promise((resolve, reject) => {
- const chrome = spawn(
- execPath,
- this.flags(),
- {
- detached: true,
- stdio: ['ignore', this.outFile, this.errFile]
- }
- );
- this.chrome = chrome;
-
- fs.writeFileSync(this.pidFile, chrome.pid.toString());
-
- console.log('Chrome running with pid =', chrome.pid);
- resolve(chrome.pid);
- })
- .then(pid => Promise.all([pid, this.waitUntilReady()]));
- }
-
- cleanup(client) {
- if (client) {
- client.removeAllListeners();
- client.end();
- client.destroy();
- client.unref();
- }
- }
-
- // resolves if ready, rejects otherwise
- isDebuggerReady(): Promise {
- return new Promise((resolve, reject) => {
- const client = net.createConnection(9222);
- client.once('error', err => {
- this.cleanup(client);
- reject(err);
- });
- client.once('connect', _ => {
- this.cleanup(client);
- resolve();
- });
- });
- }
-
- // resolves when debugger is ready, rejects after 10 polls
- waitUntilReady(): Promise {
- const launcher = this;
-
- return new Promise((resolve, reject) => {
- let retries = 0;
- (function poll() {
- const green = '\x1B[32m';
- const reset = '\x1B[0m';
-
- if (retries === 0) {
- process.stdout.write('Waiting for browser.');
- }
- retries++;
- process.stdout.write('..');
-
- launcher
- .isDebuggerReady()
- .then(() => {
- process.stdout.write(`${green}✓${reset}\n`);
- resolve();
- })
- .catch(err => {
- if (retries > 10) {
- process.stdout.write('\n');
- return reject(err);
- }
- delay(launcher.pollInterval).then(poll);
- });
- })();
- });
- }
-
- kill(): Promise {
- return new Promise(resolve => {
- if (this.chrome) {
- this.chrome.on('close', () => {
- this.destroyTmp();
- resolve();
- });
-
- console.log('Killing all Chrome Instances');
- this.chrome.kill();
-
- if (process.platform === 'win32') {
- spawnSync(`taskkill /pid ${this.chrome.pid} /T /F`);
- }
- } else {
- // fail silently as we did not start chrome
- resolve();
- }
- });
- }
-
- destroyTmp() {
- if (this.TMP_PROFILE_DIR) {
- console.log(`Removing ${this.TMP_PROFILE_DIR}`);
- rimraf.sync(this.TMP_PROFILE_DIR);
- }
- }
-};
-
-function defaults(val, def) {
- return typeof val === 'undefined' ? def : val;
-}
-
-function delay(time) {
- return new Promise(resolve => setTimeout(resolve, time));
-}
-
-function unixTmpDir() {
- return execSync('mktemp -d -t lighthouse.XXXXXXX').toString().trim();
-}
-
-function win32TmpDir() {
- const winTmpPath = process.env.TEMP ||
- process.env.TMP ||
- (process.env.SystemRoot || process.env.windir) + '\\temp';
- const randomNumber = Math.floor(Math.random() * 9e7 + 1e7);
- const tmpdir = path.join(winTmpPath, 'lighthouse.' + randomNumber);
-
- mkdirp.sync(tmpdir);
- return tmpdir;
-}
-
-export {ChromeLauncher};
diff --git a/lighthouse-cli/decl/ask.d.ts b/lighthouse-cli/decl/ask.d.ts
new file mode 100644
index 000000000000..0e939ce55bbf
--- /dev/null
+++ b/lighthouse-cli/decl/ask.d.ts
@@ -0,0 +1,3 @@
+///
+declare function ask(question: string, options: string[]): Promise;
+export { ask };
diff --git a/lighthouse-cli/decl/chrome-finder.d.ts b/lighthouse-cli/decl/chrome-finder.d.ts
new file mode 100644
index 000000000000..ab8459894575
--- /dev/null
+++ b/lighthouse-cli/decl/chrome-finder.d.ts
@@ -0,0 +1,4 @@
+///
+export declare function darwin(): any;
+export declare function linux(): any[];
+export declare function win32(): any[];
diff --git a/lighthouse-cli/decl/chrome-launcher.d.ts b/lighthouse-cli/decl/chrome-launcher.d.ts
new file mode 100644
index 000000000000..81e7744f105b
--- /dev/null
+++ b/lighthouse-cli/decl/chrome-launcher.d.ts
@@ -0,0 +1,25 @@
+///
+import * as childProcess from 'child_process';
+declare class ChromeLauncher {
+ prepared: Boolean;
+ pollInterval: number;
+ autoSelectChrome: Boolean;
+ TMP_PROFILE_DIR: string;
+ outFile: number;
+ errFile: number;
+ pidFile: string;
+ chrome: childProcess.ChildProcess;
+ constructor(opts?: {
+ autoSelectChrome?: Boolean;
+ });
+ flags(): string[];
+ prepare(): void;
+ run(): Promise;
+ spawn(execPath: string): Promise;
+ cleanup(client: any): void;
+ isDebuggerReady(): Promise;
+ waitUntilReady(): Promise;
+ kill(): Promise;
+ destroyTmp(): void;
+}
+export { ChromeLauncher };
diff --git a/lighthouse-cli/decl/index.d.ts b/lighthouse-cli/decl/index.d.ts
new file mode 100644
index 000000000000..eebc2728b868
--- /dev/null
+++ b/lighthouse-cli/decl/index.d.ts
@@ -0,0 +1 @@
+///
diff --git a/lighthouse-cli/decl/printer.d.ts b/lighthouse-cli/decl/printer.d.ts
new file mode 100644
index 000000000000..bf23408dde0b
--- /dev/null
+++ b/lighthouse-cli/decl/printer.d.ts
@@ -0,0 +1,38 @@
+///
+export declare type Mode = 'pretty' | 'json' | 'html';
+export interface Results {
+ url: string;
+ aggregations: any[];
+ audits: Object;
+}
+/**
+ * An enumeration of acceptable output modes:
+ *
+ * - 'pretty': Pretty print the results
+ * - 'json': JSON formatted results
+ * - 'html': An HTML report
+ *
+ * @enum {string}
+ */
+declare const OUTPUT_MODE: {
+ pretty: string;
+ json: string;
+ html: string;
+};
+/**
+ * Verify output mode.
+ */
+declare function checkOutputMode(mode: string): Mode;
+/**
+ * Verify output path to use, either stdout or a file path.
+ */
+declare function checkOutputPath(path: string): string;
+/**
+ * Creates the results output in a format based on the `mode`.
+ */
+declare function createOutput(results: Results, outputMode: Mode): string;
+/**
+ * Writes the results.
+ */
+declare function write(results: Results, mode: Mode, path: string): Promise;
+export { checkOutputMode, checkOutputPath, createOutput, write, OUTPUT_MODE };
diff --git a/lighthouse-cli/index.js b/lighthouse-cli/index.js
index 5032b84e1930..0a2122338883 100755
--- a/lighthouse-cli/index.js
+++ b/lighthouse-cli/index.js
@@ -2,4 +2,4 @@
'use strict';
-require('./out/index.js');
+require('./js/index.js');
diff --git a/lighthouse-cli/index.ts b/lighthouse-cli/index.ts
deleted file mode 100755
index b6a93e0675d0..000000000000
--- a/lighthouse-cli/index.ts
+++ /dev/null
@@ -1,254 +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';
-
-const environment = require('../../lighthouse-core/lib/environment.js');
-if (!environment.checkNodeCompatibility()) {
- console.warn('Compatibility error', 'Lighthouse requires node 5+ or 4 with --harmony');
- process.exit(1);
-}
-
-const path = require('path');
-const yargs = require('yargs');
-import * as Printer from './printer';
-const lighthouse = require('../../lighthouse-core');
-const assetSaver = require('../../lighthouse-core/lib/asset-saver.js');
-import {ChromeLauncher} from './chrome-launcher';
-
-const perfOnlyConfig = require('../../lighthouse-core/config/perf.json');
-
-const cli = yargs
- .help('help')
- .version(() => require('../package').version)
- .showHelpOnFail(false, 'Specify --help for available options')
-
- .usage('$0 url')
-
- // List of options
- .group([
- 'verbose',
- 'quiet'
- ], 'Logging:')
- .describe({
- verbose: 'Displays verbose logging',
- quiet: 'Displays no progress or debug logs'
- })
-
- .group([
- 'mobile',
- 'save-assets',
- 'save-artifacts',
- 'list-all-audits',
- 'list-trace-categories',
- 'config-path',
- 'perf'
- ], 'Configuration:')
- .describe({
- 'mobile': 'Emulates a Nexus 5X',
- 'save-assets': 'Save the trace contents & screenshots to disk',
- 'save-artifacts': 'Save all gathered artifacts to disk',
- 'list-all-audits': 'Prints a list of all available audits and exits',
- 'list-trace-categories': 'Prints a list of all required trace categories and exits',
- 'config-path': 'The path to the config JSON.',
- 'perf': 'Use a performance-test-only configuration',
- 'skip-autolaunch': 'Skip autolaunch of chrome when accessing port 9222 fails',
- 'select-chrome': 'Choose chrome location to use when multiple installations are found',
- })
-
- .group([
- 'output',
- 'output-path'
- ], 'Output:')
- .describe({
- 'output': 'Reporter for the results',
- 'output-path': `The file path to output the results
-Example: --output-path=./lighthouse-results.html`
- })
-
- // boolean values
- .boolean([
- 'save-assets',
- 'save-artifacts',
- 'list-all-audits',
- 'list-trace-categories',
- 'perf',
- 'skip-autolaunch',
- 'select-chrome',
- 'verbose',
- 'quiet',
- 'help'
- ])
-
- .choices('output', Object.keys(Printer.OUTPUT_MODE).map(k => Printer.OUTPUT_MODE[k]))
-
- // default values
- .default('mobile', true)
- .default('output', Printer.OUTPUT_MODE.pretty)
- .default('output-path', 'stdout')
- .check(argv => {
- // Make sure lighthouse has been passed a url, or at least one of --list-all-audits
- // or --list-trace-categories. If not, stop the program and ask for a url
- if (!argv.listAllAudits && !argv.listTraceCategories && argv._.length === 0) {
- throw new Error('Please provide a url');
- }
-
- return true;
- })
- .argv;
-
-if (cli.listAllAudits) {
- const audits = lighthouse
- .getAuditList()
- .map(i => {
- return i.replace(/\.js$/, '');
- });
-
- process.stdout.write(JSON.stringify({audits}));
- process.exit(0);
-}
-
-if (cli.listTraceCategories) {
- const traceCategories = lighthouse.traceCategories;
-
- process.stdout.write(JSON.stringify({traceCategories}));
- process.exit(0);
-}
-
-const urls = cli._;
-const outputMode = cli.output;
-const outputPath = cli['output-path'];
-const flags = cli;
-
-let config = null;
-if (cli.configPath) {
- // Resolve the config file path relative to where cli was called.
- cli.configPath = path.resolve(process.cwd(), cli.configPath);
- config = require(cli.configPath);
-} else if (cli.perf) {
- config = perfOnlyConfig;
-}
-
-// set logging preferences
-flags.logLevel = 'info';
-if (cli.verbose) {
- flags.logLevel = 'verbose';
-} else if (cli.quiet) {
- flags.logLevel = 'error';
-}
-
-const cleanup = {
- fns: [],
- register(fn) {
- this.fns.push(fn);
- },
- doCleanup() {
- return Promise.all(this.fns.map(c => c()));
- }
-};
-
-function launchChromeAndRun(addresses) {
- const launcher = new ChromeLauncher({
- autoSelectChrome: !cli.selectChrome,
- });
-
- cleanup.register(() => launcher.kill());
-
- return launcher
- .isDebuggerReady()
- .catch(() => {
- console.log('Launching Chrome...');
- return launcher.run();
- })
- .then(() => lighthouseRun(addresses))
- .then(() => launcher.kill());
-}
-
-function lighthouseRun(addresses) {
- // Process URLs once at a time
- const address = addresses.shift();
- if (!address) {
- return;
- }
-
- return lighthouse(address, flags, config)
- .then(results => Printer.write(results, outputMode, outputPath))
- .then(results => {
- if (outputMode !== 'html') {
- const filename = './' + assetSaver.getFilenamePrefix({url: address}) + '.html';
- Printer.write(results, 'html', filename)
- }
-
- return lighthouseRun(addresses);
- });
-}
-
-function showConnectionError() {
- console.error('Unable to connect to Chrome');
- console.error(
- 'If you\'re using lighthouse with --skip-autolaunch, ' +
- 'make sure you\'re running some other Chrome with a debugger.'
- );
- process.exit(1);
-}
-
-function showRuntimeError(err) {
- console.error('Runtime error encountered:', err);
- console.error(err.stack);
- process.exit(1);
-}
-
-function handleError(err) {
- if (err.code === 'ECONNREFUSED') {
- showConnectionError();
- } else {
- showRuntimeError(err);
- }
-}
-
-function run() {
- if (cli.skipAutolaunch) {
- lighthouseRun(urls).catch(handleError);
- } else {
- // because you can't cancel a promise yet
- const SIGINT = 'SIGINT';
- const isSigint = new Promise((resolve, reject) => {
- process.on('SIGINT', () => reject(SIGINT));
- });
-
- Promise
- .race([launchChromeAndRun(urls), isSigint])
- .catch(maybeSigint => {
- if (maybeSigint === SIGINT) {
- return cleanup
- .doCleanup()
- .then(() => process.exit(130))
- .catch(err => {
- console.error(err);
- console.error(err.stack);
- process.exit(130);
- });
- }
- return handleError(maybeSigint);
- });
- }
-}
-
-// kick off a lighthouse run
-run();
diff --git a/lighthouse-cli/js/ask.js b/lighthouse-cli/js/ask.js
new file mode 100644
index 000000000000..eceda17ccd42
--- /dev/null
+++ b/lighthouse-cli/js/ask.js
@@ -0,0 +1,43 @@
+///
+/**
+ * @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 readline = require('readline');
+function ask(question, options) {
+ return new Promise((resolve, reject) => {
+ const iface = readline.createInterface(process.stdin, process.stdout);
+ const optionsStr = options.map((o, i) => i + 1 + '. ' + o).join('\r\n');
+ iface.setPrompt(question + '\r\n' + optionsStr + '\r\nChoice: ');
+ iface.prompt();
+ iface.on('line', _answer => {
+ const answer = toInt(_answer);
+ if (answer > 0 && answer <= options.length) {
+ iface.close();
+ resolve(options[answer - 1]);
+ }
+ else {
+ iface.prompt();
+ }
+ });
+ });
+}
+exports.ask = ask;
+function toInt(n) {
+ const result = parseInt(n, 10);
+ return isNaN(result) ? 0 : result;
+}
+//# sourceMappingURL=ask.js.map
diff --git a/lighthouse-cli/js/ask.js.map b/lighthouse-cli/js/ask.js.map
new file mode 100644
index 000000000000..58616196b256
--- /dev/null
+++ b/lighthouse-cli/js/ask.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"ask.js","sourceRoot":"","sources":["../ask.ts"],"names":[],"mappings":"AAAA,2CAA2C;AAE3C;;;;;;;;;;;;;;;GAeG;AAEH,YAAY,CAAC;AAEb,MAAM,QAAQ,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;AAErC,aAAa,QAAgB,EAAE,OAAiB;IAC9C,MAAM,CAAC,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM;QACjC,MAAM,KAAK,GAAG,QAAQ,CAAC,eAAe,CAAC,OAAO,CAAC,KAAK,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;QACtE,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAExE,KAAK,CAAC,SAAS,CAAC,QAAQ,GAAG,MAAM,GAAG,UAAU,GAAG,cAAc,CAAC,CAAC;QACjE,KAAK,CAAC,MAAM,EAAE,CAAC;QAEf,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO;YACtB,MAAM,MAAM,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC;YAC9B,EAAE,CAAC,CAAC,MAAM,GAAG,CAAC,IAAI,MAAM,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;gBAC3C,KAAK,CAAC,KAAK,EAAE,CAAC;gBACd,OAAO,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC;YAC/B,CAAC;YAAC,IAAI,CAAC,CAAC;gBACN,KAAK,CAAC,MAAM,EAAE,CAAC;YACjB,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC;AAOO,WAAG,OAPV;AAED,eAAe,CAAS;IACtB,MAAM,MAAM,GAAG,QAAQ,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IAC/B,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC;AACpC,CAAC;AAEY"}
\ No newline at end of file
diff --git a/lighthouse-cli/js/chrome-finder.js b/lighthouse-cli/js/chrome-finder.js
new file mode 100644
index 000000000000..bc2b7cdb05e7
--- /dev/null
+++ b/lighthouse-cli/js/chrome-finder.js
@@ -0,0 +1,112 @@
+///
+/**
+ * @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 fs = require('fs');
+const path = require('path');
+const execSync = require('child_process').execSync;
+function darwin() {
+ const suffix = '/Contents/MacOS/Google Chrome Canary';
+ const LSREGISTER = '/System/Library/Frameworks/CoreServices.framework' +
+ '/Versions/A/Frameworks/LaunchServices.framework' +
+ '/Versions/A/Support/lsregister';
+ const installations = [];
+ execSync(`${LSREGISTER} -dump` +
+ ' | grep -i \'google chrome canary.app$\'' +
+ ' | awk \'{$1=""; print $0}\'').toString()
+ .split(/\r?\n/)
+ .forEach(inst => {
+ const execPath = path.join(inst.trim(), suffix);
+ if (canAccess(execPath)) {
+ installations.push(execPath);
+ }
+ });
+ /* https://github.com/Microsoft/TypeScript/issues/6574
+ const priorities = new Map([
+ [/^\/Volumes\//, -1],
+ [/^\/Applications\//, 100],
+ [new RegExp(`^${process.env.HOME}/Applications/`), 50]
+ ]);
+ */
+ const priorities = [{
+ regex: /^\/Volumes\//,
+ weight: -1
+ }, {
+ regex: /^\/Applications\//,
+ weight: 100
+ },
+ {
+ regex: new RegExp(`^${process.env.HOME}/Applications/`),
+ weight: 50
+ }
+ ];
+ return sort(installations, priorities);
+}
+exports.darwin = darwin;
+function linux() {
+ const execPath = process.env.LIGHTHOUSE_CHROMIUM_PATH;
+ if (execPath && canAccess(execPath)) {
+ return [execPath];
+ }
+ throw new Error('The environment variable LIGHTHOUSE_CHROMIUM_PATH must be set to ' +
+ 'executable of a build of Chromium version 52.0 or later.');
+}
+exports.linux = linux;
+function win32() {
+ const installations = [];
+ const suffixes = [
+ '\\Google\\Chrome SxS\\Application\\chrome.exe',
+ '\\Google\\Chrome\\Application\\chrome.exe'
+ ];
+ let prefixes = [
+ process.env.LOCALAPPDATA,
+ process.env.PROGRAMFILES,
+ process.env['PROGRAMFILES(X86)']
+ ];
+ prefixes.forEach(prefix => suffixes.forEach(suffix => {
+ const chromePath = path.join(prefix, suffix);
+ if (canAccess(chromePath)) {
+ installations.push(chromePath);
+ }
+ }));
+ return installations;
+}
+exports.win32 = win32;
+function sort(installations, priorities) {
+ const defaultPriority = 10;
+ return installations
+ .map(inst => {
+ for (let pair of priorities) {
+ if (pair.regex.test(inst)) {
+ return [inst, pair.weight];
+ }
+ }
+ return [inst, defaultPriority];
+ })
+ .sort((a, b) => b[1] - a[1])
+ .map(pair => pair[0]);
+}
+function canAccess(file) {
+ try {
+ fs.accessSync(file);
+ return true;
+ }
+ catch (e) {
+ return false;
+ }
+}
+//# sourceMappingURL=chrome-finder.js.map
diff --git a/lighthouse-cli/js/chrome-finder.js.map b/lighthouse-cli/js/chrome-finder.js.map
new file mode 100644
index 000000000000..bc7b5d80909e
--- /dev/null
+++ b/lighthouse-cli/js/chrome-finder.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"chrome-finder.js","sourceRoot":"","sources":["../chrome-finder.ts"],"names":[],"mappings":"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"}
\ No newline at end of file
diff --git a/lighthouse-cli/js/chrome-launcher.js b/lighthouse-cli/js/chrome-launcher.js
new file mode 100644
index 000000000000..56d92e6fe75a
--- /dev/null
+++ b/lighthouse-cli/js/chrome-launcher.js
@@ -0,0 +1,197 @@
+///
+/**
+ * @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 childProcess = require('child_process');
+const fs = require('fs');
+const path = require('path');
+const chromeFinder = require('./chrome-finder');
+const ask_1 = require('./ask');
+const mkdirp = require('mkdirp');
+const net = require('net');
+const rimraf = require('rimraf');
+const spawn = childProcess.spawn;
+const execSync = childProcess.execSync;
+const spawnSync = childProcess.spawnSync;
+class ChromeLauncher {
+ // We can not use default args here due to support node pre 6.
+ constructor(opts) {
+ this.prepared = false;
+ this.pollInterval = 500;
+ opts = opts || {};
+ // choose the first one (default)
+ this.autoSelectChrome = defaults(opts.autoSelectChrome, true);
+ }
+ flags() {
+ const flags = [
+ '--remote-debugging-port=9222',
+ '--no-first-run',
+ `--user-data-dir=${this.TMP_PROFILE_DIR}`
+ ];
+ if (process.platform === 'linux') {
+ flags.push('--disable-setuid-sandbox');
+ }
+ return flags;
+ }
+ prepare() {
+ switch (process.platform) {
+ case 'darwin':
+ case 'linux':
+ this.TMP_PROFILE_DIR = unixTmpDir();
+ break;
+ case 'win32':
+ this.TMP_PROFILE_DIR = win32TmpDir();
+ break;
+ default:
+ throw new Error('Platform ' + process.platform + ' is not supported');
+ }
+ this.outFile = fs.openSync(`${this.TMP_PROFILE_DIR}/chrome-out.log`, 'a');
+ this.errFile = fs.openSync(`${this.TMP_PROFILE_DIR}/chrome-err.log`, 'a');
+ // fix for Node4
+ // you can't pass a fd to fs.writeFileSync
+ this.pidFile = `${this.TMP_PROFILE_DIR}/chrome.pid`;
+ console.log(`created ${this.TMP_PROFILE_DIR}`);
+ this.prepared = true;
+ }
+ run() {
+ if (!this.prepared) {
+ this.prepare();
+ }
+ return Promise.resolve()
+ .then(() => {
+ const installations = chromeFinder[process.platform]();
+ if (installations.length < 1) {
+ return Promise.reject(new Error('No Chrome Installations Found'));
+ }
+ else if (installations.length === 1 || this.autoSelectChrome) {
+ return installations[0];
+ }
+ return ask_1.ask('Choose a Chrome installation to use with Lighthouse', installations);
+ })
+ .then(execPath => this.spawn(execPath));
+ }
+ spawn(execPath) {
+ return new Promise((resolve, reject) => {
+ const chrome = spawn(execPath, this.flags(), {
+ detached: true,
+ stdio: ['ignore', this.outFile, this.errFile]
+ });
+ this.chrome = chrome;
+ fs.writeFileSync(this.pidFile, chrome.pid.toString());
+ console.log('Chrome running with pid =', chrome.pid);
+ resolve(chrome.pid);
+ })
+ .then(pid => Promise.all([pid, this.waitUntilReady()]));
+ }
+ cleanup(client) {
+ if (client) {
+ client.removeAllListeners();
+ client.end();
+ client.destroy();
+ client.unref();
+ }
+ }
+ // resolves if ready, rejects otherwise
+ isDebuggerReady() {
+ return new Promise((resolve, reject) => {
+ const client = net.createConnection(9222);
+ client.once('error', err => {
+ this.cleanup(client);
+ reject(err);
+ });
+ client.once('connect', _ => {
+ this.cleanup(client);
+ resolve();
+ });
+ });
+ }
+ // resolves when debugger is ready, rejects after 10 polls
+ waitUntilReady() {
+ const launcher = this;
+ return new Promise((resolve, reject) => {
+ let retries = 0;
+ (function poll() {
+ const green = '\x1B[32m';
+ const reset = '\x1B[0m';
+ if (retries === 0) {
+ process.stdout.write('Waiting for browser.');
+ }
+ retries++;
+ process.stdout.write('..');
+ launcher
+ .isDebuggerReady()
+ .then(() => {
+ process.stdout.write(`${green}✓${reset}\n`);
+ resolve();
+ })
+ .catch(err => {
+ if (retries > 10) {
+ process.stdout.write('\n');
+ return reject(err);
+ }
+ delay(launcher.pollInterval).then(poll);
+ });
+ })();
+ });
+ }
+ kill() {
+ return new Promise(resolve => {
+ if (this.chrome) {
+ this.chrome.on('close', () => {
+ this.destroyTmp();
+ resolve();
+ });
+ console.log('Killing all Chrome Instances');
+ this.chrome.kill();
+ if (process.platform === 'win32') {
+ spawnSync(`taskkill /pid ${this.chrome.pid} /T /F`);
+ }
+ }
+ else {
+ // fail silently as we did not start chrome
+ resolve();
+ }
+ });
+ }
+ destroyTmp() {
+ if (this.TMP_PROFILE_DIR) {
+ console.log(`Removing ${this.TMP_PROFILE_DIR}`);
+ rimraf.sync(this.TMP_PROFILE_DIR);
+ }
+ }
+}
+exports.ChromeLauncher = ChromeLauncher;
+;
+function defaults(val, def) {
+ return typeof val === 'undefined' ? def : val;
+}
+function delay(time) {
+ return new Promise(resolve => setTimeout(resolve, time));
+}
+function unixTmpDir() {
+ return execSync('mktemp -d -t lighthouse.XXXXXXX').toString().trim();
+}
+function win32TmpDir() {
+ const winTmpPath = process.env.TEMP ||
+ process.env.TMP ||
+ (process.env.SystemRoot || process.env.windir) + '\\temp';
+ const randomNumber = Math.floor(Math.random() * 9e7 + 1e7);
+ const tmpdir = path.join(winTmpPath, 'lighthouse.' + randomNumber);
+ mkdirp.sync(tmpdir);
+ return tmpdir;
+}
+//# sourceMappingURL=chrome-launcher.js.map
diff --git a/lighthouse-cli/js/chrome-launcher.js.map b/lighthouse-cli/js/chrome-launcher.js.map
new file mode 100644
index 000000000000..6c3365fa0c97
--- /dev/null
+++ b/lighthouse-cli/js/chrome-launcher.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"chrome-launcher.js","sourceRoot":"","sources":["../chrome-launcher.ts"],"names":[],"mappings":"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"}
\ No newline at end of file
diff --git a/lighthouse-cli/js/index.js b/lighthouse-cli/js/index.js
new file mode 100644
index 000000000000..99218f98d6f7
--- /dev/null
+++ b/lighthouse-cli/js/index.js
@@ -0,0 +1,220 @@
+///
+/**
+ * @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 environment = require('../../lighthouse-core/lib/environment.js');
+if (!environment.checkNodeCompatibility()) {
+ console.warn('Compatibility error', 'Lighthouse requires node 5+ or 4 with --harmony');
+ process.exit(1);
+}
+const path = require('path');
+const yargs = require('yargs');
+const Printer = require('./printer');
+const lighthouse = require('../../lighthouse-core');
+const assetSaver = require('../../lighthouse-core/lib/asset-saver.js');
+const chrome_launcher_1 = require('./chrome-launcher');
+const perfOnlyConfig = require('../../lighthouse-core/config/perf.json');
+const cli = yargs
+ .help('help')
+ .version(() => require('../package').version)
+ .showHelpOnFail(false, 'Specify --help for available options')
+ .usage('$0 url')
+ .group([
+ 'verbose',
+ 'quiet'
+], 'Logging:')
+ .describe({
+ verbose: 'Displays verbose logging',
+ quiet: 'Displays no progress or debug logs'
+})
+ .group([
+ 'mobile',
+ 'save-assets',
+ 'save-artifacts',
+ 'list-all-audits',
+ 'list-trace-categories',
+ 'config-path',
+ 'perf'
+], 'Configuration:')
+ .describe({
+ 'mobile': 'Emulates a Nexus 5X',
+ 'save-assets': 'Save the trace contents & screenshots to disk',
+ 'save-artifacts': 'Save all gathered artifacts to disk',
+ 'list-all-audits': 'Prints a list of all available audits and exits',
+ 'list-trace-categories': 'Prints a list of all required trace categories and exits',
+ 'config-path': 'The path to the config JSON.',
+ 'perf': 'Use a performance-test-only configuration',
+ 'skip-autolaunch': 'Skip autolaunch of chrome when accessing port 9222 fails',
+ 'select-chrome': 'Choose chrome location to use when multiple installations are found',
+})
+ .group([
+ 'output',
+ 'output-path'
+], 'Output:')
+ .describe({
+ 'output': 'Reporter for the results',
+ 'output-path': `The file path to output the results
+Example: --output-path=./lighthouse-results.html`
+})
+ .boolean([
+ 'save-assets',
+ 'save-artifacts',
+ 'list-all-audits',
+ 'list-trace-categories',
+ 'perf',
+ 'skip-autolaunch',
+ 'select-chrome',
+ 'verbose',
+ 'quiet',
+ 'help'
+])
+ .choices('output', Object.keys(Printer.OUTPUT_MODE).map(k => Printer.OUTPUT_MODE[k]))
+ .default('mobile', true)
+ .default('output', Printer.OUTPUT_MODE.pretty)
+ .default('output-path', 'stdout')
+ .check(argv => {
+ // Make sure lighthouse has been passed a url, or at least one of --list-all-audits
+ // or --list-trace-categories. If not, stop the program and ask for a url
+ if (!argv.listAllAudits && !argv.listTraceCategories && argv._.length === 0) {
+ throw new Error('Please provide a url');
+ }
+ return true;
+})
+ .argv;
+if (cli.listAllAudits) {
+ const audits = lighthouse
+ .getAuditList()
+ .map(i => {
+ return i.replace(/\.js$/, '');
+ });
+ process.stdout.write(JSON.stringify({ audits }));
+ process.exit(0);
+}
+if (cli.listTraceCategories) {
+ const traceCategories = lighthouse.traceCategories;
+ process.stdout.write(JSON.stringify({ traceCategories }));
+ process.exit(0);
+}
+const urls = cli._;
+const outputMode = cli.output;
+const outputPath = cli['output-path'];
+const flags = cli;
+let config = null;
+if (cli.configPath) {
+ // Resolve the config file path relative to where cli was called.
+ cli.configPath = path.resolve(process.cwd(), cli.configPath);
+ config = require(cli.configPath);
+}
+else if (cli.perf) {
+ config = perfOnlyConfig;
+}
+// set logging preferences
+flags.logLevel = 'info';
+if (cli.verbose) {
+ flags.logLevel = 'verbose';
+}
+else if (cli.quiet) {
+ flags.logLevel = 'error';
+}
+const cleanup = {
+ fns: [],
+ register(fn) {
+ this.fns.push(fn);
+ },
+ doCleanup() {
+ return Promise.all(this.fns.map(c => c()));
+ }
+};
+function launchChromeAndRun(addresses) {
+ const launcher = new chrome_launcher_1.ChromeLauncher({
+ autoSelectChrome: !cli.selectChrome,
+ });
+ cleanup.register(() => launcher.kill());
+ return launcher
+ .isDebuggerReady()
+ .catch(() => {
+ console.log('Launching Chrome...');
+ return launcher.run();
+ })
+ .then(() => lighthouseRun(addresses))
+ .then(() => launcher.kill());
+}
+function lighthouseRun(addresses) {
+ // Process URLs once at a time
+ const address = addresses.shift();
+ if (!address) {
+ return;
+ }
+ return lighthouse(address, flags, config)
+ .then(results => Printer.write(results, outputMode, outputPath))
+ .then(results => {
+ if (outputMode !== 'html') {
+ const filename = './' + assetSaver.getFilenamePrefix({ url: address }) + '.html';
+ Printer.write(results, 'html', filename);
+ }
+ return lighthouseRun(addresses);
+ });
+}
+function showConnectionError() {
+ console.error('Unable to connect to Chrome');
+ console.error('If you\'re using lighthouse with --skip-autolaunch, ' +
+ 'make sure you\'re running some other Chrome with a debugger.');
+ process.exit(1);
+}
+function showRuntimeError(err) {
+ console.error('Runtime error encountered:', err);
+ console.error(err.stack);
+ process.exit(1);
+}
+function handleError(err) {
+ if (err.code === 'ECONNREFUSED') {
+ showConnectionError();
+ }
+ else {
+ showRuntimeError(err);
+ }
+}
+function run() {
+ if (cli.skipAutolaunch) {
+ lighthouseRun(urls).catch(handleError);
+ }
+ else {
+ // because you can't cancel a promise yet
+ const SIGINT = 'SIGINT';
+ const isSigint = new Promise((resolve, reject) => {
+ process.on('SIGINT', () => reject(SIGINT));
+ });
+ Promise
+ .race([launchChromeAndRun(urls), isSigint])
+ .catch(maybeSigint => {
+ if (maybeSigint === SIGINT) {
+ return cleanup
+ .doCleanup()
+ .then(() => process.exit(130))
+ .catch(err => {
+ console.error(err);
+ console.error(err.stack);
+ process.exit(130);
+ });
+ }
+ return handleError(maybeSigint);
+ });
+ }
+}
+// kick off a lighthouse run
+run();
+//# sourceMappingURL=index.js.map
diff --git a/lighthouse-cli/js/index.js.map b/lighthouse-cli/js/index.js.map
new file mode 100644
index 000000000000..e8215910998a
--- /dev/null
+++ b/lighthouse-cli/js/index.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"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"}
\ No newline at end of file
diff --git a/lighthouse-cli/js/printer.js b/lighthouse-cli/js/printer.js
new file mode 100644
index 000000000000..477ed7e2f4b4
--- /dev/null
+++ b/lighthouse-cli/js/printer.js
@@ -0,0 +1,184 @@
+///
+/**
+ * @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 fs = require('fs');
+const ReportGenerator = require('../../lighthouse-core/report/report-generator');
+const Formatter = require('../../lighthouse-core/formatters/formatter');
+const log = require('../../lighthouse-core/lib/log');
+/**
+ * An enumeration of acceptable output modes:
+ *
+ * - 'pretty': Pretty print the results
+ * - 'json': JSON formatted results
+ * - 'html': An HTML report
+ *
+ * @enum {string}
+ */
+const OUTPUT_MODE = {
+ pretty: 'pretty',
+ json: 'json',
+ html: 'html'
+};
+exports.OUTPUT_MODE = OUTPUT_MODE;
+/**
+ * Verify output mode.
+ */
+function checkOutputMode(mode) {
+ if (!OUTPUT_MODE.hasOwnProperty(mode)) {
+ log.warn('Printer', `Unknown output mode ${mode}; using pretty`);
+ return OUTPUT_MODE.pretty;
+ }
+ return OUTPUT_MODE[mode];
+}
+exports.checkOutputMode = checkOutputMode;
+/**
+ * Verify output path to use, either stdout or a file path.
+ */
+function checkOutputPath(path) {
+ if (!path) {
+ log.warn('Printer', 'No output path set; using stdout');
+ return 'stdout';
+ }
+ return path;
+}
+exports.checkOutputPath = checkOutputPath;
+function formatScore(score, suffix) {
+ // Until we only support node 6 we can not use default args.
+ suffix = suffix || '';
+ const green = '\x1B[32m';
+ const red = '\x1B[31m';
+ const yellow = '\x1b[33m';
+ const purple = '\x1b[95m';
+ const reset = '\x1B[0m';
+ if (typeof score === 'boolean') {
+ const check = `${green}✓${reset}`;
+ const fail = `${red}✘${reset}`;
+ return score ? check : fail;
+ }
+ if (typeof score !== 'number') {
+ return `${purple}${score}${reset}`;
+ }
+ let colorChoice = red;
+ if (score > 45) {
+ colorChoice = yellow;
+ }
+ if (score > 75) {
+ colorChoice = green;
+ }
+ return `${colorChoice}${score}${suffix}${reset}`;
+}
+/**
+ * Creates the results output in a format based on the `mode`.
+ */
+function createOutput(results, outputMode) {
+ const reportGenerator = new ReportGenerator();
+ // HTML report.
+ if (outputMode === 'html') {
+ return reportGenerator.generateHTML(results, { inline: true });
+ }
+ // JSON report.
+ if (outputMode === 'json') {
+ return JSON.stringify(results, null, 2);
+ }
+ // Pretty printed.
+ const bold = '\x1b[1m';
+ const reset = '\x1B[0m';
+ let output = `\n\n${bold}Lighthouse results:${reset} ${results.url}\n\n`;
+ results.aggregations.forEach(aggregation => {
+ output += `▫ ${bold}${aggregation.name}${reset}\n\n`;
+ aggregation.score.forEach(item => {
+ let score = (item.overall * 100).toFixed(0);
+ if (item.name) {
+ output += `${bold}${item.name}${reset}: ${formatScore(Number(score), '%')}\n`;
+ }
+ item.subItems.forEach(subitem => {
+ // Get audit object from inside of results.audits under name subitem.
+ // Coming soon events are not located inside of results.audits.
+ subitem = results.audits[subitem] || subitem;
+ if (subitem.comingSoon) {
+ return;
+ }
+ let lineItem = ` ── ${formatScore(subitem.score)} ${subitem.description}`;
+ if (subitem.displayValue) {
+ lineItem += ` (${bold}${subitem.displayValue}${reset})`;
+ }
+ output += `${lineItem}\n`;
+ if (subitem.debugString) {
+ output += ` ${subitem.debugString}\n`;
+ }
+ if (subitem.extendedInfo && subitem.extendedInfo.value) {
+ const formatter = Formatter.getByName(subitem.extendedInfo.formatter).getFormatter('pretty');
+ output += `${formatter(subitem.extendedInfo.value)}`;
+ }
+ });
+ output += '\n';
+ });
+ });
+ return output;
+}
+exports.createOutput = createOutput;
+/* istanbul ignore next */
+/**
+ * Writes the output to stdout.
+ */
+function writeToStdout(output) {
+ return new Promise((resolve, reject) => {
+ // small delay to avoid race with debug() logs
+ setTimeout(_ => {
+ process.stdout.write(`${output}\n`);
+ resolve();
+ }, 50);
+ });
+}
+/**
+ * Writes the output to a file.
+ */
+function writeFile(filePath, output, outputMode) {
+ return new Promise((resolve, reject) => {
+ // TODO: make this mkdir to the filePath.
+ fs.writeFile(filePath, output, 'utf8', err => {
+ if (err) {
+ return reject(err);
+ }
+ log.log('Printer', `${outputMode} output written to ${filePath}`);
+ resolve();
+ });
+ });
+}
+/**
+ * Writes the results.
+ */
+function write(results, mode, path) {
+ return new Promise((resolve, reject) => {
+ const outputMode = checkOutputMode(mode);
+ const outputPath = checkOutputPath(path);
+ const output = createOutput(results, outputMode);
+ // Testing stdout is out of scope, and doesn't really achieve much besides testing Node,
+ // so we will skip this chunk of the code.
+ /* istanbul ignore if */
+ if (outputPath === 'stdout') {
+ return writeToStdout(output).then(_ => resolve(results));
+ }
+ return writeFile(outputPath, output, outputMode).then(_ => {
+ resolve(results);
+ }).catch(err => reject(err));
+ });
+}
+exports.write = write;
+//# sourceMappingURL=printer.js.map
diff --git a/lighthouse-cli/js/printer.js.map b/lighthouse-cli/js/printer.js.map
new file mode 100644
index 000000000000..9e2cbd507c98
--- /dev/null
+++ b/lighthouse-cli/js/printer.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"printer.js","sourceRoot":"","sources":["../printer.ts"],"names":[],"mappings":"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"}
\ No newline at end of file
diff --git a/lighthouse-cli/jsconfig.json b/lighthouse-cli/jsconfig.json
new file mode 100644
index 000000000000..3989d9323e9e
--- /dev/null
+++ b/lighthouse-cli/jsconfig.json
@@ -0,0 +1,18 @@
+{
+ "compilerOptions": {
+ "module": "commonjs",
+ "target": "ES6",
+ "sourceMap": true,
+ "outDir": "outagain"
+ },
+ "exclude": [
+ "../../node_modules",
+ "../node_modules",
+ "node_modules",
+ "typings",
+ "scripts",
+ "out",
+ "outagain",
+ "lighthouse-core"
+ ]
+}
diff --git a/lighthouse-cli/printer.ts b/lighthouse-cli/printer.ts
deleted file mode 100644
index 5504e7409fbf..000000000000
--- a/lighthouse-cli/printer.ts
+++ /dev/null
@@ -1,226 +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';
-
-export type Mode = 'pretty' | 'json' | 'html';
-export interface Results {
- url: string;
- aggregations: any[];
- audits: Object;
-};
-
-const fs = require('fs');
-const ReportGenerator = require('../../lighthouse-core/report/report-generator');
-const Formatter = require('../../lighthouse-core/formatters/formatter');
-const log = require('../../lighthouse-core/lib/log');
-
-/**
- * An enumeration of acceptable output modes:
- *
- * - 'pretty': Pretty print the results
- * - 'json': JSON formatted results
- * - 'html': An HTML report
- *
- * @enum {string}
- */
-const OUTPUT_MODE = {
- pretty: 'pretty',
- json: 'json',
- html: 'html'
-};
-
-/**
- * Verify output mode.
- */
-function checkOutputMode(mode: string): Mode {
- if (!OUTPUT_MODE.hasOwnProperty(mode)) {
- log.warn('Printer', `Unknown output mode ${mode}; using pretty`);
- return OUTPUT_MODE.pretty as Mode;
- }
-
- return OUTPUT_MODE[mode];
-}
-
-/**
- * Verify output path to use, either stdout or a file path.
- */
-function checkOutputPath(path: string): string {
- if (!path) {
- log.warn('Printer', 'No output path set; using stdout');
- return 'stdout';
- }
-
- return path;
-}
-
-function formatScore(score, suffix?: string) {
- // Until we only support node 6 we can not use default args.
- suffix = suffix || '';
-
- const green = '\x1B[32m';
- const red = '\x1B[31m';
- const yellow = '\x1b[33m';
- const purple = '\x1b[95m';
- const reset = '\x1B[0m';
-
- if (typeof score === 'boolean') {
- const check = `${green}✓${reset}`;
- const fail = `${red}✘${reset}`;
- return score ? check : fail;
- }
- if (typeof score !== 'number') {
- return `${purple}${score}${reset}`;
- }
- let colorChoice = red;
- if (score > 45) {
- colorChoice = yellow;
- }
- if (score > 75) {
- colorChoice = green;
- }
- return `${colorChoice}${score}${suffix}${reset}`;
-}
-
-/**
- * Creates the results output in a format based on the `mode`.
- */
-function createOutput(results: Results, outputMode: Mode): string {
- const reportGenerator = new ReportGenerator();
-
- // HTML report.
- if (outputMode === 'html') {
- return reportGenerator.generateHTML(results, {inline: true});
- }
-
- // JSON report.
- if (outputMode === 'json') {
- return JSON.stringify(results, null, 2);
- }
-
- // Pretty printed.
- const bold = '\x1b[1m';
- const reset = '\x1B[0m';
- let output = `\n\n${bold}Lighthouse results:${reset} ${results.url}\n\n`;
-
- results.aggregations.forEach(aggregation => {
- output += `▫ ${bold}${aggregation.name}${reset}\n\n`;
-
- aggregation.score.forEach(item => {
- let score = (item.overall * 100).toFixed(0);
-
- if (item.name) {
- output += `${bold}${item.name}${reset}: ${formatScore(Number(score), '%')}\n`;
- }
-
- item.subItems.forEach(subitem => {
- // Get audit object from inside of results.audits under name subitem.
- // Coming soon events are not located inside of results.audits.
- subitem = results.audits[subitem] || subitem;
-
- if (subitem.comingSoon) {
- return;
- }
-
- let lineItem = ` ── ${formatScore(subitem.score)} ${subitem.description}`;
- if (subitem.displayValue) {
- lineItem += ` (${bold}${subitem.displayValue}${reset})`;
- }
- output += `${lineItem}\n`;
- if (subitem.debugString) {
- output += ` ${subitem.debugString}\n`;
- }
-
- if (subitem.extendedInfo && subitem.extendedInfo.value) {
- const formatter =
- Formatter.getByName(subitem.extendedInfo.formatter).getFormatter('pretty');
- output += `${formatter(subitem.extendedInfo.value)}`;
- }
- });
-
- output += '\n';
- });
- });
-
- return output;
-}
-
-/* istanbul ignore next */
-/**
- * Writes the output to stdout.
- */
-function writeToStdout(output: string): Promise {
- return new Promise((resolve, reject) => {
- // small delay to avoid race with debug() logs
- setTimeout(_ => {
- process.stdout.write(`${output}\n`);
- resolve();
- }, 50);
- });
-}
-
-/**
- * Writes the output to a file.
- */
-function writeFile(
- filePath: string,
- output: string,
- outputMode: Mode): Promise {
- return new Promise((resolve, reject) => {
- // TODO: make this mkdir to the filePath.
- fs.writeFile(filePath, output, 'utf8', err => {
- if (err) {
- return reject(err);
- }
- log.log('Printer', `${outputMode} output written to ${filePath}`);
- resolve();
- });
- });
-}
-
-/**
- * Writes the results.
- */
-function write(results: Results, mode: Mode, path: string): Promise {
- return new Promise((resolve, reject) => {
- const outputMode = checkOutputMode(mode);
- const outputPath = checkOutputPath(path);
-
- const output = createOutput(results, outputMode);
-
- // Testing stdout is out of scope, and doesn't really achieve much besides testing Node,
- // so we will skip this chunk of the code.
- /* istanbul ignore if */
- if (outputPath === 'stdout') {
- return writeToStdout(output).then(_ => resolve(results));
- }
-
- return writeFile(outputPath, output, outputMode).then(_ => {
- resolve(results);
- }).catch(err => reject(err));
- });
-}
-
-export {
- checkOutputMode,
- checkOutputPath,
- createOutput,
- write,
- OUTPUT_MODE
-}
diff --git a/lighthouse-cli/test/cli/printer-test.js b/lighthouse-cli/test/cli/printer-test.js
index aef39ef602c5..76c24fd7226f 100644
--- a/lighthouse-cli/test/cli/printer-test.js
+++ b/lighthouse-cli/test/cli/printer-test.js
@@ -16,7 +16,7 @@
'use strict';
-const Printer = require('../../out/printer.js');
+const Printer = require('../../js/printer.js');
const assert = require('assert');
const fs = require('fs');
const sampleResults = require('../fixtures/sample.json');
diff --git a/lighthouse-cli/tsconfig.json b/lighthouse-cli/tsconfig.json
deleted file mode 100644
index b192ffbf00b6..000000000000
--- a/lighthouse-cli/tsconfig.json
+++ /dev/null
@@ -1,21 +0,0 @@
-{
- "compilerOptions": {
- "module": "commonjs",
- "target": "es6",
-
- "noImplicitAny": false,
- "sourceMap": true,
- "declaration": true,
- "declarationDir": "dist",
- "outDir": "out"
- },
- "exclude": [
- "node_modules",
- "typings"
- ],
- "include": [
- "*.ts"
- ]
-
-
-}