Webアプリ開発における日々の煩雑な作業を自動化してくれるツールである「gulp」の基本を今回は見ていこう。
powered by Insider.NET
前回はデスクトップアプリをJavaScriptで開発するためのライブラリを二つ紹介した。今回と次回は「ビルドシステム」あるいは「タスクランナー」と呼ばれるライブラリを二つ紹介しよう。今回はVisual Studio 2015のASP.NET 5アプリのプロジェクトテンプレートでも標準で使われているgulpを取り上げる。
Webアプリ開発時には、その過程でさまざまな作業が必要になる。例えば、JavaScriptコードの最小化/難読化や、CSSプリプロセッサーによるCSSメタ言語のコンパイル(とその最小化)などがそうだ。こうしたことを自動化してくれるのが、Webアプリ開発におけるビルドシステムやタスクランナーと呼ばれるツールの役割だ。
有名なところでは、本稿で取り上げるgulp*1と次回に取り上げるGruntがある。この他にもBroccoli.js、Brunchなどのツールがある。
*1 gulpのFAQによれば「gulp is always lowercase. The only exception is in the gulp logo where gulp is capitalized」(gulpは常に小文字表記。例外はロゴで、これは先頭が「G」になっている)とあるので、本稿ではこれに従い「gulp」と表記する。
gulpは、Node.js上に実装されている「ストリーミング」ビルドシステムだ。ここでいう「ストリーミング」とは、ある入力を処理した結果(出力)を、次の処理の入力とすることで全体としての処理を進めていく概念だ(UNIXのパイプ処理を使い慣れている方であれば、よくご存じの概念だろう)。
gulpはVisual Studio 2015(以下、VS 2015)のASP.NET 5アプリのプロジェクトテンプレートでも標準で使用されるようになっている。そこで、ASP.NET 5のプロジェクトテンプレートで自動生成されるgulpのスクリプトを見ながら、その特徴を見てみよう。
VS 2015の[新しいプロジェクト]ダイアログで[ASP.NET Web アプリケーション]を選択し、次に表示される[新しい ASP.NET プロジェクト]ダイアログで[Web Application]を選択して、ASP.NET 5アプリのプロジェクトを新規に作成すると、次に示すgulpfile.jsファイルを含んだプロジェクトが生成される(ここではASP.NET 5 RCを利用している。以前はGruntが使われていたのがgulpに変更されたので、RTM版になっても恐らくはgulpが使われ続けると思われる)。
"use strict";
var gulp = require("gulp"),
rimraf = require("rimraf"),
concat = require("gulp-concat"),
cssmin = require("gulp-cssmin"),
uglify = require("gulp-uglify");
…… 省略 ……
gulp.task("min:js", function () {
return gulp.src([paths.js, "!" + paths.minJs], { base: "." })
.pipe(concat(paths.concatJsDest))
.pipe(uglify())
.pipe(gulp.dest("."));
});
gulp.task("min:css", function () {
return gulp.src([paths.css, "!" + paths.minCss])
.pipe(concat(paths.concatCssDest))
.pipe(cssmin())
.pipe(gulp.dest("."));
});
gulp.task("min", ["min:js", "min:css"]);
このようにgulpでは、JavaScriptコードを使って、その処理内容を記述していくのが大きな特徴である(gulpと並んでメジャーなタスクランナーであるGruntでは、JSON形式に構成を記述していくのが一般的だ)。冒頭部分では、gulp本体とgulpで使用するプラグインをスクリプトに導入している(requireメソッド)。プラグインは単一の処理を行うもので、これをpipeメソッドでつないでいくことで、gulpは全体として一つのタスクを処理する。
以下ではこのスクリプトをもう少し詳しく見ていこう
タスクの定義にはgulp.taskメソッドを使用する。その基本構文は次のようになる。
gulp.task("タスク名", function () {
そのタスクで実行する処理
});
「min:js」と「min:css」の二つのタスクはこの形式でタスクを定義している。
gulp.task("min:js", function () {
return gulp.src([paths.js, "!" + paths.minJs], { base: "." })
.pipe(concat(paths.concatJsDest))
.pipe(uglify())
.pipe(gulp.dest("."));
});
なお第2引数には、そのタスクが依存するタスク名を配列として指定できる。上記のgulpfile.jsファイルの最終行はこの形式でタスクを定義している。
gulp.task("min", ["min:js", "min:css"]);
次に「min:js」タスクを例に、タスク定義で行う処理をどう記述していくかを見てみる。
「min:js」タスクでは次のような処理が記述されていた。
return gulp.src([paths.js, "!" + paths.minJs], { base: "." })
.pipe(concat(paths.concatJsDest))
.pipe(uglify())
.pipe(gulp.dest("."));
「gulp.src(...)」以降を「return」している理由は後で説明する。まずは、実際に行う処理のスタート地点となるgulp.srcメソッドから見ていこう。
gulp.srcメソッドはnode-glob形式で記述された処理対象のファイル名とオプションを受け取る(後者は省略可能)。そして、名前がマッチしたファイルを「vinyl」(仮想ファイルフォーマット)形式のストリームとして生成する。
ここではファイル名として「paths.js」と「"!" + paths.minJs」が配列の形で渡されている(ファイル名を一つだけ指定するのであれば、配列にする必要はない)。なお、paths.jsとpaths.minJsは上記のリストで省略した部分でその値が次のように定義されている。
paths.minJsについては、「"!" + paths.minJs」と、その先頭に「"!"」が付加されている。「"!"」は否定を意味し、ここでは処理対象のファイルにpaths.minJSにマッチするものを含まないように指定していることになる。
node-glob形式についての詳しい説明は省略するが(リンクを参照されたい)、要するに「min:js」タスクでは「./wwwroot/js」ディレクトリ以下にある「*.min.js」ファイルを除く全ての「*.js」ファイルを処理対象として、それらのストリームを生成するという意味になる。
そして、生成されたストリームは、pipeメソッドによってconcatプラグインへと引き渡される。そこで、次にパイプによるストリーム処理を見ていこう。なお、実際のASP.NET 5アプリのデフォルトのディレクトリ構成を見ると、処理対象のJavaScriptファイルは以下の一つだけだ。
生成されたストリームは、pipeメソッドを使ってconcatプラグインへと送られる。「concat」(連結)という名前の通り、これは複数のファイルを一つにまとめてくれるプラグインだ。concatプラグインに渡している引数paths.concatJsDestは結合後のファイル名で「js/site.min.js」となっている(省略部分で定義)。
.pipe(concat(paths.concatJsDest))
.pipe(uglify())
.pipe(gulp.dest("."));
連結されたファイルは次に最小化(あるいは難読化)を行うuglifyプラグインへ送られる。処理が行われたストリームは最後にgulp.destメソッドへと送られる。
gulp.destメソッドは指定されたディレクトリへとファイルを書き込む(そして、その内容をさらにストリームとして出力するので、ここから処理をさらに進めることも可能だ。これは、複数のJavaScriptファイルを連結したものを一度ファイルとして書き込んでから、今度はそれをuglifyプラグインで処理して「.min.js」ファイルとして保存するといった場合に便利に使える)。
gulp.destメソッドには引数として「"."」(カレントディレクトリ)を指定している。実際には、指定されたディレクトリに対して、ファイルの相対パスを追加した位置に書き込みが行われる。この場合は「./wwwroot/js」ディレクトリ以下にファイルが作成されることになる。詳細については「gulp API docs」ページを参照してほしい。
「min:css」タスクも同様な処理を行っている。また、ここでは紹介を省略している「clean:js」「clean:css」「clean」の三つのタスクはnpmのrimrafパッケージを使用して、結合/最小化されたファイルの削除を行っている(これはgulpのプラグインではない)。
だいたいの説明が終わったので、実際にこれを実行してみよう(ただし、デフォルトではsite.jsファイルの中身はコメントだけで空に等しいので、ファイルが実際に作成されることだけを確認することになる)。
Copyright© Digital Advantage Corp. All Rights Reserved.