add [hash] function

This commit is contained in:
Tobias Koppers 2012-05-01 17:46:26 +02:00
parent 686756ca89
commit c44e5f8dc4
9 changed files with 126 additions and 42 deletions

View File

@ -401,6 +401,13 @@ free module variables which are replaced with a module. ex. `{ "$": "jquery" }`
`source` if `options.output` is not set `source` if `options.output` is not set
else `stats` as json see [example](/sokra/modules-webpack/tree/master/examples/code-splitting) else `stats` as json see [example](/sokra/modules-webpack/tree/master/examples/code-splitting)
## Bonus featues
### File hash
You can use `[hash]` in `scriptSrcPrefix`, `output`, `outputDirectory` and `outputPostfix`.
`webpack` will replace it with a hash of your files, when writing.
## Comparison ## Comparison
<table> <table>
@ -639,7 +646,7 @@ else `stats` as json see [example](/sokra/modules-webpack/tree/master/examples/c
require JSON require JSON
</td> </td>
<td> <td>
yes <em>NEW</em> yes
</td> </td>
<td> <td>
no no
@ -669,7 +676,7 @@ else `stats` as json see [example](/sokra/modules-webpack/tree/master/examples/c
loaders loaders
</td> </td>
<td> <td>
yes <em>NEW</em> yes
</td> </td>
<td> <td>
no no
@ -684,7 +691,7 @@ else `stats` as json see [example](/sokra/modules-webpack/tree/master/examples/c
compile coffee script compile coffee script
</td> </td>
<td> <td>
yes <em>NEW</em> yes
</td> </td>
<td> <td>
no no
@ -793,7 +800,6 @@ You are also welcome to correct any spelling mistakes or any language issues, be
## Future plans ## Future plans
* watch mode * watch mode
* append hash of file to filename, for better caching
* more polyfills for node.js buildin modules, but optional * more polyfills for node.js buildin modules, but optional
* require from protocol `require("http://...")` * require from protocol `require("http://...")`

View File

@ -125,9 +125,6 @@ if(argv.single) {
if(!options.outputDirectory) options.outputDirectory = path.dirname(output); if(!options.outputDirectory) options.outputDirectory = path.dirname(output);
if(!options.output) options.output = path.basename(output); if(!options.output) options.output = path.basename(output);
if(!options.outputPostfix) options.outputPostfix = "." + path.basename(output); if(!options.outputPostfix) options.outputPostfix = "." + path.basename(output);
var outExists = path.existsSync(options.outputDirectory);
if(!outExists)
fs.mkdirSync(options.outputDirectory);
webpack(input, options, function(err, stats) { webpack(input, options, function(err, stats) {
if(err) { if(err) {
console.error(err); console.error(err);
@ -139,15 +136,16 @@ if(argv.single) {
function c(str) { function c(str) {
return argv.colors ? str : ""; return argv.colors ? str : "";
} }
console.log("Hash: "+c("\033[1m") + stats.hash + c("\033[22m"));
console.log("Chunks: "+c("\033[1m") + stats.chunkCount + c("\033[22m")); console.log("Chunks: "+c("\033[1m") + stats.chunkCount + c("\033[22m"));
console.log("Modules: "+c("\033[1m") + stats.modulesCount + c("\033[22m")); console.log("Modules: "+c("\033[1m") + stats.modulesCount + c("\033[22m"));
console.log("Modules including duplicates: "+c("\033[1m") + stats.modulesIncludingDuplicates + c("\033[22m")); console.log("Modules including duplicates: "+c("\033[1m") + stats.modulesIncludingDuplicates + c("\033[22m"));
console.log("Modules pre chunk: "+c("\033[1m") + stats.modulesPerChunk + c("\033[22m")); console.log("Modules pre chunk: "+c("\033[1m") + stats.modulesPerChunk + c("\033[22m"));
console.log("Modules first chunk: "+c("\033[1m") + stats.modulesFirstChunk + c("\033[22m")); console.log("Modules first chunk: "+c("\033[1m") + stats.modulesFirstChunk + c("\033[22m"));
if(stats.fileSizes) if(stats.fileSizes)
for(var file in stats.fileSizes) { Object.keys(stats.fileSizes).reverse().forEach(function(file) {
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"); 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");
}; });
var cwd = process.cwd(); var cwd = process.cwd();
var cwdParent = path.dirname(cwd); var cwdParent = path.dirname(cwd);
var buildins = path.join(__dirname, ".."); var buildins = path.join(__dirname, "..");
@ -174,7 +172,7 @@ if(argv.single) {
console.log(" <id> <size> <filename>"); console.log(" <id> <size> <filename>");
if(argv.verbose) if(argv.verbose)
console.log(" <reason> from <filename>"); console.log(" <reason> from <filename>");
for(var file in stats.fileModules) { Object.keys(stats.fileModules).reverse().forEach(function(file) {
console.log(c("\033[1m\033[32m") + file + c("\033[39m\033[22m")); console.log(c("\033[1m\033[32m") + file + c("\033[39m\033[22m"));
var modules = stats.fileModules[file]; var modules = stats.fileModules[file];
if(argv["by-size"]) if(argv["by-size"])
@ -207,7 +205,7 @@ if(argv.single) {
}); });
} }
}); });
} });
} }
if(stats.warnings) { if(stats.warnings) {
stats.warnings.forEach(function(warning) { stats.warnings.forEach(function(warning) {

View File

@ -141,6 +141,7 @@ module.exports = function() {
## Uncompressed ## Uncompressed
``` ```
Hash: 839970d2bf36067bbcc02e254658eee5
Chunks: 2 Chunks: 2
Modules: 6 Modules: 6
Modules including duplicates: 6 Modules including duplicates: 6
@ -170,6 +171,7 @@ output.js
## Minimized (uglify-js, no zip) ## Minimized (uglify-js, no zip)
``` ```
Hash: 839970d2bf36067bbcc02e254658eee5
Chunks: 2 Chunks: 2
Modules: 6 Modules: 6
Modules including duplicates: 6 Modules including duplicates: 6

View File

@ -108,6 +108,7 @@ webpackJsonp(1,{3:function(a,b,c){},4:function(a,b,c){}})
## Uncompressed ## Uncompressed
``` ```
Hash: efedb0352f230f80a7d673655c226d6b
Chunks: 2 Chunks: 2
Modules: 5 Modules: 5
Modules including duplicates: 5 Modules including duplicates: 5
@ -135,6 +136,7 @@ output.js
## Minimized (uglify-js, no zip) ## Minimized (uglify-js, no zip)
``` ```
Hash: efedb0352f230f80a7d673655c226d6b
Chunks: 2 Chunks: 2
Modules: 5 Modules: 5
Modules including duplicates: 5 Modules including duplicates: 5

View File

@ -110,6 +110,7 @@ console.timeEnd = function() {
## Uncompressed ## Uncompressed
``` ```
Hash: 473faf0d98f59635bba5232deb86646a
Chunks: 1 Chunks: 1
Modules: 4 Modules: 4
Modules including duplicates: 4 Modules including duplicates: 4
@ -134,6 +135,7 @@ output.js
## Minimized (uglify-js, no zip) ## Minimized (uglify-js, no zip)
``` ```
Hash: 473faf0d98f59635bba5232deb86646a
Chunks: 1 Chunks: 1
Modules: 4 Modules: 4
Modules including duplicates: 4 Modules including duplicates: 4

View File

@ -130,6 +130,7 @@ Prints in node.js (`node example.js`) and in browser:
## Uncompressed ## Uncompressed
``` ```
Hash: 7286e7c84a0b1d6841f47269de641d85
Chunks: 1 Chunks: 1
Modules: 5 Modules: 5
Modules including duplicates: 5 Modules including duplicates: 5

View File

@ -115,6 +115,7 @@ module.exports = function() {
## Uncompressed ## Uncompressed
``` ```
Hash: 34e41b2aba89897d758177096351e8aa
Chunks: 1 Chunks: 1
Modules: 6 Modules: 6
Modules including duplicates: 6 Modules including duplicates: 6
@ -142,6 +143,7 @@ output.js
## Minimized (uglify-js, no zip) ## Minimized (uglify-js, no zip)
``` ```
Hash: 34e41b2aba89897d758177096351e8aa
Chunks: 1 Chunks: 1
Modules: 6 Modules: 6
Modules including duplicates: 6 Modules including duplicates: 6

View File

@ -7,6 +7,8 @@ var path = require("path");
var writeChunk = require("./writeChunk"); var writeChunk = require("./writeChunk");
var fs = require("fs"); var fs = require("fs");
var HASH_REGEXP = /\[hash\]/i;
var templateAsync = require("fs").readFileSync(path.join(__dirname, "templateAsync.js")); var templateAsync = require("fs").readFileSync(path.join(__dirname, "templateAsync.js"));
var templateSingle = require("fs").readFileSync(path.join(__dirname, "templateSingle.js")); var templateSingle = require("fs").readFileSync(path.join(__dirname, "templateSingle.js"));
/* /*
@ -108,29 +110,55 @@ module.exports = function(context, moduleName, options, callback) {
} }
var fileSizeMap = {}; var fileSizeMap = {};
var fileModulesMap = {}; var fileModulesMap = {};
var fileWrites = [];
var chunksCount = 0; var chunksCount = 0;
for(var chunkId in depTree.chunks) { var chunkIds = Object.keys(depTree.chunks);
chunkIds.sort(function(a,b) {
return parseInt(b, 10) - parseInt(a, 10);
});
var hash;
try {
hash = new (require("crypto").Hash)("md5");
hash.update(JSON.stringify(options.libary || ""));
hash.update(JSON.stringify(options.outputPostfix));
hash.update(JSON.stringify(options.outputJsonpFunction));
hash.update(JSON.stringify(options.scriptSrcPrefix));
hash.update(templateAsync);
hash.update(templateSingle);
hash.update("1");
} catch(e) {
callback(e);
return;
hash = null;
}
chunkIds.forEach(function(chunkId) {
var chunk = depTree.chunks[chunkId]; var chunk = depTree.chunks[chunkId];
if(chunk.empty) continue; if(chunk.empty) return;
if(chunk.equals !== undefined) continue; if(chunk.equals !== undefined) return;
chunksCount++; chunksCount++;
var filename = path.join(options.outputDirectory, var filename = path.join(options.outputDirectory,
chunk.id === 0 ? options.output : chunk.id + options.outputPostfix); chunk.id === 0 ? options.output : chunk.id + options.outputPostfix);
var content = writeChunk(depTree, chunk, options);
if(hash) hash.update(content);
buffer = []; buffer = [];
if(chunk.id === 0) { if(chunk.id === 0) {
if(hash)
hash = hash.digest("hex");
else
hash = "";
if(options.libary) { if(options.libary) {
buffer.push("/******/var "); buffer.push("/******/var ");
buffer.push(options.libary); buffer.push(options.libary);
buffer.push("=\n"); buffer.push("=\n");
} }
if(Object.keys(depTree.chunks).length > 1) { if(chunkIds.length > 1) {
buffer.push(templateAsync); buffer.push(templateAsync);
buffer.push("/******/({a:"); buffer.push("/******/({a:");
buffer.push(JSON.stringify(options.outputPostfix)); buffer.push(JSON.stringify(options.outputPostfix.replace(HASH_REGEXP, hash)));
buffer.push(",b:"); buffer.push(",b:");
buffer.push(JSON.stringify(options.outputJsonpFunction)); buffer.push(JSON.stringify(options.outputJsonpFunction));
buffer.push(",c:"); buffer.push(",c:");
buffer.push(JSON.stringify(options.scriptSrcPrefix)); buffer.push(JSON.stringify(options.scriptSrcPrefix.replace(HASH_REGEXP, hash)));
buffer.push(",\n"); buffer.push(",\n");
} else { } else {
buffer.push(templateSingle); buffer.push(templateSingle);
@ -143,7 +171,7 @@ module.exports = function(context, moduleName, options, callback) {
buffer.push(chunk.id); buffer.push(chunk.id);
buffer.push(", {\n"); buffer.push(", {\n");
} }
buffer.push(writeChunk(depTree, chunk, options)); buffer.push(content);
buffer.push("/******/})"); buffer.push("/******/})");
buffer = buffer.join(""); buffer = buffer.join("");
try { try {
@ -152,9 +180,7 @@ module.exports = function(context, moduleName, options, callback) {
callback(e); callback(e);
return; return;
} }
fs.writeFile(filename, buffer, "utf-8", function(err) { fileWrites.push([filename, buffer]);
if(err) throw err;
});
fileSizeMap[path.basename(filename)] = buffer.length; fileSizeMap[path.basename(filename)] = buffer.length;
var modulesArray = []; var modulesArray = [];
for(var moduleId in chunk.modules) { for(var moduleId in chunk.modules) {
@ -172,30 +198,75 @@ module.exports = function(context, moduleName, options, callback) {
return a.id - b.id; return a.id - b.id;
}); });
fileModulesMap[path.basename(filename)] = modulesArray; fileModulesMap[path.basename(filename)] = modulesArray;
});
// write files
var remFiles = fileWrites.length;
var outDir = options.outputDirectory.replace(HASH_REGEXP, hash);
function createDir(dir, callback) {
path.exists(dir, function(exists) {
if(exists)
callback();
else {
fs.mkdir(dir, function(err) {
if(err) {
var parentDir = path.join(dir, "..");
if(parentDir == dir)
return callback(err);
createDir(parentDir, function(err) {
if(err) return callback(err);
fs.mkdir(dir, function(err) {
if(err) return callback(err);
callback();
});
});
return;
}
callback();
});
}
});
} }
buffer = {}; createDir(outDir, function(err) {
buffer.chunkCount = chunksCount; if(err) return callback(err);
buffer.modulesCount = Object.keys(depTree.modules).length; writeFiles();
var sum = 0; });
for(var chunkId in depTree.chunks) { function writeFiles() {
for(var moduleId in depTree.chunks[chunkId].modules) { fileWrites.forEach(function(writeAction) {
if(depTree.chunks[chunkId].modules[moduleId] === "include") fs.writeFile(writeAction[0].replace(HASH_REGEXP, hash), writeAction[1], "utf-8", function(err) {
if(err) throw err;
remFiles--;
if(remFiles === 0)
writingFinished();
});
});
}
function writingFinished() {
// Stats
buffer = {};
buffer.hash = hash;
buffer.chunkCount = chunksCount;
buffer.modulesCount = Object.keys(depTree.modules).length;
var sum = 0;
for(var chunkId in depTree.chunks) {
for(var moduleId in depTree.chunks[chunkId].modules) {
if(depTree.chunks[chunkId].modules[moduleId] === "include")
sum++;
}
}
buffer.modulesIncludingDuplicates = sum;
buffer.modulesPerChunk = Math.round(sum / chunksCount*10)/10;
sum = 0;
for(var moduleId in depTree.chunks[0].modules) {
if(depTree.chunks[0].modules[moduleId] === "include")
sum++; sum++;
} }
buffer.modulesFirstChunk = sum;
buffer.fileSizes = fileSizeMap;
buffer.warnings = depTree.warnings;
buffer.errors = depTree.errors;
buffer.fileModules = fileModulesMap;
callback(null, buffer);
} }
buffer.modulesIncludingDuplicates = sum;
buffer.modulesPerChunk = Math.round(sum / chunksCount*10)/10;
sum = 0;
for(var moduleId in depTree.chunks[0].modules) {
if(depTree.chunks[0].modules[moduleId] === "include")
sum++;
}
buffer.modulesFirstChunk = sum;
buffer.fileSizes = fileSizeMap;
buffer.warnings = depTree.warnings;
buffer.errors = depTree.errors;
buffer.fileModules = fileModulesMap;
callback(null, buffer);
} else { } else {
if(options.libary) { if(options.libary) {
buffer.push("/******/var "); buffer.push("/******/var ");

View File

@ -1,6 +1,6 @@
{ {
"name": "webpack", "name": "webpack",
"version": "0.3.7", "version": "0.3.8",
"author": "Tobias Koppers @sokra", "author": "Tobias Koppers @sokra",
"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.", "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.",
"dependencies": { "dependencies": {