Node.js v18.20.0 文档


模块:ECMAScript 模块#

¥Modules: ECMAScript modules

稳定性: 2 - 稳定的

¥Stability: 2 - Stable

介绍#

¥Introduction

ECMAScript 模块是 官方标准格式,用于打包 JavaScript 代码以供重用。模块使用各种 importexport 语句定义。

¥ECMAScript modules are the official standard format to package JavaScript code for reuse. Modules are defined using a variety of import and export statements.

以下是 ES 模块导出函数的示例:

¥The following example of an ES module exports a function:

// addTwo.mjs
function addTwo(num) {
  return num + 2;
}

export { addTwo }; 

以下是 ES 模块从 addTwo.mjs 导入函数的示例:

¥The following example of an ES module imports the function from addTwo.mjs:

// app.mjs
import { addTwo } from './addTwo.mjs';

// Prints: 6
console.log(addTwo(4)); 

Node.js 完全支持当前指定的 ECMAScript 模块,并提供它们与其原始模块格式 CommonJS 之间的互操作性。

¥Node.js fully supports ECMAScript modules as they are currently specified and provides interoperability between them and its original module format, CommonJS.

启用#

¥Enabling

Node.js 有两个模块系统:CommonJS 模块和 ECMAScript 模块。

¥Node.js has two module systems: CommonJS modules and ECMAScript modules.

作者可以通过 .mjs 文件扩展名、package.json "type" 字段、--input-type 标志或 --experimental-default-type 标志告诉 Node.js 使用 ECMAScript 模块加载器。在这些情况之外,Node.js 将使用 CommonJS 模块加载器。有关详细信息,请参阅 确定模块系统

¥Authors can tell Node.js to use the ECMAScript modules loader via the .mjs file extension, the package.json "type" field, the --input-type flag, or the --experimental-default-type flag. Outside of those cases, Node.js will use the CommonJS module loader. See Determining module system for more details.

#

¥Packages

此部分已移至 模块:包

¥This section was moved to Modules: Packages.

import 说明符#

¥import Specifiers

术语#

¥Terminology

import 语句的说明符是 from 关键字之后的字符串,例如 import { sep } from 'node:path' 中的 'node:path'。说明符也用于 export from 语句,并作为 import() 表达式的参数。

¥The specifier of an import statement is the string after the from keyword, e.g. 'node:path' in import { sep } from 'node:path'. Specifiers are also used in export from statements, and as the argument to an import() expression.

有三种类型的说明符:

¥There are three types of specifiers:

  • 相对说明符,如 './startup.js''../config.mjs'。它们指的是相对于导入文件位置的路径。这些文件扩展名始终是必需的。

    ¥Relative specifiers like './startup.js' or '../config.mjs'. They refer to a path relative to the location of the importing file. The file extension is always necessary for these.

  • 纯说明符,如 'some-package''some-package/shuffle'。它们可以通过包名称来引用包的主要入口点,或者根据示例分别以包名称为前缀的包中的特定功能模块。只有没有 "exports" 字段的包才需要包含文件扩展名。

    ¥Bare specifiers like 'some-package' or 'some-package/shuffle'. They can refer to the main entry point of a package by the package name, or a specific feature module within a package prefixed by the package name as per the examples respectively. Including the file extension is only necessary for packages without an "exports" field.

  • 绝对说明符,如 'file:///opt/nodejs/config.js'。它们直接且明确地引用完整的路径。

    ¥Absolute specifiers like 'file:///opt/nodejs/config.js'. They refer directly and explicitly to a full path.

裸说明符解析由 Node.js 模块解析和加载算法 处理。所有其他说明符解析始终仅使用标准的相对 URL 解析语义进行解析。

¥Bare specifier resolutions are handled by the Node.js module resolution and loading algorithm. All other specifier resolutions are always only resolved with the standard relative URL resolution semantics.

就像在 CommonJS 中一样,包中的模块文件可以通过在包名称后附加路径来访问,除非包的 package.json 包含 "exports" 字段,在这种情况下,包中的文件只能通过 "exports" 中定义的路径访问。

¥Like in CommonJS, module files within packages can be accessed by appending a path to the package name unless the package's package.json contains an "exports" field, in which case files within packages can only be accessed via the paths defined in "exports".

有关适用于 Node.js 模块解析中的裸说明符的这些包解析规则的详细信息,请参阅 包文档

¥For details on these package resolution rules that apply to bare specifiers in the Node.js module resolution, see the packages documentation.

强制文件扩展名#

¥Mandatory file extensions

当使用 import 关键字解析相对或绝对的说明符时,必须提供文件扩展名。还必须完全指定目录索引(例如 './startup/index.js')。

¥A file extension must be provided when using the import keyword to resolve relative or absolute specifiers. Directory indexes (e.g. './startup/index.js') must also be fully specified.

此行为与 import 在浏览器环境中的行为方式相匹配,假设服务器是典型配置的。

¥This behavior matches how import behaves in browser environments, assuming a typically configured server.

URL#

ES 模块被解析并缓存为 URL。这意味着特殊字符必须是 percent-encoded,例如 # 必须是 %23? 必须是 %3F

¥ES modules are resolved and cached as URLs. This means that special characters must be percent-encoded, such as # with %23 and ? with %3F.

支持 file:node:data: URL 协议。除非使用 自定义 HTTPS 加载器,否则 Node.js 本身不支持像 'https://example.com/app.js' 这样的说明符。

¥file:, node:, and data: URL schemes are supported. A specifier like 'https://example.com/app.js' is not supported natively in Node.js unless using a custom HTTPS loader.

file: URL#

如果用于解析模块的 import 说明符具有不同的查询或片段,则会多次加载模块。

¥Modules are loaded multiple times if the import specifier used to resolve them has a different query or fragment.

import './foo.mjs?query=1'; // loads ./foo.mjs with query of "?query=1"
import './foo.mjs?query=2'; // loads ./foo.mjs with query of "?query=2" 

可以通过 ///、或 file:/// 引用卷根。考虑到 URL 和路径解析的差异(比如百分比编码细节),建议导入路径时使用 url.pathToFileURL

¥The volume root may be referenced via /, //, or file:///. Given the differences between URL and path resolution (such as percent encoding details), it is recommended to use url.pathToFileURL when importing a path.

data: 导入#

¥data: imports

data: URL 支持使用以下 MIME 类型导入:

¥data: URLs are supported for importing with the following MIME types:

  • text/javascript 用于 ES 模块

    ¥text/javascript for ES modules

  • application/json 用于 JSON

    ¥application/json for JSON

  • application/wasm 用于 Wasm

    ¥application/wasm for Wasm

import 'data:text/javascript,console.log("hello!");';
import _ from 'data:application/json,"world!"' with { type: 'json' }; 

data: URL 仅解析内置模块的 裸说明符绝对说明符。解析 相对说明符 不起作用,因为 data: 不是 特别计划。例如,尝试从 data:text/javascript,import "./foo"; 加载 ./foo 无法解析,因为 data: URL 没有相对解析的概念。

¥data: URLs only resolve bare specifiers for builtin modules and absolute specifiers. Resolving relative specifiers does not work because data: is not a special scheme. For example, attempting to load ./foo from data:text/javascript,import "./foo"; fails to resolve because there is no concept of relative resolution for data: URLs.

node: 导入#

¥node: imports

支持 node: URL 作为加载 Node.js 内置模块的替代方法。此 URL 协议允许有效的绝对的 URL 字符串引用内置模块。

¥node: URLs are supported as an alternative means to load Node.js builtin modules. This URL scheme allows for builtin modules to be referenced by valid absolute URL strings.

import fs from 'node:fs/promises'; 

导入属性#

¥Import attributes

稳定性: 2 - 稳定的

¥Stability: 2 - Stable

导入属性 是模块导入语句的内联语法,用于与模块说明符一起传递更多信息。

¥Import attributes are an inline syntax for module import statements to pass on more information alongside the module specifier.

import fooData from './foo.json' with { type: 'json' };

const { default: barData } =
  await import('./bar.json', { with: { type: 'json' } }); 

Node.js 仅支持 type 属性,它支持以下值:

¥Node.js only supports the type attribute, for which it supports the following values:

属性 type需要用于
'json'JSON 模块

导入 JSON 模块时,type: 'json' 属性是必需的。

¥The type: 'json' attribute is mandatory when importing JSON modules.

内置模块#

¥Builtin modules

核心模块 提供其公共 API 的命名导出。还提供了默认导出,其是 CommonJS 导出的值。默认导出可用于修改命名导出等。内置模块的命名导出仅通过调用 module.syncBuiltinESMExports() 进行更新。

¥Core modules provide named exports of their public API. A default export is also provided which is the value of the CommonJS exports. The default export can be used for, among other things, modifying the named exports. Named exports of builtin modules are updated only by calling module.syncBuiltinESMExports().

import EventEmitter from 'node:events';
const e = new EventEmitter(); 
import { readFile } from 'node:fs';
readFile('./foo.txt', (err, source) => {
  if (err) {
    console.error(err);
  } else {
    console.log(source);
  }
}); 
import fs, { readFileSync } from 'node:fs';
import { syncBuiltinESMExports } from 'node:module';
import { Buffer } from 'node:buffer';

fs.readFileSync = () => Buffer.from('Hello, ESM');
syncBuiltinESMExports();

fs.readFileSync === readFileSync; 

import() 表达式#

¥import() expressions

CommonJS 和 ES 模块都支持 动态 import()。在 CommonJS 模块中它可以用来加载 ES 模块。

¥Dynamic import() is supported in both CommonJS and ES modules. In CommonJS modules it can be used to load ES modules.

import.meta#

import.meta 元属性是包含以下属性的 Object

¥The import.meta meta property is an Object that contains the following properties.

import.meta.url#

  • <string> 模块的绝对 file: URL。

    ¥<string> The absolute file: URL of the module.

这与提供当前模块文件 URL 的浏览器中的定义完全相同。

¥This is defined exactly the same as it is in browsers providing the URL of the current module file.

这可以启用有用的模式,例如相对文件加载

¥This enables useful patterns such as relative file loading:

import { readFileSync } from 'node:fs';
const buffer = readFileSync(new URL('./data.proto', import.meta.url)); 

import.meta.resolve(specifier)#

稳定性: 1.2 - 发布候选

¥Stability: 1.2 - Release candidate

  • specifier <string> 相对于当前模块解析的模块说明符。

    ¥specifier <string> The module specifier to resolve relative to the current module.

  • 返回:<string> 说明符将解析为的绝对 URL 字符串。

    ¥Returns: <string> The absolute URL string that the specifier would resolve to.

import.meta.resolve 是一个模块相对解析函数,作用域为每个模块,返回 URL 字符串。

¥import.meta.resolve is a module-relative resolution function scoped to each module, returning the URL string.

const dependencyAsset = import.meta.resolve('component-lib/asset.css');
// file:///app/node_modules/component-lib/asset.css
import.meta.resolve('./dep.js');
// file:///app/dep.js 

支持 Node.js 模块解析的所有功能。依赖解析受包内允许的导出解析的约束。

¥All features of the Node.js module resolution are supported. Dependency resolutions are subject to the permitted exports resolutions within the package.

注意事项:

¥Caveats:

  • 这可能会导致同步文件系统操作,从而对性能产生类似于 require.resolve 的影响。

    ¥This can result in synchronous file-system operations, which can impact performance similarly to require.resolve.

  • 此功能在自定义加载程序中不可用(这会造成死锁)。

    ¥This feature is not available within custom loaders (it would create a deadlock).

非标准 API:

¥Non-standard API:

当使用 --experimental-import-meta-resolve 标志时,该函数接受第二个参数:

¥When using the --experimental-import-meta-resolve flag, that function accepts a second argument:

  • parent <string> | <URL> 要解析的可选绝对父模块 URL。默认值:import.meta.url

    ¥parent <string> | <URL> An optional absolute parent module URL to resolve from. Default: import.meta.url

与 CommonJS 的互操作性#

¥Interoperability with CommonJS

import 语句#

¥import statements

import 语句可以引用 ES 模块或 CommonJS 模块。import 语句只允许在 ES 模块中使用,但 CommonJS 支持动态 import() 表达式来加载 ES 模块。

¥An import statement can reference an ES module or a CommonJS module. import statements are permitted only in ES modules, but dynamic import() expressions are supported in CommonJS for loading ES modules.

导入 CommonJS 模块 时,module.exports 对象作为默认导出提供。命名导出可能可用,由静态分析提供,以方便更好的生态系统兼容性。

¥When importing CommonJS modules, the module.exports object is provided as the default export. Named exports may be available, provided by static analysis as a convenience for better ecosystem compatibility.

require#

CommonJS 模块 require 总是将它引用的文件视为 CommonJS。

¥The CommonJS module require always treats the files it references as CommonJS.

不支持使用 require 加载 ES 模块,因为 ES 模块具有异步执行。而是,使用 import() 从 CommonJS 模块加载 ES 模块。

¥Using require to load an ES module is not supported because ES modules have asynchronous execution. Instead, use import() to load an ES module from a CommonJS module.

CommonJS 命名空间#

¥CommonJS Namespaces

CommonJS 模块由可以是任何类型的 module.exports 对象组成。

¥CommonJS modules consist of a module.exports object which can be of any type.

当导入 CommonJS 模块时,可以使用 ES 模块默认导入或其对应的语法糖可靠地导入:

¥When importing a CommonJS module, it can be reliably imported using the ES module default import or its corresponding sugar syntax:

import { default as cjs } from 'cjs';

// The following import statement is "syntax sugar" (equivalent but sweeter)
// for `{ default as cjsSugar }` in the above import statement:
import cjsSugar from 'cjs';

console.log(cjs);
console.log(cjs === cjsSugar);
// Prints:
//   <module.exports>
//   true 

CommonJS 模块的 ECMAScript 模块命名空间表示始终是使用 default 导出键指向 CommonJS module.exports 值的命名空间。

¥The ECMAScript Module Namespace representation of a CommonJS module is always a namespace with a default export key pointing to the CommonJS module.exports value.

当使用 import * as m from 'cjs' 或动态导入时,可以直接观察到此模块命名空间外来对象:

¥This Module Namespace Exotic Object can be directly observed either when using import * as m from 'cjs' or a dynamic import:

import * as m from 'cjs';
console.log(m);
console.log(m === await import('cjs'));
// Prints:
//   [Module] { default: <module.exports> }
//   true 

为了更好地兼容 JS 生态系统中的现有用法,Node.js 还尝试确定每个导入的 CommonJS 模块的 CommonJS 命名导出,以使用静态分析过程将它们作为单独的 ES 模块导出提供。

¥For better compatibility with existing usage in the JS ecosystem, Node.js in addition attempts to determine the CommonJS named exports of every imported CommonJS module to provide them as separate ES module exports using a static analysis process.

例如,考虑编写的 CommonJS 模块:

¥For example, consider a CommonJS module written:

// cjs.cjs
exports.name = 'exported'; 

前面的模块支持 ES 模块中的命名导入:

¥The preceding module supports named imports in ES modules:

import { name } from './cjs.cjs';
console.log(name);
// Prints: 'exported'

import cjs from './cjs.cjs';
console.log(cjs);
// Prints: { name: 'exported' }

import * as m from './cjs.cjs';
console.log(m);
// Prints: [Module] { default: { name: 'exported' }, name: 'exported' } 

从上一个记录模块命名空间外来对象的示例中可以看出,name 导出是从 module.exports 对象复制出来的,并在导入模块时直接设置在 ES 模块命名空间上。

¥As can be seen from the last example of the Module Namespace Exotic Object being logged, the name export is copied off of the module.exports object and set directly on the ES module namespace when the module is imported.

未检测到这些命名导出的实时绑定更新或添加到 module.exports 的新导出。

¥Live binding updates or new exports added to module.exports are not detected for these named exports.

命名导出的检测基于通用语法模式,但并不总是正确地检测命名导出。在这些情况下,使用上述默认导入形式可能是更好的选择。

¥The detection of named exports is based on common syntax patterns but does not always correctly detect named exports. In these cases, using the default import form described above can be a better option.

命名导出检测涵盖了许多常见的导出模式、再导出模式、以及构建工具和转译器输出。有关实现的确切语义,请参阅 cjs-module-lexer

¥Named exports detection covers many common export patterns, reexport patterns and build tool and transpiler outputs. See cjs-module-lexer for the exact semantics implemented.

ES 模块和 CommonJS 的区别#

¥Differences between ES modules and CommonJS

requireexportsmodule.exports#

¥No require, exports, or module.exports

在大多数情况下,可以使用 ES 模块 import 加载 CommonJS 模块。

¥In most cases, the ES module import can be used to load CommonJS modules.

如果需要,可以使用 module.createRequire() 在 ES 模块中构造 require 函数。

¥If needed, a require function can be constructed within an ES module using module.createRequire().

__filename__dirname#

¥No __filename or __dirname

这些 CommonJS 变量在 ES 模块中不可用。

¥These CommonJS variables are not available in ES modules.

__filename__dirname 用例可以通过 import.meta.url 复制。

¥__filename and __dirname use cases can be replicated via import.meta.url.

没有插件加载#

¥No Addon Loading

插件 当前不支持 ES 模块导入。

¥Addons are not currently supported with ES module imports.

它们可以改为加载 module.createRequire()process.dlopen

¥They can instead be loaded with module.createRequire() or process.dlopen.

没有 require.resolve#

¥No require.resolve

相对解析可以通过 new URL('./local', import.meta.url) 处理。

¥Relative resolution can be handled via new URL('./local', import.meta.url).

对于完整的 require.resolve 替代品,有 import.meta.resolve API。

¥For a complete require.resolve replacement, there is the import.meta.resolve API.

也可以使用 module.createRequire()

¥Alternatively module.createRequire() can be used.

没有 NODE_PATH#

¥No NODE_PATH

NODE_PATH 不是解析 import 说明符的一部分。如果需要这种行为,则使用符号链接。

¥NODE_PATH is not part of resolving import specifiers. Please use symlinks if this behavior is desired.

没有 require.extensions#

¥No require.extensions

require.extensions 没有被 import 使用。模块定制钩子可以提供替代品。

¥require.extensions is not used by import. Module customization hooks can provide a replacement.

没有 require.cache#

¥No require.cache

require.cache 没有被 import 使用,因为 ES 模块加载器有自己独立的缓存。

¥require.cache is not used by import as the ES module loader has its own separate cache.

JSON 模块#

¥JSON modules

稳定性: 2 - 稳定的

¥Stability: 2 - Stable

import 可以引用 JSON 文件:

¥JSON files can be referenced by import:

import packageConfig from './package.json' with { type: 'json' }; 

with { type: 'json' } 语法是强制性的;见 导入属性

¥The with { type: 'json' } syntax is mandatory; see Import Attributes.

导入的 JSON 只暴露一个 default 导出。不支持命名导出。在 CommonJS 缓存中创建缓存条目,以避免重复。如果 JSON 模块已经从同一路径导入,则在 CommonJS 中返回相同的对象。

¥The imported JSON only exposes a default export. There is no support for named exports. A cache entry is created in the CommonJS cache to avoid duplication. The same object is returned in CommonJS if the JSON module has already been imported from the same path.

Wasm 模块#

¥Wasm modules

稳定性: 1 - 实验性的

¥Stability: 1 - Experimental

--experimental-wasm-modules 标志下支持导入 WebAssembly 模块,允许将任何 .wasm 文件作为普通模块导入,同时也支持它们的模块导入。

¥Importing WebAssembly modules is supported under the --experimental-wasm-modules flag, allowing any .wasm files to be imported as normal modules while also supporting their module imports.

这种整合是符合 WebAssembly 的 ES 模块集成提案 的。

¥This integration is in line with the ES Module Integration Proposal for WebAssembly.

例如,index.mjs 包含:

¥For example, an index.mjs containing:

import * as M from './module.wasm';
console.log(M); 

在以下条件下执行:

¥executed under:

node --experimental-wasm-modules index.mjs 

将为 module.wasm 的实例化提供导出接口。

¥would provide the exports interface for the instantiation of module.wasm.

顶层 await#

¥Top-level await

await 关键字可以用在 ECMAScript 模块的顶层主体中。

¥The await keyword may be used in the top level body of an ECMAScript module.

假设 a.mjs 具有

¥Assuming an a.mjs with

export const five = await Promise.resolve(5); 

并且 b.mjs 具有

¥And a b.mjs with

import { five } from './a.mjs';

console.log(five); // Logs `5` 
node b.mjs # works 

如果顶层 await 表达式永远无法解析,则 node 进程将退出并返回 13 状态码

¥If a top level await expression never resolves, the node process will exit with a 13 status code.

import { spawn } from 'node:child_process';
import { execPath } from 'node:process';

spawn(execPath, [
  '--input-type=module',
  '--eval',
  // Never-resolving Promise:
  'await new Promise(() => {})',
]).once('exit', (code) => {
  console.log(code); // Logs `13`
}); 

HTTPS 和 HTTP 导入#

¥HTTPS and HTTP imports

稳定性: 1 - 实验性的

¥Stability: 1 - Experimental

--experimental-network-imports 标志下支持使用 https:http: 导入基于网络的模块。这允许类似网络浏览器的导入在 Node.js 中工作,但由于应用稳定性和安全问题在特权环境而不是浏览器沙箱中运行时会有所不同,因此存在一些差异。

¥Importing network based modules using https: and http: is supported under the --experimental-network-imports flag. This allows web browser-like imports to work in Node.js with a few differences due to application stability and security concerns that are different when running in a privileged environment instead of a browser sandbox.

导入仅限于 HTTP/1#

¥Imports are limited to HTTP/1

尚不支持 HTTP/2 和 HTTP/3 的自动协议协商。

¥Automatic protocol negotiation for HTTP/2 and HTTP/3 is not yet supported.

HTTP 仅限于环回地址#

¥HTTP is limited to loopback addresses

http: 易受中间人攻击,不允许用于 IPv4 地址 127.0.0.0/8127.0.0.1127.255.255.255)和 IPv6 地址 ::1 之外的地址。对 http: 的支持旨在用于本地开发。

¥http: is vulnerable to man-in-the-middle attacks and is not allowed to be used for addresses outside of the IPv4 address 127.0.0.0/8 (127.0.0.1 to 127.255.255.255) and the IPv6 address ::1. Support for http: is intended to be used for local development.

身份验证永远不会发送到目标服务器。#

¥Authentication is never sent to the destination server.

AuthorizationCookieProxy-Authorization 标头未发送到服务器。避免在部分导入的 URL 中包含用户信息。正在研究在服务器上安全使用这些的安全模型。

¥Authorization, Cookie, and Proxy-Authorization headers are not sent to the server. Avoid including user info in parts of imported URLs. A security model for safely using these on the server is being worked on.

永远不会在目标服务器上检查 CORS#

¥CORS is never checked on the destination server

CORS 旨在允许服务器将 API 的使用者限制为一组特定的主机。这不受支持,因为它对于基于服务器的实现没有意义。

¥CORS is designed to allow a server to limit the consumers of an API to a specific set of hosts. This is not supported as it does not make sense for a server-based implementation.

无法加载非网络依赖#

¥Cannot load non-network dependencies

这些模块不能访问不超过 http:https: 的其他模块。要在避免安全问题的同时仍然访问本地模块,则传入对本地依赖的引用:

¥These modules cannot access other modules that are not over http: or https:. To still access local modules while avoiding the security concern, pass in references to the local dependencies:

// file.mjs
import worker_threads from 'node:worker_threads';
import { configure, resize } from 'https://example.com/imagelib.mjs';
configure({ worker_threads }); 
// https://example.com/imagelib.mjs
let worker_threads;
export function configure(opts) {
  worker_threads = opts.worker_threads;
}
export function resize(img, size) {
  // Perform resizing in worker_thread to avoid main thread blocking
} 

默认情况下不启用基于网络的加载#

¥Network-based loading is not enabled by default

目前,需要 --experimental-network-imports 标志来启用通过 http:https: 加载资源。将来,将使用不同的机制来执行此操作。需要选择加入以防止不经意间使用可能影响 Node.js 应用可靠性的潜在可变状态的传递依赖。

¥For now, the --experimental-network-imports flag is required to enable loading resources over http: or https:. In the future, a different mechanism will be used to enforce this. Opt-in is required to prevent transitive dependencies inadvertently using potentially mutable state that could affect reliability of Node.js applications.

加载器#

¥Loaders

以前的 Loaders 文档现在位于 模块:定制钩子

¥The former Loaders documentation is now at Modules: Customization hooks.

解析和加载算法#

¥Resolution and loading algorithm

特性#

¥Features

默认解析器具有以下属性:

¥The default resolver has the following properties:

  • ES 模块使用的基于 FileURL 的解析

    ¥FileURL-based resolution as is used by ES modules

  • 相对和绝对的网址解析

    ¥Relative and absolute URL resolution

  • 没有默认的扩展名

    ¥No default extensions

  • 没有主文件夹

    ¥No folder mains

  • 通过 node_modules 进行裸说明符包解析查找

    ¥Bare specifier package resolution lookup through node_modules

  • 不会在未知扩展或协议上失败

    ¥Does not fail on unknown extensions or protocols

  • 可以选择向加载阶段提供格式提示

    ¥Can optionally provide a hint of the format to the loading phase

默认加载器具有以下属性

¥The default loader has the following properties

  • 支持通过 node: URL 加载内置模块

    ¥Support for builtin module loading via node: URLs

  • 支持通过 data: URL 加载 "inline" 模块

    ¥Support for "inline" module loading via data: URLs

  • 支持 file: 模块加载

    ¥Support for file: module loading

  • 在任何其他 URL 协议上失败

    ¥Fails on any other URL protocol

  • 加载 file: 的未知扩展失败(仅支持 .cjs.js.mjs

    ¥Fails on unknown extensions for file: loading (supports only .cjs, .js, and .mjs)

解析算法#

¥Resolution algorithm

加载 ES 模块说明符的算法通过下面的 ESM_RESOLVE 方法给出。它返回相对于 parentURL 的模块说明符的解析 URL。

¥The algorithm to load an ES module specifier is given through the ESM_RESOLVE method below. It returns the resolved URL for a module specifier relative to a parentURL.

解析算法确定模块加载的完整解析 URL 及其建议的模块格式。解析算法不确定是否可以加载解析的 URL 协议,或者是否允许文件扩展名,而是在加载阶段由 Node.js 应用这些验证(例如,如果它被要求加载具有 不是 file:data:node: 的协议,或者如果启用了 --experimental-network-imports,则为 https:)。

¥The resolution algorithm determines the full resolved URL for a module load, along with its suggested module format. The resolution algorithm does not determine whether the resolved URL protocol can be loaded, or whether the file extensions are permitted, instead these validations are applied by Node.js during the load phase (for example, if it was asked to load a URL that has a protocol that is not file:, data:, node:, or if --experimental-network-imports is enabled, https:).

该算法还尝试根据扩展名确定文件的格式(参见下面的 ESM_FILE_FORMAT 算法)。如果它不识别文件扩展名(例如,如果它不是 .mjs.cjs.json),则返回 undefined 格式,这将在加载阶段抛出。

¥The algorithm also tries to determine the format of the file based on the extension (see ESM_FILE_FORMAT algorithm below). If it does not recognize the file extension (eg if it is not .mjs, .cjs, or .json), then a format of undefined is returned, which will throw during the load phase.

ESM_FILE_FORMAT 提供了确定已解析 URL 的模块格式的算法,该算法返回任何文件的唯一模块格式。"module" 格式为 ECMAScript 模块返回,而 "commonjs" 格式用于指示通过旧版 CommonJS 加载器加载。可以在未来的更新中扩展其他格式,例如 "addon"。

¥The algorithm to determine the module format of a resolved URL is provided by ESM_FILE_FORMAT, which returns the unique module format for any file. The "module" format is returned for an ECMAScript Module, while the "commonjs" format is used to indicate loading through the legacy CommonJS loader. Additional formats such as "addon" can be extended in future updates.

在以下算法中,除非另有说明,否则所有子程序错误都将作为这些顶层程序的错误传播。

¥In the following algorithms, all subroutine errors are propagated as errors of these top-level routines unless stated otherwise.

defaultConditions 是条件环境名称数组,["node", "import"]

¥defaultConditions is the conditional environment name array, ["node", "import"].

解析器可能会抛出以下错误:

¥The resolver can throw the following errors:

  • 无效的模块说明符:模块说明符是无效的 URL、包名称或包的子路径说明符。

    ¥Invalid Module Specifier: Module specifier is an invalid URL, package name or package subpath specifier.

  • 无效的包配置:package.json 配置无效或包含无效配置。

    ¥Invalid Package Configuration: package.json configuration is invalid or contains an invalid configuration.

  • 无效的包目标:包导出或导入为包定义了一个目标模块,该模块是无效类型或字符串目标。

    ¥Invalid Package Target: Package exports or imports define a target module for the package that is an invalid type or string target.

  • 未导出的包路径:包导出不为给定模块定义或允许包中的目标子路径。

    ¥Package Path Not Exported: Package exports do not define or permit a target subpath in the package for the given module.

  • 包导入未定义:包导入不定义说明符。

    ¥Package Import Not Defined: Package imports do not define the specifier.

  • 未找到模块:请求的包或模块不存在。

    ¥Module Not Found: The package or module requested does not exist.

  • 不支持的目录导入:解析的路径对应于一个目录,该目录不是模块导入的受支持目标。

    ¥Unsupported Directory Import: The resolved path corresponds to a directory, which is not a supported target for module imports.

解析算法规范#

¥Resolution Algorithm Specification

ESM_RESOLVE(specifier, parentURL)

  1. 让 resolved 为未定义。

    ¥Let resolved be undefined.

  2. 如果说明符是有效的 URL,则

    ¥If specifier is a valid URL, then

    1. 将 resolved 设置为将说明符解析和重新序列化为 URL 的结果。

      ¥Set resolved to the result of parsing and reserializing specifier as a URL.

  3. 否则,如果说明符以 "/"、"./" 或 "../" 开头,则

    ¥Otherwise, if specifier starts with "/", "./", or "../", then

    1. 将 resolved 设置为说明符相对于 parentURL 的 URL 解析。

      ¥Set resolved to the URL resolution of specifier relative to parentURL.

  4. 否则,如果说明符以 "" 开头,则

    ¥Otherwise, if specifier starts with "#", then

    1. 将已解析设置为 PACKAGE_IMPORTS_RESOLVE(specifier,parentURL,defaultConditions) 的结果。

      ¥Set resolved to the result of PACKAGE_IMPORTS_RESOLVE(specifier, parentURL, defaultConditions).

  5. 否则,

    ¥Otherwise,

    1. 注意:说明符现在是一个裸说明符。

      ¥Note: specifier is now a bare specifier.

    2. 设置解析 PACKAGE_RESOLVE(specifier,parentURL) 的结果。

      ¥Set resolved the result of PACKAGE_RESOLVE(specifier, parentURL).

  6. 让格式未定义。

    ¥Let format be undefined.

  7. 如果解析的是 "file:" URL,则

    ¥If resolved is a "file:" URL, then

    1. 如果已解析包含 "/" 或 ""(分别为 "%2F" 和 "%5C")的任何百分比编码,则

      ¥If resolved contains any percent encodings of "/" or "" ("%2F" and "%5C" respectively), then

      1. 抛出无效的模块说明符错误。

        ¥Throw an Invalid Module Specifier error.

    2. 如果解析的文件是一个目录,那么

      ¥If the file at resolved is a directory, then

      1. 引发不支持的目录导入错误。

        ¥Throw an Unsupported Directory Import error.

    3. 如果已解析的文件不存在,则

      ¥If the file at resolved does not exist, then

      1. 抛出模块未找到错误。

        ¥Throw a Module Not Found error.

    4. 设置 resolved 为 resolved 的真实路径,保持相同的 URL querystring 和 fragment 组件。

      ¥Set resolved to the real path of resolved, maintaining the same URL querystring and fragment components.

    5. 将格式设置为 ESM_FILE_FORMAT(resolved) 的结果。

      ¥Set format to the result of ESM_FILE_FORMAT(resolved).

  8. 否则,

    ¥Otherwise,

    1. 设置格式与解析的 URL 关联的内容类型的模块格式。

      ¥Set format the module format of the content type associated with the URL resolved.

  9. 返回格式并解析到加载阶段

    ¥Return format and resolved to the loading phase

PACKAGE_RESOLVE(packageSpecifier, parentURL)

  1. 让 packageName 未定义。

    ¥Let packageName be undefined.

  2. 如果 packageSpecifier 是空字符串,则

    ¥If packageSpecifier is an empty string, then

    1. 抛出无效的模块说明符错误。

      ¥Throw an Invalid Module Specifier error.

  3. 如果 packageSpecifier 是 Node.js 内置模块名称,则

    ¥If packageSpecifier is a Node.js builtin module name, then

    1. 返回与 packageSpecifier 连接的字符串 "node:"。

      ¥Return the string "node:" concatenated with packageSpecifier.

  4. 如果 packageSpecifier 不以 "@" 开头,则

    ¥If packageSpecifier does not start with "@", then

    1. 将 packageName 设置为 packageSpecifier 的子字符串,直到第一个 "/" 分隔符或字符串的结尾。

      ¥Set packageName to the substring of packageSpecifier until the first "/" separator or the end of the string.

  5. 否则,

    ¥Otherwise,

    1. 如果 packageSpecifier 不包含 "/" 分隔符,则

      ¥If packageSpecifier does not contain a "/" separator, then

      1. 抛出无效的模块说明符错误。

        ¥Throw an Invalid Module Specifier error.

    2. 将 packageName 设置为 packageSpecifier 的子字符串,直到第二个 "/" 分隔符或字符串的末尾。

      ¥Set packageName to the substring of packageSpecifier until the second "/" separator or the end of the string.

  6. 如果 packageName 以 "." 开头或包含 "" 或 "%",则

    ¥If packageName starts with "." or contains "" or "%", then

    1. 抛出无效的模块说明符错误。

      ¥Throw an Invalid Module Specifier error.

  7. 让 packageSubpath "." 与 packageSpecifier 的子字符串从 packageName 长度的位置连接起来。

    ¥Let packageSubpath be "." concatenated with the substring of packageSpecifier from the position at the length of packageName.

  8. 如果 packageSubpath 以 "/" 结尾,则

    ¥If packageSubpath ends in "/", then

    1. 抛出无效的模块说明符错误。

      ¥Throw an Invalid Module Specifier error.

  9. 令 selfUrl 为 PACKAGE_SELF_RESOLVE(packageName, packageSubpath, ParentURL) 的结果。

    ¥Let selfUrl be the result of PACKAGE_SELF_RESOLVE(packageName, packageSubpath, parentURL).

  10. 如果 selfUrl 不是未定义,则返回 selfUrl。

    ¥If selfUrl is not undefined, return selfUrl.

  11. 虽然 parentURL 不是文件系统根目录,

    ¥While parentURL is not the file system root,

    1. 令 packageURL 为 "node_modules/" 的 URL 解析与 packageSpecifier 连接,相对于 parentURL。

      ¥Let packageURL be the URL resolution of "node_modules/" concatenated with packageSpecifier, relative to parentURL.

    2. 将 parentURL 设置为 parentURL 的父文件夹 URL。

      ¥Set parentURL to the parent folder URL of parentURL.

    3. 如果 packageURL 处的文件夹不存在,则

      ¥If the folder at packageURL does not exist, then

      1. 继续下一个循环迭代。

        ¥Continue the next loop iteration.

    4. 令 pjson 为 READ_PACKAGE_JSON(packageURL) 的结果。

      ¥Let pjson be the result of READ_PACKAGE_JSON(packageURL).

    5. 如果 pjson 不为 null 并且 pjson.exports 不为 null 或未定义,则

      ¥If pjson is not null and pjson.exports is not null or undefined, then

      1. 返回 PACKAGE_EXPORTS_RESOLVE(packageURL, packageSubpath, pjson.exports, defaultConditions) 的结果。

        ¥Return the result of PACKAGE_EXPORTS_RESOLVE(packageURL, packageSubpath, pjson.exports, defaultConditions).

    6. 否则,如果 packageSubpath 等于 ".",则

      ¥Otherwise, if packageSubpath is equal to ".", then

      1. 如果 pjson.main 是一个字符串,那么

        ¥If pjson.main is a string, then

        1. 返回 packageURL 中 main 的 URL 解析。

          ¥Return the URL resolution of main in packageURL.

    7. 否则,

      ¥Otherwise,

      1. 返回 packageURL 中 packageSubpath 的 URL 解析。

        ¥Return the URL resolution of packageSubpath in packageURL.

  12. 抛出模块未找到错误。

    ¥Throw a Module Not Found error.

PACKAGE_SELF_RESOLVE(packageName, packageSubpath, parentURL)

  1. 令 packageURL 为 LOOKUP_PACKAGE_SCOPE(parentURL) 的结果。

    ¥Let packageURL be the result of LOOKUP_PACKAGE_SCOPE(parentURL).

  2. 如果 packageURL 为 null,则

    ¥If packageURL is null, then

    1. 返回未定义。

      ¥Return undefined.

  3. 令 pjson 为 READ_PACKAGE_JSON(packageURL) 的结果。

    ¥Let pjson be the result of READ_PACKAGE_JSON(packageURL).

  4. 如果 pjson 为 null 或者 pjson.exports 为 null 或未定义,则

    ¥If pjson is null or if pjson.exports is null or undefined, then

    1. 返回未定义。

      ¥Return undefined.

  5. 如果 pjson.name 等于 packageName,则

    ¥If pjson.name is equal to packageName, then

    1. 返回 PACKAGE_EXPORTS_RESOLVE(packageURL, packageSubpath, pjson.exports, defaultConditions) 的结果。

      ¥Return the result of PACKAGE_EXPORTS_RESOLVE(packageURL, packageSubpath, pjson.exports, defaultConditions).

  6. 否则,返回未定义。

    ¥Otherwise, return undefined.

PACKAGE_EXPORTS_RESOLVE(packageURL, subpath, exports, conditions)

  1. 如果 exports 是一个对象,其键值以 "." 开头,键值不以 "." 开头,则抛出无效包配置错误。

    ¥If exports is an Object with both a key starting with "." and a key not starting with ".", throw an Invalid Package Configuration error.

  2. 如果子路径等于 ".",则

    ¥If subpath is equal to ".", then

    1. 让 mainExport 未定义。

      ¥Let mainExport be undefined.

    2. 如果导出是字符串或数组,或者不包含以 "." 开头的键的对象,则

      ¥If exports is a String or Array, or an Object containing no keys starting with ".", then

      1. 将 mainExport 设置为导出。

        ¥Set mainExport to exports.

    3. 否则,如果导出是包含 "." 属性的对象,则

      ¥Otherwise if exports is an Object containing a "." property, then

      1. 将 mainExport 设置为导出 ["."]。

        ¥Set mainExport to exports["."].

    4. 如果 mainExport 不是未定义的,那么

      ¥If mainExport is not undefined, then

      1. 令已解析为 PACKAGE_TARGET_RESOLVE(packageURL, mainExport, null, false, criteria) 的结果。

        ¥Let resolved be the result of PACKAGE_TARGET_RESOLVE( packageURL, mainExport, null, false, conditions).

      2. 如果 resolved 不为 null 或未定义,则返回 resolved。

        ¥If resolved is not null or undefined, return resolved.

  3. 否则,如果 exports 是一个 Object 并且 exports 的所有 key 都以 "." 开头,那么

    ¥Otherwise, if exports is an Object and all keys of exports start with ".", then

    1. 令 matchKey 为与子路径连接的字符串 "./"。

      ¥Let matchKey be the string "./" concatenated with subpath.

    2. 令 resolved 为 PACKAGE_IMPORTS_EXPORTS_RESOLVE(matchKey,exports,packageURL,false,conditions)的结果。

      ¥Let resolved be the result of PACKAGE_IMPORTS_EXPORTS_RESOLVE( matchKey, exports, packageURL, false, conditions).

    3. 如果 resolved 不为 null 或未定义,则返回 resolved。

      ¥If resolved is not null or undefined, return resolved.

  4. 抛出包路径未导出错误。

    ¥Throw a Package Path Not Exported error.

PACKAGE_IMPORTS_RESOLVE(specifier, parentURL, conditions)

  1. 断言:说明符以 "" 开头。

    ¥Assert: specifier begins with "#".

  2. 如果说明符完全等于 "" 或以 "#/" 开头,则

    ¥If specifier is exactly equal to "#" or starts with "#/", then

    1. 抛出无效的模块说明符错误。

      ¥Throw an Invalid Module Specifier error.

  3. 令 packageURL 为 LOOKUP_PACKAGE_SCOPE(parentURL) 的结果。

    ¥Let packageURL be the result of LOOKUP_PACKAGE_SCOPE(parentURL).

  4. 如果 packageURL 不为 null,则

    ¥If packageURL is not null, then

    1. 令 pjson 为 READ_PACKAGE_JSON(packageURL) 的结果。

      ¥Let pjson be the result of READ_PACKAGE_JSON(packageURL).

    2. 如果 pjson.imports 是一个非空对象,那么

      ¥If pjson.imports is a non-null Object, then

      1. 令 resolved 为 PACKAGE_IMPORTS_EXPORTS_RESOLVE(specifier, pjson.imports, packageURL, true, Conditions) 的结果。

        ¥Let resolved be the result of PACKAGE_IMPORTS_EXPORTS_RESOLVE( specifier, pjson.imports, packageURL, true, conditions).

      2. 如果 resolved 不为 null 或未定义,则返回 resolved。

        ¥If resolved is not null or undefined, return resolved.

  5. 抛出包导入未定义错误。

    ¥Throw a Package Import Not Defined error.

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

  1. 如果 matchKey 是 matchObj 的键并且不包含 "*",则

    ¥If matchKey is a key of matchObj and does not contain ""*, then

    1. 令 target 为 matchObj[matchKey] 的值。

      ¥Let target be the value of matchObj[matchKey].

    2. 返回 PACKAGE_TARGET_RESOLVE(packageURL, target, null, isImports, Conditions) 的结果。

      ¥Return the result of PACKAGE_TARGET_RESOLVE(packageURL, target, null, isImports, conditions).

  2. 令 expansionKeys 为仅包含单个 "*" 的 matchObj 的键列表,由排序函数 PATTERN_KEY_COMPARE 排序,该函数按特异性降序排列。

    ¥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. 对于 expansionKeys 中的每个密钥 expansionKey,执行

    ¥For each key expansionKey in expansionKeys, do

    1. 设 patternBase 是 expansionKey 的子串,但不包括第一个 "*" 字符。

      ¥Let patternBase be the substring of expansionKey up to but excluding the first ""* character.

    2. 如果 matchKey 以 patternBase 开头但不等于 patternBase,则

      ¥If matchKey starts with but is not equal to patternBase, then

      1. 设 patternTrailer 是第一个 "*" 字符后索引中 expansionKey 的子串。

        ¥Let patternTrailer be the substring of expansionKey from the index after the first ""* character.

      2. 如果 patternTrailer 的长度为零,或者 matchKey 以 patternTrailer 结尾并且 matchKey 的长度大于或等于 expandationKey 的长度,则

        ¥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. 令 target 为 matchObj[expansionKey] 的值。

          ¥Let target be the value of matchObj[expansionKey].

        2. 令 patternMatch 为 matchKey 的子字符串,从 patternBase 长度的索引开始,直到 matchKey 的长度减去 patternTrailer 的长度。

          ¥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. 返回 PACKAGE_TARGET_RESOLVE(packageURL,target,patternMatch,isImports,conditions)的结果。

          ¥Return the result of PACKAGE_TARGET_RESOLVE(packageURL, target, patternMatch, isImports, conditions).

  4. 返回空值。

    ¥Return null.

PATTERN_KEY_COMPARE(keyA, keyB)

  1. 断言:keyA 以 "/" 结尾或仅包含一个 "*"。

    ¥Assert: keyA ends with "/" or contains only a single ""*.

  2. 断言:keyB 以 "/" 结尾或仅包含一个 "*"。

    ¥Assert: keyB ends with "/" or contains only a single ""*.

  3. 令 baseLengthA 为 "" 在 keyA 中的索引加一,如果 keyA 包含 "",否则为 keyA 的长度。

    ¥Let baseLengthA be the index of ""* in keyA plus one, if keyA contains ""*, or the length of keyA otherwise.

  4. 令 baseLengthB 为 "" 在 keyB 中的索引加一,如果 keyB 包含 "",否则为 keyB 的长度。

    ¥Let baseLengthB be the index of ""* in keyB plus one, if keyB contains ""*, or the length of keyB otherwise.

  5. 如果 baseLengthA 大于 baseLengthB,则返回 -1。

    ¥If baseLengthA is greater than baseLengthB, return -1.

  6. 如果 baseLengthB 大于 baseLengthA,则返回 1。

    ¥If baseLengthB is greater than baseLengthA, return 1.

  7. 如果 keyA 不包含 "*",则返回 1。

    ¥If keyA does not contain ""*, return 1.

  8. 如果 keyB 不包含 "*",则返回 -1。

    ¥If keyB does not contain ""*, return -1.

  9. 如果 keyA 的长度大于 keyB 的长度,返回-1。

    ¥If the length of keyA is greater than the length of keyB, return -1.

  10. 如果 keyB 的长度大于 keyA 的长度,则返回 1。

    ¥If the length of keyB is greater than the length of keyA, return 1.

  11. 返回 0。

    ¥Return 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. 如果 isImports 为 false,或者目标以 "../" 或 "/" 开头,或者目标是有效的 URL,则

        ¥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. 如果 patternMatch 是一个字符串,那么

        ¥If patternMatch is a String, then

        1. 返回 PACKAGE_RESOLVE(目标,其中 "*" 的每个实例都替换为模式匹配,packageURL + "/")。

          ¥Return PACKAGE_RESOLVE(target with every instance of ""* replaced by patternMatch, packageURL + "/").

      3. 返回 PACKAGE_RESOLVE(目标, packageURL + "/")。

        ¥Return PACKAGE_RESOLVE(target, packageURL + "/").

    2. 如果 "/" 或 "" 上的目标拆分在第一个 "." 段之后包含任何 ""、"."、".." 或 "node_modules" 段,不区分大小写并包括百分比编码变体,则抛出无效包目标错误。

      ¥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. 令 resolvedTarget 为 packageURL 和 target 串联的 URL 解析。

      ¥Let resolvedTarget be the URL resolution of the concatenation of packageURL and target.

    4. 断言:solvedTarget 包含在 packageURL 中。

      ¥Assert: resolvedTarget is contained in packageURL.

    5. 如果 patternMatch 为空,那么

      ¥If patternMatch is null, then

      1. 返回已解决的目标。

        ¥Return resolvedTarget.

    6. 如果在 "/" 或 "" 上拆分的 patternMatch 包含任何 ""、"."、".." 或 "node_modules" 段,不区分大小写并包括百分比编码变体,则抛出无效模块说明符错误。

      ¥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. 返回 resolvedTarget 的 URL 解析,其中 "*" 的每个实例都替换为 patternMatch。

      ¥Return the URL resolution of resolvedTarget with every instance of ""* replaced with patternMatch.

  2. 否则,如果 target 是非空对象,则

    ¥Otherwise, if target is a non-null Object, then

    1. 如果导出包含任何索引属性键,如 ECMA-262 6.1.7 数组索引 中所定义,则抛出无效包配置错误。

      ¥If exports contains any index property keys, as defined in ECMA-262 6.1.7 Array Index, throw an Invalid Package Configuration error.

    2. 对于目标的每个属性 p,按对象插入顺序为:

      ¥For each property p of target, in object insertion order as,

      1. 如果 p 等于 "default" 或条件包含 p 的条目,则

        ¥If p equals "default" or conditions contains an entry for p, then

        1. 设 targetValue 为 target 中 p 属性的值。

          ¥Let targetValue be the value of the p property in target.

        2. 令 resolved 为 PACKAGE_TARGET_RESOLVE(packageURL, targetValue,patternMatch,isImports,conditions) 的结果。

          ¥Let resolved be the result of PACKAGE_TARGET_RESOLVE( packageURL, targetValue, patternMatch, isImports, conditions).

        3. 如果 resolved 等于 undefined,则继续循环。

          ¥If resolved is equal to undefined, continue the loop.

        4. 返回解决。

          ¥Return resolved.

    3. 返回未定义。

      ¥Return undefined.

  3. 否则,如果 target 是一个数组,那么

    ¥Otherwise, if target is an Array, then

    1. 如果 _target.length 为零,则返回 null。

      ¥If _target.length is zero, return null.

    2. 对于目标中的每个项目 targetValue,执行

      ¥For each item targetValue in target, do

      1. 让 resolved 成为 PACKAGE_TARGET_RESOLVE(packageURL, targetValue,patternMatch,isImports,conditions)的结果,在出现任何 Invalid Package Target 错误时继续循环。

        ¥Let resolved be the result of PACKAGE_TARGET_RESOLVE( packageURL, targetValue, patternMatch, isImports, conditions), continuing the loop on any Invalid Package Target error.

      2. 如果 resolved 未定义,则继续循环。

        ¥If resolved is undefined, continue the loop.

      3. 返回解决。

        ¥Return resolved.

    3. 返回或抛出最后的后备解决方案 null 返回或错误。

      ¥Return or throw the last fallback resolution null return or error.

  4. 否则,如果 target 为 null,则返回 null。

    ¥Otherwise, if target is null, return null.

  5. 否则抛出一个 Invalid Package Target 错误。

    ¥Otherwise throw an Invalid Package Target error.

ESM_FILE_FORMAT(url)

  1. 断言:url 对应于现有文件。

    ¥Assert: url corresponds to an existing file.

  2. 如果 url 以 ".mjs" 结尾,则

    ¥If url ends in ".mjs", then

    1. 返回 "module"。

      ¥Return "module".

  3. 如果 url 以 ".cjs" 结尾,则

    ¥If url ends in ".cjs", then

    1. 返回 "commonjs"。

      ¥Return "commonjs".

  4. 如果 url 以 ".json" 结尾,则

    ¥If url ends in ".json", then

    1. 返回 "json"。

      ¥Return "json".

  5. 令 packageURL 为 LOOKUP_PACKAGE_SCOPE(url) 的结果。

    ¥Let packageURL be the result of LOOKUP_PACKAGE_SCOPE(url).

  6. 令 pjson 为 READ_PACKAGE_JSON(packageURL) 的结果。

    ¥Let pjson be the result of READ_PACKAGE_JSON(packageURL).

  7. 如果 pjson?.type 存在且为 "module",则

    ¥If pjson?.type exists and is "module", then

    1. 如果 url 以 ".js" 结尾或没有文件扩展名,则

      ¥If url ends in ".js" or has no file extension, then

      1. 如果启用了 --experimental-wasm-modules 并且 url 处的文件包含 WebAssembly 模块的标头,则

        ¥If --experimental-wasm-modules is enabled and the file at url contains the header for a WebAssembly module, then

        1. 返回 "wasm"。

          ¥Return "wasm".

      2. 否则,

        ¥Otherwise,

        1. 返回 "module"。

          ¥Return "module".

    2. 返回未定义。

      ¥Return undefined.

  8. 否则,

    ¥Otherwise,

    1. 返回未定义。

      ¥Return undefined.

LOOKUP_PACKAGE_SCOPE(url)

  1. 让 scopeURL 为 url。

    ¥Let scopeURL be url.

  2. 虽然 scopeURL 不是文件系统根目录,

    ¥While scopeURL is not the file system root,

    1. 将 scopeURL 设置为 scopeURL 的父 URL。

      ¥Set scopeURL to the parent URL of scopeURL.

    2. 如果 scopeURL 以 "node_modules" 路径段结束,则返回 null。

      ¥If scopeURL ends in a "node_modules" path segment, return null.

    3. 设 pjsonURL 为 scopeURL 中 "package.json" 的解析。

      ¥Let pjsonURL be the resolution of "package.json" within scopeURL.

    4. 如果 pjsonURL 处的文件存在,则

      ¥if the file at pjsonURL exists, then

      1. 返回范围 URL。

        ¥Return scopeURL.

  3. 返回空值。

    ¥Return null.

READ_PACKAGE_JSON(packageURL)

  1. 设 pjsonURL 为 packageURL 中 "package.json" 的解析。

    ¥Let pjsonURL be the resolution of "package.json" within packageURL.

  2. 如果 pjsonURL 处的文件不存在,则

    ¥If the file at pjsonURL does not exist, then

    1. 返回空值。

      ¥Return null.

  3. 如果 packageURL 处的文件未解析为有效的 JSON,则

    ¥If the file at packageURL does not parse as valid JSON, then

    1. 抛出一个无效的包配置错误。

      ¥Throw an Invalid Package Configuration error.

  4. 在 pjsonURL 返回文件的已解析 JSON 源。

    ¥Return the parsed JSON source of the file at pjsonURL.

自定义 ESM 说明符解析算法#

¥Customizing ESM specifier resolution algorithm

稳定性: 1 - 实验性的

¥Stability: 1 - Experimental

不要依赖这个标志。我们计划在 模块定制钩子 改进到可以通过自定义钩子实现等效功能时将其删除。

¥Do not rely on this flag. We plan to remove it once the Module customization hooks have advanced to the point that equivalent functionality can be achieved via custom hooks.

当前说明符解析不支持 CommonJS 加载器的所有默认行为。行为差异之一是文件扩展名的自动解析以及导入具有索引文件的目录的能力。

¥The current specifier resolution does not support all default behavior of the CommonJS loader. One of the behavior differences is automatic resolution of file extensions and the ability to import directories that have an index file.

--experimental-specifier-resolution=[mode] 标志可用于自定义扩展解析算法。默认模式是 explicit,它需要向加载程序提供模块的完整路径。要启用自动扩展解析并从包含索引文件的目录导入,请使用 node 模式。

¥The --experimental-specifier-resolution=[mode] flag can be used to customize the extension resolution algorithm. The default mode is explicit, which requires the full path to a module be provided to the loader. To enable the automatic extension resolution and importing from directories that include an index file use the node mode.

$ node index.mjs
success!
$ node index # Failure!
Error: Cannot find module
$ node --experimental-specifier-resolution=node index
success! 
Node.js 中文网 - 粤ICP备13048890号