読者です 読者をやめる 読者になる 読者になる

Gulp でビルドスクリプトを書く(PureScript で Hello, World!)

これらに続く第4話。前回では NPM の scripts を使ってコマンドを書いてビルドをしていたんだが、sh と cmd と2つ管理しないといけなかったり、ワンライナーなんで保守がしづらかったりと問題があるので、今回はビルドツールである Gulp を使って PureScript のコードをビルドするようにする。

おさらい

前回使ったワンライナーはこんなんだった。

bower install && psc --ffi bower_components\purescript-*\src\**\*.js bower_components\purescript-*\src\**\*.purs src\Main.purs && psc-bundle output\*\*.js --module Main --main Main -o output\main.js && copy src\index.html output\

4つのステップがあるのでそれぞれ見ていく。

bower install

bower.json に記載した依存をインストールする。

psc --ffi bower_components\purescript-*\src\**\*.js bower_components\purescript-*\src\**\*.purs src\Main.purs

PureScript ソースコードコンパイルして JavaScript に。

psc-bundle output\*\*.js --module Main --main Main -o output\main.js

CommonJS 仕様の JS コードをブラウザーで実行できるように変換する。

copy src\index.html output\

JS を読み込む HTML を成果物フォルダーにコピーする。

bower install は初回にこれをそのまま実行してもらうとして、それ以外を Gulp スクリプトに変換していく。

gulpfile.js

Gulp スクリプトは単なる JS のコードで require('gulp') すると使える内部 DSL である。今のワンライナーでは psc コマンドで生成される途中成果物と psc-bundle した後の最終成果物が同じフォルダー下にあって管理の問題上分けたいので、最終成果物は dest フォルダー下に置かれるようにする。そうすると、所望の Gulp スクリプトは下記のようになる。これを gulpfile.js として保存する。

'use strict';

var gulp = require('gulp');
var purescript = require('gulp-purescript');

var sources = [
  'src/**/*.purs',
  'bower_components/purescript-*/src/**/*.purs',
];

var foreigns = [
  'src/**/*.js',
  'bower_components/purescript-*/src/**/*.js'
];

var destination = 'dest';

gulp.task('make', function () {
  return purescript.psc({ src: sources, ffi: foreigns });
});

gulp.task('bundle', ['make'], function () {
  return purescript.pscBundle({
      src: 'output/**/*.js',
      output: destination + '/main.js',
      module: 'Main',
      main: 'Main'
  });
});

gulp.task('copy', function () {
  return gulp.src(['src/**/*.html'])
             .pipe(gulp.dest(destination));
});

gulp.task('default', ['bundle', 'copy']);

雰囲気でだいたい分かると思うが簡単に説明すると、require('gulp-purescript') で Gupl スクリプト内で PureScript 固有の記述ができるようになる。gulp.task 関数は第1引数にタスク名、第2引数に依存するタスクの配列、第3引数にタスクの内容のクロージャーを渡すようになっている。第2・第3引数は省略することができる。

このようにタスクを記述すると下記のようにタスクを実行することができるようになる。

> gulp make

引数なしで gulp とだけ実行すると default タスクが実行される。

依存の追加

新たに、Gulp を使用するようになったので package.jsondevDependenciesgulpgulp-purescript を追加する。

  "devDependencies": {
    "purescript": "0.7.2",
    "bower": "^1.4.1",
    "gulp": "^3.9.0",
    "gulp-purescript": "^0.7.0"
  }

NPM でインストールした Bower と Gulp を呼べるように package.jsonscripts を下記のようにする。当然既存のワンライナーのビルドコマンドはもう要らない。

  "scripts": {
    "bower": "bower",
    "gulp": "gulp"
  }

ビルド手順

以上をまとめるとビルド手順は下記となる。

> npm install
> npm bower install
> npm gulp

ビルドを少し速くする

今の copy タスクでは前回ビルドから変更点がなくてもコピーしてしまっているので、最終変更日時を確認して必要のあるときだけコピーするようにする。変更点は下記である。

package.json

  "devDependencies": {"gulp-newer": "^0.5.1"
  }

gulpfile.js

var newer = require('gulp-newer')
…
gulp.task('copy', function () {
  return gulp.src(['src/**/*.html'])
             .pipe(newer(destination))
             .pipe(gulp.dest(destination));
});

実行するコマンドに変更はない。