gulp導入の覚え書き(後編)- gulpとnpmの使い方

gulpを利用した当初は、なすがままに設定していただきました。
コマンドもほぼはじめましてだったので、黒い画面に呪文...と、何が起きているのか理解に苦しみました。

現在理解している範囲でnpm、gulpまわりで集めた情報をまとめます。

大まかな流れ

1. homebrewをインストール  
 ↓  
2. anyenvをインストール  
 ↓  
3. nodenvをインストール  
 ↓  
4. Node.js(npm)をインストール  
 ↓  
5. gulpをインストール・・・後編ここから  
 ↓  
6. gulpをつかう  
 ↓  
7. npmをつかう

前編の続きです。
gulpのインストールからnpmの使い方までまとめます。

gulpとは

gulpはNode.jsをベースとしたビルドシステムヘルパーです。Gruntと似た目的を持って作られたツールで、gulpを使えばさまざまな作業を自動化することができます。一番の特徴はファイルの処理をストリームで行う「ストリーミングビルドシステム」です。この特徴によって複雑なタスクも細かくカスタマイズして書くことができます。

(一部抜粋)

現場で使えるgulp入門 | 第1回 gulpとは何か | CodeGrid

ビルドシステムヘルパー...。
ストリーミングビルドシステム...。
まずはざっくり理解できればと思います。

gulpはNode.jsのライブラリのうちの一つであることは覚えておきます。

☆以下「ライブラリ」だったり「パッケージ」だったり、「モジュール」だったり用語が出てきますが、厳密な違いが分かっていないのでほぼ同じ認識で進めます。
(パッケージ > ライブラリ > モジュール の順の大きさだと思っています)

5. gulpをインストールする

ようやくgulpをインストールする準備ができました。
まずはNode.jsインストール時に一緒にインストールされたnpmの設定です。

プロジェクトのディレクトリに移動して行います。

npmの初期化

Node.jsのパッケージを使うにあたり最初に行います。
下記コマンドでpackage.jsonというパッケージ管理ファイルを作ります。

npm init

対話形式でpackage.jsonの設定値を入力していきますが、この時点で必ず必要ではないのでreturn(enter)キーで進めて大丈夫です。
最後にこれらの設定値でよいかyes/noで聞かれます。

詳細はこちらを参照しましょう。

初期化処理を行う!npm initの使い方【初心者向け】 | TechAcademyマガジン

また、npm init -ynpm init -yes とオプションを付けたコマンドを打つことで、対話などを省略することができます。

中には npm init --yes と、ハイフン(ダッシュ)が2つのコマンドも見掛けました。
これはLinuxの中で「UNIXスタイル」や「GNUスタイル」などのスタイルの違いから来ているそうです。複数のオプションをつけるときの書き方も異なるので注意です。

Linux引数(オプション)のハイフン-と--の違い - Qiita

なお、npm(Node Package Manager)については今のところNode.jsのパッケージ管理ツールである、とだけ認識して進めます。

package.json 中身の主な設定

"name":パッケージ名
"version":最初は"1.0.0"。パッケージの内容を変えるときに変更する
"author":作った人の名前
"dependencies":npmでインストールしたパッケージ(モジュール)
"scripts": script コマンド のプロパティ。キーがイベント、そして値がコマンド??

npmからglupをインストールする

npmコマンドでNode.jsのパッケージであるgulpをインストールします。

npm install gulp 

glupのバージョン確認

gulpのインストールが終わったら、

gulp -v

と、バージョンを確認しましょう。

npmでのパッケージのインストールについて

引っかかった部分や、覚えておきたい内容をまとめました。

パッケージのインストール

方法はオプションによって異なります。

npm install パッケージ名 -P

npm install パッケージ名 --save-prod
npm install パッケージ名 --save
npm install パッケージ名(オプションなし)と同じです。
package.jsondependencies フィールドに追加されます。

npm install パッケージ名 -D 

npm install パッケージ名 --save-dev と同じです。
package.jsondevDependencies フィールドに追加されます。

npm install -g パッケージ名

パッケージのグローバルインストールです。
パッケージのグローバルのインストールはお薦めされていません。

package.jsonに書かれているパッケージをインストールする

npm install

同じプロジェクトを複数人で開発する時に使うと便利なコマンドです。
Gitでpackage.jsonを管理しておくと、複数人の環境を npm install で揃えることができます。

上記のようにオプションなしではpackage.jsonのdependenciesとdevDependencies両方に書かれているパッケージをインストールします(npm install --production でdependenciesのパッケージのみインストール)。

dependenciesとdevDependenciesの違い

「dependencies = 依存(複数)」、「development = 開発」です。

ここではパッケージを開発用公開用に分けて考えます。

いつもプロジェクトで作るパッケージは開発用です。npm init でpackage.jsonを作ってパッケージを設定します。

これとは別に、公開用のパッケージ、すなわちインストールされるパッケージを作る場合はdependenciesとdevDependenciesを使い分ける必要があります。

【いまさらですが】package.jsonのdependenciesとdevDependencies - Qiita

パッケージを使いたい人がnpm install パッケージ名としたときにはdependenciesに書かれているパッケージのみがインストールされます。

公開パッケージを作る、というのは中々ないかなと思います。
開発のためのパッケージの使い方をどうすればいいか考えたところ、

npmのpackage.jsonと依存関係を理解しよう! - bagelee(ベーグリー)

全てdependenciesに追加してしまってもいいのですが、これらの違いはNode.jsサーバーとして動かすときやパッケージを公開するときに影響してきます。普段から、プログラムの動作には必要なく開発やテストのときのみに使うパッケージはdevDependenciesとして追加するように意識するといいでしょう。

開発やテストの時に使うパッケージとプログラムの動作に使うパッケージが異なる、というイメージがあまり持てず、そのパッケージに依存しているパッケージは全部必要に思えてしまうのですが、そういう場面に直面しなと実感できなさそうです。

なにはともあれ、開発だけのパッケージの場合はdevDependenciesに入るようにインストールする習慣をつけようと思います!

パッケージの開発、インストールまとめ

開発パッケージの場合 インストールされるパッケージの場合
パッケージに対して使うコマンド npm install npm install パッケージ名
コマンドのはたらき package.jsonに書かれているパッケージをインストールする
(他の人とパッケージの内容を共有できる)
パッケージをインストールする
インストール先のpackage.jsonへの追加(dependencies/devDependenciesはオプションで選べる)
dependenciesの
パッケージ
devDependenciesの
パッケージ

--productionをつけるとインストールしない

gulpのインストールはグローバルなのかローカルなのか、両方なのか問題

2021年現在ではローカルのみにインストールするのが主流のようです。

グローバルインストールでは、パスが通っている状態なので、gulp とコマンドを打つと、グローバルなgulpの実行になります。

なのでローカルのみのインストールでは不便なのかというと、npm-scriptnpx(後述)を使えば簡単にローカルのgulpを実行することができます。

問題の「両方」については、

gulpのインストールは通常ですとローカルとグローバルの両方にインストールする必要があります。グローバルにインストールされたgulpは、ローカルにインストールしたgulpを実行するのが役割です。

とのことで、仕組みは以下のように、

gulpのアプローチ "なぜグローバルとローカルにインストールが必要なのか" | じまぐてっく

グローバルなgulpのコマンドを叩くことで、ローカルなgulpを実行することができるのだと解釈しました。

現在のgulpのバージョン(4.0.2)では同じコードを見つけられなかったのですが、仕組みは同じではと思いました。

☆ローカルのみにgulpをインストールした環境で以下進めています

package-lock.jsonとは

まずは「ロックファイル」について調べました。

ロックファイル (lock file)
https://wa3.i-3-i.info/word12436.html

排他制御というのをするファイルなのですね。トイレの例を覚えよう。

他の言語などの場面では .lock という拡張子のロックファイルがあったりするみたいです。

そろそろlockファイルを理解するための最初のページ【composer.lock/package-lock.json】 - Qiita

ロックファイルの働きが、実際の作業の流れで見ていくとわかりやすかったです。

パッケージの公開も、こんなに簡単にできてしまうとは驚きでした。

こちらでも簡単にまとめられています。

【Node.js】package.jsonとpackage-lock.jsonについて簡単にまとめる - Qiita

買うものリストと領収書!

これらをまとめると

  • モジュールをインストール、またはアップデートする時、package.jsonにインストールするモジュールとバージョン(キャレット^やチルダ~付き)が書き込まれる→node_modulesフォルダにモジュールが入る→package-lock.jsonにインストールされたモジュールとバージョンが書き込まれる
  • package-lock.jsonはnode_modulesの中身やpackage.jsonによって自動で作成・変更される
  • npm install をする時、package-lock.jsonがあると、package-lock.jsonに書かれているバージョンのパッケージがインストールされる
  • npm install をする時、package-lock.jsonがないと、package.jsonに書かれているパッケージの新しいバージョンがインストールされる(package.json^2.0.0 と書かれていたら、2.5.0がインストールされることもある)

大体あっていると思います。

package-lock.jsonもGitで管理しましょう!

6. gulpをつかう

gulpfile.jsを作る

制作に便利なタスク(処理)をgulpfile.jsに書いて、コマンドで実行します。

2021年3月現在はgulp4が最新なので、その書き方で覚えていきます。

主に以下の記事を見ながら書きました。

Gulp4の変更点と新しい書き方 - Qiita

gulp4の設定方法 - SassやAutoprefixer、ejs、画像の圧縮などを自動化する | 夢みるゴリラ

調べるとgulp3を使っている人向けの記事ががほとんどなので、3と4の違いも分かります。同時に混乱します。

とても簡単にgulp(gulp4)の書き方をまとめると

  • task() を使わずに、タスクの関数宣言をする(無名関数を変数に入れたり、関数名を付けたり)
  • タスクの関数の中は大体 return ではじまる
  • exports.gulpタスク名 = タスク名(変数、関数) でgulpコマンドにタスクを設定する
  • gulpタスクの実行コマンドは gulp gulpタスク名(※gulpのローカルインストールでは実行できない)
  • exports.defalut とgulpタスク名を defalut にすると、実行時に gulp default と打たなくてよくなる(gulp でdefalutタスクが実行される)

という感じです。 肝心のタスクの中身は 心のタスクク中身はgulpのpipe()series()parallel()メソッドを使って、 処理をつなげたり、並行処理したりします。

何とかコードは読めはするものの、自力では書けないのでもう少しがんばりたいです。

***

ひとつ、冒頭のrequireの書き方で気になった部分があったので調べました。

const { src, dest, watch, lastRun, parallel, series } = require("gulp");

分割代入によりオブジェクトの特定のプロパティだけを単独変数に取得する (Object destructuring) | まくまくJavaScriptノート

これはES2015から使える分割代入というのですね。

gulpの中身はオブジェクトで、

const gulp = {
  'src': 'srcValue',
  'dest': 'destValue',
  'watch': watchVvalue'
}

などとなっているのかな、ーと思いましたが、ざっと見る限りそうでもありませんでした。
安直でした(笑)
実際どうrequireされているのか不明です。

const { src, ... のように書くと、gulpメソッドをつかう時に gulp.src ではなく src で済む、というものでした。

gulpコマンドでgulpタスクを実行する

ここではnpmの働きと分けるためにgulpコマンドから実行することを考えます。

先ほど「gulpタスクの実行コマンドは gulp gulpタスク名(※gulpのローカルインストールでは実行できない)」
と書きました。

gulpをローカルのみでインストールしている場合は簡単なコマンドではうまくいかないのです。

たとえば

gulpfile.js(一部)

// Sassファイルを監視し、変更があったらSassを変換するタスク
const watchSassFiles = () => watch('./scss/**/*.scss', compileSass);

// タスクの宣言
exports.watch = watchSassFiles;

と書いた場合、gulpfile.jsがある階層で
gulp watch とコマンドを叩けば実行できるかというとできません。

「command not found: gulp」(gulpコマンドが見つかりません)と返ってきます。
ローカルなgulpにはパスが通っていないのです。

なので./node_modules/.bin/gulp watch と叩けば実行できます。
これは毎回実行するのが大変です。

そこで便利なのがnpmです!

7. npmをつかう

npm コマンドからgulpのタスクを実行すると、コマンドもシンプルです。
また npx コマンドも使えるのでそれぞれ見ていきます。

npm-script

Node.jsユーザーなら押さえておきたいnpm-scriptsのタスク実行方法まとめ - ICS MEDIA

npm-scriptsとは、package.jsonファイルに記述可能なシェルスクリプトエイリアスです。エイリアスとはコマンド名を別のコマンド名に置き換えることです。

gulpfile.jsのたとえを受けてpackage.jsonが次のように書かれているとします。

package.json(一部)

 "scripts": {
   "test": "echo \"Error: no test specified\" && exit 1",
   "sass": "gulp sass",
   "start": "gulp watch"
}

echo \"Error: no test specified\" && exit 1シェルスクリプト(コマンド)で、これを test という別のコマンド(スクリプト名)に置き換えています。

実行するコマンドは

npm run test
npm run sass
npm start

になります。
npm runrunを付けます。

なおスクリプト名がstart、testの場合は run は不要です。
(↑は npm test でもOK)

runにまつわるお話も見つけました。

`npm run` は正式なコマンドじゃなかった件(NPMおれおれAdvent Calendar 2019 – 01日目) | Ginpen.com

gulpに関わらず、いろいろとコマンドを設定して使いやすくできるようです!

npx

npxはnpmのバージョン5.2.0以降に入っています。

npm 5.2.0の新機能! 「npx」でローカルパッケージを手軽に実行しよう - Qiita

ローカルにインストールしたnpmパッケージを、npxコマンドだけで実行できるようになります。

なので上記のnpmコマンドの代わりにnpxを使うと、

npx gulp sass
npx gulp watch

で実行できます。

こちらもgulp以外にもインストールしたパッケージを実行する時に、手軽にできて便利です。

さいごに

gulpとnpmのことは、ずっとまとめようと思って温め続けていました。
ようやく書けました。スッキリ!
(後々見返して意味が分かるのかは怪しい)

存在知りたての頃よりだいぶ分かってきたと思います。

私の場合はnpmの入り口がgulpでしたが、他の用途や機能の異なるパッケージも使っていけばもっと体系的に分かってくるのだろうと思います。

おしまい!