フロントエンドエンジニアになりたい

デザイナーなのかコーダーなのかディレクターなのかよくわからない人のブログ。趣味で絵を描きました(過去形)

gulp+browserifyでSassの@import風にrequireするレシピ

どこにも需要なさそうですが、browserifyでビルドされるJSファイル側からの指定だけで読み込みをしてみたかったのでメモです。 (いきなりルー語ですみません。。。タイトルつけるセンスが欲しいです)

なぜSassの@import風かというと、読まれるだけのファイルを出力しないために、 _filenameみたいに先頭にアンダースコアつけたファイルを出力しないSassのルールを借用したからです。

この方法は一つの静的なタスクで、スタンドアロンなJSファイルをたくさんビルドしたい時に便利かもです。

gulpfile

var gulp = require('gulp');
var browserify = require('browserify');
var transform = require('vinyl-transform');
var src = './src/**/[!_]*.js'; // _で始まるファイルを除外する
var dist = './dist'

var browserified = transform(function(filename) {
  var b = browserify(filename);
  return b.bundle();
});

gulp.task('build-js', function() {
  return gulp.src(src)
    .pipe(browserified)
    .pipe(gulp.dest(dist));
});

gulp.srcに渡すパスはglobなのでメタキャラクタやワイルドカードが使えます。

browserifyが扱うファイルストリームはReadableStreamで、gulp.srcのvinyl-fsと異なるため、 vinyl-transformでラップして処理しています。gulp-browserifyというのもありますが、公式では非推奨とのこと。

ビルド前

src/
├─ app1.js
├─ _component1.js
├─ app2.js
└─ _component2.js

src/app1.js

var component1 = require('./_component1');

src/app2.js

var component2 = require('./_component2');

src/_component1.js

module.exports = function () {
  return function(){
    // 何か
  };
};

src/_component2.js

module.exports = function () {
  return function(){
    // 何か
  };
};

ビルド後

_component1.js_component2.jsは出力されない。

dist/
├─ app1.js
└─ app2.js

dist/app1.js

(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
module.exports = function () {
  return function(){
    // 何か
  };
};
},{}],2:[function(require,module,exports){
var component1 = require('./_component1');
},{"./_component1":1}]},{},[2]);

dist/app2.js

(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
module.exports = function () {
  return function(){
    // 何か
  };
};
},{}],2:[function(require,module,exports){
var component2 = require('./_component2');
},{"./_component2":1}]},{},[2]);

注意点

requireされたファイルはクロージャ(無名関数)内に閉じ込められるところが、Sassの@importとは大きく異なります。

module側でglobalに突っ込んで、require('./_component1')();みたいな感じにすぐに実行するともっとSassっぽくなると思いますが、CommonJSのルールを無視しすぎているので、ちょっとどうかなと思いました。

参考