跳到主要内容

Module._findPath 源码解析

const trailingSlashRegex = /(?:^|\/)\.?\.$/;
Module._findPath = function (request, paths, isMain) {
const absoluteRequest = path.isAbsolute(request);
if (absoluteRequest) {
paths = [""];
} else if (!paths || paths.length === 0) {
return false;
}

const cacheKey = request + "\x00" + ArrayPrototypeJoin(paths, "\x00");
const entry = Module._pathCache[cacheKey];
if (entry) return entry;

let exts;
let trailingSlash =
request.length > 0 &&
StringPrototypeCharCodeAt(request, request.length - 1) ===
CHAR_FORWARD_SLASH;
if (!trailingSlash) {
trailingSlash = RegExpPrototypeTest(trailingSlashRegex, request);
}

// For each path
for (let i = 0; i < paths.length; i++) {
// Don't search further if path doesn't exist
const curPath = paths[i];
if (curPath && stat(curPath) < 1) continue;

if (!absoluteRequest) {
const exportsResolved = resolveExports(curPath, request);
if (exportsResolved) return exportsResolved;
}

const basePath = path.resolve(curPath, request);
let filename;

const rc = stat(basePath);
if (!trailingSlash) {
if (rc === 0) {
// File.
if (!isMain) {
if (preserveSymlinks) {
filename = path.resolve(basePath);
} else {
filename = toRealPath(basePath);
}
} else if (preserveSymlinksMain) {
// For the main module, we use the preserveSymlinksMain flag instead
// mainly for backward compatibility, as the preserveSymlinks flag
// historically has not applied to the main module. Most likely this
// was intended to keep .bin/ binaries working, as following those
// symlinks is usually required for the imports in the corresponding
// files to resolve; that said, in some use cases following symlinks
// causes bigger problems which is why the preserveSymlinksMain option
// is needed.
filename = path.resolve(basePath);
} else {
filename = toRealPath(basePath);
}
}

if (!filename) {
// Try it with each of the extensions
if (exts === undefined) exts = ObjectKeys(Module._extensions);
filename = tryExtensions(basePath, exts, isMain);
}
}

if (!filename && rc === 1) {
// Directory.
// try it with each of the extensions at "index"
if (exts === undefined) exts = ObjectKeys(Module._extensions);
filename = tryPackage(basePath, exts, isMain, request);
}

if (filename) {
Module._pathCache[cacheKey] = filename;
return filename;
}
}

return false;
};

执行流程

1. 判断是否为绝对路径

const absoluteRequest = path.isAbsolute(request);
if (absoluteRequest) {
paths = [""];
} else if (!paths || paths.length === 0) {
return false;
}
  • 判断是否为绝对路径,如果是绝对路径,paths 赋值为 ['']
  • 如果 paths 不存在,直接返回 false

2. 查找缓存 key

const cacheKey = request + "\x00" + ArrayPrototypeJoin(paths, "\x00");
const entry = Module._pathCache[cacheKey];
if (entry) return entry;
  • 通过 '\x00' 生成缓存 key
    • '\x00' 表示一个空的字符串
    • 后续可以根据 '\x00' 进行 split 分割 cacheKey.split('\x00')
  • 查找缓存 key
    • 到 _pathCache 中查找缓存 key 是否存在
    • 存在的话直接返回地址

3. 查看当前路径是否以反斜杠结尾

const trailingSlashRegex = /(?:^|\/)\.?\.$/;

let trailingSlash =
request.length > 0 &&
request.charCodeAt(request.length - 1) === CHAR_FORWARD_SLASH;
if (!trailingSlash) {
trailingSlash = RegExpPrototypeTest(trailingSlashRegex, request);
}
  • 查看当前路径是否以反斜杠结尾
  • 如果不是以反斜杠结尾的话,通过正则匹配结尾是否是一个相对路径
    • 匹配最终结尾数据是否是 /.. /. .. . 这些形式

4. 遍历 paths

for (let i = 0; i < paths.length; i++) {...}

4.1 判断当前路径是否是文件夹

const curPath = paths[i];
if (curPath && stat(curPath) < 1) continue;
  • stat() 方法返回值
    • 1:文件夹
    • 0:文件
    • -2:不存在
  • 如果当前路径是文件夹的话,继续执行

4.2 组合路径

if (!absoluteRequest) {
const exportsResolved = resolveExports(curPath, request);
if (exportsResolved) return exportsResolved;
}
  • 如果不是绝对路径的话
  • 通过 resolveExports() 方法将路径进行组合
  • 如果存在组合路径,进行返回

4.3 判断文件是否存在

const basePath = path.resolve(curPath, request);
let filename;

// 判断文件是否存在
const rc = stat(basePath);
  • 通过 stat 方法获取路径文件状态

4.4 判断文件状态

// 路径是否不以反斜杠结尾
if (!trailingSlash) {
// 是否是一个文件
if (rc === 0) {
// File.
if (!isMain) {
// 是否为 main
// 是否阻止做超链接
if (preserveSymlinks) {
filename = path.resolve(basePath);
} else {
// !!!软连接生成最终路径
filename = toRealPath(basePath);
}
} else if (preserveSymlinksMain) {
// For the main module, we use the preserveSymlinksMain flag instead
// mainly for backward compatibility, as the preserveSymlinks flag
// historically has not applied to the main module. Most likely this
// was intended to keep .bin/ binaries working, as following those
// symlinks is usually required for the imports in the corresponding
// files to resolve; that said, in some use cases following symlinks
// causes bigger problems which is why the preserveSymlinksMain option
// is needed.
filename = path.resolve(basePath);
} else {
filename = toRealPath(basePath);
}
}

if (!filename) {
// Try it with each of the extensions
if (exts === undefined) exts = ObjectKeys(Module._extensions);
filename = tryExtensions(basePath, exts, isMain);
}
}
  • 通过 toRealPath 将软连接生成最终路径

    function toRealPath(requestPath) {
    return fs.realpathSync(requestPath, {
    [internalFS.realpathCacheKey]: realpathCache,
    });
    }
    • toRealPath 实际调用 fs.realpathSync 进行功能执行
    • 传递当前路径和已经做过判断的路径缓存 realpathCache