かんがるーさんの日記

最近自分が興味をもったものを調べた時の手順等を書いています。今は Spring Boot をいじっています。

Spring Boot + npm + Geb で入力フォームを作ってテストする ( その11 )( PostCSS で common.css を minify する + autoprefixer, stylelint を導入する )

概要

記事一覧はこちらです。

Spring Boot + npm + Geb で入力フォームを作ってテストする ( その10 )( 各画面の HTML を作成する3 ) の続きです。

  • 今回の手順で確認できるのは以下の内容です。
    • これまでは cpx パッケージで watch してコピーするだけでしたが、PostCSS で watch + コピー + minify するように変更します。
    • postcss の autoprefixer, stylelint プラグインも導入します。

参照したサイト・書籍

  1. PostCSS
    https://github.com/postcss/postcss

  2. PostCSS CLI
    https://github.com/postcss/postcss-cli

  3. cssnano
    https://github.com/ben-eb/cssnano

  4. Autoprefixer
    https://github.com/postcss/autoprefixer

  5. Browserslist
    https://github.com/ai/browserslist

  6. stylelint
    https://github.com/stylelint/stylelint

  7. stylelint-config-standard
    https://github.com/stylelint/stylelint-config-standard

  8. ‘at-rule-empty-line-before’ do not accept except + ignore
    https://github.com/stylelint/stylelint/issues/691

  9. PostCSS まとめ
    http://qiita.com/morishitter/items/4a04eb144abf49f41d7d

  10. Sassを捨ててPostCSSに移行したのでそのときの工程メモ
    http://qiita.com/nabeliwo/items/0aeea21e95f3fbab3955

  11. ここがすごいぞ! stylelint!
    http://qiita.com/inuscript/items/ff4f6972c988afbec3a8

  12. PostCSSとstylelintの環境構築
    http://qiita.com/buchiya4th/items/01b4ad050b7c59b48539

目次

  1. はじめに
  2. PostCSS と cssnano プラグインをインストールして minify してみる
    1. PostCSS をインストールする
    2. cssnano プラグインをインストールする
    3. postcss.config.js を作成する
    4. npm-scripts を追加する
    5. 動作確認する
    6. npm-scripts を変更して正式に取り込む
    7. 動作確認する(その2)
  3. Autoprefixer プラグインを追加する
    1. Autoprefixer プラグインをインストールする
    2. postcss.config.js を変更する
    3. 動作確認する
  4. stylelint プラグインを追加する
    1. stylelint プラグインと stylelint-config-standard をインストールする
    2. postcss.config.js を変更する
    3. stylelint.config.js を作成する
    4. 動作確認する
    5. stylelint で出力されたメッセージに対応する
  5. 次回は。。。

手順

はじめに

作成している common.css を単にコピーするだけでなく圧縮(minify)もしたいと思い調べたところ、PostCSS を使えば出来るようなので、試してみます。

PostCSS ではいろいろなプラグインを組み合わせて使用しますが、今回は以下のプラグインを入れてみます。

  • Autoprefixer(必要なベンダープリフィックスを自動で付与する)
  • cssnano(minify)
  • stylelint(CSSリンター)

PostCSS と cssnano プラグインをインストールして minify してみる

PostCSS をインストールする

npm install --save-dev postcss-cli コマンドを実行してインストールします。

f:id:ksby:20170730191424p:plain f:id:ksby:20170730191623p:plain

cssnano プラグインをインストールする

npm install --save-dev cssnano コマンドを実行してインストールします。

f:id:ksby:20170805011113p:plain f:id:ksby:20170805011246p:plain f:id:ksby:20170805011400p:plain f:id:ksby:20170805011510p:plain

postcss.config.js を作成する

プロジェクトのルートディレクトリに postcss.config.js を新規作成し、以下の内容を記述します。

module.exports = {
    plugins: [
        require('cssnano')({
            preset: 'default'
        })
    ]
};

npm-scripts を追加する

package.json の scripts に以下の記述を追加します。

  "scripts": {
    ..........
    "copy:css:watch": "cpx src/main/assets/css/**/* src/main/resources/static/css -w",
    "postcss:watch": "postcss src/main/assets/css/**/* -d src/main/resources/static/css -x .min.css -w --poll",
    "server": "run-p copy:css:watch webpack:watch browser-sync:start"
  },
  • "postcss:watch": "postcss src/main/assets/css/**/* -d src/main/resources/static/css -x .min.css -w --poll" を追加します。-w オプションだけだとなぜかうまく watch してくれない(数回変更を検知するとそれ以降検知しなくなる時がある)ので、--poll オプションも追加しました。
    ※この後の動作確認の時はまだ --poll オプションは付けていません。うまく動かない時があることに気付いて後から付けています。
  • postcss を実行した同じディレクトリに postcss.config.js があると自動で読み込んでくれるようなので -c postcss.config.js は付けません。

動作確認する

src/main/resources/static の下の css ディレクトリを削除します。

npm run postcss:watch コマンドを実行します。

f:id:ksby:20170730231735p:plain

src/main/resources/static の下を見ると css/common.min.css が生成されており、

f:id:ksby:20170730231349p:plain

common.min.css を開くと minify されていました。

f:id:ksby:20170730231845p:plain

src/main/assets/css/common.cssbackground-color: ivory;background-color: snow; に変更すると、

f:id:ksby:20170730232034p:plain

再び Finished src\main\assets\css\common.css のメッセージが出力されて、

f:id:ksby:20170730232154p:plain

src/main/resources/static/css/common.min.css に反映されます。

f:id:ksby:20170730232357p:plain

うまく動作しているようです。ただし、以下1点注意があります。

  • IntelliJ IDEA で src/main/assets/css/common.css を変更して、IntelliJ IDEA を Active な Window にしたまま src/main/resources/static/css/common.min.css を表示しても反映はされませんでした。一度 IntelliJ IDEA から他のプロセスに Active な Window を切り替えないと(マウスか Alt+Tab 等のキーで別の Window を Active にしないと)反映されませんでした。

npm-scripts を変更して正式に取り込む

package.json の scripts を以下のように変更します。

  "scripts": {
    ..........
    "copy:ionicons": "cpx node_modules/ionicons/dist/{css,fonts}/**/* src/main/resources/static/vendor/ionicons",
    "postcss:watch": "postcss src/main/assets/css/**/* -d src/main/resources/static/css -x .min.css -w --poll",
    "webpack": "webpack",
    "webpack:watch": "webpack --watch",
    "browser-sync": "browser-sync",
    "browser-sync:start": "browser-sync start --config bs-config.js",
    "server": "run-p postcss:watch webpack:watch browser-sync:start"
  },
  • "copy:css:watch": "cpx src/main/assets/css/**/* src/main/resources/static/css -w" を削除します。
  • "server" 内の copy:css:watchpostcss:watch に変更します。
  • "postcss:watch" を記述する位置を "webpack" の上に変更します。

動作確認する(その2)

今度は npm run server コマンドを実行した後、browser-sync でブラウザに反映されるか確認します。

static/input01.html を以下のように変更します。

<head>
  ..........

  <!-- 画面共通で使用する css -->
  <link rel="stylesheet" href="/css/common.min.css">
</head>
  • head タグ内の <link rel="stylesheet" href="/css/common.css"><link rel="stylesheet" href="/css/common.min.css"> に変更します。

npm run server コマンドを実行します。

f:id:ksby:20170730234240p:plain

ブラウザで http://localhost:9080/inquiry/input01.html にアクセスすると以下の画面が表示されます。

f:id:ksby:20170730234403p:plain

src/main/assets/css/common.cssbackground-color: snow;background-color: white; に変更すると、

f:id:ksby:20170730234558p:plain

postcss と browser-sync のメッセージが出力されて、

f:id:ksby:20170730234803p:plain

ブラウザに表示している入力画面1の背景色が white に変更されました。

f:id:ksby:20170730234913p:plain

問題なさそうです。

src/main/assets/css/common.cssbackground-color: ivory; に戻した後、Ctrl+C を押して npm run server コマンドを停止します。また static の下の input01.html 以外の html ファイルも <link rel="stylesheet" href="/css/common.min.css"> に変更します。

Autoprefixer プラグインを追加する

Autoprefixer プラグインをインストールする

npm install --save-dev autoprefixer コマンドを実行してインストールします。

f:id:ksby:20170803002524p:plain

postcss.config.js を変更する

postcss.config.js に autoprefixer の設定を追加します。

module.exports = {
    plugins: [
        require('autoprefixer')({
            browsers: [
                "last 2 versions"
            ]
        }),
        require('cssnano')({
            preset: 'default'
        })
    ]
};
  • require('autoprefixer')({ ... }) を追加します。

動作確認する

npm run server コマンドを実行します。src/main/resources/static/css/common.min.css は作り直されましたが、今のファイルだと何の prefix も追加されませんでした。

src/main/resources/static/css/common.min.cssa { display: flex; } を追加してみます。

a {
    display: flex;
}
/* body 内の背景色を白より少し色がついた色にする */
.content-wrapper {
    background-color: ivory;
}
..........

変更が検知されて、

f:id:ksby:20170803000914p:plain

src/main/resources/static/css/common.min.css を見ると vendor prefixes が自動で追加されていました。

f:id:ksby:20170803001118p:plain

stylelint プラグインを追加する

stylelint プラグインと stylelint-config-standard をインストールする

stylelint で適用するルールは stylelint-config-standard をそのまま使うことにします。npm install --save-dev stylelint stylelint-config-standard コマンドを実行してインストールします。

f:id:ksby:20170803061012p:plain f:id:ksby:20170803061142p:plain

postcss.config.js を変更する

postcss.config.js の以下の点を変更します。

module.exports = {
    plugins: [
        require('stylelint'),
        require('autoprefixer')({
            browsers: [
                "last 2 versions"
            ]
        }),
        require('cssnano')({
            preset: 'default'
        })
    ]
};
  • require('stylelint') を追加します。記述された順序でプラグインが適用されるので、一番最初に stylelint を記述します。最初はアルファベット順に一番最後に記述したら common.min.css に対して stylelint が適用されて大量にメッセージが出力されました。。。

stylelint.config.js を作成する

プロジェクトのルートディレクトリの直下に stylelint.config.js を新規作成し、以下の内容を記述します。

module.exports = {
    extends: "stylelint-config-standard"
}

動作確認する

npm run server コマンドを実行します。

common.min.css 生成時に stylelint が実行されて以下の画像のように大量のメッセージが出力されました。動作しましたが、大した行数の CSS でもないのに結構メッセージが出ますね。。。

f:id:ksby:20170803065048p:plain f:id:ksby:20170803065234p:plain

stylelint で出力されたメッセージに対応する

出力されたメッセージは以下の8種類でした。出力されたメッセージの説明は https://github.com/stylelint/stylelint/tree/master/lib/rules にあります。

  • Expected indentation of 2 spaces (indentation)
    • インデントがスペース2個でないというメッセージでした。IntelliJ IDEA だとデフォルトではスペース4個に設定されているので、stylelint.config.js に "indentation": 4 を追加して設定を変更します。
  • Expected empty line before comment (comment-empty-line-before)
    • コメントの前に1行空行がないというメッセージでした。コメントの前に1行空行を追加します。
  • Expected a leading zero (number-leading-zero)
    • 小数点以下がある数値の記述を 0.nnn ではなく .nnn のように先頭の 0 がない書き方をしていたので出力されていました。指摘された CSS は Bootstrap の css ファイルから持ってきたものなので、ここは .nnn の書き方でもメッセージが出力されないよう stylelint.config.js に "number-leading-zero": "never" を追加して設定を変更します。
  • Unexpected unit (length-zero-no-unit)
    • 値が 0 の時に 0px のように単位を記述していたので出力されているメッセージでした。単位を削除して 0px → 0 に変更します。
  • Expected single space after ":" with a single-line declaration (declaration-colon-space-after)
    • ここまでの修正でメッセージが出なくなったので対応しません。
  • Expected empty line before rule (rule-empty-line-before)
    • 定義と定義の間に空行を入れていないというメッセージでした。空行を入れずに連続して記述したい場合があるので、stylelint.config.js に "rule-empty-line-before": "never-multi-line" を追加して設定を変更します。
  • Expected indentation of 4 spaces (indentation)
    • ここまでの修正でメッセージが出なくなったので対応しません。
  • Expected empty line before at-rule (at-rule-empty-line-before)
    • .label-required を定義しているところで @media (min-width: 768px) { ... }@media (max-width: 767px) { ... } の間に空行を入れていないというメッセージでした。ここに空行は入れたくないので、stylelint.config.js に "at-rule-empty-line-before": [ "always", {"except": ["after-same-name"]} ] を追加して設定を変更します。

ここまで対応すると、追加で以下の3種類のメッセージが表示されました。

  • Expected empty line before comment (comment-empty-line-before)
    • /* ... */ のコメント行を空行を入れずに2行続けて書いていたために出ているメッセージでした。コメント行は連続で書けるようにしたいので、stylelint.config.js に "comment-empty-line-before": [ "always", {"ignore": ["after-comment"]} ] を追加して設定を変更します。
  • Expected empty line before at-rule (at-rule-empty-line-before)
    • コメント行の後に @media (min-width: 768px) { を記述しているところでメッセージが出ていました。上で追加した "at-rule-empty-line-before": [ ... ] の中に "ignore": ["after-comment"] を追加して、チェックされないようにします。
  • Unexpected empty line before rule (rule-empty-line-before)
    • .form-group .float-label の定義の前に空行が入っていたのでエラーが出ていました。ここは空行を取り除きます。

以上の対応で stylelint からメッセージが出ないようになりました。最終版の stylelint.config.js は以下のようになります。

module.exports = {
    extends: "stylelint-config-standard",
    rules: {
        "at-rule-empty-line-before": [
            "always",
            {
                "except": ["after-same-name"],
                "ignore": ["after-comment"]
            }
        ],
        "comment-empty-line-before": [
            "always",
            {
                "ignore": ["after-comment"]
            }
        ],
        "indentation": 4,
        "number-leading-zero": "never",
        "rule-empty-line-before": "never-multi-line"
    }
};

次回は。。。

Controller クラスと Thymeleaf テンプレートファイルを作成する予定です。

ソースコード

package.json

{
  "name": "boot-npm-geb-sample",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "postinstall": "run-s clean:static-dir copy:all",
    "clean:static-dir": "rimraf src/main/resources/static/*",
    "copy:all": "run-p copy:bootstrap copy:admin-lte copy:font-awesome copy:ionicons",
    "copy:bootstrap": "cpx node_modules/bootstrap/dist/**/* src/main/resources/static/vendor/bootstrap",
    "copy:admin-lte": "cpx node_modules/admin-lte/dist/**/* src/main/resources/static/vendor/admin-lte",
    "copy:font-awesome": "cpx node_modules/font-awesome/{css,fonts}/**/* src/main/resources/static/vendor/font-awesome",
    "copy:ionicons": "cpx node_modules/ionicons/dist/{css,fonts}/**/* src/main/resources/static/vendor/ionicons",
    "postcss:watch": "postcss src/main/assets/css/**/* -d src/main/resources/static/css -x .min.css -w --poll",
    "webpack": "webpack",
    "webpack:watch": "webpack --watch",
    "browser-sync": "browser-sync",
    "browser-sync:start": "browser-sync start --config bs-config.js",
    "server": "run-p postcss:watch webpack:watch browser-sync:start"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "admin-lte": "^2.3.11",
    "bootstrap": "^3.3.7",
    "font-awesome": "^4.7.0",
    "ionicons": "^3.0.0"
  },
  "devDependencies": {
    "autoprefixer": "^7.1.2",
    "browser-sync": "^2.18.13",
    "cpx": "^1.5.0",
    "cssnano": "^3.10.0",
    "npm-run-all": "^4.0.2",
    "postcss-cli": "^4.1.0",
    "rimraf": "^2.6.1",
    "stylelint": "^8.0.0",
    "stylelint-config-standard": "^17.0.0",
    "webpack": "^3.3.0"
  }
}

postcss.config.js

module.exports = {
    plugins: [
        require('stylelint'),
        require('autoprefixer')({
            browsers: [
                "last 2 versions"
            ]
        }),
        require('cssnano')({
            preset: 'default'
        })
    ]
};

stylelint.config.js

module.exports = {
    extends: "stylelint-config-standard",
    rules: {
        "at-rule-empty-line-before": [
            "always",
            {
                "except": ["after-same-name"],
                "ignore": ["after-comment"]
            }
        ],
        "comment-empty-line-before": [
            "always",
            {
                "ignore": ["after-comment"]
            }
        ],
        "indentation": 4,
        "number-leading-zero": "never",
        "rule-empty-line-before": "never-multi-line"
    }
};

common.css

/* body 内の背景色を白より少し色がついた色にする */
.content-wrapper {
    background-color: ivory;
}

/* フォーカス時の入力フィールドの色合いが adminLTE より Bootstrap のものが好みなので、そちらに定義し直す */
.form-control:focus {
    border-color: #66afe9;
    outline: 0;
    -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(102, 175, 233, .6);
    box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(102, 175, 233, .6);
}

/* form-control-static の文字列を入力項目のすぐ下に隙間をほとんど空けずに表示されるようにする */
.form-control-static {
    padding-top: 0;
    padding-bottom: 0;
    margin-bottom: -15px;
}

/* 入力チェックエラーの入力項目の背景色を薄い赤色にする */
.has-error .form-control {
    background-color: #fff5ee;
}

/* form-horizontal 内で form-control, form-control-static-inline の入力フィールドを横並びで表示する */
.form-control-inline {
    float: left;
    margin-right: 10px;
}
.form-control-static-inline {
    float: left;
    padding-top: 7px;
    margin-right: 10px;
}

/* checkbox の文字列の右側に少し隙間を空ける */
.checkbox label {
    margin-right: 10px;
}

/* 必須ラベル */
/* 767px 以下の場合には必須ラベルを右側に移動する */
@media (min-width: 768px) {
    .label-required {
        background-color: red;
        margin-right: 5px;
    }
    .form-group .float-label {
        float: right;
    }
}
@media (max-width: 767px) {
    .label-required {
        background-color: red;
        margin-left: 5px;
    }
}

/* 画面下のボタンを大きく表示させる */
.btn {
    width: 150px;
}

履歴

2017/08/05 初版発行。