lts/lib/internal/main/print_help.js
'use strict';
/* eslint-disable no-restricted-globals */
const { types } = internalBinding('options');
const hasCrypto = Boolean(process.versions.openssl);
const {
prepareMainThreadExecution
} = require('internal/bootstrap/pre_execution');
const typeLookup = [];
for (const key of Object.keys(types))
typeLookup[types[key]] = key;
// Environment variables are parsed ad-hoc throughout the code base,
// so we gather the documentation here.
const { hasIntl, hasSmallICU, hasNodeOptions } = internalBinding('config');
const envVars = new Map([
['NODE_DEBUG', { helpText: "','-separated list of core modules that " +
'should print debug information' }],
['NODE_DEBUG_NATIVE', { helpText: "','-separated list of C++ core debug " +
'categories that should print debug output' }],
['NODE_DISABLE_COLORS', { helpText: 'set to 1 to disable colors in ' +
'the REPL' }],
['NODE_EXTRA_CA_CERTS', { helpText: 'path to additional CA certificates ' +
'file' }],
['NODE_NO_WARNINGS', { helpText: 'set to 1 to silence process warnings' }],
['NODE_PATH', { helpText: `'${require('path').delimiter}'-separated list ` +
'of directories prefixed to the module search path' }],
['NODE_PENDING_DEPRECATION', { helpText: 'set to 1 to emit pending ' +
'deprecation warnings' }],
['NODE_PENDING_PIPE_INSTANCES', { helpText: 'set the number of pending ' +
'pipe instance handles on Windows' }],
['NODE_PRESERVE_SYMLINKS', { helpText: 'set to 1 to preserve symbolic ' +
'links when resolving and caching modules' }],
['NODE_REDIRECT_WARNINGS', { helpText: 'write warnings to path instead ' +
'of stderr' }],
['NODE_REPL_HISTORY', { helpText: 'path to the persistent REPL ' +
'history file' }],
['NODE_TLS_REJECT_UNAUTHORIZED', { helpText: 'set to 0 to disable TLS ' +
'certificate validation' }],
['NODE_V8_COVERAGE', { helpText: 'directory to output v8 coverage JSON ' +
'to' }],
['UV_THREADPOOL_SIZE', { helpText: 'sets the number of threads used in ' +
'libuv\'s threadpool' }]
].concat(hasIntl ? [
['NODE_ICU_DATA', { helpText: 'data path for ICU (Intl object) data' +
hasSmallICU ? '' : ' (will extend linked-in data)' }]
] : []).concat(hasNodeOptions ? [
['NODE_OPTIONS', { helpText: 'set CLI options in the environment via a ' +
'space-separated list' }]
] : []).concat(hasCrypto ? [
['OPENSSL_CONF', { helpText: 'load OpenSSL configuration from file' }],
['SSL_CERT_DIR', { helpText: 'sets OpenSSL\'s directory of trusted ' +
'certificates when used in conjunction with --use-openssl-ca' }],
['SSL_CERT_FILE', { helpText: 'sets OpenSSL\'s trusted certificate file ' +
'when used in conjunction with --use-openssl-ca' }],
] : []));
function indent(text, depth) {
return text.replace(/^/gm, ' '.repeat(depth));
}
function fold(text, width) {
return text.replace(new RegExp(`([^\n]{0,${width}})( |$)`, 'g'),
(_, newLine, end) => newLine + (end === ' ' ? '\n' : ''));
}
function getArgDescription(type) {
switch (typeLookup[type]) {
case 'kNoOp':
case 'kV8Option':
case 'kBoolean':
case undefined:
break;
case 'kHostPort':
return '[host:]port';
case 'kInteger':
case 'kUInteger':
case 'kString':
case 'kStringList':
return '...';
default:
require('assert').fail(`unknown option type ${type}`);
}
}
function format({ options, aliases = new Map(), firstColumn, secondColumn }) {
let text = '';
let maxFirstColumnUsed = 0;
for (const [
name, { helpText, type, value }
] of [...options.entries()].sort()) {
if (!helpText) continue;
let displayName = name;
const argDescription = getArgDescription(type);
if (argDescription)
displayName += `=${argDescription}`;
for (const [ from, to ] of aliases) {
// For cases like e.g. `-e, --eval`.
if (to[0] === name && to.length === 1) {
displayName = `${from}, ${displayName}`;
}
// For cases like `--inspect-brk[=[host:]port]`.
const targetInfo = options.get(to[0]);
const targetArgDescription =
targetInfo ? getArgDescription(targetInfo.type) : '...';
if (from === `${name}=`) {
displayName += `[=${targetArgDescription}]`;
} else if (from === `${name} <arg>`) {
displayName += ` [${targetArgDescription}]`;
}
}
let displayHelpText = helpText;
if (value === true) {
// Mark boolean options we currently have enabled.
// In particular, it indicates whether --use-openssl-ca
// or --use-bundled-ca is the (current) default.
displayHelpText += ' (currently set)';
}
text += displayName;
maxFirstColumnUsed = Math.max(maxFirstColumnUsed, displayName.length);
if (displayName.length >= firstColumn)
text += '\n' + ' '.repeat(firstColumn);
else
text += ' '.repeat(firstColumn - displayName.length);
text += indent(fold(displayHelpText, secondColumn),
firstColumn).trimLeft() + '\n';
}
if (maxFirstColumnUsed < firstColumn - 4) {
// If we have more than 4 blank gap spaces, reduce first column width.
return format({
options,
aliases,
firstColumn: maxFirstColumnUsed + 2,
secondColumn
});
}
return text;
}
function print(stream) {
const { options, aliases } = require('internal/options');
// Use 75 % of the available width, and at least 70 characters.
const width = Math.max(70, (stream.columns || 0) * 0.75);
const firstColumn = Math.floor(width * 0.4);
const secondColumn = Math.floor(width * 0.57);
options.set('-', { helpText: 'script read from stdin ' +
'(default if no file name is provided, ' +
'interactive mode if a tty)' });
options.set('--', { helpText: 'indicate the end of node options' });
stream.write(
'Usage: node [options] [ script.js ] [arguments]\n' +
' node inspect [options] [ script.js | host:port ] [arguments]\n\n' +
'Options:\n');
stream.write(indent(format({
options, aliases, firstColumn, secondColumn
}), 2));
stream.write('\nEnvironment variables:\n');
stream.write(format({
options: envVars, firstColumn, secondColumn
}));
stream.write('\nDocumentation can be found at https://nodejs.org/\n');
}
prepareMainThreadExecution();
markBootstrapComplete();
print(process.stdout);