今更だけど。
ファイル構造はこんな感じ。
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
へとコンパイルされる。
{
"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){
gulp.watch(
['./src/**/*.html',
'./src/javascripts/**/*.js',
'./src/scss/**/*.scss',
],
gulp.series('html', 'javascript', 'sass'));
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) });
});
gulp.task('compile', gulp.series(
'html', 'javascript', 'sass'));
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 = {
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);
}
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) {
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
でコンパイルしてくれる。