mirror of
https://github.com/nodejs/node.git
synced 2025-08-15 13:48:44 +02:00
cli: implement node --run <script-in-package-json>
Co-authored-by: Daniel Lemire <daniel@lemire.me> PR-URL: https://github.com/nodejs/node/pull/52190 Reviewed-By: Daniel Lemire <daniel@lemire.me> Reviewed-By: Vinícius Lourenço Claro Cardoso <contact@viniciusl.com.br> Reviewed-By: Geoffrey Booth <webadmin@geoffreybooth.com> Reviewed-By: Moshe Atlow <moshe@atlow.co.il> Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Robert Nagy <ronagy@icloud.com> Reviewed-By: Paolo Insogna <paolo@cowtech.it> Reviewed-By: Tierney Cyren <hello@bnb.im> Reviewed-By: Chemi Atlow <chemi@atlow.co.il> Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: Ruy Adorno <ruy@vlt.sh>
This commit is contained in:
parent
ad86a12964
commit
128c60d906
20 changed files with 352 additions and 0 deletions
|
@ -1807,6 +1807,50 @@ Only CommonJS modules are supported.
|
||||||
Use [`--import`][] to preload an [ECMAScript module][].
|
Use [`--import`][] to preload an [ECMAScript module][].
|
||||||
Modules preloaded with `--require` will run before modules preloaded with `--import`.
|
Modules preloaded with `--require` will run before modules preloaded with `--import`.
|
||||||
|
|
||||||
|
### `--run`
|
||||||
|
|
||||||
|
<!-- YAML
|
||||||
|
added: REPLACEME
|
||||||
|
-->
|
||||||
|
|
||||||
|
> Stability: 1.1 - Active development
|
||||||
|
|
||||||
|
This runs a specified command from a package.json's `"scripts"` object.
|
||||||
|
If no `"command"` is provided, it will list the available scripts.
|
||||||
|
|
||||||
|
`--run` prepends `./node_modules/.bin`, relative to the current
|
||||||
|
working directory, to the `PATH` in order to execute the binaries from
|
||||||
|
dependencies.
|
||||||
|
|
||||||
|
For example, the following command will run the `test` script of
|
||||||
|
the `package.json` in the current folder:
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ node --run test
|
||||||
|
```
|
||||||
|
|
||||||
|
You can also pass arguments to the command. Any argument after `--` will
|
||||||
|
be appended to the script:
|
||||||
|
|
||||||
|
```console
|
||||||
|
$ node --run test -- --verbose
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Intentional limitations
|
||||||
|
|
||||||
|
`node --run` is not meant to match the behaviors of `npm run` or of the `run`
|
||||||
|
commands of other package managers. The Node.js implementation is intentionally
|
||||||
|
more limited, in order to focus on top performance for the most common use
|
||||||
|
cases.
|
||||||
|
Some features of other `run` implementations that are intentionally excluded
|
||||||
|
are:
|
||||||
|
|
||||||
|
* Searching for `package.json` files outside the current folder.
|
||||||
|
* Prepending the `.bin` or `node_modules/.bin` paths of folders outside the
|
||||||
|
current folder.
|
||||||
|
* Running `pre` or `post` scripts in addition to the specified script.
|
||||||
|
* Defining package manager-specific environment variables.
|
||||||
|
|
||||||
### `--secure-heap=n`
|
### `--secure-heap=n`
|
||||||
|
|
||||||
<!-- YAML
|
<!-- YAML
|
||||||
|
|
74
lib/internal/main/run.js
Normal file
74
lib/internal/main/run.js
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
'use strict';
|
||||||
|
/* eslint-disable node-core/prefer-primordials */
|
||||||
|
|
||||||
|
// There is no need to add primordials to this file.
|
||||||
|
// `run.js` is a script only executed when `node --run <script>` is called.
|
||||||
|
const {
|
||||||
|
prepareMainThreadExecution,
|
||||||
|
markBootstrapComplete,
|
||||||
|
} = require('internal/process/pre_execution');
|
||||||
|
const { getPackageJSONScripts } = internalBinding('modules');
|
||||||
|
const { execSync } = require('child_process');
|
||||||
|
const { resolve, delimiter } = require('path');
|
||||||
|
const { escapeShell } = require('internal/shell');
|
||||||
|
const { getOptionValue } = require('internal/options');
|
||||||
|
const { emitExperimentalWarning } = require('internal/util');
|
||||||
|
|
||||||
|
prepareMainThreadExecution(false, false);
|
||||||
|
markBootstrapComplete();
|
||||||
|
emitExperimentalWarning('Task runner');
|
||||||
|
|
||||||
|
// TODO(@anonrig): Search for all package.json's until root folder.
|
||||||
|
const json_string = getPackageJSONScripts();
|
||||||
|
|
||||||
|
// Check if package.json exists and is parseable
|
||||||
|
if (json_string === undefined) {
|
||||||
|
process.exitCode = 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const scripts = JSON.parse(json_string);
|
||||||
|
// Remove the first argument, which are the node binary.
|
||||||
|
const args = process.argv.slice(1);
|
||||||
|
const id = getOptionValue('--run');
|
||||||
|
let command = scripts[id];
|
||||||
|
|
||||||
|
if (!command) {
|
||||||
|
const { error } = require('internal/console/global');
|
||||||
|
|
||||||
|
error(`Missing script: "${id}"\n`);
|
||||||
|
|
||||||
|
const keys = Object.keys(scripts);
|
||||||
|
if (keys.length === 0) {
|
||||||
|
error('There are no scripts available in package.json');
|
||||||
|
} else {
|
||||||
|
error('Available scripts are:');
|
||||||
|
for (const script of keys) {
|
||||||
|
error(` ${script}: ${scripts[script]}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
process.exit(1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const env = process.env;
|
||||||
|
const cwd = process.cwd();
|
||||||
|
const binPath = resolve(cwd, 'node_modules/.bin');
|
||||||
|
|
||||||
|
// Filter all environment variables that contain the word "path"
|
||||||
|
const keys = Object.keys(env).filter((key) => /^path$/i.test(key));
|
||||||
|
const PATH = keys.map((key) => env[key]);
|
||||||
|
|
||||||
|
// Append only the current folder bin path to the PATH variable.
|
||||||
|
// TODO(@anonrig): Prepend the bin path of all parent folders.
|
||||||
|
const paths = [binPath, PATH].join(delimiter);
|
||||||
|
for (const key of keys) {
|
||||||
|
env[key] = paths;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there are any remaining arguments left, append them to the command.
|
||||||
|
// This is useful if you want to pass arguments to the script, such as
|
||||||
|
// `node --run linter -- --help` which runs `biome --check . --help`
|
||||||
|
if (args.length > 0) {
|
||||||
|
command += ' ' + escapeShell(args.map((arg) => arg.trim()).join(' '));
|
||||||
|
}
|
||||||
|
execSync(command, { stdio: 'inherit', env, shell: true });
|
37
lib/internal/shell.js
Normal file
37
lib/internal/shell.js
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
|
||||||
|
// There is no need to add primordials to this file.
|
||||||
|
// `shell.js` is a script only executed when `node run <script>` is called.
|
||||||
|
|
||||||
|
const forbiddenCharacters = /[\t\n\r "#$&'()*;<>?\\`|~]/;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Escapes a string to be used as a shell argument.
|
||||||
|
*
|
||||||
|
* Adapted from `promise-spawn` module available under ISC license.
|
||||||
|
* Ref: https://github.com/npm/promise-spawn/blob/16b36410f9b721dbe190141136432a418869734f/lib/escape.js
|
||||||
|
* @param {string} input
|
||||||
|
*/
|
||||||
|
function escapeShell(input) {
|
||||||
|
// If the input is an empty string, return a pair of quotes
|
||||||
|
if (!input.length) {
|
||||||
|
return '\'\'';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if input contains any forbidden characters
|
||||||
|
// If it doesn't, return the input as is.
|
||||||
|
if (!forbiddenCharacters.test(input)) {
|
||||||
|
return input;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Replace single quotes with '\'' and wrap the whole result in a fresh set of quotes
|
||||||
|
return `'${input.replace(/'/g, '\'\\\'\'')}'`
|
||||||
|
// If the input string already had single quotes around it, clean those up
|
||||||
|
.replace(/^(?:'')+(?!$)/, '')
|
||||||
|
.replace(/\\'''/g, '\\\'');
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
escapeShell,
|
||||||
|
};
|
|
@ -409,6 +409,10 @@ MaybeLocal<Value> StartExecution(Environment* env, StartExecutionCallback cb) {
|
||||||
return StartExecution(env, "internal/main/watch_mode");
|
return StartExecution(env, "internal/main/watch_mode");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!env->options()->run.empty()) {
|
||||||
|
return StartExecution(env, "internal/main/run");
|
||||||
|
}
|
||||||
|
|
||||||
if (!first_argv.empty() && first_argv != "-") {
|
if (!first_argv.empty() && first_argv != "-") {
|
||||||
return StartExecution(env, "internal/main/run_main_module");
|
return StartExecution(env, "internal/main/run_main_module");
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
#include "base_object-inl.h"
|
#include "base_object-inl.h"
|
||||||
#include "node_errors.h"
|
#include "node_errors.h"
|
||||||
#include "node_external_reference.h"
|
#include "node_external_reference.h"
|
||||||
|
#include "node_process-inl.h"
|
||||||
#include "node_url.h"
|
#include "node_url.h"
|
||||||
#include "permission/permission.h"
|
#include "permission/permission.h"
|
||||||
#include "permission/permission_base.h"
|
#include "permission/permission_base.h"
|
||||||
|
@ -219,6 +220,21 @@ const BindingData::PackageConfig* BindingData::GetPackageJSON(
|
||||||
if (field_value == "commonjs" || field_value == "module") {
|
if (field_value == "commonjs" || field_value == "module") {
|
||||||
package_config.type = field_value;
|
package_config.type = field_value;
|
||||||
}
|
}
|
||||||
|
} else if (key == "scripts") {
|
||||||
|
if (value.type().get(field_type)) {
|
||||||
|
return throw_invalid_package_config();
|
||||||
|
}
|
||||||
|
switch (field_type) {
|
||||||
|
case simdjson::ondemand::json_type::object: {
|
||||||
|
if (value.raw_json().get(field_value)) {
|
||||||
|
return throw_invalid_package_config();
|
||||||
|
}
|
||||||
|
package_config.scripts = field_value;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// package_config could be quite large, so we should move it instead of
|
// package_config could be quite large, so we should move it instead of
|
||||||
|
@ -344,6 +360,28 @@ void BindingData::GetNearestParentPackageJSONType(
|
||||||
args.GetReturnValue().Set(Array::New(realm->isolate(), values, 3));
|
args.GetReturnValue().Set(Array::New(realm->isolate(), values, 3));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BindingData::GetPackageJSONScripts(
|
||||||
|
const FunctionCallbackInfo<Value>& args) {
|
||||||
|
Realm* realm = Realm::GetCurrent(args);
|
||||||
|
std::string_view path = "package.json";
|
||||||
|
|
||||||
|
THROW_IF_INSUFFICIENT_PERMISSIONS(
|
||||||
|
realm->env(), permission::PermissionScope::kFileSystemRead, path);
|
||||||
|
|
||||||
|
auto package_json = GetPackageJSON(realm, path);
|
||||||
|
if (package_json == nullptr) {
|
||||||
|
printf("Can't read package.json\n");
|
||||||
|
return;
|
||||||
|
} else if (!package_json->scripts.has_value()) {
|
||||||
|
printf("Can't read package.json \"scripts\" object\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
args.GetReturnValue().Set(
|
||||||
|
ToV8Value(realm->context(), package_json->scripts.value())
|
||||||
|
.ToLocalChecked());
|
||||||
|
}
|
||||||
|
|
||||||
void BindingData::GetPackageScopeConfig(
|
void BindingData::GetPackageScopeConfig(
|
||||||
const FunctionCallbackInfo<Value>& args) {
|
const FunctionCallbackInfo<Value>& args) {
|
||||||
CHECK_GE(args.Length(), 1);
|
CHECK_GE(args.Length(), 1);
|
||||||
|
@ -424,6 +462,7 @@ void BindingData::CreatePerIsolateProperties(IsolateData* isolate_data,
|
||||||
"getNearestParentPackageJSON",
|
"getNearestParentPackageJSON",
|
||||||
GetNearestParentPackageJSON);
|
GetNearestParentPackageJSON);
|
||||||
SetMethod(isolate, target, "getPackageScopeConfig", GetPackageScopeConfig);
|
SetMethod(isolate, target, "getPackageScopeConfig", GetPackageScopeConfig);
|
||||||
|
SetMethod(isolate, target, "getPackageJSONScripts", GetPackageJSONScripts);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BindingData::CreatePerContextProperties(Local<Object> target,
|
void BindingData::CreatePerContextProperties(Local<Object> target,
|
||||||
|
@ -440,6 +479,7 @@ void BindingData::RegisterExternalReferences(
|
||||||
registry->Register(GetNearestParentPackageJSONType);
|
registry->Register(GetNearestParentPackageJSONType);
|
||||||
registry->Register(GetNearestParentPackageJSON);
|
registry->Register(GetNearestParentPackageJSON);
|
||||||
registry->Register(GetPackageScopeConfig);
|
registry->Register(GetPackageScopeConfig);
|
||||||
|
registry->Register(GetPackageJSONScripts);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace modules
|
} // namespace modules
|
||||||
|
|
|
@ -32,6 +32,7 @@ class BindingData : public SnapshotableObject {
|
||||||
std::string type = "none";
|
std::string type = "none";
|
||||||
std::optional<std::string> exports;
|
std::optional<std::string> exports;
|
||||||
std::optional<std::string> imports;
|
std::optional<std::string> imports;
|
||||||
|
std::optional<std::string> scripts;
|
||||||
std::string raw_json;
|
std::string raw_json;
|
||||||
|
|
||||||
v8::Local<v8::Array> Serialize(Realm* realm) const;
|
v8::Local<v8::Array> Serialize(Realm* realm) const;
|
||||||
|
@ -60,6 +61,8 @@ class BindingData : public SnapshotableObject {
|
||||||
const v8::FunctionCallbackInfo<v8::Value>& args);
|
const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||||
static void GetPackageScopeConfig(
|
static void GetPackageScopeConfig(
|
||||||
const v8::FunctionCallbackInfo<v8::Value>& args);
|
const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||||
|
static void GetPackageJSONScripts(
|
||||||
|
const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||||
|
|
||||||
static void CreatePerIsolateProperties(IsolateData* isolate_data,
|
static void CreatePerIsolateProperties(IsolateData* isolate_data,
|
||||||
v8::Local<v8::ObjectTemplate> ctor);
|
v8::Local<v8::ObjectTemplate> ctor);
|
||||||
|
|
|
@ -573,6 +573,9 @@ EnvironmentOptionsParser::EnvironmentOptionsParser() {
|
||||||
&EnvironmentOptions::prof_process);
|
&EnvironmentOptions::prof_process);
|
||||||
// Options after --prof-process are passed through to the prof processor.
|
// Options after --prof-process are passed through to the prof processor.
|
||||||
AddAlias("--prof-process", { "--prof-process", "--" });
|
AddAlias("--prof-process", { "--prof-process", "--" });
|
||||||
|
AddOption("--run",
|
||||||
|
"Run a script specified in package.json",
|
||||||
|
&EnvironmentOptions::run);
|
||||||
#if HAVE_INSPECTOR
|
#if HAVE_INSPECTOR
|
||||||
AddOption("--cpu-prof",
|
AddOption("--cpu-prof",
|
||||||
"Start the V8 CPU profiler on start up, and write the CPU profile "
|
"Start the V8 CPU profiler on start up, and write the CPU profile "
|
||||||
|
|
|
@ -161,6 +161,7 @@ class EnvironmentOptions : public Options {
|
||||||
bool heap_prof = false;
|
bool heap_prof = false;
|
||||||
#endif // HAVE_INSPECTOR
|
#endif // HAVE_INSPECTOR
|
||||||
std::string redirect_warnings;
|
std::string redirect_warnings;
|
||||||
|
std::string run;
|
||||||
std::string diagnostic_dir;
|
std::string diagnostic_dir;
|
||||||
std::string env_file;
|
std::string env_file;
|
||||||
bool has_env_file_string = false;
|
bool has_env_file_string = false;
|
||||||
|
|
1
test/fixtures/run-script/.env
vendored
Normal file
1
test/fixtures/run-script/.env
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
CUSTOM_ENV="hello world"
|
2
test/fixtures/run-script/node_modules/.bin/ada
generated
vendored
Executable file
2
test/fixtures/run-script/node_modules/.bin/ada
generated
vendored
Executable file
|
@ -0,0 +1,2 @@
|
||||||
|
#!/bin/sh
|
||||||
|
echo "06062023"
|
1
test/fixtures/run-script/node_modules/.bin/ada.bat
generated
vendored
Executable file
1
test/fixtures/run-script/node_modules/.bin/ada.bat
generated
vendored
Executable file
|
@ -0,0 +1 @@
|
||||||
|
@echo "06062023"
|
2
test/fixtures/run-script/node_modules/.bin/custom-env
generated
vendored
Executable file
2
test/fixtures/run-script/node_modules/.bin/custom-env
generated
vendored
Executable file
|
@ -0,0 +1,2 @@
|
||||||
|
#!/bin/bash
|
||||||
|
echo "$CUSTOM_ENV"
|
1
test/fixtures/run-script/node_modules/.bin/custom-env.bat
generated
vendored
Executable file
1
test/fixtures/run-script/node_modules/.bin/custom-env.bat
generated
vendored
Executable file
|
@ -0,0 +1 @@
|
||||||
|
echo %CUSTOM_ENV%
|
2
test/fixtures/run-script/node_modules/.bin/positional-args
generated
vendored
Executable file
2
test/fixtures/run-script/node_modules/.bin/positional-args
generated
vendored
Executable file
|
@ -0,0 +1,2 @@
|
||||||
|
#!/bin/bash
|
||||||
|
echo $@
|
2
test/fixtures/run-script/node_modules/.bin/positional-args.bat
generated
vendored
Executable file
2
test/fixtures/run-script/node_modules/.bin/positional-args.bat
generated
vendored
Executable file
|
@ -0,0 +1,2 @@
|
||||||
|
@shift
|
||||||
|
@echo %*
|
11
test/fixtures/run-script/package.json
vendored
Normal file
11
test/fixtures/run-script/package.json
vendored
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
{
|
||||||
|
"scripts": {
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1",
|
||||||
|
"ada": "ada",
|
||||||
|
"ada-windows": "ada.bat",
|
||||||
|
"positional-args": "positional-args",
|
||||||
|
"positional-args-windows": "positional-args.bat",
|
||||||
|
"custom-env": "custom-env",
|
||||||
|
"custom-env-windows": "custom-env.bat"
|
||||||
|
}
|
||||||
|
}
|
14
test/message/node_run_non_existent.js
Normal file
14
test/message/node_run_non_existent.js
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
require('../common');
|
||||||
|
const assert = require('node:assert').strict;
|
||||||
|
const childProcess = require('node:child_process');
|
||||||
|
const fixtures = require('../common/fixtures');
|
||||||
|
|
||||||
|
const child = childProcess.spawnSync(
|
||||||
|
process.execPath,
|
||||||
|
[ '--run', 'non-existent-command'],
|
||||||
|
{ cwd: fixtures.path('run-script'), encoding: 'utf8' },
|
||||||
|
);
|
||||||
|
assert.strictEqual(child.status, 1);
|
||||||
|
console.log(child.stderr);
|
10
test/message/node_run_non_existent.out
Normal file
10
test/message/node_run_non_existent.out
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
Missing script: "non-existent-command"
|
||||||
|
|
||||||
|
Available scripts are:
|
||||||
|
test: echo "Error: no test specified" && exit 1
|
||||||
|
ada: ada
|
||||||
|
ada-windows: ada.bat
|
||||||
|
positional-args: positional-args
|
||||||
|
positional-args-windows: positional-args.bat
|
||||||
|
custom-env: custom-env
|
||||||
|
custom-env-windows: custom-env.bat
|
99
test/parallel/test-node-run.js
Normal file
99
test/parallel/test-node-run.js
Normal file
|
@ -0,0 +1,99 @@
|
||||||
|
// Flags: --expose-internals
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const common = require('../common');
|
||||||
|
const { it, describe } = require('node:test');
|
||||||
|
const assert = require('node:assert');
|
||||||
|
|
||||||
|
const fixtures = require('../common/fixtures');
|
||||||
|
const envSuffix = common.isWindows ? '-windows' : '';
|
||||||
|
|
||||||
|
describe('node run [command]', () => {
|
||||||
|
it('should emit experimental warning', async () => {
|
||||||
|
const child = await common.spawnPromisified(
|
||||||
|
process.execPath,
|
||||||
|
[ '--run', 'test'],
|
||||||
|
{ cwd: __dirname },
|
||||||
|
);
|
||||||
|
assert.match(child.stderr, /ExperimentalWarning: Task runner is an experimental feature and might change at any time/);
|
||||||
|
assert.match(child.stdout, /Can't read package\.json/);
|
||||||
|
assert.strictEqual(child.code, 1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns error on non-existent file', async () => {
|
||||||
|
const child = await common.spawnPromisified(
|
||||||
|
process.execPath,
|
||||||
|
[ '--no-warnings', '--run', 'test'],
|
||||||
|
{ cwd: __dirname },
|
||||||
|
);
|
||||||
|
assert.match(child.stdout, /Can't read package\.json/);
|
||||||
|
assert.strictEqual(child.stderr, '');
|
||||||
|
assert.strictEqual(child.code, 1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('runs a valid command', async () => {
|
||||||
|
// Run a script that just log `no test specified`
|
||||||
|
const child = await common.spawnPromisified(
|
||||||
|
process.execPath,
|
||||||
|
[ '--run', 'test', '--no-warnings'],
|
||||||
|
{ cwd: fixtures.path('run-script') },
|
||||||
|
);
|
||||||
|
assert.match(child.stdout, /Error: no test specified/);
|
||||||
|
assert.strictEqual(child.code, 1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('adds node_modules/.bin to path', async () => {
|
||||||
|
const child = await common.spawnPromisified(
|
||||||
|
process.execPath,
|
||||||
|
[ '--no-warnings', '--run', `ada${envSuffix}`],
|
||||||
|
{ cwd: fixtures.path('run-script') },
|
||||||
|
);
|
||||||
|
assert.match(child.stdout, /06062023/);
|
||||||
|
assert.strictEqual(child.stderr, '');
|
||||||
|
assert.strictEqual(child.code, 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('appends positional arguments', async () => {
|
||||||
|
const child = await common.spawnPromisified(
|
||||||
|
process.execPath,
|
||||||
|
[ '--no-warnings', '--run', `positional-args${envSuffix}`, '--', '--help "hello world test"'],
|
||||||
|
{ cwd: fixtures.path('run-script') },
|
||||||
|
);
|
||||||
|
assert.match(child.stdout, /--help "hello world test"/);
|
||||||
|
assert.strictEqual(child.stderr, '');
|
||||||
|
assert.strictEqual(child.code, 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should support having --env-file cli flag', async () => {
|
||||||
|
const child = await common.spawnPromisified(
|
||||||
|
process.execPath,
|
||||||
|
[ '--no-warnings', `--env-file=${fixtures.path('run-script/.env')}`, '--run', `custom-env${envSuffix}`],
|
||||||
|
{ cwd: fixtures.path('run-script') },
|
||||||
|
);
|
||||||
|
assert.match(child.stdout, /hello world/);
|
||||||
|
assert.strictEqual(child.stderr, '');
|
||||||
|
assert.strictEqual(child.code, 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should properly escape shell', async () => {
|
||||||
|
const { escapeShell } = require('internal/shell');
|
||||||
|
|
||||||
|
const expectations = [
|
||||||
|
['', '\'\''],
|
||||||
|
['test', 'test'],
|
||||||
|
['test words', '\'test words\''],
|
||||||
|
['$1', '\'$1\''],
|
||||||
|
['"$1"', '\'"$1"\''],
|
||||||
|
['\'$1\'', '\\\'\'$1\'\\\''],
|
||||||
|
['\\$1', '\'\\$1\''],
|
||||||
|
['--arg="$1"', '\'--arg="$1"\''],
|
||||||
|
['--arg=node exec -c "$1"', '\'--arg=node exec -c "$1"\''],
|
||||||
|
['--arg=node exec -c \'$1\'', '\'--arg=node exec -c \'\\\'\'$1\'\\\''],
|
||||||
|
['\'--arg=node exec -c "$1"\'', '\\\'\'--arg=node exec -c "$1"\'\\\''],
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const [input, expectation] of expectations) {
|
||||||
|
assert.strictEqual(escapeShell(input), expectation);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
1
typings/internalBinding/modules.d.ts
vendored
1
typings/internalBinding/modules.d.ts
vendored
|
@ -26,4 +26,5 @@ export interface ModulesBinding {
|
||||||
string, // raw content
|
string, // raw content
|
||||||
]
|
]
|
||||||
getPackageScopeConfig(path: string): SerializedPackageConfig | undefined
|
getPackageScopeConfig(path: string): SerializedPackageConfig | undefined
|
||||||
|
getPackageJSONScripts(): string | undefined
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue