diff --git a/addons/c_addons.md b/addons/c_addons.md index e69550b8..986a998c 100644 --- a/addons/c_addons.md +++ b/addons/c_addons.md @@ -26,7 +26,7 @@ - Node.js 包含了其他的静态链接库,如 OpenSSL。 这些库位于 Node.js 源代码树中的 `deps/` 目录。 只有 libuv、OpenSSL、V8 和 zlib 符号是被 Node.js 有目的地重新公开,并且可以被插件在不同程度上使用。 - 更多信息可查看[链接到 Node.js 自有的依赖项][Linking to Node.js' own dependencies]。 + 更多信息可查看[链接到 Node.js 自带的库][Linking to libraries included with Node.js]。 以下所有示例均可供[下载][download],并可用作学习插件的起点。 diff --git a/addons/linking_to_node_js_own_dependencies.md b/addons/linking_to_libraries_included_with_node_js.md similarity index 100% rename from addons/linking_to_node_js_own_dependencies.md rename to addons/linking_to_libraries_included_with_node_js.md diff --git a/assert/assert.md b/assert/assert.md index d235a8ab..cdfe6454 100644 --- a/assert/assert.md +++ b/assert/assert.md @@ -4,5 +4,4 @@ > 稳定性: 2 - 稳定 `assert` 模块提供了一组简单的断言测试,可用于测试不变量。 -该模块提供了建议的[严格模式][`strict` mode]和更宽松的遗留模式。 diff --git a/assert/assert_deepequal_actual_expected_message.md b/assert/assert_deepequal_actual_expected_message.md index 830d57b2..be560bea 100644 --- a/assert/assert_deepequal_actual_expected_message.md +++ b/assert/assert_deepequal_actual_expected_message.md @@ -26,18 +26,18 @@ changes: * `expected` {any} * `message` {string|Error} -**Strict mode** +**Strict assertion mode** An alias of [`assert.deepStrictEqual()`][]. -**Legacy mode** +**Legacy assertion mode** > Stability: 0 - Deprecated: Use [`assert.deepStrictEqual()`][] instead. Tests for deep equality between the `actual` and `expected` parameters. Consider using [`assert.deepStrictEqual()`][] instead. [`assert.deepEqual()`][] can have -potentially surprising results. +surprising results. -"Deep" equality means that the enumerable "own" properties of child objects +_Deep equality_ means that the enumerable "own" properties of child objects are also recursively evaluated by the following rules. diff --git a/assert/assert_equal_actual_expected_message.md b/assert/assert_equal_actual_expected_message.md index 61b4458f..4389d04b 100644 --- a/assert/assert_equal_actual_expected_message.md +++ b/assert/assert_equal_actual_expected_message.md @@ -6,11 +6,11 @@ added: v0.1.21 * `expected` {any} * `message` {string|Error} -**Strict mode** +**Strict assertion mode** An alias of [`assert.strictEqual()`][]. -**Legacy mode** +**Legacy assertion mode** > Stability: 0 - Deprecated: Use [`assert.strictEqual()`][] instead. diff --git a/assert/assert_notdeepequal_actual_expected_message.md b/assert/assert_notdeepequal_actual_expected_message.md index 50ae36ed..17649f43 100644 --- a/assert/assert_notdeepequal_actual_expected_message.md +++ b/assert/assert_notdeepequal_actual_expected_message.md @@ -22,11 +22,11 @@ changes: * `expected` {any} * `message` {string|Error} -**Strict mode** +**Strict assertion mode** An alias of [`assert.notDeepStrictEqual()`][]. -**Legacy mode** +**Legacy assertion mode** > Stability: 0 - Deprecated: Use [`assert.notDeepStrictEqual()`][] instead. diff --git a/assert/assert_notequal_actual_expected_message.md b/assert/assert_notequal_actual_expected_message.md index a970ada0..b206bd5e 100644 --- a/assert/assert_notequal_actual_expected_message.md +++ b/assert/assert_notequal_actual_expected_message.md @@ -6,11 +6,11 @@ added: v0.1.21 * `expected` {any} * `message` {string|Error} -**Strict mode** +**Strict assertion mode** An alias of [`assert.notStrictEqual()`][]. -**Legacy mode** +**Legacy assertion mode** > Stability: 0 - Deprecated: Use [`assert.notStrictEqual()`][] instead. diff --git a/assert/assert_rejects_asyncfn_error_message.md b/assert/assert_rejects_asyncfn_error_message.md index 1433eddd..0840e658 100644 --- a/assert/assert_rejects_asyncfn_error_message.md +++ b/assert/assert_rejects_asyncfn_error_message.md @@ -32,6 +32,21 @@ added: v10.0.0 })(); ``` +```js +(async () => { + await assert.rejects( + async () => { + throw new TypeError('错误值'); + }, + (err) => { + assert.strictEqual(err.name, 'TypeError'); + assert.strictEqual(err.message, '错误值'); + return true; + } + ); +})(); +``` + ```js assert.rejects( Promise.reject(new Error('错误值')), diff --git a/assert/legacy_mode.md b/assert/legacy_assertion_mode.md similarity index 66% rename from assert/legacy_mode.md rename to assert/legacy_assertion_mode.md index 7e1e1d35..f2194de1 100644 --- a/assert/legacy_mode.md +++ b/assert/legacy_assertion_mode.md @@ -1,19 +1,19 @@ -遗留模式在以下方法中使用[抽象的相等性比较][Abstract Equality Comparison]: +遗留的断言模式在以下方法中使用[抽象的相等性比较][Abstract Equality Comparison]: * [`assert.deepEqual()`][] * [`assert.equal()`][] * [`assert.notDeepEqual()`][] * [`assert.notEqual()`][] -使用遗留模式: +使用遗留的断言模式: ```js const assert = require('assert'); ``` -只要有可能,请使用[严格模式][`strict` mode]。 +只要有可能,请使用[严格的断言模式][strict assertion mode]。 否则,[抽象的相等性比较][Abstract Equality Comparison]可能会导致意外的结果。 特别是对于 [`assert.deepEqual()`],其中的比较规则是松散的: diff --git a/assert/strict_mode.md b/assert/strict_assertion_mode.md similarity index 55% rename from assert/strict_mode.md rename to assert/strict_assertion_mode.md index 3f757a63..cd5fd09a 100644 --- a/assert/strict_mode.md +++ b/assert/strict_assertion_mode.md @@ -1,21 +1,25 @@ -在严格模式中(不要与 `"use strict"` 混淆),任何 `assert` 函数都将使用严格函数模式中使用的相等性。 +在严格的断言模式中,非严格的方法与它们对应的严格方法的行为是一样的。 例如,[`assert.deepEqual()`] 将与 [`assert.deepStrictEqual()`] 一样效果。 -在严格模式中,对象的错误消息会显示差异。 -在遗留模式下,对象的错误消息会显示对象,通常是截断的。 +在严格的断言模式中,对象的错误消息会显示差异。 +在遗留的断言模式下,对象的错误消息会显示对象,通常是截断的。 -使用严格模式: +使用严格的断言模式: ```js const assert = require('assert').strict; @@ -43,7 +47,6 @@ assert.deepEqual([[[1, 2, 3]], 4, 5], [[[1, 2, '3']], 4, 5]); ``` 要停用颜色,则使用 `NO_COLOR` 或 `NODE_DISABLE_COLORS` 环境变量。 -注意,这也将停用 REPL 中的颜色。 - +这也将停用 REPL 中的颜色。 有关终端环境中颜色支持的更多信息,参阅 [getColorDepth()][_tty_writestream_getcolordepth] 文档。 diff --git a/async_hooks/async_hooks.md b/async_hooks/async_hooks.md index dc84973c..591788ba 100644 --- a/async_hooks/async_hooks.md +++ b/async_hooks/async_hooks.md @@ -3,9 +3,8 @@ > Stability: 1 - Experimental -The `async_hooks` module provides an API to register callbacks tracking the -lifetime of asynchronous resources created inside a Node.js application. -It can be accessed using: +The `async_hooks` module provides an API to track asynchronous resources. It +can be accessed using: ```js const async_hooks = require('async_hooks'); diff --git a/async_hooks/asynchronous_context_example.md b/async_hooks/asynchronous_context_example.md index 879fcced..52a31466 100644 --- a/async_hooks/asynchronous_context_example.md +++ b/async_hooks/asynchronous_context_example.md @@ -70,8 +70,8 @@ Timeout(7) -> TickObject(6) -> root(1) ``` The `TCPSERVERWRAP` is not part of this graph, even though it was the reason for -`console.log()` being called. This is because binding to a port without a -hostname is a *synchronous* operation, but to maintain a completely asynchronous +`console.log()` being called. This is because binding to a port without a host +name is a *synchronous* operation, but to maintain a completely asynchronous API the user's callback is placed in a `process.nextTick()`. The graph only shows *when* a resource was created, not *why*, so to track diff --git a/async_hooks/asyncresource_triggerasyncid.md b/async_hooks/asyncresource_triggerasyncid.md index b6cd2e94..01592e33 100644 --- a/async_hooks/asyncresource_triggerasyncid.md +++ b/async_hooks/asyncresource_triggerasyncid.md @@ -2,12 +2,4 @@ * Returns: {number} The same `triggerAsyncId` that is passed to the `AsyncResource` constructor. - - - - - - - - - + diff --git a/async_hooks/resource.md b/async_hooks/resource.md index 40773705..7ef4854a 100644 --- a/async_hooks/resource.md +++ b/async_hooks/resource.md @@ -2,7 +2,7 @@ `resource` is an object that represents the actual async resource that has been initialized. This can contain useful information that can vary based on the value of `type`. For instance, for the `GETADDRINFOREQWRAP` resource type, -`resource` provides the hostname used when looking up the IP address for the +`resource` provides the host name used when looking up the IP address for the host in `net.Server.listen()`. The API for accessing this information is currently not considered public, but using the Embedder API, users can provide and document their own resource objects. For example, such a resource object diff --git a/async_hooks/using_asyncresource_for_a_worker_thread_pool.md b/async_hooks/using_asyncresource_for_a_worker_thread_pool.md new file mode 100644 index 00000000..d1ba9033 --- /dev/null +++ b/async_hooks/using_asyncresource_for_a_worker_thread_pool.md @@ -0,0 +1,129 @@ + +The following example shows how to use the `AsyncResource` class to properly +provide async tracking for a [`Worker`][] pool. Other resource pools, such as +database connection pools, can follow a similar model. + +Assuming that the task is adding two numbers, using a file named +`task_processor.js` with the following content: + +```js +const { parentPort } = require('worker_threads'); +parentPort.on('message', (task) => { + parentPort.postMessage(task.a + task.b); +}); +``` + +a Worker pool around it could use the following structure: + +```js +const { AsyncResource } = require('async_hooks'); +const { EventEmitter } = require('events'); +const path = require('path'); +const { Worker } = require('worker_threads'); + +const kTaskInfo = Symbol('kTaskInfo'); +const kWorkerFreedEvent = Symbol('kWorkerFreedEvent'); + +class WorkerPoolTaskInfo extends AsyncResource { + constructor(callback) { + super('WorkerPoolTaskInfo'); + this.callback = callback; + } + + done(err, result) { + this.runInAsyncScope(this.callback, null, err, result); + this.emitDestroy(); // `TaskInfo`s are used only once. + } +} + +class WorkerPool extends EventEmitter { + constructor(numThreads) { + super(); + this.numThreads = numThreads; + this.workers = []; + this.freeWorkers = []; + + for (let i = 0; i < numThreads; i++) + this.addNewWorker(); + } + + addNewWorker() { + const worker = new Worker(path.resolve(__dirname, 'task_processor.js')); + worker.on('message', (result) => { + // In case of success: Call the callback that was passed to `runTask`, + // remove the `TaskInfo` associated with the Worker, and mark it as free + // again. + worker[kTaskInfo].done(null, result); + worker[kTaskInfo] = null; + this.freeWorkers.push(worker); + this.emit(kWorkerFreedEvent); + }); + worker.on('error', (err) => { + // In case of an uncaught exception: Call the callback that was passed to + // `runTask` with the error. + if (worker[kTaskInfo]) + worker[kTaskInfo].done(err, null); + else + this.emit('error', err); + // Remove the worker from the list and start a new Worker to replace the + // current one. + this.workers.splice(this.workers.indexOf(worker), 1); + this.addNewWorker(); + }); + this.workers.push(worker); + this.freeWorkers.push(worker); + } + + runTask(task, callback) { + if (this.freeWorkers.length === 0) { + // No free threads, wait until a worker thread becomes free. + this.once(kWorkerFreedEvent, () => this.runTask(task, callback)); + return; + } + + const worker = this.freeWorkers.pop(); + worker[kTaskInfo] = new WorkerPoolTaskInfo(callback); + worker.postMessage(task); + } + + close() { + for (const worker of this.workers) worker.terminate(); + } +} + +module.exports = WorkerPool; +``` + +Without the explicit tracking added by the `WorkerPoolTaskInfo` objects, +it would appear that the callbacks are associated with the individual `Worker` +objects. However, the creation of the `Worker`s is not associated with the +creation of the tasks and does not provide information about when tasks +were scheduled. + +This pool could be used as follows: + +```js +const WorkerPool = require('./worker_pool.js'); +const os = require('os'); + +const pool = new WorkerPool(os.cpus().length); + +let finished = 0; +for (let i = 0; i < 10; i++) { + pool.runTask({ a: 42, b: 100 }, (err, result) => { + console.log(i, err, result); + if (++finished === 10) + pool.close(); + }); +} +``` + + + + + + + + + + diff --git a/buffer/buffers_and_character_encodings.md b/buffer/buffers_and_character_encodings.md index dbfc93b6..55b963e8 100644 --- a/buffer/buffers_and_character_encodings.md +++ b/buffer/buffers_and_character_encodings.md @@ -42,6 +42,17 @@ Node.js 当前支持的字符编码有: * `'hex'`: 将每个字节编码成两个十六进制的字符。 +```js +Buffer.from('1ag', 'hex'); +// 打印 ,当遇到第一个非十六进制的值('g')时,则数据会被截断。 + +Buffer.from('1a7g', 'hex'); +// 打印 ,当数据以一个数字('7')结尾时,则数据会被截断。 + +Buffer.from('1634', 'hex'); +// 打印 ,所有数据均可用。 +``` + 现代的 Web 浏览器遵循 [WHATWG 编码标准][WHATWG Encoding Standard],将 `'latin1'` 和 `'ISO-8859-1'` 别名为 `'win-1252'`。 这意味着当执行 `http.get()` 之类的操作时,如果返回的字符集是 WHATWG 规范中列出的字符集之一,则服务器可能实际返回 `'win-1252'` 编码的数据,而使用 `'latin1'` 编码可能错误地解码字符。 diff --git a/buffer/class_method_buffer_from_arraybuffer_byteoffset_length.md b/buffer/class_method_buffer_from_arraybuffer_byteoffset_length.md index fc766596..3cf11e5c 100644 --- a/buffer/class_method_buffer_from_arraybuffer_byteoffset_length.md +++ b/buffer/class_method_buffer_from_arraybuffer_byteoffset_length.md @@ -4,7 +4,7 @@ added: v5.10.0 * `arrayBuffer` {ArrayBuffer|SharedArrayBuffer} 一个 [`ArrayBuffer`]、[`SharedArrayBuffer`]、或 [`TypedArray`] 的 `.buffer` 属性。 * `byteOffset` {integer} 开始拷贝的索引。**默认值:** `0`。 -* `length` {integer} 拷贝的字节数。**默认值:** `arrayBuffer.length - byteOffset`。 +* `length` {integer} 拷贝的字节数。**默认值:** `arrayBuffer.byteLength - byteOffset`。 创建 [`ArrayBuffer`] 的视图,但不会拷贝底层内存。 例如,当传入 [`TypedArray`] 的 `.buffer` 属性的引用时,新建的 `Buffer` 会与 [`TypedArray`] 共享同一内存。 diff --git a/buffer/new_buffer_arraybuffer_byteoffset_length.md b/buffer/new_buffer_arraybuffer_byteoffset_length.md index 1e2e4726..2e71b3ce 100644 --- a/buffer/new_buffer_arraybuffer_byteoffset_length.md +++ b/buffer/new_buffer_arraybuffer_byteoffset_length.md @@ -25,7 +25,7 @@ changes: [`SharedArrayBuffer`][] or the `.buffer` property of a [`TypedArray`][]. * `byteOffset` {integer} Index of first byte to expose. **Default:** `0`. * `length` {integer} Number of bytes to expose. - **Default:** `arrayBuffer.length - byteOffset`. + **Default:** `arrayBuffer.byteLength - byteOffset`. This creates a view of the [`ArrayBuffer`][] or [`SharedArrayBuffer`][] without copying the underlying memory. For example, when passed a reference to the diff --git a/buffer/the_zero_fill_buffers_command_line_option.md b/buffer/the_zero_fill_buffers_command_line_option.md index e814f500..beced024 100644 --- a/buffer/the_zero_fill_buffers_command_line_option.md +++ b/buffer/the_zero_fill_buffers_command_line_option.md @@ -3,10 +3,9 @@ added: v5.10.0 --> 可以使用 `--zero-fill-buffers` 命令行选项启动 Node.js,以使所有新分配的 `Buffer` 实例在创建时默认使用零来填充。 -在 Node.js 8.0.0 之前,这包括 `new Buffer(size)`、[`Buffer.allocUnsafe()`]、[`Buffer.allocUnsafeSlow()`] 和 `new SlowBuffer(size)` 分配的 buffer。 -从 Node.js 8.0.0 开始,无论是否使用此选项,使用 `new` 分配的 buffer 始终用零填充。 +如果没有该选项,则使用 [`Buffer.allocUnsafe()`]、[`Buffer.allocUnsafeSlow()`] 和 `new SlowBuffer(size)` 创建的 buffer 不会填充零。 使用这个选项可能会对性能产生重大的负面影响。 -建议仅在需要强制新分配的 `Buffer` 实例不能包含可能敏感的旧数据时,才使用 `--zero-fill-buffers` 选项。 +仅在需要强制新分配的 `Buffer` 实例不能包含可能敏感的旧数据时,才使用 `--zero-fill-buffers` 选项。 ```console $ node --zero-fill-buffers diff --git a/child_process/event_exit.md b/child_process/event_exit.md index ceb36676..dcaf3609 100644 --- a/child_process/event_exit.md +++ b/child_process/event_exit.md @@ -8,7 +8,7 @@ added: v0.1.90 当子进程结束后时会触发 `'exit'` 事件。 如果进程退出,则 `code` 是进程的最终退出码,否则为 `null`。 如果进程是因为收到的信号而终止,则 `signal` 是信号的字符串名称,否则为 `null`。 -这两个值至少有一个是非空的。 +这两个值至少有一个是非 `null` 的。 当 `'exit'` 事件被触发时,子进程的 stdio 流可能依然是打开的。 diff --git a/child_process/subprocess_exitcode.md b/child_process/subprocess_exitcode.md new file mode 100644 index 00000000..916d838d --- /dev/null +++ b/child_process/subprocess_exitcode.md @@ -0,0 +1,6 @@ + +* {integer} + +The `subprocess.exitCode` property indicates the exit code of the child process. +If the child process is still running, the field will be `null`. + diff --git a/child_process/subprocess_signalcode.md b/child_process/subprocess_signalcode.md new file mode 100644 index 00000000..08433eff --- /dev/null +++ b/child_process/subprocess_signalcode.md @@ -0,0 +1,6 @@ + +* {integer} + +The `subprocess.signalCode` property indicates the signal number received by +the child process if any, else `null`. + diff --git a/child_process/subprocess_spawnargs.md b/child_process/subprocess_spawnargs.md new file mode 100644 index 00000000..6cd80641 --- /dev/null +++ b/child_process/subprocess_spawnargs.md @@ -0,0 +1,6 @@ + +* {Array} + +The `subprocess.spawnargs` property represents the full list of command line +arguments the child process was launched with. + diff --git a/child_process/subprocess_spawnfile.md b/child_process/subprocess_spawnfile.md new file mode 100644 index 00000000..082a0ba2 --- /dev/null +++ b/child_process/subprocess_spawnfile.md @@ -0,0 +1,13 @@ + +* {string} + +The `subprocess.spawnfile` property indicates the executable file name of +the child process that is launched. + +For [`child_process.fork()`][], its value will be equal to +[`process.execPath`][]. +For [`child_process.spawn()`][], its value will be the name of +the executable file. +For [`child_process.exec()`][], its value will be the name of the shell +in which the child process is launched. + diff --git a/cli/abort_on_uncaught_exception.md b/cli/abort_on_uncaught_exception.md index d6365c27..d5248d99 100644 --- a/cli/abort_on_uncaught_exception.md +++ b/cli/abort_on_uncaught_exception.md @@ -1,5 +1,5 @@ Aborting instead of exiting causes a core file to be generated for post-mortem diff --git a/cli/experimental_import_meta_resolve.md b/cli/experimental_import_meta_resolve.md new file mode 100644 index 00000000..06a2ba9d --- /dev/null +++ b/cli/experimental_import_meta_resolve.md @@ -0,0 +1,6 @@ + + +Enable experimental `import.meta.resolve()` support. + diff --git a/cli/jitless.md b/cli/jitless.md new file mode 100644 index 00000000..d1e6caf4 --- /dev/null +++ b/cli/jitless.md @@ -0,0 +1,11 @@ + + +Disable [runtime allocation of executable memory][jitless]. This may be +required on some platforms for security reasons. It can also reduce attack +surface on other platforms, but the performance impact may be severe. + +This flag is inherited from V8 and is subject to change upstream. It may +disappear in a non-semver-major release. + diff --git a/cli/node_options_options.md b/cli/node_options_options.md index 336cde99..66c62e9a 100644 --- a/cli/node_options_options.md +++ b/cli/node_options_options.md @@ -37,6 +37,7 @@ Node.js options that are allowed are: * `--enable-fips` * `--enable-source-maps` +* `--experimental-import-meta-resolve` * `--experimental-json-modules` * `--experimental-loader` * `--experimental-modules` @@ -111,6 +112,7 @@ V8 options that are allowed are: * `--abort-on-uncaught-exception` * `--disallow-code-generation-from-strings` * `--interpreted-frames-native-stack` +* `--jitless` * `--max-old-space-size` * `--perf-basic-prof-only-functions` * `--perf-basic-prof` @@ -119,3 +121,6 @@ V8 options that are allowed are: * `--stack-trace-limit` +`--perf-basic-prof-only-functions`, `--perf-basic-prof`, +`--perf-prof-unwinding-info`, and `--perf-prof` are only available on Linux. + diff --git a/cli/uv_threadpool_size_size.md b/cli/uv_threadpool_size_size.md index ee336b2d..e6c0bc55 100644 --- a/cli/uv_threadpool_size_size.md +++ b/cli/uv_threadpool_size_size.md @@ -39,5 +39,6 @@ greater than `4` (its current default value). For more information, see the + diff --git a/crypto/class_sign.md b/crypto/class_sign.md index 08952469..15b0a235 100644 --- a/crypto/class_sign.md +++ b/crypto/class_sign.md @@ -31,8 +31,8 @@ const signature = sign.sign(privateKey, 'hex'); const verify = crypto.createVerify('SHA256'); verify.write('要生成签名的数据'); verify.end(); -console.log(verify.verify(publicKey, signature)); -// 打印 true 或 false。 +console.log(verify.verify(publicKey, signature, 'hex')); +// 打印 true ``` 示例,使用 [`sign.update()`] 和 [`verify.update()`] 方法: diff --git a/crypto/crypto_privatedecrypt_privatekey_buffer.md b/crypto/crypto_privatedecrypt_privatekey_buffer.md index f91d3202..91a69a63 100644 --- a/crypto/crypto_privatedecrypt_privatekey_buffer.md +++ b/crypto/crypto_privatedecrypt_privatekey_buffer.md @@ -15,6 +15,8 @@ changes: * `privateKey` {Object | string | Buffer | KeyObject} * `oaepHash` {string} The hash function to use for OAEP padding. **Default:** `'sha1'` + * `oaepLabel` {Buffer | TypedArray | DataView} The label to use for OAEP + padding. If not specified, no label is used. * `padding` {crypto.constants} An optional padding value defined in `crypto.constants`, which may be: `crypto.constants.RSA_NO_PADDING`, `crypto.constants.RSA_PKCS1_PADDING`, or diff --git a/crypto/crypto_publicencrypt_key_buffer.md b/crypto/crypto_publicencrypt_key_buffer.md index f771256f..5168fa3e 100644 --- a/crypto/crypto_publicencrypt_key_buffer.md +++ b/crypto/crypto_publicencrypt_key_buffer.md @@ -16,6 +16,8 @@ changes: * `key` {string | Buffer | KeyObject} A PEM encoded public or private key. * `oaepHash` {string} The hash function to use for OAEP padding. **Default:** `'sha1'` + * `oaepLabel` {Buffer | TypedArray | DataView} The label to use for OAEP + padding. If not specified, no label is used. * `passphrase` {string | Buffer} An optional passphrase for the private key. * `padding` {crypto.constants} An optional padding value defined in `crypto.constants`, which may be: `crypto.constants.RSA_NO_PADDING`, diff --git a/deprecations/dep0118_dns_lookup_support_for_a_falsy_host_name.md b/deprecations/dep0118_dns_lookup_support_for_a_falsy_host_name.md new file mode 100644 index 00000000..4bbba609 --- /dev/null +++ b/deprecations/dep0118_dns_lookup_support_for_a_falsy_host_name.md @@ -0,0 +1,15 @@ + + +Type: Runtime + +Previous versions of Node.js supported `dns.lookup()` with a falsy host name +like `dns.lookup(false)` due to backward compatibility. +This behavior is undocumented and is thought to be unused in real world apps. +It will become an error in future versions of Node.js. + + diff --git a/errors/common_system_errors.md b/errors/common_system_errors.md index 3c633a06..77b682d0 100644 --- a/errors/common_system_errors.md +++ b/errors/common_system_errors.md @@ -21,13 +21,12 @@ 当一次并行打开多个文件时会发生这个错误,尤其是在进程的文件描述限制数量较低的操作系统(如 macOS)。 要解决这个限制,可在运行 Node.js 进程的同一 shell 中运行 `ulimit -n 2048`。 -- `ENOENT` (无此文件或目录): 通常是由 [`fs`] 操作引起的,表明指定的路径不存在,即给定的路径找不到文件或目录。 +- `ENOENT` (无此文件或目录): 通常是由 [`fs`] 操作引起的,表明指定的路径不存在。给定的路径找不到文件或目录。 - `ENOTDIR` (不是一个目录): 给定的路径虽然存在,但不是一个目录。 通常是由 [`fs.readdir`] 引起的。 -- `ENOTEMPTY` (目录非空): 一个操作的目标是一个非空的目录,而要求的是一个空目录。 - 通常是由 [`fs.unlink`] 引起的。 +- `ENOTEMPTY` (目录非空): 一个操作的目标是一个非空的目录,而要求的是一个空目录,通常是由 [`fs.unlink`] 引起的。 - `ENOTFOUND` (DNS查找失败): 表示 `EAI_NODATA` 或 `EAI_NONAME` 的 DNS 失败。 这不是标准的 POSIX 错误。 diff --git a/errors/err_incompatible_option_pair.md b/errors/err_incompatible_option_pair.md index 3e8b1b7e..33b64a4a 100644 --- a/errors/err_incompatible_option_pair.md +++ b/errors/err_incompatible_option_pair.md @@ -1,5 +1,5 @@ -An option pair is incompatible with each other and can not be used at the same +An option pair is incompatible with each other and cannot be used at the same time. diff --git a/errors/err_invalid_ip_address.md b/errors/err_invalid_ip_address.md index 6b58bf9e..08bff1d6 100644 --- a/errors/err_invalid_ip_address.md +++ b/errors/err_invalid_ip_address.md @@ -1,4 +1,4 @@ An IP address is not valid. - + diff --git a/errors/err_invalid_module_specifier.md b/errors/err_invalid_module_specifier.md new file mode 100644 index 00000000..f15db3b6 --- /dev/null +++ b/errors/err_invalid_module_specifier.md @@ -0,0 +1,5 @@ + +The imported module string is an invalid URL, package name, or package subpath +specifier. + + diff --git a/errors/err_invalid_package_config.md b/errors/err_invalid_package_config.md index 8499fac0..571aebf4 100644 --- a/errors/err_invalid_package_config.md +++ b/errors/err_invalid_package_config.md @@ -1,4 +1,4 @@ An invalid `package.json` file was found which failed parsing. - + diff --git a/errors/err_invalid_package_target.md b/errors/err_invalid_package_target.md new file mode 100644 index 00000000..512474c9 --- /dev/null +++ b/errors/err_invalid_package_target.md @@ -0,0 +1,5 @@ + +The `package.json` [exports][] field contains an invalid target mapping value +for the attempted module resolution. + + diff --git a/errors/err_out_of_range.md b/errors/err_out_of_range.md index 2088c2cf..a8976b01 100644 --- a/errors/err_out_of_range.md +++ b/errors/err_out_of_range.md @@ -1,4 +1,4 @@ A given value is out of the accepted range. - + diff --git a/errors/err_package_path_not_exported.md b/errors/err_package_path_not_exported.md new file mode 100644 index 00000000..45e7f53e --- /dev/null +++ b/errors/err_package_path_not_exported.md @@ -0,0 +1,6 @@ + +The `package.json` [exports][] field does not export the requested subpath. +Because exports are encapsulated, private internal modules that are not exported +cannot be imported through the package resolution, unless using an absolute URL. + + diff --git a/errors/err_tls_cert_altname_invalid.md b/errors/err_tls_cert_altname_invalid.md index 15e7e075..520f1b03 100644 --- a/errors/err_tls_cert_altname_invalid.md +++ b/errors/err_tls_cert_altname_invalid.md @@ -1,5 +1,5 @@ -While using TLS, the hostname/IP of the peer did not match any of the +While using TLS, the host name/IP of the peer did not match any of the `subjectAltNames` in its certificate. diff --git a/errors/err_tls_required_server_name.md b/errors/err_tls_required_server_name.md index 07848a81..a561427d 100644 --- a/errors/err_tls_required_server_name.md +++ b/errors/err_tls_required_server_name.md @@ -1,5 +1,5 @@ While using TLS, the `server.addContext()` method was called without providing -a hostname in the first parameter. +a host name in the first parameter. diff --git a/errors/err_tty_writable_not_readable.md b/errors/err_tty_writable_not_readable.md index f565a826..a9b8591b 100644 --- a/errors/err_tty_writable_not_readable.md +++ b/errors/err_tty_writable_not_readable.md @@ -63,5 +63,6 @@ such as `process.stdout.on('data')`. + diff --git a/errors/err_wasi_already_started.md b/errors/err_wasi_already_started.md index c88243fe..b0801fec 100644 --- a/errors/err_wasi_already_started.md +++ b/errors/err_wasi_already_started.md @@ -1,4 +1,4 @@ The WASI instance has already started. - + diff --git a/errors/err_worker_init_failed.md b/errors/err_worker_init_failed.md new file mode 100644 index 00000000..ae9ae4b5 --- /dev/null +++ b/errors/err_worker_init_failed.md @@ -0,0 +1,4 @@ + +The `Worker` initialization failed. + + diff --git a/esm/approach_1_use_an_es_module_wrapper.md b/esm/approach_1_use_an_es_module_wrapper.md index 14800c09..d5309509 100644 --- a/esm/approach_1_use_an_es_module_wrapper.md +++ b/esm/approach_1_use_an_es_module_wrapper.md @@ -11,8 +11,8 @@ CommonJS entry point for `require`. "type": "module", "main": "./index.cjs", "exports": { - "require": "./index.cjs", - "import": "./wrapper.mjs" + "import": "./wrapper.mjs", + "require": "./index.cjs" } } ``` diff --git a/esm/approach_2_isolate_state.md b/esm/approach_2_isolate_state.md index 4dbb3add..9c491e27 100644 --- a/esm/approach_2_isolate_state.md +++ b/esm/approach_2_isolate_state.md @@ -1,6 +1,6 @@ -The most straightforward `package.json` would be one that defines the separate -CommonJS and ES module entry points directly: +A `package.json` file can define the separate CommonJS and ES module entry +points directly: ```js diff --git a/esm/code_getglobalpreloadcode_code_hook.md b/esm/code_getglobalpreloadcode_code_hook.md new file mode 100644 index 00000000..c65c4926 --- /dev/null +++ b/esm/code_getglobalpreloadcode_code_hook.md @@ -0,0 +1,32 @@ + +> Note: The loaders API is being redesigned. This hook may disappear or its +> signature may change. Do not rely on the API described below. + +Sometimes it can be necessary to run some code inside of the same global scope +that the application will run in. This hook allows to return a string that will +be ran as sloppy-mode script on startup. + +Similar to how CommonJS wrappers work, the code runs in an implicit function +scope. The only argument is a `require`-like function that can be used to load +builtins like "fs": `getBuiltin(request: string)`. + +If the code needs more advanced `require` features, it will have to construct +its own `require` using `module.createRequire()`. + +```js +/** + * @returns {string} Code to run before application startup + */ +export function getGlobalPreloadCode() { + return `\ +globalThis.someInjectedProperty = 42; +console.log('I just set some globals!'); + +const { createRequire } = getBuiltin('module'); + +const require = createRequire(process.cwd + '/'); +// [...] +`; +} +``` + diff --git a/esm/code_package_json_code_code_main_code.md b/esm/code_package_json_code_code_main_code.md deleted file mode 100644 index 4587284b..00000000 --- a/esm/code_package_json_code_code_main_code.md +++ /dev/null @@ -1,36 +0,0 @@ - -The `package.json` `"main"` field defines the entry point for a package, -whether the package is included into CommonJS via `require` or into an ES -module via `import`. - - -```js -// ./node_modules/es-module-package/package.json -{ - "type": "module", - "main": "./src/index.js" -} -``` - -```js -// ./my-app.mjs - -import { something } from 'es-module-package'; -// Loads from ./node_modules/es-module-package/src/index.js -``` - -An attempt to `require` the above `es-module-package` would attempt to load -`./node_modules/es-module-package/src/index.js` as CommonJS, which would throw -an error as Node.js would not be able to parse the `export` statement in -CommonJS. - -As with `import` statements, for ES module usage the value of `"main"` must be -a full path including extension: `"./index.mjs"`, not `"./index"`. - -If the `package.json` `"type"` field is omitted, a `.js` file in `"main"` will -be interpreted as CommonJS. - -The `"main"` field can point to exactly one file, regardless of whether the -package is referenced via `require` (in a CommonJS context) or `import` (in an -ES module context). - diff --git a/esm/conditional_exports.md b/esm/conditional_exports.md index 9d2617e5..4dc5d563 100644 --- a/esm/conditional_exports.md +++ b/esm/conditional_exports.md @@ -3,47 +3,64 @@ Conditional exports provide a way to map to different paths depending on certain conditions. They are supported for both CommonJS and ES module imports. For example, a package that wants to provide different ES module exports for -Node.js and the browser can be written: +`require()` and `import` can be written: ```js -// ./node_modules/pkg/package.json +// package.json { - "type": "module", - "main": "./index.js", + "main": "./main-require.cjs", "exports": { - "./feature": { - "import": "./feature-default.js", - "browser": "./feature-browser.js" - } - } + "import": "./main-module.js", + "require": "./main-require.cjs" + }, + "type": "module" } ``` -When resolving the `"."` export, if no matching target is found, the `"main"` -will be used as the final fallback. +Node.js supports the following conditions: -The conditions supported in Node.js condition matching: - -* `"default"` - the generic fallback that will always match. Can be a CommonJS - or ES module file. * `"import"` - matched when the package is loaded via `import` or - `import()`. Can be any module format, this field does not set the type - interpretation. -* `"node"` - matched for any Node.js environment. Can be a CommonJS or ES - module file. + `import()`. Can reference either an ES module or CommonJS file, as both + `import` and `import()` can load either ES module or CommonJS sources. * `"require"` - matched when the package is loaded via `require()`. + As `require()` only supports CommonJS, the referenced file must be CommonJS. +* `"node"` - matched for any Node.js environment. Can be a CommonJS or ES + module file. _This condition should always come after `"import"` or + `"require"`._ +* `"default"` - the generic fallback that will always match. Can be a CommonJS + or ES module file. _This condition should always come last._ Condition matching is applied in object order from first to last within the -`"exports"` object. - -Using the `"require"` condition it is possible to define a package that will -have a different exported value for CommonJS and ES modules, which can be a -hazard in that it can result in having two separate instances of the same -package in use in an application, which can cause a number of bugs. +`"exports"` object. _The general rule is that conditions should be used +from most specific to least specific in object order._ Other conditions such as `"browser"`, `"electron"`, `"deno"`, `"react-native"`, -etc. could be defined in other runtimes or tools. Condition names must not start -with `"."` or be numbers. Further restrictions, definitions or guidance on -condition names may be provided in future. +etc. are ignored by Node.js but may be used by other runtimes or tools. +Further restrictions, definitions or guidance on condition names may be +provided in the future. + +Using the `"import"` and `"require"` conditions can lead to some hazards, +which are explained further in +[the dual CommonJS/ES module packages section][]. + +Conditional exports can also be extended to exports subpaths, for example: + + +```js +{ + "main": "./main.js", + "exports": { + ".": "./main.js", + "./feature": { + "browser": "./feature-browser.js", + "default": "./feature.js" + } + } +} +``` + +Defines a package where `require('pkg/feature')` and `import 'pkg/feature'` +could provide different implementations between the browser and Node.js, +given third-party tool support for a `"browser"` condition. diff --git a/esm/customizing_esm_specifier_resolution_algorithm.md b/esm/customizing_esm_specifier_resolution_algorithm.md index 81d43c35..9e0b4489 100644 --- a/esm/customizing_esm_specifier_resolution_algorithm.md +++ b/esm/customizing_esm_specifier_resolution_algorithm.md @@ -41,6 +41,8 @@ success! + + diff --git a/esm/enabling.md b/esm/enabling.md index f003e5f4..57ac49c7 100644 --- a/esm/enabling.md +++ b/esm/enabling.md @@ -10,9 +10,8 @@ ES module code: * Files ending in `.mjs`. -* Files ending in `.js`, or extensionless files, when the nearest parent - `package.json` file contains a top-level field `"type"` with a value of - `"module"`. +* Files ending in `.js` when the nearest parent `package.json` file contains a + top-level field `"type"` with a value of `"module"`. * Strings passed in as an argument to `--eval` or `--print`, or piped to `node` via `STDIN`, with the flag `--input-type=module`. @@ -27,9 +26,8 @@ or when referenced by `import` statements within ES module code: * Files ending in `.cjs`. -* Files ending in `.js`, or extensionless files, when the nearest parent - `package.json` file contains a top-level field `"type"` with a value of - `"commonjs"`. +* Files ending in `.js` when the nearest parent `package.json` file contains a + top-level field `"type"` with a value of `"commonjs"`. * Strings passed in as an argument to `--eval` or `--print`, or piped to `node` via `STDIN`, with the flag `--input-type=commonjs`. diff --git a/esm/exports_sugar.md b/esm/exports_sugar.md index ffee250c..a85d0d11 100644 --- a/esm/exports_sugar.md +++ b/esm/exports_sugar.md @@ -23,46 +23,3 @@ can be written: } ``` -When using [Conditional Exports][], the rule is that all keys in the object -mapping must not start with a `"."` otherwise they would be indistinguishable -from exports subpaths. - - -```js -{ - "exports": { - ".": { - "import": "./main.js", - "require": "./main.cjs" - } - } -} -``` - -can be written: - - -```js -{ - "exports": { - "import": "./main.js", - "require": "./main.cjs" - } -} -``` - -If writing any exports value that mixes up these two forms, an error will be -thrown: - - -```js -{ - // Throws on resolution! - "exports": { - "./feature": "./lib/feature.js", - "import": "./main.js", - "require": "./main.cjs" - } -} -``` - diff --git a/esm/import_expressions.md b/esm/import_expressions.md index d8507d13..2b564796 100644 --- a/esm/import_expressions.md +++ b/esm/import_expressions.md @@ -1,10 +1,4 @@ -Dynamic `import()` is supported in both CommonJS and ES modules. It can be used -to include ES module files from CommonJS code. - -```js -(async () => { - await import('./my-app.mjs'); -})(); -``` +[Dynamic `import()`][] is supported in both CommonJS and ES modules. It can be +used to include ES module files from CommonJS code. diff --git a/esm/import_statements.md b/esm/import_statements.md index 7b0a0bd2..5d5d5357 100644 --- a/esm/import_statements.md +++ b/esm/import_statements.md @@ -11,8 +11,8 @@ can either be an URL-style relative path like `'./file.mjs'` or a package name like `'fs'`. Like in CommonJS, 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 need to be accessed via the path +the package name; unless the package’s `package.json` contains an `"exports"` +field, in which case files within packages need to be accessed via the path defined in `"exports"`. ```js @@ -28,3 +28,6 @@ import packageMain from 'commonjs-package'; // Works import { method } from 'commonjs-package'; // Errors ``` +It is also possible to +[import an ES or CommonJS module for its side effects only][]. + diff --git a/esm/main_entry_point_export.md b/esm/main_entry_point_export.md new file mode 100644 index 00000000..9fce1e7d --- /dev/null +++ b/esm/main_entry_point_export.md @@ -0,0 +1,23 @@ + +To set the main entry point for a package, it is advisable to define both +`"exports"` and `"main"` in the package’s `package.json` file: + + +```js +{ + "main": "./main.js", + "exports": "./main.js" +} +``` + +The benefit of doing this is that when using the `"exports"` field all +subpaths of the package will no longer be available to importers under +`require('pkg/subpath.js')`, and instead they will get a new error, +`ERR_PACKAGE_PATH_NOT_EXPORTED`. + +This encapsulation of exports provides more reliable guarantees +about package interfaces for tools and when handling semver upgrades for a +package. It is not a strong encapsulation since a direct require of any +absolute subpath of the package such as +`require('/path/to/node_modules/pkg/subpath.js')` will still load `subpath.js`. + diff --git a/esm/nested_conditions.md b/esm/nested_conditions.md new file mode 100644 index 00000000..b24a3489 --- /dev/null +++ b/esm/nested_conditions.md @@ -0,0 +1,25 @@ + +In addition to direct mappings, Node.js also supports nested condition objects. + +For example, to define a package that only has dual mode entry points for +use in Node.js but not the browser: + + +```js +{ + "main": "./main.js", + "exports": { + "browser": "./feature-browser.mjs", + "node": { + "import": "./feature-node.mjs", + "require": "./feature-node.cjs" + } + } +} +``` + +Conditions continue to be matched in order as with flat conditions. If +a nested conditional does not have any mapping it will continue checking +the remaining conditions of the parent condition. In this way nested +conditions behave analogously to nested JavaScript `if` statements. + diff --git a/esm/no_require_resolve.md b/esm/no_require_resolve.md new file mode 100644 index 00000000..371e7ce2 --- /dev/null +++ b/esm/no_require_resolve.md @@ -0,0 +1,25 @@ + +Former use cases relying on `require.resolve` to determine the resolved path +of a module can be supported via `import.meta.resolve`, which is experimental +and supported via the `--experimental-import-meta-resolve` flag: + +```js +(async () => { + const dependencyAsset = await import.meta.resolve('component-lib/asset.css'); +})(); +``` + +`import.meta.resolve` also accepts a second argument which is the parent module +from which to resolve from: + +```js +(async () => { + // Equivalent to import.meta.resolve('./dep') + await import.meta.resolve('./dep', import.meta.url); +})(); +``` + +This function is asynchronous since the ES module resolver in Node.js is +asynchronous. With the introduction of [Top-Level Await][], these use cases +will be easier as they won't require an async function wrapper. + diff --git a/esm/package_entry_points.md b/esm/package_entry_points.md index f6e1e022..45db5142 100644 --- a/esm/package_entry_points.md +++ b/esm/package_entry_points.md @@ -1,12 +1,27 @@ -There are two fields that can define entry points for a package: `"main"` and -`"exports"`. The `"main"` field is supported in all versions of Node.js, but its -capabilities are limited: it only defines the main entry point of the package. -The `"exports"` field, part of [Package Exports][], provides an alternative to -`"main"` where the package main entry point can be defined while also -encapsulating the package, preventing any other entry points besides those -defined in `"exports"`. If package entry points are defined in both `"main"` and -`"exports"`, the latter takes precedence in versions of Node.js that support -`"exports"`. [Conditional Exports][] can also be used within `"exports"` to -define different package entry points per environment. +In a package’s `package.json` file, two fields can define entry points for a +package: `"main"` and `"exports"`. The `"main"` field is supported in all +versions of Node.js, but its capabilities are limited: it only defines the main +entry point of the package. + +The `"exports"` field provides an alternative to `"main"` where the package +main entry point can be defined while also encapsulating the package, preventing +any other entry points besides those defined in `"exports"`. If package entry +points are defined in both `"main"` and `"exports"`, the latter takes precedence +in versions of Node.js that support `"exports"`. [Conditional Exports][] can +also be used within `"exports"` to define different package entry points per +environment, including whether the package is referenced via `require` or via +`import`. + +If both `"exports"` and `"main"` are defined, the `"exports"` field takes +precedence over `"main"`. + +Both `"main"` and `"exports"` entry points are not specific to ES modules or +CommonJS; `"main"` will be overridden by `"exports"` in a `require` so it is +not a CommonJS fallback. + +This is important with regard to `require`, since `require` of ES module files +throws an error in all versions of Node.js. To create a package that works both +in modern Node.js via `import` and `require` and also legacy Node.js versions, +see [the dual CommonJS/ES module packages section][]. diff --git a/esm/package_exports.md b/esm/package_exports.md deleted file mode 100644 index 3047c7ae..00000000 --- a/esm/package_exports.md +++ /dev/null @@ -1,86 +0,0 @@ - -By default, all subpaths from a package can be imported (`import 'pkg/x.js'`). -Custom subpath aliasing and encapsulation can be provided through the -`"exports"` field. - - -```js -// ./node_modules/es-module-package/package.json -{ - "exports": { - "./submodule": "./src/submodule.js" - } -} -``` - -```js -import submodule from 'es-module-package/submodule'; -// Loads ./node_modules/es-module-package/src/submodule.js -``` - -In addition to defining an alias, subpaths not defined by `"exports"` will -throw when an attempt is made to import them: - -```js -import submodule from 'es-module-package/private-module.js'; -// Throws ERR_MODULE_NOT_FOUND -``` - -> Note: this is not a strong encapsulation as any private modules can still be -> loaded by absolute paths. - -Folders can also be mapped with package exports: - - -```js -// ./node_modules/es-module-package/package.json -{ - "exports": { - "./features/": "./src/features/" - } -} -``` - -```js -import feature from 'es-module-package/features/x.js'; -// Loads ./node_modules/es-module-package/src/features/x.js -``` - -If a package has no exports, setting `"exports": false` can be used instead of -`"exports": {}` to indicate the package does not intend for submodules to be -exposed. - -Any invalid exports entries will be ignored. This includes exports not -starting with `"./"` or a missing trailing `"/"` for directory exports. - -Array fallback support is provided for exports, similarly to import maps -in order to be forwards-compatible with possible fallback workflows in future: - - -```js -{ - "exports": { - "./submodule": ["not:valid", "./submodule.js"] - } -} -``` - -Since `"not:valid"` is not a supported target, `"./submodule.js"` is used -instead as the fallback, as if it were the only target. - -Defining a `"."` export will define the main entry point for the package, -and will always take precedence over the `"main"` field in the `package.json`. - -This allows defining a different entry point for Node.js versions that support -ECMAScript modules and versions that don't, for example: - - -```js -{ - "main": "./main-legacy.cjs", - "exports": { - ".": "./main-modern.cjs" - } -} -``` - diff --git a/esm/package_exports_fallbacks.md b/esm/package_exports_fallbacks.md new file mode 100644 index 00000000..6f7b6f46 --- /dev/null +++ b/esm/package_exports_fallbacks.md @@ -0,0 +1,16 @@ + +For possible new specifier support in future, array fallbacks are +supported for all invalid specifiers: + + +```js +{ + "exports": { + "./submodule": ["not:valid", "./submodule.js"] + } +} +``` + +Since `"not:valid"` is not a valid specifier, `"./submodule.js"` is used +instead as the fallback, as if it were the only target. + diff --git a/esm/package_json_type_field.md b/esm/package_json_type_field.md index 9c7fc496..a3a7da9b 100644 --- a/esm/package_json_type_field.md +++ b/esm/package_json_type_field.md @@ -1,7 +1,7 @@ -Files ending with `.js` or lacking any extension will be loaded as ES modules -when the nearest parent `package.json` file contains a top-level field `"type"` -with a value of `"module"`. +Files ending with `.js` will be loaded as ES modules when the nearest parent +`package.json` file contains a top-level field `"type"` with a value of +`"module"`. The nearest parent `package.json` is defined as the first `package.json` found when searching in the current folder, that folder’s parent, and so on up @@ -21,14 +21,12 @@ node --experimental-modules my-app.js # Runs as ES module ``` If the nearest parent `package.json` lacks a `"type"` field, or contains -`"type": "commonjs"`, extensionless and `.js` files are treated as CommonJS. -If the volume root is reached and no `package.json` is found, -Node.js defers to the default, a `package.json` with no `"type"` -field. "Extensionless" refers to file paths which do not contain -an extension as opposed to optionally dropping a file extension in a specifier. - -`import` statements of `.js` and extensionless files are treated as ES modules -if the nearest parent `package.json` contains `"type": "module"`. +`"type": "commonjs"`, `.js` files are treated as CommonJS. If the volume root is +reached and no `package.json` is found, Node.js defers to the default, a +`package.json` with no `"type"` field. + +`import` statements of `.js` files are treated as ES modules if the nearest +parent `package.json` contains `"type": "module"`. ```js // my-app.js, part of the same example as above diff --git a/esm/package_scope_and_file_extensions.md b/esm/package_scope_and_file_extensions.md index f2a91c3f..6f8ba741 100644 --- a/esm/package_scope_and_file_extensions.md +++ b/esm/package_scope_and_file_extensions.md @@ -1,12 +1,11 @@ -A folder containing a `package.json` file, and all subfolders below that -folder down until the next folder containing another `package.json`, is -considered a _package scope_. The `"type"` field defines how `.js` and -extensionless files should be treated within a particular `package.json` file’s -package scope. Every package in a project’s `node_modules` folder contains its -own `package.json` file, so each project’s dependencies have their own package -scopes. A `package.json` lacking a `"type"` field is treated as if it contained -`"type": "commonjs"`. +A folder containing a `package.json` file, and all subfolders below that folder +down until the next folder containing another `package.json`, is considered a +_package scope_. The `"type"` field defines how `.js` files should be treated +within a particular `package.json` file’s package scope. Every package in a +project’s `node_modules` folder contains its own `package.json` file, so each +project’s dependencies have their own package scopes. A `package.json` lacking a +`"type"` field is treated as if it contained `"type": "commonjs"`. The package scope applies not only to initial entry points (`node --experimental-modules my-app.js`) but also to files referenced by `import` diff --git a/esm/resolver_algorithm.md b/esm/resolver_algorithm.md index 24708a8a..0aa19a2e 100644 --- a/esm/resolver_algorithm.md +++ b/esm/resolver_algorithm.md @@ -1,13 +1,14 @@ 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, in addition to the unique module -format for that resolved URL given by the **ESM_FORMAT** routine. +module specifier relative to a parentURL. -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. +The algorithm to determine the module format of a resolved URL is +provided by **ESM_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. @@ -15,6 +16,17 @@ of these top-level routines unless stated otherwise. _defaultEnv_ is the conditional environment name priority array, `["node", "import"]`. +The resolver can throw the following errors: +* _Invalid Module Specifier_: Module specifier is an invalid URL, package name + or package subpath specifier. +* _Invalid Package Configuration_: package.json configuration is invalid or + contains an invalid configuration. +* _Invalid Package Target_: Package exports define a target module within 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. +* _Module Not Found_: The package or module requested does not exist. +
Resolver algorithm specification @@ -25,7 +37,7 @@ _defaultEnv_ is the conditional environment name priority array, > 1. Set _resolvedURL_ to the result of parsing and reserializing > _specifier_ as a URL. > 1. Otherwise, if _specifier_ starts with _"/"_, then -> 1. Throw an _Invalid Specifier_ error. +> 1. Throw an _Invalid Module Specifier_ error. > 1. Otherwise, if _specifier_ starts with _"./"_ or _"../"_, then > 1. Set _resolvedURL_ to the URL resolution of _specifier_ relative to > _parentURL_. @@ -35,26 +47,28 @@ _defaultEnv_ is the conditional environment name priority array, > **PACKAGE_RESOLVE**(_specifier_, _parentURL_). > 1. If _resolvedURL_ contains any percent encodings of _"/"_ or _"\\"_ (_"%2f"_ > and _"%5C"_ respectively), then -> 1. Throw an _Invalid Specifier_ error. -> 1. If the file at _resolvedURL_ does not exist, then +> 1. Throw an _Invalid Module Specifier_ error. +> 1. If _resolvedURL_ does not end with a trailing _"/"_ and the file at +> _resolvedURL_ does not exist, then > 1. Throw a _Module Not Found_ error. > 1. Set _resolvedURL_ to the real path of _resolvedURL_. > 1. Let _format_ be the result of **ESM_FORMAT**(_resolvedURL_). > 1. Load _resolvedURL_ as module format, _format_. +> 1. Return _resolvedURL_. **PACKAGE_RESOLVE**(_packageSpecifier_, _parentURL_) > 1. Let _packageName_ be *undefined*. > 1. Let _packageSubpath_ be *undefined*. > 1. If _packageSpecifier_ is an empty string, then -> 1. Throw an _Invalid Specifier_ error. +> 1. Throw an _Invalid Module Specifier_ error. > 1. Otherwise, > 1. If _packageSpecifier_ does not contain a _"/"_ separator, then -> 1. Throw an _Invalid Specifier_ error. +> 1. Throw an _Invalid Module Specifier_ error. > 1. Set _packageName_ to the substring of _packageSpecifier_ > until the second _"/"_ separator or the end of the string. > 1. If _packageName_ starts with _"."_ or contains _"\\"_ or _"%"_, then -> 1. Throw an _Invalid Specifier_ error. +> 1. Throw an _Invalid Module Specifier_ error. > 1. Let _packageSubpath_ be _undefined_. > 1. If the length of _packageSpecifier_ is greater than the length of > _packageName_, then @@ -62,13 +76,13 @@ _defaultEnv_ is the conditional environment name priority array, > _packageSpecifier_ from the position at the length of _packageName_. > 1. If _packageSubpath_ contains any _"."_ or _".."_ segments or percent > encoded strings for _"/"_ or _"\\"_, then -> 1. Throw an _Invalid Specifier_ error. +> 1. Throw an _Invalid Module Specifier_ error. > 1. Set _selfUrl_ to the result of > **SELF_REFERENCE_RESOLVE**(_packageName_, _packageSubpath_, _parentURL_). > 1. If _selfUrl_ isn't empty, return _selfUrl_. > 1. If _packageSubpath_ is _undefined_ and _packageName_ is a Node.js builtin > module, then -> 1. Return the string _"node:"_ concatenated with _packageSpecifier_. +> 1. Return the string _"nodejs:"_ concatenated with _packageSpecifier_. > 1. While _parentURL_ is not the file system root, > 1. Let _packageURL_ be the URL resolution of _"node_modules/"_ > concatenated with _packageSpecifier_, relative to _parentURL_. @@ -77,6 +91,8 @@ _defaultEnv_ is the conditional environment name priority array, > 1. Set _parentURL_ to the parent URL path of _parentURL_. > 1. Continue the next loop iteration. > 1. Let _pjson_ be the result of **READ_PACKAGE_JSON**(_packageURL_). +> 1. If _packageSubpath_ is equal to _"./"_, then +> 1. Return _packageURL_ + _"/"_. > 1. If _packageSubpath_ is _undefined__, then > 1. Return the result of **PACKAGE_MAIN_RESOLVE**(_packageURL_, > _pjson_). @@ -98,6 +114,8 @@ _defaultEnv_ is the conditional environment name priority array, > 1. If _pjson_ does not include an _"exports"_ property, then > 1. Return **undefined**. > 1. If _pjson.name_ is equal to _packageName_, then +> 1. If _packageSubpath_ is equal to _"./"_, then +> 1. Return _packageURL_ + _"/"_. > 1. If _packageSubpath_ is _undefined_, then > 1. Return the result of **PACKAGE_MAIN_RESOLVE**(_packageURL_, _pjson_). > 1. Otherwise, @@ -115,7 +133,7 @@ _defaultEnv_ is the conditional environment name priority array, > 1. Throw a _Module Not Found_ error. > 1. If _pjson.exports_ is not **null** or **undefined**, then > 1. If _exports_ is an Object with both a key starting with _"."_ and a key -> not starting with _"."_, throw an "Invalid Package Configuration" error. +> not starting with _"."_, throw an _Invalid Package Configuration_ error. > 1. If _pjson.exports_ is a String or Array, or an Object containing no > keys starting with _"."_, then > 1. Return **PACKAGE_EXPORTS_TARGET_RESOLVE**(_packageURL_, @@ -124,6 +142,7 @@ _defaultEnv_ is the conditional environment name priority array, > 1. Let _mainExport_ be the _"."_ property in _pjson.exports_. > 1. Return **PACKAGE_EXPORTS_TARGET_RESOLVE**(_packageURL_, > _mainExport_, _""_). +> 1. Throw a _Package Path Not Exported_ error. > 1. If _pjson.main_ is a String, then > 1. Let _resolvedMain_ be the URL resolution of _packageURL_, "/", and > _pjson.main_. @@ -138,7 +157,7 @@ _defaultEnv_ is the conditional environment name priority array, **PACKAGE_EXPORTS_RESOLVE**(_packageURL_, _packagePath_, _exports_) > 1. If _exports_ is an Object with both a key starting with _"."_ and a key not -> starting with _"."_, throw an "Invalid Package Configuration" error. +> starting with _"."_, throw an _Invalid Package Configuration_ error. > 1. If _exports_ is an Object and all keys of _exports_ start with _"."_, then > 1. Set _packagePath_ to _"./"_ concatenated with _packagePath_. > 1. If _packagePath_ is a key of _exports_, then @@ -154,59 +173,58 @@ _defaultEnv_ is the conditional environment name priority array, > of the length of _directory_. > 1. Return **PACKAGE_EXPORTS_TARGET_RESOLVE**(_packageURL_, _target_, > _subpath_, _defaultEnv_). -> 1. Throw a _Module Not Found_ error. +> 1. Throw a _Package Path Not Exported_ error. **PACKAGE_EXPORTS_TARGET_RESOLVE**(_packageURL_, _target_, _subpath_, _env_) > 1. If _target_ is a String, then -> 1. If _target_ does not start with _"./"_, throw a _Module Not Found_ -> error. -> 1. If _subpath_ has non-zero length and _target_ does not end with _"/"_, -> throw a _Module Not Found_ error. -> 1. If _target_ or _subpath_ contain any _"node_modules"_ segments including -> _"node_modules"_ percent-encoding, throw a _Module Not Found_ error. +> 1. If _target_ does not start with _"./"_ or contains any _"node_modules"_ +> segments including _"node_modules"_ percent-encoding, throw an +> _Invalid Package Target_ error. > 1. Let _resolvedTarget_ be the URL resolution of the concatenation of > _packageURL_ and _target_. -> 1. If _resolvedTarget_ is contained in _packageURL_, then -> 1. Let _resolved_ be the URL resolution of the concatenation of -> _subpath_ and _resolvedTarget_. -> 1. If _resolved_ is contained in _resolvedTarget_, then -> 1. Return _resolved_. +> 1. If _resolvedTarget_ is not contained in _packageURL_, throw an +> _Invalid Package Target_ error. +> 1. If _subpath_ has non-zero length and _target_ does not end with _"/"_, +> throw an _Invalid Module Specifier_ error. +> 1. Let _resolved_ be the URL resolution of the concatenation of +> _subpath_ and _resolvedTarget_. +> 1. If _resolved_ is not contained in _resolvedTarget_, throw an +> _Invalid Module Specifier_ error. +> 1. Return _resolved_. > 1. 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. > 1. For each property _p_ of _target_, in object insertion order as, > 1. If _env_ contains an entry for _p_, then > 1. Let _targetValue_ be the value of the _p_ property in _target_. -> 1. Let _resolved_ be the result of **PACKAGE_EXPORTS_TARGET_RESOLVE** -> (_packageURL_, _targetValue_, _subpath_, _env_). -> 1. Assert: _resolved_ is a String. -> 1. Return _resolved_. +> 1. Return the result of **PACKAGE_EXPORTS_TARGET_RESOLVE**( +> _packageURL_, _targetValue_, _subpath_, _env_), continuing the +> loop on any _Package Path Not Exported_ error. +> 1. Throw a _Package Path Not Exported_ error. > 1. Otherwise, if _target_ is an Array, then +> 1. If _target.length is zero, throw an _Invalid Package Target_ error. > 1. For each item _targetValue_ in _target_, do > 1. If _targetValue_ is an Array, continue the loop. -> 1. Let _resolved_ be the result of -> **PACKAGE_EXPORTS_TARGET_RESOLVE**(_packageURL_, _targetValue_, -> _subpath_, _env_), continuing the loop on abrupt completion. -> 1. Assert: _resolved_ is a String. -> 1. Return _resolved_. -> 1. Throw a _Module Not Found_ error. +> 1. Return the result of **PACKAGE_EXPORTS_TARGET_RESOLVE**(_packageURL_, +> _targetValue_, _subpath_, _env_), continuing the loop on any +> _Package Path Not Exported_ or _Invalid Package Target_ error. +> 1. Throw the last fallback resolution error. +> 1. Otherwise throw an _Invalid Package Target_ error. **ESM_FORMAT**(_url_) -> 1. Assert: _url_ corresponds to an existing file pathname. +> 1. Assert: _url_ corresponds to an existing file. > 1. Let _pjson_ be the result of **READ_PACKAGE_SCOPE**(_url_). > 1. If _url_ ends in _".mjs"_, then > 1. Return _"module"_. > 1. If _url_ ends in _".cjs"_, then > 1. Return _"commonjs"_. > 1. If _pjson?.type_ exists and is _"module"_, then -> 1. If _url_ ends in _".js"_ or lacks a file extension, then +> 1. If _url_ ends in _".js"_, then > 1. Return _"module"_. > 1. Throw an _Unsupported File Extension_ error. > 1. Otherwise, -> 1. If _url_ lacks a file extension, then -> 1. Return _"commonjs"_. > 1. Throw an _Unsupported File Extension_ error. **READ_PACKAGE_SCOPE**(_url_) diff --git a/esm/self_referencing_a_package_using_its_name.md b/esm/self_referencing_a_package_using_its_name.md new file mode 100644 index 00000000..ca1f3faa --- /dev/null +++ b/esm/self_referencing_a_package_using_its_name.md @@ -0,0 +1,44 @@ + +Within a package, the values defined in the package’s +`package.json` `"exports"` field can be referenced via the package’s name. +For example, assuming the `package.json` is: + +```json +// package.json +{ + "name": "a-package", + "exports": { + ".": "./main.mjs", + "./foo": "./foo.js" + } +} +``` + +Then any module _in that package_ can reference an export in the package itself: + +```js +// ./a-module.mjs +import { something } from 'a-package'; // Imports "something" from ./main.mjs. +``` + +Self-referencing is available only if `package.json` has `exports`, and will +allow importing only what that `exports` (in the `package.json`) allows. +So the code below, given the package above, will generate a runtime error: + +```js +// ./another-module.mjs + +// Imports "another" from ./m.mjs. Fails because +// the "package.json" "exports" field +// does not provide an export named "./m.mjs". +import { another } from 'a-package/m.mjs'; +``` + +Self-referencing is also available when using `require`, both in an ES module, +and in a CommonJS one. For example, this code will also work: + +```js +// ./a-module.js +const { something } = require('a-package/foo'); // Loads from ./foo.js. +``` + diff --git a/esm/subpath_exports.md b/esm/subpath_exports.md new file mode 100644 index 00000000..28b96d4b --- /dev/null +++ b/esm/subpath_exports.md @@ -0,0 +1,56 @@ + +When using the `"exports"` field, custom subpaths can be defined along +with the main entry point by treating the main entry point as the +`"."` subpath: + + +```js +{ + "main": "./main.js", + "exports": { + ".": "./main.js", + "./submodule": "./src/submodule.js" + } +} +``` + +Now only the defined subpath in `"exports"` can be imported by a +consumer: + +```js +import submodule from 'es-module-package/submodule'; +// Loads ./node_modules/es-module-package/src/submodule.js +``` + +While other subpaths will error: + +```js +import submodule from 'es-module-package/private-module.js'; +// Throws ERR_PACKAGE_PATH_NOT_EXPORTED +``` + +Entire folders can also be mapped with package exports: + + +```js +// ./node_modules/es-module-package/package.json +{ + "exports": { + "./features/": "./src/features/" + } +} +``` + +With the above, all modules within the `./src/features/` folder +are exposed deeply to `import` and `require`: + +```js +import feature from 'es-module-package/features/x.js'; +// Loads ./node_modules/es-module-package/src/features/x.js +``` + +When using folder mappings, ensure that you do want to expose every +module inside the subfolder. Any modules which are not public +should be moved to another folder to retain the encapsulation +benefits of exports. + diff --git a/esm/transpiler_loader.md b/esm/transpiler_loader.md index 5f865e30..fcd80053 100644 --- a/esm/transpiler_loader.md +++ b/esm/transpiler_loader.md @@ -4,7 +4,7 @@ JavaScript using the [`transformSource` hook][]. Before that hook gets called, however, other hooks need to tell Node.js not to throw an error on unknown file types; and to tell Node.js how to load this new file type. -This is obviously less performant than transpiling source files before running +This is less performant than transpiling source files before running Node.js; a transpiler loader should only be used for development and testing purposes. diff --git a/esm/writing_dual_packages_while_avoiding_or_minimizing_hazards.md b/esm/writing_dual_packages_while_avoiding_or_minimizing_hazards.md index 38217056..a3ddf5a8 100644 --- a/esm/writing_dual_packages_while_avoiding_or_minimizing_hazards.md +++ b/esm/writing_dual_packages_while_avoiding_or_minimizing_hazards.md @@ -9,7 +9,7 @@ would be usable by any version of Node.js, since `import` can refer to CommonJS files; but it would not provide any of the advantages of using ES module syntax. A package could also switch from CommonJS to ES module syntax in a breaking -change version bump. This has the obvious disadvantage that the newest version +change version bump. This has the disadvantage that the newest version of the package would only be usable in ES module-supporting versions of Node.js. Every pattern has tradeoffs, but there are two broad approaches that satisfy the diff --git a/fs/filehandle_appendfile_data_options.md b/fs/filehandle_appendfile_data_options.md index 4f31f8ef..32fa218b 100644 --- a/fs/filehandle_appendfile_data_options.md +++ b/fs/filehandle_appendfile_data_options.md @@ -4,15 +4,11 @@ added: v10.0.0 * `data` {string|Buffer} * `options` {Object|string} * `encoding` {string|null} **默认值:** `'utf8'`。 - * `mode` {integer} **默认值:** `0o666` - * `flag` {string} 参阅[支持的文件系统标志][support of file system `flags`]。**默认值:** `'a'`。 * 返回: {Promise} -异步地将数据追加到文件,如果文件尚不存在则创建该文件。 -`data` 可以是字符串或 [`Buffer`]。 -`Promise` 将会在成功时解决,且不带参数。 +[`filehandle.writeFile()`] 的别名。 -如果 `options` 是字符串,则它指定字符编码。 +当在文件句柄上进行操作时,无法将模式更改为使用 [`fsPromises.open()`] 设置的模式。 +因此,这等效于 [`filehandle.writeFile()`]。 -`FileHandle` 必须被打开用以追加。 diff --git a/fs/filehandle_readfile_options.md b/fs/filehandle_readfile_options.md index d25d0ea7..0a447670 100644 --- a/fs/filehandle_readfile_options.md +++ b/fs/filehandle_readfile_options.md @@ -2,8 +2,7 @@ added: v10.0.0 --> * `options` {Object|string} - * `encoding` {string|null} **默认值:** `null` - * `flag` {string} 参阅[支持的文件系统标志][support of file system `flags`]。**默认值:** `'r'`。 + * `encoding` {string|null} **默认值:** `null`。 * 返回: {Promise} 异步地读取文件的全部内容。 @@ -14,10 +13,6 @@ added: v10.0.0 如果 `options` 是字符串,则它指定字符编码。 -当 `path` 是目录时,`fsPromises.readFile()` 的行为是特定于平台的。 -在 macOS、Linux 和 Windows 上,promise 将会被拒绝并带上一个错误。 -在 FreeBSD 上,则将会返回目录内容的表示。 - `FileHandle` 必须支持读取。 如果对文件句柄进行了一次或多次 `filehandle.read()` 调用,然后再调用 `filehandle.readFile()`,则将从当前位置读取数据,直到文件结束。 diff --git a/fs/filehandle_writefile_data_options.md b/fs/filehandle_writefile_data_options.md index 235206cf..6c1e09ae 100644 --- a/fs/filehandle_writefile_data_options.md +++ b/fs/filehandle_writefile_data_options.md @@ -4,8 +4,6 @@ added: v10.0.0 * `data` {string|Buffer|Uint8Array} * `options` {Object|string} * `encoding` {string|null} **默认值:** `'utf8'`。 - * `mode` {integer} **默认值:** `0o666`。 - * `flag` {string} 参阅[支持的文件系统标志][support of file system `flags`]。**默认值:** `'w'`。 * 返回: {Promise} diff --git a/fs/fs_existssync_path.md b/fs/fs_existssync_path.md index a3ad2b36..ecb6e9f4 100644 --- a/fs/fs_existssync_path.md +++ b/fs/fs_existssync_path.md @@ -20,7 +20,7 @@ changes: ```js if (fs.existsSync('/etc/passwd')) { - console.log('文件已存在'); + console.log('路径已存在'); } ``` diff --git a/fs/fs_watchfile_filename_options_listener.md b/fs/fs_watchfile_filename_options_listener.md index 4043e4fa..38c47e1e 100644 --- a/fs/fs_watchfile_filename_options_listener.md +++ b/fs/fs_watchfile_filename_options_listener.md @@ -1,6 +1,9 @@ -* `authority` {string|URL} +* `authority` {string|URL} The remote HTTP/2 server to connect to. This must + be in the form of a minimal, valid URL with the `http://` or `https://` + prefix, host name, and IP port (if a non-default port is used). Userinfo + (user ID and password), path, querystring, and fragment details in the + URL will be ignored. * `options` {Object} * `maxDeflateDynamicTableSize` {number} Sets the maximum dynamic table size for deflating header fields. **Default:** `4Kib`. @@ -63,6 +67,9 @@ changes: * `selectPadding` {Function} When `options.paddingStrategy` is equal to `http2.constants.PADDING_STRATEGY_CALLBACK`, provides the callback function used to determine the padding. See [Using `options.selectPadding()`][]. + * `protocol` {string} The protocol to connect with, if not set in the + `authority`. Value may be either `'http:'` or `'https:'`. **Default:** + `'https:'` * `settings` {HTTP/2 Settings Object} The initial settings to send to the remote peer upon connection. * `createConnection` {Function} An optional callback that receives the `URL` diff --git a/http2/request_headers.md b/http2/request_headers.md index 27594c97..b5a18f85 100644 --- a/http2/request_headers.md +++ b/http2/request_headers.md @@ -19,7 +19,7 @@ console.log(request.headers); See [HTTP/2 Headers Object][]. -In HTTP/2, the request path, hostname, protocol, and method are represented as +In HTTP/2, the request path, host name, protocol, and method are represented as special headers prefixed with the `:` character (e.g. `':path'`). These special headers will be included in the `request.headers` object. Care must be taken not to inadvertently modify these special headers or errors may occur. For instance, diff --git a/modules/all_together.md b/modules/all_together.md index 9a991961..3bfc3ed3 100644 --- a/modules/all_together.md +++ b/modules/all_together.md @@ -18,7 +18,7 @@ require(X) from module at path Y c. THROW "not found" 4. LOAD_SELF_REFERENCE(X, dirname(Y)) 5. LOAD_NODE_MODULES(X, dirname(Y)) -7. THROW "not found" +6. THROW "not found" LOAD_AS_FILE(X) 1. If X is a file, load X as JavaScript text. STOP diff --git a/modules/require_id.md b/modules/require_id.md index 0cc0f64c..fbe16405 100644 --- a/modules/require_id.md +++ b/modules/require_id.md @@ -10,9 +10,11 @@ added: v0.1.13 用于引入模块、`JSON`、或本地文件。 可以从 `node_modules` 引入模块。 可以使用相对路径(例如 `./`、`./foo`、`./bar/baz`、`../foo`)引入本地模块或 JSON 文件,路径会根据 [`__dirname`] 定义的目录名或当前工作目录进行处理。 +POSIX 风格的相对路径会以与操作系统无关的方式解析,这意味着上面的示例将会在 Windows 上以与在 Unix 系统上相同的方式工作。 ```js -// 引入本地模块: +// 使用相对于 `__dirname` 或当前工作目录的路径引入一个本地模块。 +// (在 Windows 上,这会解析为 .\path\myLocalModule。) const myLocalModule = require('./path/myLocalModule'); // 引入 JSON 文件: diff --git a/modules/the_module_object_1.md b/modules/the_module_object_1.md index 3039cd6f..18841ccd 100644 --- a/modules/the_module_object_1.md +++ b/modules/the_module_object_1.md @@ -5,8 +5,7 @@ added: v0.3.7 * {Object} -为 `Module` 实例提供通用方法。 -`module` 变量常见于文件模块中。 +为 `Module` 实例提供通用方法,`module` 变量常见于文件模块中。 通过 `require('module')` 获取。 diff --git a/n-api/napi_env.md b/n-api/napi_env.md index 185f75fc..d1012788 100644 --- a/n-api/napi_env.md +++ b/n-api/napi_env.md @@ -4,6 +4,10 @@ implementation can use to persist VM-specific state. This structure is passed to native functions when they're invoked, and it must be passed back when making N-API calls. Specifically, the same `napi_env` that was passed in when the initial native function was called must be passed to any subsequent -nested N-API calls. Caching the `napi_env` for the purpose of general reuse is -not allowed. +nested N-API calls. Caching the `napi_env` for the purpose of general reuse, +and passing the `napi_env` between instances of the same addon running on +different [`Worker`][] threads is not allowed. The `napi_env` becomes invalid +when an instance of a native addon is unloaded. Notification of this event is +delivered through the callbacks given to [`napi_add_env_cleanup_hook`][] and +[`napi_set_instance_data`][]. diff --git a/n-api/napi_is_promise.md b/n-api/napi_is_promise.md index 0b3a23e6..72acc816 100644 --- a/n-api/napi_is_promise.md +++ b/n-api/napi_is_promise.md @@ -5,12 +5,12 @@ napiVersion: 1 ```C napi_status napi_is_promise(napi_env env, - napi_value promise, + napi_value value, bool* is_promise); ``` * `[in] env`: The environment that the API is invoked under. -* `[in] promise`: The promise to examine +* `[in] value`: The value to examine * `[out] is_promise`: Flag indicating whether `promise` is a native promise object (that is, a promise object created by the underlying engine). diff --git a/n-api/napi_reference_unref.md b/n-api/napi_reference_unref.md index 4d75e5bc..9f49749a 100644 --- a/n-api/napi_reference_unref.md +++ b/n-api/napi_reference_unref.md @@ -6,7 +6,7 @@ napiVersion: 1 ```C NAPI_EXTERN napi_status napi_reference_unref(napi_env env, napi_ref ref, - uint32_t* result);); + uint32_t* result); ``` * `[in] env`: The environment that the API is invoked under. diff --git a/n-api/napi_unref_threadsafe_function.md b/n-api/napi_unref_threadsafe_function.md index 03ba586a..22dd5190 100644 --- a/n-api/napi_unref_threadsafe_function.md +++ b/n-api/napi_unref_threadsafe_function.md @@ -120,6 +120,9 @@ This API may only be called from the main thread. + + + diff --git a/net/identifying_paths_for_ipc_connections.md b/net/identifying_paths_for_ipc_connections.md index fd898ceb..a3c582eb 100644 --- a/net/identifying_paths_for_ipc_connections.md +++ b/net/identifying_paths_for_ipc_connections.md @@ -4,13 +4,13 @@ 在 Unix 上,本地域也称为 Unix 域。 参数 `path` 是文件系统路径名。 -它被截断为 `sizeof(sockaddr_un.sun_path) - 1` 的长度,该长度因操作系统而异,在 91 至 107 字节之间。 +它会被截断为依赖于系统的 `sizeof(sockaddr_un.sun_path) - 1` 的长度。 典型值在 Linux 上为 107,在 macOS 上为 103。 -该路径受到与创建文件相同的命名约定和权限检查。 -如果创建 Unix 域套接字(作为文件系统路径可见)并与 Node.js 的 API 抽象之一(例如 [`net.createServer()`])一起使用,则它将作为 [`server.close()`] 的一部分取消链接。 -另一方面,如果在这些抽象之外创建和使用它,则用户需要手动删除它。 -当路径由 Node.js API 创建但程序突然崩溃时,同样适用。 -简而言之,一旦成功创建的 Unix 域套接字将在文件系统中可见,并将持续到取消链接。 +如果一个 Node.js API 的抽象创建了 Unix 域 socket,则它也可以 unlink 该 Unix 域 socket。 +例如,[`net.createServer()`] 可以创建 Unix 域 socket,而 [`server.close()`] 可以 unlink 它。 +但是,如果用户在这些抽象之外创建 Unix 域 socket,则用户需要自己删除它。 +当 Node.js API 创建 Unix 域 socket 但该程序随后崩溃时,情况也是如此。 +简而言之,Unix 域 socket 会在文件系统中可见,且持续到被 unlink。 在 Windows 上,本地域通过命名管道实现。路径必须是以 `\\?\pipe\` 或 `\\.\pipe\` 为入口。路径允许任何字符,但后面的字符可能会对管道名称进行一些处理,例如解析 `..` 序列。尽管如此,管道空间是平面的。管道不会持续,当最后一次引用关闭时,管道就会被删除。 diff --git a/net/socket_setnodelay_nodelay.md b/net/socket_setnodelay_nodelay.md index 5bcf8447..af480b20 100644 --- a/net/socket_setnodelay_nodelay.md +++ b/net/socket_setnodelay_nodelay.md @@ -5,4 +5,12 @@ added: v0.1.90 * `noDelay` {boolean} **默认值:** `true`。 * 返回: {net.Socket} Socket 本身。 -禁止 Nagle 算法。默认情况下 TCP 连接使用 Nagle 算法,在发送之前缓冲数据。将 `noDelay` 设置为 `true` 将会在每次 `socket.write()` 被调用的时候立即发送数据。 +启用或禁用 Nagle 算法的使用。 + +当TCP 连接被创建时,它会启用 Nagle 算法。 + +Nagle 算法会延迟数据,然后再通过网络发送数据。 +它会尝试以延迟为代价优化吞吐量。 + +为 `noDelay` 传入 `true` 或不传参数,则会禁用 Nagle 的 socket 算法。 +为 `noDelay` 传入 `false` 则会启用 Nagle 算法。 diff --git a/net/socket_write_data_encoding_callback.md b/net/socket_write_data_encoding_callback.md index 7f13caca..471c1813 100644 --- a/net/socket_write_data_encoding_callback.md +++ b/net/socket_write_data_encoding_callback.md @@ -7,7 +7,8 @@ added: v0.1.90 * `callback` {Function} * 返回: {boolean} -在 socket 上发送数据。第二个参数制定了字符串的编码 - 默认是 UTF8 编码。 +在 socket 上发送数据。第二个参数制定了字符串的编码。 +默认是 UTF8 编码。 如果全部数据都成功刷新到内核的缓冲则返回 `true`。如果全部或部分数据在用户内中排队,则返回 `false`。当缓冲再次空闲的时候将触发 [`'drain'`][] 事件。 diff --git a/path/path.md b/path/path.md index badf7670..1724fbf6 100644 --- a/path/path.md +++ b/path/path.md @@ -3,7 +3,7 @@ > 稳定性: 2 - 稳定 -`path` 模块提供用于处理文件路径和目录路径的实用工具。 +`path` 模块提供了一些用于处理文件与目录的路径的实用工具。 它可以使用以下方式访问: ```js diff --git a/path/path_basename_path_ext.md b/path/path_basename_path_ext.md index 4787b0c8..e6d9db2d 100644 --- a/path/path_basename_path_ext.md +++ b/path/path_basename_path_ext.md @@ -3,7 +3,7 @@ added: v0.1.25 changes: - version: v6.0.0 pr-url: https://github.com/nodejs/node/pull/5348 - description: Passing a non-string as the `path` argument will throw now. + description: 现在将非字符串作为 `path` 参数传入会抛出异常。 --> * `path` {string} @@ -22,5 +22,5 @@ path.basename('/foo/bar/baz/asdf/quux.html', '.html'); // 返回: 'quux' ``` -如果 `path` 不是字符串或者给定了 `ext` 且不是字符串,则抛出 [`TypeError`]。 +如果 `path` 不是字符串、或给定了 `ext` 但不是字符串,则抛出 [`TypeError`]。 diff --git a/path/windows_vs_posix.md b/path/windows_vs_posix.md index 6393b44a..2ec7ce50 100644 --- a/path/windows_vs_posix.md +++ b/path/windows_vs_posix.md @@ -1,6 +1,6 @@ -`path` 模块的默认操作因 Node.js 应用程序运行所在的操作系统而异。 -具体来说,当在 Windows 操作系统上运行时,`path` 模块将假定正在使用 Windows 风格的路径。 +`path` 模块的默认操作会因 Node.js 应用程序运行所在的操作系统而异。 +具体来说,当在 Windows 操作系统上运行时,`path` 模块会假定正被使用的是 Windows 风格的路径。 因此,使用 `path.basename()` 可能会在 POSIX 和 Windows 上产生不同的结果: @@ -18,7 +18,7 @@ path.basename('C:\\temp\\myfile.html'); // 返回: 'myfile.html' ``` -要在任何操作系统上使用 Windows 文件路径时获得一致的结果,则使用 [`path.win32`]: +如果要在任意操作系统上使用 Windows 文件路径时获得一致的结果,则使用 [`path.win32`]: 在 POSIX 和 Windows 上: @@ -27,7 +27,7 @@ path.win32.basename('C:\\temp\\myfile.html'); // 返回: 'myfile.html' ``` -要在任何操作系统上使用 POSIX 文件路径时获得一致的结果,则使用 [`path.posix`]: +如果要在任意操作系统上使用 POSIX 文件路径时获得一致的结果,则使用 [`path.posix`]: 在 POSIX 和 Windows 上: @@ -36,7 +36,7 @@ path.posix.basename('/tmp/myfile.html'); // 返回: 'myfile.html' ``` -在 Windows 上,Node.js 遵循每个驱动器工作目录的概念。 +在 Windows 上,Node.js 遵循各自驱动器工作目录的理念。 当使用没有反斜杠的驱动器路径时,可以观察到此行为。 例如,`path.resolve('C:\\')` 可能会返回与 `path.resolve('C:')` 不同的结果。 有关详细信息,参阅[此 MSDN 页面][MSDN-Rel-Path]。 diff --git a/process/process_stderr_fd.md b/process/process_stderr_fd.md new file mode 100644 index 00000000..6781fa02 --- /dev/null +++ b/process/process_stderr_fd.md @@ -0,0 +1,7 @@ + +* {number} + +This property refers to the value of underlying file descriptor of +`process.stderr`. The value is fixed at `2`. In [`Worker`][] threads, +this field does not exist. + diff --git a/process/process_stdin_fd.md b/process/process_stdin_fd.md new file mode 100644 index 00000000..0ef7738c --- /dev/null +++ b/process/process_stdin_fd.md @@ -0,0 +1,7 @@ + +* {number} + +This property refers to the value of underlying file descriptor of +`process.stdin`. The value is fixed at `0`. In [`Worker`][] threads, +this field does not exist. + diff --git a/process/process_stdout_fd.md b/process/process_stdout_fd.md new file mode 100644 index 00000000..2f6cfe75 --- /dev/null +++ b/process/process_stdout_fd.md @@ -0,0 +1,7 @@ + +* {number} + +This property refers to the value of underlying file descriptor of +`process.stdout`. The value is fixed at `1`. In [`Worker`][] threads, +this field does not exist. + diff --git a/readline/rl_cursor.md b/readline/rl_cursor.md index 45f651d8..4c0fa6f6 100644 --- a/readline/rl_cursor.md +++ b/readline/rl_cursor.md @@ -1,5 +1,5 @@ * {number|undefined} diff --git a/readline/rl_line.md b/readline/rl_line.md index 151afd04..e6f938c2 100644 --- a/readline/rl_line.md +++ b/readline/rl_line.md @@ -1,5 +1,5 @@ * {string|undefined} diff --git a/report/configuration.md b/report/configuration.md index 16cb4709..2b8f8fd7 100644 --- a/report/configuration.md +++ b/report/configuration.md @@ -57,4 +57,3 @@ NODE_OPTIONS="--experimental-report --report-uncaught-exception \ Specific API documentation can be found under [`process API documentation`][] section. - diff --git a/report/diagnostic_report.md b/report/diagnostic_report.md index 2703bf89..2949fb6b 100644 --- a/report/diagnostic_report.md +++ b/report/diagnostic_report.md @@ -296,6 +296,7 @@ is provided below for reference. "address": "0x000055fc7b2cb180" } ], + "workers": [], "environmentVariables": { "REMOTEHOST": "REMOVED", "MANPATH": "/opt/rh/devtoolset-3/root/usr/share/man:", diff --git a/report/interaction_with_workers.md b/report/interaction_with_workers.md new file mode 100644 index 00000000..636df1d3 --- /dev/null +++ b/report/interaction_with_workers.md @@ -0,0 +1,20 @@ + + +[`Worker`][] threads can create reports in the same way that the main thread +does. + +Reports will include information on any Workers that are children of the current +thread as part of the `workers` section, with each Worker generating a report +in the standard report format. + +The thread which is generating the report will wait for the reports from Worker +threads to finish. However, the latency for this will usually be low, as both +running JavaScript and the event loop are interrupted to generate the report. + + + diff --git a/stream/piping_to_writable_streams_from_async_iterators.md b/stream/piping_to_writable_streams_from_async_iterators.md index 6645f890..34301830 100644 --- a/stream/piping_to_writable_streams_from_async_iterators.md +++ b/stream/piping_to_writable_streams_from_async_iterators.md @@ -7,15 +7,33 @@ const finished = util.promisify(stream.finished); const writable = fs.createWriteStream('./file'); -(async function() { - for await (const chunk of iterator) { +function drain(writable) { + if (writable.destroyed) { + return Promise.reject(new Error('过早关闭')); + } + return Promise.race([ + once(writable, 'drain'), + once(writable, 'close') + .then(() => Promise.reject(new Error('过早关闭'))) + ]); +} + +async function pump(iterable, writable) { + for await (const chunk of iterable) { // 处理 write() 上的背压。 - if (!writable.write(chunk)) - await once(writable, 'drain'); + if (!writable.write(chunk)) { + await drain(writable); + } } writable.end(); +} + +(async function() { // 确保完成没有错误。 - await finished(writable); + await Promise.all([ + pump(iterable, writable), + finished(writable) + ]); })(); ``` @@ -31,7 +49,7 @@ const finished = util.promisify(stream.finished); const writable = fs.createWriteStream('./file'); (async function() { - const readable = Readable.from(iterator); + const readable = Readable.from(iterable); readable.pipe(writable); // 确保完成没有错误。 await finished(writable); @@ -46,7 +64,7 @@ const pipeline = util.promisify(stream.pipeline); const writable = fs.createWriteStream('./file'); (async function() { - const readable = Readable.from(iterator); + const readable = Readable.from(iterable); await pipeline(readable, writable); })(); ``` diff --git a/stream/writable_writev_chunks_callback.md b/stream/writable_writev_chunks_callback.md index 5272c4d1..a35a7939 100644 --- a/stream/writable_writev_chunks_callback.md +++ b/stream/writable_writev_chunks_callback.md @@ -7,7 +7,7 @@ 该函数应该由子类实现,且只能被内部的 `Writable` 类的方法调用。 除了在流实现中的 `writable._write()` 之外,还可以实现 `writable._writev()` 方法,其能够一次处理多个数据块。 -如果实现了该方法,调用该方法时会传入当前缓冲在写入队列中的所有数据块。 +如果已实现且之前写入的数据有缓冲,则会调用 `_writev()` 而不是 `_write()`。 `writable._writev()` 方法有下划线前缀,因为它是在定义在类的内部,不应该被用户程序直接调用。 diff --git a/tls/server_addcontext_hostname_context.md b/tls/server_addcontext_hostname_context.md index acc65de6..90fcf5b1 100644 --- a/tls/server_addcontext_hostname_context.md +++ b/tls/server_addcontext_hostname_context.md @@ -2,7 +2,7 @@ added: v0.5.3 --> -* `hostname` {string} A SNI hostname or wildcard (e.g. `'*'`) +* `hostname` {string} A SNI host name or wildcard (e.g. `'*'`) * `context` {Object} An object containing any of the possible properties from the [`tls.createSecureContext()`][] `options` arguments (e.g. `key`, `cert`, `ca`, etc). diff --git a/tls/tls_connect_options_callback.md b/tls/tls_connect_options_callback.md index 364aa3f7..56154c56 100644 --- a/tls/tls_connect_options_callback.md +++ b/tls/tls_connect_options_callback.md @@ -68,7 +68,7 @@ changes: with optional identity `hint` provided by the server or `null` in case of TLS 1.3 where `hint` was removed. It will be necessary to provide a custom `tls.checkServerIdentity()` - for the connection as the default one will try to check hostname/IP + for the connection as the default one will try to check host name/IP of the server against the certificate but that's not applicable for PSK because there won't be a certificate present. More information can be found in the [RFC 4279][]. @@ -88,7 +88,7 @@ changes: `SNICallback` option to [`tls.createServer()`][]. * `checkServerIdentity(servername, cert)` {Function} A callback function to be used (instead of the builtin `tls.checkServerIdentity()` function) - when checking the server's hostname (or the provided `servername` when + when checking the server's host name (or the provided `servername` when explicitly set) against the certificate. This should return an {Error} if verification fails. The method should return `undefined` if the `servername` and `cert` are verified. diff --git a/util/util.md b/util/util.md index fc4faa47..e9f1d048 100644 --- a/util/util.md +++ b/util/util.md @@ -3,7 +3,7 @@ > 稳定性: 2 - 稳定 -`util` 模块主要用于支持 Node.js 内部 API 的需求。 +`util` 模块用于支持 Node.js 内部 API 的需求。 大部分实用工具也可用于应用程序与模块开发者。 使用方法如下: diff --git a/util/util_log_string.md b/util/util_log_string.md index 7f4c141b..46db231c 100644 --- a/util/util_log_string.md +++ b/util/util_log_string.md @@ -68,6 +68,8 @@ util.log('Timestamped message.'); + + diff --git a/util/util_promisify_custom.md b/util/util_promisify_custom.md index ffbc3d88..1d01a8f2 100644 --- a/util/util_promisify_custom.md +++ b/util/util_promisify_custom.md @@ -1,6 +1,23 @@ * {symbol} 可用于声明函数的自定义的 promise 化变量,参阅[自定义的 promise 化函数][Custom promisified functions]。 +除了可以通过 `util.promisify.custom` 进行访问之外,该符号还被[注册为全局]的[global symbol registry],并且可以在任何环境中作为 `Symbol.for('nodejs.util.promisify.custom')` 进行访问。 + +例如,使用一个接受 `(foo, onSuccessCallback, onErrorCallback)` 的函数: + +```js +const kCustomPromisifiedSymbol = Symbol.for('nodejs.util.promisify.custom'); + +doSomething[kCustomPromisifiedSymbol] = (foo) => { + return new Promise((resolve, reject) => { + doSomething(foo, resolve, reject); + }); +}; +``` diff --git a/util/util_types_isexternal_value.md b/util/util_types_isexternal_value.md index 7ea69e84..23d7d878 100644 --- a/util/util_types_isexternal_value.md +++ b/util/util_types_isexternal_value.md @@ -7,3 +7,38 @@ added: v10.0.0 Returns `true` if the value is a native `External` value. +A native `External` value is a special type of object that contains a +raw C++ pointer (`void*`) for access from native code, and has no other +properties. Such objects are created either by Node.js internals or native +addons. In JavaScript, they are [frozen][`Object.freeze()`] objects with a +`null` prototype. + +```c +#include +#include +napi_value result; +static napi_value MyNapi(napi_env env, napi_callback_info info) { + int* raw = (int*) malloc(1024); + napi_status status = napi_create_external(env, (void*) raw, NULL, NULL, &result); + if (status != napi_ok) { + napi_throw_error(env, NULL, "napi_create_external failed"); + return NULL; + } + return result; +} +... +DECLARE_NAPI_PROPERTY("myNapi", MyNapi) +... +``` + +```js +const native = require('napi_addon.node'); +const data = native.myNapi(); +util.types.isExternal(data); // returns true +util.types.isExternal(0); // returns false +util.types.isExternal(new String('foo')); // returns false +``` + +For further information on `napi_create_external`, refer to +[`napi_create_external()`][]. + diff --git a/v8/serialization_api.md b/v8/serialization_api.md index aeb11e17..53ec8157 100644 --- a/v8/serialization_api.md +++ b/v8/serialization_api.md @@ -1,5 +1,7 @@ The serialization API provides means of serializing JavaScript values in a way that is compatible with the [HTML structured clone algorithm][]. + The format is backward-compatible (i.e. safe to store to disk). +Equal JavaScript values may result in different serialized output. diff --git a/vm/script_runincontext_contextifiedobject_options.md b/vm/script_runincontext_contextifiedobject_options.md index c231c8d3..28981f4e 100644 --- a/vm/script_runincontext_contextifiedobject_options.md +++ b/vm/script_runincontext_contextifiedobject_options.md @@ -34,9 +34,8 @@ for (let i = 0; i < 10; ++i) { script.runInContext(context); } -console.log(util.inspect(context)); - -// { animal: 'cat', count: 12, name: 'kitty' } +console.log(context); +// 打印: { animal: 'cat', count: 12, name: 'kitty' } ``` 使用 `timeout` 或者 `breakOnSigint` 选项会导致若干新的事件循环以及对应的线程,这有一个非零的性能消耗。 diff --git a/vm/script_runinnewcontext_contextobject_options.md b/vm/script_runinnewcontext_contextobject_options.md index 8c3ca38e..1d3220df 100644 --- a/vm/script_runinnewcontext_contextobject_options.md +++ b/vm/script_runinnewcontext_contextobject_options.md @@ -38,8 +38,7 @@ contexts.forEach((context) => { script.runInNewContext(context); }); -console.log(util.inspect(contexts)); - -// [{ globalVar: 'set' }, { globalVar: 'set' }, { globalVar: 'set' }] +console.log(contexts); +// 打印: [{ globalVar: 'set' }, { globalVar: 'set' }, { globalVar: 'set' }] ``` diff --git a/vm/vm_createcontext_contextobject_options.md b/vm/vm_createcontext_contextobject_options.md index 9ec4ef4c..2b754fe8 100644 --- a/vm/vm_createcontext_contextobject_options.md +++ b/vm/vm_createcontext_contextobject_options.md @@ -35,9 +35,11 @@ vm.createContext(context); vm.runInContext('globalVar *= 2;', context); -console.log(util.inspect(context)); // { globalVar: 2 } +console.log(context); +// 打印: { globalVar: 2 } -console.log(util.inspect(globalVar)); // 3 +console.log(global.globalVar); +// 打印: 3 ``` 如果未提供 `contextObject`(或者传入`undefined`),那么会返回一个全新的空的[上下文隔离化][contextified]对象。 diff --git a/vm/vm_runincontext_code_contextifiedobject_options.md b/vm/vm_runincontext_code_contextifiedobject_options.md index b08c0751..1eb27150 100644 --- a/vm/vm_runincontext_code_contextifiedobject_options.md +++ b/vm/vm_runincontext_code_contextifiedobject_options.md @@ -48,8 +48,7 @@ vm.createContext(contextObject); for (let i = 0; i < 10; ++i) { vm.runInContext('globalVar *= 2;', contextObject); } -console.log(util.inspect(contextObject)); - -// { globalVar: 1024 } +console.log(contextObject); +// 打印: { globalVar: 1024 } ``` diff --git a/vm/vm_runinnewcontext_code_contextobject_options.md b/vm/vm_runinnewcontext_code_contextobject_options.md index 9ece22a4..8bfb7d7f 100644 --- a/vm/vm_runinnewcontext_code_contextobject_options.md +++ b/vm/vm_runinnewcontext_code_contextobject_options.md @@ -56,8 +56,7 @@ const contextObject = { }; vm.runInNewContext('count += 1; name = "kitty"', contextObject); -console.log(util.inspect(contextObject)); - -// { animal: 'cat', count: 3, name: 'kitty' } +console.log(contextObject); +// 打印: { animal: 'cat', count: 3, name: 'kitty' } ``` diff --git a/vm/vm_runinthiscontext_code_options.md b/vm/vm_runinthiscontext_code_options.md index ce62dc86..0ed7c960 100644 --- a/vm/vm_runinthiscontext_code_options.md +++ b/vm/vm_runinthiscontext_code_options.md @@ -42,15 +42,12 @@ const vm = require('vm'); let localVar = 'initial value'; const vmResult = vm.runInThisContext('localVar = "vm";'); -console.log('vmResult:', vmResult); -console.log('localVar:', localVar); +console.log(`vmResult: '${vmResult}', localVar: '${localVar}'`); +// 打印: vmResult: 'vm', localVar: 'initial value' const evalResult = eval('localVar = "eval";'); -console.log('evalResult:', evalResult); -console.log('localVar:', localVar); - -// vmResult: 'vm', localVar: 'initial value' -// evalResult: 'eval', localVar: 'eval' +console.log(`evalResult: '${evalResult}', localVar: '${localVar}'`); +// 打印: evalResult: 'eval', localVar: 'eval' ``` 正因 `vm.runInThisContext()` 无法获取本地作用域,故 `localVar` 的值不变。 diff --git a/worker_threads/event_exit.md b/worker_threads/event_exit.md index 4b8cc9ee..0d20d45d 100644 --- a/worker_threads/event_exit.md +++ b/worker_threads/event_exit.md @@ -9,3 +9,5 @@ exited by calling [`process.exit()`][], the `exitCode` parameter will be the passed exit code. If the worker was terminated, the `exitCode` parameter will be `1`. +This is the final event emitted by any `Worker` instance. + diff --git a/worker_threads/event_message_1.md b/worker_threads/event_message_1.md index ae982f47..876a94dc 100644 --- a/worker_threads/event_message_1.md +++ b/worker_threads/event_message_1.md @@ -8,3 +8,6 @@ The `'message'` event is emitted when the worker thread has invoked [`require('worker_threads').parentPort.postMessage()`][]. See the [`port.on('message')`][] event for more details. +All messages sent from the worker thread will be emitted before the +[`'exit'` event][] is emitted on the `Worker` object. + diff --git a/worker_threads/worker_threads.md b/worker_threads/worker_threads.md index b348e0e0..7b1db23e 100644 --- a/worker_threads/worker_threads.md +++ b/worker_threads/worker_threads.md @@ -48,4 +48,8 @@ if (isMainThread) { 否则,创建工作线程的开销可能会超出其收益。 当实现工作线程池时,可使用 [`AsyncResource`] API 来通知诊断的工具(例如为了提供异步的堆栈跟踪)有关任务及其结果之间的相关性。 +有关示例的实现,请参阅 `async_hooks` 文档中的[“为 `Worker` 线程池使用 `AsyncResource`”][async-resource-worker-pool]。 + +默认情况下,工作线程继承非特定于进程的选项。 +请参阅[工作线程的构造函数选项][`Worker constructor options`],以了解如何自定义工作线程的选项,特别是 `argv` 和 `execArgv` 选项。 diff --git a/worker_threads/worker_unref.md b/worker_threads/worker_unref.md index 7f76ff5a..ae72e504 100644 --- a/worker_threads/worker_unref.md +++ b/worker_threads/worker_unref.md @@ -48,6 +48,8 @@ active handle in the event system. If the worker is already `unref()`ed calling + + diff --git a/zlib/class_options.md b/zlib/class_options.md index afa991ef..c5e2b070 100644 --- a/zlib/class_options.md +++ b/zlib/class_options.md @@ -15,7 +15,7 @@ changes: 每一个类都有一个 `options` 对象。 -所有的选项都是可选的。 +所有的选项都不是必需的。 注意一些选项只与压缩相关,会被解压类忽视。 diff --git a/zlib/compressing_http_requests_and_responses.md b/zlib/compressing_http_requests_and_responses.md index 91e99332..edcfc7e9 100644 --- a/zlib/compressing_http_requests_and_responses.md +++ b/zlib/compressing_http_requests_and_responses.md @@ -13,6 +13,8 @@ HTTP 的 [`Accept-Encoding`] 消息头用来标记客户端接受的压缩编码 const zlib = require('zlib'); const http = require('http'); const fs = require('fs'); +const { pipeline } = require('stream'); + const request = http.get({ host: 'example.com', path: '/', port: 80, @@ -20,19 +22,26 @@ const request = http.get({ host: 'example.com', request.on('response', (response) => { const output = fs.createWriteStream('example.com_index.html'); + const onError = (err) => { + if (err) { + console.error('发生错误:', err); + process.exitCode = 1; + } + }; + switch (response.headers['content-encoding']) { case 'br': - response.pipe(zlib.createBrotliDecompress()).pipe(output); + pipeline(response, zlib.createBrotliDecompress(), output, onError); break;    // 或者, 只是使用 zlib.createUnzip() 方法去处理这两种情况: case 'gzip': - response.pipe(zlib.createGunzip()).pipe(output); + pipeline(response, zlib.createGunzip(), output, onError); break; case 'deflate': - response.pipe(zlib.createInflate()).pipe(output); + pipeline(response, zlib.createInflate(), outout, onError); break; default: - response.pipe(output); + pipeline(response, output, onError); break; } }); @@ -45,29 +54,42 @@ request.on('response', (response) => { const zlib = require('zlib'); const http = require('http'); const fs = require('fs'); +const { pipeline } = require('stream'); + http.createServer((request, response) => { const raw = fs.createReadStream('index.html'); // 存储资源的压缩版本和未压缩版本。 - response.setHeader('Vary: Accept-Encoding'); + response.setHeader('Vary', 'Accept-Encoding'); let acceptEncoding = request.headers['accept-encoding']; if (!acceptEncoding) { acceptEncoding = ''; } + const onError = (err) => { + if (err) { + // 如果发生错误,则我们将会无能为力, + // 因为服务器已经发送了 200 响应码, + // 并且已经向客户端发送了一些数据。 + // 我们能做的最好就是立即终止响应并记录错误。 + response.end(); + console.error('发生错误:', err); + } + }; +  // 注意:这不是一个合适的 accept-encoding 解析器。  // 查阅 https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.3 if (/\bdeflate\b/.test(acceptEncoding)) { response.writeHead(200, { 'Content-Encoding': 'deflate' }); - raw.pipe(zlib.createDeflate()).pipe(response); + pipeline(raw, zlib.createDeflate(), response, onError); } else if (/\bgzip\b/.test(acceptEncoding)) { response.writeHead(200, { 'Content-Encoding': 'gzip' }); - raw.pipe(zlib.createGzip()).pipe(response); + pipeline(raw, zlib.createGzip(), response, onError); } else if (/\bbr\b/.test(acceptEncoding)) { response.writeHead(200, { 'Content-Encoding': 'br' }); - raw.pipe(zlib.createBrotliCompress()).pipe(response); + pipeline(raw, zlib.createBrotliCompress(), response, onError); } else { response.writeHead(200, {}); - raw.pipe(response); + pipeline(raw, response, onError); } }).listen(1337); ``` @@ -84,11 +106,11 @@ zlib.unzip( // 对于 Brotli,等效的是 zlib.constants.BROTLI_OPERATION_FLUSH。 { finishFlush: zlib.constants.Z_SYNC_FLUSH }, (err, buffer) => { - if (!err) { - console.log(buffer.toString()); - } else { - // 错误处理 + if (err) { + console.error('发生错误:', err); + process.exitCode = 1; } + console.log(buffer.toString()); }); ``` diff --git a/zlib/flushing.md b/zlib/flushing.md index 958dbdee..19e6616c 100644 --- a/zlib/flushing.md +++ b/zlib/flushing.md @@ -6,14 +6,27 @@ ```js const zlib = require('zlib'); const http = require('http'); +const { pipeline } = require('stream'); http.createServer((request, response) => { // 为了简单起见,省略了对 Accept-Encoding 的检测。 response.writeHead(200, { 'content-encoding': 'gzip' }); const output = zlib.createGzip(); - output.pipe(response); + let i; - setInterval(() => { + pipeline(output, response, (err) => { + if (err) { + // 如果发生错误,则我们将会无能为力, + // 因为服务器已经发送了 200 响应码, + // 并且已经向客户端发送了一些数据。 + // 我们能做的最好就是立即终止响应并记录错误。 + clearInterval(i); + response.end(); + console.error('发生错误:', err); + } + }); + + i = setInterval(() => { output.write(`The current time is ${Date()}\n`, () => { // 数据已经传递给了 zlib,但压缩算法看能已经决定缓存数据以便得到更高的压缩效率。 // 一旦客户端准备接收数据,调用 .flush() 将会使数据可用。 diff --git a/zlib/threadpool_usage.md b/zlib/threadpool_usage.md deleted file mode 100644 index 7a0464a5..00000000 --- a/zlib/threadpool_usage.md +++ /dev/null @@ -1,4 +0,0 @@ - -除显式同步的 API 之外,所有 zlib API 均使用 libuv 的线程池。 -这可能会在某些应用程序中产生意外的效果,例如性能不佳(可以通过调整[池的大小][pool size]来缓和)和/或无法恢复的灾难性内存碎片。 - diff --git a/zlib/threadpool_usage_and_performance_considerations.md b/zlib/threadpool_usage_and_performance_considerations.md new file mode 100644 index 00000000..734fab55 --- /dev/null +++ b/zlib/threadpool_usage_and_performance_considerations.md @@ -0,0 +1,24 @@ + + +除显式同步的 API 之外,所有 `zlib` API 均使用 Node.js 内部的线程池。 +这可能会在某些应用程序中产生意外的效果或性能限制。 + +同时创建和使用大量的 zlib 对象可能会导致明显的内存碎片。 + + +```js +const zlib = require('zlib'); + +const payload = Buffer.from('这是一些数据'); + +// 警告:请勿这样做! +for (let i = 0; i < 30000; ++i) { + zlib.deflate(payload, (err, buffer) => {}); +} +``` + +在前面的示例中,同时创建了 30,000 个 deflate 实例。 +由于某些操作系统处理内存分配和释放的方式,因此可能会导致内存碎片严重。 + +强烈建议对压缩操作的结果进行缓存,以避免重复工作。 + diff --git a/zlib/zlib.md b/zlib/zlib.md index e1b881e0..fc122952 100644 --- a/zlib/zlib.md +++ b/zlib/zlib.md @@ -3,50 +3,89 @@ > 稳定性: 2 - 稳定 -`zlib` 模块提供通过 Gzip 和 Deflate/Inflate 实现的压缩功能,Brotli 也是如此。 +`zlib` 模块提供通过 Gzip、Deflate/Inflate、和 Brotli 实现的压缩功能。 + 可以通过这样使用它: ```js const zlib = require('zlib'); ``` -压缩或者解压数据流(例如一个文件)通过 `zlib` 流将源数据流传输到目标流中来完成。 +压缩和解压都是围绕 Node.js 的 [Streams API] 构建的。 + +压缩或者解压流(例如一个文件)通过 `zlib` `Transform` 流将源数据流传输到目标流中来完成。 ```js -const gzip = zlib.createGzip(); -const fs = require('fs'); -const inp = fs.createReadStream('input.txt'); -const out = fs.createWriteStream('input.txt.gz'); - -inp.pipe(gzip) - .on('error', () => { - // 处理错误 - }) - .pipe(out) - .on('error', () => { - // 处理错误 +const { createGzip } = require('zlib'); +const { pipeline } = require('stream'); +const { + createReadStream, + createWriteStream +} = require('fs'); + +const gzip = createGzip(); +const source = createReadStream('input.txt'); +const destination = createWriteStream('input.txt.gz'); + +pipeline(source, gzip, destination, (err) => { + if (err) { + console.error('发生错误:', err); + process.exitCode = 1; + } +}); + +// 或 Promise 化: + +const { promisify } = require('util'); +const pipe = promisify(pipeline); + +async function do_gzip(input, output) { + const gzip = createGzip(); + const source = createReadStream(input); + const destination = createWriteStream(output); + await pipe(source, gzip, destination); +} + +do_gzip('input.txt', 'input.txt.gz') + .catch((err) => { + console.error('发生错误:', err); + process.exitCode = 1; }); ``` 数据的压缩或解压缩也可以只用一个步骤完成: ```js +const { deflate, unzip } = require('zlib'); + const input = '.................................'; -zlib.deflate(input, (err, buffer) => { - if (!err) { - console.log(buffer.toString('base64')); - } else { - // 处理错误 +deflate(input, (err, buffer) => { + if (err) { + console.error('发生错误:', err); + process.exitCode = 1; } + console.log(buffer.toString('base64')); }); const buffer = Buffer.from('eJzT0yMAAGTvBe8=', 'base64'); -zlib.unzip(buffer, (err, buffer) => { - if (!err) { - console.log(buffer.toString()); - } else { - // 处理错误 +unzip(buffer, (err, buffer) => { + if (err) { + console.error('发生错误:', err); + process.exitCode = 1; } + console.log(buffer.toString()); }); + +// 或 Promise 化: + +const { promisify } = require('util'); +const do_unzip = promisify(unzip); + +do_unzip(buffer) + .then((buf) => console.log(buf.toString())) + .catch((err) => { + console.error('发生错误:', err); + process.exitCode = 1; + }); ```