Skip to content

Commit 9bf23ad

Browse files
committed
src: merge env-file and env vars of NODE_OPTIONS
1 parent 413c16e commit 9bf23ad

File tree

7 files changed

+58
-24
lines changed

7 files changed

+58
-24
lines changed

src/node.cc

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -848,19 +848,23 @@ static ExitCode InitializeNodeWithArgsInternal(
848848
std::string path = cwd + kPathSeparator + file_path.value();
849849
CHECK(!per_process::v8_initialized);
850850
per_process::dotenv_file.ParsePath(path);
851-
per_process::dotenv_file.AssignNodeOptionsIfAvailable(&node_options);
852851
}
853852

854853
#if !defined(NODE_WITHOUT_NODE_OPTIONS)
855854
if (!(flags & ProcessInitializationFlags::kDisableNodeOptionsEnv)) {
856-
// NODE_OPTIONS environment variable is preferred over the file one.
857-
if (credentials::SafeGetenv("NODE_OPTIONS", &node_options) ||
858-
!node_options.empty()) {
859-
std::vector<std::string> env_argv =
860-
ParseNodeOptionsEnvVar(node_options, errors);
855+
std::string node_options;
856+
std::vector<std::string> env_argv;
861857

862-
if (!errors->empty()) return ExitCode::kInvalidCommandLineArgument;
858+
// This behaves like a noop if dotenv does not exist.
859+
per_process::dotenv_file.AssignNodeOptionsIfAvailable(&env_argv, errors);
863860

861+
if (credentials::SafeGetenv("NODE_OPTIONS", &node_options)) {
862+
ParseNodeOptionsEnvVar(node_options, &env_argv, errors);
863+
}
864+
865+
if (!errors->empty()) return ExitCode::kInvalidCommandLineArgument;
866+
867+
if (!env_argv.empty()) {
864868
// [0] is expected to be the program name, fill it in from the real argv.
865869
env_argv.insert(env_argv.begin(), argv->at(0));
866870

src/node_dotenv.cc

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#include "node_dotenv.h"
22
#include "env-inl.h"
33
#include "node_file.h"
4+
#include "node_options.h"
45
#include "uv.h"
56

67
namespace node {
@@ -101,12 +102,15 @@ void Dotenv::ParsePath(const std::string_view path) {
101102
}
102103
}
103104

104-
void Dotenv::AssignNodeOptionsIfAvailable(std::string* node_options) {
105+
void Dotenv::AssignNodeOptionsIfAvailable(std::vector<std::string>* env_argv,
106+
std::vector<std::string>* errors) {
105107
auto match = store_.find("NODE_OPTIONS");
106108

107-
if (match != store_.end()) {
108-
*node_options = match->second;
109+
if (match == store_.end()) {
110+
return;
109111
}
112+
113+
ParseNodeOptionsEnvVar(match->second, env_argv, errors);
110114
}
111115

112116
void Dotenv::ParseLine(const std::string_view line) {

src/node_dotenv.h

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

88
#include <map>
99
#include <optional>
10+
#include <vector>
1011

1112
namespace node {
1213

@@ -20,7 +21,8 @@ class Dotenv {
2021
~Dotenv() = default;
2122

2223
void ParsePath(const std::string_view path);
23-
void AssignNodeOptionsIfAvailable(std::string* node_options);
24+
void AssignNodeOptionsIfAvailable(std::vector<std::string>* node_options,
25+
std::vector<std::string>* errors);
2426
void SetEnvironment(Environment* env);
2527

2628
static std::optional<std::string> GetPathFromArgs(

src/node_options.cc

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1316,21 +1316,20 @@ void HandleEnvOptions(std::shared_ptr<EnvironmentOptions> env_options,
13161316
env_options->redirect_warnings = opt_getter("NODE_REDIRECT_WARNINGS");
13171317
}
13181318

1319-
std::vector<std::string> ParseNodeOptionsEnvVar(
1320-
const std::string& node_options, std::vector<std::string>* errors) {
1321-
std::vector<std::string> env_argv;
1322-
1319+
void ParseNodeOptionsEnvVar(const std::string& node_options,
1320+
std::vector<std::string>* env_argv,
1321+
std::vector<std::string>* errors) {
13231322
bool is_in_string = false;
13241323
bool will_start_new_arg = true;
1325-
for (std::string::size_type index = 0; index < node_options.size(); ++index) {
1324+
for (size_t index = 0; index < node_options.size(); ++index) {
13261325
char c = node_options.at(index);
13271326

13281327
// Backslashes escape the following character
13291328
if (c == '\\' && is_in_string) {
13301329
if (index + 1 == node_options.size()) {
13311330
errors->push_back("invalid value for NODE_OPTIONS "
13321331
"(invalid escape)\n");
1333-
return env_argv;
1332+
break;
13341333
} else {
13351334
c = node_options.at(++index);
13361335
}
@@ -1343,18 +1342,17 @@ std::vector<std::string> ParseNodeOptionsEnvVar(
13431342
}
13441343

13451344
if (will_start_new_arg) {
1346-
env_argv.emplace_back(std::string(1, c));
1345+
env_argv->emplace_back(std::string(1, c));
13471346
will_start_new_arg = false;
13481347
} else {
1349-
env_argv.back() += c;
1348+
env_argv->back() += c;
13501349
}
13511350
}
13521351

13531352
if (is_in_string) {
13541353
errors->push_back("invalid value for NODE_OPTIONS "
13551354
"(unterminated string)\n");
13561355
}
1357-
return env_argv;
13581356
}
13591357
} // namespace node
13601358

src/node_options.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -529,8 +529,9 @@ void HandleEnvOptions(std::shared_ptr<EnvironmentOptions> env_options);
529529
void HandleEnvOptions(std::shared_ptr<EnvironmentOptions> env_options,
530530
std::function<std::string(const char*)> opt_getter);
531531

532-
std::vector<std::string> ParseNodeOptionsEnvVar(
533-
const std::string& node_options, std::vector<std::string>* errors);
532+
void ParseNodeOptionsEnvVar(const std::string& node_options,
533+
std::vector<std::string>* env_argv,
534+
std::vector<std::string>* errors);
534535
} // namespace node
535536

536537
#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS

src/node_worker.cc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -537,8 +537,8 @@ void Worker::New(const FunctionCallbackInfo<Value>& args) {
537537
if (maybe_node_opts.ToLocal(&node_opts)) {
538538
std::string node_options(*String::Utf8Value(isolate, node_opts));
539539
std::vector<std::string> errors{};
540-
std::vector<std::string> env_argv =
541-
ParseNodeOptionsEnvVar(node_options, &errors);
540+
std::vector<std::string> env_argv{};
541+
ParseNodeOptionsEnvVar(node_options, &env_argv, &errors);
542542
// [0] is expected to be the program name, add dummy string.
543543
env_argv.insert(env_argv.begin(), "");
544544
std::vector<std::string> invalid_args{};

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

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,18 @@
22

33
const common = require('../common');
44
const assert = require('node:assert');
5+
const path = require('node:path');
6+
const fs = require('node:fs');
57
const { describe, it } = require('node:test');
68

79
if (process.config.variables.node_without_node_options) {
810
common.skip('missing NODE_OPTIONS support');
911
}
1012

13+
const tmpdir = require('../common/tmpdir');
14+
const testDir = tmpdir.path;
15+
tmpdir.refresh();
16+
1117
const relativePath = '../fixtures/dotenv/node-options.env';
1218

1319
describe('.env supports NODE_OPTIONS', () => {
@@ -70,4 +76,23 @@ describe('.env supports NODE_OPTIONS', () => {
7076
assert.strictEqual(child.code, 0);
7177
});
7278

79+
it('should merge env variables with .env file', async () => {
80+
const filePath = path.join(testDir, 'should-write.txt');
81+
const code = `
82+
const path = require('path');
83+
require('fs').writeFileSync('${filePath}', 'hello', 'utf-8')
84+
`.trim();
85+
const child = await common.spawnPromisified(
86+
process.execPath,
87+
[ `--env-file=${relativePath}`, '--eval', code ],
88+
{ cwd: __dirname, env: { ...process.env, NODE_OPTIONS: '--allow-fs-write=*' } },
89+
);
90+
assert.strictEqual(child.stderr, '');
91+
assert.strictEqual(child.stdout, '');
92+
assert.strictEqual(child.code, 0);
93+
assert(fs.existsSync(filePath));
94+
fs.unlinkSync(filePath);
95+
assert(!fs.existsSync(filePath));
96+
});
97+
7398
});

0 commit comments

Comments
 (0)