node模块查找策略
在路径 Y 中引入 X 模块:
-
如果 X 是核心模块
a. 返回核心模块
b. 停止
-
如果 X 以
/
开头a. 将 Y 设置为文件系统根目录
-
如果 X 以
./
或/
或../
开头a. LOAD_AS_FILE(Y + X)
b. LOAD_AS_DIRECTORY(Y + X)
c. THROW “not found”
-
如果 X 以
#
开头a. LOAD_PACKAGE_IMPORTS(X, dirname(Y))
-
LOAD_PACKAGE_SELF(X, dirname(Y))
-
LOAD_NODE_MODULES(X, dirname(Y))
-
抛出 “not found” 错误
LOAD_AS_FILE(X)
-
如果 X 是一个文件,则按照其文件扩展名格式加载 X。 停止
-
如果 X.js 是一个文件
a. 找到距离 X 最近的包作用域 SCOPE。
b. 如果没有找到 scope- MAYBE_DETECT_AND_LOAD(X.js)
c. 如果 SCOPE/package.json 包含
type
字段1. 如果 `type` 字段为 `module` ,则将 X.js 作为 ECMAScript 模块加载。停止。 2. 如果 `type` 字段为 `commonjs` ,则将 X.js 作为 commonjs 模块加载。停止。
d. MAYBE_DETECT_AND_LOAD(X.js)
-
如果 X.json 是一个文件,则将 X.json 加载为一个 JavaScript 对象。停止。
-
如果 X.node 是一个文件,则将 X.node 作为二进制插件加载。停止。
LOAD_INDEX(X)
- 如果 X/index.js 是一个文件
a. 找到距离 X 最近的包作用域 SCOPE。
b. 如果没有找到 scope, 则将 X/index.js 作为 commonjs 模块加载。停止。
c. 如果 SCOPE/package.json 包含type
字段- 如果
type
字段为module
,则将 X/index.js 作为 ECMAScript 模块加载。停止。 - 如果
type
字段为commonjs
,则将 X/index.js 作为 commonjs 模块加载。停止。
- 如果
- 如果 X/index.json 是一个文件,则将 X/index.json 加载为一个 JavaScript 对象。停止。
- 如果 X/index.node 是一个文件,则将 X/index.node 作为二进制插件加载。停止。
LOAD_AS_DIRECTORY(X)
- 如果 X/package.json 是一个文件
a. 解析 X/package.json ,查找main
字段。
b. 如果main
是一个假值,则跳到第 2 步
c. let M = X + (json main field)
d. LOAD_AS_FILE(M)
e. LOAD_INDEX(M)
f. LOAD_INDEX(X) DEPRECATED
g. THROW “not found” - LOAD_INDEX(X)
LOAD_PACKAGE_IMPORTS(X, DIR)
- 找到与 DIR 最近的包作用域 SCOPE。
- 如果没有找到 scope,返回。
- 如果 SCOPE/package.json 中的
imports
为null
或undefined
,则返回。 - let MATCH = PACKAGE_IMPORTS_RESOLVE(X, pathToFileURL(SCOPE), [“node”, “require”])
- RESOLVE_ESM_MATCH(MATCH).
LOAD_PACKAGE_SELF(X, DIR)
- 找到与 DIR 最近的包作用域 SCOPE。
- 如果没有找到 scope,返回。
- 如果 SCOPE/package.json 中的
exports
为null
或undefined
,则返回。 - 如果 SCOPE/package.json 中的
name
不是 X 的第一个部分,则返回。 - let MATCH = PACKAGE_EXPORTS_RESOLVE(pathToFileURL(SCOPE), “.” + X.slice(“name”.length), `package.json` “exports”, [“node”, “require”])
- RESOLVE_ESM_MATCH(MATCH)
LOAD_NODE_MODULES(X, START)
- let DIRS = NODE_MODULES_PATHS(START)
- for each DIR in DIRS:
a. LOAD_PACKAGE_EXPORTS(X, DIR)
b. LOAD_AS_FILE(DIR/X)
c. LOAD_AS_DIRECTORY(DIR/X)
RESOLVE_ESM_MATCH(MATCH)
- let RESOLVED_PATH = fileURLToPath(MATCH)
- 如果 RESOLVED_PATH 中的文件存在,则按其扩展名格式加载 RESOLVED_PATH。停止。
- THROW “not found”
MAYBE_DETECT_AND_LOAD(X)
-
如果 X 解析为 CommonJS 模块,则将 X 加载为 CommonJS 模块。停止。
-
否则,如果启用了
--experimental-require-module
选项,并且 X 的源代码可以被解析为 ECMAScript 模块a. 加载 X 为 ECMAScript 模块。停止。
-
在 1 步中抛出尝试将 X 解析为 CommonJS 时的 SyntaxError,停止。
NODE_MODULES_PATHS(START)
- let PARTS = path split(START)
- let I = count of PARTS - 1
- let DIRS = []
- while I >= 0,
a. if PARTS[I] = “node_modules” CONTINUE
b. DIR = path join(PARTS[0 … I] + “node_modules”)
c. DIRS = DIR + DIRS
d. let I = I - 1 - return DIRS + GLOBAL_FOLDERS
模块查找策略粗略图解
graph TB
A(模块查找策略) --> B{是否以 ./ 或 ../ 开头}
B --是--> C{是否带文件扩展名}
C --是--> D{根据路径查找文件}
C --否--> E{加上 .js 或 .json
后缀查找文件}
D --找到--> I(成功)
D --找不到--> J(失败)
E --找到--> F(成功)
E --找不到--> G{
当成文件夹
查找文件夹内的
package.json 文件
}
G --找到--> H{是否有 main 字段}
H --是--> M{根据 main 字段查找文件}
H --否--> L
M --找到--> N(成功)
M -- 找不到--> O(失败)
G --找不到--> L{
文件夹内是否有
inexd.js 文件
}
L --找到--> P(成功)
L --找不到--> Q(失败)
B --否--> R{是否为核心模块}
R --是--> S(成功)
R --否--> T(进入 node_module 文件夹查找)
T --> E