node.jsを使ったタスクランナーgulp。
Sassのコンパイルなどに使用していますが、EJSのコンパイルやAMPページ作成の際にもこれ使えるんじゃない? とgulp.jsを書いてみたので公開してみます。
特にAMPについてはなかなか記事がなかったので、同じようなブログが増えてくれると良いなあと願って……。
(ちなみに、Bootstrap+Sassのみの時はYeomanのweb-appジェネレータを使っています)
目次
EJSファイルは細分化していくうちにごちゃつくので、ejsフォルダにまとめておきます。
ejs-project/
├ package.json
├ gulpfile.js
├ src/
├ assets/
├ fonts/
├ icons/
├ images/
├ ejs/
├ index.ejs
├ js/
├ main.js
├ scss/
├ main.scss
$ npm init
で作ったものにパッケージを入れただけです。
brouserslist
はautoprefixerの設定になります。
{ "name": "project", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], "author": "", "license": "ISC", "devDependencies": { "autoprefixer": "^9.7.6", "browser-sync": "^2.26.7", "css-declaration-sorter": "^5.1.2", "css-mqpacker": "^7.0.0", "del": "^5.1.0", "gulp": "^4.0.2", "gulp-changed": "^4.0.2", "gulp-clean-css": "^4.3.0", "gulp-ejs": "^5.1.0", "gulp-htmlmin": "^5.0.1", "gulp-imagemin": "^7.1.0", "gulp-notify": "^3.2.0", "gulp-plumber": "^1.2.1", "gulp-postcss": "^8.0.0", "gulp-rename": "^2.0.0", "gulp-sass": "^4.0.2", "gulp-sass-glob": "^1.1.0", "gulp-sourcemaps": "^2.6.4", "gulp-uglify-es": "^2.0.0", "imagemin-mozjpeg": "^8.0.0", "imagemin-pngquant": "^8.0.0", "gulp-html-beautify": "^1.0.1" }, "browserslist": [ "last 2 versions", "ie >= 11", "Android >= 7" ] }
**.min.cssと**.min.jsを用意できるようにはしていますが、あまり使ってないですね……。
htmlminとhtmlbeautifyは同時に使うことはないかも。でもとりあえず載せておきます。
var gulp = require('gulp'); var autoprefixer = require('autoprefixer'); var browserSync = require("browser-sync"); var cssdeclsort = require('css-declaration-sorter'); // プロパティをソートし直す var changed = require("gulp-changed"); var cleanCSS = require('gulp-clean-css'); var ejs = require("gulp-ejs"); var htmlmin = require("gulp-htmlmin"); var imagemin = require("gulp-imagemin"); var notify = require('gulp-notify'); //エラー発生時にデスクトップ通知する var plumber = require("gulp-plumber"); var postcss = require('gulp-postcss'); //autoprefixerとセット var rename = require("gulp-rename"); var sass = require("gulp-sass"); var sassGlob = require('gulp-sass-glob'); var sourcemaps = require('gulp-sourcemaps'); var uglify = require("gulp-uglify-es").default; var mozjpeg = require('imagemin-mozjpeg'); var mqpacker = require("css-mqpacker"); var pngquant = require('imagemin-pngquant'); var del = require('del'); var htmlbeautify = require("gulp-html-beautify"); // ディレクトリ var dir = { src: 'src/', dest: 'dist/' } // scssのコンパイル・圧縮 gulp.task('sass', (done) => { gulp .src([dir.src + '/scss/**/*.scss', '!' + dir.src + '/scss/**/_*.scss']) .pipe(plumber({ errorHandler: notify.onError("Error: <%= error.message %>") })) .pipe(sassGlob()) //importの読み込みを簡潔にする .pipe(sass({ outputStyle: 'expanded' })) .pipe(postcss([ autoprefixer({ cascade: false }), cssdeclsort({ order: 'smacss' }), mqpacker() ])) .pipe(sourcemaps.write()) // .min.cssの書き出し // .pipe(gulp.dest(dir.dest + '/css')) // .pipe(cleanCSS({ rebase: false })) // .pipe(rename({ // extname: '.min.css' //minifyしたファイルの名前変更 // })) .pipe(gulp.dest(dir.dest + '/css')) //コンパイル後の出力先 done() }); // ejsのコンパイル・圧縮 gulp.task('ejs', (done) => { gulp .src([dir.src + 'ejs/**/*.ejs', '!' + dir.src + 'ejs/**/_*.ejs']) .pipe(plumber({ errorHandler: notify.onError("Error: <%= error.message %>") })) .pipe(ejs({}, {}, { "ext": ".html" })) .pipe(rename({ extname: ".html" })) .pipe(htmlmin({ collapseWhitespace: true, // 余白を除去する removeComments: true // HTMLコメントを除去する })) .pipe(htmlbeautify({ // HTMLをきれいにする indent_size: 2, indent_with_tabs: false })) .pipe(gulp.dest(dir.dest)) done() }); // JS圧縮 gulp.task('js', (done) => { gulp .src([dir.src + 'js/*.js']) // src/js/ 配下の全ファイルを対象 .pipe(plumber({ errorHandler: notify.onError("Error: <%= error.message %>") })) .pipe(uglify({ compress: true, // 圧縮する mangle: true, // 変数の難読化を行う output: { comments: /^!/ //Licenseコメントの頭にある「*!」を残す } })) // .min.jsの書き出し // .pipe(gulp.dest(dir.dest + 'js')) // .pipe(rename({ // extname: '.min.js' // })) .pipe(gulp.dest(dir.dest + 'js')) // js 配下に出力する done() }); // fonts gulp.task('fonts', (done) => { gulp.src([dir.src + 'assets/fonts/**/*']) .pipe(gulp.dest(dir.dest + 'assets/fonts')) done() }) // icons gulp.task('icons', (done) => { gulp.src([dir.src + 'assets/icons/**/*']) .pipe(gulp.dest(dir.dest + 'assets/icons')) done() }) // その他コピー gulp.task('extras', (done) => { gulp.src([dir.src + '*' , '!' + dir.src + '*.html', '!' + dir.src + 'ejs', '!' + dir.src + 'scss', '!' + dir.src + 'js'], {dot: true}) .pipe(gulp.dest(dir.dest)) done() }) //圧縮率の定義 var imageminOption = [ pngquant({ quality: [0.7, 0.85], }), mozjpeg({ quality: 85 }), imagemin.gifsicle({ interlaced: false, optimizationLevel: 1, colors: 256 }), imagemin.mozjpeg(), imagemin.optipng(), imagemin.svgo() ]; // 画像圧縮 gulp.task('img', function() { return gulp .src([dir.src + 'assets/images/**/*.{png,jpg,gif,svg}']) .pipe(changed(dir.dest + 'assets/images')) .pipe(imagemin(imageminOption)) .pipe(gulp.dest(dir.dest + 'assets/images')) }); // Browser Sync gulp.task('browser-sync', function(done) { browserSync.init({ server: { //ローカルサーバ baseDir: dir.dest, index: "index.html" } }); done(); }); //Clean gulp.task('clean', function(done) { del.sync(dir.dest + '/**', '!' + dir.dest); done(); }); // 監視 gulp.task('watch', function() { const reload = () => { browserSync.reload(); //リロード }; gulp.watch(dir.src + 'scss/**/*.scss').on('change', gulp.series('sass', reload)); gulp.watch(dir.src + 'js/**/*.js').on('change', gulp.series('js', reload)); gulp.watch(dir.src + 'ejs/**/*.ejs').on('change', gulp.series('ejs', reload)); gulp.watch(dir.src + 'assets/images/**/*').on('change', gulp.series('img', reload)); gulp.watch(dir.src + 'assets/fonts/**/*').on('change', gulp.series('fonts', reload)); gulp.watch(dir.src + 'assets/icons/**/*').on('change', gulp.series('icons', reload)); gulp.watch(dir.src + '*').on('change', gulp.series('extras', reload)); }); // gulpコマンドで最初に動作 gulp.task('default', gulp.series( 'clean', gulp.parallel( 'ejs', 'sass', 'js', 'img', 'fonts', 'icons', 'extras', 'watch', 'browser-sync' ) ) );
AMPページのコーディングでSassをコンパイルして容量を測ってからHTMLに差し込んで……の流れが面倒くさかったので、Sassから圧縮したCSSをHTML内のstyleタグに挿入して書き出せるようにしたものです。
amp-project/
├ package.json
├ gulpfile.js
├ src/
├ assets/
├ images/
├ scss/
├ main.scss
├ index.html
<!-- inject:head:css -->〜<!-- endinject -->
の行に圧縮したCSSを挿入したHTMLをdistフォルダに書き出します。
<!doctype html> <html amp lang="ja"> <head> <meta charset="utf-8" /> <title>タイトル</title> <meta name="description" content="ページの説明"/> <meta name="keywords" content="キーワード"/> <meta name="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0,user-scalable=no" /> <link rel="canonical" href="ページURL(AMPのみの時はAMPを指定)" /> <link rel="amphtml" href="AMPのページURL(通常版がある時に指定)" /> <script type="application/ld+json"> <!-- 省略 --> </script> <!-- inject:head:css --> <!-- この中にcssが入ります --> <!-- endinject --> <script async src="https://cdn.ampproject.org/v0.js"></script> </head> <body> <h1>ページコンテンツ</h1> </body> </html>
JSを使わなかったりrenameしなかったりなので、EJS版から不要なものを消します。
{ "name": "project", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], "author": "", "license": "ISC", "devDependencies": { "autoprefixer": "^9.7.6", "browser-sync": "^2.26.7", "css-declaration-sorter": "^5.1.2", "del": "^5.1.0", "gulp": "^4.0.2", "gulp-changed": "^4.0.2", "gulp-htmlmin": "^5.0.1", "gulp-imagemin": "^7.1.0", "gulp-inject": "^5.0.4", "gulp-notify": "^3.2.0", "gulp-plumber": "^1.2.1", "gulp-postcss": "^8.0.0", "gulp-sass": "^4.0.2", "gulp-sass-glob": "^1.1.0", "imagemin-mozjpeg": "^8.0.0", "imagemin-pngquant": "^8.0.0" }, "browserslist": [ "last 2 versions", "ie >= 11", "Android >= 7" ] }
var gulp = require('gulp'); var autoprefixer = require('autoprefixer'); var browserSync = require("browser-sync"); var cssdeclsort = require('css-declaration-sorter'); // プロパティをソートし直す var changed = require("gulp-changed"); var htmlmin = require("gulp-htmlmin"); var imagemin = require("gulp-imagemin"); var notify = require('gulp-notify'); //エラー発生時にデスクトップ通知する var plumber = require("gulp-plumber"); var postcss = require('gulp-postcss'); //autoprefixerとセット var sass = require("gulp-sass"); var sassGlob = require('gulp-sass-glob'); var mozjpeg = require('imagemin-mozjpeg'); var pngquant = require('imagemin-pngquant'); var del = require('del'); var inject = require('gulp-inject'); // ディレクトリ var dir = { src: 'src/', dest: 'dist/' } // htmlにcss挿入しdest gulp.task('inject-css', (done) => { gulp .src([dir.src + '*.html']) .pipe(inject(gulp.src([dir.dest + '/css/main.css']), { starttag: '<!-- inject:head:{{ext}} -->', removeTags: true, transform: function (filePath, file) { const styleTagStart = '<style amp-custom>'; const styleTagEnd = '</style>'; let styles = file.contents.toString('utf8'); // パスを相対パスに変更 styles = styles.replace('url(../', 'url('); // sassコンパイル時に生成される行を削除 styles = styles.replace(/\/\*\#\ssourceMappingURL\=.*\.map\s\*\//i, ''); styles = styles.replace(/\@charset \"utf-8\"\;/i, '').trim(); return styleTagStart + styles + styleTagEnd; } })) .pipe(htmlmin({ collapseWhitespace: false, // 余白を除去する removeComments: false, // HTMLコメントを除去する minifyCSS: true, // CSS圧縮 })) .pipe(gulp.dest(dir.dest)) done(); }); // scssのコンパイル・圧縮 gulp.task('sass', (done) => { gulp .src([dir.src + 'scss/**/*.scss', '!' + dir.src + 'scss/**/_*.scss']) .pipe(plumber({ errorHandler: notify.onError("Error: <%= error.message %>") })) .pipe(sassGlob()) //importの読み込みを簡潔にする .pipe(sass({ outputStyle: 'expanded' })) .pipe(postcss([autoprefixer({ cascade: false })])) .pipe(postcss([cssdeclsort({ order: 'smacss' })])) .pipe(gulp.dest(dir.dest + '/css')) //コンパイル後の出力先 done() }); // その他コピー gulp.task('extras', (done) => { gulp.src([dir.src + '*' , '!' + dir.src + '*.html', '!' + dir.src + 'ejs', '!' + dir.src + 'scss', '!' + dir.src + 'js'], {dot: true}) .pipe(gulp.dest(dir.dest)) done() }) //圧縮率の定義 var imageminOption = [ pngquant({ quality: [0.7, 0.85], }), mozjpeg({ quality: 85 }), imagemin.gifsicle({ interlaced: false, optimizationLevel: 1, colors: 256 }), imagemin.mozjpeg(), imagemin.optipng(), imagemin.svgo() ]; // 画像圧縮 gulp.task('img', function() { return gulp .src([dir.src + 'assets/images/**/*.{png,jpg,gif,svg}']) .pipe(changed(dir.dest + 'assets/images')) .pipe(imagemin(imageminOption)) .pipe(gulp.dest(dir.dest + 'assets/images')) }); // Browser Sync gulp.task('browser-sync', function(done) { browserSync.init({ server: { //ローカルサーバ baseDir: dir.dest, index: "index.html" } }); done(); }); //Clean gulp.task('clean', function(done) { del.sync(dir.dest + '/**', '!' + dir.dest); done(); }); // 監視 gulp.task('watch', function() { const reload = () => { browserSync.reload(); //リロード }; gulp.watch(dir.src + 'scss/**/*.scss').on('change', gulp.series('sass', reload)); gulp.watch(dir.src + 'assets/images/**/*').on('change', gulp.series('img', reload)); gulp.watch(dir.src + '*').on('change', gulp.series('extras', reload)); gulp.watch(dir.src + '**/*.html').on('change', gulp.series('inject-css', reload)); gulp.watch(dir.src + 'scss/**/*.scss').on('change', gulp.series('inject-css', reload)); }); // gulpコマンドで最初に動作 gulp.task('default', gulp.series( 'clean', 'sass', 'img', 'extras', 'inject-css', gulp.parallel( 'watch', 'browser-sync', ), ) ); gulp.task('amp-css', gulp.series( 'inject-css' ));
CSSの圧縮・挿入が不安だったので、テスト用のamp-cssタスクを残しています。
$gulp amp-css
で動かせます。
$gulp
でコンパイル&Browser Syncを起動します。
なんとなく書いて動けばラッキー的になってるので、もっとしっかり内容を理解して行きたいです。