1
1
#include " node_task_runner.h"
2
2
#include " util.h"
3
3
4
- #include < filesystem>
5
4
#include < regex> // NOLINT(build/c++11)
6
5
7
6
namespace node ::task_runner {
8
7
9
8
#ifdef _WIN32
10
- static constexpr const char * bin_path = " \\ node_modules \\ .bin " ;
9
+ static constexpr const char * env_var_separator = " ; " ;
11
10
#else
12
- static constexpr const char * bin_path = " /node_modules/.bin " ;
11
+ static constexpr const char * env_var_separator = " : " ;
13
12
#endif // _WIN32
14
13
15
14
ProcessRunner::ProcessRunner (std::shared_ptr<InitializationResultImpl> result,
16
- std::string_view package_json_path,
15
+ const std::filesystem::path& package_json_path,
17
16
std::string_view script_name,
18
17
std::string_view command,
18
+ std::string_view path_env_var,
19
19
const PositionalArgs& positional_args) {
20
20
memset (&options_, 0 , sizeof (uv_process_options_t ));
21
21
22
- // Get the current working directory.
23
- char cwd[PATH_MAX_BYTES];
24
- size_t cwd_size = PATH_MAX_BYTES;
25
- CHECK_EQ (uv_cwd (cwd, &cwd_size), 0 );
26
- CHECK_GT (cwd_size, 0 );
27
-
28
- #ifdef _WIN32
29
- std::string current_bin_path = cwd + std::string (bin_path) + " ;" ;
30
- #else
31
- std::string current_bin_path = cwd + std::string (bin_path) + " :" ;
32
- #endif // _WIN32
22
+ package_json_path_ = std::filesystem::path (package_json_path);
23
+ script_name_ = std::string (script_name);
24
+ path_env_var_ = path_env_var;
33
25
34
26
// Inherit stdin, stdout, and stderr from the parent process.
35
27
options_.stdio_count = 3 ;
@@ -54,10 +46,7 @@ ProcessRunner::ProcessRunner(std::shared_ptr<InitializationResultImpl> result,
54
46
// callback.
55
47
process_.data = this ;
56
48
57
- SetEnvironmentVariables (current_bin_path,
58
- std::string_view (cwd, cwd_size),
59
- package_json_path,
60
- script_name);
49
+ SetEnvironmentVariables ();
61
50
62
51
std::string command_str (command);
63
52
@@ -75,10 +64,7 @@ ProcessRunner::ProcessRunner(std::shared_ptr<InitializationResultImpl> result,
75
64
#ifdef _WIN32
76
65
// We check whether file_ ends with cmd.exe in a case-insensitive manner.
77
66
// C++20 provides ends_with, but we roll our own for compatibility.
78
- const char * cmdexe = " cmd.exe" ;
79
- if (file_.size () >= strlen (cmdexe) &&
80
- StringEqualNoCase (cmdexe,
81
- file_.c_str () + file_.size () - strlen (cmdexe))) {
67
+ if (file_.ends_with (" cmd.exe" )) {
82
68
// If the file is cmd.exe, use the following command line arguments:
83
69
// "/c" Carries out the command and exit.
84
70
// "/d" Disables execution of AutoRun commands.
@@ -106,10 +92,7 @@ ProcessRunner::ProcessRunner(std::shared_ptr<InitializationResultImpl> result,
106
92
options_.args [argc] = nullptr ;
107
93
}
108
94
109
- void ProcessRunner::SetEnvironmentVariables (const std::string& current_bin_path,
110
- std::string_view cwd,
111
- std::string_view package_json_path,
112
- std::string_view script_name) {
95
+ void ProcessRunner::SetEnvironmentVariables () {
113
96
uv_env_item_t * env_items;
114
97
int env_count;
115
98
CHECK_EQ (0 , uv_os_environ (&env_items, &env_count));
@@ -130,28 +113,21 @@ void ProcessRunner::SetEnvironmentVariables(const std::string& current_bin_path,
130
113
#endif // _WIN32
131
114
132
115
if (StringEqualNoCase (name.c_str (), " path" )) {
133
- // Add bin_path to the beginning of the PATH
134
- value = current_bin_path + value;
116
+ // Add path env variable to the beginning of the PATH
117
+ value = path_env_var_ + value;
135
118
}
136
119
env_vars_.push_back (name + " =" + value);
137
120
}
138
121
uv_os_free_environ (env_items, env_count);
139
122
140
123
// Add NODE_RUN_SCRIPT_NAME environment variable to the environment
141
124
// to indicate which script is being run.
142
- env_vars_.push_back (" NODE_RUN_SCRIPT_NAME=" + std::string (script_name) );
125
+ env_vars_.push_back (" NODE_RUN_SCRIPT_NAME=" + script_name_ );
143
126
144
127
// Add NODE_RUN_PACKAGE_JSON_PATH environment variable to the environment to
145
128
// indicate which package.json is being processed.
146
- if (std::filesystem::path (package_json_path).is_absolute ()) {
147
- // TODO(anonrig): Traverse up the directory tree until we find a
148
- // package.json
149
- env_vars_.push_back (" NODE_RUN_PACKAGE_JSON_PATH=" +
150
- std::string (package_json_path));
151
- } else {
152
- auto path = std::filesystem::path (cwd) / std::string (package_json_path);
153
- env_vars_.push_back (" NODE_RUN_PACKAGE_JSON_PATH=" + path.string ());
154
- }
129
+ env_vars_.push_back (" NODE_RUN_PACKAGE_JSON_PATH=" +
130
+ package_json_path_.string ());
155
131
156
132
env = std::unique_ptr<char *[]>(new char *[env_vars_.size () + 1 ]);
157
133
options_.env = env.get ();
@@ -240,19 +216,58 @@ void ProcessRunner::Run() {
240
216
uv_run (loop_, UV_RUN_DEFAULT);
241
217
}
242
218
219
+ std::optional<std::tuple<std::filesystem::path, std::string, std::string>>
220
+ FindPackageJson (const std::filesystem::path& cwd) {
221
+ auto package_json_path = cwd / " package.json" ;
222
+ std::string raw_content;
223
+ std::string path_env_var;
224
+ auto root_path = cwd.root_path ();
225
+
226
+ for (auto directory_path = cwd;
227
+ !std::filesystem::equivalent (root_path, directory_path);
228
+ directory_path = directory_path.parent_path ()) {
229
+ // Append "path/node_modules/.bin" to the env var, if it is a directory.
230
+ auto node_modules_bin = directory_path / " node_modules/.bin" ;
231
+ if (std::filesystem::is_directory (node_modules_bin)) {
232
+ path_env_var += node_modules_bin.string () + env_var_separator;
233
+ }
234
+
235
+ if (raw_content.empty ()) {
236
+ package_json_path = directory_path / " package.json" ;
237
+ // No need to exclude BOM since simdjson will skip it.
238
+ USE (ReadFileSync (&raw_content, package_json_path.c_str ()) > 0 );
239
+ }
240
+ }
241
+
242
+ // This means that there is no package.json until the root directory.
243
+ // In this case, we just return nullopt, which will terminate the process..
244
+ if (raw_content.empty ()) {
245
+ return std::nullopt;
246
+ }
247
+
248
+ return std::tuple (package_json_path, raw_content, path_env_var);
249
+ }
250
+
243
251
void RunTask (std::shared_ptr<InitializationResultImpl> result,
244
252
std::string_view command_id,
245
253
const std::vector<std::string_view>& positional_args) {
246
- std::string_view path = " package.json " ;
247
- std::string raw_json ;
254
+ auto cwd = std::filesystem::current_path () ;
255
+ auto package_json = FindPackageJson (cwd) ;
248
256
249
- // No need to exclude BOM since simdjson will skip it.
250
- if (ReadFileSync (&raw_json, path.data ()) < 0 ) {
257
+ if (!package_json.has_value ()) {
251
258
fprintf (stderr, " Can't read package.json\n " );
252
259
result->exit_code_ = ExitCode::kGenericUserError ;
253
260
return ;
254
261
}
255
262
263
+ // Path to the package.json file.
264
+ auto path = std::get<0 >(*package_json);
265
+ // Raw content of the package.json file.
266
+ auto raw_json = std::get<1 >(*package_json);
267
+ // This represents the `PATH` environment variable.
268
+ // It always ends with ";" or ":" depending on the platform.
269
+ auto path_env_var = std::get<2 >(*package_json);
270
+
256
271
simdjson::ondemand::parser json_parser;
257
272
simdjson::ondemand::document document;
258
273
simdjson::ondemand::object main_object;
@@ -302,8 +317,8 @@ void RunTask(std::shared_ptr<InitializationResultImpl> result,
302
317
return ;
303
318
}
304
319
305
- auto runner =
306
- ProcessRunner ( result, path, command_id, command, positional_args);
320
+ auto runner = ProcessRunner (
321
+ result, path, command_id, command, path_env_var , positional_args);
307
322
runner.Run ();
308
323
}
309
324
@@ -317,10 +332,11 @@ PositionalArgs GetPositionalArgs(const std::vector<std::string>& args) {
317
332
if (auto dash_dash = std::find (args.begin (), args.end (), " --" );
318
333
dash_dash != args.end ()) {
319
334
PositionalArgs positional_args{};
335
+ positional_args.reserve (args.size () - (dash_dash - args.begin ()));
320
336
for (auto it = dash_dash + 1 ; it != args.end (); ++it) {
321
337
// SAFETY: The following code is safe because the lifetime of the
322
338
// arguments is guaranteed to be valid until the end of the task runner.
323
- positional_args.push_back ( std::string_view ( it->c_str (), it->size () ));
339
+ positional_args.emplace_back ( it->c_str (), it->size ());
324
340
}
325
341
return positional_args;
326
342
}
0 commit comments