Files
node/test/parallel/test-module-relative-resolve-cache.js
T
Samuel Attard 471fe813bb module: avoid allocating a cache key string for every require()
The relative resolve cache was keyed by a concatenated
${parent.path}\x00${request} string, allocating a new key for every
require() call including fully cached ones. Key the cache by the
parent directory first (a Map keyed by the already-retained
module.path string) and then by the request (a dictionary object,
whose property access internalizes dynamically-constructed
specifiers). Faster on every measured workload shape and slightly
smaller in memory, since the concatenated keys are no longer
retained.

Signed-off-by: Sam Attard <sattard@anthropic.com>
PR-URL: https://github.com/nodejs/node/pull/63884
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Trivikram Kamat <trivikr.dev@gmail.com>
2026-09-28 23:30:13 +00:00

59 lines
1.8 KiB
JavaScript

'use strict';
// Checks that Module._load() only creates a relative resolve cache entry for
// the parent directory when there is a resolved filename to store in it.
require('../common');
const assert = require('assert');
const fs = require('fs');
const path = require('path');
const Module = require('module');
const tmpdir = require('../common/tmpdir');
tmpdir.refresh();
let uniqueId = 0;
function createParent() {
const dir = tmpdir.resolve(`dir-${uniqueId++}`);
fs.mkdirSync(dir);
const parent = new Module(path.join(dir, 'parent.js'));
parent.filename = parent.id;
parent.paths = Module._nodeModulePaths(dir);
let pathReads = 0;
Object.defineProperty(parent, 'path', {
__proto__: null,
get() {
pathReads++;
return dir;
},
});
return { dir, parent, getPathReads: () => pathReads };
}
// Builtins return before the relative resolve cache is populated, so the
// parent directory should only be looked up, not also used to create a bucket.
for (const request of ['node:path', 'path']) {
const { parent, getPathReads } = createParent();
assert.strictEqual(Module._load(request, parent, false), path);
assert.strictEqual(getPathReads(), 1);
}
// Same for resolutions that throw.
{
const { parent, getPathReads } = createParent();
assert.throws(() => Module._load('./missing', parent, false),
{ code: 'MODULE_NOT_FOUND' });
assert.strictEqual(getPathReads(), 1);
}
// A successful load populates the cache, and the second load hits it.
{
const { dir, parent, getPathReads } = createParent();
fs.writeFileSync(path.join(dir, 'child.js'), 'module.exports = {};');
const first = Module._load('./child', parent, false);
assert.strictEqual(getPathReads(), 1);
assert.strictEqual(Module._load('./child', parent, false), first);
assert.strictEqual(getPathReads(), 2);
}