browsertime
Version:
Get performance metrics from your web page using Browsertime.
119 lines (110 loc) • 3.52 kB
JavaScript
import path from 'node:path';
import { promisify } from 'node:util';
import {
writeFile as _writeFile,
unlink as _unlink,
readFile as _readFile
} from 'node:fs';
import { pathToFileURL } from 'node:url';
import { getLogger } from '@sitespeed.io/log';
import { toArray, localTime } from '../support/util.js';
const log = getLogger('browsertime');
const writeFile = promisify(_writeFile);
const readFile = promisify(_readFile);
const unlink = promisify(_unlink);
const AsyncFunction = Object.getPrototypeOf(async function () {}).constructor;
async function loadFile(script, options, throwError) {
// if the script is an AsyncFunction, return it.
if (script instanceof AsyncFunction) {
return script;
} else if (!script.endsWith('js') && !script.endsWith('ts')) {
// This is an inline script
return script;
}
if (script.endsWith('ts')) {
const [major, minor] = process.versions.node.split('.').map(Number);
if (major < 22 || (major === 22 && minor < 18)) {
throw new Error(
`TypeScript scripts require Node.js 22.18.0 or later. You are running ${process.versions.node}.`
);
}
}
// If we follow correct naming for modules (.mjs/.mts) or commonjs (.cjs/.cts)
// we just loads it. .js/.ts files is loaded as commonjs if you set
// --cjs true
if (
options.cjs === false ||
script.endsWith('.mjs') ||
script.endsWith('.cjs') ||
script.endsWith('.mts') ||
script.endsWith('.cts')
) {
let myFunction = await import(pathToFileURL(path.resolve(script)));
return myFunction.default ?? myFunction;
} else {
// Hack a way! Try to add a package.json file in the same folder as the
// script file. That way, the .js file will be treated as commonjs =
// working as before we moved Browsertime to module
log.info(
'Will try to load load JS as a commonjs file by adding an emtpy package.json'
);
let createdPackageJson = false;
try {
const packageJson = path.join(
path.dirname(path.resolve(script)),
'package.json'
);
try {
await writeFile(packageJson, '{}');
createdPackageJson = true;
} catch {
log.error(
'Could not create package.json file, the %s file will be treated as a esmodule',
script
);
}
const myFunction = await import(path.resolve(script));
try {
if (createdPackageJson) {
await unlink(packageJson);
}
} catch (error) {
log.error(error);
}
return myFunction.default ?? myFunction;
} catch (error) {
// assume the script is a valid inline script by default
if (throwError) {
log.error(
'Could not parse user script %s with error %s',
script,
error
);
throw error;
}
}
}
}
export async function loadPrePostScripts(scripts, options) {
const readScripts = [];
for (let script of toArray(scripts)) {
const loadedScript = await loadFile(script, options, true);
readScripts.push(loadedScript);
}
return readScripts;
}
export async function loadPageCompleteScript(script) {
if (script && (script.endsWith('js') || script.endsWith('ts'))) {
return readFile(path.resolve(script), 'utf8');
}
return script;
}
export async function loadScript(script, options, throwError) {
if (script) {
return loadFile(script, options, throwError);
}
return script;
}
export function timestamp() {
return localTime();
}