diff --git a/.github/workflows/build-tarball.yml b/.github/workflows/build-tarball.yml index 72b638cd0be1a2..0e4e54e2500f5e 100644 --- a/.github/workflows/build-tarball.yml +++ b/.github/workflows/build-tarball.yml @@ -46,13 +46,13 @@ jobs: with: persist-credentials: false - name: Set up Python ${{ env.PYTHON_VERSION }} - uses: actions/setup-python@39cd14951b08e74b54015e9e001cdefcf80e669f # v5.1.1 + uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3 # v5.2.0 with: python-version: ${{ env.PYTHON_VERSION }} - name: Set up sccache uses: mozilla-actions/sccache-action@89e9040de88b577a072e3760aaf59f585da083af # v0.0.5 with: - version: v0.8.0 + version: v0.8.1 - name: Environment Information run: npx envinfo - name: Make tarball @@ -64,7 +64,7 @@ jobs: mkdir tarballs mv *.tar.gz tarballs - name: Upload tarball artifact - uses: actions/upload-artifact@0b2256b8c012f0828dc542b3febcab082c67f72b # v4.3.4 + uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0 with: name: tarballs path: tarballs @@ -76,13 +76,13 @@ jobs: with: persist-credentials: false - name: Set up Python ${{ env.PYTHON_VERSION }} - uses: actions/setup-python@39cd14951b08e74b54015e9e001cdefcf80e669f # v5.1.1 + uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3 # v5.2.0 with: python-version: ${{ env.PYTHON_VERSION }} - name: Set up sccache uses: mozilla-actions/sccache-action@89e9040de88b577a072e3760aaf59f585da083af # v0.0.5 with: - version: v0.8.0 + version: v0.8.1 - name: Environment Information run: npx envinfo - name: Download tarball diff --git a/.github/workflows/build-windows.yml b/.github/workflows/build-windows.yml index 929c7d0362f61e..f8bc1d979a647f 100644 --- a/.github/workflows/build-windows.yml +++ b/.github/workflows/build-windows.yml @@ -42,7 +42,7 @@ jobs: with: persist-credentials: false - name: Set up Python ${{ env.PYTHON_VERSION }} - uses: actions/setup-python@39cd14951b08e74b54015e9e001cdefcf80e669f # v5.1.1 + uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3 # v5.2.0 with: python-version: ${{ env.PYTHON_VERSION }} - name: Install deps diff --git a/.github/workflows/coverage-linux-without-intl.yml b/.github/workflows/coverage-linux-without-intl.yml index 757c479dbb564a..49ba0662d62b6f 100644 --- a/.github/workflows/coverage-linux-without-intl.yml +++ b/.github/workflows/coverage-linux-without-intl.yml @@ -50,13 +50,13 @@ jobs: with: persist-credentials: false - name: Set up Python ${{ env.PYTHON_VERSION }} - uses: actions/setup-python@39cd14951b08e74b54015e9e001cdefcf80e669f # v5.1.1 + uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3 # v5.2.0 with: python-version: ${{ env.PYTHON_VERSION }} - name: Set up sccache uses: mozilla-actions/sccache-action@89e9040de88b577a072e3760aaf59f585da083af # v0.0.5 with: - version: v0.8.0 + version: v0.8.1 - name: Environment Information run: npx envinfo - name: Install gcovr diff --git a/.github/workflows/coverage-linux.yml b/.github/workflows/coverage-linux.yml index c97253621c8dcb..1a8e926dd82129 100644 --- a/.github/workflows/coverage-linux.yml +++ b/.github/workflows/coverage-linux.yml @@ -50,13 +50,13 @@ jobs: with: persist-credentials: false - name: Set up Python ${{ env.PYTHON_VERSION }} - uses: actions/setup-python@39cd14951b08e74b54015e9e001cdefcf80e669f # v5.1.1 + uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3 # v5.2.0 with: python-version: ${{ env.PYTHON_VERSION }} - name: Set up sccache uses: mozilla-actions/sccache-action@89e9040de88b577a072e3760aaf59f585da083af # v0.0.5 with: - version: v0.8.0 + version: v0.8.1 - name: Environment Information run: npx envinfo - name: Install gcovr diff --git a/.github/workflows/coverage-windows.yml b/.github/workflows/coverage-windows.yml index 67e503895b25c3..b234e8554ee7c4 100644 --- a/.github/workflows/coverage-windows.yml +++ b/.github/workflows/coverage-windows.yml @@ -47,7 +47,7 @@ jobs: with: persist-credentials: false - name: Set up Python ${{ env.PYTHON_VERSION }} - uses: actions/setup-python@39cd14951b08e74b54015e9e001cdefcf80e669f # v5.1.1 + uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3 # v5.2.0 with: python-version: ${{ env.PYTHON_VERSION }} - name: Install deps diff --git a/.github/workflows/daily-wpt-fyi.yml b/.github/workflows/daily-wpt-fyi.yml index 54c2ead0ae787b..d68b27aee57307 100644 --- a/.github/workflows/daily-wpt-fyi.yml +++ b/.github/workflows/daily-wpt-fyi.yml @@ -33,7 +33,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Set up Python ${{ env.PYTHON_VERSION }} - uses: actions/setup-python@39cd14951b08e74b54015e9e001cdefcf80e669f # v5.1.1 + uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3 # v5.2.0 with: python-version: ${{ env.PYTHON_VERSION }} - name: Environment Information @@ -121,7 +121,7 @@ jobs: run: cp wptreport.json wptreport-${{ steps.setup-node.outputs.node-version }}.json - name: Upload GitHub Actions artifact if: ${{ env.WPT_REPORT != '' }} - uses: actions/upload-artifact@0b2256b8c012f0828dc542b3febcab082c67f72b # v4.3.4 + uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0 with: path: out/wpt/wptreport-*.json name: WPT Report for ${{ steps.setup-node.outputs.node-version }} diff --git a/.github/workflows/doc.yml b/.github/workflows/doc.yml index d5b24bc2fcf1e3..16844a46def95f 100644 --- a/.github/workflows/doc.yml +++ b/.github/workflows/doc.yml @@ -35,7 +35,7 @@ jobs: run: npx envinfo - name: Build run: NODE=$(command -v node) make doc-only - - uses: actions/upload-artifact@0b2256b8c012f0828dc542b3febcab082c67f72b # v4.3.4 + - uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0 with: name: docs path: out/doc diff --git a/.github/workflows/linters.yml b/.github/workflows/linters.yml index 566f393787b5d8..3d31ea9a17712f 100644 --- a/.github/workflows/linters.yml +++ b/.github/workflows/linters.yml @@ -44,7 +44,7 @@ jobs: with: persist-credentials: false - name: Set up Python ${{ env.PYTHON_VERSION }} - uses: actions/setup-python@39cd14951b08e74b54015e9e001cdefcf80e669f # v5.1.1 + uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3 # v5.2.0 with: python-version: ${{ env.PYTHON_VERSION }} - name: Environment Information @@ -64,7 +64,7 @@ jobs: with: node-version: ${{ env.NODE_VERSION }} - name: Set up Python ${{ env.PYTHON_VERSION }} - uses: actions/setup-python@39cd14951b08e74b54015e9e001cdefcf80e669f # v5.1.1 + uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3 # v5.2.0 with: python-version: ${{ env.PYTHON_VERSION }} - name: Environment Information @@ -122,7 +122,7 @@ jobs: with: persist-credentials: false - name: Set up Python ${{ env.PYTHON_VERSION }} - uses: actions/setup-python@39cd14951b08e74b54015e9e001cdefcf80e669f # v5.1.1 + uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3 # v5.2.0 with: python-version: ${{ env.PYTHON_VERSION }} - name: Environment Information @@ -139,7 +139,7 @@ jobs: with: persist-credentials: false - name: Use Python ${{ env.PYTHON_VERSION }} - uses: actions/setup-python@39cd14951b08e74b54015e9e001cdefcf80e669f # v5.1.1 + uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3 # v5.2.0 with: python-version: ${{ env.PYTHON_VERSION }} - name: Environment Information diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index 26b3c6b9c900e6..866b3e36caccc9 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -33,7 +33,7 @@ jobs: steps: - name: Harden Runner - uses: step-security/harden-runner@0d381219ddf674d61a7572ddd19d7941e271515c # v2.9.0 + uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1 with: egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs @@ -65,7 +65,7 @@ jobs: # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF # format to the repository Actions tab. - name: Upload artifact - uses: actions/upload-artifact@0b2256b8c012f0828dc542b3febcab082c67f72b # v4.3.4 + uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0 with: name: SARIF file path: results.sarif @@ -73,6 +73,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard. - name: Upload to code-scanning - uses: github/codeql-action/upload-sarif@afb54ba388a7dca6ecae48f608c4ff05ff4cc77a # v3.25.15 + uses: github/codeql-action/upload-sarif@4dd16135b69a43b6c8efb853346f8437d92d3c93 # v3.26.6 with: sarif_file: results.sarif diff --git a/.github/workflows/test-asan.yml b/.github/workflows/test-asan.yml index cd0416658fff0c..64317d335c81cf 100644 --- a/.github/workflows/test-asan.yml +++ b/.github/workflows/test-asan.yml @@ -51,13 +51,13 @@ jobs: with: persist-credentials: false - name: Set up Python ${{ env.PYTHON_VERSION }} - uses: actions/setup-python@39cd14951b08e74b54015e9e001cdefcf80e669f # v5.1.1 + uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3 # v5.2.0 with: python-version: ${{ env.PYTHON_VERSION }} - name: Set up sccache uses: mozilla-actions/sccache-action@89e9040de88b577a072e3760aaf59f585da083af # v0.0.5 with: - version: v0.8.0 + version: v0.8.1 - name: Environment Information run: npx envinfo - name: Build diff --git a/.github/workflows/test-internet.yml b/.github/workflows/test-internet.yml index 002e6dda71b679..9d8458cbc3bc70 100644 --- a/.github/workflows/test-internet.yml +++ b/.github/workflows/test-internet.yml @@ -48,7 +48,7 @@ jobs: with: persist-credentials: false - name: Set up Python ${{ env.PYTHON_VERSION }} - uses: actions/setup-python@39cd14951b08e74b54015e9e001cdefcf80e669f # v5.1.1 + uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3 # v5.2.0 with: python-version: ${{ env.PYTHON_VERSION }} - name: Environment Information diff --git a/.github/workflows/test-linux.yml b/.github/workflows/test-linux.yml index 8789322522ce43..9d1cb796007710 100644 --- a/.github/workflows/test-linux.yml +++ b/.github/workflows/test-linux.yml @@ -41,13 +41,13 @@ jobs: with: persist-credentials: false - name: Set up Python ${{ env.PYTHON_VERSION }} - uses: actions/setup-python@39cd14951b08e74b54015e9e001cdefcf80e669f # v5.1.1 + uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3 # v5.2.0 with: python-version: ${{ env.PYTHON_VERSION }} - name: Set up sccache uses: mozilla-actions/sccache-action@89e9040de88b577a072e3760aaf59f585da083af # v0.0.5 with: - version: v0.8.0 + version: v0.8.1 - name: Environment Information run: npx envinfo - name: Build diff --git a/.github/workflows/test-macos.yml b/.github/workflows/test-macos.yml index c64676978d11fa..ecbd3184a1c145 100644 --- a/.github/workflows/test-macos.yml +++ b/.github/workflows/test-macos.yml @@ -48,13 +48,13 @@ jobs: with: persist-credentials: false - name: Set up Python ${{ env.PYTHON_VERSION }} - uses: actions/setup-python@39cd14951b08e74b54015e9e001cdefcf80e669f # v5.1.1 + uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3 # v5.2.0 with: python-version: ${{ env.PYTHON_VERSION }} - name: Set up sccache uses: mozilla-actions/sccache-action@89e9040de88b577a072e3760aaf59f585da083af # v0.0.5 with: - version: v0.8.0 + version: v0.8.1 - name: Environment Information run: npx envinfo # The `npm ci` for this step fails a lot as part of the Test step. Run it diff --git a/.github/workflows/test-ubsan.yml b/.github/workflows/test-ubsan.yml index 12ca68c064b912..f62f0a39072b95 100644 --- a/.github/workflows/test-ubsan.yml +++ b/.github/workflows/test-ubsan.yml @@ -52,13 +52,13 @@ jobs: run: | echo "UBSAN_OPTIONS=suppressions=$GITHUB_WORKSPACE/suppressions.supp" >> $GITHUB_ENV - name: Set up Python ${{ env.PYTHON_VERSION }} - uses: actions/setup-python@39cd14951b08e74b54015e9e001cdefcf80e669f # v5.1.1 + uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3 # v5.2.0 with: python-version: ${{ env.PYTHON_VERSION }} - name: Set up sccache uses: mozilla-actions/sccache-action@89e9040de88b577a072e3760aaf59f585da083af # v0.0.5 with: - version: v0.8.0 + version: v0.8.1 - name: Environment Information run: npx envinfo - name: Build diff --git a/.github/workflows/tools.yml b/.github/workflows/tools.yml index 79fb0c3324f7b8..4e9d6660db6c1e 100644 --- a/.github/workflows/tools.yml +++ b/.github/workflows/tools.yml @@ -330,7 +330,7 @@ jobs: persist-credentials: false - name: Set up Python ${{ env.PYTHON_VERSION }} if: matrix.id == 'icu' && (github.event_name == 'schedule' || inputs.id == 'all' || inputs.id == matrix.id) - uses: actions/setup-python@39cd14951b08e74b54015e9e001cdefcf80e669f # v5.1.1 + uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3 # v5.2.0 with: python-version: ${{ env.PYTHON_VERSION }} - run: ${{ matrix.run }} diff --git a/CHANGELOG.md b/CHANGELOG.md index fe3f88074b4205..1dea2d6885ac6e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -38,7 +38,8 @@ release. -22.7.0
+22.8.0
+22.7.0
22.6.0
22.5.1
22.5.0
diff --git a/SECURITY.md b/SECURITY.md index eac71b4e45e9e0..fc95e1941698e6 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -222,6 +222,6 @@ Security notifications will be distributed via the following methods. ## Comments on this policy -If you have suggestions on how this process could be improved please submit a -[pull request](https://github.com/nodejs/nodejs.org) or -[file an issue](https://github.com/nodejs/security-wg/issues/new) to discuss. +If you have suggestions on how this process could be improved, please visit +the [nodejs/security-wg](https://github.com/nodejs/security-wg) +repository. diff --git a/benchmark/README.md b/benchmark/README.md index 96bd0318465f45..2f52a44f251af7 100644 --- a/benchmark/README.md +++ b/benchmark/README.md @@ -40,6 +40,7 @@ directories. * `_cli.R`: parses the command line arguments passed to `compare.R` * `_http-benchmarkers.js`: selects and runs external tools for benchmarking the `http` subsystem. +* `bar.R`: R script for visualizing the output of benchmarks with bar plots. * `common.js`: see [Common API](#common-api). * `compare.js`: command line tool for comparing performance between different Node.js binaries. diff --git a/benchmark/common.js b/benchmark/common.js index efe21e871571fc..b4978e8e140b18 100644 --- a/benchmark/common.js +++ b/benchmark/common.js @@ -22,27 +22,36 @@ class Benchmark { this.name = require.main.filename.slice(__dirname.length + 1); // Execution arguments i.e. flags used to run the jobs - this.flags = process.env.NODE_BENCHMARK_FLAGS ? - process.env.NODE_BENCHMARK_FLAGS.split(/\s+/) : - []; + this.flags = process.env.NODE_BENCHMARK_FLAGS?.split(/\s+/) ?? []; // Parse job-specific configuration from the command line arguments const argv = process.argv.slice(2); const parsed_args = this._parseArgs(argv, configs, options); + this.options = parsed_args.cli; this.extra_options = parsed_args.extra; + this.combinationFilter = typeof options.combinationFilter === 'function' ? options.combinationFilter : allow; + + if (options.byGroups) { + this.queue = []; + const groupNames = process.env.NODE_RUN_BENCHMARK_GROUPS?.split(',') ?? Object.keys(configs); + + for (const groupName of groupNames) { + const config = { ...configs[groupName][0], group: groupName }; + const parsed_args = this._parseArgs(argv, config, options); + + this.options = parsed_args.cli; + this.extra_options = parsed_args.extra; + this.queue = this.queue.concat(this._queue(this.options)); + } + } else { + this.queue = this._queue(this.options); + } + if (options.flags) { this.flags = this.flags.concat(options.flags); } - if (typeof options.combinationFilter === 'function') - this.combinationFilter = options.combinationFilter; - else - this.combinationFilter = allow; - - // The configuration list as a queue of jobs - this.queue = this._queue(this.options); - if (this.queue.length === 0) return; diff --git a/benchmark/cpu.sh b/benchmark/cpu.sh new file mode 100755 index 00000000000000..9c9dd7fa4ddc58 --- /dev/null +++ b/benchmark/cpu.sh @@ -0,0 +1,24 @@ +#!/bin/sh + +CPUPATH=/sys/devices/system/cpu + +MAXID=$(cat $CPUPATH/present | awk -F- '{print $NF}') + +set_governor() { + echo "Setting CPU frequency governor to \"$1\"" + i=0 + while [ "$i" -le "$MAXID" ]; do + echo "$1" > "$CPUPATH/cpu$i/cpufreq/scaling_governor" + i=$((i + 1)) + done +} + +case "$1" in + fast | performance) + set_governor "performance" + ;; + *) + echo "Usage: $0 fast" + exit 1 + ;; +esac diff --git a/benchmark/http/headers.js b/benchmark/http/headers.js index e995f380cef151..62612d9fda1911 100644 --- a/benchmark/http/headers.js +++ b/benchmark/http/headers.js @@ -4,10 +4,22 @@ const common = require('../common.js'); const http = require('http'); const bench = common.createBenchmark(main, { - n: [10, 600], - len: [1, 100], - duration: 5, -}); + fewHeaders: { + n: [10], + len: [1, 5], + duration: 5, + }, + mediumHeaders: { + n: [50], + len: [1, 10], + duration: 5, + }, + manyHeaders: { + n: [600], + len: [1, 100], + duration: 5, + }, +}, { byGroups: true }); function main({ len, n, duration }) { const headers = { @@ -15,10 +27,9 @@ function main({ len, n, duration }) { 'Transfer-Encoding': 'chunked', }; - // TODO(BridgeAR): Change this benchmark to use grouped arguments when - // implemented. https://github.com/nodejs/node/issues/26425 - const Is = [ ...Array(Math.max(n / len, 1)).keys() ]; - const Js = [ ...Array(len).keys() ]; + const Is = [...Array(n / len).keys()]; + const Js = [...Array(len).keys()]; + for (const i of Is) { headers[`foo${i}`] = Js.map(() => `some header value ${i}`); } @@ -27,6 +38,7 @@ function main({ len, n, duration }) { res.writeHead(200, headers); res.end(); }); + server.listen(0, () => { bench.http({ path: '/', diff --git a/benchmark/util/get-callsite.js b/benchmark/util/get-callsite.js new file mode 100644 index 00000000000000..9270f841a243d3 --- /dev/null +++ b/benchmark/util/get-callsite.js @@ -0,0 +1,65 @@ +'use strict'; + +const common = require('../common'); +const { getCallSite } = require('node:util'); +const assert = require('node:assert'); + +const bench = common.createBenchmark(main, { + n: [1e6], + method: ['ErrorCallSite', 'ErrorCallSiteSerialized', 'CPP'], +}); + +function ErrorGetCallSite() { + const originalStackFormatter = Error.prepareStackTrace; + Error.prepareStackTrace = (_err, stack) => { + if (stack && stack.length > 1) { + // Remove node:util + return stack.slice(1); + } + return stack; + }; + const err = new Error(); + // With the V8 Error API, the stack is not formatted until it is accessed + err.stack; // eslint-disable-line no-unused-expressions + Error.prepareStackTrace = originalStackFormatter; + return err.stack; +} + +function ErrorCallSiteSerialized() { + const callsite = ErrorGetCallSite(); + const serialized = []; + for (let i = 0; i < callsite.length; ++i) { + serialized.push({ + functionName: callsite[i].getFunctionName(), + scriptName: callsite[i].getFileName(), + lineNumber: callsite[i].getLineNumber(), + column: callsite[i].getColumnNumber(), + }); + } + return serialized; +} + +function main({ n, method }) { + let fn; + switch (method) { + case 'ErrorCallSite': + fn = ErrorGetCallSite; + break; + case 'ErrorCallSiteSerialized': + fn = ErrorCallSiteSerialized; + break; + case 'CPP': + fn = getCallSite; + break; + } + let lastStack = {}; + + bench.start(); + for (let i = 0; i < n; i++) { + const stack = fn(); + lastStack = stack; + } + bench.end(n); + // Attempt to avoid dead-code elimination + assert.ok(lastStack); +} diff --git a/doc/api/child_process.md b/doc/api/child_process.md index b08ba65917aadc..045f34352a92ee 100644 --- a/doc/api/child_process.md +++ b/doc/api/child_process.md @@ -10,7 +10,7 @@ The `node:child_process` module provides the ability to spawn subprocesses in a manner that is similar, but not identical, to popen(3). This capability is primarily provided by the [`child_process.spawn()`][] function: -```js +```cjs const { spawn } = require('node:child_process'); const ls = spawn('ls', ['-lh', '/usr']); @@ -27,6 +27,23 @@ ls.on('close', (code) => { }); ``` +```mjs +import { spawn } from 'node:child_process'; +const ls = spawn('ls', ['-lh', '/usr']); + +ls.stdout.on('data', (data) => { + console.log(`stdout: ${data}`); +}); + +ls.stderr.on('data', (data) => { + console.error(`stderr: ${data}`); +}); + +ls.on('close', (code) => { + console.log(`child process exited with code ${code}`); +}); +``` + By default, pipes for `stdin`, `stdout`, and `stderr` are established between the parent Node.js process and the spawned subprocess. These pipes have limited (and platform-specific) capacity. If the subprocess writes to @@ -108,27 +125,30 @@ When running on Windows, `.bat` and `.cmd` files can be invoked using [`child_process.exec()`][] do). In any case, if the script filename contains spaces it needs to be quoted. -```js -// On Windows Only... -const { spawn } = require('node:child_process'); -const bat = spawn('cmd.exe', ['/c', 'my.bat']); - -bat.stdout.on('data', (data) => { - console.log(data.toString()); -}); +```cjs +// OR... +const { exec, spawn } = require('node:child_process'); -bat.stderr.on('data', (data) => { - console.error(data.toString()); +exec('my.bat', (err, stdout, stderr) => { + if (err) { + console.error(err); + return; + } + console.log(stdout); }); -bat.on('exit', (code) => { - console.log(`Child exited with code ${code}`); +// Script with spaces in the filename: +const bat = spawn('"my script.cmd"', ['a', 'b'], { shell: true }); +// or: +exec('"my script.cmd" a b', (err, stdout, stderr) => { + // ... }); ``` -```js +```mjs // OR... -const { exec, spawn } = require('node:child_process'); +import { exec, spawn } from 'node:child_process'; + exec('my.bat', (err, stdout, stderr) => { if (err) { console.error(err); @@ -197,7 +217,7 @@ directly by the shell and special characters (vary based on [shell](https://en.wikipedia.org/wiki/List_of_command-line_interpreters)) need to be dealt with accordingly: -```js +```cjs const { exec } = require('node:child_process'); exec('"/path/to/test file/test.sh" arg1 arg2'); @@ -208,6 +228,17 @@ exec('echo "The \\$HOME variable is $HOME"'); // The $HOME variable is escaped in the first instance, but not in the second. ``` +```mjs +import { exec } from 'node:child_process'; + +exec('"/path/to/test file/test.sh" arg1 arg2'); +// Double quotes are used so that the space in the path is not interpreted as +// a delimiter of multiple arguments. + +exec('echo "The \\$HOME variable is $HOME"'); +// The $HOME variable is escaped in the first instance, but not in the second. +``` + **Never pass unsanitized user input to this function. Any input containing shell metacharacters may be used to trigger arbitrary command execution.** @@ -225,7 +256,7 @@ can be used to specify the character encoding used to decode the stdout and stderr output. If `encoding` is `'buffer'`, or an unrecognized character encoding, `Buffer` objects will be passed to the callback instead. -```js +```cjs const { exec } = require('node:child_process'); exec('cat *.js missing_file | wc -l', (error, stdout, stderr) => { if (error) { @@ -237,6 +268,18 @@ exec('cat *.js missing_file | wc -l', (error, stdout, stderr) => { }); ``` +```mjs +import { exec } from 'node:child_process'; +exec('cat *.js missing_file | wc -l', (error, stdout, stderr) => { + if (error) { + console.error(`exec error: ${error}`); + return; + } + console.log(`stdout: ${stdout}`); + console.error(`stderr: ${stderr}`); +}); +``` + If `timeout` is greater than `0`, the parent will send the signal identified by the `killSignal` property (the default is `'SIGTERM'`) if the child runs longer than `timeout` milliseconds. @@ -251,7 +294,7 @@ case of an error (including any error resulting in an exit code other than 0), a rejected promise is returned, with the same `error` object given in the callback, but with two additional properties `stdout` and `stderr`. -```js +```cjs const util = require('node:util'); const exec = util.promisify(require('node:child_process').exec); @@ -263,11 +306,24 @@ async function lsExample() { lsExample(); ``` +```mjs +import { promisify } from 'node:util'; +import child_process from 'node:child_process'; +const exec = promisify(child_process.exec); + +async function lsExample() { + const { stdout, stderr } = await exec('ls'); + console.log('stdout:', stdout); + console.error('stderr:', stderr); +} +lsExample(); +``` + If the `signal` option is enabled, calling `.abort()` on the corresponding `AbortController` is similar to calling `.kill()` on the child process except the error passed to the callback will be an `AbortError`: -```js +```cjs const { exec } = require('node:child_process'); const controller = new AbortController(); const { signal } = controller; @@ -277,6 +333,16 @@ const child = exec('grep ssh', { signal }, (error) => { controller.abort(); ``` +```mjs +import { exec } from 'node:child_process'; +const controller = new AbortController(); +const { signal } = controller; +const child = exec('grep ssh', { signal }, (error) => { + console.error(error); // an AbortError +}); +controller.abort(); +``` + ### `child_process.execFile(file[, args][, options][, callback])` > Stability: 1.0 - Early development @@ -1406,6 +1406,9 @@ Follows [ECMAScript module][] resolution rules. Use [`--require`][] to load a [CommonJS module][]. Modules preloaded with `--require` will run before modules preloaded with `--import`. +Modules are preloaded into the main thread as well as any worker threads, +forked processes, or clustered processes. + ### `--input-type=type` > Stability: 1 - Experimental @@ -2249,7 +2255,7 @@ files must meet **both** criteria to be included in the coverage report. ### `--test-coverage-functions=threshold` > Stability: 1 - Experimental @@ -2277,7 +2283,7 @@ files must meet **both** criteria to be included in the coverage report. ### `--test-coverage-lines=threshold` > Stability: 1 - Experimental @@ -2867,7 +2873,7 @@ When set, colors will not be used in the REPL. ### `NODE_DISABLE_COMPILE_CACHE=1` > Stability: 1.1 - Active Development diff --git a/doc/api/module.md b/doc/api/module.md index 9b98231f02e52c..ad5ce86c5328a1 100644 --- a/doc/api/module.md +++ b/doc/api/module.md @@ -67,7 +67,7 @@ const siblingModule = require('./sibling-module'); ### `module.constants.compileCacheStatus` > Stability: 1.1 - Active Development @@ -120,7 +120,7 @@ The following constants are returned as the `status` field in the object returne ### `module.enableCompileCache([cacheDir])` > Stability: 1.1 - Active Development @@ -165,7 +165,7 @@ be inheritend into the child workers. The directory can be obtained either from @@ -202,7 +202,7 @@ separately if the same base directory is used to persist the cache, so they can ### `module.getCompileCacheDir()` > Stability: 1.1 - Active Development diff --git a/doc/api/perf_hooks.md b/doc/api/perf_hooks.md index 719c6f11ea946b..e8a9022f4f0a39 100644 --- a/doc/api/perf_hooks.md +++ b/doc/api/perf_hooks.md @@ -890,7 +890,7 @@ initialized. ### `performanceNodeTiming.uvMetricsInfo` * Returns: {Object} diff --git a/doc/api/sqlite.md b/doc/api/sqlite.md index ac17ecca640d21..e0d102a1c473a4 100644 --- a/doc/api/sqlite.md +++ b/doc/api/sqlite.md @@ -198,7 +198,8 @@ added: v22.5.0 * Returns: {string} The source SQL expanded to include parameter values. This method returns the source SQL of the prepared statement with parameter -placeholders replaced by values. This method is a wrapper around +placeholders replaced by the values that were used during the most recent +execution of this prepared statement. This method is a wrapper around [`sqlite3_expanded_sql()`][]. ### `statement.get([namedParameters][, ...anonymousParameters])` @@ -308,11 +309,11 @@ exception. | SQLite | JavaScript | | --------- | -------------------- | -| `NULL` | `null` | -| `INTEGER` | `number` or `BigInt` | -| `REAL` | `number` | -| `TEXT` | `string` | -| `BLOB` | `Uint8Array` | +| `NULL` | {null} | +| `INTEGER` | {number} or {bigint} | +| `REAL` | {number} | +| `TEXT` | {string} | +| `BLOB` | {Uint8Array} | [SQL injection]: https://en.wikipedia.org/wiki/SQL_injection [`--experimental-sqlite`]: cli.md#--experimental-sqlite diff --git a/doc/api/test.md b/doc/api/test.md index a7af114880ea87..19819202d1d2e1 100644 --- a/doc/api/test.md +++ b/doc/api/test.md @@ -1246,7 +1246,7 @@ added: - v18.9.0 - v16.19.0 changes: - - version: REPLACEME + - version: v22.8.0 pr-url: https://github.com/nodejs/node/pull/53927 description: Added the `isolation` option. - version: v22.6.0 diff --git a/doc/api/util.md b/doc/api/util.md index ee6a91af5159d4..c0e55621a15756 100644 --- a/doc/api/util.md +++ b/doc/api/util.md @@ -364,6 +364,63 @@ util.formatWithOptions({ colors: true }, 'See object %O', { foo: 42 }); // when printed to a terminal. ``` +## `util.getCallSite(frames)` + +> Stability: 1.1 - Active development + + + +* `frames` {number} Number of frames returned in the stacktrace. + **Default:** `10`. Allowable range is between 1 and 200. +* Returns: {Object\[]} An array of stacktrace objects + * `functionName` {string} Returns the name of the function associated with this stack frame. + * `scriptName` {string} Returns the name of the resource that contains the script for the + function for this StackFrame. + * `lineNumber` {number} Returns the number, 1-based, of the line for the associate function call. + * `column` {number} Returns the 1-based column offset on the line for the associated function call. + +Returns an array of stacktrace objects containing the stack of +the caller function. + +```js +const util = require('node:util'); + +function exampleFunction() { + const callSites = util.getCallSite(); + + console.log('Call Sites:'); + callSites.forEach((callSite, index) => { + console.log(`CallSite ${index + 1}:`); + console.log(`Function Name: ${callSite.functionName}`); + console.log(`Script Name: ${callSite.scriptName}`); + console.log(`Line Number: ${callSite.lineNumer}`); + console.log(`Column Number: ${callSite.column}`); + }); + // CallSite 1: + // Function Name: exampleFunction + // Script Name: /home/example.js + // Line Number: 5 + // Column Number: 26 + + // CallSite 2: + // Function Name: anotherFunction + // Script Name: /home/example.js + // Line Number: 22 + // Column Number: 3 + + // ... +} + +// A function to simulate another stack layer +function anotherFunction() { + exampleFunction(); +} + +anotherFunction(); +``` + ## `util.getSystemErrorName(err)`