Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 35 additions & 2 deletions doc/api/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -1016,7 +1016,7 @@ in the `$schema` must be replaced with the version of Node.js you are using.
"watch-path": "src",
"watch-preserve-output": true
},
"testRunner": {
"test": {
"test-isolation": "process"
},
"watch": {
Expand All @@ -1029,7 +1029,40 @@ The configuration file supports namespace-specific options:

* The `nodeOptions` field contains CLI flags that are allowed in [`NODE_OPTIONS`][].

* Namespace fields like `testRunner` contain configuration specific to that subsystem.
* Namespace fields like `test`, `watch`, and `permission` contain configuration specific to that subsystem.

When a namespace is present in the
configuration file, Node.js automatically enables the corresponding flag
(e.g., `--test`, `--watch`, `--permission`). This allows you to configure
subsystem-specific options without explicitly passing the flag on the command line.

For example:

```json
{
"test": {
"test-isolation": "process"
}
}
```

is equivalent to:

```bash
node --test --test-isolation=process
```

To disable the automatic flag while still using namespace options, you can
explicitly set the flag to `false` within the namespace:

```json
{
"test": {
"test": false,
"test-isolation": "process"
}
}
```

No-op flags are not supported.
Not all V8 flags are currently supported.
Expand Down
5 changes: 3 additions & 2 deletions doc/api/permissions.md
Original file line number Diff line number Diff line change
Expand Up @@ -175,10 +175,11 @@ Example `node.config.json`:
}
```

Run with the configuration file:
When the `permission` namespace is present in the configuration file, Node.js
automatically enables the `--permission` flag. Run with:

```console
$ node --permission --experimental-default-config-file app.js
$ node --experimental-default-config-file app.js
```

#### Using the Permission Model with `npx`
Expand Down
14 changes: 10 additions & 4 deletions doc/node-config-schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,9 @@
"experimental-websocket": {
"type": "boolean"
},
"experimental-webstorage": {
"type": "boolean"
},
"extra-info-on-fatal-exception": {
"type": "boolean"
},
Expand Down Expand Up @@ -594,9 +597,6 @@
"watch-preserve-output": {
"type": "boolean"
},
"webstorage": {
"type": "boolean"
},
"zero-fill-buffers": {
"type": "boolean"
}
Expand Down Expand Up @@ -652,10 +652,13 @@
},
"allow-worker": {
"type": "boolean"
},
"permission": {
"type": "boolean"
}
}
},
"testRunner": {
"test": {
"type": "object",
"additionalProperties": false,
"properties": {
Expand All @@ -665,6 +668,9 @@
"experimental-test-module-mocks": {
"type": "boolean"
},
"test": {
"type": "boolean"
},
"test-concurrency": {
"type": "number"
},
Expand Down
37 changes: 36 additions & 1 deletion src/node_config_file.cc
Original file line number Diff line number Diff line change
Expand Up @@ -255,20 +255,44 @@ ParseResult ConfigReader::ParseConfig(const std::string_view& config_path) {
available_namespaces.end());
// Create a set to track unique options
std::unordered_set<std::string> unique_options;
// Namespaces in OPTION_NAMESPACE_LIST
std::unordered_set<std::string> namespaces_with_implicit_flags;

// Iterate through the main object to find all namespaces
for (auto field : main_object) {
std::string_view field_name;
if (field.unescaped_key().get(field_name)) {
return ParseResult::InvalidContent;
}

// Check if this field is a valid namespace
std::string namespace_name(field_name);

// TODO(@marco-ippolito): Remove warning for testRunner namespace
if (namespace_name == "testRunner") {
FPrintF(stderr,
"the \"testRunner\" namespace has been removed. "
"Use \"test\" instead.\n");
// Better to throw an error than to ignore it
// Otherwise users might think their test suite is green
// when it's not running
return ParseResult::InvalidContent;
}

// Check if this field is a valid namespace
if (!valid_namespaces.contains(namespace_name)) {
// If not, skip it
continue;
}

// List of implicit namespace flags
for (auto ns_enum : options_parser::AllNamespaces()) {
std::string ns_str = options_parser::NamespaceEnumToString(ns_enum);
if (!ns_str.empty() && namespace_name == ns_str) {
namespaces_with_implicit_flags.insert(namespace_name);
break;
}
}

// Get the namespace object
simdjson::ondemand::object namespace_object;
auto field_error = field.value().get_object().get(namespace_object);
Expand All @@ -290,6 +314,17 @@ ParseResult ConfigReader::ParseConfig(const std::string_view& config_path) {
}
}

// Add implicit flags for namespaces (--test, --permission, --watch)
// These flags are automatically enabled when their namespace is present
for (const auto& ns : namespaces_with_implicit_flags) {
std::string flag = "--" + ns;
std::string no_flag = "--no-" + ns;
// We skip if the user has already set the flag or its negation
if (!unique_options.contains(flag) && !unique_options.contains(no_flag)) {
namespace_options_.push_back(flag);
}
}

return ParseResult::Valid;
}

Expand Down
7 changes: 5 additions & 2 deletions src/node_options.cc
Original file line number Diff line number Diff line change
Expand Up @@ -599,7 +599,8 @@ EnvironmentOptionsParser::EnvironmentOptionsParser() {
"enable the permission system",
&EnvironmentOptions::permission,
kAllowedInEnvvar,
false);
false,
OptionNamespaces::kPermissionNamespace);
AddOption("--allow-fs-read",
"allow permissions to read the filesystem",
&EnvironmentOptions::allow_fs_read,
Expand Down Expand Up @@ -839,7 +840,9 @@ EnvironmentOptionsParser::EnvironmentOptionsParser() {
AddOption("--test",
"launch test runner on startup",
&EnvironmentOptions::test_runner,
kDisallowedInEnvvar);
kDisallowedInEnvvar,
false,
OptionNamespaces::kTestRunnerNamespace);
AddOption("--test-concurrency",
"specify test runner concurrency",
&EnvironmentOptions::test_runner_concurrency,
Expand Down
2 changes: 1 addition & 1 deletion src/node_options.h
Original file line number Diff line number Diff line change
Expand Up @@ -415,7 +415,7 @@ std::vector<std::string> MapAvailableNamespaces();
// Define all namespace entries
#define OPTION_NAMESPACE_LIST(V) \
V(kNoNamespace, "") \
V(kTestRunnerNamespace, "testRunner") \
V(kTestRunnerNamespace, "test") \
V(kWatchNamespace, "watch") \
V(kPermissionNamespace, "permission")

Expand Down
3 changes: 2 additions & 1 deletion test/fixtures/options-as-flags/test-config.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
"experimental-transform-types": true,
"max-http-header-size": 8192
},
"testRunner": {
"test": {
"test": false,
"test-isolation": "none"
}
}
5 changes: 5 additions & 0 deletions test/fixtures/rc/deprecated-testrunner-namespace.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"testRunner": {
"test-isolation": "none"
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"testRunner": {
"test": {
"test-name-pattern": "first-pattern",
"test-name-pattern": "second-pattern"
}
Expand Down
2 changes: 1 addition & 1 deletion test/fixtures/rc/empty-valid-namespace.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
"testRunner": {}
"permission": {}
}
2 changes: 1 addition & 1 deletion test/fixtures/rc/namespace-with-array.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"testRunner": {
"test": {
"test-coverage-exclude": ["config-pattern1", "config-pattern2"]
}
}
2 changes: 1 addition & 1 deletion test/fixtures/rc/namespace-with-disallowed-envvar.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"testRunner": {
"test": {
"test-concurrency": 1,
"experimental-test-coverage": true
}
Expand Down
2 changes: 1 addition & 1 deletion test/fixtures/rc/namespaced/node.config.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"testRunner": {
"test": {
"test-isolation": "none"
}
}
2 changes: 1 addition & 1 deletion test/fixtures/rc/override-namespace.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"testRunner": {
"test": {
"test-isolation": "process"
},
"nodeOptions": {
Expand Down
2 changes: 1 addition & 1 deletion test/fixtures/rc/override-node-option-with-namespace.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"nodeOptions": {
"test-isolation": "none"
},
"testRunner": {
"test": {
"test-isolation": "process"
}
}
5 changes: 5 additions & 0 deletions test/fixtures/rc/permission-namespace.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"permission": {
"allow-fs-read": "*"
}
}
6 changes: 6 additions & 0 deletions test/fixtures/rc/test-namespace-explicit-false.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"test": {
"test": false,
"test-isolation": "none"
}
}
2 changes: 1 addition & 1 deletion test/fixtures/rc/unknown-flag-namespace.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"testRunner": {
"test": {
"unknown-flag": true
}
}
5 changes: 5 additions & 0 deletions test/fixtures/rc/watch-namespace.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"watch": {
"watch-preserve-output": true
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"testRunner": {
"test": {
"experimental-test-coverage": true
}
}
Loading
Loading