Skip to content

Commit 66da386

Browse files
committed
watch: expose watch flags in child execArgv
Previously, watch-related flags were consumed by the watch mode manager and stripped from the arguments passed to the child process. This commit ensures these flags are preserved in 'process.execArgv' of the watched process by passing them via an internal environment variable. Fixes regression where flag order was not preserved.
1 parent 4d1557a commit 66da386

File tree

3 files changed

+89
-8
lines changed

3 files changed

+89
-8
lines changed

lib/internal/main/watch_mode.js

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ const {
77
ArrayPrototypePush,
88
ArrayPrototypePushApply,
99
ArrayPrototypeSlice,
10+
JSONStringify,
1011
StringPrototypeStartsWith,
1112
} = primordials;
1213

@@ -44,17 +45,22 @@ const kCommand = ArrayPrototypeSlice(process.argv, 1);
4445
const kCommandStr = inspect(ArrayPrototypeJoin(kCommand, ' '));
4546

4647
const argsWithoutWatchOptions = [];
47-
for (let i = 0; i < process.execArgv.length; i++) {
48-
const arg = process.execArgv[i];
48+
const removedWatchFlags = [];
49+
const args = process.execArgv;
50+
for (let i = 0; i < args.length; i++) {
51+
const arg = args[i];
4952
if (StringPrototypeStartsWith(arg, '--watch=')) {
53+
ArrayPrototypePush(removedWatchFlags, arg);
5054
continue;
5155
}
5256
if (arg === '--watch') {
53-
const nextArg = process.execArgv[i + 1];
57+
ArrayPrototypePush(removedWatchFlags, arg);
58+
const nextArg = args[i + 1];
5459
if (nextArg && nextArg[0] !== '-') {
5560
// If `--watch` doesn't include `=` and the next
5661
// argument is not a flag then it is interpreted as
5762
// the watch argument, so we need to skip that as well
63+
ArrayPrototypePush(removedWatchFlags, nextArg);
5864
i++;
5965
}
6066
continue;
@@ -65,7 +71,14 @@ for (let i = 0; i < process.execArgv.length; i++) {
6571
// if --watch-path doesn't include `=` it means
6672
// that the next arg is the target path, so we
6773
// need to skip that as well
68-
i++;
74+
ArrayPrototypePush(removedWatchFlags, arg);
75+
const nextArg = args[i + 1];
76+
if (nextArg) {
77+
ArrayPrototypePush(removedWatchFlags, nextArg);
78+
i++;
79+
}
80+
} else {
81+
ArrayPrototypePush(removedWatchFlags, arg);
6982
}
7083
continue;
7184
}
@@ -94,12 +107,16 @@ let exited;
94107
function start() {
95108
exited = false;
96109
const stdio = kShouldFilterModules ? ['inherit', 'inherit', 'inherit', 'ipc'] : 'inherit';
110+
const env = {
111+
...process.env,
112+
WATCH_REPORT_DEPENDENCIES: '1',
113+
};
114+
if (removedWatchFlags.length > 0) {
115+
env.NODE_WATCH_ARGS = JSONStringify(removedWatchFlags);
116+
}
97117
child = spawn(process.execPath, argsWithoutWatchOptions, {
98118
stdio,
99-
env: {
100-
...process.env,
101-
WATCH_REPORT_DEPENDENCIES: '1',
102-
},
119+
env,
103120
});
104121
watcher.watchChildProcessModules(child);
105122
if (kEnvFiles.length > 0) {

lib/internal/process/pre_execution.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
'use strict';
22

33
const {
4+
ArrayIsArray,
45
ArrayPrototypeForEach,
6+
ArrayPrototypeUnshiftApply,
57
Date,
68
DatePrototypeGetDate,
79
DatePrototypeGetFullYear,
810
DatePrototypeGetHours,
911
DatePrototypeGetMinutes,
1012
DatePrototypeGetMonth,
1113
DatePrototypeGetSeconds,
14+
JSONParse,
1215
NumberParseInt,
1316
ObjectDefineProperty,
1417
ObjectFreeze,
@@ -270,6 +273,19 @@ function patchProcessObject(expandArgv1) {
270273
process._exiting = false;
271274
process.argv[0] = process.execPath;
272275

276+
const watchArgsFromLauncher = process.env.NODE_WATCH_ARGS;
277+
if (watchArgsFromLauncher !== undefined) {
278+
delete process.env.NODE_WATCH_ARGS;
279+
try {
280+
const parsed = JSONParse(watchArgsFromLauncher);
281+
if (ArrayIsArray(parsed) && parsed.length > 0) {
282+
ArrayPrototypeUnshiftApply(process.execArgv, parsed);
283+
}
284+
} catch {
285+
// Ignore malformed payloads.
286+
}
287+
}
288+
273289
/** @type {string} */
274290
let mainEntry;
275291
// If requested, update process.argv[1] to replace whatever the user provided with the resolved absolute file path of

test/sequential/test-watch-mode-watch-flags.mjs

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,4 +94,52 @@ describe('watch mode - watch flags', { concurrency: !process.env.TEST_PARALLEL,
9494
`Completed running ${inspect(file)}. Waiting for file changes before restarting...`,
9595
]);
9696
});
97+
98+
it('exposes watch flags through process.execArgv inside the watched script', async () => {
99+
const projectDir = tmpdir.resolve('project-watch-exec-argv');
100+
mkdirSync(projectDir);
101+
102+
const file = createTmpFile(`
103+
console.log(JSON.stringify(process.execArgv));
104+
`, '.js', projectDir);
105+
const watchPath = path.join(projectDir, 'template.html');
106+
writeFileSync(watchPath, '');
107+
108+
async function assertExecArgv(args, expectedSubsequences) {
109+
const { stdout, stderr } = await runNode({
110+
args, options: { cwd: projectDir }
111+
});
112+
113+
assert.strictEqual(stderr, '');
114+
115+
const execArgvLine = stdout[0];
116+
const execArgv = JSON.parse(execArgvLine);
117+
assert.ok(Array.isArray(execArgv));
118+
const matched = expectedSubsequences.some((expectedSeq) => {
119+
for (let i = 0; i <= execArgv.length - expectedSeq.length; i++) {
120+
let ok = true;
121+
for (let j = 0; j < expectedSeq.length; j++) {
122+
if (execArgv[i + j] !== expectedSeq[j]) {
123+
ok = false;
124+
break;
125+
}
126+
}
127+
if (ok) return true;
128+
}
129+
return false;
130+
});
131+
assert.ok(matched,
132+
`execArgv (${execArgv}) does not contain any expected sequence (${expectedSubsequences.map((seq) => `[${seq}]`).join(', ')})`);
133+
assert.match(stdout.at(-1), /^Completed running/);
134+
}
135+
136+
await assertExecArgv(['--watch', file], [['--watch']]);
137+
await assertExecArgv(['--watch-path=template.html', file], [['--watch-path=template.html']]);
138+
await assertExecArgv(
139+
['--watch-path', 'template.html', file],
140+
[
141+
['--watch-path', 'template.html'],
142+
['--watch-path=template.html'],
143+
]);
144+
});
97145
});

0 commit comments

Comments
 (0)