util: fix parseArgs skipping positional arg with --eval and --print

PR-URL: https://github.com/nodejs/node/pull/60814
Reviewed-By: René <contact.9a5d6388@renegade334.me.uk>
Reviewed-By: Ilyas Shabi <ilyasshabi94@gmail.com>
This commit is contained in:
azadgupta1 2025-11-23 11:16:19 +05:30 committed by Renegade334
parent 67527c427e
commit 060deae99b
2 changed files with 47 additions and 80 deletions

View File

@ -2,7 +2,6 @@
const {
ArrayPrototypeForEach,
ArrayPrototypeIncludes,
ArrayPrototypeMap,
ArrayPrototypePush,
ArrayPrototypePushApply,
@ -54,20 +53,16 @@ const {
kEmptyObject,
} = require('internal/util');
const { getOptionValue } = require('internal/options');
function getMainArgs() {
// Work out where to slice process.argv for user supplied arguments.
// -p / --print internally sets --eval, so this works for all cases
const evalValue = getOptionValue('--eval');
// Check node options for scenarios where user CLI args follow executable.
const execArgv = process.execArgv;
if (ArrayPrototypeIncludes(execArgv, '-e') ||
ArrayPrototypeIncludes(execArgv, '--eval') ||
ArrayPrototypeIncludes(execArgv, '-p') ||
ArrayPrototypeIncludes(execArgv, '--print')) {
if (evalValue.length !== 0) {
return ArrayPrototypeSlice(process.argv, 1);
}
// Normally first two arguments are executable and script, then CLI arguments
return ArrayPrototypeSlice(process.argv, 2);
}

View File

@ -1,6 +1,6 @@
import '../common/index.mjs';
import { spawnPromisified } from '../common/index.mjs';
import assert from 'node:assert';
import { test } from 'node:test';
import { suite, test } from 'node:test';
import { parseArgs } from 'node:util';
test('when short option used as flag then stored as flag', () => {
@ -211,73 +211,48 @@ test('order of option and positional does not matter (per README)', () => {
);
});
test('correct default args when use node -p', () => {
const holdArgv = process.argv;
process.argv = [process.argv0, '--foo'];
const holdExecArgv = process.execArgv;
process.execArgv = ['-p', '0'];
const result = parseArgs({ strict: false });
suite('correct default args', () => {
suite('with CLI flags', () => {
const evalCode = "JSON.stringify(require('util').parseArgs({ strict: false }).values)";
const evalCodePrinted = `process.stdout.write(${evalCode})`;
const execArgsTests = {
'-e <script>': ['-e', evalCodePrinted],
'-p <script>': ['-p', evalCode],
'-pe <script>': ['-pe', evalCode],
'--eval <script>': ['--eval', evalCodePrinted],
'--eval=<script>': [`--eval=${evalCodePrinted}`],
'--print <script>': ['--print', evalCode],
'--print --eval <script>': ['--print', '--eval', evalCode],
'--print --eval=<script>': ['--print', `--eval=${evalCode}`],
};
for (const description in execArgsTests) {
const execArgs = execArgsTests[description];
test(description, async () => {
const { code, signal, stderr, stdout } = await spawnPromisified(
process.execPath,
[...execArgs, '--', '--foo', '--bar']);
assert.deepStrictEqual({
code,
signal,
stderr,
stdout: JSON.parse(stdout),
}, {
code: 0,
signal: null,
stderr: '',
stdout: { foo: true, bar: true },
});
});
}
});
const expected = { values: { __proto__: null, foo: true },
positionals: [] };
assert.deepStrictEqual(result, expected);
process.argv = holdArgv;
process.execArgv = holdExecArgv;
});
test('correct default args when use node --print', () => {
const holdArgv = process.argv;
process.argv = [process.argv0, '--foo'];
const holdExecArgv = process.execArgv;
process.execArgv = ['--print', '0'];
const result = parseArgs({ strict: false });
const expected = { values: { __proto__: null, foo: true },
positionals: [] };
assert.deepStrictEqual(result, expected);
process.argv = holdArgv;
process.execArgv = holdExecArgv;
});
test('correct default args when use node -e', () => {
const holdArgv = process.argv;
process.argv = [process.argv0, '--foo'];
const holdExecArgv = process.execArgv;
process.execArgv = ['-e', '0'];
const result = parseArgs({ strict: false });
const expected = { values: { __proto__: null, foo: true },
positionals: [] };
assert.deepStrictEqual(result, expected);
process.argv = holdArgv;
process.execArgv = holdExecArgv;
});
test('correct default args when use node --eval', () => {
const holdArgv = process.argv;
process.argv = [process.argv0, '--foo'];
const holdExecArgv = process.execArgv;
process.execArgv = ['--eval', '0'];
const result = parseArgs({ strict: false });
const expected = { values: { __proto__: null, foo: true },
positionals: [] };
assert.deepStrictEqual(result, expected);
process.argv = holdArgv;
process.execArgv = holdExecArgv;
});
test('correct default args when normal arguments', () => {
const holdArgv = process.argv;
process.argv = [process.argv0, 'script.js', '--foo'];
const holdExecArgv = process.execArgv;
process.execArgv = [];
const result = parseArgs({ strict: false });
const expected = { values: { __proto__: null, foo: true },
positionals: [] };
assert.deepStrictEqual(result, expected);
process.argv = holdArgv;
process.execArgv = holdExecArgv;
test('without CLI flags', () => {
const holdArgv = process.argv;
process.argv = [process.argv0, 'script.js', '--foo', '--bar'];
const { values } = parseArgs({ strict: false });
assert.deepStrictEqual(values, { __proto__: null, foo: true, bar: true });
process.argv = holdArgv;
});
});
test('excess leading dashes on options are retained', () => {
@ -1052,13 +1027,10 @@ test('allow negative options and passed multiple arguments', () => {
test('auto-detect --no-foo as negated when strict:false and allowNegative', () => {
const holdArgv = process.argv;
process.argv = [process.argv0, 'script.js', '--no-foo'];
const holdExecArgv = process.execArgv;
process.execArgv = [];
const result = parseArgs({ strict: false, allowNegative: true });
const expected = { values: { __proto__: null, foo: false },
positionals: [] };
assert.deepStrictEqual(result, expected);
process.argv = holdArgv;
process.execArgv = holdExecArgv;
});