/
index.js
executable file
·143 lines (121 loc) · 4.61 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
'use strict';
/**
* gulp-cache-bust-meta is a Gulp plugin for hash based (meta) cache busting
*
* @see https://github.com/jkphl/gulp-cache-bust-meta
*
* @author Joschi Kuphal <joschi@kuphal.net> (https://github.com/jkphl)
* @copyright © 2016 Joschi Kuphal
* @license MIT https://raw.github.com/jkphl/gulp-cache-bust-meta/master/LICENSE
*/
var through = require('through2');
var path = require('path');
var fs = require('fs');
var hash = require('./lib/hash');
var vinylFile = require('vinyl-file');
/**
* Cache busting including (meta) hash replacement in template files
*
* @param {Object} map Optional: Templates to process
* {
* '/path/to/template/file.xyz': 'target.xyz'
* }
* @param {Object} opt Optional: Options, defaulting to
* {
* hashLength: 8,
* separator: '.',
* metaHashPlaceholder: '@@metaHash'
* }
* @returns {*} Transform
*/
module.exports = function (map, opt) {
// Sanitize options / set defaults
opt = opt || {};
opt.hashLength = parseInt(opt.hashLength) || 8;
opt.separator = ('separator' in opt) ? opt.separator : '.';
opt.metaHashPlaceholder = opt.metaHashPlaceholder || '@@metaHash';
// Variables
var hashReplacements = {};
var hashes = [];
var sourceFiles = [];
var templates = [];
// Validate & prepare the replacement templates
if (map && (map.constructor === Object) && Object.keys(map).length) {
for (var from in map) {
// Fail if replacement name is invalid
if ((typeof map[from] !== 'string') || (map[from].constructor !== String) || !map[from].trim().length) {
throw new Error('gulp-cache-bust-meta: Template target path is invalid');
}
try {
var templateFile = vinylFile.readSync(from);
if (!templateFile.stat.isFile()) {
throw 'error';
}
templateFile.path = map[from];
templates.push(templateFile);
} catch (e) {
throw new Error('gulp-cache-bust-meta: Template "' + from + '" is invalid');
}
}
}
/**
* Buffer incoming contents
*
* @param {File} file File
* @param enc
* @param {Function} cb Callback
*/
function bufferContents(file, enc, cb) {
// Ignore empty files
if (file.isNull()) {
cb();
return;
}
// We don't do streams (yet)
if (file.isStream()) {
this.emit('error', new Error('gulp-cache-bust-meta: Streaming not supported'));
cb();
return;
}
// Calculate the content hash and file name replacement
var fileContentHash = hash(file.contents, opt.hashLength);
var fileExtension = path.extname(file.path);
var fileRelativePath = file.relative;
var fileRelativeDir = path.dirname(fileRelativePath);
fileRelativeDir = fileRelativeDir.length ? (fileRelativeDir + path.sep) : '';
var fileHashedName = path.basename(fileRelativePath, fileExtension) + opt.separator + fileContentHash + fileExtension;
// Register the file name replacement
hashReplacements[fileRelativePath] = fileRelativeDir + fileHashedName;
// Register the hash
hashes.push(fileContentHash);
// Register the source file
sourceFiles.push(file);
cb();
}
/**
* End the stream
*
* @param {Function} cb Callback
*/
function endStream(cb) {
// Register the hashed target files
sourceFiles.forEach(function (file) {
var targetFile = file.clone();
targetFile.path = targetFile.base + hashReplacements[targetFile.relative];
this.push(targetFile);
}, this);
// Calculate & register the meta hash
hashReplacements[opt.metaHashPlaceholder] = hash(hashes.join('-'), opt.hashLength);
// Process the replacement templates
templates.forEach(function (template) {
var templateContent = template.contents.toString();
for (var repl in hashReplacements) {
templateContent = templateContent.replace(repl, hashReplacements[repl]);
}
template.contents = Buffer.fromString ? Buffer.fromString(templateContent) : new Buffer(templateContent);
this.push(template);
}, this);
cb();
}
return through.obj(bufferContents, endStream);
};