JavaScript Basics: Add Command-Line Options with parseArgs
A command-line option lets someone change a script’s behavior without editing its source. This tutorial builds a small greeting command with a name, repetition count, uppercase switch, and help message. Node’s built-in parseArgs handles the option syntax; our code checks what the values mean.
Prepare a scratch directory
You need Linux, Bash, a text editor, and Node.js on your PATH. The commands were tested with Node.js v24.19.0. They require no npm packages, package manifest, network access, or application environment variables. The .mjs extension makes the source a JavaScript module.
Run these commands and keep this Bash session open:
node --version
start_dir="$PWD"
work_dir="$(mktemp -d)" || exit 1
cd -- "$work_dir" || exit 1The tested version prints:
v24.19.0Use your editor to save the following UTF-8 source file inside the temporary directory. This script only prints messages; it does not modify files or contact a service.
Describe the accepted options
Save as greet-with-options.mjs:
import { parseArgs } from "node:util";
const usage = `Usage: node greet-with-options.mjs [options]
-n, --name TEXT Name to greet (default: reader)
-t, --times N Repeat 1 to 5 times (default: 1)
-u, --upper Use uppercase
-h, --help Show this help`;
function main() {
const { values } = parseArgs({
args: process.argv.slice(2),
options: {
name: { type: "string", short: "n", default: "reader" },
times: { type: "string", short: "t", default: "1" },
upper: { type: "boolean", short: "u", default: false },
help: { type: "boolean", short: "h", default: false },
},
strict: true,
allowPositionals: false,
});
if (values.help) {
console.log(usage);
return;
}
const name = values.name.trim();
if (name.length === 0) {
throw new Error("--name must not be empty.");
}
if (!["1", "2", "3", "4", "5"].includes(values.times)) {
throw new Error("--times must be a digit from 1 to 5.");
}
const times = Number(values.times);
const greeting = `Hello, ${name}!`;
for (let index = 0; index < times; index += 1) {
console.log(values.upper ? greeting.toUpperCase() : greeting);
}
}
try {
main();
} catch (error) {
console.error(`Error: ${error.message}`);
console.error("Try --help for usage.");
process.exitCode = 2;
}process.argv starts with the Node executable and script path. slice(2) gives the parser only the arguments after the filename. Each option declares a string or boolean type, with an optional one-letter alias and default. There is no numeric option type here: we accept a string, validate it, and convert it with Number.
For this deliberately small command, the allowed count is exactly one of the strings "1" through "5". That rejects fractions, trailing text, an empty value, and alternative spellings such as "02". Adjust this rule if your own command needs a larger range.
strict: true rejects unknown options and incompatible option syntax. allowPositionals: false rejects unnamed arguments such as a bare Ada. The version-specific parseArgs reference documents these settings.
The two checks serve different purposes. The parser can tell that --times 2x supplies a string, but our program must decide whether that string is an acceptable count. Likewise, --name "" supplies a value even though the value is empty. Keeping these rules together before the loop means invalid input never produces a partial greeting. When you add another option, update its parser description, application checks, and help text together so they stay consistent.
Run the command
First, use all the defaults:
node greet-with-options.mjsOutput:
Hello, reader!Next, supply a name containing a space and use the shorter option names:
node greet-with-options.mjs -n "Ada Lovelace" -t 2 -uOutput:
HELLO, ADA LOVELACE!
HELLO, ADA LOVELACE!Bash’s quotes keep the name together as one argument. --upper and -u are switches: their presence means true. Leave them out for false; --upper=false is not accepted by this command.
A string value can also follow an equals sign. This is useful when the value itself begins with a dash:
node greet-with-options.mjs --name=-Ada --times=1Output:
Hello, -Ada!Get the help message:
node greet-with-options.mjs --helpOutput:
Usage: node greet-with-options.mjs [options]
-n, --name TEXT Name to greet (default: reader)
-t, --times N Repeat 1 to 5 times (default: 1)
-u, --upper Use uppercase
-h, --help Show this helpHelp and successful greetings exit with status 0. Parsing happens before the help branch, so --help does not make an unknown option valid. After parsing, help skips the name and count checks.
Make failures visible to the shell
Run an invalid count inside an if statement. This lets you inspect the failure even if your shell has set -e enabled:
if node greet-with-options.mjs --times 0; then
printf 'Unexpected success\n'
else
printf 'Exit status: %s\n' "$?"
fiOutput, including the error written to standard error:
Error: --times must be a digit from 1 to 5.
Try --help for usage.
Exit status: 2Try an option with its required value missing:
if node greet-with-options.mjs --name; then
printf 'Unexpected success\n'
else
printf 'Exit status: %s\n' "$?"
fiOutput on the tested Node version:
Error: Option '-n, --name <value>' argument missing
Try --help for usage.
Exit status: 2The catch handles both parser errors and our validation errors. Setting process.exitCode lets Node finish normally with a failure status, rather than forcing an immediate exit that could interrupt pending output. See Node’s exit guidance.
If an option appears more than once, the final value wins with these settings. For a command that collects repeated inputs, investigate multiple: true; do not assume duplicates are automatically rejected.
Clean up
In the same Bash session, remove only the file you created and then the empty directory:
rm -- greet-with-options.mjs
cd -- "$start_dir" || exit 1
rmdir -- "$work_dir"
unset start_dir work_dirAcknowledgements
This post was created with assistance from AI.