react-automation-profiler
Version:
Automated React profiling and data visualization using React's Profiler API, Puppeteer, and D3.
154 lines • 5.72 kB
JavaScript
var __asyncValues = (this && this.__asyncValues) || function (o) {
if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
var m = o[Symbol.asyncIterator], i;
return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i);
function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }
function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
};
var e_1, _a;
import browserSync from 'browser-sync';
import fs from 'fs/promises';
import path from 'path';
import { fileURLToPath } from 'url';
import yargs from 'yargs';
import automate, { OutputType } from './automation.js';
import { MessageTypes, printMessage } from './util.js';
import { hideBin } from 'yargs/helpers';
import { deleteJsonFiles } from './file.util.js';
const { AUTOMATION_START, AUTOMATION_STOP, ERROR } = MessageTypes;
console.log(`
█▀█ ▄▀█ █▀█
█▀▄ █▀█ █▀▀ \x1b[1;32mreact automation profiler\x1b[37m
`);
const options = yargs(hideBin(process.argv))
.usage(`Usage: --averageOf <averageOf> --changeInterval <changeInterval> \
--includeMount <includeMount> --page <page> --port <port> --watch <watch>`)
.option('averageOf', {
describe: 'run each flow n number of times and average out the metrics',
type: 'number',
})
.option('changeInterval', {
describe: 'number of changes before automation is rerun',
type: 'number',
})
.option('includeMount', {
describe: 'includes the initial mount render',
type: 'boolean',
})
.option('page', {
describe: 'page to be tested',
type: 'string',
demandOption: true,
})
.option('port', {
describe: 'port to be used for server',
type: 'number',
})
.option('watch', {
describe: 'generate charts on every new build',
type: 'boolean' || 'string',
})
.option('output', {
describe: 'choose the desired output. Acceptable values: [chart, json]',
type: 'string',
})
.option('headless', {
describe: 'determines if chromium browser window should be visible',
type: 'boolean',
}).argv;
const { averageOf = 1, changeInterval = 1, includeMount = false, page, port = 1235, watch = false, headless = true, output = OutputType.CHART, } = options;
const cwd = path.resolve();
const scriptPath = fileURLToPath(import.meta.url);
const packagePath = `${scriptPath.slice(0, scriptPath.lastIndexOf('/'))}`;
const serverPath = `http://localhost:${port + 1}`;
let changeCount = 0;
let versionCount = 0;
let isServerReady = false;
let timer;
function checkShouldAutomate() {
clearTimeout(timer);
timer = setTimeout(async () => {
changeCount++;
if (changeCount >= changeInterval) {
changeCount = 0;
await handleAutomation();
browserSync.reload();
}
// will re-automate after 10 seconds to give time for the host project
// to rebuild after changes
}, 10000);
}
function getStopMessage(output) {
return output === OutputType.CHART
? `Displaying ${versionCount++ ? `${versionCount} versions of ` : ''}charts at: \x1b[1;32mhttp://localhost:${port}\x1b[37m`
: 'Automation finished';
}
async function handleAutomation() {
for (let automationCount = 1; automationCount <= averageOf; automationCount++) {
printMessage(AUTOMATION_START);
await automate({
automationCount,
averageOf,
cwd,
includeMount,
isServerReady,
packagePath,
serverPort: port + 1,
url: page,
headless,
output,
});
printMessage(AUTOMATION_STOP, { log: getStopMessage(output) });
if (!isServerReady && automationCount === averageOf)
isServerReady = true;
}
}
function setupProxy() {
browserSync.init({
browser: 'google chrome',
logLevel: 'silent',
notify: false,
open: true,
port,
proxy: serverPath,
reloadOnRestart: true,
});
}
// run
/***********************/
// delete any previously cached json files created during old automation runs
await deleteJsonFiles(packagePath);
try {
await handleAutomation();
}
catch (_b) {
process.exit();
}
if (output === OutputType.CHART) {
setupProxy();
if (watch) {
const watchDir = typeof watch === 'string' ? `${cwd}/${watch}` : cwd;
const events = fs.watch(watchDir, { recursive: true }
// node types are saying that fs.watch returns AsyncIterable<string>, but
// it's actually AsyncIterable<{ eventType: string; filename: string }>.
// Have to cast as unknown first to get around this.
);
try {
for (var events_1 = __asyncValues(events), events_1_1; events_1_1 = await events_1.next(), !events_1_1.done;) {
const { eventType, filename } = events_1_1.value;
if (eventType === 'change' && !filename.startsWith('node_modules')) {
checkShouldAutomate();
}
}
}
catch (e_1_1) { e_1 = { error: e_1_1 }; }
finally {
try {
if (events_1_1 && !events_1_1.done && (_a = events_1.return)) await _a.call(events_1);
}
finally { if (e_1) throw e_1.error; }
}
}
}
//# sourceMappingURL=bin.js.map