1'use strict'; 2 3const common = require('../common'); 4 5const assert = require('assert'); 6const fs = require('fs'); 7const path = require('path'); 8 9const rootDir = path.resolve(__dirname, '..', '..'); 10const cliMd = path.join(rootDir, 'doc', 'api', 'cli.md'); 11const cliText = fs.readFileSync(cliMd, { encoding: 'utf8' }); 12 13const parseSection = (text, startMarker, endMarker) => { 14 const regExp = new RegExp(`${startMarker}\r?\n([^]*)\r?\n${endMarker}`); 15 const match = text.match(regExp); 16 assert(match, 17 `Unable to locate text between '${startMarker}' and '${endMarker}'.`); 18 return match[1].split(/\r?\n/); 19}; 20 21const nodeOptionsLines = parseSection(cliText, 22 '<!-- node-options-node start -->', 23 '<!-- node-options-node end -->'); 24const v8OptionsLines = parseSection(cliText, 25 '<!-- node-options-v8 start -->', 26 '<!-- node-options-v8 end -->'); 27// Check the options are documented in alphabetical order. 28assert.deepStrictEqual(nodeOptionsLines, [...nodeOptionsLines].sort()); 29assert.deepStrictEqual(v8OptionsLines, [...v8OptionsLines].sort()); 30 31const documented = new Set(); 32for (const line of [...nodeOptionsLines, ...v8OptionsLines]) { 33 for (const match of line.matchAll(/`(-[^`]+)`/g)) { 34 // Remove negation from the option's name. 35 const option = match[1].replace('--no-', '--'); 36 assert(!documented.has(option), 37 `Option '${option}' was documented more than once as an ` + 38 `allowed option for NODE_OPTIONS in ${cliMd}.`); 39 documented.add(option); 40 } 41} 42 43// Filter out options that are conditionally present. 44const conditionalOpts = [ 45 { 46 include: common.hasCrypto, 47 filter: (opt) => { 48 return [ 49 '--openssl-config', 50 '--tls-cipher-list', 51 '--use-bundled-ca', 52 '--use-openssl-ca', 53 '--secure-heap', 54 '--secure-heap-min', 55 '--enable-fips', 56 '--force-fips', 57 ].includes(opt); 58 } 59 }, { 60 include: common.hasIntl, 61 filter: (opt) => opt === '--icu-data-dir' 62 }, { 63 include: process.features.inspector, 64 filter: (opt) => opt.startsWith('--inspect') || opt === '--debug-port' 65 }, 66]; 67documented.forEach((opt) => { 68 conditionalOpts.forEach(({ include, filter }) => { 69 if (!include && filter(opt)) { 70 documented.delete(opt); 71 } 72 }); 73}); 74 75const difference = (setA, setB) => { 76 return new Set([...setA].filter((x) => !setB.has(x))); 77}; 78 79const overdocumented = difference(documented, 80 process.allowedNodeEnvironmentFlags); 81assert.strictEqual(overdocumented.size, 0, 82 'The following options are documented as allowed in ' + 83 `NODE_OPTIONS in ${cliMd}: ` + 84 `${[...overdocumented].join(' ')} ` + 85 'but are not in process.allowedNodeEnvironmentFlags'); 86const undocumented = difference(process.allowedNodeEnvironmentFlags, 87 documented); 88// Remove intentionally undocumented options. 89assert(undocumented.delete('--debug-arraybuffer-allocations')); 90assert(undocumented.delete('--no-debug-arraybuffer-allocations')); 91assert(undocumented.delete('--es-module-specifier-resolution')); 92assert(undocumented.delete('--experimental-report')); 93assert(undocumented.delete('--experimental-worker')); 94assert(undocumented.delete('--node-snapshot')); 95assert(undocumented.delete('--no-node-snapshot')); 96assert(undocumented.delete('--loader')); 97assert(undocumented.delete('--verify-base-objects')); 98assert(undocumented.delete('--no-verify-base-objects')); 99 100// Remove negated versions of the flags. 101for (const flag of undocumented) { 102 if (flag.startsWith('--no-')) { 103 assert(documented.has(`--${flag.slice(5)}`), flag); 104 undocumented.delete(flag); 105 } 106} 107 108assert.strictEqual(undocumented.size, 0, 109 'The following options are not documented as allowed in ' + 110 `NODE_OPTIONS in ${cliMd}: ${[...undocumented].join(' ')}`); 111