• 最終更新日:

gulp4の設定方法 - DartSassやAutoprefixer、ejs、Babel、を自動化する

gulp4の設定方法

gulpとはnode.jsベースで動くパッケージで、いままで面倒だった作業を自動化してくれる優れもの。今回gulp4で設定する内容は以下になります。

  • DartSass
  • autoprefixer(grid対応も)
  • htmlの共通部分のパーツ化(ejs)
  • JavaScriptはBableによってトランスパイル 
  • cssとJavaScriptの圧縮処理

説明する環境は以下の通りです。

  • macOS Catalina v10.15.5
  • Visual Studio Code v1.57.0
  • node.js v16.13.1
この記事の目次

gulp4に必要なパッケージを確認する

gulp4の設定を説明する前に、必要なパッケージがインストールできているか確認しましょう。必要なパッケージは3つ。

  • Homebrew
  • nodebrew
  • node.js

gulpを使うにはまずnode.jsが必要になります。node.jsで使うgulpのようなパッケージはnpmで管理されますが、npmはnode.jsをインストールすると自動で付いてきます。

node.jsのバージョンによっては動かないパッケージがあるのでnode.jsのバージョンを自由に切り替えられるnodebrewが必要になります。Macにパッケージをバラバラにインストールすると管理が大変。そこでMacでインストールしたパッケージを一括して管理するためにhomebrewが必要になる、ということです。

このように管理する場所を明確にすることで、パッケージのインストール、バージョンアップや切り替えなどがスムーズに行えるようになります。

これらのHomebrew、nodebrew、node.jsをインストールする流れは以下の記事を参考にしてみてください。

gulp4の設定をする

まずは「gulp-model」というフォルダを作って、その中に必要なファイルやフォルダを作っていくことにします。ワークツリーのイメージは以下。「src」には処理前のデータを入れておいて、「dist」には処理後のデータを出力させます。

「dist」と「src」の中は以下のようになっています。

package.jsonを準備する

まずはターミナルを使ってpackage.jsonを作ります。VScodeを使っている場合は、【 control + shift + @ 】でターミナルを開いて、以下のコマンドを打ちましょう。

npm init -y

gulp4をインストールする

ローカルとグローバルの両方にインストールする方法を見かけますが、バージョン違いでエラーになることもあるので、ローカルのみにインストールします。

npm install -D gulp

gulp4で使うパッケージをインストールする

gulpに本体をインストールできたら、自動化に必要はパッケージをインストールしていきます。以下のコマンドを必ず1行ずつコピーして実行してください。

npm i -D gulp-dart-sass # DartSassを使う
npm i -D gulp-sass-glob-use-forward # DartSassのimportを楽にする
npm i -D gulp-postcss # Autoprefixと一緒に使うもの
npm i -D autoprefixer # Autoprefix
npm i -D gulp-plumber # エラーでも強制終了させない
npm i -D gulp-notify # エラーのときはデスクトップに通知
npm i -D del # ファイル、ディレクトリの削除
npm i -D gulp-ejs # htmlのパーツ化
npm i -D gulp-rename # ejsの拡張子を変更
npm i -D gulp-uglify # script圧縮用
npm i -D browser-sync # ブザウザ読み込み・反映
npm i -D gulp-replace # 余計なテキストを削除
npm i -D gulp-babel @babel/core @babel/preset-env #JavaScriptのトランスパイル

本体ごとまとめてインストールしたい方は半角スペースをあけて繋げて書くこともできます。

npm i -D gulp gulp-dart-sass gulp-sass-glob-use-forward gulp-postcss autoprefixer gulp-plumber gulp-notify del gulp-ejs gulp-rename gulp-uglify browser-sync gulp-replace gulp-babel @babel/core @babel/preset-env

パッケージがインストールできると、package.jsonにパッケージ名が載っているので確認してみましょう。(以下は例です)

gulpfile.jsに自動化処理を書く

gulpfile.jsを作成します。これはターミナルを使わず、通常のファイルを作成する方法と同じです。

gulpfile.jsには以下の内容を書いています。

//gulpとgulpのパッケージを読み込み
const { src, dest, watch, lastRun, parallel, series } = require("gulp");
const sass = require("gulp-dart-sass"); //DartSassを使う
const glob = require("gulp-sass-glob-use-forward"); //DartSassのimportを楽にする
const postcss = require("gulp-postcss"); //Autoprefixと一緒に使うもの
const autoprefixer = require("autoprefixer"); //Autoprefix
const plumber = require("gulp-plumber"); //エラーでも強制終了させない
const notify = require("gulp-notify"); //エラーのときはデスクトップに通知
const del = require("del"); //ファイル、ディレクトリの削除
const ejs = require("gulp-ejs"); //htmlのパーツ化
const rename = require("gulp-rename"); //ejsの拡張子を変更
const babel = require("gulp-babel"); //JavaScriptのトランスパイル用
const uglify = require("gulp-uglify"); //JavaScriptの圧縮用
const browserSync = require("browser-sync"); //ブザウザ読み込み・反映
const replace = require("gulp-replace"); //余計なテキストを削除

//読み込むパスと出力するパスを指定
const srcPath = {
  html: {
    src: ["./src/ejs/**/*.ejs", "!" + "./src/ejs/**/_*.ejs"],
    dist: "./dist/"
  },
  styles: {
    src: "./src/scss/**/*.scss",
    dist: "./dist/css/",
    map: "./dist/css/map"
  },
  scripts: {
    src: "./src/js/**/*.js",
    dist: "./dist/js/",
    map: "./dist/js/map"
  },
  images: {
    src: "./src/img/**/*.{jpg,jpeg,png,gif,svg}",
    dist: "./dist/img/"
  }
};

//htmlの処理自動化
const htmlFunc = () => {
  return src(srcPath.html.src)
    .pipe(
      plumber({ errorHandler: notify.onError("Error: <%= error.message %>") })
    )
    .pipe(ejs({}, {}, { ext: ".html" })) //ejsを纏める
    .pipe(rename({ extname: ".html" })) //拡張子をhtmlに
    .pipe(replace(/[\s\S]*?(<!DOCTYPE)/, "$1"))
    .pipe(dest(srcPath.html.dist))
    .pipe(browserSync.reload({ stream: true }));
};

//Sassの処理自動化(開発用)
const stylesFunc = () => {
  return src(srcPath.styles.src, { sourcemaps: true })
    .pipe(
      plumber({ errorHandler: notify.onError("Error: <%= error.message %>") })
    )
    .pipe(glob())
    .pipe(
        sass
          .sync({
            includePaths: ["node_modules", "src/scss"], //パスを指定
            outputStyle: "expanded"
          })
          .on("error", sass.logError)
      )
    .pipe(
      postcss([
        autoprefixer({
          // IEは11以上、Androidは4、ios safariは8以上
          // その他は最新2バージョンで必要なベンダープレフィックスを付与する
          //指定の内容はpackage.jsonに記入している
          cascade: false,
          grid: true
        })
      ])
    )
    .pipe(dest(srcPath.styles.dist, { sourcemaps: "./map" }))
    .pipe(browserSync.reload({ stream: true }));
};

//Sassの処理自動化(圧縮用)
const stylesCompress = () => {
  return src(srcPath.styles.src)
    .pipe(
      plumber({ errorHandler: notify.onError("Error: <%= error.message %>") })
    )
    .pipe(glob())
    .pipe(
      sass
        .sync({
          includePaths: ["node_modules", "src/scss"], //パスを指定
          outputStyle: "compressed"
        })
        .on("error", sass.logError)
    )
    .pipe(
      postcss([
        autoprefixer({
          //上の指定と同じ
          cascade: false,
          grid: true
        })
      ])
    )
    .pipe(
      rename({
        suffix: ".min"
      })
    )
    .pipe(dest(srcPath.styles.dist))
};

//JavaScriptの処理自動化(開発用)
const scriptFunc = () => {
  return (
    src(srcPath.scripts.src, { sourcemaps: true })
      .pipe(plumber({ errorHandler: notify.onError("Error: <%= error.message %>") }))
      .pipe(
        babel({
          presets: ["@babel/env"]
        })
      )
      .pipe(dest(srcPath.scripts.dist, { sourcemaps: "./map" }))
      .pipe(browserSync.reload({ stream: true }))
  );
};

//JavaScriptの処理自動化(圧縮用)
const scriptCompress = () => {
  return (
    src(srcPath.scripts.src)
      .pipe(plumber({ errorHandler: notify.onError("Error: <%= error.message %>") }))
      .pipe(
        babel({
          presets: ["@babel/env"]
        })
      )
      .pipe(uglify({ output: { comments: /^!/ } }))
      .pipe(
        rename({
          suffix: ".min"
        })
      )
      .pipe(dest(srcPath.scripts.dist))
  );
};

// マップファイル除去
const cleanMap = () => {
  return del([srcPath.styles.map, srcPath.scripts.map]);
};

// ブラウザの読み込み処理
const browserSyncFunc = () => {
  browserSync({
    server: {
      baseDir: "./",
      index: "index.html"
    },
    reloadOnRestart: true
  });
};

// ファイルに変更があったら反映
const watchFiles = () => {
  watch(srcPath.html.src, htmlFunc);
  watch(srcPath.styles.src, stylesFunc);
  watch(srcPath.scripts.src, scriptFunc);
};

exports.default = parallel(watchFiles, browserSyncFunc); //初期設定(フォルダ、ファイルの監視、ブラウザへの反映)
exports.build = parallel(htmlFunc, stylesFunc, scriptFunc); //出力するだけの設定
exports.compress = parallel(stylesCompress, scriptCompress, cleanMap); //cssとJavaScriptの圧縮、mapフォルダの削除

Autoprefixerの指定を書く

Autoprefixerの指定をpackage.jsonに加えます。今回の指定は以下のようにしています。

  • ie11以上
  • Android4以上
  • ios safari8以上
  • その他は最新の2バージョンに対してベンダープレフィックスをつける

package.jsonを開いて以下を追記します。

"browserslist": [
    "last 2 versions",
    "ie >= 11",
    "Android >= 4",
    "ios_saf >= 8"
  ]

gulpfile.jsの中身を説明

gulpfile.jsに書いた内容を簡単に説明していきます。

読み込むパスと出力するパスを指定

gulpで自動化する上で、処理前にどのフォルダからファイルを読み込むか、処理後に出力されるファイルをどこのフォルダへ出力するか指定する必要があります。

例えばcssの処理するパスは以下のように設定しています。srcの部分は、処理前のファイルパスを、distの部分は処理後に出力するフォルダまでのパスを、mapの部分にはsourcemapを出力するフォルダまでのパスを示しています。

styles: {
    src: "./src/scss/**/*.scss", // ここから読み込んで
    dist: "./dist/css/", // ここに出力する
    map: "./dist/css/map" //sourcemapはここに出力する
  }

ワークツリーで見るとこんな感じになります。

htmlの処理自動化

headerやfooterなど各ページで共通部分があればejsを使ってパーツ化することができます。それぞれのページと共通部分をejsの拡張子で作成。そのあとに拡張子をhtmlに変更して、それぞれのフォルダに出力させる処理をしています。

細かい設定については、はにわまんさんの記事を参考にしてみてください。

ワークツリーでは以下のようになります。

DartSassの処理自動化

通常は「expanded」でcssへ出力し、sourcemapも作成する設定をしています。Autoprefixerを通すことで、package.jsonで書いた指定にあわせてベンダープレフィックスを出力してくれます。

圧縮処理の場合は、「compressed」で1行にして、コメントも削除します。sourcemapも出力させない設定です。最後にファイル名をstlyle.min.cssに変更します。

圧縮処理を実行するコマンドはこちら

DartSasssではimportを使ってパーシャルファイルを読み込むことができなくなりました。importの代わりに@useや@forwardを使う必要があります。フォルダ設計については以下の記事を参考にしてみてください。

JavaScriptの処理自動化

JavaScriptについてはinit.jsで書いたコードを、Babelによってトランスパイルさせて「dist」フォルダにある「js」フォルダに出力させています。

いまはjQueryも使っていないこと、それからプラグインについてはCDNから読み込んでいるので、今回のようなシンプルな設計になりました。

圧縮処理の場合は、コメントを削除して1行にする設定をしています。最後に名前をinit.min.jsに変更します。

圧縮処理を実行するコマンドはこちら

ファイルが更新されたら自動で反映させる

以下の部分は更新されたファイルや画像をチェックして、先に設定しておいた自動化処理を反映させるための設定です。watchすることでファイルが保存された瞬間に自動化処理が実行されます。

// ファイルに変更があったら反映
const watchFiles = () => {
  watch(srcPath.html.src, htmlFunc); // ejsファイルに変更があったら…
  watch(srcPath.styles.src, stylesFunc); // sassファイルに変更があったら… 
  watch(srcPath.scripts.src, scriptFunc); // scriptに変更があったら…
};

自動化処理を実行する

自動化処理のための定義を書いていたので、実際に定義した内容を実行するには以下のコマンドが必要になります。
グローバルにgulpをインストールしていないので、実行するコマンドに注意しましょう。

npx gulp

このコマンドによって、htmlとcss、JavaScript、画像の処理、ブラウザへの反映をまとめてやってくれます。コマンドを打ったあとはファイルを保存するだけで自動で反映されます。

ブラウザに表示させず、全体のファイルを更新するだけなら以下です。

npx gulp build

圧縮処理を実行する

サーバーにアップするときはデータ量を軽くするために圧縮処理をします。以下のコマンドでstyle.cssとinit.jsの圧縮、そしてmapファイルの削除ができます。

npx gulp compress

gulp4の設定を他のプロジェクトでも使うには?

まずは設定済みのgulpfile.jsとpackage.jsonをコピーして他のプロジェクトのフォルダへ移しましょう。移せたら以下のコマンドを実行します。

npm install -D

これでpackage.jsonに書かれたパッケージがインストールされるので、gulpfile.jsがあれば同じ設定で使うことができます。

インストールしたら念のために以下のコマンドを打ってpackage.jsonに書かれたパッケージが正しくインストールできているか確認しましょう。

npm ls --depth=0

複数の同じ案件ならシンボリック化もあり

同じような案件が複数ある場合はnode_modulesをシンボリック化すると何度もインストールしなくて済みます。シンボリックとはwindowsで言うショートカットですね。ベースとなるディレクトリにnode_modulesの本体とpackage.jsonを用意して、それぞれのプロジェクトのディレクトリにはgulpfile.jsとnode_modulesのシンボリックを置いておきます。

シンボリックはfinderから移動させるのではなく、ターミナルから作成しましょう。以下のコマンドで作成することができます。/Users/path/to/の部分にはnode_modulesの本体までのパスを書いてください。

ln -s /Users/path/to/node_modules ./node_modules

gitではnode_modulesを除外する

node_modulesはnpmでインストールしたパッケージを保管する場所。package.jsonと同じ階層に作成されます。ただgitで管理するには重く、また特にnode_modulesがなくてもgulpfile.jsとpackage.jsonがあれば同じ環境を構築することができるので、gitの管理から外しておきます。

.gitignoreという名前のファイルを作成して、以下の内容を書きます。*のワイルドカードをつけることでどの階層にあってもnode_modulesを除外することができます。

#node_modulesを除外する
*node_modules/

まとめと参照サイト

今回はgulp4の設定方法を説明しました。プロジェクトに合わせて設定内容を調整してみてください。今回の設定をするにあたって多くのサイトを参考させてもらいました。特に勉強になったサイトを載せておきます。