タスクランナーとしてのnpm-scripts(パート1)

私たちの選択できるビジネスツールや開発ツール、またはサービスは数多く存在します。これらをうまく取り入れることで作業効率をアップし、より重要なことに時間を当てることができます。

コードグラフではリサーチや試用期間を経て、扱いやすさはもちろん主に相互連動性や信頼性、ワークフローなどの側面からよりベターなツールを絞り込んで選ぶようにしています。そしてなるべくワークフローをシンプルに保てるよう、使用するツールの数は抑えます。また便利であっても学習コストが極端に高いツールは採用しないようにしています。チーム内外での扱いやすさ、学習コストにどう影響するかも重要です。

この記事または今後、私たちのチームが実際にワークフローに組み込んでいるツールやサービスを随時紹介していこうと思います。

タスクランナ―としてのnpm-scripts

フロントエンド開発では、複雑なタスクをより自動化して効率よく作業を行うためのタスクランナ―が欠かせません。Grunt、gulp、Webpackを組み合わせた構成やGUIを提供するCodekit等、いつかのオプションがあります。

コードグラフではこれまでそれらのツールを試してきましたが、最近npm-scriptsをタスクランナーとして使うようになりました。npm-scriptsとはpackage.jsonでサポートされるスクリプトで、シェルスクリプトとエイリアスコマンドを使用できます。package.json内のscriptsプロパティに記述します。

scripts | npm Documentation

npm-scriptsを使うメリット

npm-scriptsを使う、ざっと下記のようなメリットが挙げられます。

  • ラッパーに依存せず、直接npmのツール群を扱える。
  • タスクランナー独自の設定・ルールに縛られない。
  • package.jsonのみでパッケージ管理と処理が一元管理できる。

このように、ミニマムな構成で開発環境を共有できるのが魅力的です。

コラボレーターは、$ npm installでパッケージ展開を行い、package.jsonのscriptsプロパティに全て記載されているタスクを$ npm runコマンドを使って実行するだけで環境を実装できます。グローバルな依存はNode.jsのみなので、比較的環境を汚さないといえます。

実際に、npm-scriptsを使ったタスクランナーのサンプルを紹介したいと思います。

サンプルプロジェクト構成

今回はシンプルな静的サイトの構築を想定して、下記のファイルを扱うこととします。

  • HTML
  • CSS
  • JavaScript
  • Images

コードグラフではCSSはPostCSSを導入、JSはES6のバージョンで開発し、比較的最新の仕様でコーディングを行うように心掛けています。

PostCSS
PostCSSはJSプラグインでスタイルを変換するためのツールです。これらのプラグインは、CSS、変数やミックスインのサポート、将来のCSS構文、インライン画像などの表示に役立ちます。
PostCSS - a tool for transforming CSS with JavaScript

そこで下記のファイル構成を用意します。

├─ dist
├─ src
│   ├─ css
│   │   └─ main.pcss
│   ├─ images
│   └─ js
│         └─ main.js
├─ index.html
└─ package.json

このファイル構成をベースに下記のタスクを実現したいと思います。

  • 拡張CSSをPostCSSでトランスパイルする
  • ES6で記述されたJavaScriptをBabelでES5にトランスパイルする
  • 画像を圧縮する
  • ファイル変更時に上記の処理を自動的に行う
  • Browsersyncでローカルサーバーを立ち上げ、ファイル変更時にブラウザを自動リロードさせる
  • CSSとJavaScriptの構文チェックを行う

※PostCSSで標準構文へトランスパイルする前のCSSは、区別しやすいように拡張子を.pcss としています。

それでは順を追って機能を実装していきます。

package.json

これは最初に用意するpackage.jsonの雛形です。

{
"name": "sample",
  "version": "1.0.0",
  "scripts": {
  },
  "devDependencies": {
  }
}

公式ドキュメントによると、nameとversionフィールドは必須項目なので必ず入力するように、とあります。プロジェクト固有のドメイン名とバージョンを設定しておきます。

scriptsプロパティにタスクを、devDependenciesオブジェクトにタスクに必要なパッケージを追加していきます。パッケージの追加は直接記述せずにコマンドラインで行います。

CSSのビルドタスク

前述したPostCSSをCLIで操作するために、postcss-cliをインストールします。

$ npm i -D postcss-cli

下記コマンドを実行してみます。

./node_modules/.bin/postcss src/css/main.pcss -o dist/css/main.css

src/css/main.pcss をPostCSSで処理し、dist/css/main.cssを出力しました。デフォルトでインラインのソースマップが記述される以外、PostCSS自体はCSSコードに手を加えることはありません。内容は変更されずそのまま出力されているかと思います。

ひとまずこのタスクを、package.jsonのscriptsプロパティに追加します。

"scripts": {
  "build:css": "postcss src/css/main.pcss -o dist/css/main.css --no-map"
},

これは公開用のCSSをビルドするタスクなので最後の--no-mapオプションでSourcemapを生成しないようにします。これで、下記コマンドでこのタスクを実行できるようになりました。

$ npm run build:css

私たちのチームはCSS開発のために下記のPostCSSプラグインを標準で導入しています。

  • postcss-cssnext
  • postcss-import
  • cssnano
postcss-cssnext
次世代CSSの構文で記述することができるプラグインです。ブラウザのベンダープレフィックスを自動で付与する機能や、カスタム変数、セレクタのネストなど、CSS開発に便利な機能が揃っています。
postcss-cssnext
cssnext - Use tomorrow’s CSS syntax, today.
postcss-import
@import 記述で別ファイルのインポートを可能にします。CSSをコンポーネント管理するのに便利です。
postcss-import
cssnano
CSSの圧縮を行います。
cssnano
cssnano: A modular minifier based on the PostCSS ecosystem. - cssnano

まとめてインストールします。

$ npm i -D postcss-cssnext postcss-import cssnano

これらのプラグインを使う際は、PostCSS CLIの -u オプションで指定することができますが、スクリプトの見通しを良くするために、PostCSSの設定ファイルから有効化するのがよいでしょう。必要に応じてプラグイン固有のオプションを指定することもできます。

PostCSSの設定ファイルは、postcss.config.js というファイル名でプロジェクトルートに設置します。CLIでオプションを設定する場合は、postcss.config.jsのctx.optionsを参照することが必須のようなので、次のように記述してください。

module.exports = (ctx) => ({
    map: ctx.options.map,
    plugins: [
        require('postcss-import')(),
        require('postcss-cssnext')({
            warnForDuplicates: false
        }),
        require('cssnano')()
    ]
})

プラグインをrequireで読み込みますが、処理のプロセスを考慮してpostcss-importを最初に読み込むようにしないとうまく動作しないので気をつけます。

また、cssnextとcssnanoのautoprefixerが重複するためそのままだと警告が出ます。そのまま無視しても大丈夫そうですが、毎回表示されるのも邪魔なのでcssnextのwarnForDuplicatesオプションをfalseにしておきます。

これで、PostCSSのCSSトランスパイル環境が実現できました。

ちなみに、私たちのチームはOOCSS(Object-Oriented CSS)を取り入れたCSSの設計を行っており、複数のレイヤーごとに構成された複数の.pcssファイルをmain.pcssにインポートすることでコンポーネント管理を実現しています。インポートされた後は、cssnextの拡張構文が通常のCSSコードに変換され、cssnanoで圧縮されて、最後にdist/css/main.cssとして出力されます。

現時点のpackage.jsonはこうなっています。

{
  "name": "sample",
  "version": "1.0.0",
  "scripts": {
    "build:css": "postcss src/css/main.pcss -o dist/css/main.css --no-map"
  },
  "devDependencies": {
    "cssnano": "^3.10.0",
    "postcss-cli": "^4.1.0",
    "postcss-cssnext": "^3.0.2",
    "postcss-import": "^10.0.0"
  }
}

CSSのビルドタスクを実行すると、ファイル構成は次のようになっているはずです。

├─ dist
│   └─ css
│         └─ main.css
├─ src
│   ├─ css
│   │   └─ main.pcss
│   ├─ images
│   └─ js
│         └─ main.js
├─ index.html
├─ package-lock.json
├─ package.json
└─ postcss.config.js

※package-lock.jsonは、node_moduleのツリー情報を管理するために、自動生成されるファイルです。

ちょっと長くなってきたのでパート2の記事に続きたいと思います。