全部一起


🌐 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:

require(X) 来自路径 Y 的模

1. 如果 X 是核心模块,a. 返回核心模块 b. 停止
2. 如果 X 以 '/' 开头  a. 将 Y 设置为文件系统根目录
3. 如果 X 以 './' 或 '/' 或 '../' 开头:
a. LOAD_AS_FILE(Y + X)
b. LOAD_AS_DIRECTORY(Y + X)
c. 抛出 "未找到"
4. 如果 X 以 '#' 开头   a. LOAD_PACKAGE_IMPORTS(X, dirname(Y))
5. LOAD_PACKAGE_SELF(X, dirname(Y))
6. LOAD_NODE_MODULES(X, dirname(Y))
7. 抛出“未找到”

LOAD_AS_FILE(X)

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

LOAD_INDEX(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_AS_DIRECTORY(X)

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

LOAD_NODE_MODULES(X, START)

1. let DIRS = NODE_MODULES_PATHS(START)
2. 对于 DIRS 中的每个 DIR:
   a. LOAD_PACKAGE_EXPORTS(X, DIR)
   b. LOAD_AS_FILE(DIR/X)
   c. LOAD_AS_DIRECTORY(DIR/X)

NODE_MODULES_PATHS(START)

1. let PARTS = path split(START)
2. let I = PARTS 的数量 - 1
3. let 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

LOAD_PACKAGE_IMPORTS(X, DIR)

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

LOAD_PACKAGE_EXPORTS(X, DIR)

1. 尝试将 X 解释为 NAME 和 SUBPATH 的组合,其中名称可能有 @scope/ 前缀,子路径以斜杠开头(`/`)。
2. 如果 X 不符合该模式,或者 DIR/NAME/package.json 不是文件,则返回。
3. 解析 DIR/NAME/package.json,并查找“exports”字段。
4. 如果“exports”为 null 或未定义,则返回。
5. let 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. let 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. let RESOLVED_PATH = fileURLToPath(MATCH)
2. 如果 RESOLVED_PATH 处的文件存在,则以其扩展名对应的格式加载 RESOLVED_PATH。停止
3. 抛出“未找到”