gulp-sassで自動でSass/SCSSからcssファイルを生成する - 人生リアルタイムアタック の続き。完全上位互換のつもり。
前回は gulp-sass の導入だけだったので、この記事では gulp / Sass (SCSS) 周り全般について述べたい。
構成
以下のディレクトリ構成を例にする。
./test ├── css │ └── // cssファイルの生成先 └── assets │ └── sass │ └── **/*.scss └── index.html
Node.jsは LTSのv4.4.5 がインストール済みで、$ npm
コマンドが利用可能になっている前提。
package.jsonをつくる
最初に、Node.jsのパッケージ管理ファイルであるpackage.jsonを生成する。 package.jsonはコマンドで生成できる。
$ npm init -y Wrote to /Users/<user_name>/git/test/package.json: { "name": "test", "version": "1.0.0", "description": "", "main": "index.js", "dependencies": {}, "devDependencies": {}, "scripts": { # 後でちょっと追加する予定 "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], "author": "", "license": "ISC" }
scripts に gulp default コマンドを追加する
package.jsonのscripts部分に "start": "gulp default",
を追加する。詳細は後述。
"scripts": { "start": "gulp default", // ここを追加 "test": "echo \"Error: no test specified\" && exit 1" },
package.jsonはこうなればOK.
{ "name": "test", "version": "1.0.0", "main": "index.js", "dependencies": {}, "devDependencies": {}, "scripts": { "start": "gulp default", "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], "author": "", "license": "ISC", "description": "" }
インストール
それではgulpとgulp-sassをインストールする。
-g
オプションを付けずにプロジェクト配下にインストールすることで、複数人で開発する場合でもバージョンを揃えて利用するようにする。
先程package.jsonに "start": "gulp default",
を追加したのは、$ npm start
コマンドでプロジェクト配下にインストールしたgulpからdefaultタスクを実行する為の設定。
なお、開発環境用のツールなので--save-dev
を必ず書こう。
$ npm install gulp gulp-sass --save-dev
インストール完了後、package.jsonを開いてみよう。
... "devDependencies": { "gulp": "^3.9.1", "gulp-sass": "^2.3.2" }, ...
devDependencies
に、gulpとgulp-sassが追加されていればOK.
package.jsonとは
package.jsonとは、Node.jsのパッケージ管理ファイル。
bundleのように今後は $ npm install
するだけで同じバージョンのパッケージをインストールすることが可能になる。
ただし、 --save-dev
を付け忘れると、devDependenciesに追加されないので忘れずに付けよう。
gulpfile.jsの作成
インストールが完了したので、今度はgulp-sassを利用する為のタスクをgulpfile.jsに記述していく。
まずは最も簡単な例として、*.scssに変更があれば自動で*.cssを生成する例。
'use strict'; var SCSS_SRC = './assets/sass/**/*.scss'; var CSS_DEST = './css/'; var gulp = require('gulp'); var sass = require('gulp-sass'); // Sassコンパイルタスク gulp.task('sass-compile', function(){ return gulp.src(SCSS_SRC) .pipe(sass.sync().on('error', sass.logError)) .pipe(gulp.dest(CSS_DEST)); }); // scssファイル群の変更を監視するタスク gulp.task('scss:watch', function(){ var watcher = gulp.watch(SCSS_SRC, ['sass-compile']); watcher.on('change', function(event) { console.log('File ' + event.path + ' was ' + event.type + ', running tasks...'); }); }); // gulpのデフォルトタスクとしてscss:watchタスクを指定 gulp.task('default', ['scss:watch']);
sassとscssという単語が混在しているのはちょっと諦め気味...
なお、最近はgulp-plumberは不要。
試してみる。
ここまでで、既に自動でSCSSからcssファイルを生成できるので試してみる。
$ npm start
を叩いて、ファイルを保存するだけで既に自動でcssファイルが生成可能になる。
$ npm start > test@1.0.0 start /Users/<user_name>/git/test > gulp default [11:57:40] Using gulpfile ~/git/test/gulpfile.js [11:57:40] Starting 'scss:watch'... [11:57:40] Finished 'scss:watch' after 12 ms [11:57:40] Starting 'default'... [11:57:40] Finished 'default' after 9.47 μs
この状態で、assets/sass/hoge/fuge.scss にこんなSCSSを書いてみる。
* { font-size: 10rem; p { color: #ccc; } }
File /Users/<user_name>/git/test/assets/sass/hoge/fuge.scss was changed, running tasks... [12:01:58] Starting 'sass-compile'... [12:01:58] Finished 'sass-compile' after 35 ms
こんな感じのログがでて、./css/hoge/fuge.cssが生成されれば完了。終了するにはctrl + c
。
だが、SCSS環境構築はまだ終わりではない。
Lintツールを併用する
Sass/SCSSを書くにあたり、Lintツールを導入しなければCSS同様すぐ煩雑なコードになってしまう。
Lintツール無しで作ったSCSSはCSSの大差無く万人がみても分かりづらい。最初からLintを入れてどのように書けばいいのか学びながら書いた方がよい。
gulpがあれば自動でチェックできるので、このタイミングでLintツールを導入しよう。
Ruby製の scss-lint はドキュメントが充実しており、オプションも豊富で、一時的な除外もできて使い勝手がよい。airbnbがgithubに公開している有名なスタイルルールもこのscss-lint用だ。
ということで、scss-lintと、gulp上でscss-lintを実行する為の gulp-scss-lint の2つを導入する。
インストール
$ gem install scss_lint # -でなく_に注意。 $ npm install gulp-scss-lint --save-dev
gulpfile.jsにgulp-scss-lint設定を追加
先程つくったgulpfile.jsを以下のように3箇所書き換える。
var SCSS_SRC = './app/assets/stylesheets/**/*.scss'; var CSS_DEST = './css/'; var gulp = require('gulp'); var sass = require('gulp-sass'); var scsslint = require('gulp-scss-lint'); // 1. gulp-scss-lintを追加 ---- // Sassコンパイルタスク gulp.task('sass', function(){ ...(略) }); // 2. 以下を追加する ---- // scss-lintタスク gulp.task('scss-lint', function(){ return gulp.src(SCSS_SRC) .pipe(scsslint()); }); // ココマデ ---- // scssファイル群の変更を監視するタスク gulp.task('scss:watch', function(){ // 3. ここに 'scss-lint' を追加する ------↓ var watcher = gulp.watch(SCSS_SRC, ['scss-lint', 'sass-compile']); watcher.on('change', function(event) { console.log('File ' + event.path + ' was ' + event.type + ', running tasks...'); }); }); // gulpのデフォルトタスクとしてscss:watchタスクを指定 gulp.task('default', ['scss:watch']);
scss:watchタスク内の配列にscss-lintタスクを追加したことで、sass-compile後にscss-lintを実行するようになった。
scss-lintタスクの書き方については幾つかあるが、上記のようにlintタスク単体で記述しておけばコンパイルせずにlintだけ単体実行することも可能になるので、独立したタスクにして配列で渡すとよい。
ちなみに.scss-lint.ymlを指定する場合にはこう書く。
// scss-lintタスク gulp.task('scss-lint', function(){ return gulp.src(SCSS_SRC) .pipe(scsslint({'config': '.scss-lint.yml'})); });
Sass/SCSS書き換え時にブラウザ自動リロードしたい
Browsersync - Time-saving synchronised browser testing を利用する。
インストール
$ npm install browser-sync --save-dev
gulpfile.jsの書き換え
// HTML, Haml, JavaScriptなども監視したければこんな形で定義しておく var HTML_SRC = './template/**/*.html'; var CSS_SRC = './css/**/*.css'; // browser-sync 読み込み var bs = require('browser-sync').create(); ...(略) // browser-syncタスク gulp.task('bs', function(){ var bsOptions = {}; bsOptions.files = [HTML_SRC, CSS_SRC]; // ここに監視対象ファイルを書く bsOptions.server = './'; // 単体実行する時 // bsOptions.proxy = 'localhost:3000'; // proxy実行する時のターゲット // bsOptions.proxy = '192.168.99.100:3000'; // docker用 // bsOptions.port = 3001; // proxy実行する時のport bs.init(bsOptions); }); gulp.task('default', ['bs', 'sass-watch']); // 'bs' タスクを追加
試してみる
$ npm start > test@1.0.0 start /Users/<user_name>/git/test > gulp default [16:08:24] Using gulpfile ~/git/test/gulpfile.js [16:08:24] Starting 'bs'... [16:08:24] Finished 'bs' after 16 ms [16:08:24] Starting 'scss:watch'... [16:08:24] Finished 'scss:watch' after 14 ms [16:08:24] Starting 'default'... [16:08:24] Finished 'default' after 13 μs [BS] Access URLs: ---------------------------------------- Local: http://localhost:3000 External: http://xxx.xxx.xxx.xxx:3000 ---------------------------------------- UI: http://localhost:3001 UI External: http://xxx.xxx.xxx.xxx:3001 ---------------------------------------- [BS] Serving files from: ./ [BS] Watching files...
ブラウザで http://localhost:3000 index.html が開き、htmlやcssに変更があれば自動で再読み込みされるようになる。
Railsなどで開発している場合には、serverプロパティをコメントアウトした上で、コメントアウトしている bsOptions.proxy などの設定を有効化すれば利用可能。
オマケ1. gulp default以外もscriptsに登録する
gulpfile.jsにgulp defalutを追加した要領で、他のタスクも追加できる。
"scripts": { "start": "gulp default", "scss-lint": "gulp scss-lint", "test": "echo \"Error: no test specified\" && exit 1" }
上記のように記述すると、$ npm run-script scss-lint
で scss-lintタスクが実行可能。
start
, test
など、一部のコマンド以外は run-script を含めたコマンドになる、$ npm run
で確認しよう。
$ npm run Lifecycle scripts included in test-project: start gulp default available via `npm run-script`: scss-lint gulp scss-lint
オマケ2. gulpのタスクを都度package.jsonに書くのはダルい
"scripts": { "start": "gulp default", "gulp": "gulp", "test": "echo \"Error: no test specified\" && exit 1" }
とすると、$ npm run-script gulp <task_name>
で実行できる。
しかし開発環境として配布する場合には、メンバー間の余計なコストが掛からないようscriptsに幾つか纏めておいておいた方が良さ気。
オマケ3. ここでは語らないもの
- .scss-lint.ymlのつくり方
- 各エディタでのscss-lint実行方法
- お好みのエディタ名 + scss-lintでググろう
- Browsersyncの設定
- Browsersync options を読もう
- gulpfile.jsのscriptsの仕様
- 本番, デプロイ環境でのSCSSコンパイル環境
- Railsなどならそちらでアセットパイプラインの中で纏めてくれる。ベストプラクティス模索中。
- アプリケーションサーバ側でSCSSコンパイルしてくれるなら、gulpすら要らずBrowsersyncだけで事足りたりすることも。
〆
ということで、大体こんな感じのpackage.jsonとgulpfile.jsを現在は利用している。
package.json
{ "name": "test", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "start": "gulp default", "gulp": "gulp", "scss-lint": "gulp scss-lint", "test": "echo \"Error: no test specified\" && exit 1" }, "devDependencies": { "browser-sync": "^2.13.0", "gulp": "^3.9.1", "gulp-sass": "^2.3.2", "gulp-scss-lint": "^0.4.0" } }
gulpfile.js
'use strict'; var HTML_SRC = './template/**/*.html'; var CSS_SRC = './css/**/*.css'; var SCSS_SRC = './assets/sass/**/*.scss'; var CSS_DEST = './css/'; var gulp = require('gulp'); var sass = require('gulp-sass'); var scss_lint = require('gulp-scss-lint'); var bs = require('browser-sync').create(); // browser-syncタスク gulp.task('bs', function(){ var bsOptions = {}; bsOptions.files = [HTML_SRC, CSS_SRC]; // ここに監視対象ファイルを書く bsOptions.server = './'; // 単体実行する時 // bsOptions.proxy = 'localhost:3001'; // proxy実行する時 // bsOptions.port = 3001; // bs.init(bsOptions); }); // Sassコンパイルタスク gulp.task('sass-compile', function(){ return gulp.src(SCSS_SRC) .pipe(sass.sync().on('error', sass.logError)) .pipe(gulp.dest(CSS_DEST)); }); // scss-lintタスク gulp.task('scss-lint', function(){ return gulp.src(SCSS_SRC) .pipe(scss_lint()); }); // scssファイル群の変更を監視するタスク gulp.task('scss:watch', function(){ var watcher = gulp.watch(SCSS_SRC, ['scss-lint', 'sass-compile']); watcher.on('change', function(event) { console.log('File ' + event.path + ' was ' + event.type + ', running tasks...'); }); }); // gulpのデフォルトタスクとしてscss:watchタスクを指定 gulp.task('default', ['bs', 'scss:watch']);
もうちょっと何かあったら追記する。