watch: strip watch flags from NODE_OPTIONS in child process · nodejs/node@be7f637 · GitHub
Skip to content

Commit be7f637

Browse files
marcopiracciniaduh95
authored andcommitted
watch: strip watch flags from NODE_OPTIONS in child process
Signed-off-by: marcopiraccini <marco.piraccini@gmail.com> PR-URL: #62143 Fixes: #61740 Reviewed-By: Moshe Atlow <moshe@atlow.co.il> Reviewed-By: Paolo Insogna <paolo@cowtech.it> Reviewed-By: René <contact.9a5d6388@renegade334.me.uk> Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Trivikram Kamat <trivikr.dev@gmail.com>
1 parent b46b699 commit be7f637

5 files changed

Lines changed: 199 additions & 2 deletions

File tree

‎lib/internal/main/watch_mode.js‎

Lines changed: 34 additions & 1 deletion

‎lib/internal/options.js‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ const {
1616
getEmbedderOptions: getEmbedderOptionsFromBinding,
1717
getEnvOptionsInputType,
1818
getNamespaceOptionsInputType,
19+
parseNodeOptionsEnvVar,
1920
} = internalBinding('options');
2021

2122
let warnOnAllowUnauthorized = true;
@@ -222,5 +223,6 @@ module.exports = {
222223
getAllowUnauthorized,
223224
getEmbedderOptions,
224225
generateConfigJsonSchema,
226+
parseNodeOptionsEnvVar,
225227
refreshOptions,
226228
};

‎src/node_options.cc‎

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2337,6 +2337,29 @@ void GetOptionsAsFlags(const FunctionCallbackInfo<Value>& args) {
23372337
args.GetReturnValue().Set(result);
23382338
}
23392339

2340+
void ParseNodeOptionsEnvVarBinding(const FunctionCallbackInfo<Value>& args) {
2341+
Isolate* isolate = args.GetIsolate();
2342+
Local<Context> context = isolate->GetCurrentContext();
2343+
2344+
Utf8Value node_options(isolate, args[0]);
2345+
std::string options_str(*node_options, node_options.length());
2346+
2347+
std::vector<std::string> errors;
2348+
std::vector<std::string> result =
2349+
ParseNodeOptionsEnvVar(options_str, &errors);
2350+
2351+
if (!errors.empty()) {
2352+
Environment* env = Environment::GetCurrent(context);
2353+
env->ThrowError(errors[0].c_str());
2354+
return;
2355+
}
2356+
2357+
Local<Value> v8_result;
2358+
if (ToV8Value(context, result).ToLocal(&v8_result)) {
2359+
args.GetReturnValue().Set(v8_result);
2360+
}
2361+
}
2362+
23402363
void Initialize(Local<Object> target,
23412364
Local<Value> unused,
23422365
Local<Context> context,
@@ -2357,6 +2380,8 @@ void Initialize(Local<Object> target,
23572380
target,
23582381
"getNamespaceOptionsInputType",
23592382
GetNamespaceOptionsInputType);
2383+
SetMethodNoSideEffect(
2384+
context, target, "parseNodeOptionsEnvVar", ParseNodeOptionsEnvVarBinding);
23602385
Local<Object> env_settings = Object::New(isolate);
23612386
NODE_DEFINE_CONSTANT(env_settings, kAllowedInEnvvar);
23622387
NODE_DEFINE_CONSTANT(env_settings, kDisallowedInEnvvar);
@@ -2385,6 +2410,7 @@ void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
23852410
registry->Register(GetEmbedderOptions);
23862411
registry->Register(GetEnvOptionsInputType);
23872412
registry->Register(GetNamespaceOptionsInputType);
2413+
registry->Register(ParseNodeOptionsEnvVarBinding);
23882414
}
23892415
} // namespace options_parser
23902416

‎test/parallel/test-options-binding.js‎

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
const common = require('../common');
55
const assert = require('assert');
6-
const { getOptionValue } = require('internal/options');
6+
const { getOptionValue, parseNodeOptionsEnvVar } = require('internal/options');
77

88
Map.prototype.get =
99
common.mustNotCall('`getOptionValue` must not call user-mutable method');
@@ -14,3 +14,30 @@ assert.strictEqual(getOptionValue('--nonexistent-option'), undefined);
1414

1515
// Make the test common global leak test happy.
1616
delete Object.prototype['--nonexistent-option'];
17+
18+
// parseNodeOptionsEnvVar tokenizes a NODE_OPTIONS-style string.
19+
assert.deepStrictEqual(
20+
parseNodeOptionsEnvVar('--max-old-space-size=4096 --no-warnings'),
21+
['--max-old-space-size=4096', '--no-warnings']
22+
);
23+
24+
// Quoted strings are unquoted during parsing.
25+
assert.deepStrictEqual(
26+
parseNodeOptionsEnvVar('--require "file with spaces.js"'),
27+
['--require', 'file with spaces.js']
28+
);
29+
30+
// Empty string returns an empty array.
31+
assert.deepStrictEqual(parseNodeOptionsEnvVar(''), []);
32+
33+
// Throws on unterminated string.
34+
assert.throws(
35+
() => parseNodeOptionsEnvVar('--require "unterminated'),
36+
{ name: 'Error', message: /unterminated string/ }
37+
);
38+
39+
// Throws on invalid escape at end of string.
40+
assert.throws(
41+
() => parseNodeOptionsEnvVar('--require "foo\\'),
42+
{ name: 'Error', message: /invalid escape/ }
43+
);

‎test/sequential/test-watch-mode.mjs‎

Lines changed: 109 additions & 0 deletions

0 commit comments

Comments
 (0)