解析算法规范


【Resolution Algorithm Specification】

ESM_RESOLVE(specifierparentURL)

  1. resolved未定义
  2. 如果 specifier 是一个有效的 URL,那么
    1. resolved 设置为解析并重新序列化 specifier 为 URL 的结果。
  3. 否则,如果 specifier"/""./""../" 开头,那么
    1. resolved 设置为相对于 parentURLspecifier 的 URL 解析结果。
  4. 否则,如果 specifier"#" 开头,则
    1. resolved 设置为 PACKAGE_IMPORTS_RESOLVE(specifier, parentURL, defaultConditions) 的结果。
  5. 否则,
    1. 注意:specifier 现在是一个裸规范符。
    2. resolved 设置为> PACKAGE_RESOLVE(specifier, parentURL) 的结果。
  6. format未定义
  7. 如果 resolved 是一个 "file:" URL,那么
    1. 如果 resolved 包含任何 "/""" 的百分比编码(分别为 "%2F""%5C"),那么
      1. 抛出一个 无效模块说明符 错误。
    2. 如果 resolved 处的文件是一个目录,则
      1. 抛出“不支持的目录导入”错误。
    3. 如果 resolved 处的文件不存在,则
      1. 抛出 模块未找到 错误。
    4. resolved 设置为 resolved 的真实路径,同时保持相同的 URL 查询字符串和片段组件。
    5. format 设置为 ESM_FILE_FORMAT(resolved) 的结果。
  8. 否则,
    1. 设置 format 为与 URL resolved 关联的内容类型的模块格式。
  9. formatresolved 返回到加载阶段

PACKAGE_RESOLVE(packageSpecifierparentURL)

  1. packageName未定义.
  2. 如果 packageSpecifier 是空字符串,则
    1. 抛出一个 无效模块说明符 错误。
  3. 如果 packageSpecifier 是 Node.js 内置模块名称,则
    1. 返回字符串 "node:"packageSpecifier 连接后的结果。
  4. 如果 packageSpecifier 不以 "@" 开头,则
    1. packageName 设置为 packageSpecifier 的子字符串,直到第一个 "/" 分隔符或字符串末尾。
  5. 否则,
    1. 如果 packageSpecifier 不包含 "/" 分隔符,则
      1. 抛出一个 无效模块说明符 错误。
    2. packageName 设置为 packageSpecifier 的子字符串直到第二个 "/" 分隔符或字符串末尾。
  6. 如果 packageName"." 开头或包含 """%",那么
    1. 抛出一个 无效的模块说明符 错误。
  7. packageSubpath"."packageSpecifierpackageName 长度位置开始的子字符串连接而成。
  8. selfUrlPACKAGE_SELF_RESOLVE(packageName, packageSubpath, parentURL) 的结果。
  9. 如果 selfUrl 不是 undefined,则返回 selfUrl
  10. 虽然 parentURL 不是文件系统根目录,
    1. packageURL 为以 parentURL 为基准、将 "node_modules/"packageName 连接后的 URL 解析结果。
    2. parentURL 设置为 parentURL 的父文件夹 URL。
    3. 如果 packageURL 处的文件夹不存在,则
      1. 继续下一次循环迭代。
    4. pjsonREAD_PACKAGE_JSON(packageURL) 的结果。
    5. 如果 pjson 不是 null,并且 pjson.exports 不是 nullundefined,那么
      1. 返回 PACKAGE_EXPORTS_RESOLVE(packageURL, packageSubpath, pjson.exports, defaultConditions) 的结果。
    6. 否则,如果 packageSubpath 等于 ".",那么
      1. 如果 pjson.main 是一个字符串,那么
        1. 返回 packageURLmain 的 URL 解析。
    7. 否则,
      1. 返回 packageURLpackageSubpath 的 URL 解析结果。
  11. 抛出 模块未找到 错误。

PACKAGE_SELF_RESOLVE(packageName, packageSubpath, parentURL)

  1. packageURLLOOKUP_PACKAGE_SCOPE(parentURL) 的结果。
  2. 如果 packageURLnull,则
    1. 返回 undefined
  3. pjsonREAD_PACKAGE_JSON(packageURL) 的结果。
  4. 如果 pjsonnull 或者 pjson.exportsnullundefined,则
    1. 返回 undefined
  5. 如果 pjson.name 等于 packageName,则
    1. 返回 PACKAGE_EXPORTS_RESOLVE(packageURL, packageSubpath, pjson.exports, defaultConditions) 的结果。
  6. 否则,返回 undefined

PACKAGE_EXPORTS_RESOLVE(packageURLsubpathexportsconditions)

注意:此函数由 CommonJS 解析算法直接调用。

【Note: This function is directly invoked by the CommonJS resolution algorithm.】

  1. 如果 exports 是一个对象,同时包含一个以 "." 开头的键和一个不以 "." 开头的键,则抛出 Invalid Package Configuration 错误。
  2. 如果 subpath 等于 ".",那么
    1. mainExport未定义
    2. 如果 exports 是字符串或数组,或者是一个不包含任何以 "." 开头的键的对象,那么
      1. mainExport 设置为 exports
    3. 否则,如果 exports 是一个包含 "." 属性的对象,则
      1. mainExport 设置为 exports["."]。
    4. 如果 mainExport 不是 undefined,那么
      1. resolved 成为 PACKAGE_TARGET_RESOLVE(packageURL, mainExport, null, false, conditions) 的结果。
      2. 如果 resolved 不是 nullundefined,则返回 resolved
  3. 否则,如果 exports 是一个对象,并且 exports 的所有键都以 "." 开头,那么
    1. 断言:subpath"./" 开头。
    2. resolvedPACKAGE_IMPORTS_EXPORTS_RESOLVE(subpath, exports, packageURL, false, conditions) 的结果。
    3. 如果 resolved 不是 nullundefined,则返回 resolved
  4. 抛出 Package Path Not Exported 错误。

PACKAGE_IMPORTS_RESOLVE(specifierparentURLconditions)

注意:此函数由 CommonJS 解析算法直接调用。

【Note: This function is directly invoked by the CommonJS resolution algorithm.】

  1. 断言:specifier"#" 开头。
  2. 如果 specifier 恰好等于 "#" 或以 "#/" 开头,则
    1. 抛出 无效模块指定符 错误。
  3. packageURLLOOKUP_PACKAGE_SCOPE(parentURL) 的结果。
  4. 如果 packageURL 不为 null,则
    1. pjsonREAD_PACKAGE_JSON(packageURL) 的结果。
    2. 如果 pjson.imports 是非空对象,则
      1. resolvedPACKAGE_IMPORTS_EXPORTS_RESOLVE( specifier, pjson.imports, packageURL, true, conditions) 的结果。
      2. 如果 resolved 不为 nullundefined,返回 resolved
  5. 抛出 包导入未定义 错误。

PACKAGE_IMPORTS_EXPORTS_RESOLVE(matchKey, matchObj, packageURL, isImports, conditions)

  1. If matchKey ends in "/", then
    1. Throw an Invalid Module Specifier error.
  2. If matchKey is a key of matchObj and does not contain "*", then
    1. Let target be the value of matchObj[matchKey].
    2. Return the result of PACKAGE_TARGET_RESOLVE(packageURL, target, null, isImports, conditions).
  3. Let expansionKeys be the list of keys of matchObj containing only a single "*", sorted by the sorting function PATTERN_KEY_COMPARE which orders in descending order of specificity.
  4. For each key expansionKey in expansionKeys, do
    1. Let patternBase be the substring of expansionKey up to but excluding the first "*" character.
    2. If matchKey starts with but is not equal to patternBase, then
      1. Let patternTrailer be the substring of expansionKey from the index after the first "*" character.
      2. If patternTrailer has zero length, or if matchKey ends with patternTrailer and the length of matchKey is greater than or equal to the length of expansionKey, then
        1. Let target be the value of matchObj[expansionKey].
        2. Let patternMatch be the substring of matchKey starting at the index of the length of patternBase up to the length of matchKey minus the length of patternTrailer.
        3. Return the result of PACKAGE_TARGET_RESOLVE(packageURL, target, patternMatch, isImports, conditions).
  5. Return null.

模式_键_比较(keyA, keyB)

  1. 断言:keyA 仅包含一个 "*"。
  2. 断言:keyB 仅包含一个 "*"。
  3. baseLengthAkeyA 中 "*" 的索引。
  4. baseLengthBkeyB 中 "*" 的索引。
  5. 如果 baseLengthA 大于 baseLengthB,返回 -1。
  6. 如果 baseLengthB 大于 baseLengthA,返回 1。
  7. 如果 keyA 的长度大于 keyB 的长度,返回 -1。
  8. 如果 keyB 的长度大于 keyA 的长度,返回 1。
  9. 返回 0。

PACKAGE_TARGET_RESOLVE(packageURL, target, patternMatch, isImports, conditions)

  1. If target is a String, then
    1. If target does not start with "./", then
      1. If isImports is false, or if target starts with "../" or "/", or if target is a valid URL, then
        1. Throw an Invalid Package Target error.
      2. If patternMatch is a String, then
        1. Return PACKAGE_RESOLVE(target with every instance of "*" replaced by patternMatch, packageURL + "/").
      3. Return PACKAGE_RESOLVE(target, packageURL + "/").
    2. If target split on "/" or "\" contains any "", ".", "..", or "node_modules" segments after the first "." segment, case insensitive and including percent encoded variants, throw an Invalid Package Target error.
    3. Let resolvedTarget be the URL resolution of the concatenation of packageURL and target.
    4. Assert: packageURL is contained in resolvedTarget.
    5. If patternMatch is null, then
      1. Return resolvedTarget.
    6. If patternMatch split on "/" or "\" contains any "", ".", "..", or "node_modules" segments, case insensitive and including percent encoded variants, throw an Invalid Module Specifier error.
    7. Return the URL resolution of resolvedTarget with every instance of "*" replaced with patternMatch.
  2. Otherwise, if target is a non-null Object, then
    1. If target contains any index property keys, as defined in ECMA-262 6.1.7 Array Index, throw an Invalid Package Configuration error.
    2. For each property p of target, in object insertion order as,
      1. If p equals "default" or conditions contains an entry for p, then
        1. Let targetValue be the value of the p property in target.
        2. Let resolved be the result of PACKAGE_TARGET_RESOLVE( packageURL, targetValue, patternMatch, isImports, conditions).
        3. If resolved is equal to undefined, continue the loop.
        4. Return resolved.
    3. Return undefined.
  3. Otherwise, if target is an Array, then
    1. If _target.length is zero, return null.
    2. For each item targetValue in target, do
      1. Let resolved be the result of PACKAGE_TARGET_RESOLVE( packageURL, targetValue, patternMatch, isImports, conditions), continuing the loop on any Invalid Package Target error.
      2. If resolved is undefined, continue the loop.
      3. Return resolved.
    3. Return or throw the last fallback resolution null return or error.
  4. Otherwise, if target is null, return null.
  5. Otherwise throw an Invalid Package Target error.

ESM_文件_格式(url)

  1. 断言:url 对应一个已存在的文件。
  2. 如果 url".mjs" 结尾,那么
    1. 返回 "commonjs"
  3. 如果 url".cjs" 结尾,那么
    1. 返回 "commonjs"
  4. 如果 url".json" 结尾,则
    1. 返回 "json"
  5. 如果 url".wasm" 结尾,则
    1. 返回 "wasm"
  6. 如果启用了 --experimental-addon-modules 并且 url".node" 结尾,则
    1. 返回 "addon"
  7. packageURLLOOKUP_PACKAGE_SCOPE(url) 的结果。
  8. pjsonREAD_PACKAGE_JSON(packageURL) 的结果。
  9. packageTypenull
  10. 如果 pjson?.type"module""commonjs",那么
    1. packageType 设置为 pjson.type
  11. 如果 url".js" 结尾,则
    1. 如果 packageType 不是 null,那么
      1. 返回 packageType
    2. 如果 DETECT_MODULE_SYNTAX(source) 的结果为真,那么
      1. 返回 "commonjs"
    3. 返回 "commonjs"
  12. 如果 url 没有任何扩展名,那么
    1. 如果 packageType"module",并且位于 url 的文件包含 WebAssembly 模块的 "application/wasm" 内容类型头,则
      1. 返回 "wasm"
    2. 如果 packageType 不是 null,那么
      1. 返回 packageType
    3. 如果 DETECT_MODULE_SYNTAX(source) 的结果为真,那么
      1. 返回 "commonjs"。 ...
    4. 返回 "commonjs"
  13. 返回 未定义(将在加载阶段抛出)。

**查找_PACKAGE_SCOPE(url

  1. scopeURLurl
  2. scopeURL 不是文件系统根目录时,
    1. scopeURL 设置为 scopeURL 的父级 URL。
    2. 如果 scopeURL"node_modules" 路径段结束,则返回 null
    3. pjsonURL 为在 scopeURL 内解析出的 "package.json"
    4. 如果 pjsonURL 对应的文件存在,则
      1. 返回 scopeURL
  3. 返回 null

读取_包_JSON(packageURL)

  1. pjsonURLpackageURL"package.json" 的解析结果。
  2. 如果 pjsonURL 处的文件不存在,则
    1. 返回 null
  3. 如果 packageURL 处的文件无法解析为有效的 JSON,则
    1. 抛出 无效的包配置 错误。
  4. 返回 pjsonURL 处文件的解析后的 JSON 源。

DETECT_MODULE_SYNTAX(source)

  1. source 解析为 ECMAScript 模块。
  2. 如果解析成功,则
    1. 如果 source 包含顶层 await、静态 importexport 语句,或 import.meta,则返回 true
    2. 如果 source 包含任何 CommonJS 封装变量(requireexportsmodule__filename__dirname)的顶层词法声明(constletclass),则返回 true
  3. 否则返回 false