LoginSignup
11
10

More than 5 years have passed since last update.

gulp を Visual Studio Code から使う

Last updated at Posted at 2016-07-28

Visual Studio Code ではビルド等のタスクを実行するために外部コマンドを呼び出します。以前の記事 では cmd.exe を叩くという原始的な方式でしたが、勉強がてら gulp.js に乗り換えました。

今回のタスクは以下になります。素朴なスタンドアロンの単体アプリです。(プロジェクト全体は GitHub にあります。)

  1. build: 複数の TypeScript (.ts) ファイルをコンパイルして、単体の JavaScript (.js) ファイルを生成する。
  2. test: デバッグ用に、生成した .js ファイルを使う .html をブラウザで開く。
  3. release: リリース用に、生成した .js ファイルを圧縮 + 最適化する。
  4. clean: 中間ファイルを一括で削除する。

必要なモジュールの収集

まず最初に npm initpackage.json を生成し、npm install --save-dev で依存関係を追加しながら必要なモジュールを集めます。次回以降、まっさらな状態から再度環境を構築する際に npm install だけで必要なモジュールをインストールできるようになります。

java -version  # 前もってJavaを導入しておく。closure compiler用。

npm init       # 設問には適当に答える

npm install --save-dev typescript
npm install --save-optional tslint

npm install --global gulp-cli
npm install --save-dev gulp
npm install --save-dev gulp-closurecompiler
npm install --save-dev gulp-insert
npm install --save-dev gulp-newer
npm install --save-dev gulp-open
npm install --save-dev gulp-typescript
npm install --save-dev del

JavaScriptの圧縮 + 最適化には選択肢がありますが、圧縮率が高く、積極的な最適化を見込める Google Closure Compiler を使うことにしました。また、gulp と連携するための拡張モジュールも、これまた何種類かありますが、モジュールのインストール時に compiler.jar を一緒にダウンロードしてくれて楽という理由から、gulp-closurecompiler にしました。

タスクの作成

タスク本体は gulpfile.js に記述しますが、 Visual Studio Code から gulp を呼び出すために tasks.json も必要です。なお、tasks に1つ1つタスク名を書かなくても、VSCode は gulpfile.js のタスクを認識してくれるらしく、workbench.action.tasks.runTask の一覧に表示され、実行もできました。

tasks.json

.vscode/tasks.json
{
    "version": "0.1.0",
    "command": "gulp",
    "isShellCommand": true,
    "showOutput": "never",
    "suppressTaskName": false,
    "args": [ "--no-color" ],
    "tasks": [
        {
            "taskName": "build",
            "showOutput": "silent",
            "isBuildCommand": true,
            "isWatching": false,
            "problemMatcher": [
                "$tsc",
                "$gulp-tsc"
            ]
        }
    ]
}

problemMatcher の指定には、いまいち確信を持てていません。

build

TypeScriptをJavaScriptにコンパイルするタスク buildgulpfile.js に記述します。build という名前のタスクは、Visual Studio Code から workbench.action.tasks.build としてワンボタンで実行できます。
tsconfig.json を使う場合には gulp.src() ではなく ts.createProject() を使うとのこと。
また、コンパイル前に gulp-newer を挟み、.ts ファイルに更新がない場合はコンパイルをスキップします。今回は、複数の入力ファイルを1個の出力ファイルにする処理なので、newer() の引数は出力ファイル名を指定します。一般的な、入力と出力が1対1対応の場合は出力ディレクトリを指定します。

gulpfile.js
var newer = require("gulp-newer");
var ts = require("gulp-typescript");

gulp.task("build", function () {
    var project = ts.createProject("./src/tsconfig.json", {
        outFile: OUT_NAME
    });
    return project.src().
        pipe(newer(DEBUG_DIR + OUT_NAME)).
        pipe(ts(project)).
        pipe(gulp.dest(DEBUG_DIR));
});

test

HTMLファイルをブラウザで開くタスク test です。こちらも workbench.action.tasks.test として実行できます。
依存関係に build を追加し、必要に応じてコンパイル後に gulp-open を使ってHTMLを開きます。もちろん start (Windows), sensible-browser (Linux) が手っ取り早いですが、移植性や統一感から、こういうやり方も良いかなと思います。

gulpfile.js
var open = require("gulp-open");

gulp.task("test", ["build"], function () {
    gulp.src("./index-debug.html").pipe(open());
});

release

タスク releasebuild で作ったJavaScriptファイルを加工して最適化します。Closure Compilerに渡す前に、コード全体を (function() {})(); で包んで、より積極的な最適化を促します。同時に、var NDEBUG=true を定義して動作確認のためのコードを削って欲しい旨も伝えます。
なお、buildtest 以外のタスクは workbench.action.tasks.runTask でタスク一覧を表示させてから選択して実行する必要があります(もしくは端末から gulp release)。

gulpfile.js
var insert = require("gulp-insert");
var minify = require("gulp-closurecompiler");

gulp.task("release", ["build"], function () {
    return gulp.
        src(DEBUG_DIR + OUT_NAME).
        pipe(newer(RELEASE_DIR)).
        pipe(insert.wrap("(function() {\nvar NDEBUG=true;\n", "\n})();")).
        pipe(gulp.dest(WRAPPED_DIR)).
        pipe(minify({ fileName: OUT_NAME })).
        pipe(gulp.dest(RELEASE_DIR));
});

さて、ここが今回のハマり所だったのですが、gulp-closurecompiler はパイプラインで加工中のコードを処理できず、ディスク上に実体のあるファイルが必要でした。そのため、途中で gulp.dest() を挟んで中間ファイル WRAPPED_DIR ディレクトリに書き出しています1。gulp本体はパイプラインを使った流れ作業を推奨する設計思想だと思うのですが、呼び出す外部プログラムや拡張モジュールが対応していなければ仕方ないということでしょうか……。

また、元ファイルに変更がなければ処理をスキップできるよう newer を加えていますが、処理対象がない場合に gulp-closurecompiler がクラッシュしました。モジュール自体の不具合と思われ、下記の1行を追加することで解決しました。gulpfile.js の書き方で回避できるならそうしたいのですが……。

gulp-closurecompiler.js
        beforeEnd = function() {
+           if (files.length === 0) { this.queue(null); return; }

clean

タスク clean は中間ファイルを削除します。gulpで rm -rf 相当を行う標準的な書き方だそうです。

gulpfile.js
var del = require("del");

gulp.task("clean", function(cb) {
    del([BIN_DIR, DEBUG_DIR], cb);
});

さいごに

Visual Studio Code のタスクを gulp で管理できるようになりました。タスクの依存関係を定義し、ファイルの最終更新日時をチェックして必要な場合のみ処理できたので、古来からの Makefile 程度にはなりました。
cmd.exe ベタ書きと比べて、新しいことをできるようになったわけではないですが、少なくともWindowsに完全に依存していたことは脱却できました。パイプラインの連結により効率的に処理でき、タスクの並列化もしやすいようなので、より複雑で時間のかかるケースではもっと利点が生きると思われます。


  1. 当初は gulp-rename を使っていましたが、複数の入力ファイルがある一般的にはなケースを考えると、別ディレクトリを用意する(もしくは rename で拡張子のみ変更する)ほうが汎用性が高いと思われます。 

11
10
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
10