'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(/(.*)/s)[1]; const v8OptionsText = cliText.match(/(.*)/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)); } } } { const sections = /^## (.+)$/mg; const cliOptionPattern = /^### (?:`-\w.*`, )?`([^`]+)`/mg; let match; let previousIndex = 0; do { const sectionTitle = match?.[1]; match = sections.exec(cliText); const filteredCLIText = cliText.slice(previousIndex, match?.index); const options = Array.from(filteredCLIText.matchAll(cliOptionPattern), (match) => match[1]); assert.deepStrictEqual(options, options.toSorted(), `doc/api/cli.md ${sectionTitle} subsections are not in alphabetical order`); previousIndex = match?.index; } while (match); } // add alias handling manPagesOptions.delete('-trace-events-enabled'); assert.strictEqual(manPagesOptions.size, 0, `Man page options not documented: ${[...manPagesOptions]}`);