gulpとwebpackでJSのコンパイル環境を構築する

gulp 4とwebpack 5を使用してJSのコンパイル環境を構築します。

よく紹介されている方法は、webpack.config.jsのentryに対象ファイルを設定をしていますが、ファイルが増えるたびにentryも増やす必要があり手間なので、ここではgulpfile.jsで自動で処理されるようにします。
またビルドファイルはソースと同じディレクトリ構成で出力するようにします。

やること
  1. webpackのコンパイル環境を構築
  2. webpack.config.jsのentryは使用しない
  3. ビルドファイルはソースと同じディレクトリ構成で出力

node.jsやyarnのインストールについては省いています。

目次

webpackのコンパイル環境を構築

全体構造

src/assets/jsをコンパイルしてpublic/assets/jsに出力します。

├─ public/
│   └─ assets/
│       └─ js/
│           └─ コンパイルされたjs
│
├─ src/
│   └─ assets/
│       └─ js/
│           ├─ child/
│           │   └─ child-index.js
│           └─ index.js
│
├─ gulpfile.js
├─ webpack.config.js
└─ package.json

パッケージの追加

まずはgulpを実行するために必要なパッケージを入れます。

yarn add -D gulp gulp-notify gulp-plumber gulp-debug
パッケージ説明
gulpgulp本体
gulp-notify エラー通知
gulp-plumberエラーが発生した場合にタスクが停止するのを防止
gulp-debug 処理中のファイルをログに表示

次にwebpackでコンパイルするために必要なパッケージを追加します。

yarn add -D webpack webpack-stream babel-loader @babel/core @babel/preset-env vinyl-named gulp-filter
パッケージ説明
webpackwebpack本体
webpack-streamgulpでwebpackを使えるようにする
babel~トランスパイル関係のパッケージ
vinyl-namedgulpでwebpackのentryを自動作成
gulp-filter_から始まるファイル名をコンパイル対象外にする

最低限これだけあれば良いかと思います。

jsファイルの作成

src/assets/jsディレクトリにindex.jschild/child-index.jsを作成していきます。

index.jsはビルド後にimport文のバンドルとトランスパイルされているか確認できる内容にします。

import $ from 'jquery';
console.log($);

const array1 = [1, 2, 3, 4];
const reducer = (previousValue, currentValue) => previousValue + currentValue;

// 1 + 2 + 3 + 4
console.log(array1.reduce(reducer));
// expected output: 10

// 5 + 1 + 2 + 3 + 4
console.log(array1.reduce(reducer, 5));
// expected output: 15

1行目のimport文でjqueryを使用しているのでパッケージを追加しておきます。

yarn add -D jquery

child/child-index.js は階層を維持してビルドされるか確認するためだけなので中身は何でもOKです。

console.log('child-index');

webpack.config.jsの作成

webpack.config.jsを以下の内容で作成します。

const webpack = require('webpack');
const TerserPlugin = require('terser-webpack-plugin');

module.exports = {
  mode: 'production',
  //entry: '', // gulpで設定
  output: {
    filename: '[name].min.js' // [name]はgulpで設定
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        use: {
          loader: 'babel-loader',
          options: {
            'presets': [
              ["@babel/preset-env"]
            ],
            'compact': false
          }
        },
      }
    ]
  },
  plugins: [
    // ファイルを分割しない
    new webpack.optimize.LimitChunkCountPlugin({
      maxChunks: 1,
    }),
  ],
  optimization: {
    minimize: true, // ファイル圧縮機能を有効にする
    minimizer: [
      new TerserPlugin({
        extractComments: false, // コメントを外部ファイルにしない
        terserOptions: {
          compress: {
            drop_console: false,  // console.logを残す
          }
        },
      }),
    ],
  },
  performance: {
    hints: false // パフォーマンス警告を表示しない
  },
  resolve: {
    extensions: ['.js']
  }
}

ところどころコメントを書いていますが、4行目のentryはgulpで設定するのでコメントアウトしています。
同様に5,6行目のoutputfilename: '[name].min.js' となっている部分もgulpで処理されます。

※ファイル圧縮にTerserPluginを使用していますが、もしwebpack4を使っている場合はパッケージを追加する必要があります。

gulpfile.jsの作成と実行

gulpfile.jsにwebpackのタスクを記述します。

const gulp = require('gulp');
const notify = require('gulp-notify');  // エラー通知
const plumber = require('gulp-plumber'); // エラー時のタスク停止防止
const debug = require('gulp-debug'); // ログ表示

const path = require('path');
const named = require('vinyl-named');
const filter = require('gulp-filter'); // ファイルフィルター

const webpack = require('webpack');
const webpackStream = require('webpack-stream');
const webpackConfig = require('./webpack.config.js');

const paths = {
  js: {
    src: 'src/assets/js/**/*.js', // コンパイル対象
    dest: 'public/assets/js' // 出力先
  }
}

/**
 * jsタスクを実行する関数
 */
function js() {
  return gulp.src(paths.js.src)
    .pipe(plumber({
      errorHandler: notify.onError('Error: <%= error.message %>')
    }))
    .pipe(filter(function (file) { // _から始まるファイルを除外
      return !/\/_/.test(file.path) && !/^_/.test(file.relative);
    }))
    .pipe(named((file) => {
        const p = path.parse(file.relative);
        return ((p.dir) ? p.dir + path.sep : '') + p.name;
      })
    )
    .pipe(webpackStream(webpackConfig, webpack))
    .pipe(gulp.dest(paths.js.dest))
    .pipe(debug({title: 'js dest:'}));
}

/**
 * watchタスクを実行する関数
 */
function watch() {
  return gulp.watch(paths.js.src, gulp.series(js))
}

exports.js = js; // jsタスク
exports.watch = watch; // watchタスク
exports.default = gulp.series(js); // defaultタスク

このタスクで大事なのは32~36行目のnamed (vinyl-named)です。
答えはWebpackStreamのMultiple Entry Pointsに書いてあり、このnamedを使用してgulp.src に指定されたファイル(複数)をエントリーポイントにします。
ただnamed() のままで使用すると階層を無視した出力になるため少し手を加えています。

作成したタスクが正常に動作するか確認するため、ターミナルからyarn gulpyarn gulp js、環境によってはgulpgulp jsでコンパイルできるか試してみます。

# defaultタスクの実行
yarn gulp

# jsタスクの実行
yarn gulp js

正常に動作した場合は以下のファイルが出力されます。import文がバンドルされてトランスパイルされているでしょうか?

  • public/assets/js/index.min.js
  • public/assets/js/child/child-index.min.js

ざっくりとした説明ですがこれでgulpとwebpackを使用したコンパイル環境の完成です。

webpack.config.jsでentryを自動登録することも可能ですが、gulpでもこのようにできるので活用してみてはどうでしょうか。

今回作成した環境は以下からダウンロードできます。

  • URLをコピーしました!
  • URLをコピーしました!

コメント

コメントする

目次