gulp.spritesmith の対象ディレクトリを自動取得する

以前書いた記事

Grunt から gulp へ移行してみた:
https://bunlog.dreamseeker.dev/2014/06/08/gulpjs-setting/

を読み返してみて、そういえば gulp.spritesmith の対象ディレクトリを自動取得できるようにしてたな・・・と思い出したので、記事にしてみます。

gulp.spritesmith のインストール

パッケージがインストールされていることが前提となりますので、下記コマンドを実行します。

$ npm install gulp.spritesmith --save-dev

プラグインを読み込む

gulpfile.js では gulp.spritesmith を $.spritesmith で実行できるよう、gulp-load-plugins を読み込む際に置換しつつ変数にセットしておきます。

// load gulp.js & plugins
var gulp = require('gulp'),
    $    = require('gulp-load-plugins')({
             pattern: ['gulp-*', 'gulp.*'],
             replaceString: /\bgulp[\-.]/
           });

上記の例だと、gulp- もしくは gulp. ではじまるプラグインが対象となります。

ディレクトリ一覧を取得するための関数を定義

よい方法がないかなと探していたところ、Stack Overflow で気になる話題を見かけました。

node.js - Get all directories within directory nodejs - Stack Overflow:
http://stackoverflow.com/questions/18112204/get-all-directories-within-directory-nodejs

こちらを参考に、指定したディレクトリ直下のディレクトリ一覧を取得する関数を定義します。

// load Node.js API
var fs   = require('fs'),
    path = require('path');

// function.getFolders
var getFolders = function (dir) {
  return fs.readdirSync(dir)
           .filter(function (file) {
              return fs.statSync(path.join(dir, file)).isDirectory();
           });
}

File System の readdirSync を利用しているため、戻り値は配列になるようです。

fs.readdirSync(path) - Node.js v0.10.34 Manual & Documentation:
http://nodejs.org/api/fs.html#fs_fs_readdirsync_path

gulp.spritesmith のタスクを調整

名称を変更した際の手間を省くため、あらかじめ取得や出力の対象になるディレクトリ名を変数にセットしておきます。

// directory
var dir  = {
      source: 'source',
      scss:   'scss',
      img:    'img',
      sprite: 'sprite'
    };

タスクの定義では、前述の getFolders 関数を利用しつつ、JavaScript の map 関数でループ処理を行います。

// task.sprites
gulp.task('sprites', function () {
  // set target folders
  var folders = getFolders(dir.source + '/' + dir.img + '/sprite/');

  // generate image & sass files
  folders.map(function (folder) {
    var spriteData = gulp.src('sprite/' + folder + '/*.png', {cwd: dir.source + '/' + dir.img})
                         .pipe($.spritesmith({
                            imgName:   'sprite-' + folder + '.png',
                            imgPath:   '../' + dir.img + '/sprite-' + folder + '.png',
                            cssName:   folder + '.scss',
                            algorithm: 'binary-tree',
                            padding:   4,
                            cssFormat: 'scss'
                         }));

    spriteData.img.pipe(gulp.dest(dir.source + '/' + dir.img));
    spriteData.css.pipe(gulp.dest(dir.source + '/' + dir.scss + '/' + dir.sprite));
  });
});

タスクの実行

下記コマンドを実行すると、プロジェクト直下の source/img に sprite-[ディレクトリ名].png、source/scss/sprite に [ディレクトリ名].scss が生成されます。

$ gulp sprites

やってみて

以前の定義だと、プロジェクトごとにディレクトリの配列を調整しないといけなかったのですが、やはり自動化できると便利ですよね。

なお、夏の終わり頃に調整した内容をまとめているため、現時点ではもっと効率的な手法があるかもしれません w

おまけ

自分で判りやすいようにコメントなどを附記した、今回の gulpfile.js のサンプルです。

// ---------------------------------
// config
// ---------------------------------
// load gulp.js & plugins
var gulp = require('gulp'),
    $    = require('gulp-load-plugins')({
             pattern: ['gulp-*', 'gulp.*'],
             replaceString: /\bgulp[\-.]/
           });

// load Node.js API
var fs   = require('fs'),
    path = require('path');

// directory
var dir  = {
      source: 'source',
      scss:   'scss',
      img:    'img',
      sprite: 'sprite'
    };

// ---------------------------------
// Functions
// ---------------------------------
// function.getFolders
var getFolders = function (dir) {
  return fs.readdirSync(dir)
           .filter(function (file) {
              return fs.statSync(path.join(dir, file)).isDirectory();
           });
}

// ---------------------------------
// Tasks
// ---------------------------------
// task.sprites
gulp.task('sprites', function () {
  // set target folders
  var folders = getFolders(dir.source + '/' + dir.img + '/sprite/');

  // generate image & sass files
  folders.map(function (folder) {
    var spriteData = gulp.src('sprite/' + folder + '/*.png', {cwd: dir.source + '/' + dir.img})
                         .pipe($.spritesmith({
                            imgName:   'sprite-' + folder + '.png',
                            imgPath:   '../' + dir.img + '/sprite-' + folder + '.png',
                            cssName:   folder + '.scss',
                            algorithm: 'binary-tree',
                            padding:   4,
                            cssFormat: 'scss'
                         }));

    spriteData.img.pipe(gulp.dest(dir.source + '/' + dir.img));
    spriteData.css.pipe(gulp.dest(dir.source + '/' + dir.scss + '/' + dir.sprite));
  });
});