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

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/',
        browserSupport: ['last 2 versions', 'ie >= 8']
    },
    sp: {
        src: './src/sp/',
        dest: './release/sp/',
        browserSupport: ['last 3 versions', 'android >= 2']
    }
};

// Sass
gulp.task('sass', function() {
    return gulp.src(config[yargs.device].src + 'sass/*.scss')
        .pipe(sass({
            outputStyle: 'compressed'
        }))
        .pipe(autoprefix({
            browsers: config[yargs.device].browserSupport,
            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に消耗してはいないつもりだったのだけど、どうやらちゃんと消耗していたらしい。