npm-scripts で引数を間に埋め込みたい
◆ scripts に指定するコマンドで引数を最後じゃなくて間に埋め込みたい
◆ 調べても環境変数を指定するようなやり方
◆ 環境変数の使い方はシェルに依存するし 環境変数の設定自体もこの機能でやりたいこと
◆ 自作した
◆ 調べても環境変数を指定するようなやり方
◆ 環境変数の使い方はシェルに依存するし 環境変数の設定自体もこの機能でやりたいこと
◆ 自作した
npm-scripts
package.json を{
"scripts": {
"foo": "echo foo"
}
}
と書くと
yarn run foo
を実行すれば foo に書いたコマンドが実行できます
C:\tmp\a>yarn run foo
yarn run v1.22.4
$ echo foo
foo
Done in 0.11s.
引数を渡すとその後ろに付きます
C:\tmp\a>yarn run foo bar
yarn run v1.22.4
$ echo foo bar
foo bar
Done in 0.09s.
やりたいこと
npm-scripts を多用しているパッケージでは この引数を後ろ以外の場所に埋め込みたいときがときどきあります例えば foo にこういうコマンドを割り当てる場合に
command1 arg1 arg2 && command2 arg3 arg4
arg1 と arg3 を使う側で指定したいです
yarn run foo bar baz
が
command1 bar arg2 && command2 baz arg4
のようになってほしいです
これくらい標準でできてほしいのですけど サポートされてないみたいです
ネットで探してみると Stackoverflow では環境変数を使って
{
"foo": "command $FOO arg2"
}
FOO=BAR yarn run foo
という方法が紹介されてるくらいです
環境変数周りは shell 依存になるのでできれば避けたい方法です
npm-scripts は OS や shell 問わず動いてほしいです
そもそも 環境変数を指定するためにこの機能を使いたいことがあるくらいです
{
"start": "mode=$mode node foo.js"
}
yarn run start dev
これで mode に dev を設定する感じです
手動で埋め込む
標準機能にないので自分で用意しますscripts に設定した部分はそのまま呼び出されるので 文字列としていったん別のコマンドに渡して埋め込み処理と実行はそこでやってもらいます
{
"foo": "node scripts/run.js \"node print.js {1} arg2 && node print.js {2} arg4\""
}
これを
yarn run foo bar baz
のように使います
実行されるコマンドはこうなります
node scripts/run.js "node print.js {1} arg2 && node print.js {2} arg4" bar baz
scripts/run.js がいったん実行されるスクリプトです
ここで埋め込みを行ってコマンドを実行します
const [comm, ...args] = process.argv[2].replace(/\{(\d+)\}/g, (_, x) => process.argv[~~x + 2]).trim().split(/\s+/)
require("child_process").spawn(comm, args, { shell: true, stdio: "inherit" })
1つ目の引数の文字列中の 「{1}」 のようなところに残りの引数から埋め込みを行ってます
今回の場合に実行されるコマンドは
node print.js bar arg2 && node print.js baz arg4
となります
print.js は引数を出力する処理にしておきます
[print.js]
console.log(process.argv.slice(2))
これで実行した結果はこうなりました
C:\tmp\a>yarn run foo bar baz
yarn run v1.22.4
$ node scripts/run.js "node print.js {1} arg2 && node print.js {2} arg4" bar baz
[ 'bar', 'arg2' ]
[ 'baz', 'arg4' ]
Done in 0.35s.
見やすくする
動きましたが scripts/run.js をパッケージごとに用意しないといけないですそれに package.json の scripts 中に書くコマンドはエスケープが必要で読むのも書くのもつらいです
なので もう少し見やすく書けるように変更して パッケージ化しました
exsc
インストールすると exsc コマンドが使えるようになるので scripts には exsc コマンドと名前を記述します
また package.json に exsc プロパティを追加して 名前ごとの実行コマンドを記述します
{
"scripts": {
"foo": "exsc foo",
"bar": "exsc bar"
},
"exsc": {
"foo": "node print.js {1} {2} {p1} {p1} {p2} last",
"bar": {
"command": "node print.js {1} && node print.js {a} arg1",
"defaults": {
"1": "111",
"a": "aaa"
}
}
},
"devDependencies": {
"exsc": "gist:cc7ad5bc1a8417b57accb254f31be6a1"
}
}
scripts と exsc で名前を揃えてますが 別に揃える必要はありません
また foo のように直接コマンドを書いても良いですが bar のように command と defaults プロパティを用意してデフォルト値を書くこともできます
あとから考えると scripts 側に引数として書いておけばデフォルト値になるので foo のようなコマンドだけ書ければ十分だったように思います
実行してみた結果です
C:\tmp\a>yarn foo A B --p1 C --p2 D
yarn run v1.22.4
$ exsc foo A B --p1 C --p2 D
[ 'A', 'B', 'C', 'C', 'D', 'last' ]
Done in 0.28s.
{p1} に埋め込むものを指定するには 「--p1 C」 のようになります
「-」 の数は一つ以上ならいくつでも構いません
「--p=X」 形式には対応していません
{1} のように数値のものは 「--1 abc」 のようにも書けますが 「--」 をなしで書けば自動で連番が振られます
この例だと 1 が A で 2 が B になっています
デフォルト値付きの bar はこうなります
C:\tmp\a>yarn run bar
yarn run v1.22.4
warning package.json: No license field
$ exsc bar
[ '111' ]
[ 'aaa', 'arg1' ]
Done in 0.35s.
引数を指定すると
C:\tmp\a>yarn run bar -a AAA
yarn run v1.22.4
warning package.json: No license field
$ exsc bar -a AAA
[ '111' ]
[ 'AAA', 'arg1' ]
Done in 0.36s.
aaa が AAA に変わってますね
foo のほうでも scripts の exsc コマンドのところでデフォルト値を書けます
{
"foo": "exsc foo --p1 xxx -p2 yyy",
}
に変更します
C:\tmp\a>yarn run foo AAA BBB -p2 DDD
yarn run v1.22.4
warning package.json: No license field
$ exsc foo --p1 xxx -p2 yyy AAA BBB -p2 DDD
[ 'AAA', 'BBB', 'xxx', 'xxx', 'DDD', 'last' ]
Done in 0.27s.
p1 はデフォルト値が使われて p2 は指定したほうが使われます