Skip to content

Commit cec1bd5

Browse files
src: add watch config namespace
PR-URL: #60178 Reviewed-By: Pietro Marchini <pietro.marchini94@gmail.com> Reviewed-By: Chemi Atlow <chemi@atlow.co.il>
1 parent 6a0c9c8 commit cec1bd5

File tree

6 files changed

+130
-11
lines changed

6 files changed

+130
-11
lines changed

doc/api/cli.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1022,6 +1022,9 @@ in the `$schema` must be replaced with the version of Node.js you are using.
10221022
},
10231023
"testRunner": {
10241024
"test-isolation": "process"
1025+
},
1026+
"watch": {
1027+
"watch-preserve-output": true
10251028
}
10261029
}
10271030
```

doc/node-config-schema.json

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -734,6 +734,35 @@
734734
"type": "boolean"
735735
}
736736
}
737+
},
738+
"watch": {
739+
"type": "object",
740+
"additionalProperties": false,
741+
"properties": {
742+
"watch": {
743+
"type": "boolean"
744+
},
745+
"watch-kill-signal": {
746+
"type": "string"
747+
},
748+
"watch-path": {
749+
"oneOf": [
750+
{
751+
"type": "string"
752+
},
753+
{
754+
"items": {
755+
"type": "string",
756+
"minItems": 1
757+
},
758+
"type": "array"
759+
}
760+
]
761+
},
762+
"watch-preserve-output": {
763+
"type": "boolean"
764+
}
765+
}
737766
}
738767
},
739768
"type": "object"

lib/internal/main/watch_mode.js

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
'use strict';
22
const {
33
ArrayPrototypeForEach,
4+
ArrayPrototypeIncludes,
45
ArrayPrototypeJoin,
56
ArrayPrototypeMap,
67
ArrayPrototypePush,
@@ -17,7 +18,7 @@ const {
1718
triggerUncaughtException,
1819
exitCodes: { kNoFailure },
1920
} = internalBinding('errors');
20-
const { getOptionValue } = require('internal/options');
21+
const { getOptionValue, getOptionsAsFlagsFromBinding } = require('internal/options');
2122
const { FilesWatcher } = require('internal/watch_mode/files_watcher');
2223
const { green, blue, red, white, clear } = require('internal/util/colors');
2324
const { convertToValidSignal } = require('internal/util');
@@ -40,14 +41,14 @@ const kCommand = ArrayPrototypeSlice(process.argv, 1);
4041
const kCommandStr = inspect(ArrayPrototypeJoin(kCommand, ' '));
4142

4243
const argsWithoutWatchOptions = [];
43-
44-
for (let i = 0; i < process.execArgv.length; i++) {
45-
const arg = process.execArgv[i];
44+
const argsFromBinding = getOptionsAsFlagsFromBinding();
45+
for (let i = 0; i < argsFromBinding.length; i++) {
46+
const arg = argsFromBinding[i];
4647
if (StringPrototypeStartsWith(arg, '--watch=')) {
4748
continue;
4849
}
4950
if (arg === '--watch') {
50-
const nextArg = process.execArgv[i + 1];
51+
const nextArg = argsFromBinding[i + 1];
5152
if (nextArg && nextArg[0] !== '-') {
5253
// If `--watch` doesn't include `=` and the next
5354
// argument is not a flag then it is interpreted as
@@ -66,6 +67,16 @@ for (let i = 0; i < process.execArgv.length; i++) {
6667
}
6768
continue;
6869
}
70+
if (StringPrototypeStartsWith(arg, '--experimental-config-file')) {
71+
if (!ArrayPrototypeIncludes(arg, '=')) {
72+
// Skip the flag and the next argument (the config file path)
73+
i++;
74+
}
75+
continue;
76+
}
77+
if (arg === '--experimental-default-config-file') {
78+
continue;
79+
}
6980
ArrayPrototypePush(argsWithoutWatchOptions, arg);
7081
}
7182

src/node_options.cc

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,7 @@ void EnvironmentOptions::CheckOptions(std::vector<std::string>* errors,
250250
} else if (test_runner_force_exit) {
251251
errors->push_back("either --watch or --test-force-exit "
252252
"can be used, not both");
253-
} else if (!test_runner && (argv->size() < 1 || (*argv)[1].empty())) {
253+
} else if (!test_runner && watch_mode_paths.empty() && argv->size() < 1) {
254254
errors->push_back("--watch requires specifying a file");
255255
}
256256

@@ -1021,20 +1021,26 @@ EnvironmentOptionsParser::EnvironmentOptionsParser() {
10211021
AddOption("--watch",
10221022
"run in watch mode",
10231023
&EnvironmentOptions::watch_mode,
1024-
kAllowedInEnvvar);
1024+
kAllowedInEnvvar,
1025+
false,
1026+
OptionNamespaces::kWatchNamespace);
10251027
AddOption("--watch-path",
10261028
"path to watch",
10271029
&EnvironmentOptions::watch_mode_paths,
1028-
kAllowedInEnvvar);
1030+
kAllowedInEnvvar,
1031+
OptionNamespaces::kWatchNamespace);
10291032
AddOption("--watch-kill-signal",
10301033
"kill signal to send to the process on watch mode restarts"
10311034
"(default: SIGTERM)",
10321035
&EnvironmentOptions::watch_mode_kill_signal,
1033-
kAllowedInEnvvar);
1036+
kAllowedInEnvvar,
1037+
OptionNamespaces::kWatchNamespace);
10341038
AddOption("--watch-preserve-output",
10351039
"preserve outputs on watch mode restart",
10361040
&EnvironmentOptions::watch_mode_preserve_output,
1037-
kAllowedInEnvvar);
1041+
kAllowedInEnvvar,
1042+
false,
1043+
OptionNamespaces::kWatchNamespace);
10381044
Implies("--watch-path", "--watch");
10391045
AddOption("--check",
10401046
"syntax check script without executing",

src/node_options.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -415,7 +415,8 @@ std::vector<std::string> MapAvailableNamespaces();
415415
// Define all namespace entries
416416
#define OPTION_NAMESPACE_LIST(V) \
417417
V(kNoNamespace, "") \
418-
V(kTestRunnerNamespace, "testRunner")
418+
V(kTestRunnerNamespace, "testRunner") \
419+
V(kWatchNamespace, "watch")
419420

420421
enum class OptionNamespaces {
421422
#define V(name, _) name,

test/sequential/test-watch-mode.mjs

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -791,4 +791,73 @@ process.on('message', (message) => {
791791
`Completed running ${inspect(file)}. Waiting for file changes before restarting...`,
792792
]);
793793
});
794+
795+
it('should watch changes to a file from config file', async () => {
796+
const file = createTmpFile();
797+
const configFile = createTmpFile(JSON.stringify({ watch: { 'watch': true } }), '.json');
798+
const { stderr, stdout } = await runWriteSucceed({
799+
file, watchedFile: file, args: ['--experimental-config-file', configFile, file], options: {
800+
timeout: 10000
801+
}
802+
});
803+
804+
assert.strictEqual(stderr, '');
805+
assert.deepStrictEqual(stdout, [
806+
'running',
807+
`Completed running ${inspect(file)}. Waiting for file changes before restarting...`,
808+
`Restarting ${inspect(file)}`,
809+
'running',
810+
`Completed running ${inspect(file)}. Waiting for file changes before restarting...`,
811+
]);
812+
});
813+
814+
it('should watch changes to a file with watch-path from config file', {
815+
skip: !supportsRecursive,
816+
}, async () => {
817+
const dir = tmpdir.resolve('subdir4');
818+
mkdirSync(dir);
819+
const file = createTmpFile();
820+
const watchedFile = createTmpFile('', '.js', dir);
821+
const configFile = createTmpFile(JSON.stringify({ watch: { 'watch-path': [dir] } }), '.json', dir);
822+
823+
const args = ['--experimental-config-file', configFile, file];
824+
const { stderr, stdout } = await runWriteSucceed({ file, watchedFile, args });
825+
826+
assert.strictEqual(stderr, '');
827+
assert.deepStrictEqual(stdout, [
828+
'running',
829+
`Completed running ${inspect(file)}. Waiting for file changes before restarting...`,
830+
`Restarting ${inspect(file)}`,
831+
'running',
832+
`Completed running ${inspect(file)}. Waiting for file changes before restarting...`,
833+
]);
834+
assert.strictEqual(stderr, '');
835+
});
836+
837+
it('should watch changes to a file from default config file', async () => {
838+
const dir = tmpdir.resolve('subdir5');
839+
mkdirSync(dir);
840+
841+
const file = createTmpFile('console.log("running");', '.js', dir);
842+
writeFileSync(path.join(dir, 'node.config.json'), JSON.stringify({ watch: { 'watch': true } }));
843+
844+
const { stderr, stdout } = await runWriteSucceed({
845+
file,
846+
watchedFile: file,
847+
args: ['--experimental-default-config-file', file],
848+
options: {
849+
timeout: 10000,
850+
cwd: dir
851+
}
852+
});
853+
854+
assert.strictEqual(stderr, '');
855+
assert.deepStrictEqual(stdout, [
856+
'running',
857+
`Completed running ${inspect(file)}. Waiting for file changes before restarting...`,
858+
`Restarting ${inspect(file)}`,
859+
'running',
860+
`Completed running ${inspect(file)}. Waiting for file changes before restarting...`,
861+
]);
862+
});
794863
});

0 commit comments

Comments
 (0)