解析器算法规范
【Resolver Algorithm Specification】
ESM_RESOLVE(specifier,parentURL)
- 令 resolved 为 未定义。
- 如果 specifier 是一个有效的 URL,那么
- 将 resolved 设置为解析并重新序列化 specifier 为 URL 的结果。
- 否则,如果 specifier 以 "/"、"./" 或 "../" 开头,那么
- 将 resolved 设置为相对于 parentURL 的 specifier 的 URL 解析结果。
- 否则,如果 specifier 以 "#" 开头,则
- 将 resolved 设置为 PACKAGE_IMPORTS_RESOLVE(specifier,parentURL,defaultConditions) 结果的解构值。
- 否则,
- 注意:specifier 现在是一个裸规范符。
- 将 resolved 设置为 PACKAGE_RESOLVE(specifier, parentURL) 的结果。
- 让 format 为 未定义。
- 如果 resolved 是一个 "file:" URL,那么
- 如果 resolved 包含任何 "/" 或 "" 的百分比编码(分别为 "%2F" 和 "%5C"),那么
- 抛出一个 无效模块说明符 错误。
- 如果 resolved 处的文件是一个目录,则
- 抛出“不支持的目录导入”错误。
- 如果 resolved 处的文件不存在,则
- 抛出 模块未找到 错误。
- 将 resolved 设置为 resolved 的真实路径,同时保持相同的 URL 查询字符串和片段组件。
- 将 format 设置为 ESM_FILE_FORMAT(resolved) 的结果。
- 否则,
- 设置 format 为与 URL resolved 关联的内容类型的模块格式。
- 将 resolved 作为模块格式加载,format。
PACKAGE_RESOLVE(packageSpecifier,parentURL)
- 让 packageName 为 未定义。
- 如果 packageSpecifier 是空字符串,则
- 抛出一个 无效模块说明符 错误。 3.如果 packageSpecifier 是 Node.js 内置模块名称,则
- 返回字符串 "node:" 与 packageSpecifier 连接后的结果。 4.如果 packageSpecifier 不以 "@" 开头,则
- 将 packageName 设置为 packageSpecifier 的子字符串,直到第一个 "/" 分隔符或字符串末尾。
- 否则,
- 如果 packageSpecifier 不包含 "/" 分隔符,则
- 抛出一个 无效模块说明符 错误。
- 将 packageName 设置为 packageSpecifier 的子字符串直到第二个 "/" 分隔符或字符串末尾。
- 如果 packageName 以 "." 开头或包含 "" 或 "%",那么
- 抛出一个 无效模块说明符 错误。
- 设 packageSubpath 为 "." 与 packageSpecifier 从 packageName 长度位置开始的子字符串连接而成。
- 如果 packageSubpath 以 "/" 结尾,那么
- 抛出一个 无效模块说明符 错误。
- 设 selfUrl 为 PACKAGE_SELF_RESOLVE(packageName, packageSubpath, parentURL) 的结果。
- 如果 selfUrl 不是 undefined,则返回 selfUrl。
- 当 parentURL 不是文件系统根目录时,
- 设 packageURL 为以 parentURL 为基准、将 "node_modules/" 与 packageSpecifier 连接后的 URL 解析结果。
- 将 parentURL 设置为 parentURL 的父文件夹 URL。
- 如果 packageURL 处的文件夹不存在,则
- 继续下一次循环迭代。
- 设 pjson 为 READ_PACKAGE_JSON(packageURL) 的结果。
- 如果 pjson 不是 null,并且 pjson.exports 不是 null 或 undefined,那么
- 返回 PACKAGE_EXPORTS_RESOLVE(packageURL, packageSubpath, pjson.exports, defaultConditions) 的结果。
- 否则,如果 packageSubpath 等于 ".",那么
- 如果 pjson.main 是一个字符串,那么
- 返回 packageURL 中 main 的 URL 解析。
- 否则,n> 1. 返回 packageURL 中 packageSubpath 的 URL 解析。
- 抛出 模块未找到 错误。
PACKAGE_SELF_RESOLVE(packageName, packageSubpath, parentURL)
- 设 packageURL 为 LOOKUP_PACKAGE_SCOPE(parentURL) 的结果。
- 如果 packageURL 为 null,则
- 返回 undefined。
- 设 pjson 为 READ_PACKAGE_JSON(packageURL) 的结果。
- 如果 pjson 为 null 或者 pjson.exports 为 null 或 undefined,则
- 返回 undefined。
- 如果 pjson.name 等于 packageName,则
- 返回 PACKAGE_EXPORTS_RESOLVE(packageURL, packageSubpath, pjson.exports, defaultConditions) 的结果的 已解析 解构值。
- 否则,返回 undefined。
PACKAGE_EXPORTS_RESOLVE(packageURL,subpath,exports,conditions)
- 如果 exports 是一个对象,同时包含一个以 "." 开头的键和一个不以 "." 开头的键,则抛出 Invalid Package Configuration 错误。
- 如果 subpath 等于 ".",那么
- 让 mainExport 为 未定义。
- 如果 exports 是字符串或数组,或者是一个不包含任何以 "." 开头的键的对象,那么
- 将 mainExport 设置为 exports。
- 否则,如果 exports 是一个包含 "." 属性的对象,则
- 将 mainExport 设置为 exports["."]。
- 如果 mainExport 不是 undefined,那么
- 令 resolved 为 PACKAGE_TARGET_RESOLVE(packageURL, mainExport, "", false, false, conditions) 的结果。
- 如果 resolved 不是 null 或 undefined,那么
- 返回 resolved。
- 否则,如果 exports 是一个对象,并且 exports 的所有键都以 "." 开头,那么 1.让 matchKey 等于字符串 "./" 与 subpath 的连接。
- 令 resolvedMatch 为 PACKAGE_IMPORTS_EXPORTS_RESOLVE(matchKey, exports, packageURL, false, conditions) 的结果。
- 如果 resolvedMatch.resolve 不是 null 或 undefined,那么
- 返回 resolvedMatch。
- 抛出 Package Path Not Exported 错误。
PACKAGE_IMPORTS_RESOLVE(specifier,parentURL,conditions)
- 断言:specifier 以 "#" 开头。
- 如果 specifier 恰好等于 "#" 或以 "#/" 开头,则
- 抛出 无效模块说明符 错误。
- 令 packageURL 为 LOOKUP_PACKAGE_SCOPE(parentURL) 的结果。
- 如果 packageURL 不为 null,则
- 令 pjson 为 READ_PACKAGE_JSON(packageURL) 的结果。
- 如果 pjson.imports 是非空对象,则
- 令 resolvedMatch 为 PACKAGE_IMPORTS_EXPORTS_RESOLVE(specifier, pjson.imports, packageURL, true, conditions) 的结果。
- 如果 resolvedMatch.resolve 不为 null 或 undefined,则
- 返回 resolvedMatch。
- 抛出 未定义的包导入 错误。
PACKAGE_IMPORTS_EXPORTS_RESOLVE(matchKey, matchObj, packageURL, isImports, conditions)
- If matchKey is a key of matchObj and does not end in "/" or contain "*", then
- Let target be the value of matchObj[matchKey].
- Let resolved be the result of PACKAGE_TARGET_RESOLVE( packageURL, target, "", false, isImports, conditions).
- Return the object { resolved, exact: true }.
- 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.
- For each key expansionKey in expansionKeys, do
- Let patternBase be null.
- If expansionKey contains "*", set patternBase to the substring of expansionKey up to but excluding the first "*" character.
- If patternBase is not null and matchKey starts with but is not equal to patternBase, then
- If matchKey ends with "/", throw an Invalid Module Specifier error.
- Let patternTrailer be the substring of expansionKey from the index after the first "*" character.
- 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
- Let target be the value of matchObj[expansionKey].
- 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.
- Let resolved be the result of PACKAGE_TARGET_RESOLVE( packageURL, target, subpath, true, isImports, conditions).
- Return the object { resolved, exact: true }.
- Otherwise if patternBase is null and matchKey starts with expansionKey, then
- Let target be the value of matchObj[expansionKey].
- Let subpath be the substring of matchKey starting at the index of the length of expansionKey.
- Let resolved be the result of PACKAGE_TARGET_RESOLVE( packageURL, target, subpath, false, isImports, conditions).
- Return the object { resolved, exact: false }.
- Return the object { resolved: null, exact: true }.
模式_键_比较(keyA, keyB)
- 断言:keyA 以 "/" 结尾或仅包含一个 "*"。
- 断言:keyB 以 "/" 结尾或仅包含一个 "*"。
- 令 baseLengthA 为 keyA 中 "*" 的索引加一(如果 keyA 包含 "*"),否则为 keyA 的长度。
- 令 baseLengthB 为 keyB 中 "*" 的索引加一(如果 keyB 包含 "*"),否则为 keyB 的长度。
- 如果 baseLengthA 大于 baseLengthB,返回 -1。
- 如果 baseLengthB 大于 baseLengthA,返回 1。
- 如果 keyA 不包含 "*",返回 1。
- 如果 keyB 不包含 "*",返回 -1。
- 如果 keyA 的长度大于 keyB,返回 -1。
- 如果 keyB 的长度大于 keyA,返回 1。
- 返回 0。
PACKAGE_TARGET_RESOLVE(packageURL, target, subpath, pattern, internal, conditions)
- If target is a String, then
- If pattern is false, subpath has non-zero length and target does not end with "/", throw an Invalid Module Specifier error.
- If target does not start with "./", then
- If internal is true and target does not start with "../" or "/" and is not a valid URL, then
- If pattern is true, then
- Return PACKAGE_RESOLVE(target with every instance of "*" replaced by subpath, packageURL + "/").
- Return PACKAGE_RESOLVE(target + subpath, packageURL + "/").
- Otherwise, throw an Invalid Package Target error.
- 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.
- Let resolvedTarget be the URL resolution of the concatenation of packageURL and target.
- Assert: resolvedTarget is contained in packageURL.
- If subpath split on "/" or "\" contains any ".", "..", or "node_modules" segments, case insensitive and including percent encoded variants, throw an Invalid Module Specifier error.
- If pattern is true, then
- Return the URL resolution of resolvedTarget with every instance of "*" replaced with subpath.
- Otherwise,
- Return the URL resolution of the concatenation of subpath and resolvedTarget.
- Otherwise, if target is a non-null Object, then
- If exports contains any index property keys, as defined in ECMA-262 6.1.7 Array Index, throw an Invalid Package Configuration error.
- For each property p of target, in object insertion order as,
- If p equals "default" or conditions contains an entry for p, then
- Let targetValue be the value of the p property in target.
- Let resolved be the result of PACKAGE_TARGET_RESOLVE( packageURL, targetValue, subpath, pattern, internal, conditions).
- If resolved is equal to undefined, continue the loop.
- Return resolved.
- Return undefined.
- Otherwise, if target is an Array, then
- If _target.length is zero, return null.
- For each item targetValue in target, do
- Let resolved be the result of PACKAGE_TARGET_RESOLVE( packageURL, targetValue, subpath, pattern, internal, conditions), continuing the loop on any Invalid Package Target error.
- If resolved is undefined, continue the loop.
- Return resolved.
- Return or throw the last fallback resolution null return or error.
- Otherwise, if target is null, return null.
- Otherwise throw an Invalid Package Target error.
ESM_文件_格式(url)
- 断言:url 对应一个存在的文件。
- 如果 url 以 ".mjs" 结尾,
- 返回 "module"。
- 如果 url 以 ".cjs" 结尾,
- 返回 "commonjs"。
- 如果 url 以 ".json" 结尾,
- 返回 "json"。
- 令 packageURL 为 LOOKUP_PACKAGE_SCOPE(url) 的结果。
- 令 pjson 为 READ_PACKAGE_JSON(packageURL) 的结果。
- 如果 pjson?.type 存在并且为 "module",
- 如果 url 以 ".js" 结尾,
- 返回 "module"。
- 抛出 不支持的文件扩展名 错误。
- 否则,
- 抛出 不支持的文件扩展名 错误。
**查找_PACKAGE_SCOPE(url)
- 令 scopeURL 为 url。
- 当 scopeURL 不是文件系统根目录时,
- 将 scopeURL 设置为 scopeURL 的父级 URL。
- 如果 scopeURL 以 "node_modules" 路径段结束,则返回 null。
- 令 pjsonURL 为在 scopeURL 内解析出的 "package.json"。
- 如果 pjsonURL 对应的文件存在,则
- 返回 scopeURL。
- 返回 null。
读取_包_JSON(packageURL)
- 令 pjsonURL 为 packageURL 中 "package.json" 的解析结果。
- 如果 pjsonURL 处的文件不存在,则
- 返回 null。
- 如果 packageURL 处的文件无法解析为有效的 JSON,则
- 抛出 无效的包配置 错误。
- 返回 pjsonURL 处文件的解析后的 JSON 源。