sitespeed.io
Version:
sitespeed.io is an open-source tool for comprehensive web performance analysis, enabling you to test, monitor, and optimize your website’s speed using real browsers in various environments.
279 lines (248 loc) • 9.09 kB
JavaScript
import path from 'node:path';
import { platform } from 'node:os';
import { readFileSync } from 'node:fs';
import yargs from 'yargs';
import { hideBin } from 'yargs/helpers';
import merge from 'lodash.merge';
import set from 'lodash.set';
import get from 'lodash.get';
import { config, globalPluginsToAdd, version } from './configLoader.js';
import { validateInput } from './validate.js';
import { addOptions as addCoreOptions } from './options/core.js';
import { addOptions as addBrowserOptions } from './options/browser.js';
import { addOptions as addVideoOptions } from './options/video.js';
import { addOptions as addFirefoxOptions } from './options/firefox.js';
import { addOptions as addChromeOptions } from './options/chrome.js';
import { addOptions as addEdgeOptions } from './options/edge.js';
import { addOptions as addSafariOptions } from './options/safari.js';
import { addOptions as addAndroidOptions } from './options/android.js';
import { addOptions as addScreenshotOptions } from './options/screenshot.js';
import { addOptions as addGraphiteOptions } from './options/graphite.js';
import { addOptions as addGrafanaOptions } from './options/grafana.js';
import { addOptions as addHtmlOptions } from './options/html.js';
import { addOptions as addSlackOptions } from './options/slack.js';
import { addOptions as addS3Options } from './options/s3.js';
import { addOptions as addGcsOptions } from './options/gcs.js';
import { addOptions as addScpOptions } from './options/scp.js';
import { addOptions as addMatrixOptions } from './options/matrix.js';
import { addOptions as addBudgetOptions } from './options/budget.js';
import { addOptions as addCruxOptions } from './options/crux.js';
import { addOptions as addSustainableOptions } from './options/sustainable.js';
import { addOptions as addCrawlerOptions } from './options/crawler.js';
import { addOptions as addMetricsOptions } from './options/metrics.js';
import { addOptions as addCompareOptions } from './options/compare.js';
import { addOptions as addApiOptions } from './options/api.js';
import { addOptions as addPluginsOptions } from './options/plugins.js';
import { addOptions as addTextOptions } from './options/text.js';
import { registerPluginOptions } from './pluginOptions.js';
import { getURLs, getAliases } from './util.js';
function fixAndroidArgs(args) {
return args.map(arg => (arg === '--android' ? '--android.enabled' : arg));
}
export async function parseCommandLine() {
const fixedArgs = fixAndroidArgs(hideBin(process.argv));
const yargsInstance = yargs(fixedArgs);
yargsInstance
.parserConfiguration({
'camel-case-expansion': false,
'deep-merge-config': true
})
.env('SITESPEED_IO')
.usage('$0 [options] <url>/<file>');
addCoreOptions(yargsInstance);
addBrowserOptions(yargsInstance);
addVideoOptions(yargsInstance);
addFirefoxOptions(yargsInstance);
addChromeOptions(yargsInstance);
addEdgeOptions(yargsInstance);
addSafariOptions(yargsInstance);
addAndroidOptions(yargsInstance);
addScreenshotOptions(yargsInstance);
addGraphiteOptions(yargsInstance);
addGrafanaOptions(yargsInstance);
addHtmlOptions(yargsInstance);
addSlackOptions(yargsInstance);
addS3Options(yargsInstance);
addGcsOptions(yargsInstance);
addScpOptions(yargsInstance);
addMatrixOptions(yargsInstance);
addBudgetOptions(yargsInstance);
addCruxOptions(yargsInstance);
addSustainableOptions(yargsInstance);
addCrawlerOptions(yargsInstance);
addMetricsOptions(yargsInstance);
addCompareOptions(yargsInstance);
addApiOptions(yargsInstance);
addPluginsOptions(yargsInstance);
addTextOptions(yargsInstance);
yargsInstance
.help('h')
.alias('help', 'h')
.config(config)
.hide('disableAPI')
.hide('browsertime.screenshotLCP')
.hide('browsertime.screenshotLS')
.alias('version', 'V')
.version(version);
await registerPluginOptions(yargsInstance, globalPluginsToAdd);
let parsed = yargsInstance
.wrap(yargsInstance.terminalWidth())
// .check(validateInput)
.epilog(
'Read the docs at https://www.sitespeed.io/documentation/sitespeed.io/'
)
.check(validateInput);
const aliases = parsed.getOptions().alias,
argv = parsed.argv;
// aliases are long options -> short option
const aliasLookup = new Map();
for (const [key, value] of Object.entries(aliases)) {
aliasLookup.set(value[0], key);
}
let explicitOptions = yargs(hideBin(process.argv)).argv;
explicitOptions = merge(
explicitOptions,
yargsInstance.getOptions().configObjects[0]
);
explicitOptions = Object.entries(explicitOptions).reduce(
(result, [key, value]) => {
if (aliasLookup.has(key)) {
const fullKey = aliasLookup.get(key);
result = set(result, fullKey, value);
}
result = set(result, key, value);
return result;
},
{}
);
if (argv.config) {
const config = JSON.parse(
await readFileSync(path.resolve(process.cwd(), argv.config))
);
explicitOptions = merge(explicitOptions, config);
}
if (argv.summaryDetail) argv.summary = true;
if (
argv.firefox &&
(argv.firefox.nightly || argv.firefox.beta || argv.firefox.developer)
) {
argv.browsertime.browser = 'firefox';
if (argv.android) {
// TODO add support for Firefox dev
set(
argv,
'browsertime.firefox.android.package',
argv.firefox.nightly ? 'org.mozilla.fenix' : 'org.mozilla.firefox_beta'
);
set(
argv,
'browsertime.firefox.android.activity',
'org.mozilla.fenix.IntentReceiverActivity'
);
}
} else if (argv.safari && argv.safari.ios) {
argv.browsertime.browser = 'safari';
}
if (argv.ios) {
set(argv, 'safari.ios', true);
} else if (argv.android.enabled === true && argv.browser === 'chrome') {
// Default to Chrome Android.
set(
argv,
'browsertime.chrome.android.package',
get(argv, 'browsertime.chrome.android.package', 'com.android.chrome')
);
}
// Always use hash by default when you configure spa
if (argv.spa) {
set(argv, 'useHash', true);
set(argv, 'browsertime.useHash', true);
}
if (
argv.cpu &&
(argv.browsertime.browser === 'chrome' ||
argv.browsertime.browser === 'edge')
) {
set(argv, 'browsertime.chrome.collectLongTasks', true);
set(argv, 'browsertime.chrome.timeline', true);
}
// Enable when we know how much overhead it will add
/*
else if (argv.browsertime.browser === 'firefox') {
set(argv, 'browsertime.firefox.geckoProfiler', true);
}*/
// we missed to populate this to Browsertime in the cli
// so to stay backward compatible we do it manually
if (argv.useHash) {
set(argv, 'browsertime.useHash', true);
}
// Pass on webpagereplay setup to browsertime
if (argv.webpagereplay) {
set(argv, 'browsertime.webpagereplay', true);
}
if (argv.browsertime.docker) {
set(argv, 'browsertime.video', get(argv, 'browsertime.video', true));
set(
argv,
'browsertime.visualMetrics',
get(argv, 'browsertime.visualMetrics', true)
);
}
if (argv.browsertime.safari && argv.browsertime.safari.useSimulator) {
set(argv, 'browsertime.connectivity.engine', 'throttle');
} else if (
(platform() === 'darwin' || platform() === 'linux') &&
!argv.browsertime.android.enabled &&
!argv.browsertime.safari.ios &&
!argv.browsertime.docker &&
get(explicitOptions, 'browsertime.connectivity.engine', '') === '' &&
get(explicitOptions, 'connectivity.engine', '') === ''
) {
set(argv, 'browsertime.connectivity.engine', 'throttle');
}
if (
argv.gcs &&
argv.gcs.bucketname &&
!argv.resultBaseUrl &&
!argv.resultBaseURL
) {
set(
argv,
'resultBaseURL',
'https://storage.googleapis.com/' + argv.gcs.bucketname
);
}
let urlsMetaData = argv.multi
? {}
: getAliases(argv._, argv.urlAlias, argv.groupAlias);
// Copy the alias so it is also used by Browsertime
if (argv.urlAlias) {
// Browsertime has it own way of handling alias
// We could probably hack this better next year
let urls = argv._;
let meta = {};
if (!Array.isArray(argv.urlAlias)) argv.urlAlias = [argv.urlAlias];
for (const [index, url] of urls.entries()) {
meta[url] = argv.urlAlias[index];
}
set(argv, 'browsertime.urlMetaData', meta);
} else if (Object.keys(urlsMetaData).length > 0) {
let meta = {};
for (let key of Object.keys(urlsMetaData)) {
meta[key] = urlsMetaData[key].urlAlias;
}
set(argv, 'browsertime.urlMetaData', meta);
}
// Set the timeouts to a maximum while debugging
if (argv.debug) {
set(argv, 'browsertime.timeouts.pageload', 2_147_483_647);
set(argv, 'browsertime.timeouts.script', 2_147_483_647);
set(argv, 'browsertime.timeouts.pageCompleteCheck', 2_147_483_647);
}
return {
urls: argv.multi ? argv._ : getURLs(argv._),
urlsMetaData,
options: argv,
explicitOptions: explicitOptions
};
}