Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs: test for cli options being in sync between node_options.cc and cli.md #51623

Merged
merged 1 commit into from
Mar 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions doc/api/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -2618,11 +2618,15 @@ V8 options that are allowed are:

<!-- node-options-v8 end -->

<!-- node-options-others start -->

`--perf-basic-prof-only-functions`, `--perf-basic-prof`,
`--perf-prof-unwinding-info`, and `--perf-prof` are only available on Linux.

`--enable-etw-stack-walking` is only available on Windows.

<!-- node-options-others end -->

### `NODE_PATH=path[:…]`

<!-- YAML
Expand Down Expand Up @@ -2915,6 +2919,32 @@ options are of interest only to V8 developers. Despite this, there is a small
set of V8 options that are widely applicable to Node.js, and they are
documented here:

<!-- v8-options start -->

### `--abort-on-uncaught-exception`

### `--disallow-code-generation-from-strings`

### `--enable-etw-stack-walking`

### `--harmony-shadow-realm`

### `--huge-max-old-generation-size`

### `--jitless`

### `--interpreted-frames-native-stack`

### `--prof`

### `--perf-basic-prof`

### `--perf-basic-prof-only-functions`

### `--perf-prof`

### `--perf-prof-unwinding-info`

### `--max-old-space-size=SIZE` (in megabytes)

Sets the max memory size of V8's old memory section. As memory
Expand Down Expand Up @@ -2953,6 +2983,19 @@ for MiB in 16 32 64 128; do
done
```

### `--security-revert`

### `--stack-trace-limit=limit`

The maximum number of stack frames to collect in an error's stack trace.
Setting it to 0 disables stack trace collection. The default value is 10.

```bash
node --stack-trace-limit=12 -p -e "Error.stackTraceLimit" # prints 12
```

<!-- v8-options end -->

[#42511]: https://github.com/nodejs/node/issues/42511
[Chrome DevTools Protocol]: https://chromedevtools.github.io/devtools-protocol/
[CommonJS]: modules.md
Expand Down
14 changes: 14 additions & 0 deletions doc/contributing/internal-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@ rules. The core developers may remove these flags in any version of Node.js.

### Flags

#### `--debug-arraybuffer-allocations`

#### `--expose-internals`

Allows to require the `internal/*` modules.

#### `--inspect-brk-node[=[host:]port]`

<!-- YAML
Expand All @@ -17,3 +23,11 @@ added: v7.6.0
Activate inspector on `host:port` and break at start of the first internal
JavaScript script executed when the inspector is available.
Default `host:port` is `127.0.0.1:9229`.

#### `--node-snapshot`

#### `--test-udp-no-try-send`

#### `--trace-promises`

#### `--verify-base-objects`
3 changes: 0 additions & 3 deletions doc/node.1
Original file line number Diff line number Diff line change
Expand Up @@ -160,9 +160,6 @@ Interpret as either ES modules or CommonJS modules input via --eval or STDIN, wh
.js or extensionless files with no sibling or parent package.json;
.js or extensionless files whose nearest parent package.json lacks a "type" field, unless under node_modules.
.
.It Fl -experimental-global-webcrypto
Expose the Web Crypto API on the global scope.
.
.It Fl -experimental-import-meta-resolve
Enable experimental ES modules support for import.meta.resolve().
.
Expand Down
129 changes: 129 additions & 0 deletions test/parallel/test-cli-node-options-docs.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
'use strict';
const common = require('../common');
if (process.config.variables.node_without_node_options)
common.skip('missing NODE_OPTIONS support');

// Test options specified by env variable.

const assert = require('assert');
const fs = require('fs');
const path = require('path');

const rootDir = path.resolve(__dirname, '..', '..');
const cliMd = path.join(rootDir, 'doc', 'api', 'cli.md');
const cliText = fs.readFileSync(cliMd, { encoding: 'utf8' });

const internalApiMd = path.join(rootDir, 'doc', 'contributing', 'internal-api.md');
const internalApiText = fs.readFileSync(internalApiMd, { encoding: 'utf8' });

const nodeOptionsCC = fs.readFileSync(path.resolve(rootDir, 'src', 'node_options.cc'), 'utf8');
const addOptionRE = /AddOption[\s\n\r]*\([\s\n\r]*"([^"]+)"(.*?)\);/gs;

const nodeOptionsText = cliText.match(/<!-- node-options-node start -->(.*)<!-- node-options-others end -->/s)[1];
const v8OptionsText = cliText.match(/<!-- v8-options start -->(.*)<!-- v8-options end -->/s)[1];

const manPage = path.join(rootDir, 'doc', 'node.1');
const manPageText = fs.readFileSync(manPage, { encoding: 'utf8' });

// Documented in /doc/api/deprecations.md
const deprecated = [
'--debug',
'--debug-brk',
];


const manPagesOptions = new Set();

for (const [, envVar] of manPageText.matchAll(/\.It Fl (-[a-zA-Z0-9._-]+)/g)) {
manPagesOptions.add(envVar);
}

for (const [, envVar, config] of nodeOptionsCC.matchAll(addOptionRE)) {
let hasTrueAsDefaultValue = false;
let isInNodeOption = false;
let isV8Option = false;
let isNoOp = false;

if (config.includes('NoOp{}')) {
isNoOp = true;
}

if (config.includes('kAllowedInEnvvar')) {
isInNodeOption = true;
}
if (config.includes('kDisallowedInEnvvar')) {
isInNodeOption = false;
}

if (config.includes('V8Option{}')) {
isV8Option = true;
}

if (/^\s*true\s*$/.test(config.split(',').pop())) {
hasTrueAsDefaultValue = true;
}

if (
envVar.startsWith('[') ||
deprecated.includes(envVar) ||
isNoOp
) {
// assert(!manPagesOptions.has(envVar.slice(1)), `Option ${envVar} should not be documented`)
manPagesOptions.delete(envVar.slice(1));
continue;
}

// Internal API options are documented in /doc/contributing/internal-api.md
if (new RegExp(`####.*\`${envVar}[[=\\s\\b\`]`).test(internalApiText) === true) {
manPagesOptions.delete(envVar.slice(1));
continue;
}

// CLI options
if (!isV8Option && !hasTrueAsDefaultValue) {
if (new RegExp(`###.*\`${envVar}[[=\\s\\b\`]`).test(cliText) === false) {
assert(false, `Should have option ${envVar} documented`);
} else {
manPagesOptions.delete(envVar.slice(1));
}
}

if (!hasTrueAsDefaultValue && new RegExp(`###.*\`--no${envVar.slice(1)}[[=\\s\\b\`]`).test(cliText) === true) {
assert(false, `Should not have option --no${envVar.slice(1)} documented`);
}

if (!isV8Option && hasTrueAsDefaultValue) {
if (new RegExp(`###.*\`--no${envVar.slice(1)}[[=\\s\\b\`]`).test(cliText) === false) {
assert(false, `Should have option --no${envVar.slice(1)} documented`);
} else {
manPagesOptions.delete(`-no${envVar.slice(1)}`);
}
}

// NODE_OPTIONS
if (isInNodeOption && !hasTrueAsDefaultValue && new RegExp(`\`${envVar}\``).test(nodeOptionsText) === false) {
assert(false, `Should have option ${envVar} in NODE_OPTIONS documented`);
}

if (isInNodeOption && hasTrueAsDefaultValue && new RegExp(`\`--no${envVar.slice(1)}`).test(cliText) === false) {
assert(false, `Should have option --no${envVar.slice(1)} in NODE_OPTIONS documented`);
}

if (!hasTrueAsDefaultValue && new RegExp(`\`--no${envVar.slice(1)}`).test(cliText) === true) {
assert(false, `Should not have option --no${envVar.slice(1)} in NODE_OPTIONS documented`);
}

// V8 options
if (isV8Option) {
if (new RegExp(`###.*\`${envVar}[[=\\s\\b\`]`).test(v8OptionsText) === false) {
assert(false, `Should have option ${envVar} in V8 options documented`);
} else {
manPagesOptions.delete(envVar.slice(1));
}
}
}

// add alias handling
manPagesOptions.delete('-trace-events-enabled');

assert.strictEqual(manPagesOptions.size, 0, `Man page options not documented: ${[...manPagesOptions]}`);