GeezerVison is showing github.com — src: support namespace options in configuration file · nodejs/node@8ea1fc5
File treeExpand file treeCollapse file tree Open diff view settings duplicate-namespace-option
Expand file treeCollapse file tree Open diff view settings Collapse file Copy file name to clipboardExpand all lines: doc/api/cli.md +12-5Lines changed: 12 additions & 5 deletions - Display the source diff
- Display the rich diff
| Original file line number | Diff line number | Diff line change |
|---|
@@ -931,11 +931,19 @@ in the `$schema` must be replaced with the version of Node.js you are using. | 931 | 931 | ], | 932 | 932 | "watch-path": "src", | 933 | 933 | "watch-preserve-output": true | | 934 | + }, | | 935 | + "testRunner": { | | 936 | + "test-isolation": "process" | 934 | 937 | } | 935 | 938 | } | 936 | 939 | ``` | 937 | 940 |
| 938 | | -In the `nodeOptions` field, only flags that are allowed in [`NODE_OPTIONS`][] are supported. | | 941 | +The configuration file supports namespace-specific options: | | 942 | + | | 943 | +* The `nodeOptions` field contains CLI flags that are allowed in [`NODE_OPTIONS`][]. | | 944 | + | | 945 | +* Namespace fields like `testRunner` contain configuration specific to that subsystem. | | 946 | + | 939 | 947 | No-op flags are not supported. | 940 | 948 | Not all V8 flags are currently supported. | 941 | 949 |
| @@ -949,7 +957,7 @@ For example, the configuration file above is equivalent to | 949 | 957 | the following command-line arguments: | 950 | 958 |
| 951 | 959 | ```bash | 952 | | -node --import amaro/strip --watch-path=src --watch-preserve-output | | 960 | +node --import amaro/strip --watch-path=src --watch-preserve-output --test-isolation=process | 953 | 961 | ``` | 954 | 962 |
| 955 | 963 | The priority in configuration is as follows: | @@ -962,11 +970,10 @@ Values in the configuration file will not override the values in the environment | 962 | 970 | variables and command-line options, but will override the values in the `NODE_OPTIONS` | 963 | 971 | env file parsed by the `--env-file` flag. | 964 | 972 |
| 965 | | -If duplicate keys are present in the configuration file, only | 966 | | -the first key will be used. | | 973 | +Keys cannot be duplicated within the same or different namespaces. | 967 | 974 |
| 968 | 975 | The configuration parser will throw an error if the configuration file contains | 969 | | -unknown keys or keys that cannot used in `NODE_OPTIONS`. | | 976 | +unknown keys or keys that cannot be used in a namespace. | 970 | 977 |
| 971 | 978 | Node.js will not sanitize or perform validation on the user-provided configuration, | 972 | 979 | so **NEVER** use untrusted configuration files. | |
Collapse file Copy file name to clipboardExpand all lines: doc/node-config-schema.json +129Lines changed: 129 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
| 584 | 584 | } | 585 | 585 | }, | 586 | 586 | "type": "object" | | 587 | + }, | | 588 | + "testRunner": { | | 589 | + "type": "object", | | 590 | + "additionalProperties": false, | | 591 | + "properties": { | | 592 | + "experimental-test-coverage": { | | 593 | + "type": "boolean" | | 594 | + }, | | 595 | + "experimental-test-module-mocks": { | | 596 | + "type": "boolean" | | 597 | + }, | | 598 | + "test-concurrency": { | | 599 | + "type": "number" | | 600 | + }, | | 601 | + "test-coverage-branches": { | | 602 | + "type": "number" | | 603 | + }, | | 604 | + "test-coverage-exclude": { | | 605 | + "oneOf": [ | | 606 | + { | | 607 | + "type": "string" | | 608 | + }, | | 609 | + { | | 610 | + "items": { | | 611 | + "type": "string", | | 612 | + "minItems": 1 | | 613 | + }, | | 614 | + "type": "array" | | 615 | + } | | 616 | + ] | | 617 | + }, | | 618 | + "test-coverage-functions": { | | 619 | + "type": "number" | | 620 | + }, | | 621 | + "test-coverage-include": { | | 622 | + "oneOf": [ | | 623 | + { | | 624 | + "type": "string" | | 625 | + }, | | 626 | + { | | 627 | + "items": { | | 628 | + "type": "string", | | 629 | + "minItems": 1 | | 630 | + }, | | 631 | + "type": "array" | | 632 | + } | | 633 | + ] | | 634 | + }, | | 635 | + "test-coverage-lines": { | | 636 | + "type": "number" | | 637 | + }, | | 638 | + "test-force-exit": { | | 639 | + "type": "boolean" | | 640 | + }, | | 641 | + "test-global-setup": { | | 642 | + "type": "string" | | 643 | + }, | | 644 | + "test-isolation": { | | 645 | + "type": "string" | | 646 | + }, | | 647 | + "test-name-pattern": { | | 648 | + "oneOf": [ | | 649 | + { | | 650 | + "type": "string" | | 651 | + }, | | 652 | + { | | 653 | + "items": { | | 654 | + "type": "string", | | 655 | + "minItems": 1 | | 656 | + }, | | 657 | + "type": "array" | | 658 | + } | | 659 | + ] | | 660 | + }, | | 661 | + "test-only": { | | 662 | + "type": "boolean" | | 663 | + }, | | 664 | + "test-reporter": { | | 665 | + "oneOf": [ | | 666 | + { | | 667 | + "type": "string" | | 668 | + }, | | 669 | + { | | 670 | + "items": { | | 671 | + "type": "string", | | 672 | + "minItems": 1 | | 673 | + }, | | 674 | + "type": "array" | | 675 | + } | | 676 | + ] | | 677 | + }, | | 678 | + "test-reporter-destination": { | | 679 | + "oneOf": [ | | 680 | + { | | 681 | + "type": "string" | | 682 | + }, | | 683 | + { | | 684 | + "items": { | | 685 | + "type": "string", | | 686 | + "minItems": 1 | | 687 | + }, | | 688 | + "type": "array" | | 689 | + } | | 690 | + ] | | 691 | + }, | | 692 | + "test-shard": { | | 693 | + "type": "string" | | 694 | + }, | | 695 | + "test-skip-pattern": { | | 696 | + "oneOf": [ | | 697 | + { | | 698 | + "type": "string" | | 699 | + }, | | 700 | + { | | 701 | + "items": { | | 702 | + "type": "string", | | 703 | + "minItems": 1 | | 704 | + }, | | 705 | + "type": "array" | | 706 | + } | | 707 | + ] | | 708 | + }, | | 709 | + "test-timeout": { | | 710 | + "type": "number" | | 711 | + }, | | 712 | + "test-update-snapshots": { | | 713 | + "type": "boolean" | | 714 | + } | | 715 | + } | 587 | 716 | } | 588 | 717 | }, | 589 | 718 | "type": "object" | |
Collapse file Copy file name to clipboardExpand all lines: lib/internal/options.js +57-14Lines changed: 57 additions & 14 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -13,6 +13,7 @@ const { | 13 | 13 | getCLIOptionsInfo, | 14 | 14 | getEmbedderOptions: getEmbedderOptionsFromBinding, | 15 | 15 | getEnvOptionsInputType, | | 16 | + getNamespaceOptionsInputType, | 16 | 17 | } = internalBinding('options'); | 17 | 18 |
| 18 | 19 | let warnOnAllowUnauthorized = true; | @@ -38,7 +39,22 @@ function getEmbedderOptions() { | 38 | 39 | } | 39 | 40 |
| 40 | 41 | function generateConfigJsonSchema() { | 41 | | - const map = getEnvOptionsInputType(); | | 42 | + const envOptionsMap = getEnvOptionsInputType(); | | 43 | + const namespaceOptionsMap = getNamespaceOptionsInputType(); | | 44 | + | | 45 | + function createPropertyForType(type) { | | 46 | + if (type === 'array') { | | 47 | + return { | | 48 | + __proto__: null, | | 49 | + oneOf: [ | | 50 | + { __proto__: null, type: 'string' }, | | 51 | + { __proto__: null, items: { __proto__: null, type: 'string', minItems: 1 }, type: 'array' }, | | 52 | + ], | | 53 | + }; | | 54 | + } | | 55 | + | | 56 | + return { __proto__: null, type }; | | 57 | + } | 42 | 58 |
| 43 | 59 | const schema = { | 44 | 60 | __proto__: null, | @@ -60,31 +76,58 @@ function generateConfigJsonSchema() { | 60 | 76 | type: 'object', | 61 | 77 | }; | 62 | 78 |
| 63 | | - const nodeOptions = schema.properties.nodeOptions.properties; | | 79 | + // Get the root properties object for adding namespaces | | 80 | + const rootProperties = schema.properties; | | 81 | + const nodeOptions = rootProperties.nodeOptions.properties; | 64 | 82 |
| 65 | | - for (const { 0: key, 1: type } of map) { | | 83 | + // Add env options to nodeOptions (backward compatibility) | | 84 | + for (const { 0: key, 1: type } of envOptionsMap) { | 66 | 85 | const keyWithoutPrefix = StringPrototypeReplace(key, '--', ''); | 67 | | - if (type === 'array') { | 68 | | - nodeOptions[keyWithoutPrefix] = { | 69 | | - __proto__: null, | 70 | | - oneOf: [ | 71 | | - { __proto__: null, type: 'string' }, | 72 | | - { __proto__: null, items: { __proto__: null, type: 'string', minItems: 1 }, type: 'array' }, | 73 | | - ], | 74 | | - }; | 75 | | - } else { | 76 | | - nodeOptions[keyWithoutPrefix] = { __proto__: null, type }; | | 86 | + nodeOptions[keyWithoutPrefix] = createPropertyForType(type); | | 87 | + } | | 88 | + | | 89 | + // Add namespace properties at the root level | | 90 | + for (const { 0: namespace, 1: optionsMap } of namespaceOptionsMap) { | | 91 | + // Create namespace object at the root level | | 92 | + rootProperties[namespace] = { | | 93 | + __proto__: null, | | 94 | + type: 'object', | | 95 | + additionalProperties: false, | | 96 | + properties: { __proto__: null }, | | 97 | + }; | | 98 | + | | 99 | + const namespaceProperties = rootProperties[namespace].properties; | | 100 | + | | 101 | + // Add all options for this namespace | | 102 | + for (const { 0: optionName, 1: optionType } of optionsMap) { | | 103 | + const keyWithoutPrefix = StringPrototypeReplace(optionName, '--', ''); | | 104 | + namespaceProperties[keyWithoutPrefix] = createPropertyForType(optionType); | 77 | 105 | } | | 106 | + | | 107 | + // Sort the namespace properties alphabetically | | 108 | + const sortedNamespaceKeys = ArrayPrototypeSort(ObjectKeys(namespaceProperties)); | | 109 | + const sortedNamespaceProperties = ObjectFromEntries( | | 110 | + ArrayPrototypeMap(sortedNamespaceKeys, (key) => [key, namespaceProperties[key]]), | | 111 | + ); | | 112 | + rootProperties[namespace].properties = sortedNamespaceProperties; | 78 | 113 | } | 79 | 114 |
| 80 | | - // Sort the proerties by key alphabetically. | | 115 | + // Sort the top-level properties by key alphabetically | 81 | 116 | const sortedKeys = ArrayPrototypeSort(ObjectKeys(nodeOptions)); | 82 | 117 | const sortedProperties = ObjectFromEntries( | 83 | 118 | ArrayPrototypeMap(sortedKeys, (key) => [key, nodeOptions[key]]), | 84 | 119 | ); | 85 | 120 |
| 86 | 121 | schema.properties.nodeOptions.properties = sortedProperties; | 87 | 122 |
| | 123 | + // Also sort the root level properties | | 124 | + const sortedRootKeys = ArrayPrototypeSort(ObjectKeys(rootProperties)); | | 125 | + const sortedRootProperties = ObjectFromEntries( | | 126 | + ArrayPrototypeMap(sortedRootKeys, (key) => [key, rootProperties[key]]), | | 127 | + ); | | 128 | + | | 129 | + schema.properties = sortedRootProperties; | | 130 | + | 88 | 131 | return schema; | 89 | 132 | } | 90 | 133 |
| |
Collapse file Copy file name to clipboardExpand all lines: lib/internal/test_runner/runner.js +7-1Lines changed: 7 additions & 1 deletion | Original file line number | Diff line number | Diff line change |
|---|
@@ -98,7 +98,13 @@ let debug = require('internal/util/debuglog').debuglog('test_runner', (fn) => { | 98 | 98 | }); | 99 | 99 |
| 100 | 100 | const kIsolatedProcessName = Symbol('kIsolatedProcessName'); | 101 | | -const kFilterArgs = ['--test', '--experimental-test-coverage', '--watch']; | | 101 | +const kFilterArgs = [ | | 102 | + '--test', | | 103 | + '--experimental-test-coverage', | | 104 | + '--watch', | | 105 | + '--experimental-default-config-file', | | 106 | + '--experimental-config-file', | | 107 | +]; | 102 | 108 | const kFilterArgValues = ['--test-reporter', '--test-reporter-destination']; | 103 | 109 | const kDiagnosticsFilterArgs = ['tests', 'suites', 'pass', 'fail', 'cancelled', 'skipped', 'todo', 'duration_ms']; | 104 | 110 |
| |
Collapse file Copy file name to clipboardExpand all lines: src/node.cc +14-2Lines changed: 14 additions & 2 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -910,7 +910,7 @@ static ExitCode InitializeNodeWithArgsInternal( | 910 | 910 | default: | 911 | 911 | UNREACHABLE(); | 912 | 912 | } | 913 | | - node_options_from_config = per_process::config_reader.AssignNodeOptions(); | | 913 | + node_options_from_config = per_process::config_reader.GetNodeOptions(); | 914 | 914 | // (@marco-ippolito) Avoid reparsing the env options again | 915 | 915 | std::vector<std::string> env_argv_from_config = | 916 | 916 | ParseNodeOptionsEnvVar(node_options_from_config, errors); | @@ -956,7 +956,19 @@ static ExitCode InitializeNodeWithArgsInternal( | 956 | 956 | #endif | 957 | 957 |
| 958 | 958 | if (!(flags & ProcessInitializationFlags::kDisableCLIOptions)) { | 959 | | - const ExitCode exit_code = | | 959 | + // Parse the options coming from the config file. | | 960 | + // This is done before parsing the command line options | | 961 | + // as the cli flags are expected to override the config file ones. | | 962 | + std::vector<std::string> extra_argv = | | 963 | + per_process::config_reader.GetNamespaceFlags(); | | 964 | + // [0] is expected to be the program name, fill it in from the real argv. | | 965 | + extra_argv.insert(extra_argv.begin(), argv->at(0)); | | 966 | + // Parse the extra argv coming from the config file | | 967 | + ExitCode exit_code = ProcessGlobalArgsInternal( | | 968 | + &extra_argv, nullptr, errors, kDisallowedInEnvvar); | | 969 | + if (exit_code != ExitCode::kNoFailure) return exit_code; | | 970 | + // Parse options coming from the command line. | | 971 | + exit_code = | 960 | 972 | ProcessGlobalArgsInternal(argv, exec_argv, errors, kDisallowedInEnvvar); | 961 | 973 | if (exit_code != ExitCode::kNoFailure) return exit_code; | 962 | 974 | } | |
|