1'use strict'; 2 3const { 4 ArrayPrototypeConcat, 5 ArrayPrototypeSort, 6 Boolean, 7 MathFloor, 8 MathMax, 9 ObjectKeys, 10 RegExp, 11 StringPrototypeLocaleCompare, 12 StringPrototypeSlice, 13 StringPrototypeTrimLeft, 14 StringPrototypeRepeat, 15 StringPrototypeReplace, 16 SafeMap, 17} = primordials; 18 19const { types } = internalBinding('options'); 20const hasCrypto = Boolean(process.versions.openssl); 21 22const { 23 prepareMainThreadExecution 24} = require('internal/bootstrap/pre_execution'); 25 26const typeLookup = []; 27for (const key of ObjectKeys(types)) 28 typeLookup[types[key]] = key; 29 30// Environment variables are parsed ad-hoc throughout the code base, 31// so we gather the documentation here. 32const { hasIntl, hasSmallICU, hasNodeOptions } = internalBinding('config'); 33const envVars = new SafeMap(ArrayPrototypeConcat([ 34 ['FORCE_COLOR', { helpText: "when set to 'true', 1, 2, 3, or an empty " + 35 'string causes NO_COLOR and NODE_DISABLE_COLORS to be ignored.' }], 36 ['NO_COLOR', { helpText: 'Alias for NODE_DISABLE_COLORS' }], 37 ['NODE_DEBUG', { helpText: "','-separated list of core modules that " + 38 'should print debug information' }], 39 ['NODE_DEBUG_NATIVE', { helpText: "','-separated list of C++ core debug " + 40 'categories that should print debug output' }], 41 ['NODE_DISABLE_COLORS', { helpText: 'set to 1 to disable colors in ' + 42 'the REPL' }], 43 ['NODE_EXTRA_CA_CERTS', { helpText: 'path to additional CA certificates ' + 44 'file. Only read once during process startup.' }], 45 ['NODE_NO_WARNINGS', { helpText: 'set to 1 to silence process warnings' }], 46 ['NODE_PATH', { helpText: `'${require('path').delimiter}'-separated list ` + 47 'of directories prefixed to the module search path' }], 48 ['NODE_PENDING_DEPRECATION', { helpText: 'set to 1 to emit pending ' + 49 'deprecation warnings' }], 50 ['NODE_PENDING_PIPE_INSTANCES', { helpText: 'set the number of pending ' + 51 'pipe instance handles on Windows' }], 52 ['NODE_PRESERVE_SYMLINKS', { helpText: 'set to 1 to preserve symbolic ' + 53 'links when resolving and caching modules' }], 54 ['NODE_REDIRECT_WARNINGS', { helpText: 'write warnings to path instead ' + 55 'of stderr' }], 56 ['NODE_REPL_HISTORY', { helpText: 'path to the persistent REPL ' + 57 'history file' }], 58 ['NODE_TLS_REJECT_UNAUTHORIZED', { helpText: 'set to 0 to disable TLS ' + 59 'certificate validation' }], 60 ['NODE_V8_COVERAGE', { helpText: 'directory to output v8 coverage JSON ' + 61 'to' }], 62 ['UV_THREADPOOL_SIZE', { helpText: 'sets the number of threads used in ' + 63 'libuv\'s threadpool' }], 64], hasIntl ? [ 65 ['NODE_ICU_DATA', { helpText: 'data path for ICU (Intl object) data' + 66 hasSmallICU ? '' : ' (will extend linked-in data)' }], 67] : []), (hasNodeOptions ? [ 68 ['NODE_OPTIONS', { helpText: 'set CLI options in the environment via a ' + 69 'space-separated list' }], 70] : []), hasCrypto ? [ 71 ['OPENSSL_CONF', { helpText: 'load OpenSSL configuration from file' }], 72 ['SSL_CERT_DIR', { helpText: 'sets OpenSSL\'s directory of trusted ' + 73 'certificates when used in conjunction with --use-openssl-ca' }], 74 ['SSL_CERT_FILE', { helpText: 'sets OpenSSL\'s trusted certificate file ' + 75 'when used in conjunction with --use-openssl-ca' }], 76] : []); 77 78 79function indent(text, depth) { 80 return StringPrototypeReplace(text, /^/gm, StringPrototypeRepeat(' ', depth)); 81} 82 83function fold(text, width) { 84 return StringPrototypeReplace(text, 85 new RegExp(`([^\n]{0,${width}})( |$)`, 'g'), 86 (_, newLine, end) => 87 newLine + (end === ' ' ? '\n' : '')); 88} 89 90function getArgDescription(type) { 91 switch (typeLookup[type]) { 92 case 'kNoOp': 93 case 'kV8Option': 94 case 'kBoolean': 95 case undefined: 96 break; 97 case 'kHostPort': 98 return '[host:]port'; 99 case 'kInteger': 100 case 'kUInteger': 101 case 'kString': 102 case 'kStringList': 103 return '...'; 104 default: 105 require('assert').fail(`unknown option type ${type}`); 106 } 107} 108 109function format( 110 { options, aliases = new SafeMap(), firstColumn, secondColumn } 111) { 112 let text = ''; 113 let maxFirstColumnUsed = 0; 114 115 const sortedOptions = ArrayPrototypeSort( 116 [...options.entries()], 117 ({ 0: name1, 1: option1 }, { 0: name2, 1: option2 }) => { 118 if (option1.defaultIsTrue) { 119 name1 = `--no-${StringPrototypeSlice(name1, 2)}`; 120 } 121 if (option2.defaultIsTrue) { 122 name2 = `--no-${StringPrototypeSlice(name2, 2)}`; 123 } 124 return StringPrototypeLocaleCompare(name1, name2); 125 }, 126 ); 127 128 for (const { 129 0: name, 1: { helpText, type, value, defaultIsTrue } 130 } of sortedOptions) { 131 if (!helpText) continue; 132 133 let displayName = name; 134 135 if (defaultIsTrue) { 136 displayName = `--no-${StringPrototypeSlice(displayName, 2)}`; 137 } 138 139 const argDescription = getArgDescription(type); 140 if (argDescription) 141 displayName += `=${argDescription}`; 142 143 for (const { 0: from, 1: to } of aliases) { 144 // For cases like e.g. `-e, --eval`. 145 if (to[0] === name && to.length === 1) { 146 displayName = `${from}, ${displayName}`; 147 } 148 149 // For cases like `--inspect-brk[=[host:]port]`. 150 const targetInfo = options.get(to[0]); 151 const targetArgDescription = 152 targetInfo ? getArgDescription(targetInfo.type) : '...'; 153 if (from === `${name}=`) { 154 displayName += `[=${targetArgDescription}]`; 155 } else if (from === `${name} <arg>`) { 156 displayName += ` [${targetArgDescription}]`; 157 } 158 } 159 160 let displayHelpText = helpText; 161 if (value === !defaultIsTrue) { 162 // Mark boolean options we currently have enabled. 163 // In particular, it indicates whether --use-openssl-ca 164 // or --use-bundled-ca is the (current) default. 165 displayHelpText += ' (currently set)'; 166 } 167 168 text += displayName; 169 maxFirstColumnUsed = MathMax(maxFirstColumnUsed, displayName.length); 170 if (displayName.length >= firstColumn) 171 text += '\n' + StringPrototypeRepeat(' ', firstColumn); 172 else 173 text += StringPrototypeRepeat(' ', firstColumn - displayName.length); 174 175 text += StringPrototypeTrimLeft( 176 indent(fold(displayHelpText, secondColumn), firstColumn)) + '\n'; 177 } 178 179 if (maxFirstColumnUsed < firstColumn - 4) { 180 // If we have more than 4 blank gap spaces, reduce first column width. 181 return format({ 182 options, 183 aliases, 184 firstColumn: maxFirstColumnUsed + 2, 185 secondColumn 186 }); 187 } 188 189 return text; 190} 191 192function print(stream) { 193 const { options, aliases } = require('internal/options'); 194 195 // Use 75 % of the available width, and at least 70 characters. 196 const width = MathMax(70, (stream.columns || 0) * 0.75); 197 const firstColumn = MathFloor(width * 0.4); 198 const secondColumn = MathFloor(width * 0.57); 199 200 options.set('-', { helpText: 'script read from stdin ' + 201 '(default if no file name is provided, ' + 202 'interactive mode if a tty)' }); 203 options.set('--', { helpText: 'indicate the end of node options' }); 204 stream.write( 205 'Usage: node [options] [ script.js ] [arguments]\n' + 206 ' node inspect [options] [ script.js | host:port ] [arguments]\n\n' + 207 'Options:\n'); 208 stream.write(indent(format({ 209 options, aliases, firstColumn, secondColumn 210 }), 2)); 211 212 stream.write('\nEnvironment variables:\n'); 213 214 stream.write(format({ 215 options: envVars, firstColumn, secondColumn 216 })); 217 218 stream.write('\nDocumentation can be found at https://nodejs.org/\n'); 219} 220 221prepareMainThreadExecution(); 222 223markBootstrapComplete(); 224 225print(process.stdout); 226