当サイトはアフィリエイト広告を使用しています

npm scriptsを使ってWeb制作を効率化しよう【その2】

npm scriptsをタスクランナーとして実用化できるようにします。

Pug、Sassについて前回までの記事で取り上げましたが、他にもnpmで配布されているパッケージはたくさんあります。

その中でもWEB制作をもっと便利にする、おすすめのパッケージを何点か紹介し、WEB制作のタスクランナーとして使用できるようにしていきましょう。

前回までの記事のフォルダ構成で進めていきます。

sample/
  ├ node_modules/
  ├ public/
  │  ├ index.html
  │  └ css/
  │     └ style.css
  ├ src/
  │  ├ pug/
  │  │  ├ index.pug
  │  │  └ include/
  │  │     ├ _header.pug
  │  │     └ _footer_.pug
  │  └ sass/
  │     └ style.scss
  └ package.json

package.json"scripts"部分は以下です。

{
  ...
  "scripts": {
    "pug": "pug --watch src/pug --out public --pretty",
    "sass": "sass --watch src/sass:public/css",
    "start": "npm-run-all -p pug sass"
  }
  ...
}

Sass、Pugの導入は↓の記事で紹介しています!

ホットリロード

変更のあったファイルを検知して、ブラウザの表示を更新する機能(ホットリロード)を使うことができます。

Pugにより.htmlファイルが更新されたタイミング等でホットリロードすることで、いちいちブラウザの更新ボタンを押すことなく変更が反映がされます。

インストール

browser-syncをインストールします。

> npm install --save-dev browser-sync

使用方法

browser-sync start [オプション]で開始します。

主なオプションは次の通りです。

オプション効果
-s,--serverローカルサーバーを立ち上げる
--startPathローカルサーバー立上げの初期ページを指定する
-w,--watchファイルの変更を監視する
-f,--files変更を監視するファイルを指定する
--ignore変更を無視するファイルを指定する
--reload-delayファイル変更からリロードまでの遅延時間をミリ秒で指定する
-b,--browser立ち上げるブラウザを指定する

次のような要件でのコマンドを組み立ててみます。

  • 立ち上げ時のパスはpublic/index.htmlとする
  • ファイルの変更を監視する
  • 監視するファイルはpublic以下の.html.csspublic/images以下の全ファイルとする
  • ファイルの変更から、1000ミリ秒遅延してリロードさせる

これは以下の様になります。実際にコマンドプロンプトまたはターミナルで起動してみましょう。

> npx browser-sync start -s --startPath public/index.html -w -f "public/**/*.html, public/css/**/*.css, public/images/**/*" --reload-delay 1000

コマンドを実行する際は、ローカルインストールしたのでnpxで実行します。

また、パスの指定の部分のフォルダA/**/*は、globパターンと呼ばれ、フォルダA以下にある、サブフォルダを含む全てのファイルを指定するものです。

コマンドを実行するとブラウザが立ち上がり、指定した初期ページが表示されます。

監視対象のファイルを更新してみましょう。

ブラウザシンク

ファイルの保存後、ブラウザが自動的にリロードされて、更新が反映されているのが分かります。

上記のコマンドをnpm-scriptsへ追加しましょう。

npm-scriptsでは先頭にnpxは不要です。また、JSONではダブルクォーテーションを使うため、先のコマンドで使用していたダブルクォーテーションは\でエスケープします。

{
  ...
  "scripts": {
    ...
    "watch:server": "browser-sync start -s --startPath public/index.html -w -f \"public/**/*.html, public/css/**/*.css, public/images/**/*\" --reload-delay 1000"    ...
  }
  ...
}

その他詳細はhttps://browsersync.io/を参照してください。

ベンダープレフィックスの付与

CSSのプロパティにベンダープレフィックスを付けます。

ベンダープレフィックスとは次のようなものです。

ブラウザーベンダー (提供元) は、時に試験的または非標準な CSS プロパティおよび JavaScript API に接頭辞を追加することがあります。これにより、開発者は標準化プロセスの中で、理論上は、ウェブ開発者のコードを壊すことなく新しいアイデアを試すことができます。開発者は、ブラウザーの振る舞いが標準化されるまで、接頭辞を外したプロパティを導入するのを待つべきです。

MDNより引用

要は、ブラウザベンダーがお試しで導入していたりするCSSプロパティには、プロパティ名の頭に接頭辞(ベンダープレフィックス)が付けてあったりするよ~ということです。

このベンダープレフィックスはブラウザごとに決まっており、-webkit--moz--ms--o- 等を付けます。

autoprefixerで、このベンダープレフィックスの付与をCan I Useを元に判断、実行してくれます。

インストール

必要なパッケージはpostcsspostcss-cliautoprefixerとなります。

> npm install --save-dev postcss postcss-cli autoprefixer

使用方法

autoprefixerは対象とするブラウザを指定する為に、.browserslistrcという設定ファイルを使用します。

プロジェクトのルートに.browserslistrcファイルを用意します。

sample/
  ├ node_modules/
  ├ public/
  │  ├ index.html
  │  └ css/
  │     └ style.css
  ├ src/
  │  ├ pug/
  │  │  ├ index.pug
  │  │  └ include/
  │  │     ├ _header.pug
  │  │     └ _footer_.pug
  │  └ sass/
  │     └ style.scss
  ├ .browserslistrc  └ package.json

細かく指定する場合はbrowserslistを参考にする必要がありますが、とりあえず各ブラウザの最新版から3つ分を指定しましょう。

次のように.browserslistrcに記述・保存します。

last 3 versions

autoprefixerpostcss <入力となるフォルダ・ファイル> [オプション]で開始します。

主なオプションは次の通りです。

オプション効果
-u,--use使用するプラグインを指定する(ここではautoprefixer)
-o,--outputプレフィックス付与後のCSSを出力するファイルを指定する
-d,--dirプレフィックス付与後のCSSを出力するディレクトリを指定する
-w,--watchファイルの変更を監視する
--base指定したディレクトリ構造を維持してCSSを出力する(-d,--dirとセットで使う)

次のような要件でベンダープレフィックスを付与するコマンドを組み立ててみます。

  • 入力となるフォルダはpublic/cssとする
  • 出力するディレクトリは入力同様public/cssとし、ディレクトリ構造を維持する
  • ファイルの変更を監視する

これは以下の様になります。ここでもサブフォルダ含む全てのファイルを監視できるよう、globパターンを使いましょう。

> npx postcss public/css/**/*.css -u autoprefixer -d public/css --base public/css -w

コマンドを実行すると、フォルダの監視が始まります。

試しにpublic/css以下のフォルダ内に次のようなCSSを書いて保存してみましょう。

※実際に使う際は、SASSによりCSSが出力されたのを受けて、ファイルの変更を検知し、ベンダープレフィックスを付与する流れになります。

img {
  transform: translate(10px);
  object-fit: contain;
}

ファイルを保存すると、以下のように自動的にベンダープレフィックスを付与してくれます。

img {
  -webkit-transform: translate(10px);
  -ms-transform: translate(10px);
  transform: translate(10px);
  -o-object-fit: contain;
  object-fit: contain;
}

npm scriptsに追加しておきましょう。

{
  ...
  "scripts": {
    ...
    "watch:css": "postcss public/css/**/*.css -u autoprefixer -d public/css --base public/css -w"    ...
  }
  ...
}

画像圧縮

圧縮していない画像をWEBサイトに使用すると、読込に時間がかかってしまいます。用途、元画像のサイズにもよりますが、数百KB程度までを目安に画像サイズを圧縮しましょう。

インストール

画像圧縮に必要なパッケージはimagemin-keep-folderimagemin-gifsicleimagemin-svgoimagemin-webpとなります。

> npm install --save-dev imagemin-keep-folder imagemin-gifsicle imagemin-svgo imagemin-webp

今までのパッケージはファイルの変更を監視するオプションがありましたが、上記パッケージには無いため、ファイルの変更を監視してくれるchokidarというパッケージもインストールします。

> npm install --save-dev chokidar

使用方法

プロジェクトのルートにimagemin.jsを作成します。同時にsrc/imagesフォルダを作成して、圧縮する画像をいれておきましょう。

sample/
  ├ node_modules/
  ├ public/
  │  ├ index.html
  │  └ css/
  │     └ style.css
  ├ src/
  │  ├ images/  │  ├ pug/
  │  │  ├ index.pug
  │  │  └ include/
  │  │     ├ _header.pug
  │  │     └ _footer_.pug
  │  └ sass/
  │     └ style.scss
  ├ .browserslistrc
  ├ imagemin.js  └ package.json

このパッケージはコマンドラインからではなく、作成したimagemin.jsスクリプトをnodeで実行して画像圧縮することとなります。

.jpg.png.webpに変換し、.gif.svg.webpは適当に圧縮しましょう。

次のように記述します。ハイライト部分は環境により適宜変更してください。

import imagemin from "imagemin-keep-folder";
import gifsicle from "imagemin-gifsicle";
import webp from "imagemin-webp";
import svgo from "imagemin-svgo";

// 引数で入力となるファイルのパス、拡張子を指定するimagemin(['src/images/**/*.{jpg,png,gif,svg,webp}'], {  use: [
    gifsicle(),
    svgo(),
    webp({quality: 50})
  ],
  replaceOutputDir: output => {
    // 入力フォルダのパスの文字列を指定して差替えて、出力フォルダを決める    // ここでは、`src/images/**/*`から`public/images/**/*`に変更される    return output.replace("src/", 'public/')  }
});

.jpg.pngを変換せずにそのまま使用したい場合は次のように記述します。(別途imagemin-jpegtranimagemin-pngquantをインストールする必要があります)

import imagemin from "imagemin-keep-folder";
import gifsicle from "imagemin-gifsicle";
import svgo from "imagemin-svgo";
import jpegtran from "imagemin-jpegtran";
import pngquant from "imagemin-pngquant";

// 引数で入力となるファイルのパスを指定する
imagemin(['src/images/**/*.{jpg,png,gif,svg,webp}'], {
  use: [
    gifsicle(),
    svgo(),
    jpegtran(),
    pngquant({
      quality: [0.6,0.8]
    })
  ],
  replaceOutputDir: output => {
    // 入力となるフォルダのパスをもとに、出力となるフォルダを決める
    // ここでは、`src/images/**/*`から`public/images/**/*`に変更される
    return output.replace("src/", 'public/')
  }
});

このimagemin.jsnodeで動かす為に、package.jsonに以下を追加します。

{
  ...
  "type": "module",
  ...
}

正常に動作するか確認してみましょう。

src/imagesフォルダに適当な画像ファイルを入れ、コマンドプロンプトまたはターミナルで次を入力します。

> node imagemin.js

うまくいくとpublic/imagesフォルダに圧縮された画像が出力されます。

例としてある.pngの画像を圧縮すると次のようになります。

画像圧縮結果

画像圧縮がうまくいったら、ファイルの変更を監視するchokidarを試してみましょう。

chokidar <変更を監視するファイル> [オプション]でファイルの監視を実行できます。

オプションとして、--commandで監視しているファイルが変更されたら実行するコマンドを指定し、--initial では開始時にそのコマンドを一度実行することができます。

src/imagesフォルダ以下を監視して、変化があれば画像圧縮を実行するには次のようなコマンドになります。ここでもglobパターンを使用します。

> npx chokidar src/images/**/* --command "node imagemin.js" --initial

npm scriptsに追加します。これもコマンド中で使用しているダブルクォーテーションは\でエスケープしましょう。

{
  ...
  "scripts": {
    ...
    "watch:image": "chokidar src/images/**/* --command \"node imagemin.js\" --initial",    ...
  }
  ...
}

公開用フォルダの削除

以前の作業時に作成された公開用のファイルが残っている状態で作業すると、トラブルにつながる恐れがあるため、コーディング開始時にいったん公開用のファイルを削除するようにしましょう。

インストール

フォルダーを削除するには、rimrafをインストールします。

> npm install --save-dev rimraf

使用方法

使い方は簡単でrimraf <削除したいフォルダーのパス>とするだけです。

> npx rimraf public

npm scriptsに追加します。

{
  ...
  "scripts": {
    ...
    "clean": "rimraf public",
    ...
  }
  ...
}

HTMLのバリデーション

バリデーションとは、作成したコードがルールに沿って記述されているかどうかをチェックするものです。

HTMLのバリデーションを実行することで、記述した納品するコードの品質を担保することができます。

インストール

htmlhintをインストールします。

> npm install --save-dev htmlhint

使用方法

バリエーションチェックする項目の設定は.htmlhintrcファイルで行うことができます。(このファイルを作成しない場合は、デフォルト設定でチェックされます。)

プロジェクトルートに.htmlhintrcを作成して次を入力します。

{
  "tagname-lowercase": true,
  "attr-lowercase": true,
  "attr-no-duplication": true,
  "doctype-first": true,
  "tag-pair": true,
  "empty-tag-not-self-closed": true,
  "spec-char-escape": true,
  "id-unique": true,
  "src-not-empty": true,
  "title-require": true,
  "alt-require": true,
  "doctype-html5": true,
  "space-tab-mixed-disabled": "space",
  "attr-unsafe-chars": true,
}

代表的なものを選んでみました。これらは次のようなチェック内容となります。

項目チェックする内容
tagname-lowercaseタグ名が小文字か
attr-lowercase属性名が小文字か
attr-no-duplication属性が重複していないか
doctype-firstDOCTYPEが先頭にあるか
tag-pairタグのペアが揃っているか
empty-tag-not-self-closed空要素の終端のスラッシュを省略しているか
spec-char-escape特殊文字がエスケープされているか
id-uniqueIDがユニークか
src-not-emptysrcが空でないか
title-requiretitleタグがあるか
alt-requirealtがあるか
doctype-html5DOCTYPEがHTML5か
space-tab-mixed-disabledインデントにtabspaceを使っているか
attr-unsafe-chars属性値に安全でない文字を使用していないか

その他の設定項目はHTMLHintを参考にしてみてください。(尚、このファイルで指定しなかった項目はチェックされません。)

早速、公開用フォルダ下にあるHTMLをバリデーションしてみましょう。

> npx htmlhint "public/**/*.html"

コマンドを実行すると次のようにバリデーション結果を出力してくれます。

バリデーション結果

指摘された項目は納品迄に修正しておきましょう。

npm scriptsに追加します。

{
  ...
  "scripts": {
    ...
    "hint": "htmlhint public/**/*.html",
    ...
  }
  ...
}

各タスクを繋げる

これまでに紹介したタスクを連携して、1コマンドでタスクが起動するようにします。

今回の記事までを完了すると、下記のようなnpm scriptsができているかと思います。

{
  ...
  "scripts": {
    "pug": "pug --watch src/pug --out public --pretty",
    "sass": "sass --watch src/sass:public/css",
    "start": "npm-run-all -p pug sass",
    "watch:server": "browser-sync start -s --startPath public/index.html -w -f \"public/**/*.html, public/css/**/*.css, public/images/**/*\" --reload-delay 1000",
    "watch:css": "postcss public/css/**/*.css -u autoprefixer -d public/css --base public/css -w",
    "watch:image": "chokidar src/images/**/* --command \"node imagemin.js\" --initial",
    "clean": "rimraf public",
    "hint": "htmlhint public/**/*.html"
  }
  ...
}

これらを次のように変更します。

  • 新しくwatchキーを追加し、watch:で始まるキーのスクリプトを並列で実行出来るようにする
  • pugsassキーのスクリプトはwatchで起動出来るようにwatch:pugwatch:sassに変更する
  • startキーのスクリプトはcleanを実行してからwatchを実行する
{
  ...
  "scripts": {
    "watch:pug": "pug --watch src/pug --out public --pretty",    "watch:sass": "sass --watch src/sass:public/css",    "watch:server": "browser-sync start -s --startPath public/index.html -w -f \"public/**/*.html, public/css/**/*.css, public/images/**/*\" --reload-delay 1000",
    "watch:css": "postcss public/css/**/*.css -u autoprefixer -d public/css --base public/css -w",
    "watch:image": "chokidar src/images/**/* --command \"node imagemin.js\" --initial",
    "watch": "npm-run-all -p watch:*",    "clean": "rimraf public",
    "start": "npm-run-all -s clean watch",    "hint": "htmlhint public/**/*.html"
  }
  ...
}

これにより、npm startを実行すると次のようになります。

  • 公開用フォルダの削除
  • Pugファイルの監視→HTMLへ変換して公開フォルダへ→ホットリロード
  • Sassファイルの監視→CSSへ変換して公開フォルダへ→ベンダープレフィックス付与→ホットリロード
  • 画像ファイルの監視→画像圧縮して公開フォルダへ→ホットリロード

まとめ

お疲れ様でした!!

ここまでの環境を整えるのは長かったかも知れません…

しかし一回構築してしまえば、後はガリガリコーディングするのみです!!