• 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  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