jest-resolve
Version:
101 lines (89 loc) • 3.35 kB
JavaScript
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/**
* Measures what the default resolver costs per resolution once the fs cache
* is warm, by resolving every package in the repo's node_modules from the
* repo root, alternating cjs/esm conditions like a real run does.
*
* The raw `ResolverFactory` number is the floor: the gap between it and
* `Resolver.findNodeModule` is jest-resolve's own overhead. A
* `ResolverFactory` construction (`new` or `cloneWithOptions`) is a ~5µs
* NAPI call - several warm resolutions' worth - so anything that puts one
* back on the per-resolution path shows up here immediately.
*
* Run with: node packages/jest-resolve/__benchmarks__/test.js
* (requires `yarn build:js`)
*/
;
const path = require('node:path');
const fs = require('graceful-fs');
const {ResolverFactory} = require('unrs-resolver');
const Resolver = require('../build').default;
const repoRoot = path.resolve(__dirname, '../../..');
const nodeModules = path.join(repoRoot, 'node_modules');
const specifiers = [];
for (const entry of fs.readdirSync(nodeModules)) {
if (entry.startsWith('.')) continue;
if (entry.startsWith('@')) {
for (const scoped of fs.readdirSync(path.join(nodeModules, entry))) {
specifiers.push(`${entry}/${scoped}`);
}
} else {
specifiers.push(entry);
}
}
const extensions = ['.js', '.mjs', '.cjs', '.jsx', '.ts', '.mts', '.cts'];
const cjsConditions = ['require', 'node', 'default'];
const esmConditions = ['import', 'module-sync', 'default'];
function resolveAllViaFindNodeModule() {
let resolved = 0;
for (const [i, specifier] of specifiers.entries()) {
const result = Resolver.findNodeModule(specifier, {
basedir: repoRoot,
conditions: i % 2 === 0 ? cjsConditions : esmConditions,
extensions,
moduleDirectory: ['node_modules'],
});
if (result) resolved++;
}
return resolved;
}
const cjsFactory = new ResolverFactory({
conditionNames: cjsConditions,
extensions,
modules: ['node_modules'],
});
const esmFactory = cjsFactory.cloneWithOptions({
conditionNames: esmConditions,
extensions,
modules: ['node_modules'],
});
function resolveAllViaRawFactory() {
let resolved = 0;
for (const [i, specifier] of specifiers.entries()) {
const factory = i % 2 === 0 ? cjsFactory : esmFactory;
if (factory.sync(repoRoot, specifier).path) resolved++;
}
return resolved;
}
function bench(label, resolveAll, rounds) {
resolveAll(); // warm the fs cache so the numbers measure CPU, not IO
const start = process.hrtime.bigint();
let resolved = 0;
for (let round = 0; round < rounds; round++) {
resolved = resolveAll();
}
const ns = Number(process.hrtime.bigint() - start);
const perCall = ns / (rounds * specifiers.length);
console.log(
`${label.padEnd(26)} ${(ns / 1e6 / rounds).toFixed(2).padStart(8)} ms/round ${perCall.toFixed(0).padStart(6)} ns/resolution (${resolved}/${specifiers.length} resolved)`,
);
}
const ROUNDS = 30;
console.log(`${specifiers.length} specifiers, ${ROUNDS} rounds\n`);
bench('Resolver.findNodeModule', resolveAllViaFindNodeModule, ROUNDS);
bench('raw ResolverFactory (floor)', resolveAllViaRawFactory, ROUNDS);