pagespeed-quest
Version:
A framework for efficient web front-end speed improvement
163 lines • 16.4 kB
JavaScript
#!/usr/bin/env node
import { Command } from 'commander';
import Watch from 'node-watch';
import { Dependency } from './dependency.js';
import { execLighthouse } from './lighthouse.js';
import { execLoadshow } from './loadshow.js';
import { InventoryRepository, withPlaybackProxy, withRecordingProxy } from './index.js';
const dependency = new Dependency();
const main = new Command();
main.option('-i, --inventory <dir>', 'Inventory directory', './inventory');
function registerLighthouseCommands(main) {
const lighthouse = main.command('lighthouse');
lighthouse.description('Run Lighthouse (performance category) via a proxy');
lighthouse.option('-a, --artifacts <dir>', 'Artifacts directory', './artifacts');
lighthouse.option('-q, --quiet', 'Run headless', false);
lighthouse.option('-t, --timeout <ms>', 'Timeout milliseconds', '30000');
const recording = lighthouse.command('recording');
recording.description('Record contents by lighthouse');
recording.option('-d, --device <mobile|desktop>', 'Device type', 'mobile');
recording.argument('<url>', 'Url to measure performance');
recording.action(async (url) => {
const inventoryRepository = new InventoryRepository(main.opts().inventory || './inventory');
const deviceType = recording.opts().device || 'mobile';
const artifactsDir = lighthouse.opts().artifacts || './artifacts';
const quiet = !!lighthouse.opts().quiet;
const timeout = Number(lighthouse.opts().timeout || '30000');
await withRecordingProxy({
entryUrl: url,
deviceType,
inventoryRepository,
}, dependency, async (proxy) => {
await execLighthouse({
url,
proxyPort: proxy.port,
deviceType,
noThrottling: true,
view: false,
artifactsDir,
headless: quiet,
timeout,
}, dependency);
dependency.logger?.info('Lighthouse completed. Saving inventory...');
});
});
const playback = lighthouse.command('playback');
playback.description('Playback contents for lighthouse');
playback.action(async () => {
const inventoryRepository = new InventoryRepository(main.opts().inventory || './inventory');
const artifactsDir = lighthouse.opts().artifacts || './artifacts';
const quiet = !!lighthouse.opts().quiet;
const timeout = Number(lighthouse.opts().timeout || '30000');
await withPlaybackProxy({
inventoryRepository,
}, dependency, async (proxy) => {
await execLighthouse({
url: proxy.entryUrl,
proxyPort: proxy.port,
deviceType: proxy.deviceType,
view: !quiet,
artifactsDir,
headless: quiet,
timeout,
}, dependency);
dependency.logger?.info('Lighthouse completed');
});
});
}
function registerLoadshowCommands(main) {
const loadshow = main.command('loadshow');
loadshow.description('Run loadshow via a proxy');
loadshow.option('-a, --artifacts <dir>', 'Artifacts directory', './artifacts');
loadshow.option('-c, --credit <string>', 'Credit string');
loadshow.option('-t, --timeout <ms>', 'Timeout milliseconds', '30000');
const recording = loadshow.command('recording');
recording.description('Record contents by loadshow');
recording.option('-d, --device <mobile|desktop>', 'Device type', 'mobile');
recording.argument('<url>', 'Url to measure performance');
recording.action(async (url) => {
const inventoryRepository = new InventoryRepository(main.opts().inventory || './inventory');
const deviceType = recording.opts().device || 'mobile';
const artifactsDir = loadshow.opts().artifacts || './artifacts';
const credit = loadshow.opts().credit || '';
const timeout = Number(loadshow.opts().timeout || '30000');
await withRecordingProxy({ entryUrl: url, deviceType, inventoryRepository }, dependency, async (proxy) => {
await execLoadshow({ url, proxyPort: proxy.port, deviceType, artifactsDir, credit, timeout }, dependency);
dependency.logger?.info('Loadshow completed. Saving inventory...');
});
});
const playback = loadshow.command('playback');
playback.description('Playback contents for loadshow');
playback.option('-l, --lighthouse', 'Loadshow with lighthouse throttling');
playback.action(async () => {
const inventoryRepository = new InventoryRepository(main.opts().inventory || './inventory');
const lighthouse = playback.opts().lighthouse;
const artifactsDir = loadshow.opts().artifacts || './artifacts';
const credit = loadshow.opts().credit || '';
const timeout = Number(loadshow.opts().timeout || '30000');
await withPlaybackProxy({
inventoryRepository,
}, dependency, async (proxy) => {
await execLoadshow({
url: proxy.entryUrl,
proxyPort: proxy.port,
deviceType: proxy.deviceType,
syncLighthouseSpec: lighthouse,
artifactsDir,
credit,
timeout,
}, dependency);
dependency.logger?.info('Loadshow completed');
});
});
}
function registerProxyCommands(main) {
const proxy = main.command('proxy');
proxy.option('-p, --port <number>', 'Proxy port', '8080');
proxy.option('-r, --record <url>', 'Recording URL to start the proxy as recording mode', '');
proxy.action(async () => {
const inventoryRepository = new InventoryRepository(main.opts().inventory || './inventory');
const proxyOptions = {
inventoryRepository,
port: Number(proxy.opts().port || '8080'),
};
if (proxy.opts().record) {
const url = proxy.opts().record;
if (!url) {
throw new Error('Recording URL must be specified with --record option.');
}
// Recordingモード
await withRecordingProxy({ ...proxyOptions, entryUrl: url }, dependency, async () => {
dependency.logger?.info(`Recording proxy started on port ${proxyOptions.port}. Press Ctrl+C to stop.`);
// Wait for Ctrl+C signal
return new Promise((resolve) => {
process.on('SIGINT', () => {
dependency.logger?.info('Saving the inventory...');
resolve();
});
});
});
}
else {
// Playbackモード
// eslint-disable-next-line no-constant-condition
while (true) {
await withPlaybackProxy(proxyOptions, dependency, async () => {
const watcher = Watch(inventoryRepository.dirPath, { recursive: true });
return new Promise((ok) => {
watcher.on('change', () => {
watcher.close();
dependency.logger?.info('Inventory changed. Restarting proxy...');
ok();
});
});
});
}
}
});
}
registerLighthouseCommands(main);
registerLoadshowCommands(main);
registerProxyCommands(main);
main.parse(process.argv);
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29tbWFuZC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy9jb21tYW5kLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7QUFFQSxPQUFPLEVBQUUsT0FBTyxFQUFFLE1BQU0sV0FBVyxDQUFBO0FBQ25DLE9BQU8sS0FBSyxNQUFNLFlBQVksQ0FBQTtBQUU5QixPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0saUJBQWlCLENBQUE7QUFDNUMsT0FBTyxFQUFFLGNBQWMsRUFBRSxNQUFNLGlCQUFpQixDQUFBO0FBQ2hELE9BQU8sRUFBRSxZQUFZLEVBQUUsTUFBTSxlQUFlLENBQUE7QUFHNUMsT0FBTyxFQUFFLG1CQUFtQixFQUFnQixpQkFBaUIsRUFBRSxrQkFBa0IsRUFBRSxNQUFNLFlBQVksQ0FBQTtBQUVyRyxNQUFNLFVBQVUsR0FBRyxJQUFJLFVBQVUsRUFBRSxDQUFBO0FBRW5DLE1BQU0sSUFBSSxHQUFHLElBQUksT0FBTyxFQUFFLENBQUE7QUFDMUIsSUFBSSxDQUFDLE1BQU0sQ0FBQyx1QkFBdUIsRUFBRSxxQkFBcUIsRUFBRSxhQUFhLENBQUMsQ0FBQTtBQUUxRSxTQUFTLDBCQUEwQixDQUFDLElBQWE7SUFDL0MsTUFBTSxVQUFVLEdBQUcsSUFBSSxDQUFDLE9BQU8sQ0FBQyxZQUFZLENBQUMsQ0FBQTtJQUM3QyxVQUFVLENBQUMsV0FBVyxDQUFDLG1EQUFtRCxDQUFDLENBQUE7SUFDM0UsVUFBVSxDQUFDLE1BQU0sQ0FBQyx1QkFBdUIsRUFBRSxxQkFBcUIsRUFBRSxhQUFhLENBQUMsQ0FBQTtJQUNoRixVQUFVLENBQUMsTUFBTSxDQUFDLGFBQWEsRUFBRSxjQUFjLEVBQUUsS0FBSyxDQUFDLENBQUE7SUFDdkQsVUFBVSxDQUFDLE1BQU0sQ0FBQyxvQkFBb0IsRUFBRSxzQkFBc0IsRUFBRSxPQUFPLENBQUMsQ0FBQTtJQUV4RSxNQUFNLFNBQVMsR0FBRyxVQUFVLENBQUMsT0FBTyxDQUFDLFdBQVcsQ0FBQyxDQUFBO0lBQ2pELFNB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