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,
19
- const PositionalArgs& positional_args) {
18
+ std::string_view path_env_var,
19
+ const PositionalArgs& positional_args)
20
+ : init_result(std::move(result)),
21
+ package_json_path_ (package_json_path),
22
+ script_name_(script_name),
23
+ path_env_var_(path_env_var) {
20
24
memset (&options_, 0 , sizeof (uv_process_options_t ));
21
25
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
33
-
34
26
// Inherit stdin, stdout, and stderr from the parent process.
35
27
options_.stdio_count = 3 ;
36
28
child_stdio[0 ].flags = UV_INHERIT_FD;
@@ -46,18 +38,13 @@ ProcessRunner::ProcessRunner(std::shared_ptr<InitializationResultImpl> result,
46
38
options_.flags |= UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS;
47
39
#endif
48
40
49
- init_result = std::move (result);
50
-
51
41
// Set the process handle data to this class instance.
52
42
// This is used to access the class instance from the OnExit callback.
53
43
// It is required because libuv doesn't allow passing lambda functions as a
54
44
// callback.
55
45
process_.data = this ;
56
46
57
- SetEnvironmentVariables (current_bin_path,
58
- std::string_view (cwd, cwd_size),
59
- package_json_path,
60
- script_name);
47
+ SetEnvironmentVariables ();
61
48
62
49
std::string command_str (command);
63
50
@@ -73,12 +60,7 @@ ProcessRunner::ProcessRunner(std::shared_ptr<InitializationResultImpl> result,
73
60
}
74
61
75
62
#ifdef _WIN32
76
- // We check whether file_ ends with cmd.exe in a case-insensitive manner.
77
- // 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))) {
63
+ if (file_.ends_with (" cmd.exe" )) {
82
64
// If the file is cmd.exe, use the following command line arguments:
83
65
// "/c" Carries out the command and exit.
84
66
// "/d" Disables execution of AutoRun commands.
@@ -106,10 +88,7 @@ ProcessRunner::ProcessRunner(std::shared_ptr<InitializationResultImpl> result,
106
88
options_.args [argc] = nullptr ;
107
89
}
108
90
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) {
91
+ void ProcessRunner::SetEnvironmentVariables () {
113
92
uv_env_item_t * env_items;
114
93
int env_count;
115
94
CHECK_EQ (0 , uv_os_environ (&env_items, &env_count));
@@ -130,28 +109,21 @@ void ProcessRunner::SetEnvironmentVariables(const std::string& current_bin_path,
130
109
#endif // _WIN32
131
110
132
111
if (StringEqualNoCase (name.c_str (), " path" )) {
133
- // Add bin_path to the beginning of the PATH
134
- value = current_bin_path + value;
112
+ // Add path env variable to the beginning of the PATH
113
+ value = path_env_var_ + value;
135
114
}
136
115
env_vars_.push_back (name + " =" + value);
137
116
}
138
117
uv_os_free_environ (env_items, env_count);
139
118
140
119
// Add NODE_RUN_SCRIPT_NAME environment variable to the environment
141
120
// to indicate which script is being run.
142
- env_vars_.push_back (" NODE_RUN_SCRIPT_NAME=" + std::string (script_name) );
121
+ env_vars_.push_back (" NODE_RUN_SCRIPT_NAME=" + script_name_ );
143
122
144
123
// Add NODE_RUN_PACKAGE_JSON_PATH environment variable to the environment to
145
124
// 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
- }
125
+ env_vars_.push_back (" NODE_RUN_PACKAGE_JSON_PATH=" +
126
+ package_json_path_.string ());
155
127
156
128
env = std::unique_ptr<char *[]>(new char *[env_vars_.size () + 1 ]);
157
129
options_.env = env.get ();
@@ -240,19 +212,58 @@ void ProcessRunner::Run() {
240
212
uv_run (loop_, UV_RUN_DEFAULT);
241
213
}
242
214
215
+ std::optional<std::tuple<std::filesystem::path, std::string, std::string>>
216
+ FindPackageJson (const std::filesystem::path& cwd) {
217
+ auto package_json_path = cwd / " package.json" ;
218
+ std::string raw_content;
219
+ std::string path_env_var;
220
+ auto root_path = cwd.root_path ();
221
+
222
+ for (auto directory_path = cwd;
223
+ !std::filesystem::equivalent (root_path, directory_path);
224
+ directory_path = directory_path.parent_path ()) {
225
+ // Append "path/node_modules/.bin" to the env var, if it is a directory.
226
+ auto node_modules_bin = directory_path / " node_modules" / " .bin" ;
227
+ if (std::filesystem::is_directory (node_modules_bin)) {
228
+ path_env_var += node_modules_bin.string () + env_var_separator;
229
+ }
230
+
231
+ if (raw_content.empty ()) {
232
+ package_json_path = directory_path / " package.json" ;
233
+ // This is required for Windows because std::filesystem::path::c_str()
234
+ // returns wchar_t* on Windows, and char* on other platforms.
235
+ std::string contents = package_json_path.string ();
236
+ USE (ReadFileSync (&raw_content, contents.c_str ()) > 0 );
237
+ }
238
+ }
239
+
240
+ // This means that there is no package.json until the root directory.
241
+ // In this case, we just return nullopt, which will terminate the process..
242
+ if (raw_content.empty ()) {
243
+ return std::nullopt;
244
+ }
245
+
246
+ return {{package_json_path, raw_content, path_env_var}};
247
+ }
248
+
243
249
void RunTask (std::shared_ptr<InitializationResultImpl> result,
244
250
std::string_view command_id,
245
251
const std::vector<std::string_view>& positional_args) {
246
- std::string_view path = " package.json " ;
247
- std::string raw_json ;
252
+ auto cwd = std::filesystem::current_path () ;
253
+ auto package_json = FindPackageJson (cwd) ;
248
254
249
- // No need to exclude BOM since simdjson will skip it.
250
- if (ReadFileSync (&raw_json, path.data ()) < 0 ) {
255
+ if (!package_json.has_value ()) {
251
256
fprintf (stderr, " Can't read package.json\n " );
252
257
result->exit_code_ = ExitCode::kGenericUserError ;
253
258
return ;
254
259
}
255
260
261
+ // - path: Path to the package.json file.
262
+ // - raw_json: Raw content of the package.json file.
263
+ // - path_env_var: This represents the `PATH` environment variable.
264
+ // It always ends with ";" or ":" depending on the platform.
265
+ auto [path, raw_json, path_env_var] = *package_json;
266
+
256
267
simdjson::ondemand::parser json_parser;
257
268
simdjson::ondemand::document document;
258
269
simdjson::ondemand::object main_object;
@@ -302,8 +313,8 @@ void RunTask(std::shared_ptr<InitializationResultImpl> result,
302
313
return ;
303
314
}
304
315
305
- auto runner =
306
- ProcessRunner ( result, path, command_id, command, positional_args);
316
+ auto runner = ProcessRunner (
317
+ result, path, command_id, command, path_env_var , positional_args);
307
318
runner.Run ();
308
319
}
309
320
@@ -317,10 +328,11 @@ PositionalArgs GetPositionalArgs(const std::vector<std::string>& args) {
317
328
if (auto dash_dash = std::find (args.begin (), args.end (), " --" );
318
329
dash_dash != args.end ()) {
319
330
PositionalArgs positional_args{};
331
+ positional_args.reserve (args.size () - (dash_dash - args.begin ()));
320
332
for (auto it = dash_dash + 1 ; it != args.end (); ++it) {
321
333
// SAFETY: The following code is safe because the lifetime of the
322
334
// 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 () ));
335
+ positional_args.emplace_back ( it->c_str (), it->size ());
324
336
}
325
337
return positional_args;
326
338
}
0 commit comments