Browserifyでbundleしたファイルをconcatする

Browserifyでrequireじゃなくて単にconcatしたいときがある。UMD対応してないライブラリを読み込むときとか、必ず最初に実行したい処理(ポリフィルとか)がある場合だ。

Webpackだと、以下のようにentryに配列を指定すればconcatできる。

module.exports = {
  entry: [
    'picturefill',
    'src/js/index.js',
  ],
  output: {
    filename: 'bundle.js',
    path: './dist',
  },
}

とはいえBrowserifyを使いたい。以下のようにしたら、Gulpでストリームとしていい感じに捌けた。

const gulp = require('gulp')
const gutil = require('gulp-util')
const sourcemaps = require('gulp-sourcemaps')
const concat = require('gulp-concat')
const uglify = require('gulp-uglify')
const mergeStream = require('merge-stream')
const browserify = require('browserify')
const source = require('vinyl-source-stream')
const buffer = require('vinyl-buffer')

export const js = () => {
  const ENTRY_FILES = [
    'node_modules/picturefill/dist/picturefill.js',
  ]

  const bundler = browserify('src/js/index.js', {
    debug: true,
  })

  const bundle = () => mergeStream(
    gulp.src(ENTRY_FILES),
    bundler
      .bundle()
      .on('error', err => gutil.log('Browserify Error', err))
      .pipe(source('index.js'))
      .pipe(buffer()),
  )
    .pipe(sourcemaps.init({loadMaps: true}))
    .pipe(concat('app.js'))
    .pipe(uglify({preserveComments: 'license'}))
    .pipe(sourcemaps.write('.'))
    .pipe(gulp.dest('dist/js'))

  return bundle()
}

watchも含めるとこんな感じになる。


追記:上記のやり方だとStreamの解決順にconcatされてしまうので絶対に安全では無い。Browserifyでbundleするよりもgulp.srcに時間がかかることはまずないけど、不安になるコードだった。gulp-headerとかでやったら良さそう。

const fs = require('fs')
const gulp = require('gulp')
const gutil = require('gulp-util')
const sourcemaps = require('gulp-sourcemaps')
const header = require('gulp-header')
const uglify = require('gulp-uglify')
const browserify = require('browserify')
const source = require('vinyl-source-stream')
const buffer = require('vinyl-buffer')

export const js = () => {
  const ENTRY_FILES = [
    'node_modules/picturefill/dist/picturefill.js',
  ]
  const concatedScripts = ENTRY_FILES.map(file => fs.readFileSync(file, 'utf8'))
    .concat('')
    .join('\n')

  const bundler = browserify('src/js/index.js', {
    debug: true,
  })

  const bundle = () => bundler
    .bundle()
    .on('error', err => gutil.log('Browserify Error', err))
    .pipe(source('app.js'))
    .pipe(buffer())
    .pipe(sourcemaps.init({loadMaps: true}))
    .pipe(header(concatedScripts))
    .pipe(uglify({preserveComments: 'license'}))
    .pipe(sourcemaps.write('.'))
    .pipe(gulp.dest('dist/js'))

  return bundle()
}

これだと、ENTRY_FILESのログがソースマップでうまいことできないけど、たぶんこっちのほうがよさげ。