---
title: "npm-scriptで開発環境を作ってみよう"
canonical_url: "https://olein-design.com/blog/build-webpage-by-npm-script"
language: "ja"
date_published: "2019-01-07T09:00:36+09:00"
date_modified: "2019-01-07T09:00:36+09:00"
content_type: "post"
author: "久野晃司"
taxonomies:
  category:
    - "Web制作豆知識"
  post_tag:
    - "npm-script"
---

# npm-scriptで開発環境を作ってみよう

最近、Gulpが4.xにバージョンアップしましたね。そのタイミングで開発環境でもGulpをアップデートしてエラーに困っている声を聞いたりします。

僕自身もGulpを使っている環境で4.xにアップデートして、それまで使っていたライブラリからエラーが出て四苦八苦しました。その時、ふと思ったんです。「Gulpに依存してるなー」と。そして、「だったらnpm-scriptの方がいいかも」と思い、npm-scriptで開発環境を作る流れを書いてみようと思います。

****ソースは最下部です**今回作成したコードなどのソースはGitHubに掲載してあります。最下部にリンクが置いてあるので、お急ぎの方はそちらを参照してみてください。

## タスクの流れ

1. Sassの処理
   1. node-sassでコンパイル
   2. プリフィックス付与と圧縮処理
   3. ディレクトリを削除して新しくディレクトリを作る
   4. Sassの処理をまとめて一連のタスクを流して処理
2. JavaScriptの処理
   1. バンドルする
   2. 圧縮する
   3. 一連のタスクを流して処理
3. 画像の処理
   1. 画像圧縮の処理
   2. 画像圧縮の一連の流れのタスクを作る
4. ファイルを監視する
   1. ブラウザシンクでサーバーを立ち上げる
   2. SCSS、JS、imagesを監視する
      1. HTMLの監視
      2. EJSの監視
   3. 常時監視用タスクを作る

このような流れで開発環境（タスクランナー）を作っていこうと思います。不要な部分は飛ばして読んでもらっても大丈夫だと思いますので、よしなに参照してください。

## ディレクトリ構造

今回は静的ウェブサイトの構築を想定して開発環境を作ってみます。ディレクトリ構造は以下を想定して書いていますので、よしなに噛み砕いて活用してみてください。

```
package.json
-- dist
  index.html
  -- assets
    -- css
    -- js
    -- images
-- src
  -- assets
    -- sass
      style.scss
    -- js
      index.js
    -- images
      image.jpg
      image.gif
      image.png
      image.svg
    -- html
      index.html
postcss.config.js
imagemin.js
rollup.config.js
```

## 下準備

npm-scriptを使うためには下準備が少し必要です。node.jsは入れておいてくださいね。プロジェクトのルートにターミナルで移動して、

```
$ npm init
```

を実行してください。そして、必要な情報を入力してください。そうすると、`package.json`というファイルが自動的に作られると思うので、あとは以下を読み進めて色々と試して見てください。

## Sassの処理

まずはSassの処理部分を作っていきましょう。

### node-sassでscssファイルをコンパイル

scssファイルをコンパイルする処理を作ります。ここでは`node-sass`を使います。

```
$ npm install node-sass --save-dev
```

これで`node-sass`のパッケージがインストールされます。

`scripts`は以下のように書きます。

```
"css/sass": "node-sass src/assets/sass/style.scss -o dist/css --output-style expanded --source-map dist/assets/css"
```

| -o | 出力するディレクトリを指定 |
| --- | --- |
| --output-style | 書き出した後のスタイルを指定（nested **\|** expanded **\|** compact **\|** compressed） |
| --source-map | SourceMapの書き出し先ディレクトリを指定 |

これで完了です。試しに`src/sass`内に`style.scss`を作ってから、

```
$ npm run css/sass
```

これを実行すると`dist/css`内に`style.css`と`style.css.map`が作られていることが確認できるはずです。

以下、コマンドの打ち方（ここで言う $npm run css/sass ）は省略します。適時読み替えてください。

### ベンダープレフィックス付与と圧縮処理

コンパイルしたCSSに対して、ベンダープレフィックスを自動的に付けさせ、その後CSSを圧縮させます。

ベンダープレフィックスを付与するために`autoprefixer`を、cssを圧縮するために`cssnano`を使います。そして、それらをPostCSSで処理するので`postcss-cli`も入れます。

```
$ npm install postcss-cli cssnano autoprefixer --dave-dev
```

`scripts`には以下のように書きます。

```
"css/postcss": "postcss dist/assets/css/style.css -o dist/assets/css/style.min.css"
```

そして、このスクリプトを実行すると、

```
You did not set any plugins, parser, or stringifier. Right now, PostCSS does nothing. Pick plugins for your case on https://www.postcss.parts/ and use them in postcss.config.js.
```

このようにエラーが出ます。`postcss.config.js`を用意しなさい、というようなことですね。ですので、ルートに用意します。中身はこんな感じです。

```
module.exports = {
  plugins: [
    require('autoprefixer')({
      browsers: ['last 2 versions'],
      cascade: false
    }),
    require('cssnano')({
      preset: 'default',
    })
  ]
}
```

#### ベンダープレフィックスの付与だけの場合

cssの圧縮を行わない（必要としない）場合もあると思いますので、こちらのケースだけの場合を書いておきますね。

`postcss-cli`と`sutoprefixer`をインストールします。

```
$ npm install postcss-cli autoprefixer --save-dev
```

`scripts`にはこう書きます。

```
"css/postcss/autoprefix": "postcss dist/assets/css/style.css -o dist/assets/css/style.css -c=postcss.config.js"
```

そして、`postcss.config.js`には、

```
module.exports = {
  plugins: [
    require('autoprefixer')({
      browsers: ['last 2 versions'],
      cascade: true
    })
  ]
}
```

というような感じで書いておきます。`cascade`というのは、出力値を整列させるかどうか、という指定です。

```
body {
// cascade: false の場合
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;

// cascade: true の場合
-webkit-user-select: none;
    -moz-user-select: none;
      -ms-user-select: none;
             user-select: none;
}
```

先ほどの流れで`cascade: true` と記述していますが、デフォルトでは`cascade: true`なので、特に記述する必要はありません。

### ディレクトリを削除して新しくディレクトリを作る

どんどん処理を重ねていくと、不要なファイルが存在するようになったりして、手動で対応しなくてはならなくなったりと面倒です。

ですので、毎回コンパイル〜ベンダープレフィックス付与〜圧縮を行う前に`dist/css`ディレクトリを削除して、新たに`dist/css`ディレクトリを作成するという処理を行います。

`trash`を使ってディレクトリを削除することもできるのですが、こちらはディレクトリが存在しない場合などにエラーで処理が止まってしまうので微妙な感じです。ですので、ここでは`rimraf`を利用します。

こちらを使う際には特に何かしらインストールする必要はありません。

`scripts`に以下の処理を加えます。

```
"clean/css": "rimraf dist/css && mkdir -p dist/css"
```

今後、他のディレクトリなどのcleanタスクを書くのでcssのディレクトリを削除・新規作成するタスクはこのように書きました。他にもjs、imagesなどでも同様の処理を行いますので覚えておきましょう。

スクリプトを実行すると、テストで書き出した`dist/css`内のデータが消えていることが確認できます。これは、い一度`dist/css`を削除して再度`dist/css`を作成しているので、見た目では中のデータが消えたというように見えるということです。

### Sassの処理をまとめて一連のタスクを流して処理

ここまで作ったタスクをまとめて１つのスクリプトで実行できるようにします。この考え方はnpm-scriptを利用して開発環境を作る上ではベースとなる考え方・作り方なのでよく覚えておきましょう。

流れとしては、

1. ディレクトリをクリーンにする
2. scssファイルをコンパイル
3. ベンダープレフィックスを付与
4. 圧縮

となります。

タスクを流れで処理するために`npm-run-all`を利用します。

```
$ npm install npm-run-all --save-dev
```

スクリプトはこのように書きます。

```
"css": "npm run clean/css && npm-run-all -p css/*"
```

`npm-run-all`にはこのようなオプションがあります。

| -p | 並行処理 |
| --- | --- |
| -s | 順次処理 |

ここでは、コンパイルしてからプリフィックス付与、圧縮と順次処理してほしいのでこのように設定しています。順次というのは、`package.json`の`script`に書いてある順に、という意味です。

## JavaScriptの処理

ここからはJavaScriptの処理について書いていきます。といっても、特に難しいことをするつもりはなく（あまり僕の環境でそこまで必要じゃないので）、ただバンドルして圧縮するだけです。

### バンドルする

まずは複数あるjsファイルを１つのファイルにまとめます。ここでは`rollup`を利用します。

```
$ npm install rollup --save-dev
```

あと、付随する`rollup`のプラグインも入れておきましょう。

```
$ npm install rollup-plugin-node-resolve rollup-plugin-commonjs rollup-plugin-babel --save-dev
```

そして、大元の`@babel/core`も入れないとエラーが出ます。あと、`@babel/preset-env`も。

```
$ npm install @babel/core @babel/preset-env --save-dev
```

そして、ルートに`rollup.config.js`を用意して以下のように書いてみます。

```
import resolve  from 'rollup-plugin-node-resolve';
import commonjs from 'rollup-plugin-commonjs';
import babel    from 'rollup-plugin-babel';

export default {
    output: {
        format: 'iife',
        dir: 'dist/assets/js'
    },
    plugins: [
        resolve({ jsnext: true }),
        commonjs(),
        babel({
            presets: [
                [
                    "@babel/preset-env", {
                    "modules": false,
                    "targets": {
                        "browsers": ['last 2 versions']
                    }
                }
                ]
            ],
            babelrc: false
        })
    ],
    experimentalCodeSplitting: true
};
```

`output:`の部分で`format`と`dir`を設定しています。

| format | 書き出すフォーマットを設定（`amd, cjs, esm, iife, umd`） |
| --- | --- |
| dir | 書き出し先を設定 |

詳しくはこちらを見てみるとオプションとか書いてあるのでわかりやすいです。

[埋め込みコンテンツ](https://rollupjs.org/guide/en#javascript-api)

### 圧縮する

バンドルしたJavaScriptを圧縮します。ここでは`uglify-js`を利用します。

```
$ npm install uglify-js --save-dev
```

スクリプトにはこのように記述しています。

```
"js/uglifyjs": "uglifyjs dir/assets/js/index.js -o dir/assets/js/index.min.js"
```

### 一連のタスクを流して処理

JavaScriptに関する一連のタスクを流して処理を作ります。詳しくは上記のCSS部分を参照してください。ここでは同じような処理なので詳しい説明は割愛します。

まず、CSSの時と同じように`dist`ディレクトリを綺麗にして作り直すタスクを作ります。

```
"clean/js": "rimraf dist/assets/js && mkdir -p dist/assets/js"
```

そして、JavaScriptのバンドル→圧縮の処理をディレクトリを綺麗にした後に動作させます。

```
"js": "npm run clean/js && npm-run-all -s js/*"
```

## 画像の処理

画像の処理といってもそれほどやることは多くありません。画像の容量を最適化して`dist`ディレクトリにコピーするくらいです。

### 画像圧縮の処理

ここでがちょっと多めのパッケージを使います。

| [imagemin](https://www.npmjs.com/package/imagemin) | 画像を圧縮するパッケージの親玉みたいもの |
| --- | --- |
| [imagemin-keep-folder](https://www.npmjs.com/package/imagemin-keep-folder) | ディレクトリ構造を保ったままにする |
| [imagemin-mozjpeg](https://www.npmjs.com/package/imagemin-mozjpeg) | JPEGの圧縮用 |
| [imagemin-pngquant](https://www.npmjs.com/package/imagemin-pngquant) | PNGの圧縮用 |
| [imagemin-gifsicle](https://www.npmjs.com/package/imagemin-gifsicle) | GIFの圧縮用 |
| [imagemin-svgo](https://www.npmjs.com/package/imagemin-svgo) | SVGの圧縮用 |

全て一緒にインストールしてあげましょう。

```
$ npm install imagemin imagemin-keep-folder imagemin-mozjpeg imagemin-pngquant imagemin-gifsicle imagemin-svgo --save-dev
```

そして、ルートに`imagemin.js`という設定用ファイルを用意して、そちらをnodeで実行する形でタスクを実行します。`imagemin.js`の記述はこのような感じです。

```
const imagemin         = require('imagemin-keep-folder');
const imageminMozjpeg  = require('imagemin-mozjpeg');
const imageminPngquant = require('imagemin-pngquant');
const imageminGifsicle = require('imagemin-gifsicle');
const imageminSvgo     = require('imagemin-svgo');

imagemin(['src/assets/images/**/*.{jpg,png,gif,svg}'], {
    plugins: [
        imageminMozjpeg({ quality: 80 }),
        imageminPngquant({ quality: '65-80' }),
        imageminGifsicle(),
        imageminSvgo()
    ],
    replaceOutputDir: output => {
        return output.replace(/images\//, '../../dist/assets/images/')
    }
}).then(() => {
    console.log('Images optimized');
});
```

スクリプトの方にはこのようなタスクを記述します。

```
"images/imagemin": "node imagemin.js"
```

これで圧縮させる部分の出来上がりです。

### 画像圧縮の一連の流れのタスクを作る

まずは恒例のディレクトリをクリーンにするタスクを作ります。

```
"clean/images": "rimraf dist/assets/images && midir -p dist/assets/images"
```

そして、画像圧縮のタスクを`clean/images`のあとで処理させます。

```
"images": "npm run clean/images && npm-run-all -s images/*"
```

これで画像の圧縮から一連の流れが完成です。

## ファイルを監視する

ここからは実際の開発を進めていく上で、効率よくコードがかけるようにするためのチップスになります。**ファイルを監視する**というタイトルになっていますが、読んで字のごとく、ファイルを更新したかどうかを常に監視する部分になります。

### ブラウザシンクでサーバーを立ち上げる

まずは`browser-sync`でサーバーを立ち上げます。まずはインストール。

```
$ npm install browser-sync --save-dev
```

そして、スクリプトには、

```
"watch/server": "browser-sync start -s dist -f 'src, **/*.html, !node_modules/**/*'"
```

ここでは、サーバーでは`dist`のデータを表示するようにしています。詳しくはこちらを読むと全てわかります。

[埋め込みコンテンツ](https://www.browsersync.io/docs)

*英語だけどきになるコマンドとかをページ内検索すればすぐに見つけられるよ*

### SCSS、JS、imagesを監視する

ではまずSCSSファイルの監視から。`watch`パッケージをインストールします。

```
$ npm install watch --save-dev
```

そして、SCSSファイルがある`src/assets/sass/`を監視させます。スクリプトにはこんな感じで書きました。

```
"watch/css": "watch 'npm run css' ./src/assets/sass"
```

`./src/assets/sass`で変更があれば`npm run css`を実行しろ、ということが書いてあります。

---

JavaScriptの監視もSCSSと同じ要領でOKです。

`src/assets/js/`を監視させるので、

```
"watch/js": "watch 'npm run js' ./src/assets/js"
```

---

画像の監視には`onchange`を利用します。

```
$ npm install onchange --save-dev
```

そしてスクリプトには、

```
"watch/images": "onchange 'src/assets/images' -e '**/*.DS_Store' -- npm run images"
```

これで画像に変更を加えると、`npm run images`が走るようになります。

#### HTMLの監視

おまけですが、テンプレートエンジンを使わずに直にHTMLファイルを`src`ディレクトリ下で作成していく場合には、`cpx`というパッケージを使って、`dist`ディレクトリへコピーします。`cpx`の良いところはディレクトリ構造を保持したままコピーしてくれるところです。

```
$ npm install cpx --save-dev
```

そして、スクリプトでこんな感じに書いています。

```
"html/plain": "cpx 'src/assets/html/**/*.html' 'dist/'",
"html": "npm-run-all html/*",
```

監視させる記述に関してはこのように書いてみました。

```
"watch/html": "watch 'npm run html' ./src/assets/html",
```

#### EJSの監視

EJSなどのテンプレートエンジンを使うこともあるかと思います（Jadeとかもやり方は同じような感じのはず）。ここではEJSのコンパイルを取り上げてみます。まずは、`ejs-cli`パッケージをインストールします。

```
$ npm install ejs-cli --save-dev
```

スクリプトにはこのように書いてみました。

```
"html/ejscli": "ejs-cli --base-dir src/assets/ejs '**/*.ejs' --out dist"
```

これで`src/assets/ejs`内の`*.ejs`ファイルをコンパイルしてくれます。階層なども保持したまま`dist`に出力されます。

### 常時監視用タスクを作る

ここまで作ってきたタスクを１つにまとめて一括して実行できるようにすることでとても便利になります。

```
"watch": "npm-run-all -p watch/*"
```

こうすることで、開発を開始する際にはコマンドラインで`npm run watch`を打つだけで、サーバーが立ち上がり、プロジェクトのファイルを監視開始してくれます。

## まとめ

このような流れで開発環境を作ることができます。最初は黒い画面に慣れていない方は、ちょっと腰が重くなってしまうかもしれませんが、失敗したら消しちゃえば良いんで安心してください。

GulpやGruntなどといったタスクランナーはnpmの上で動いています。ですので、大元のnpm scriptsで開発環境を作ることで、外枠（GulpやGruntなどのバージョンアップなど）の影響を受けにくいものを手に入れることができます。

どんどん失敗して、最適な開発環境を構築してみてください。

今日作ったものはこちらに置いておきますね。

[埋め込みコンテンツ](https://github.com/Olein-jp/static-starter)
