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