From 4e558bc73d5acdb5f1f8c956b54f97f83843576f Mon Sep 17 00:00:00 2001 From: Marco Ippolito Date: Tue, 27 Aug 2024 13:46:11 +0200 Subject: [PATCH 1/2] src: add config file support --- Makefile | 1 + doc/api/cli.md | 63 ++ doc/node.1 | 3 + doc/node_config_json_schema.json | 578 ++++++++++++++++++ lib/internal/options.js | 49 ++ lib/internal/process/pre_execution.js | 8 + node.gyp | 2 + src/node.cc | 34 ++ src/node_config_file.cc | 195 ++++++ src/node_config_file.h | 43 ++ src/node_options.cc | 96 ++- src/node_options.h | 6 + .../dotenv/node-options-no-tranform.env | 1 + test/fixtures/rc/empty-object.json | 3 + test/fixtures/rc/empty.json | 1 + test/fixtures/rc/host-port.json | 3 + test/fixtures/rc/import-as-string.json | 3 + test/fixtures/rc/import.json | 7 + test/fixtures/rc/invalid-import.json | 3 + test/fixtures/rc/negative-numeric.json | 3 + test/fixtures/rc/no-op.json | 3 + test/fixtures/rc/not-node-options-flag.json | 3 + test/fixtures/rc/numeric.json | 3 + test/fixtures/rc/override-property.json | 4 + test/fixtures/rc/sneaky-flag.json | 3 + test/fixtures/rc/string.json | 3 + test/fixtures/rc/test.js | 6 + test/fixtures/rc/transform-types.json | 3 + test/fixtures/rc/unknown-flag.json | 3 + test/fixtures/rc/v8-flag.json | 3 + test/parallel/test-config-file.js | 256 ++++++++ test/parallel/test-config-json-schema.js | 40 ++ tools/doc/generate-json-schema.mjs | 7 + 33 files changed, 1438 insertions(+), 1 deletion(-) create mode 100644 doc/node_config_json_schema.json create mode 100644 src/node_config_file.cc create mode 100644 src/node_config_file.h create mode 100644 test/fixtures/dotenv/node-options-no-tranform.env create mode 100644 test/fixtures/rc/empty-object.json create mode 100644 test/fixtures/rc/empty.json create mode 100644 test/fixtures/rc/host-port.json create mode 100644 test/fixtures/rc/import-as-string.json create mode 100644 test/fixtures/rc/import.json create mode 100644 test/fixtures/rc/invalid-import.json create mode 100644 test/fixtures/rc/negative-numeric.json create mode 100644 test/fixtures/rc/no-op.json create mode 100644 test/fixtures/rc/not-node-options-flag.json create mode 100644 test/fixtures/rc/numeric.json create mode 100644 test/fixtures/rc/override-property.json create mode 100644 test/fixtures/rc/sneaky-flag.json create mode 100644 test/fixtures/rc/string.json create mode 100644 test/fixtures/rc/test.js create mode 100644 test/fixtures/rc/transform-types.json create mode 100644 test/fixtures/rc/unknown-flag.json create mode 100644 test/fixtures/rc/v8-flag.json create mode 100644 test/parallel/test-config-file.js create mode 100644 test/parallel/test-config-json-schema.js create mode 100644 tools/doc/generate-json-schema.mjs diff --git a/Makefile b/Makefile index 39f5ee5e12a9b7..69c026755bf230 100644 --- a/Makefile +++ b/Makefile @@ -809,6 +809,7 @@ doc: $(NODE_EXE) doc-only ## Build Node.js, and then build the documentation wit out/doc: mkdir -p $@ + cp doc/node_config_json_schema.json $@ # If it's a source tarball, doc/api already contains the generated docs. # Just copy everything under doc/api over. diff --git a/doc/api/cli.md b/doc/api/cli.md index 855d83a2d2ba37..37ce213570b25b 100644 --- a/doc/api/cli.md +++ b/doc/api/cli.md @@ -911,6 +911,69 @@ added: v23.6.0 Enable experimental import support for `.node` addons. +### `--experimental-config-file` + + + +> Stability: 1.0 - Early development + +Use this flag to specify a configuration file that will be loaded and parsed +before the application starts. +Node.js will read the configuration file and apply the settings. +The configuration file should be a JSON file +with the following structure: + +```json +{ + "$schema": "https://nodejs.org/dist/REPLACEME/docs/node_config_json_schema.json", + "experimental-transform-types": true, + "import": [ + "amaro/transform" + ], + "disable-warning": "ExperimentalWarning", + "watch-path": "src", + "watch-preserve-output": true +} +``` + +Only flags that are allowed in [`NODE_OPTIONS`][] are supported. +No-op flags are not supported. +Not all V8 flags are currently supported. + +It is possible to use the [official JSON schema](../node_config_json_schema.json) +to validate the configuration file, which may vary depending on the Node.js version. +Each key in the configuration file corresponds to a flag that can be passed +as a command-line argument. The value of the key is the value that would be +passed to the flag. + +For example, the configuration file above is equivalent to +the following command-line arguments: + +```bash +node --experimental-transform-types --import amaro/transform --disable-warning=ExperimentalWarning --watch-path=src --watch-preserve-output +``` + +The priority in configuration is as follows: + +1. NODE\_OPTIONS and command-line options +2. Configuration file +3. Dotenv NODE\_OPTIONS + +Values in the configuration file will not override the values in the environment +variables and command-line options, but will override the values in the `NODE_OPTIONS` +env file parsed by the `--env-file` flag. + +If duplicate keys are present in the configuration file, only +the first key will be used. + +The configuration parser will throw an error if the configuration file contains +unknown keys or keys that cannot used in `NODE_OPTIONS`. + +Node.js will not sanitize or perform validation on the user-provided configuration, +so **NEVER** use untrusted configuration files. + ### `--experimental-eventsource` + +An attempt was made to get options before the bootstrapping was completed. + ### `ERR_OUT_OF_RANGE` diff --git a/src/node_errors.h b/src/node_errors.h index 801b19fc91810b..8c714d7f6fb142 100644 --- a/src/node_errors.h +++ b/src/node_errors.h @@ -101,6 +101,7 @@ void OOMErrorHandler(const char* location, const v8::OOMDetails& details); V(ERR_MODULE_NOT_FOUND, Error) \ V(ERR_NON_CONTEXT_AWARE_DISABLED, Error) \ V(ERR_OPERATION_FAILED, TypeError) \ + V(ERR_OPTIONS_BEFORE_BOOTSTRAPPING, Error) \ V(ERR_OUT_OF_RANGE, RangeError) \ V(ERR_REQUIRE_ASYNC_MODULE, Error) \ V(ERR_SCRIPT_EXECUTION_INTERRUPTED, Error) \ diff --git a/src/node_options.cc b/src/node_options.cc index dd449200db8789..89af5ff4422996 100644 --- a/src/node_options.cc +++ b/src/node_options.cc @@ -3,6 +3,7 @@ #include "env-inl.h" #include "node_binding.h" +#include "node_errors.h" #include "node_external_reference.h" #include "node_internals.h" #include "node_sea.h" @@ -1344,8 +1345,8 @@ void GetCLIOptionsValues(const FunctionCallbackInfo& args) { if (!env->has_run_bootstrapping_code()) { // No code because this is an assertion. - return env->ThrowError( - "Should not query options before bootstrapping is done"); + THROW_ERR_OPTIONS_BEFORE_BOOTSTRAPPING( + isolate, "Should not query options before bootstrapping is done"); } env->set_has_serialized_options(true); @@ -1466,8 +1467,8 @@ void GetCLIOptionsInfo(const FunctionCallbackInfo& args) { if (!env->has_run_bootstrapping_code()) { // No code because this is an assertion. - return env->ThrowError( - "Should not query options before bootstrapping is done"); + THROW_ERR_OPTIONS_BEFORE_BOOTSTRAPPING( + isolate, "Should not query options before bootstrapping is done"); } Mutex::ScopedLock lock(per_process::cli_options_mutex); @@ -1535,7 +1536,8 @@ void GetEmbedderOptions(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); if (!env->has_run_bootstrapping_code()) { // No code because this is an assertion. - return env->ThrowError( + THROW_ERR_OPTIONS_BEFORE_BOOTSTRAPPING( + env->isolate(), "Should not query options before bootstrapping is done"); } Isolate* isolate = args.GetIsolate(); @@ -1571,8 +1573,8 @@ void GetEnvOptionsInputType(const FunctionCallbackInfo& args) { if (!env->has_run_bootstrapping_code()) { // No code because this is an assertion. - return env->ThrowError( - "Should not query options before bootstrapping is done"); + THROW_ERR_OPTIONS_BEFORE_BOOTSTRAPPING( + isolate, "Should not query options before bootstrapping is done"); } Mutex::ScopedLock lock(per_process::cli_options_mutex);