11cb0ef41Sopenharmony_ci'use strict';
21cb0ef41Sopenharmony_ci
31cb0ef41Sopenharmony_ciconst common = require('../common');
41cb0ef41Sopenharmony_ci
51cb0ef41Sopenharmony_ciconst assert = require('assert');
61cb0ef41Sopenharmony_ciconst fs = require('fs');
71cb0ef41Sopenharmony_ciconst path = require('path');
81cb0ef41Sopenharmony_ci
91cb0ef41Sopenharmony_ciconst rootDir = path.resolve(__dirname, '..', '..');
101cb0ef41Sopenharmony_ciconst cliMd = path.join(rootDir, 'doc', 'api', 'cli.md');
111cb0ef41Sopenharmony_ciconst cliText = fs.readFileSync(cliMd, { encoding: 'utf8' });
121cb0ef41Sopenharmony_ci
131cb0ef41Sopenharmony_ciconst parseSection = (text, startMarker, endMarker) => {
141cb0ef41Sopenharmony_ci  const regExp = new RegExp(`${startMarker}\r?\n([^]*)\r?\n${endMarker}`);
151cb0ef41Sopenharmony_ci  const match = text.match(regExp);
161cb0ef41Sopenharmony_ci  assert(match,
171cb0ef41Sopenharmony_ci         `Unable to locate text between '${startMarker}' and '${endMarker}'.`);
181cb0ef41Sopenharmony_ci  return match[1]
191cb0ef41Sopenharmony_ci         .split(/\r?\n/)
201cb0ef41Sopenharmony_ci         .filter((val) => val.trim() !== '');
211cb0ef41Sopenharmony_ci};
221cb0ef41Sopenharmony_ci
231cb0ef41Sopenharmony_ciconst nodeOptionsLines = parseSection(cliText,
241cb0ef41Sopenharmony_ci                                      '<!-- node-options-node start -->',
251cb0ef41Sopenharmony_ci                                      '<!-- node-options-node end -->');
261cb0ef41Sopenharmony_ciconst v8OptionsLines = parseSection(cliText,
271cb0ef41Sopenharmony_ci                                    '<!-- node-options-v8 start -->',
281cb0ef41Sopenharmony_ci                                    '<!-- node-options-v8 end -->');
291cb0ef41Sopenharmony_ci
301cb0ef41Sopenharmony_ci// Check the options are documented in alphabetical order.
311cb0ef41Sopenharmony_ciassert.deepStrictEqual(nodeOptionsLines, [...nodeOptionsLines].sort());
321cb0ef41Sopenharmony_ciassert.deepStrictEqual(v8OptionsLines, [...v8OptionsLines].sort());
331cb0ef41Sopenharmony_ci
341cb0ef41Sopenharmony_ciconst documented = new Set();
351cb0ef41Sopenharmony_cifor (const line of [...nodeOptionsLines, ...v8OptionsLines]) {
361cb0ef41Sopenharmony_ci  for (const match of line.matchAll(/`(-[^`]+)`/g)) {
371cb0ef41Sopenharmony_ci    // Remove negation from the option's name.
381cb0ef41Sopenharmony_ci    const option = match[1].replace('--no-', '--');
391cb0ef41Sopenharmony_ci    assert(!documented.has(option),
401cb0ef41Sopenharmony_ci           `Option '${option}' was documented more than once as an ` +
411cb0ef41Sopenharmony_ci           `allowed option for NODE_OPTIONS in ${cliMd}.`);
421cb0ef41Sopenharmony_ci    documented.add(option);
431cb0ef41Sopenharmony_ci  }
441cb0ef41Sopenharmony_ci}
451cb0ef41Sopenharmony_ci
461cb0ef41Sopenharmony_ciif (!common.hasOpenSSL3) {
471cb0ef41Sopenharmony_ci  documented.delete('--openssl-legacy-provider');
481cb0ef41Sopenharmony_ci  documented.delete('--openssl-shared-config');
491cb0ef41Sopenharmony_ci}
501cb0ef41Sopenharmony_ci
511cb0ef41Sopenharmony_ci// Filter out options that are conditionally present.
521cb0ef41Sopenharmony_ciconst conditionalOpts = [
531cb0ef41Sopenharmony_ci  {
541cb0ef41Sopenharmony_ci    include: common.hasCrypto,
551cb0ef41Sopenharmony_ci    filter: (opt) => {
561cb0ef41Sopenharmony_ci      return [
571cb0ef41Sopenharmony_ci        '--openssl-config',
581cb0ef41Sopenharmony_ci        common.hasOpenSSL3 ? '--openssl-legacy-provider' : '',
591cb0ef41Sopenharmony_ci        common.hasOpenSSL3 ? '--openssl-shared-config' : '',
601cb0ef41Sopenharmony_ci        '--tls-cipher-list',
611cb0ef41Sopenharmony_ci        '--use-bundled-ca',
621cb0ef41Sopenharmony_ci        '--use-openssl-ca',
631cb0ef41Sopenharmony_ci        '--secure-heap',
641cb0ef41Sopenharmony_ci        '--secure-heap-min',
651cb0ef41Sopenharmony_ci        '--enable-fips',
661cb0ef41Sopenharmony_ci        '--force-fips',
671cb0ef41Sopenharmony_ci      ].includes(opt);
681cb0ef41Sopenharmony_ci    }
691cb0ef41Sopenharmony_ci  }, {
701cb0ef41Sopenharmony_ci    include: common.hasIntl,
711cb0ef41Sopenharmony_ci    filter: (opt) => opt === '--icu-data-dir'
721cb0ef41Sopenharmony_ci  }, {
731cb0ef41Sopenharmony_ci    include: process.features.inspector,
741cb0ef41Sopenharmony_ci    filter: (opt) => opt.startsWith('--inspect') || opt === '--debug-port'
751cb0ef41Sopenharmony_ci  },
761cb0ef41Sopenharmony_ci];
771cb0ef41Sopenharmony_cidocumented.forEach((opt) => {
781cb0ef41Sopenharmony_ci  conditionalOpts.forEach(({ include, filter }) => {
791cb0ef41Sopenharmony_ci    if (!include && filter(opt)) {
801cb0ef41Sopenharmony_ci      documented.delete(opt);
811cb0ef41Sopenharmony_ci    }
821cb0ef41Sopenharmony_ci  });
831cb0ef41Sopenharmony_ci});
841cb0ef41Sopenharmony_ci
851cb0ef41Sopenharmony_ciconst difference = (setA, setB) => {
861cb0ef41Sopenharmony_ci  return new Set([...setA].filter((x) => !setB.has(x)));
871cb0ef41Sopenharmony_ci};
881cb0ef41Sopenharmony_ci
891cb0ef41Sopenharmony_ciconst overdocumented = difference(documented,
901cb0ef41Sopenharmony_ci                                  process.allowedNodeEnvironmentFlags);
911cb0ef41Sopenharmony_ciassert.strictEqual(overdocumented.size, 0,
921cb0ef41Sopenharmony_ci                   'The following options are documented as allowed in ' +
931cb0ef41Sopenharmony_ci                   `NODE_OPTIONS in ${cliMd}: ` +
941cb0ef41Sopenharmony_ci                   `${[...overdocumented].join(' ')} ` +
951cb0ef41Sopenharmony_ci                   'but are not in process.allowedNodeEnvironmentFlags');
961cb0ef41Sopenharmony_ciconst undocumented = difference(process.allowedNodeEnvironmentFlags,
971cb0ef41Sopenharmony_ci                                documented);
981cb0ef41Sopenharmony_ci// Remove intentionally undocumented options.
991cb0ef41Sopenharmony_ciassert(undocumented.delete('--debug-arraybuffer-allocations'));
1001cb0ef41Sopenharmony_ciassert(undocumented.delete('--no-debug-arraybuffer-allocations'));
1011cb0ef41Sopenharmony_ciassert(undocumented.delete('--es-module-specifier-resolution'));
1021cb0ef41Sopenharmony_ciassert(undocumented.delete('--experimental-report'));
1031cb0ef41Sopenharmony_ciassert(undocumented.delete('--experimental-worker'));
1041cb0ef41Sopenharmony_ciassert(undocumented.delete('--node-snapshot'));
1051cb0ef41Sopenharmony_ciassert(undocumented.delete('--no-node-snapshot'));
1061cb0ef41Sopenharmony_ciassert(undocumented.delete('--loader'));
1071cb0ef41Sopenharmony_ciassert(undocumented.delete('--verify-base-objects'));
1081cb0ef41Sopenharmony_ciassert(undocumented.delete('--no-verify-base-objects'));
1091cb0ef41Sopenharmony_ci
1101cb0ef41Sopenharmony_ci// Remove negated versions of the flags.
1111cb0ef41Sopenharmony_cifor (const flag of undocumented) {
1121cb0ef41Sopenharmony_ci  if (flag.startsWith('--no-')) {
1131cb0ef41Sopenharmony_ci    assert(documented.has(`--${flag.slice(5)}`), flag);
1141cb0ef41Sopenharmony_ci    undocumented.delete(flag);
1151cb0ef41Sopenharmony_ci  }
1161cb0ef41Sopenharmony_ci}
1171cb0ef41Sopenharmony_ci
1181cb0ef41Sopenharmony_ciassert.strictEqual(undocumented.size, 0,
1191cb0ef41Sopenharmony_ci                   'The following options are not documented as allowed in ' +
1201cb0ef41Sopenharmony_ci                   `NODE_OPTIONS in ${cliMd}: ${[...undocumented].join(' ')}`);
121