解析算法规范


【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 内置模块名称,则
    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. resolved 成为 PACKAGE_TARGET_RESOLVE(packageURL, mainExport, null, false, conditions) 的结果。
      2. 如果 resolved 不是 nullundefined,则返回 resolved
  3. 否则,如果 exports 是一个对象,并且 exports 的所有键都以 "." 开头,那么 1.让 matchKey 等于字符串 "./"subpath 的连接。
    1. resolvedPACKAGE_IMPORTS_EXPORTS_RESOLVE(matchKey, exports, packageURL, false, conditions) 的结果。
    2. 如果 resolved 不是 nullundefined,则返回 resolved
  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. 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 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).
  2. 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.
  3. 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).
  4. Return null.

模式_键_比较(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, 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: resolvedTarget is contained in packageURL.
    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 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, 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. 返回 "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. 如果启用了 --experimental-wasm-modules 并且 url 对应的文件包含 WebAssembly 模块的头信息,则
        1. 返回 "wasm"
      2. 否则,
        1. 返回 "module"
    2. 返回 undefined
  8. 否则,
    1. 返回 undefined

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