Skip to content
Closed
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
module: add extra caching layer
This adds an extra modules caching layer that operates on the parent's
`path` property and the current require argument. That together can
be used as unique identifier to speed up loading the same module more
than once. It is a cache on top of the current modules cache.

It has the nice feature that this cache does not only work in the same
file but it works for the whole current directory. So if the same file
is loaded in any other file from the same directory, it will also hit
this cache instead of having to resolve the file again.

To keep it backwards compatible with the old modules cache, it detects
invalidation of that cache.
  • Loading branch information
BridgeAR committed Mar 31, 2019
commit 9137c56963bee921273522dc91e6c7dd90cbaafa
24 changes: 23 additions & 1 deletion lib/internal/modules/cjs/loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ const {

const isWindows = process.platform === 'win32';

const relativeResolveCache = Object.create(null);

let requireDepth = 0;
let statCache = new Map();
function stat(filename) {
Expand Down Expand Up @@ -598,14 +600,28 @@ Module._resolveLookupPaths = function(request, parent, newReturn) {
// Then have it load the file contents before returning its exports
// object.
Module._load = function(request, parent, isMain) {
let relResolveCacheIdentifier;
if (parent) {
debug('Module._load REQUEST %s parent: %s', request, parent.id);
// Fast path for (lazy loaded) modules in the same directory. The indirect
// caching is required to allow cache invalidation without changing the old
// cache key names.
relResolveCacheIdentifier = `${parent.path}\x00${request}`;
const filename = relativeResolveCache[relResolveCacheIdentifier];
if (filename !== undefined) {
const cachedModule = Module._cache[filename];
if (cachedModule !== undefined) {
updateChildren(parent, cachedModule, true);
return cachedModule.exports;
}
delete relativeResolveCache[relResolveCacheIdentifier];
}
}

const filename = Module._resolveFilename(request, parent, isMain);

const cachedModule = Module._cache[filename];
if (cachedModule) {
if (cachedModule !== undefined) {
updateChildren(parent, cachedModule, true);
return cachedModule.exports;
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could this be done at the Module._resolveFilename level?
A cache key could be something like

cacheKey =
  request + "\0" +
  dirPath + "\0" +
  (isMain ? "1" : "")

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That would not be beneficial as far as I can tell. I would also rather not move any parts around since the loader is highly monkey patched and every subtle change can cause side-effects.

isMain should not have any impact on the key as it's only about the very first require call and that is not required for the cache key.

Expand All @@ -625,6 +641,9 @@ Module._load = function(request, parent, isMain) {
}

Module._cache[filename] = module;
if (parent !== undefined) {
relativeResolveCache[relResolveCacheIdentifier] = filename;
}

let threw = true;
try {
Expand All @@ -633,6 +652,9 @@ Module._load = function(request, parent, isMain) {
} finally {
if (threw) {
delete Module._cache[filename];
if (parent !== undefined) {
delete relativeResolveCache[relResolveCacheIdentifier];
}
}
}

Expand Down