yargsを使ってタスク自動化ツールのコマンドにオプションを指定する

Node.js

Grunt や gulp を使っていると、タスクにオプションを渡して処理を切り分けたいっていう欲求が出てくる。そういう時はyargsを使えば、コマンドにオプションを指定することでそれをタスク側で受け取ることができるようになる。

npm install --save-dev yargsしたのち、タスクを書いてる JS ファイル(Gruntfile.js とか gulpfile.js とか)で、

var yargs = require("yargs").argv;

とやって、タスクの実行で

$ gulp sass --device=pc

とすると、タスクを書いてる JS 内でyargsにキーと値がオブジェクトで格納されるので、

yargs.device  // → "pc"

という按配に取得できる。便利。

オプションは複数渡せるし、真偽値にもできる。試しに以下のようなタスクを書いて実行してみる。

gulp.task("test", function() {
  console.log(yargs);
});
$ gulp test --name=oti --male --female=false --"rain bringer" --note="oti is not real name."

結果はこちら。

[11:34:21] Using gulpfile ~/develop/mbrs/test-yargs/gulpfile.js
[11:34:21] Starting "test"...
{ _: [ "test" ],
  name: "oti",
  male: true,
  female: "false",
  "rain bringer": true,
  note: "oti is not real name.",
  "$0": "/Users/tkg/.nodebrew/current/bin/gulp" }
[11:34:21] Finished "conslog" after 1.83 ms

yargsに渡されたオブジェクトにいろいろ入る。見づらいので整形。

{
  _: [ "test" ],
  name: "oti",
  male: true,
  female: "false",
  "rain bringer": true,
  note: "oti is not real name.",
  "$0": "/Users/tkg/.nodebrew/current/bin/gulp"
}

値を指定せずにつけた--maletrueとなる。

値を指定すると全て文字列になる。なので、--female=falseとしてもブーリアンのfalseにはならず文字列の"false"となる。ここは注意が必要かも。

--"rain bringer"の結果を見るとキーを文字列にすることもできる。

\_にタスク名、"\$0"に gulp の元ファイルのディレクトリも格納されるようだ。面白そう。

オプションの有無で if 文を作れるというだけでもありがたい。

gulp での具体的な例

もうちょっと具体的な例にすると以下のようなこんな感じ。自分で使ったのが gulp だったので、gulpfile.js のサンプル。

"use strict";

// load plugins
var gulp = require("gulp");
var sass = require("gulp-sass");
var autoprefix = require("gulp-autoprefixer");
var yargs = require("yargs").argv;

// config
var config = {
  pc: {
    src: "./src/pc/",
    dest: "./release/pc/",
    browsers: ["last 2 versions", "ie > 7"]
  },
  sp: {
    src: "./src/sp/",
    dest: "./release/sp/",
    browsers: ["last 3 versions", "android > 1"]
  }
};

// Sass
gulp.task("sass", function() {
  return gulp.src(config[yargs.device].src + "sass/*.scss")
    .pipe(sass({
      outputStyle: "compressed"
    }))
    .pipe(autoprefix({
      browsers: config[yargs.device].browsers,
      cascade: false
    })
    .pipe(gulp.dest(config[yargs.device].dest + "css"));
});

で、タスクの実行はこんな感じ。

$ gulp sass --device=pc

こうすると PC 用の sass ファイルをコンパイルして PC 向けの autoprefixer に通して PC 用のディレクトリに出力できる。SP 用ならオプションを--device=spとすればよい。

PC/SP 両方を開発してる時って、同じディレクトリ構成にすることが多いと思うんだけど、それなのに source と destination が違うがためにタスクをたくさん書くことになりがち。PC の SP もまとめて処理するようなタスクにするのは watch でつらいしそもそも無駄がある。gulp.task("sass:pc", ...とかgulp.task("sass:sp", ...とかでネームスペースを分けて 2 倍書くのもめんどうくさい。

オプションで指定すれようにすれば、仮にディレクトリ構成が違ってもそれを吸収できる config を作ればいいだけなので gulpfile.js に対するストレスは減る。

ただデメリットも当然あって、タスクによってオプションをつけるのが前提になるというのが、これはこれでまたストレスになり得る。どんなキーにどんな値が必要なのかを JS ファイル内にコメントで書いておくとかしないと 1 日で忘れる。

そこで、npm run-script を使う。

npm run-script でオプション付きのタスクをラップする

npm run-script がなんであるかは他のブログでいろいろ書かれているので割愛。「npm run-script」で検索すれば良い記事がたくさんでてくるのでぜひ見てみてほしい。

今回はオプション指定をラップするタスクを npm run-script で作ればよい。

package.json で

"scripts": {
  "sass:pc" : "gulp sass --device=pc",
  "sass:sp" : "gulp sass --device=sp"
}

こうして、コマンドラインでの実行は

$ npm run sass:sp

こう。

結局\*\*\*:spなのかいと言うことなかれ、npm run-script でラップすることでタイプ数自体も少なくなるし、Grunt なのか gulp なのかを気にする必要もなくなる。"preinstall""start"なども整えれば、新規に加わったメンバーには「npm iしてnpm startでオッケ〜」と伝えればよい。細かくタスクを実行したいなら package.json か Gruntfile.js もしくは gulpfile.js を見てもらえばいいという具合になる。


yargs を使って npm run-script の良さがわかった。僕はそんなに Grunt/gulp に消耗してはいないつもりだったのだけど、どうやらちゃんと消耗していたらしい。