gulpの使い方:Aureliaのgulpをベースに解説

Node.js,Express環境でAurelia.jsを動かしてみる–その4 Node.js,Express環境でAurelia.jsを動かしてみる–その1 Node.js,Express環境で […]

Node.js,Express環境でAurelia.jsを動かしてみる–その4

Node.js,Express環境でAurelia.jsを動かしてみる–その1
Node.js,Express環境でAurelia.jsを動かしてみる–その2
Node.js,Express環境でAurelia.jsを動かしてみる–その3

もう少しローカル環境を整えていこうと思います。
Aurelia-Skeleton-Navigationではビルドツールにgulpを使用しています。
そしてBrowsersyncを使用してファイル変更を即時ブラウザに反映できるように環境が構築されています。
⇒参考https://www.browsersync.io/
gulpになじみにがある方は良いですがわからないとつまづくことも多いと思い、実際にAurelia.jsで使われているgulpから紐解いてみようと思います。

gulpfile.jsについて

gulpの基本的な設定はgulpfile.jsで行います。Aurelia.jsgulpfile.jsをみてみると下記のようになっています。

require('require-dir')('build/tasks');

require-dirというNodeのhelperを使ってbuild/tasksフォルダの中身を参照しています。
package.jsonを見るとdevDependenciesに記載されています。
通常はここでtask等を設定したりしますが、管理しやすく分離して使っているようです。
まずはgulp watchで実行されるwatch.jsを見てみます。

var gulp = require('gulp');
var paths = require('../paths');
var browserSync = require('browser-sync');

// outputs changes to files to the console
function reportChange(event) {
  console.log('File ' + event.path + ' was ' + event.type + ', running tasks...');
}

// this task wil watch for changes
// to js, html, and css files and call the
// reportChange method. Also, by depending on the
// serve task, it will instantiate a browserSync session
gulp.task('watch', ['serve'], function() {
  gulp.watch(paths.source, ['build-system', browserSync.reload]).on('change', reportChange);
  gulp.watch(paths.html, ['build-html', browserSync.reload]).on('change', reportChange);
  gulp.watch(paths.css, ['build-css']).on('change', reportChange);
  gulp.watch(paths.style, function() {
    return gulp.src(paths.style)
      .pipe(browserSync.stream());
  }).on('change', reportChange);
});

gulpnode.jsベースですので必要なプラグインやモジュールをrequireで指定します。

gulp
gulpを使用する際に呼び出します。
../paths
paths.jsにnodeのモジュールとして各pathが設定されています。
browser-sync
ファイルの変更とブラウザでの更新を同期させるパッケージです。ソースの変更が即時ブラウザに反映されるので作業効率が良くなります。

gulp.taskについて

gulp.task('watch', ['a', 'b']);

仮に上記のように設定されていた場合、watch,a,bという三つのタスクが実行されます。
特に指定がなければタスクはすべて並列で処理されます。

タスクの実行順序を指定したい場合
⇒参考タスクランナーgulp.js最速入門
⇒参考gulpで複数タスクの実行順序を指定したい場合

watch.jsの場合はserveタスクが実行されたのちwatchされる。
単純に順番だけ定義したい場合はrun-sequenceパッケージを使うと楽。build.jsで実際に使われているので後述します。

gulp.watchについて

ファイルの変更を検知してタスクの実行を行ないます。

gulp.watch(paths.source, ['build-system', browserSync.reload]).on('change', reportChange);

上記を解説すると、paths.sourceにある対象のファイルが変更されたら、build-systemタスクとbrowserSync.reloadを実行する。その際に、reportChange関数を呼び出します。
前述しましたがpathspaths.jsにて定義されています。

serveタスクについて

watchタスクが実行される前に実行されるserveタスクについてみてみます。

var gulp = require('gulp');
var browserSync = require('browser-sync');

// this task utilizes the browsersync plugin
// to create a dev server instance
// at http://localhost:9000
gulp.task('serve', ['build'], function(done) {
  browserSync({
    online: false,
    open: false,
    port: 9000,
    server: {
      baseDir: ['.'],
      middleware: function(req, res, next) {
        res.setHeader('Access-Control-Allow-Origin', '*');
        next();
      }
    }
  }, done);
});

今度はserveが実行される前にbuildタスクが実行されています。
buildタスクが終了すると、browserSyncを使用してローカルサーバが立ち上がる仕組みになっています。
上記設定の場合は http://localhost:9000 にアクセスすることが可能になります。
browserSyncに関しては次回もう少し掘り下げて説明します。

Access-Control-Allow-Origin

CORS関連問題の解決のため設定されています。
ブラウザのアドレスバーに表示されているドメインとは別のドメインへデータを取得しにいくときに同一生成元ポリシー(クロスドメイン制約)に引っかからないようにするためのものです。

buildタスクについて

build.js中身

var gulp = require('gulp');
var runSequence = require('run-sequence');
var changed = require('gulp-changed');
var plumber = require('gulp-plumber');
var to5 = require('gulp-babel');
var sourcemaps = require('gulp-sourcemaps');
var paths = require('../paths');
var compilerOptions = require('../babel-options');
var assign = Object.assign || require('object.assign');
var notify = require("gulp-notify");
var browserSync = require('browser-sync');

// transpiles changed es6 files to SystemJS format
// the plumber() call prevents 'pipe breaking' caused
// by errors from other gulp plugins
// https://www.npmjs.com/package/gulp-plumber
gulp.task('build-system', function() {
  return gulp.src(paths.source)
    .pipe(plumber({errorHandler: notify.onError('Error: <%= error.message %>')}))
    .pipe(changed(paths.output, {extension: '.js'}))
    .pipe(sourcemaps.init({loadMaps: true}))
    .pipe(to5(assign({}, compilerOptions, {modules: 'system'})))
    .pipe(sourcemaps.write({includeContent: true}))
    .pipe(gulp.dest(paths.output));
});

// copies changed html files to the output directory
gulp.task('build-html', function() {
  return gulp.src(paths.html)
    .pipe(changed(paths.output, {extension: '.html'}))
    .pipe(gulp.dest(paths.output));
});

// copies changed css files to the output directory
gulp.task('build-css', function() {
  return gulp.src(paths.css)
    .pipe(changed(paths.output, {extension: '.css'}))
    .pipe(gulp.dest(paths.output))
    .pipe(browserSync.stream());
});

// this task calls the clean task (located
// in ./clean.js), then runs the build-system
// and build-html tasks in parallel
// https://www.npmjs.com/package/gulp-run-sequence
gulp.task('build', function(callback) {
  return runSequence(
    'clean',
    ['build-system', 'build-html', 'build-css'],
    callback
  );
});
gulp.src()
処理するファイルを指定
.pipe
設定された順番に処理を実行
gulp.dest()
処理されたファイルが書き込まれるフォルダ

パッケージとモジュールについて

run-sequence
実行順序を指定する
gulp-changed
変更されたファイルだけ検地して反映
gulp-plumber
watchでファイル変更を監視している際にエラーなどで監視が終了しないようにする
gulp-notify
plumberとあわせて使用されている。タスク終了しない代わりに通知を出している
gulp-sourcemaps
SourceMapとは、コンパイルや圧縮されて難読化されたコードがオリジナルファイルの何行目になるのか、マッピング情報が書かれたファイル。ここではbabelで生成されたものに対してSoucemapを作成している
gulp-babel
ES6記法でかかれたものをES5にする。
babel-options
babelのoptionを指定したモジュールを読み込んでいる
object.assign
gulp-babelを読み込むタイミングで使われている。複数のオブジェクトを一つのオブジェクトにまとめている

⇒参考babel option(英語)

build.jsないのbuild-systemタスクをみてみます。

gulp.task('build-system', function() {
  return gulp.src(paths.source)
    .pipe(plumber({errorHandler: notify.onError('Error: <%= error.message %>')}))
    .pipe(changed(paths.output, {extension: '.js'}))
    .pipe(sourcemaps.init({loadMaps: true}))
    .pipe(to5(assign({}, compilerOptions, {modules: 'system'})))
    .pipe(sourcemaps.write({includeContent: true}))
    .pipe(gulp.dest(paths.output));
});

処理するソースはpaths.sourceでここではsrc/以下のjsファイルになります。(paths.jsで指定されている)
上から簡単に書くと下記のようになります。

エラーの処理

src/以下のjsファイルの変更の検知

ES6からES5へ変換

paths.output(distフォルダ)への書き出し

serveタスクから呼び出されているbuildタスクが実行されるとrunSequenceにより実行順が定義されclean,実行の後、build-system, build-html, build-css 左記三つのタスクが並列で実行されています。

gulp.task('build', function(callback) {
  return runSequence(
    'clean',
    ['build-system', 'build-html', 'build-css'],
    callback
  );
});

gulp watchが実行されたときに行われるタスクを追ってきましたがBrowserSyncと連動してなかなか良い環境が構築されていると思います。
説明足らずでわかりにくいかもしれません。ご了承ください。