gulp-ya-merge
Version:
gulp-ya-merge用于在静态文件被压缩等处理后,对html页面引用的的静态文件进行合并、追加文件版本等操作。
176 lines (158 loc) • 4.99 kB
JavaScript
const through = require('through2');
const path = require('path');
const crypto = require('crypto');
const matchFlags = {
'leftFlag': '<!--min[',
'rightFlag': ']-->',
'newPath': '<?=$this->StaticUrl(\'{$base}-{$stamp}{$ext}\')?>',
'hashLength': 8,
'scriptExp': /(<script\s(?:.*)\$this->StaticUrl\([\'\"])([^\'\"]+)([\'\"]\)(?:[^\/]*)><\/script>)/gm,
'linkExp': /(<link\s(?:.*)\$this->StaticUrl\([\'\"])([^\'\"]+)([\'\"]\)(?:[^\/\>]*)(?:\/?>|<\/link>))/gm
};
const fs = require('fs');
/**
* 获取要合并的文件列表
* @param {string} content
* @param {RegExp} exp
* @return [string]
*/
function getMergingFiles(content, exp) {
var ret = [], line;
while (line = exp.exec(content)) {
ret.push(line[2]);
}
return ret;
}
/**
* 合并文件
* @param files 要合并的文件
* @param mergedName 合并后的文件
* @param rootPath 根目录
*/
function mergeFiles(files, mergedName, rootPath) {
var content = '';
files.forEach(function (file) {
var filepath = rootPath + file;
if (fs.existsSync(filepath)) {
content += '/* ' + file + "*/\n" + fs.readFileSync(filepath, "utf-8") + "\n";
} else {
console.log(filepath + " not exist");
}
});
fs.writeFileSync(rootPath + mergedName, content, 'utf-8');
return content;
}
module.exports = function (options) {
options = options || {};
for (var k in matchFlags) {
if (!(k in options)) {
options[k] = matchFlags[k];
}
}
var rootPath = options.rootPath || '';
/**
* 替换网址
* @param all
* @param seg1
* @param url
* @param seg2
* @returns {*}
*/
function replaceUrl(all, seg1, url, seg2){
var filepath = rootPath + url;
if (fs.existsSync(filepath)) {
var md5sum = crypto.createHash('md5');
md5sum.update(fs.readFileSync(filepath));
var md5 = md5sum.digest('hex');
var info = path.parse(url);
url = info.dir + '/' + info.name + '-' + md5.substr(0, options.hashLength) + info.ext;
return seg1 + url + seg2;
}
return all;
}
return through.obj(function (file, encoding, callback) {
if (file.isNull()) {
return callback(null, file);
}
if (!file.isBuffer()) {
throw new Error('file must be buffer');
}
var content = new String(file.contents);
var start = 0;
var replaces = [];
while (true) {
var pos1 = content.indexOf(options.leftFlag, start);
if (pos1 == -1) {
break;
}
pos1 += 8;
var pos2 = content.indexOf(options.rightFlag, pos1);
if (pos2 == -1) {
break;
}
var flag = content.substr(pos1, pos2 - pos1);
var arr = flag.split(/\s+/);
if (arr.length != 2) {
break;
}
var mergedId = arr[0];
mergedName = arr[1];
// 寻找闭合标签
var closedMatcher = options.leftFlag + mergedId + options.rightFlag;
var pos3 = content.indexOf(closedMatcher, pos2 + 4);
if (pos3 === -1) {
start = pos2;
continue;
}
// 找到闭合标签后,对中间的内容进行处理
pos2 += 4;
// 根据要合成的文件的后缀,决定是用scriptExp还是linkExp
var mergedExt = path.extname(mergedName).toLowerCase(), fileExp;
if (mergedExt == '.js') {
fileExp = options.scriptExp;
} else {
fileExp = options.linkExp;
}
var files = getMergingFiles(content.substr(pos2, pos3 - pos2), fileExp);
var mergedContent = mergeFiles(files, mergedName, rootPath);
// 修改html代码
// console.log("write finish: " + rootPath + mergedName);
var md5sum = crypto.createHash('md5');
md5sum.update(mergedContent);
pos3 += closedMatcher.length
replaces.push({
'start': pos1 - 8,
'end': pos3,
'type': path.extname(mergedName).substr(1).toLowerCase(),
'path': mergedName,
'stamp': md5sum.digest('hex')
});
start = pos3;
}
var changed = false;
// 对内容进行替换
for (var i = replaces.length - 1; i >= 0; i--) {
var rep = replaces[i], repStr, newPath;
var info = path.parse(rep.path);
newPath = options.newPath
.replace('{$base}', info.dir + '/' + info.name)
.replace('{$ext}', info.ext)
.replace('{$stamp}', rep.stamp.substr(0, options.hashLength));
if (rep.type == 'js') {
repStr = '<script src="' + newPath + '"></script>';
} else if (rep.type == 'css') {
repStr = '<link rel="stylesheet" type="text/css" href="' + newPath + '"/>';
}
content = content.substr(0, rep.start) + repStr + "\n" + content.substr(rep.end);
}
// 对剩余的js或样式进行替换
var newContent = content
.replace(options.scriptExp, replaceUrl)
.replace(options.linkExp, replaceUrl);
if (replaces.length || newContent != content) {
file.contents = new Buffer(newContent);
}
this.push(file);
callback();
});
};