2012-03-12 04:50:55 +08:00
|
|
|
/*
|
|
|
|
MIT License http://www.opensource.org/licenses/mit-license.php
|
|
|
|
Author Tobias Koppers @sokra
|
|
|
|
*/
|
2012-03-10 20:11:23 +08:00
|
|
|
var buildDeps = require("./buildDeps");
|
|
|
|
var path = require("path");
|
|
|
|
var writeChunk = require("./writeChunk");
|
|
|
|
var fs = require("fs");
|
|
|
|
|
2012-05-01 23:46:26 +08:00
|
|
|
var HASH_REGEXP = /\[hash\]/i;
|
|
|
|
|
2012-03-10 20:11:23 +08:00
|
|
|
var templateAsync = require("fs").readFileSync(path.join(__dirname, "templateAsync.js"));
|
|
|
|
var templateSingle = require("fs").readFileSync(path.join(__dirname, "templateSingle.js"));
|
|
|
|
/*
|
|
|
|
webpack(context, moduleName, options, callback);
|
|
|
|
webpack(context, moduleName, callback);
|
|
|
|
webpack(absoluteModulePath, options, callback);
|
|
|
|
webpack(absoluteModulePath, callback);
|
2012-03-12 04:50:55 +08:00
|
|
|
|
2012-03-10 20:11:23 +08:00
|
|
|
callback: function(err, source / stats)
|
|
|
|
source if options.output is not set
|
|
|
|
else stats json
|
2012-03-12 04:50:55 +08:00
|
|
|
|
2012-03-10 20:11:23 +08:00
|
|
|
options:
|
|
|
|
- outputJsonpFunction
|
|
|
|
JSONP function used to load chunks
|
|
|
|
- scriptSrcPrefix
|
|
|
|
Path from where chunks are loaded
|
|
|
|
- outputDirectory
|
|
|
|
write files to this directory (absolute path)
|
|
|
|
- output
|
|
|
|
write first chunk to this file
|
|
|
|
- outputPostfix
|
|
|
|
write chunks to files named chunkId plus outputPostfix
|
|
|
|
- libary
|
|
|
|
exports of input file are stored in this variable
|
|
|
|
- minimize
|
|
|
|
minimize outputs with uglify-js
|
|
|
|
- includeFilenames
|
|
|
|
add absolute filenames of input files as comments
|
2012-03-16 20:59:19 +08:00
|
|
|
- resolve.alias (object)
|
|
|
|
replace a module. ex {"old-module": "new-module"}
|
|
|
|
- resolve.extensions (object)
|
2012-03-27 06:00:32 +08:00
|
|
|
possible extensions for files
|
2012-03-16 20:59:19 +08:00
|
|
|
- resolve.paths (array)
|
|
|
|
search paths
|
2012-03-27 06:00:32 +08:00
|
|
|
- resolve.loaders (array)
|
|
|
|
extension to loader mappings
|
|
|
|
{test: /\.extension$/, loader: "myloader"}
|
|
|
|
loads files that matches the RegExp to the loader if no other loader set
|
2012-03-16 20:59:19 +08:00
|
|
|
- parse.overwrites (object)
|
|
|
|
free module varables which are replaced with a module
|
|
|
|
ex. { "$": "jquery" }
|
2012-03-10 20:11:23 +08:00
|
|
|
*/
|
|
|
|
module.exports = function(context, moduleName, options, callback) {
|
|
|
|
if(typeof moduleName === "object") {
|
|
|
|
callback = options;
|
|
|
|
options = moduleName;
|
|
|
|
moduleName = "./" + path.basename(context);
|
|
|
|
context = path.dirname(context);
|
|
|
|
}
|
|
|
|
if(typeof moduleName === "function") {
|
|
|
|
callback = moduleName;
|
|
|
|
options = {};
|
|
|
|
moduleName = "./" + path.basename(context);
|
|
|
|
context = path.dirname(context);
|
|
|
|
}
|
|
|
|
if(!callback) {
|
|
|
|
callback = options;
|
|
|
|
options = {};
|
|
|
|
}
|
2012-05-02 03:33:59 +08:00
|
|
|
if(!options.events) options.events = new (require("events").EventEmitter)();
|
|
|
|
if(options.watch) {
|
|
|
|
console.log("start watching...");
|
|
|
|
var fs = require("fs");
|
|
|
|
var watchers = [];
|
|
|
|
var isRunning = true;
|
|
|
|
var runAgain = false;
|
|
|
|
function startAgain() {
|
|
|
|
watchers.forEach(function(watcher) {
|
|
|
|
watcher.close();
|
|
|
|
});
|
|
|
|
watchers.length = 0;
|
|
|
|
isRunning = true;
|
|
|
|
setTimeout(function() {
|
|
|
|
runAgain = false;
|
|
|
|
webpack(context, moduleName, options, callback);
|
|
|
|
}, 200);
|
|
|
|
}
|
|
|
|
function change() {
|
|
|
|
if(isRunning)
|
|
|
|
runAgain = true;
|
|
|
|
else
|
|
|
|
startAgain()
|
|
|
|
}
|
|
|
|
options.events.on("module", function(module, filename) {
|
|
|
|
if(!filename) return;
|
|
|
|
var w = fs.watch(filename, function() {
|
|
|
|
change();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
options.events.on("context", function(module, dirname) {
|
|
|
|
if(!dirname) return;
|
|
|
|
fs.watch(dirname, function() {
|
|
|
|
change();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
options.events.on("bundle", function(stats) {
|
|
|
|
isRunning = false;
|
|
|
|
if(runAgain)
|
|
|
|
startAgain();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
return webpack(context, moduleName, options, callback);
|
|
|
|
}
|
|
|
|
function webpack(context, moduleName, options, finalCallback) {
|
2012-03-15 21:38:55 +08:00
|
|
|
options.parse = options.parse || {};
|
|
|
|
options.parse.overwrites = options.parse.overwrites || {};
|
|
|
|
options.parse.overwrites.process = options.parse.overwrites.process || ("__webpack_process");
|
2012-04-05 20:59:01 +08:00
|
|
|
options.parse.overwrites.module = options.parse.overwrites.module || ("__webpack_module+(module)");
|
2012-03-16 06:01:42 +08:00
|
|
|
options.parse.overwrites.console = options.parse.overwrites.console || ("__webpack_console");
|
|
|
|
options.parse.overwrites.global = options.parse.overwrites.global || ("__webpack_global");
|
2012-04-05 20:59:01 +08:00
|
|
|
options.parse.overwrites.Buffer = options.parse.overwrites.Buffer || ("buffer+.Buffer");
|
|
|
|
options.parse.overwrites["__dirname"] = options.parse.overwrites["__dirname"] || ("__webpack_dirname");
|
|
|
|
options.parse.overwrites["__filename"] = options.parse.overwrites["__filename"] || ("__webpack_filename");
|
2012-03-15 21:38:55 +08:00
|
|
|
options.resolve = options.resolve || {};
|
|
|
|
options.resolve.paths = options.resolve.paths || [];
|
2012-03-16 20:59:19 +08:00
|
|
|
options.resolve.paths.push(path.join(path.dirname(__dirname), "buildin"));
|
|
|
|
options.resolve.paths.push(path.join(path.dirname(__dirname), "buildin", "web_modules"));
|
|
|
|
options.resolve.paths.push(path.join(path.dirname(__dirname), "buildin", "node_modules"));
|
2012-03-27 06:00:32 +08:00
|
|
|
options.resolve.paths.push(path.join(path.dirname(__dirname), "node_modules"));
|
2012-03-16 20:59:19 +08:00
|
|
|
options.resolve.alias = options.resolve.alias || {};
|
2012-04-03 22:26:08 +08:00
|
|
|
options.resolve.loaders = options.resolve.loaders || [];
|
2012-03-27 06:00:32 +08:00
|
|
|
options.resolve.loaders.push({test: /\.coffee$/, loader: "coffee"});
|
|
|
|
options.resolve.loaders.push({test: /\.json$/, loader: "json"});
|
|
|
|
options.resolve.loaders.push({test: /\.jade$/, loader: "jade"});
|
2012-04-07 09:20:45 +08:00
|
|
|
options.resolve.loaders.push({test: /\.css$/, loader: "style!css"});
|
2012-05-01 21:47:37 +08:00
|
|
|
options.resolve.loaders.push({test: /\.less$/, loader: "style!less"});
|
2012-05-02 03:33:59 +08:00
|
|
|
options.events.emit("task");
|
|
|
|
function callback(err, result) {
|
|
|
|
options.events.emit("task-end");
|
|
|
|
finalCallback(err, result);
|
|
|
|
}
|
2012-03-10 20:11:23 +08:00
|
|
|
buildDeps(context, moduleName, options, function(err, depTree) {
|
|
|
|
if(err) {
|
|
|
|
callback(err);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
var buffer = [];
|
|
|
|
if(options.output) {
|
|
|
|
if(!options.outputJsonpFunction)
|
|
|
|
options.outputJsonpFunction = "webpackJsonp" + (options.libary || "");
|
|
|
|
options.scriptSrcPrefix = options.scriptSrcPrefix || "";
|
|
|
|
if(!options.outputDirectory) {
|
|
|
|
options.outputDirectory = path.dirname(options.output);
|
|
|
|
options.output = path.basename(options.output);
|
|
|
|
}
|
|
|
|
if(!options.outputPostfix) {
|
|
|
|
options.outputPostfix = "." + options.output;
|
|
|
|
}
|
|
|
|
var fileSizeMap = {};
|
2012-03-14 23:33:46 +08:00
|
|
|
var fileModulesMap = {};
|
2012-05-01 23:46:26 +08:00
|
|
|
var fileWrites = [];
|
2012-03-10 20:11:23 +08:00
|
|
|
var chunksCount = 0;
|
2012-05-01 23:46:26 +08:00
|
|
|
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) {
|
2012-03-10 20:11:23 +08:00
|
|
|
var chunk = depTree.chunks[chunkId];
|
2012-05-01 23:46:26 +08:00
|
|
|
if(chunk.empty) return;
|
|
|
|
if(chunk.equals !== undefined) return;
|
2012-03-10 20:11:23 +08:00
|
|
|
chunksCount++;
|
|
|
|
var filename = path.join(options.outputDirectory,
|
|
|
|
chunk.id === 0 ? options.output : chunk.id + options.outputPostfix);
|
2012-05-01 23:46:26 +08:00
|
|
|
var content = writeChunk(depTree, chunk, options);
|
|
|
|
if(hash) hash.update(content);
|
2012-03-10 20:11:23 +08:00
|
|
|
buffer = [];
|
|
|
|
if(chunk.id === 0) {
|
2012-05-01 23:46:26 +08:00
|
|
|
if(hash)
|
|
|
|
hash = hash.digest("hex");
|
|
|
|
else
|
|
|
|
hash = "";
|
2012-03-10 20:11:23 +08:00
|
|
|
if(options.libary) {
|
|
|
|
buffer.push("/******/var ");
|
|
|
|
buffer.push(options.libary);
|
|
|
|
buffer.push("=\n");
|
|
|
|
}
|
2012-05-01 23:46:26 +08:00
|
|
|
if(chunkIds.length > 1) {
|
2012-03-10 20:11:23 +08:00
|
|
|
buffer.push(templateAsync);
|
|
|
|
buffer.push("/******/({a:");
|
2012-05-01 23:46:26 +08:00
|
|
|
buffer.push(JSON.stringify(options.outputPostfix.replace(HASH_REGEXP, hash)));
|
2012-03-10 20:11:23 +08:00
|
|
|
buffer.push(",b:");
|
2012-04-03 22:26:08 +08:00
|
|
|
buffer.push(JSON.stringify(options.outputJsonpFunction));
|
2012-03-10 20:11:23 +08:00
|
|
|
buffer.push(",c:");
|
2012-05-01 23:46:26 +08:00
|
|
|
buffer.push(JSON.stringify(options.scriptSrcPrefix.replace(HASH_REGEXP, hash)));
|
2012-03-10 20:11:23 +08:00
|
|
|
buffer.push(",\n");
|
|
|
|
} else {
|
|
|
|
buffer.push(templateSingle);
|
|
|
|
buffer.push("/******/({\n");
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
buffer.push("/******/");
|
|
|
|
buffer.push(options.outputJsonpFunction);
|
|
|
|
buffer.push("(");
|
|
|
|
buffer.push(chunk.id);
|
|
|
|
buffer.push(", {\n");
|
|
|
|
}
|
2012-05-01 23:46:26 +08:00
|
|
|
buffer.push(content);
|
2012-03-10 20:11:23 +08:00
|
|
|
buffer.push("/******/})");
|
|
|
|
buffer = buffer.join("");
|
2012-03-27 06:00:32 +08:00
|
|
|
try {
|
|
|
|
if(options.minimize) buffer = uglify(buffer, filename);
|
|
|
|
} catch(e) {
|
|
|
|
callback(e);
|
|
|
|
return;
|
|
|
|
}
|
2012-05-01 23:46:26 +08:00
|
|
|
fileWrites.push([filename, buffer]);
|
2012-03-10 20:11:23 +08:00
|
|
|
fileSizeMap[path.basename(filename)] = buffer.length;
|
2012-03-14 23:33:46 +08:00
|
|
|
var modulesArray = [];
|
|
|
|
for(var moduleId in chunk.modules) {
|
2012-03-15 07:05:29 +08:00
|
|
|
var modu = depTree.modules[moduleId];
|
2012-04-08 03:36:55 +08:00
|
|
|
if(chunk.modules[moduleId] === "include") {
|
2012-03-15 07:05:29 +08:00
|
|
|
modulesArray.push({
|
2012-04-08 03:36:55 +08:00
|
|
|
id: modu.realId,
|
|
|
|
size: modu.size,
|
|
|
|
filename: modu.filename,
|
|
|
|
dirname: modu.dirname,
|
2012-03-15 07:05:29 +08:00
|
|
|
reasons: modu.reasons});
|
2012-04-08 03:36:55 +08:00
|
|
|
}
|
2012-03-14 23:33:46 +08:00
|
|
|
}
|
2012-04-08 03:36:55 +08:00
|
|
|
modulesArray.sort(function(a, b) {
|
|
|
|
return a.id - b.id;
|
|
|
|
});
|
2012-03-14 23:33:46 +08:00
|
|
|
fileModulesMap[path.basename(filename)] = modulesArray;
|
2012-05-01 23:46:26 +08:00
|
|
|
});
|
2012-05-02 03:33:59 +08:00
|
|
|
options.events.emit("start-writing", hash);
|
2012-05-01 23:46:26 +08:00
|
|
|
// 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();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
createDir(outDir, function(err) {
|
|
|
|
if(err) return callback(err);
|
|
|
|
writeFiles();
|
|
|
|
});
|
|
|
|
function writeFiles() {
|
|
|
|
fileWrites.forEach(function(writeAction) {
|
|
|
|
fs.writeFile(writeAction[0].replace(HASH_REGEXP, hash), writeAction[1], "utf-8", function(err) {
|
|
|
|
if(err) throw err;
|
|
|
|
remFiles--;
|
|
|
|
if(remFiles === 0)
|
|
|
|
writingFinished();
|
|
|
|
});
|
|
|
|
});
|
2012-03-10 20:11:23 +08:00
|
|
|
}
|
2012-05-01 23:46:26 +08:00
|
|
|
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")
|
2012-03-10 20:11:23 +08:00
|
|
|
sum++;
|
|
|
|
}
|
2012-05-01 23:46:26 +08:00
|
|
|
buffer.modulesFirstChunk = sum;
|
|
|
|
buffer.fileSizes = fileSizeMap;
|
|
|
|
buffer.warnings = depTree.warnings;
|
|
|
|
buffer.errors = depTree.errors;
|
|
|
|
buffer.fileModules = fileModulesMap;
|
2012-05-02 03:33:59 +08:00
|
|
|
options.events.emit("bundle", buffer);
|
2012-05-01 23:46:26 +08:00
|
|
|
callback(null, buffer);
|
2012-03-10 20:11:23 +08:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if(options.libary) {
|
|
|
|
buffer.push("/******/var ");
|
|
|
|
buffer.push(options.libary);
|
|
|
|
buffer.push("=\n");
|
|
|
|
}
|
|
|
|
buffer.push(templateSingle);
|
|
|
|
buffer.push("/******/({\n");
|
|
|
|
buffer.push(writeChunk(depTree, options));
|
|
|
|
buffer.push("/******/})");
|
|
|
|
buffer = buffer.join("");
|
2012-03-27 06:00:32 +08:00
|
|
|
try {
|
|
|
|
if(options.minimize) buffer = uglify(buffer, "output");
|
|
|
|
callback(null, buffer);
|
|
|
|
} catch(e) {
|
|
|
|
callback(e);
|
|
|
|
}
|
2012-03-10 20:11:23 +08:00
|
|
|
}
|
|
|
|
});
|
2012-05-02 03:33:59 +08:00
|
|
|
return options.events;
|
2012-03-10 20:11:23 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
function uglify(input, filename) {
|
|
|
|
var uglify = require("uglify-js");
|
|
|
|
try {
|
|
|
|
source = uglify.parser.parse(input);
|
|
|
|
source = uglify.uglify.ast_mangle(source);
|
|
|
|
source = uglify.uglify.ast_squeeze(source);
|
|
|
|
source = uglify.uglify.gen_code(source);
|
|
|
|
} catch(e) {
|
2012-03-27 06:00:32 +08:00
|
|
|
throw new Error(filename + " @ Line " + e.line + ", Col " + e.col + ", " + e.message);
|
2012-03-10 20:11:23 +08:00
|
|
|
return input;
|
|
|
|
}
|
|
|
|
return source;
|
|
|
|
}
|