Skip to content

Commit c44e5f8

Browse files
committed
add [hash] function
1 parent 686756c commit c44e5f8

File tree

9 files changed

+126
-42
lines changed

9 files changed

+126
-42
lines changed

README.md

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -401,6 +401,13 @@ free module variables which are replaced with a module. ex. `{ "$": "jquery" }`
401401
`source` if `options.output` is not set
402402
else `stats` as json see [example](/sokra/modules-webpack/tree/master/examples/code-splitting)
403403

404+
## Bonus featues
405+
406+
### File hash
407+
408+
You can use `[hash]` in `scriptSrcPrefix`, `output`, `outputDirectory` and `outputPostfix`.
409+
`webpack` will replace it with a hash of your files, when writing.
410+
404411
## Comparison
405412

406413
<table>
@@ -639,7 +646,7 @@ else `stats` as json see [example](/sokra/modules-webpack/tree/master/examples/c
639646
require JSON
640647
</td>
641648
<td>
642-
yes <em>NEW</em>
649+
yes
643650
</td>
644651
<td>
645652
no
@@ -669,7 +676,7 @@ else `stats` as json see [example](/sokra/modules-webpack/tree/master/examples/c
669676
loaders
670677
</td>
671678
<td>
672-
yes <em>NEW</em>
679+
yes
673680
</td>
674681
<td>
675682
no
@@ -684,7 +691,7 @@ else `stats` as json see [example](/sokra/modules-webpack/tree/master/examples/c
684691
compile coffee script
685692
</td>
686693
<td>
687-
yes <em>NEW</em>
694+
yes
688695
</td>
689696
<td>
690697
no
@@ -793,7 +800,6 @@ You are also welcome to correct any spelling mistakes or any language issues, be
793800
## Future plans
794801

795802
* watch mode
796-
* append hash of file to filename, for better caching
797803
* more polyfills for node.js buildin modules, but optional
798804
* require from protocol `require("http://...")`
799805

bin/webpack.js

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -125,9 +125,6 @@ if(argv.single) {
125125
if(!options.outputDirectory) options.outputDirectory = path.dirname(output);
126126
if(!options.output) options.output = path.basename(output);
127127
if(!options.outputPostfix) options.outputPostfix = "." + path.basename(output);
128-
var outExists = path.existsSync(options.outputDirectory);
129-
if(!outExists)
130-
fs.mkdirSync(options.outputDirectory);
131128
webpack(input, options, function(err, stats) {
132129
if(err) {
133130
console.error(err);
@@ -139,15 +136,16 @@ if(argv.single) {
139136
function c(str) {
140137
return argv.colors ? str : "";
141138
}
139+
console.log("Hash: "+c("\033[1m") + stats.hash + c("\033[22m"));
142140
console.log("Chunks: "+c("\033[1m") + stats.chunkCount + c("\033[22m"));
143141
console.log("Modules: "+c("\033[1m") + stats.modulesCount + c("\033[22m"));
144142
console.log("Modules including duplicates: "+c("\033[1m") + stats.modulesIncludingDuplicates + c("\033[22m"));
145143
console.log("Modules pre chunk: "+c("\033[1m") + stats.modulesPerChunk + c("\033[22m"));
146144
console.log("Modules first chunk: "+c("\033[1m") + stats.modulesFirstChunk + c("\033[22m"));
147145
if(stats.fileSizes)
148-
for(var file in stats.fileSizes) {
146+
Object.keys(stats.fileSizes).reverse().forEach(function(file) {
149147
console.log(c("\033[1m") + sprintf("%" + (5 + options.output.length) + "s", file) + c("\033[22m")+": "+c("\033[1m") + sprintf("%8d", stats.fileSizes[file]) + c("\033[22m") + " characters");
150-
};
148+
});
151149
var cwd = process.cwd();
152150
var cwdParent = path.dirname(cwd);
153151
var buildins = path.join(__dirname, "..");
@@ -174,7 +172,7 @@ if(argv.single) {
174172
console.log(" <id> <size> <filename>");
175173
if(argv.verbose)
176174
console.log(" <reason> from <filename>");
177-
for(var file in stats.fileModules) {
175+
Object.keys(stats.fileModules).reverse().forEach(function(file) {
178176
console.log(c("\033[1m\033[32m") + file + c("\033[39m\033[22m"));
179177
var modules = stats.fileModules[file];
180178
if(argv["by-size"])
@@ -207,7 +205,7 @@ if(argv.single) {
207205
});
208206
}
209207
});
210-
}
208+
});
211209
}
212210
if(stats.warnings) {
213211
stats.warnings.forEach(function(warning) {

examples/code-splitted-require.context/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@ module.exports = function() {
141141
## Uncompressed
142142

143143
```
144+
Hash: 839970d2bf36067bbcc02e254658eee5
144145
Chunks: 2
145146
Modules: 6
146147
Modules including duplicates: 6
@@ -170,6 +171,7 @@ output.js
170171
## Minimized (uglify-js, no zip)
171172

172173
```
174+
Hash: 839970d2bf36067bbcc02e254658eee5
173175
Chunks: 2
174176
Modules: 6
175177
Modules including duplicates: 6

examples/code-splitting/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ webpackJsonp(1,{3:function(a,b,c){},4:function(a,b,c){}})
108108
## Uncompressed
109109

110110
```
111+
Hash: efedb0352f230f80a7d673655c226d6b
111112
Chunks: 2
112113
Modules: 5
113114
Modules including duplicates: 5
@@ -135,6 +136,7 @@ output.js
135136
## Minimized (uglify-js, no zip)
136137

137138
```
139+
Hash: efedb0352f230f80a7d673655c226d6b
138140
Chunks: 2
139141
Modules: 5
140142
Modules including duplicates: 5

examples/coffee-script/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ console.timeEnd = function() {
110110
## Uncompressed
111111

112112
```
113+
Hash: 473faf0d98f59635bba5232deb86646a
113114
Chunks: 1
114115
Modules: 4
115116
Modules including duplicates: 4
@@ -134,6 +135,7 @@ output.js
134135
## Minimized (uglify-js, no zip)
135136

136137
```
138+
Hash: 473faf0d98f59635bba5232deb86646a
137139
Chunks: 1
138140
Modules: 4
139141
Modules including duplicates: 4

examples/loader/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ Prints in node.js (`node example.js`) and in browser:
130130
## Uncompressed
131131

132132
```
133+
Hash: 7286e7c84a0b1d6841f47269de641d85
133134
Chunks: 1
134135
Modules: 5
135136
Modules including duplicates: 5

examples/require.context/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ module.exports = function() {
115115
## Uncompressed
116116

117117
```
118+
Hash: 34e41b2aba89897d758177096351e8aa
118119
Chunks: 1
119120
Modules: 6
120121
Modules including duplicates: 6
@@ -142,6 +143,7 @@ output.js
142143
## Minimized (uglify-js, no zip)
143144

144145
```
146+
Hash: 34e41b2aba89897d758177096351e8aa
145147
Chunks: 1
146148
Modules: 6
147149
Modules including duplicates: 6

lib/webpack.js

Lines changed: 101 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ var path = require("path");
77
var writeChunk = require("./writeChunk");
88
var fs = require("fs");
99

10+
var HASH_REGEXP = /\[hash\]/i;
11+
1012
var templateAsync = require("fs").readFileSync(path.join(__dirname, "templateAsync.js"));
1113
var templateSingle = require("fs").readFileSync(path.join(__dirname, "templateSingle.js"));
1214
/*
@@ -108,29 +110,55 @@ module.exports = function(context, moduleName, options, callback) {
108110
}
109111
var fileSizeMap = {};
110112
var fileModulesMap = {};
113+
var fileWrites = [];
111114
var chunksCount = 0;
112-
for(var chunkId in depTree.chunks) {
115+
var chunkIds = Object.keys(depTree.chunks);
116+
chunkIds.sort(function(a,b) {
117+
return parseInt(b, 10) - parseInt(a, 10);
118+
});
119+
var hash;
120+
try {
121+
hash = new (require("crypto").Hash)("md5");
122+
hash.update(JSON.stringify(options.libary || ""));
123+
hash.update(JSON.stringify(options.outputPostfix));
124+
hash.update(JSON.stringify(options.outputJsonpFunction));
125+
hash.update(JSON.stringify(options.scriptSrcPrefix));
126+
hash.update(templateAsync);
127+
hash.update(templateSingle);
128+
hash.update("1");
129+
} catch(e) {
130+
callback(e);
131+
return;
132+
hash = null;
133+
}
134+
chunkIds.forEach(function(chunkId) {
113135
var chunk = depTree.chunks[chunkId];
114-
if(chunk.empty) continue;
115-
if(chunk.equals !== undefined) continue;
136+
if(chunk.empty) return;
137+
if(chunk.equals !== undefined) return;
116138
chunksCount++;
117139
var filename = path.join(options.outputDirectory,
118140
chunk.id === 0 ? options.output : chunk.id + options.outputPostfix);
141+
var content = writeChunk(depTree, chunk, options);
142+
if(hash) hash.update(content);
119143
buffer = [];
120144
if(chunk.id === 0) {
145+
if(hash)
146+
hash = hash.digest("hex");
147+
else
148+
hash = "";
121149
if(options.libary) {
122150
buffer.push("/******/var ");
123151
buffer.push(options.libary);
124152
buffer.push("=\n");
125153
}
126-
if(Object.keys(depTree.chunks).length > 1) {
154+
if(chunkIds.length > 1) {
127155
buffer.push(templateAsync);
128156
buffer.push("/******/({a:");
129-
buffer.push(JSON.stringify(options.outputPostfix));
157+
buffer.push(JSON.stringify(options.outputPostfix.replace(HASH_REGEXP, hash)));
130158
buffer.push(",b:");
131159
buffer.push(JSON.stringify(options.outputJsonpFunction));
132160
buffer.push(",c:");
133-
buffer.push(JSON.stringify(options.scriptSrcPrefix));
161+
buffer.push(JSON.stringify(options.scriptSrcPrefix.replace(HASH_REGEXP, hash)));
134162
buffer.push(",\n");
135163
} else {
136164
buffer.push(templateSingle);
@@ -143,7 +171,7 @@ module.exports = function(context, moduleName, options, callback) {
143171
buffer.push(chunk.id);
144172
buffer.push(", {\n");
145173
}
146-
buffer.push(writeChunk(depTree, chunk, options));
174+
buffer.push(content);
147175
buffer.push("/******/})");
148176
buffer = buffer.join("");
149177
try {
@@ -152,9 +180,7 @@ module.exports = function(context, moduleName, options, callback) {
152180
callback(e);
153181
return;
154182
}
155-
fs.writeFile(filename, buffer, "utf-8", function(err) {
156-
if(err) throw err;
157-
});
183+
fileWrites.push([filename, buffer]);
158184
fileSizeMap[path.basename(filename)] = buffer.length;
159185
var modulesArray = [];
160186
for(var moduleId in chunk.modules) {
@@ -172,30 +198,75 @@ module.exports = function(context, moduleName, options, callback) {
172198
return a.id - b.id;
173199
});
174200
fileModulesMap[path.basename(filename)] = modulesArray;
201+
});
202+
// write files
203+
var remFiles = fileWrites.length;
204+
var outDir = options.outputDirectory.replace(HASH_REGEXP, hash);
205+
function createDir(dir, callback) {
206+
path.exists(dir, function(exists) {
207+
if(exists)
208+
callback();
209+
else {
210+
fs.mkdir(dir, function(err) {
211+
if(err) {
212+
var parentDir = path.join(dir, "..");
213+
if(parentDir == dir)
214+
return callback(err);
215+
createDir(parentDir, function(err) {
216+
if(err) return callback(err);
217+
fs.mkdir(dir, function(err) {
218+
if(err) return callback(err);
219+
callback();
220+
});
221+
});
222+
return;
223+
}
224+
callback();
225+
});
226+
}
227+
});
228+
}
229+
createDir(outDir, function(err) {
230+
if(err) return callback(err);
231+
writeFiles();
232+
});
233+
function writeFiles() {
234+
fileWrites.forEach(function(writeAction) {
235+
fs.writeFile(writeAction[0].replace(HASH_REGEXP, hash), writeAction[1], "utf-8", function(err) {
236+
if(err) throw err;
237+
remFiles--;
238+
if(remFiles === 0)
239+
writingFinished();
240+
});
241+
});
175242
}
176-
buffer = {};
177-
buffer.chunkCount = chunksCount;
178-
buffer.modulesCount = Object.keys(depTree.modules).length;
179-
var sum = 0;
180-
for(var chunkId in depTree.chunks) {
181-
for(var moduleId in depTree.chunks[chunkId].modules) {
182-
if(depTree.chunks[chunkId].modules[moduleId] === "include")
243+
function writingFinished() {
244+
// Stats
245+
buffer = {};
246+
buffer.hash = hash;
247+
buffer.chunkCount = chunksCount;
248+
buffer.modulesCount = Object.keys(depTree.modules).length;
249+
var sum = 0;
250+
for(var chunkId in depTree.chunks) {
251+
for(var moduleId in depTree.chunks[chunkId].modules) {
252+
if(depTree.chunks[chunkId].modules[moduleId] === "include")
253+
sum++;
254+
}
255+
}
256+
buffer.modulesIncludingDuplicates = sum;
257+
buffer.modulesPerChunk = Math.round(sum / chunksCount*10)/10;
258+
sum = 0;
259+
for(var moduleId in depTree.chunks[0].modules) {
260+
if(depTree.chunks[0].modules[moduleId] === "include")
183261
sum++;
184262
}
263+
buffer.modulesFirstChunk = sum;
264+
buffer.fileSizes = fileSizeMap;
265+
buffer.warnings = depTree.warnings;
266+
buffer.errors = depTree.errors;
267+
buffer.fileModules = fileModulesMap;
268+
callback(null, buffer);
185269
}
186-
buffer.modulesIncludingDuplicates = sum;
187-
buffer.modulesPerChunk = Math.round(sum / chunksCount*10)/10;
188-
sum = 0;
189-
for(var moduleId in depTree.chunks[0].modules) {
190-
if(depTree.chunks[0].modules[moduleId] === "include")
191-
sum++;
192-
}
193-
buffer.modulesFirstChunk = sum;
194-
buffer.fileSizes = fileSizeMap;
195-
buffer.warnings = depTree.warnings;
196-
buffer.errors = depTree.errors;
197-
buffer.fileModules = fileModulesMap;
198-
callback(null, buffer);
199270
} else {
200271
if(options.libary) {
201272
buffer.push("/******/var ");

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "webpack",
3-
"version": "0.3.7",
3+
"version": "0.3.8",
44
"author": "Tobias Koppers @sokra",
55
"description": "Packs CommonJs Modules for the browser. Allows to split your codebase into multiple bundles, which can be loaded on demand. Support loading of js, json, jade, coffee, css, ... out of the box and more with custom loaders.",
66
"dependencies": {

0 commit comments

Comments
 (0)