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