今更だけど。
ファイル構造はこんな感じ。
gulpfile.js gulp-functions.js package.json package-lock.json htdocs/ ├── css │ └── images ├── images └── js src ├── javascripts │ └── vendor └── scss
src/scss => htdocs/css
src/javascripts => htdocs/js
へとコンパイルされる。
package.json
{ "name": "bt-gulpfile", "version": "1.0.0", "description": "", "main": "gulpfile.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], "author": "", "license": "ISC", "private": true, "devDependencies": { "cheerio": "^1.0.0-rc.3", "compass-mixins": "^0.12.10", "event-stream": "^4.0.1", "gulp": "^4.0.2", "gulp-autoprefixer": "^6.1.0", "gulp-concat": "^2.6.1", "gulp-connect": "^5.7.0", "gulp-ejs": "^4.1.1", "gulp-htmlmin": "^5.0.1", "gulp-if": "^2.0.2", "gulp-plumber": "^1.2.1", "gulp-postcss": "^8.0.0", "gulp-sass": "^4.0.2", "gulp-sourcemaps": "^2.6.5", "gulp-uglify": "^3.0.2", "minimist": "^1.2.0", "postcss-cachebuster": "^0.1.6" }, "dependencies": {}, "browserslist": [ "last 1 version", "ie 9", "Android 4.2" ] }
gulpfile.js
var gulp = require('gulp'); var gulpF = require('./gulp-functions.js'); var connect = require('gulp-connect'); var ejs = require('gulp-ejs'); var uglify = require("gulp-uglify"); var plumber = require("gulp-plumber"); var concat = require("gulp-concat"); var sourcemaps = require("gulp-sourcemaps"); var autoprefixer= require('gulp-autoprefixer'); var sass = require('gulp-sass'); var htmlmin = require('gulp-htmlmin'); var minimist = require("minimist"); var gulpif = require('gulp-if'); var postcss = require('gulp-postcss'); var cachebuster = require('postcss-cachebuster'); const fs = require('fs'); var options = minimist(process.argv.slice(2), { string: 'env', default: { env: 'production' } }); const config = { production: options.env !== 'dev', sourceMaps: options.env === 'dev' }; var packageJson = JSON.parse(fs.readFileSync('./package.json')); gulp.task('javascript', function(cb){ gulp.src(['./src/javascripts/vendor/*.js', './src/javascripts/*.js']) .pipe(plumber()) .pipe(gulpif(config.sourceMaps, sourcemaps.init())) .pipe(concat('dest.js')) .pipe(uglify()) .pipe(gulpif(config.sourceMaps, sourcemaps.write())) .pipe(gulp.dest("htdocs/js/")); cb(); }); gulp.task('sass', function (cb) { gulp.src(['./src/scss/*.scss', './src/scss/vendor/*.scss']) .pipe(plumber()) .pipe(gulpif(config.sourceMaps, sourcemaps.init())) .pipe(concat('style.css')) .pipe(sass({ outputStyle: 'compressed', })) .pipe(autoprefixer({ cascade: false })) .pipe(postcss([ cachebuster({ type: 'checksum', cssPath : '/htdocs/css', }), ])) .pipe(gulpif(config.sourceMaps, sourcemaps.write())) .pipe(gulp.dest('./htdocs/css/')); cb(); }); gulp.task('html', function (cb) { gulp.src(['./src/**/*.html', '!./src/**/_*.html']) .pipe(plumber()) .pipe(ejs()) .pipe(gulpF.htmlrev()) .pipe(htmlmin({ collapseWhitespace : false, removeComments : true })) .pipe(gulp.dest('./htdocs')); cb(); }); gulp.task('watch', function(cb){ // auto compile gulp.watch( ['./src/**/*.html', './src/javascripts/**/*.js', './src/scss/**/*.scss', ], gulp.series('html', 'javascript', 'sass')); // browser live reload gulp.watch('htdocs').on('change', function(filepath){ gulp.src(filepath, { read: false }).pipe(connect.reload()); }); cb(); }); gulp.task('connect', function(done) { connect.server({ root: 'htdocs', livereload: true }, function () { this.server.on('close', done) }); }); // only-compile gulp.task('compile', gulp.series( 'html', 'javascript', 'sass')); // dev gulp.task('default', gulp.series( 'html', 'javascript', 'sass', 'watch', 'connect'));
gulp-functions.js
var eventstream = require('event-stream'); const cheerio = require('cheerio'); const path = require('path'); const fs = require('fs'); const crypto = require('crypto'); module.exports = { // htmlファイルのimg/script/linkのファイルにmd5値でリビジョンを付ける htmlrev : ()=>{ return eventstream.map(function(file, cb) { var $ = cheerio.load(file.contents.toString(), { decodeEntities: false }); $('link[rel="stylesheet"],script[src],img').each(function(i, ele) { let attrname = ""; let hash; switch (ele.name.toLowerCase()){ case 'link': attrname = 'href'; break; case 'img': case 'script': attrname = 'src'; break; } if (attrname) { let src = $(ele).attr(attrname); if (!src.match(new RegExp('^https?:\/\/'))) { var normPath = path.normalize(src); if (normPath.indexOf(path.sep) === 0) { dependencyPath = path.join(file.base, normPath); } else { dependencyPath = path.resolve(path.dirname(file.path), normPath); } let hasQS = dependencyPath.lastIndexOf("?"); if (hasQS >= 0){ dependencyPath = dependencyPath.substring(0, hasQS); } // replace src => htdocs dependencyPath = dependencyPath.replace( path.sep+'src'+path.sep, path.sep+'htdocs'+path.sep); try { data = fs.readFileSync(dependencyPath); hash = crypto.createHash('md5'); hash.update(data.toString(), 'utf8'); $(ele).attr(attrname, $(ele).attr(attrname) + '?rev=' + hash.digest('hex')); } catch(e) { // fail silently. console.log('[ERROR] revision attach failed: ' + e.message); } } } }); file.contents = new Buffer.from($.root().html()); cb(null, file); }); } }
実行する
npxで実行
$ npx gulp watch
でファイル監視の開発モード
$ npx gulp compile
でコンパイルしてくれる。