Skip to content

Commit 3ef1512

Browse files
Maël Nisonaddaleax
Maël Nison
authored andcommitted
src: allows escaping NODE_OPTIONS with backslashes
The characters specified within NODE_OPTIONS can now be escaped, which is handy especially in conjunction with `--require` (where the file path might happen to contain spaces that shouldn't cause the option to be split into two). Fixes: #12971 PR-URL: #24065 Reviewed-By: Anna Henningsen <[email protected]>
1 parent ba74e42 commit 3ef1512

File tree

4 files changed

+55
-4
lines changed

4 files changed

+55
-4
lines changed

doc/api/cli.md

+7
Original file line numberDiff line numberDiff line change
@@ -774,6 +774,13 @@ if they had been specified on the command line before the actual command line
774774
(so they can be overridden). Node.js will exit with an error if an option
775775
that is not allowed in the environment is used, such as `-p` or a script file.
776776

777+
In case an option value happens to contain a space (for example a path listed in
778+
`--require`), it must be escaped using double quotes. For example:
779+
780+
```bash
781+
--require "./my path/file.js"
782+
```
783+
777784
Node.js options that are allowed are:
778785
- `--diagnostic-report-directory`
779786
- `--diagnostic-report-filename`

src/node.cc

+42-4
Original file line numberDiff line numberDiff line change
@@ -671,11 +671,49 @@ int InitializeNodeWithArgs(std::vector<std::string>* argv,
671671

672672
#if !defined(NODE_WITHOUT_NODE_OPTIONS)
673673
std::string node_options;
674+
674675
if (credentials::SafeGetenv("NODE_OPTIONS", &node_options)) {
675-
// [0] is expected to be the program name, fill it in from the real argv
676-
// and use 'x' as a placeholder while parsing.
677-
std::vector<std::string> env_argv = SplitString("x " + node_options, ' ');
678-
env_argv[0] = argv->at(0);
676+
std::vector<std::string> env_argv;
677+
// [0] is expected to be the program name, fill it in from the real argv.
678+
env_argv.push_back(argv->at(0));
679+
680+
bool is_in_string = false;
681+
bool will_start_new_arg = true;
682+
for (std::string::size_type index = 0;
683+
index < node_options.size();
684+
++index) {
685+
char c = node_options.at(index);
686+
687+
// Backslashes escape the following character
688+
if (c == '\\' && is_in_string) {
689+
if (index + 1 == node_options.size()) {
690+
errors->push_back("invalid value for NODE_OPTIONS "
691+
"(invalid escape)\n");
692+
return 9;
693+
} else {
694+
c = node_options.at(++index);
695+
}
696+
} else if (c == ' ' && !is_in_string) {
697+
will_start_new_arg = true;
698+
continue;
699+
} else if (c == '"') {
700+
is_in_string = !is_in_string;
701+
continue;
702+
}
703+
704+
if (will_start_new_arg) {
705+
env_argv.push_back(std::string(1, c));
706+
will_start_new_arg = false;
707+
} else {
708+
env_argv.back() += c;
709+
}
710+
}
711+
712+
if (is_in_string) {
713+
errors->push_back("invalid value for NODE_OPTIONS "
714+
"(unterminated string)\n");
715+
return 9;
716+
}
679717

680718
const int exit_code = ProcessGlobalArgs(&env_argv, nullptr, errors, true);
681719
if (exit_code != 0) return exit_code;

test/fixtures/print A.js

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
console.log('A')

test/parallel/test-cli-node-options.js

+5
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,12 @@ const tmpdir = require('../common/tmpdir');
1212
tmpdir.refresh();
1313

1414
const printA = require.resolve('../fixtures/printA.js');
15+
const printSpaceA = require.resolve('../fixtures/print A.js');
16+
17+
expect(` -r ${printA} `, 'A\nB\n');
1518
expect(`-r ${printA}`, 'A\nB\n');
19+
expect(`-r ${JSON.stringify(printA)}`, 'A\nB\n');
20+
expect(`-r ${JSON.stringify(printSpaceA)}`, 'A\nB\n');
1621
expect(`-r ${printA} -r ${printA}`, 'A\nB\n');
1722
expect(` -r ${printA} -r ${printA}`, 'A\nB\n');
1823
expect(` --require ${printA} --require ${printA}`, 'A\nB\n');

0 commit comments

Comments
 (0)