cli: ensure --run has proper pwd · nodejs/node@048a1ab · GitHub
Skip to content

Commit 048a1ab

Browse files
anonrigtargos
authored andcommitted
cli: ensure --run has proper pwd
PR-URL: #54949 Refs: #53600 Reviewed-By: Matthew Aitken <maitken033380023@gmail.com> Reviewed-By: Vinícius Lourenço Claro Cardoso <contact@viniciusl.com.br>
1 parent 90cce6e commit 048a1ab

9 files changed

Lines changed: 180 additions & 36 deletions

File tree

doc/api/cli.md

Lines changed: 2 additions & 0 deletions

src/node_task_runner.cc

Lines changed: 55 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ std::string EscapeShell(const std::string_view input) {
149149
#endif
150150
}
151151

152-
static const std::string_view forbidden_characters =
152+
static constexpr std::string_view forbidden_characters =
153153
"[\t\n\r \"#$&'()*;<>?\\\\`|~]";
154154

155155
// Check if input contains any forbidden characters
@@ -191,7 +191,7 @@ std::string EscapeShell(const std::string_view input) {
191191
void ProcessRunner::ExitCallback(uv_process_t* handle,
192192
int64_t exit_status,
193193
int term_signal) {
194-
auto self = reinterpret_cast<ProcessRunner*>(handle->data);
194+
const auto self = static_cast<ProcessRunner*>(handle->data);
195195
uv_close(reinterpret_cast<uv_handle_t*>(handle), nullptr);
196196
self->OnExit(exit_status, term_signal);
197197
}
@@ -205,6 +205,9 @@ void ProcessRunner::OnExit(int64_t exit_status, int term_signal) {
205205
}
206206

207207
void ProcessRunner::Run() {
208+
// keeps the string alive until destructor
209+
cwd = package_json_path_.parent_path().string();
210+
options_.cwd = cwd.c_str();
208211
if (int r = uv_spawn(loop_, &process_, &options_)) {
209212
fprintf(stderr, "Error: %s\n", uv_strerror(r));
210213
}
@@ -246,14 +249,16 @@ FindPackageJson(const std::filesystem::path& cwd) {
246249
return {{package_json_path, raw_content, path_env_var}};
247250
}
248251

249-
void RunTask(std::shared_ptr<InitializationResultImpl> result,
252+
void RunTask(const std::shared_ptr<InitializationResultImpl>& result,
250253
std::string_view command_id,
251254
const std::vector<std::string_view>& positional_args) {
252255
auto cwd = std::filesystem::current_path();
253256
auto package_json = FindPackageJson(cwd);
254257

255258
if (!package_json.has_value()) {
256-
fprintf(stderr, "Can't read package.json\n");
259+
fprintf(stderr,
260+
"Can't find package.json for directory %s\n",
261+
cwd.string().c_str());
257262
result->exit_code_ = ExitCode::kGenericUserError;
258263
return;
259264
}
@@ -267,46 +272,67 @@ void RunTask(std::shared_ptr<InitializationResultImpl> result,
267272
simdjson::ondemand::parser json_parser;
268273
simdjson::ondemand::document document;
269274
simdjson::ondemand::object main_object;
270-
simdjson::error_code error = json_parser.iterate(raw_json).get(document);
271275

276+
if (json_parser.iterate(raw_json).get(document)) {
277+
fprintf(stderr, "Can't parse %s\n", path.string().c_str());
278+
result->exit_code_ = ExitCode::kGenericUserError;
279+
return;
280+
}
272281
// If document is not an object, throw an error.
273-
if (error || document.get_object().get(main_object)) {
274-
fprintf(stderr, "Can't parse package.json\n");
282+
if (auto root_error = document.get_object().get(main_object)) {
283+
if (root_error == simdjson::error_code::INCORRECT_TYPE) {
284+
fprintf(stderr,
285+
"Root value unexpected not an object for %s\n\n",
286+
path.string().c_str());
287+
} else {
288+
fprintf(stderr, "Can't parse %s\n", path.string().c_str());
289+
}
275290
result->exit_code_ = ExitCode::kGenericUserError;
276291
return;
277292
}
278293

279294
// If package_json object doesn't have "scripts" field, throw an error.
280295
simdjson::ondemand::object scripts_object;
281296
if (main_object["scripts"].get_object().get(scripts_object)) {
282-
fprintf(stderr, "Can't find \"scripts\" field in package.json\n");
297+
fprintf(
298+
stderr, "Can't find \"scripts\" field in %s\n", path.string().c_str());
283299
result->exit_code_ = ExitCode::kGenericUserError;
284300
return;
285301
}
286302

287303
// If the command_id is not found in the scripts object, throw an error.
288304
std::string_view command;
289-
if (scripts_object[command_id].get_string().get(command)) {
290-
fprintf(stderr,
291-
"Missing script: \"%.*s\"\n\n",
292-
static_cast<int>(command_id.size()),
293-
command_id.data());
294-
fprintf(stderr, "Available scripts are:\n");
295-
296-
// Reset the object to iterate over it again
297-
scripts_object.reset();
298-
simdjson::ondemand::value value;
299-
for (auto field : scripts_object) {
300-
std::string_view key_str;
301-
std::string_view value_str;
302-
if (!field.unescaped_key().get(key_str) && !field.value().get(value) &&
303-
!value.get_string().get(value_str)) {
304-
fprintf(stderr,
305-
" %.*s: %.*s\n",
306-
static_cast<int>(key_str.size()),
307-
key_str.data(),
308-
static_cast<int>(value_str.size()),
309-
value_str.data());
305+
if (auto command_error =
306+
scripts_object[command_id].get_string().get(command)) {
307+
if (command_error == simdjson::error_code::INCORRECT_TYPE) {
308+
fprintf(stderr,
309+
"Script \"%.*s\" is unexpectedly not a string for %s\n\n",
310+
static_cast<int>(command_id.size()),
311+
command_id.data(),
312+
path.string().c_str());
313+
} else {
314+
fprintf(stderr,
315+
"Missing script: \"%.*s\" for %s\n\n",
316+
static_cast<int>(command_id.size()),
317+
command_id.data(),
318+
path.string().c_str());
319+
fprintf(stderr, "Available scripts are:\n");
320+
321+
// Reset the object to iterate over it again
322+
scripts_object.reset();
323+
simdjson::ondemand::value value;
324+
for (auto field : scripts_object) {
325+
std::string_view key_str;
326+
std::string_view value_str;
327+
if (!field.unescaped_key().get(key_str) && !field.value().get(value) &&
328+
!value.get_string().get(value_str)) {
329+
fprintf(stderr,
330+
" %.*s: %.*s\n",
331+
static_cast<int>(key_str.size()),
332+
key_str.data(),
333+
static_cast<int>(value_str.size()),
334+
value_str.data());
335+
}
310336
}
311337
}
312338
result->exit_code_ = ExitCode::kGenericUserError;

src/node_task_runner.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ class ProcessRunner {
4444
std::vector<std::string> env_vars_{};
4545
std::unique_ptr<char* []> env {}; // memory for options_.env
4646
std::unique_ptr<char* []> arg {}; // memory for options_.args
47+
std::string cwd;
4748

4849
// OnExit is the callback function that is called when the process exits.
4950
void OnExit(int64_t exit_status, int term_signal);
@@ -78,7 +79,7 @@ class ProcessRunner {
7879
std::optional<std::tuple<std::filesystem::path, std::string, std::string>>
7980
FindPackageJson(const std::filesystem::path& cwd);
8081

81-
void RunTask(std::shared_ptr<InitializationResultImpl> result,
82+
void RunTask(const std::shared_ptr<InitializationResultImpl>& result,
8283
std::string_view command_id,
8384
const PositionalArgs& positional_args);
8485
PositionalArgs GetPositionalArgs(const std::vector<std::string>& args);
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"scripts": {},
3+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"scripts": {
3+
"array": [],
4+
"boolean": true,
5+
"null": null,
6+
"number": 1.0,
7+
"object": {}
8+
}
9+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{}

test/fixtures/run-script/package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
"path-env": "path-env",
1111
"path-env-windows": "path-env.bat",
1212
"special-env-variables": "special-env-variables",
13-
"special-env-variables-windows": "special-env-variables.bat"
13+
"special-env-variables-windows": "special-env-variables.bat",
14+
"pwd": "pwd",
15+
"pwd-windows": "cd"
1416
}
1517
}

test/message/node_run_non_existent.out

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
Missing script: "non-existent-command"
1+
Missing script: "non-existent-command" for *
22

33
Available scripts are:
44
test: echo "Error: no test specified" && exit 1
@@ -12,3 +12,5 @@ Available scripts are:
1212
path-env-windows: path-env.bat
1313
special-env-variables: special-env-variables
1414
special-env-variables-windows: special-env-variables.bat
15+
pwd: pwd
16+
pwd-windows: cd

test/parallel/test-node-run.js

Lines changed: 102 additions & 4 deletions

0 commit comments

Comments
 (0)