全部一起


【All together】

要获取在调用 require() 时将被加载的确切文件名,可以使用 require.resolve() 函数。

【To get the exact filename that will be loaded when require() is called, use the require.resolve() function.】

综合以上所有内容,这里是 require() 的高层次算法伪代码:

【Putting together all of the above, here is the high-level algorithm in pseudocode of what require() does:】

从路径 Y 的模块中加载 X
1. 如果 X 是核心模块,
   a. 返回核心模块
   b. 停止
2. 如果 X 以 '/' 开头,
   a. 将 Y 设置为文件系统根目录
3. 如果 X 以 './'、'/' 或 '../' 开头,
   a. 以文件方式加载(Y + X)
   b. 以目录方式加载(Y + X)
   c. 抛出“未找到”异常
4. 如果 X 以 '#' 开头,
   a. 加载包导入(X, dirname(Y))
5. 加载包自身(X, dirname(Y))
6. 加载 Node 模块(X, dirname(Y))
7. 抛出“未找到”异常

LOAD_AS_FILE(X)
1. 如果 X 是一个文件,则按其文件扩展名格式加载 X。停止
2. 如果 X.js 是一个文件,则将 X.js 作为 JavaScript 文本加载。停止
3. 如果 X.json 是一个文件,则将 X.json 解析为 JavaScript 对象。停止
4. 如果 X.node 是一个文件,则将 X.node 作为二进制插件加载。停止

【LOAD_AS_FILE(X)
1. If X is a file, load X as its file extension format. STOP
2. If X.js is a file, load X.js as JavaScript text. STOP
3. If X.json is a file, parse X.json to a JavaScript Object. STOP
4. If X.node is a file, load X.node as binary addon. STOP】

加载索引(X)
1. 如果 X/index.js 是一个文件,加载 X/index.js 作为 JavaScript 文本。停止
2. 如果 X/index.json 是一个文件,将 X/index.json 解析为 JavaScript 对象。停止
3. 如果 X/index.node 是一个文件,加载 X/index.node 作为二进制插件。停止

【LOAD_INDEX(X)
1. If X/index.js is a file, load X/index.js as JavaScript text. STOP
2. If X/index.json is a file, parse X/index.json to a JavaScript object. STOP
3. If X/index.node is a file, load X/index.node as binary addon. STOP】

LOAD_AS_DIRECTORY(X)
1. 如果 X/package.json 是一个文件,
   a. 解析 X/package.json,并查找 "main" 字段。
   b. 如果 "main" 是假值,跳转到 2。
   c. 令 M = X +(json 中的 main 字段)
   d. 以文件方式加载 M(LOAD_AS_FILE(M))
   e. 加载 index(M)(LOAD_INDEX(M))
   f. 加载 index(X)(LOAD_INDEX(X) 已弃用)
   g. 抛出 "未找到"(THROW "not found")
2. 加载 index(X)(LOAD_INDEX(X))

【LOAD_AS_DIRECTORY(X)
1. If X/package.json is a file,
   a. Parse X/package.json, and look for "main" field.
   b. If "main" is a falsy value, GOTO 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"
2. LOAD_INDEX(X)】

加载节点模块(X, START)
1. 让 DIRS = 节点模块路径(START)
2. 对于 DIRS 中的每个 DIR:
   a. 加载包导出(X, DIR)
   b. 作为文件加载(DIR/X)
   c. 作为目录加载(DIR/X)

【LOAD_NODE_MODULES(X, START)
1. let DIRS = NODE_MODULES_PATHS(START)
2. for each DIR in DIRS:
   a. LOAD_PACKAGE_EXPORTS(X, DIR)
   b. LOAD_AS_FILE(DIR/X)
   c. LOAD_AS_DIRECTORY(DIR/X)】

NODE_MODULES_PATHS(START)
1. 设 PARTS = path split(START)
2. 设 I = PARTS 的数量 - 1
3. 设 DIRS = []
4. 当 I >= 0 时,
   a. 如果 PARTS[I] = "node_modules",则跳过
   b. DIR = path join(PARTS[0 .. I] + "node_modules")
   c. DIRS = DIR + DIRS
   d. 设 I = I - 1
5. 返回 DIRS + GLOBAL_FOLDERS

【NODE_MODULES_PATHS(START)
1. let PARTS = path split(START)
2. let I = count of PARTS - 1
3. let DIRS = []
4. 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
5. return DIRS + GLOBAL_FOLDERS】

LOAD_PACKAGE_IMPORTS(X, DIR)
1. 找到距离 DIR 最近的包作用域 SCOPE。
2. 如果未找到作用域,则返回。
3. 如果 SCOPE/package.json 中的“imports”为 null 或 undefined,则返回。
4. 令 MATCH = PACKAGE_IMPORTS_RESOLVE(X, pathToFileURL(SCOPE), ["node", "require"]) 在 ESM 解析器中定义。
5. RESOLVE_ESM_MATCH(MATCH).

LOAD_PACKAGE_EXPORTS(X, DIR)
1. 尝试将 X 解释为 NAME 和 SUBPATH 的组合,其中 NAME 可能带有 @scope/ 前缀,SUBPATH 以斜杠 (`/`) 开头。
2. 如果 X 不符合此模式或者 DIR/NAME/package.json 不是文件,则返回。
3. 解析 DIR/NAME/package.json,并查找 "exports" 字段。
4. 如果 "exports" 为 null 或 undefined,则返回。
5. 让 MATCH = PACKAGE_EXPORTS_RESOLVE(pathToFileURL(DIR/NAME), "." + SUBPATH, `package.json` "exports", ["node", "require"]) 在 ESM 解析器中定义。
6. RESOLVE_ESM_MATCH(MATCH)

LOAD_PACKAGE_SELF(X, DIR)
1. 找到离 DIR 最近的包作用域 SCOPE。
2. 如果没有找到作用域,则返回。
3. 如果 SCOPE/package.json 中的 "exports" 为 null 或 undefined,则返回。
4. 如果 SCOPE/package.json 中的 "name" 不是 X 的第一个部分,则返回。
5. 令 MATCH = PACKAGE_EXPORTS_RESOLVE(pathToFileURL(SCOPE),
   "." + X.slice("name".length), `package.json` 的 "exports", ["node", "require"]) 
   在 ESM 解析器中定义。
6. RESOLVE_ESM_MATCH(MATCH)

RESOLVE_ESM_MATCH(MATCH)
1. 让 { RESOLVED, EXACT } = MATCH
2. 让 RESOLVED_PATH = fileURLToPath(RESOLVED)
3. 如果 EXACT 为真,
   a. 如果 RESOLVED_PATH 处的文件存在,按其扩展名格式加载 RESOLVED_PATH。停止
4. 否则,如果 EXACT 为假,
   a. LOAD_AS_FILE(RESOLVED_PATH)
   b. LOAD_AS_DIRECTORY(RESOLVED_PATH)
5. 抛出 “未找到”