解析器算法规范


【Resolver 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(specifierparentURLdefaultConditions) 结果的解构值。
  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. resolved 作为模块格式加载,format

PACKAGE_RESOLVE(packageSpecifierparentURL)

  1. packageName未定义
  2. 如果 packageSpecifier 是空字符串,则
    1. 抛出一个 无效模块说明符 错误。 3.如果 packageSpecifier 是 Node.js 内置模块名称,则
    2. 返回字符串 "node:"packageSpecifier 连接后的结果。 4.如果 packageSpecifier 不以 "@" 开头,则
    3. packageName 设置为 packageSpecifier 的子字符串,直到第一个 "/" 分隔符或字符串末尾。
  3. 否则,
    1. 如果 packageSpecifier 不包含 "/" 分隔符,则
      1. 抛出一个 无效模块说明符 错误。
    2. packageName 设置为 packageSpecifier 的子字符串直到第二个 "/" 分隔符或字符串末尾。
  4. 如果 packageName"." 开头或包含 """%",那么
    1. 抛出一个 无效模块说明符 错误。
  5. packageSubpath"."packageSpecifierpackageName 长度位置开始的子字符串连接而成。
  6. 如果 packageSubpath"/" 结尾,那么
    1. 抛出一个 无效模块说明符 错误。
  7. selfUrlPACKAGE_SELF_RESOLVE(packageName, packageSubpath, parentURL) 的结果。
  8. 如果 selfUrl 不是 undefined,则返回 selfUrl
  9. parentURL 不是文件系统根目录时,
    1. packageURL 为以 parentURL 为基准、将 "node_modules/"packageSpecifier 连接后的 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. 否则,n> 1. 返回 packageURLpackageSubpath 的 URL 解析。
  10. 抛出 模块未找到 错误。

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)

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

PACKAGE_IMPORTS_RESOLVE(specifierparentURLconditions)

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

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

  1. If matchKey is a key of matchObj and does not end in "/" or contain "*", then
    1. Let target be the value of matchObj[matchKey].
    2. Let resolved be the result of PACKAGE_TARGET_RESOLVE( packageURL, target, "", false, isImports, conditions).
    3. Return the object { resolved, exact: true }.
  2. Let expansionKeys be the list of keys of matchObj either ending in "/" or containing only a single "*", sorted by the sorting function PATTERN_KEY_COMPARE which orders in descending order of specificity.
  3. For each key expansionKey in expansionKeys, do
    1. Let patternBase be null.
    2. If expansionKey contains "*", set patternBase to the substring of expansionKey up to but excluding the first "*" character.
    3. If patternBase is not null and matchKey starts with but is not equal to patternBase, then
      1. If matchKey ends with "/", throw an Invalid Module Specifier error.
      2. Let patternTrailer be the substring of expansionKey from the index after the first "*" character.
      3. 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 subpath 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. Let resolved be the result of PACKAGE_TARGET_RESOLVE( packageURL, target, subpath, true, isImports, conditions).
        4. Return the object { resolved, exact: true }.
    4. Otherwise if patternBase is null and matchKey starts with expansionKey, then
      1. Let target be the value of matchObj[expansionKey].
      2. Let subpath be the substring of matchKey starting at the index of the length of expansionKey.
      3. Let resolved be the result of PACKAGE_TARGET_RESOLVE( packageURL, target, subpath, false, isImports, conditions).
      4. Return the object { resolved, exact: false }.
  4. Return the object { resolved: null, exact: true }.

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

  1. 断言:keyA"/" 结尾或仅包含一个 "*"
  2. 断言:keyB"/" 结尾或仅包含一个 "*"
  3. baseLengthAkeyA"*" 的索引加一(如果 keyA 包含 "*"),否则为 keyA 的长度。
  4. baseLengthBkeyB"*" 的索引加一(如果 keyB 包含 "*"),否则为 keyB 的长度。
  5. 如果 baseLengthA 大于 baseLengthB,返回 -1。
  6. 如果 baseLengthB 大于 baseLengthA,返回 1。
  7. 如果 keyA 不包含 "*",返回 1。
  8. 如果 keyB 不包含 "*",返回 -1。
  9. 如果 keyA 的长度大于 keyB,返回 -1。
  10. 如果 keyB 的长度大于 keyA,返回 1。
  11. 返回 0。

PACKAGE_TARGET_RESOLVE(packageURL, target, subpath, pattern, internal, conditions)

  1. If target is a String, then
    1. If pattern is false, subpath has non-zero length and target does not end with "/", throw an Invalid Module Specifier error.
    2. If target does not start with "./", then
      1. If internal is true and target does not start with "../" or "/" and is not a valid URL, then
        1. If pattern is true, then
          1. Return PACKAGE_RESOLVE(target with every instance of "*" replaced by subpath, packageURL + "/").
        2. Return PACKAGE_RESOLVE(target + subpath, packageURL + "/").
      2. Otherwise, throw an Invalid Package Target error.
    3. 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.
    4. Let resolvedTarget be the URL resolution of the concatenation of packageURL and target.
    5. Assert: resolvedTarget is contained in packageURL.
    6. If subpath split on "/" or "\" contains any ".", "..", or "node_modules" segments, case insensitive and including percent encoded variants, throw an Invalid Module Specifier error.
    7. If pattern is true, then
      1. Return the URL resolution of resolvedTarget with every instance of "*" replaced with subpath.
    8. Otherwise,
      1. Return the URL resolution of the concatenation of subpath and resolvedTarget.
  2. Otherwise, if target is a non-null Object, then
    1. If exports 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, subpath, pattern, internal, 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, subpath, pattern, internal, 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. 返回 "module"
  3. 如果 url".cjs" 结尾,
    1. 返回 "commonjs"
  4. 如果 url".json" 结尾,
    1. 返回 "json"
  5. packageURLLOOKUP_PACKAGE_SCOPE(url) 的结果。
  6. pjsonREAD_PACKAGE_JSON(packageURL) 的结果。
  7. 如果 pjson?.type 存在并且为 "module"
    1. 如果 url".js" 结尾,
      1. 返回 "module"
    2. 抛出 不支持的文件扩展名 错误。
  8. 否则,
    1. 抛出 不支持的文件扩展名 错误。

**查找_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 源。