解析算法规范
【Resolution 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 关联的内容类型的模块格式。
- 将 format 和 resolved 返回到加载阶段
PACKAGE_RESOLVE(packageSpecifier,parentURL)
- 让 packageName 为 未定义.
- 如果 packageSpecifier 是空字符串,则
- 抛出一个 无效模块说明符 错误。
- 如果 packageSpecifier 是 Node.js 内置模块名称,则
- 返回字符串 "node:" 与 packageSpecifier 连接后的结果。
- 如果 packageSpecifier 不以 "@" 开头,则
- 将 packageName 设置为 packageSpecifier 的子字符串,直到第一个 "/" 分隔符或字符串末尾。
- 否则,
- 如果 packageSpecifier 不包含 "/" 分隔符,则
- 抛出一个 无效模块说明符 错误。
- 将 packageName 设置为 packageSpecifier 的子字符串直到第二个 "/" 分隔符或字符串末尾。
- 如果 packageName 以 "." 开头或包含 "" 或 "%",那么
- 抛出一个 无效的模块说明符 错误。
- 设 packageSubpath 为 "." 与 packageSpecifier 从 packageName 长度位置开始的子字符串连接而成。
- 设 selfUrl 为 PACKAGE_SELF_RESOLVE(packageName, packageSubpath, parentURL) 的结果。
- 如果 selfUrl 不是 undefined,则返回 selfUrl。
- 虽然 parentURL 不是文件系统根目录,
- 设 packageURL 为以 parentURL 为基准、将 "node_modules/" 与 packageName 连接后的 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 解析。
- 否则,
- 返回 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)
注意:此函数由 CommonJS 解析算法直接调用。
【Note: This function is directly invoked by the CommonJS resolution algorithm.】
- 如果 exports 是一个对象,同时包含一个以 "." 开头的键和一个不以 "." 开头的键,则抛出 Invalid Package Configuration 错误。
- 如果 subpath 等于 ".",那么
- 让 mainExport 为 未定义。
- 如果 exports 是字符串或数组,或者是一个不包含任何以 "." 开头的键的对象,那么
- 将 mainExport 设置为 exports。
- 否则,如果 exports 是一个包含 "." 属性的对象,则
- 将 mainExport 设置为 exports["."]。
- 如果 mainExport 不是 undefined,那么
- 让 resolved 成为 PACKAGE_TARGET_RESOLVE(packageURL, mainExport, null, false, conditions) 的结果。
- 如果 resolved 不是 null 或 undefined,则返回 resolved。
- 否则,如果 exports 是一个对象,并且 exports 的所有键都以 "." 开头,那么
- 断言:subpath 以 "./" 开头。
- 令 resolved 为 PACKAGE_IMPORTS_EXPORTS_RESOLVE(subpath, exports, packageURL, false, conditions) 的结果。
- 如果 resolved 不是 null 或 undefined,则返回 resolved。
- 抛出 Package Path Not Exported 错误。
PACKAGE_IMPORTS_RESOLVE(specifier,parentURL,conditions)
注意:此函数由 CommonJS 解析算法直接调用。
【Note: This function is directly invoked by the CommonJS resolution algorithm.】
- 断言:specifier 以 "#" 开头。
- 如果 specifier 恰好等于 "#" 或以 "#/" 开头,则
- 抛出 无效模块指定符 错误。
- 令 packageURL 为 LOOKUP_PACKAGE_SCOPE(parentURL) 的结果。
- 如果 packageURL 不为 null,则
- 令 pjson 为 READ_PACKAGE_JSON(packageURL) 的结果。
- 如果 pjson.imports 是非空对象,则
- 令 resolved 为 PACKAGE_IMPORTS_EXPORTS_RESOLVE( specifier, pjson.imports, packageURL, true, conditions) 的结果。
- 如果 resolved 不为 null 或 undefined,返回 resolved。
- 抛出 包导入未定义 错误。
PACKAGE_IMPORTS_EXPORTS_RESOLVE(matchKey, matchObj, packageURL, isImports, conditions)
- If matchKey ends in "/", then
- Throw an Invalid Module Specifier error.
- If matchKey is a key of matchObj and does not contain "*", then
- Let target be the value of matchObj[matchKey].
- Return the result of PACKAGE_TARGET_RESOLVE(packageURL, target, null, isImports, conditions).
- 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.
- For each key expansionKey in expansionKeys, do
- Let patternBase be the substring of expansionKey up to but excluding the first "*" character.
- If matchKey starts with but is not equal to patternBase, then
- 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 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.
- Return the result of PACKAGE_TARGET_RESOLVE(packageURL, target, patternMatch, isImports, conditions).
- Return null.
模式_键_比较(keyA, keyB)
- 断言:keyA 仅包含一个 "*"。
- 断言:keyB 仅包含一个 "*"。
- 设 baseLengthA 为 keyA 中 "*" 的索引。
- 设 baseLengthB 为 keyB 中 "*" 的索引。
- 如果 baseLengthA 大于 baseLengthB,返回 -1。
- 如果 baseLengthB 大于 baseLengthA,返回 1。
- 如果 keyA 的长度大于 keyB 的长度,返回 -1。
- 如果 keyB 的长度大于 keyA 的长度,返回 1。
- 返回 0。
PACKAGE_TARGET_RESOLVE(packageURL, target, patternMatch, isImports, conditions)
- If target is a String, then
- If target does not start with "./", then
- If isImports is false, or if target starts with "../" or "/", or if target is a valid URL, then
- Throw an Invalid Package Target error.
- If patternMatch is a String, then
- Return PACKAGE_RESOLVE(target with every instance of "*" replaced by patternMatch, packageURL + "/").
- Return PACKAGE_RESOLVE(target, packageURL + "/").
- 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: packageURL is contained in resolvedTarget.
- If patternMatch is null, then
- Return resolvedTarget.
- If patternMatch split on "/" or "\" contains any "", ".", "..", or "node_modules" segments, case insensitive and including percent encoded variants, throw an Invalid Module Specifier error.
- Return the URL resolution of resolvedTarget with every instance of "*" replaced with patternMatch.
- Otherwise, if target is a non-null Object, then
- If target 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, patternMatch, isImports, 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, patternMatch, isImports, 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" 结尾,那么
- 返回 "commonjs"。
- 如果 url 以 ".cjs" 结尾,那么
- 返回 "commonjs"。
- 如果 url 以 ".json" 结尾,则
- 返回 "json"。
- 如果 url 以 ".wasm" 结尾,则
- 返回 "wasm"。
- 如果启用了
--experimental-addon-modules并且 url 以 ".node" 结尾,则
- 返回 "addon"。
- 设 packageURL 为 LOOKUP_PACKAGE_SCOPE(url) 的结果。
- 设 pjson 为 READ_PACKAGE_JSON(packageURL) 的结果。
- 让 packageType 为 null。
- 如果 pjson?.type 是 "module" 或 "commonjs",那么
- 将 packageType 设置为 pjson.type。
- 如果 url 以 ".js" 结尾,则
- 如果 packageType 不是 null,那么
- 返回 packageType。
- 如果 DETECT_MODULE_SYNTAX(source) 的结果为真,那么
- 返回 "commonjs"。
- 返回 "commonjs"。
- 如果 url 没有任何扩展名,那么
- 如果 packageType 是 "module",并且位于 url 的文件包含 WebAssembly 模块的 "application/wasm" 内容类型头,则
- 返回 "wasm"。
- 如果 packageType 不是 null,那么
- 返回 packageType。
- 如果 DETECT_MODULE_SYNTAX(source) 的结果为真,那么
- 返回 "commonjs"。 ...
- 返回 "commonjs"。
- 返回 未定义(将在加载阶段抛出)。
**查找_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 源。
DETECT_MODULE_SYNTAX(source)
- 将 source 解析为 ECMAScript 模块。
- 如果解析成功,则
- 如果 source 包含顶层
await、静态import或export语句,或import.meta,则返回 true。- 如果 source 包含任何 CommonJS 封装变量(
require、exports、module、__filename或__dirname)的顶层词法声明(const、let或class),则返回 true。- 否则返回 false。