◆ util.parseArgs

Node.js 18 の LTS 化が近いので新機能を見ていると 18.3 で追加された機能にコマンドライン引数のパース機能がありました
さらにこの機能 バックポートされて 16.17 でも使えるようになっていました
まだ experimental のステータスですが LTS 化後のバージョンにあとから使えるようにするくらいなので 使っていっても大丈夫そうな印象です

使ってみる

こういうスクリプトを用意します

[cmd-parse.js]
const util = require("util")
const parsed = util.parseArgs({
options: {
aaa: {
type: "string",
},
bbb: {
type: "string",
multiple: true,
},
ccc: {
type: "string",
short: "c",
},
ddd: {
type: "boolean",
multiple: true,
},
eee: {
type: "boolean",
short: "e",
},
fff: {
type: "boolean",
short: "f",
}
}
})
console.log(parsed)

このファイルにコマンドライン引数を渡して実行します
何も渡さないとこんな感じです

wsl@LAPTOP10:~$ node cmd-parse.js 
{ values: [Object: null prototype] {}, positionals: [] }

オプション aaa を指定してみます

wsl@LAPTOP10:~$ node cmd-parse.js --aaa 123
{ values: [Object: null prototype] { aaa: '123' }, positionals: [] }

bbb は multiple が true なので配列になります
ccc は short で c を指定したので --ccc の代わりに -c にもできます

wsl@LAPTOP10:~$ node cmd-parse.js --aaa 123 --bbb foo -c bar
{
values: [Object: null prototype] { aaa: '123', bbb: [ 'foo' ], ccc: 'bar' },
positionals: []
}

同じものを複数指定すると multiple がないなら最後のになって multiple があればすべてを配列で取得します

wsl@LAPTOP10:~$ node cmd-parse.js --aaa 1 --aaa 2 --bbb 3 --bbb 4
{
values: [Object: null prototype] { aaa: '2', bbb: [ '3', '4' ] },
positionals: []
}

「--foo bar」 形式でも 「--foo=bar」 形式でも対応しています

wsl@LAPTOP10:~$ node cmd-parse.js --bbb="a a" --bbb "b b" --bbb=c
{
values: [Object: null prototype] { bbb: [ 'a a', 'b b', 'c' ] },
positionals: []
}

type が boolean だとオプションの値を受け取らず オプションがあれば値は true になります
こっちも multiple だと配列になります

wsl@LAPTOP10:~$ node cmd-parse.js --ddd --eee -f
{
values: [Object: null prototype] { ddd: [ true ], eee: true, fff: true },
positionals: []
}

--ddd=false みたいなことをしても false は入れられなかったので キーがないか true のどちらかにしかできなそうです

args

デフォルトではコマンドライン引数が使われますが parseArgs の引数に args を渡せばそれをパース対象にできます

util.parseArgs({
args: ["--foo", "bar"],
options: {
foo: { type: "string" },
},
})
{ values: [Object: null prototype] { foo: 'bar' }, positionals: [] }

一文字

最初からオプションのキーが 1 文字だと short を指定しなくても -a のように指定できます

util.parseArgs({
args: ["-a", "foo", "--a", "bar"],
options: {
a: { type: "string", multiple: true },
},
})
{
values: [Object: null prototype] { a: [ 'foo', 'bar' ] },
positionals: []
}

また -abc のようにまとめることもできます

util.parseArgs({
args: ["-abc"],
options: {
a: { type: "boolean" },
b: { type: "boolean" },
c: { type: "boolean" },
},
})
{
values: [Object: null prototype] { a: true, b: true, c: true },
positionals: []
}

util.parseArgs({
args: ["-xy", "1"],
options: {
x: { type: "boolean" },
y: { type: "string" },
},
})
{
values: [Object: null prototype] { x: true, y: '1' },
positionals: []
}

positionals

デフォルトでは positional な引数は許可されていないので

node cmd-parse.js foo bar

みたいにオプションを指定しない引数があるとエラーです
allowPositionals を true にするとオプション以外の引数も受け取れます

util.parseArgs({
args: ["a", "-b", "c", "d", "-e", "f"],
options: {
b: { type: "string" },
e: { type: "boolean" },
},
allowPositionals: true,
})
{
values: [Object: null prototype] { b: 'c', e: true },
positionals: [ 'a', 'd', 'f' ]
}

-- を使うとそれ以降はオプションとしてパースせず positional な引数として扱えます

util.parseArgs({
args: ["-a", "1", "--", "-a", "2"],
options: {
a: { type: "string" },
},
allowPositionals: true,
})
{
values: [Object: null prototype] { a: '1' },
positionals: [ '-a', '2' ]
}

tokens

tokens を true に指定すると返り値の中に tokens が追加されてパースされた内部値のトークン情報も受け取れます

util.parseArgs({
args: ["a", "--bbb", "c", "-d"],
options: {
bbb: { type: "string" },
d: { type: "boolean" },
},
allowPositionals: true,
tokens: true,
})
{
values: [Object: null prototype] { bbb: 'c', d: true },
positionals: [ 'a' ],
tokens: [
{ kind: 'positional', index: 0, value: 'a' },
{
kind: 'option',
name: 'bbb',
rawName: '--bbb',
index: 1,
value: 'c',
inlineValue: false
},
{
kind: 'option',
name: 'd',
rawName: '-d',
index: 3,
value: undefined,
inlineValue: undefined
}
]
}

ドキュメントを見た感じだと 否定形みたいな順番に意味があるところで使えるようです
上に書いたように boolean タイプのオプションに false を指定できないので 有効にするオプションをつけたあとで無効にしたいなら否定形のオプションを別に用意してそれを指定するしかないです

node foo.js --enable-xxx --disable-xxx

と書いたときに enable も disable も true だとどっちを優先すればいいかわかりません
tokens だと順番も取れるので後ろにあるのが disable の方だとわかって無効化できます