UNPKG

browsertime

Version:

Get performance metrics from your web page using Browsertime.

137 lines (120 loc) 3.85 kB
import path from 'node:path'; import { createHash } from 'node:crypto'; import { gunzip as _gunzip, gzip as _gzip, createGzip } from 'node:zlib'; import { parse } from 'node:url'; import { promisify } from 'node:util'; import { writeFile as _writeFile, readFile as _readFile, unlink as _unlink, mkdir as _mkdir, createReadStream, createWriteStream } from 'node:fs'; import isEmpty from 'lodash.isempty'; import intel from 'intel'; import dayjs from 'dayjs'; const gunzip = promisify(_gunzip); const writeFile = promisify(_writeFile); const readFile = promisify(_readFile); const gzip = promisify(_gzip); const unlink = promisify(_unlink); const mkdir = promisify(_mkdir); const log = intel.getLogger('browsertime'); const defaultDir = 'browsertime-results'; let timestamp = dayjs().format().replaceAll(':', ''); function pathNameFromUrl(url) { const parsedUrl = parse(url), pathSegments = parsedUrl.pathname.split('/'); pathSegments.unshift(parsedUrl.hostname); if (!isEmpty(parsedUrl.search)) { const md5 = createHash('md5'), hash = md5.update(parsedUrl.search).digest('hex').slice(0, 8); pathSegments.push('query-' + hash); } return pathSegments.filter(Boolean).join('-'); } async function streamToString(stream) { return new Promise((resolve, reject) => { const chunks = []; stream.on('error', reject); stream.on('data', chunk => chunks.push(chunk)); stream.on('end', () => resolve(Buffer.concat(chunks))); }); } export class StorageManager { constructor(url, { resultDir, prettyPrint = false } = {}) { this.baseDir = resultDir ? path.resolve(resultDir) : path.resolve(defaultDir, pathNameFromUrl(url), timestamp); this.jsonIndentation = prettyPrint ? 2 : 0; } async createDataDir() { await mkdir(this.baseDir, { recursive: true }); return this.baseDir; } async createSubDataDir(...name) { const dir = path.join(this.baseDir, ...name); await mkdir(dir, { recursive: true }); return dir; } async rm(filename) { return unlink(path.join(this.baseDir, filename)); } async writeData(filename, data, subdir) { let dirPath; dirPath = await (subdir ? this.createSubDataDir(subdir) : this.createDataDir()); const fullPath = path.join(dirPath, filename); return writeFile(fullPath, data).then(() => { return fullPath; }); } async writeJson(filename, json, shouldGzip) { if (shouldGzip) { const data = await gzip(Buffer.from(JSON.stringify(json)), { level: 1 }); return this.writeData(`${filename}.gz`, data); } else { const data = JSON.stringify(json, undefined, this.jsonIndentation); return this.writeData(filename, data); } } async readData(filename, subdir) { let filepath; filepath = subdir ? path.join(this.baseDir, subdir, filename) : path.join(this.baseDir, filename); if (filename.endsWith('.gz')) { const readStream = createReadStream(filepath); const text = await streamToString(readStream); const unzipped = await gunzip(text); return unzipped.toString(); } else { return readFile(filepath); } } async gzip(inputFile, outputFile, removeInput) { const promise = new Promise(function (resolve, reject) { const gzip = createGzip(); const input = createReadStream(inputFile); const out = createWriteStream(outputFile); out.on('finish', function () { if (removeInput) { unlink(inputFile).then(() => resolve()); } else { resolve(); } }); out.on('error', function (e) { log.error('Could not gzip %s to %s', inputFile, outputFile, e); reject(); }); input.pipe(gzip).pipe(out); }); return promise; } get directory() { return this.baseDir; } }