API: loaderContext.depencency is more relaxed and don't need to be called before reading

API: loader.seperable cannot combined with
 loaderContext.emitFile and loaderContext.emitSubStats
 loaderContext.options.resolve
 loaderContext.options.events
 loaderContext.resolve and .sync
API: added profile option (and --profile)
API: added workers option (and --workers)
API: added closeWorkers option
API: if option workers is used:
 options must be JSON.stringify-able. Except options.resolve and options.events.
 Any error thrown in loader must be an object (i. e. an Error object). Only message, stack and value of toString is passed to main process.
API: The expected Cache object for options.cache has changed.
API: event module is emited after the module is finished.
API: event context is now named context-enum
API: added event context which is emited after the context is finished.
API: event dependency is removed. Use stats.dependencies for this.
API: event loader is removed. Use stats.loaders for this.
API: added stats.contexts as a list of contexts.
API: added stats...modules[..].dependencies for as list of files which affect the module's content.
API: added stats...modules[..].loaders for as list of loaders which affect the module's content.
API: removed stats.modulesPerChunk, it is useless and was deprecated.
API: added stats.chunkNameFiles which export the files for named chunks
API: added stats.startTime, timestamp as number
cmd: more colorful output to indicate caching and timing
API: webpack in watch mode emits the event watch-end if watch mode have to end (i. e. loader changed). You may restart it after clearing require.cache.
API: added loaderContext.loaderType as one of loader, preLoader or postLoader.
API: added loaderContext.currentLoaders as list of all loader of the current type.
API: added loaderContext.loaderIndex as index of current loader in loaderContext.currentLoaders.
API: added loaderContext.loaders, loaderContext.preLoaders and loaderContext.postLoaders.
This commit is contained in:
Tobias Koppers 2012-09-25 16:45:53 +02:00
parent ab357031ba
commit 2eeb4923e1
13 changed files with 590 additions and 289 deletions

View File

@ -325,21 +325,23 @@ if invoked without arguments it prints a usage:
Usage: webpack <options> <input> <output>
Options:
--min Minimize it with uglifyjs [boolean] [default: false]
--filenames Output Filenames Into File [boolean] [default: false]
--options Options JSON File [string]
--public-prefix Path Prefix For JavaScript Loading [string]
--libary Stores the exports into this variable [string]
--colors Output Stats with colors [boolean] [default: false]
--single Disable lazy loading [boolean] [default: false]
--json Output Stats as JSON [boolean] [default: false]
--by-size Sort modules by size in Stats [boolean] [default: false]
--verbose Output dependencies in Stats [boolean] [default: false]
--alias Set a alias name for a module. ex. http=http-browserify [string]
--debug Prints debug info to output files [boolean] [default: false]
--watch Recompiles on changes (except loaders) [boolean] [default: false]
--watch-delay Timeout to wait for the last change [string]
--progress Displays a progress while compiling [boolean] [default: false]
--min Minimize it with uglifyjs [boolean] [default: false]
--filenames Output Filenames Into File [boolean] [default: false]
--options Options JSON File [string]
--public-prefix Path Prefix For JavaScript Loading [string]
--libary Stores the exports into this variable [string]
--colors Output Stats with colors [boolean] [default: false]
--single Disable lazy loading [boolean] [default: false]
--json Output Stats as JSON [boolean] [default: false]
--by-size Sort modules by size in Stats [boolean] [default: false]
--verbose Output dependencies in Stats [boolean] [default: false]
--profile Capture timings for modules [boolean] [default: false]
--alias Set a alias name for a module. ex. http=http-browserify [string]
--debug Prints debug info to output files [boolean] [default: false]
--watch Recompiles on changes (except loaders) [boolean] [default: false]
--watch-delay Timeout to wait for the last change
--workers Use worker processes to be faster (BETA) [boolean] [default: false]
--progress Displays a progress while compiling [boolean] [default: false]
```
### Programmatically Usage
@ -407,12 +409,11 @@ You can also save this options object in a JSON file and use it with the shell c
// "bundle-invalid" () fired when the bundle gets invalid
// [bundle-invalid is only fired in watch mode]
// "start-writing" (hash) fired when webpack starts writing
// -- events for dependencies --
// "module" (module, filename) before a module is loaded
// "context" (module, dirname) before a context is loaded
// "dependency" (filename) before a dependency is loaded
// "static-dependency"(filename) after a dependency is flagged as not recompile-able
// "loader" (filename) before a loader is required
// "watch-end" () watch ended because of loader change
// -- events for modules --
// "module" (module, filename) after a module is loaded
// "context-enum" (module, dirname) before a context is enumerated
// "context" (module, dirname) after a context is loaded
// -- events for progress --
// "task" (name?) start of a task
// "task-end" (name?) end of a task
@ -513,6 +514,26 @@ You can also save this options object in a JSON file and use it with the shell c
// syntax like resolve.loaders
// all loaders which matches the file are applied before the
// normal loaders. This cannot be overridden in the require call.
workers: true,
// default: false
// options: true, false, number > 0, object of type webpack/lib/Workers
// Use worker processes to do some work.
// This *can* boost performance, but starting these processes has some
// overhead (~100-200ms). If loaders are used they need to have the
// seperable flag to work in worker process. If they havn't they work in
// the main process.
// In watch mode, worker processes only start once. So workers = true
// is recommended for watch mode.
closeWorkers: false,
// default: true
// close the worker processes on webpack exit.
profile: true,
// default: false
// capture timings for the build.
// they are stored in the stats
}
```
@ -525,6 +546,7 @@ else `stats` as json:
``` javascript
{
hash: "52bd9213...38d",
startTime: 237467691, // in ms since 1.1.1990
time: 1234, // in ms
chunkCount: 2,
modulesCount: 10,
@ -534,13 +556,20 @@ else `stats` as json:
"output.js": 1234,
"1.output.js": 2345
},
chunkNameFiles: {
"main": "output.js",
"namedChunk": "1.output.js"
}
warnings: [ "Some warning" ],
errors: [ "Some error" ],
fileModules: {
"output.js": [
{ id: 0, size: 123, filename: "/home/.../main.js", reasons: [
{ type: "main" }
]},
],
dependencies: [ "filename", ... ],
loaders: [ "filename of loader", ... ]
},
{ id: 1, size: 234, filename: "...", reasons: [
{ type: "require", // or "context", "async require", "async context"
count: 2,
@ -919,9 +948,7 @@ You are also welcome to correct any spelling mistakes or any language issues, be
## Future plans
* more and better polyfills for node.js buildin modules
* cache in folder and allow reuseing it
* write it into the wiki if you have more ideas...
see [/webpack/webpack/wiki/Ideas](wiki Ideas)
## License

View File

@ -56,6 +56,10 @@ var argv = require("optimist")
.describe("verbose", "Output dependencies in Stats")
.default("verbose", false)
.boolean("profile")
.describe("profile", "Capture timings for modules")
.default("profile", false)
.string("alias")
.describe("alias", "Set a alias name for a module. ex. http=http-browserify")
@ -67,10 +71,13 @@ var argv = require("optimist")
.describe("watch", "Recompiles on changes (except loaders)")
.default("watch", false)
.string("watch-delay")
.describe("watch-delay", "Timeout to wait for the last change")
.default("watch", false)
.boolean("workers")
.describe("workers", "Use worker processes to be faster (BETA)")
.default("workers", false)
.boolean("progress")
.describe("progress", "Displays a progress while compiling")
.default("progress", false)
@ -122,8 +129,16 @@ if(argv.watch) {
options.watch = true;
}
if(argv.workers) {
options.workers = true;
}
if(argv.profile) {
options.profile = true;
}
if(argv["watch-delay"]) {
options.watchDelay = parseInt(argv["watch-delay"], 10);
options.watchDelay = argv["watch-delay"];
}
if(argv.filenames) {
@ -164,17 +179,25 @@ if(argv.progress) {
if(!options.events) options.events = new (require("events").EventEmitter)();
var events = options.events;
var nextUpdate = 0;
var sum = 0;
var finished = 0;
var chars = 0;
function print() {
function print(force) {
if(!force && nextUpdate > new Date().getTime()) return;
nextUpdate = new Date().getTime() + 100;
var msg = "";
if(sum > 0) {
var precentage = Math.floor(finished*100/sum);
msg += "compiling... (" + c("\033[1m\033[33m");
msg += sprintf("%4s", finished+"") + "/" + sprintf("%4s", sum+"");
msg += " " + sprintf("%4s", Math.floor(finished*100/sum)+"%");
msg += " " + sprintf("%4s", precentage+"%");
msg += c("\033[39m\033[22m") + ")";
for(var i = 0; i < Math.floor(precentage/2); i++)
msg += "#";
}
for(var i = msg.length; i < chars; i++)
msg += " ";
for(var i = 0; i < chars; i++)
process.stderr.write("\b");
process.stderr.write(msg);
@ -191,6 +214,7 @@ if(argv.progress) {
process.stderr.write("\b \b");
process.stderr.write(name + " " + c("\033[1m\033[32m") + "done" + c("\033[39m\033[22m") + "\n");
chars = 0;
return print(true);
}
print();
});
@ -210,7 +234,7 @@ webpack(input, options, function(err, stats) {
return;
}
if(argv.json)
console.log(JSON.stringify(stats));
console.log(JSON.stringify(stats, null, 2));
else {
console.log(formatOutput(stats, {
colors: argv.colors,

33
bm.js
View File

@ -5,7 +5,7 @@
var webpack = require("./lib/webpack");
var path = require("path");
var TIMES = 5;
var TIMES = 2;
/* TESTS */
@ -27,22 +27,29 @@ var testCases = {
var TESTS = {}
Object.keys(testCases).forEach(function(name) {
TESTS[name + " "] = runWebpack.bind(null, name, testCases[name], false, false, false);
TESTS[name + " single "] = runWebpack.bind(null, name, testCases[name], true, false, false);
TESTS[name + " debug "] = runWebpack.bind(null, name, testCases[name], false, true, false);
TESTS[name + " single debug "] = runWebpack.bind(null, name, testCases[name], true, true, false);
TESTS[name + " min"] = runWebpack.bind(null, name, testCases[name], false, false, true );
TESTS[name + " single min"] = runWebpack.bind(null, name, testCases[name], true, false, true );
TESTS[name + " debug min"] = runWebpack.bind(null, name, testCases[name], false, true, true );
TESTS[name + " single debug min"] = runWebpack.bind(null, name, testCases[name], true, true, true );
TESTS[name + " "] = runWebpack.bind(null, name, testCases[name], false, false, false, false);
TESTS[name + " workers"] = runWebpack.bind(null, name, testCases[name], false, false, false, true);
TESTS[name + " single "] = runWebpack.bind(null, name, testCases[name], true, false, false, false);
TESTS[name + " single workers"] = runWebpack.bind(null, name, testCases[name], true, false, false, true);
TESTS[name + " debug "] = runWebpack.bind(null, name, testCases[name], false, true, false, false);
TESTS[name + " debug workers"] = runWebpack.bind(null, name, testCases[name], false, true, false, true);
TESTS[name + " single debug "] = runWebpack.bind(null, name, testCases[name], true, true, false, false);
TESTS[name + " single debug workers"] = runWebpack.bind(null, name, testCases[name], true, true, false, true);
TESTS[name + " min "] = runWebpack.bind(null, name, testCases[name], false, false, true , false);
TESTS[name + " min workers"] = runWebpack.bind(null, name, testCases[name], false, false, true , true);
TESTS[name + " single min "] = runWebpack.bind(null, name, testCases[name], true, false, true , false);
TESTS[name + " single min workers"] = runWebpack.bind(null, name, testCases[name], true, false, true , true);
});
function runWebpack(name, file, single, debug, min, cb) {
var workers = new (require("./lib/Workers"))(path.join(__dirname, "lib", "buildModuleFork.js"), require("os").cpus().length)
function runWebpack(name, file, single, debug, min, withWorkers, cb) {
webpack(file, {
output: path.join(root, "js", "bm", name.trim() + ".js"),
single: single,
debug: debug,
minimize: min
minimize: min,
workers: withWorkers && workers,
closeWorkers: false
}, cb);
}
@ -60,7 +67,9 @@ asyncForEach(Object.keys(TESTS), function(name, done) {
done();
});
});
}, function() {});
}, function() {
workers.close();
});
/* HELPERS */

View File

@ -6,6 +6,7 @@ var fs = require("fs");
function Cache(options) {
this.options = options = options || {};
if(!this.options.stat) this.options.stat = fs.stat;
this.map = {};
}
var $ = Cache.prototype;
@ -18,14 +19,14 @@ $.get = function(request, callback) {
var count = item.files.length;
var invalid = false;
item.files.forEach(function(file) {
fs.stat(file.path, function(err, stat) {
this.options.stat(file.path, function(err, stat) {
if(err) return invalidate(err.toString());
if(!stat) return invalidate("Cannot read stat");
if(stat.mtime.getTime() !== file.time)
return invalidate("File " + file.path + " has changed");
valid();
});
});
}, this);
if(item.files.length === 0) valid();
function invalidate(msg) {
if(invalid) return;
@ -40,27 +41,27 @@ $.get = function(request, callback) {
}
}
$.create = function(request) {
var self = this;
var item = {
files: []
};
var cacheEntry = {
add: function(path) {
item.files.push({
path: path,
// TODO do it asynchronly
time: fs.statSync(path).mtime.getTime()
});
},
clear: function() {
item.files.length = 0;
},
save: function(value) {
item.value = value;
self.map[request] = item;
cacheEntry.saved = true;
$.store = function(request, dependencies, startTime, value) {
var count = dependencies.length + 1;
var files = [];
var endOne = function() {
if(--count == 0) {
this.map[request] = {
files: files,
value: value
}
}
}
return cacheEntry;
}
}.bind(this);
dependencies.forEach(function(file) {
this.options.stat(file, function(err, stat) {
if(err) return; // do not cache it.
if(stat.mtime.getTime() > startTime.getTime()) return; // do not cache it.
files.unshift({
path: file,
time: stat.mtime.getTime()
});
endOne();
});
}, this);
endOne();
}

58
lib/Workers.js Normal file
View File

@ -0,0 +1,58 @@
var child_process = require("child_process");
function Workers(moduleToFork, count) {
this.nextId = 1;
this.jobs = [];
this.freeWorkers = [];
this.workers = [];
this.callbacks = {};
for(var i = 0; i < count; i++) {
var worker = child_process.fork(moduleToFork);
this.workers.push(worker);
this.freeWorkers.push(worker);
this.bindWorker(worker);
}
}
module.exports = Workers;
Workers.prototype.run = function(parameters, callback) {
if(this.freeWorkers.length > 0)
this.pushJob(this.freeWorkers.shift(), parameters, callback);
else
this.jobs.push([parameters, callback]);
}
Workers.prototype.bindWorker = function(worker) {
worker.on("message", function(result) {
if(Array.isArray(result)) {
var id = result.shift();
var callback = this.callbacks[id];
delete this.callbacks[id];
callback.apply(null, result);
} else {
if(this.jobs.length > 0) {
var job = this.jobs.shift();
this.pushJob(worker, job[0], job[1]);
} else {
this.freeWorkers.push(worker);
}
}
}.bind(this));
}
Workers.prototype.pushJob = function(worker, parameters, callback) {
var id = this.nextId++;
this.callbacks[id] = callback;
worker.send([id, parameters]);
}
Workers.prototype.close = function() {
this.workers.forEach(function(worker) {
worker.disconnect();
});
this.workers.length = 0;
}
Workers.prototype.toJSON = function() {
return {};
}

View File

@ -5,6 +5,7 @@
var parse = require("./parse");
var resolve = require("enhanced-resolve");
var execLoaders = require("enhanced-require/lib/execLoaders");
var buildModule = require("./buildModule");
var fs = require("fs");
var path = require("path");
var assert = require("assert");
@ -94,9 +95,24 @@ module.exports = function buildDeps(context, mainModule, options, callback) {
}
function addModule(depTree, context, modu, options, reason, finalCallback) {
var profile = options.profile && {
start: new Date()
};
options.events.emit("task");
function callback(err, result) {
options.events.emit("task-end");
if(profile && profile.module) {
profile.end = new Date();
profile.module.profile = {
time: profile.end - profile.start,
timeResolve: profile.resolveEnd - profile.start,
timeResolvePrePostLoaders: profile.resolvePrePostLoadersEnd - profile.resolveEnd,
timeLoadersCheck: profile.loadersCheckEnd - profile.resolvePrePostLoadersEnd,
timeBuildModule: profile.buildModuleEnd - profile.loadersCheckEnd,
timeChildren: profile.end - profile.buildModuleEnd
}
}
finalCallback(err, result);
}
@ -112,6 +128,7 @@ function addModule(depTree, context, modu, options, reason, finalCallback) {
depTree.modules[filename].reasons.push(reason);
callback(null, depTree.modules[filename].id);
} else {
profile && (profile.resolveEnd = new Date());
// create a new module
var modu = depTree.modules[filename] = {
id: depTree.nextModuleId++,
@ -120,17 +137,23 @@ function addModule(depTree, context, modu, options, reason, finalCallback) {
};
depTree.modulesById[modu.id] = modu;
profile && (profile.module = modu);
// split the loaders from the require
var filenameWithLoaders = filename;
var loaders = filename.split(/!/g);
filename = loaders.pop();
options.events.emit("module", modu, filename);
if(options.cache) {
options.cache.get(filenameWithLoaders, function(err, cachedData) {
if(err) return readFile();
if(profile) {
profile.buildModuleEnd = profile.loadersCheckEnd = profile.resolvePrePostLoadersEnd = new Date()
}
modu.fromCache = true;
cachedData = JSON.parse(cachedData);
modu.dependencies = cachedData.dependencies;
modu.loaders = cachedData.loaders;
processParsedJs(cachedData.source, cachedData.deps);
});
} else
@ -138,38 +161,61 @@ function addModule(depTree, context, modu, options, reason, finalCallback) {
// Read the file and process it with loaders
// [this step is cached]
var cacheEntry;
function readFile() {
cacheEntry = options.cache && options.cache.create(filenameWithLoaders);
// read file content
if(cacheEntry) cacheEntry.add(filename);
fs.readFile(filename, function(err, content) {
if(err) {
callback(err);
return;
}
var preLoaders = options.preLoaders ? matchLoadersList(options.preLoaders) : "";
var postLoaders = options.postLoaders ? matchLoadersList(options.postLoaders) : "";
var preLoaders = options.preLoaders ? matchLoadersList(options.preLoaders) : "";
var postLoaders = options.postLoaders ? matchLoadersList(options.postLoaders) : "";
resolve.loaders(context, preLoaders, options.resolve, function(err, preLoaders) {
resolve.loaders(context, preLoaders, options.resolve, function(err, preLoaders) {
if(err) return callback(err);
resolve.loaders(context, postLoaders, options.resolve, function(err, postLoaders) {
if(err) return callback(err);
resolve.loaders(context, postLoaders, options.resolve, function(err, postLoaders) {
profile && (profile.resolvePrePostLoadersEnd = new Date());
var allLoaders = [];
allLoaders.push.apply(allLoaders, preLoaders);
allLoaders.push.apply(allLoaders, loaders);
allLoaders.push.apply(allLoaders, postLoaders);
var seperate = !!options.workers;
try {
for(var i = 0; i < allLoaders.length && seperate; i++) {
var loaderFilename = allLoaders[i];
options.events && options.events.emit("loader", loaderFilename);
if(!require(loaderFilename).seperable)
seperate = false;
}
} catch(e) {
return callback(e);
}
var buildModuleStart = new Date();
profile && (profile.loadersCheckEnd = buildModuleStart);
(seperate ? seperateBuildModule : buildModule)({
context: context,
filenameWithLoaders: filenameWithLoaders,
preLoaders: preLoaders,
loaders: loaders,
postLoaders: postLoaders,
filename: filename,
options: options
}, function(err, source, deps, dependencyInfo, profileBuild) {
if(err) return callback(err);
execLoaders(context, filenameWithLoaders, preLoaders, [filename], [content], cacheEntry, options,
function(err, result, preLoadersCacheable) {
if(err) return callback(err);
execLoaders(context, filenameWithLoaders, loaders, [filename], result, cacheEntry, options,
function(err, result, loadersCacheable) {
if(err) return callback(err);
execLoaders(context, filenameWithLoaders, postLoaders, [filename], result, cacheEntry, options,
function(err, result, postLoadersCacheable) {
if(err) return callback(err);
return processJs(result, loadersCacheable && preLoadersCacheable && postLoadersCacheable)
});
});
});
if(profile) {
profile.buildModule = profileBuild;
profile.buildModuleEnd = new Date();
}
modu.seperate = seperate;
modu.dependencies = dependencyInfo.files;
modu.loaders = allLoaders;
if(dependencyInfo.cacheable && options.cache) {
modu.toCache = true;
options.cache.store(filenameWithLoaders, dependencyInfo.files, buildModuleStart, JSON.stringify({
deps: deps,
source: source,
dependencies: dependencyInfo.files,
loaders: allLoaders
}));
}
return processParsedJs(source, deps);
});
});
});
@ -183,30 +229,29 @@ function addModule(depTree, context, modu, options, reason, finalCallback) {
}).join("!");
}
// process the result delivered from loaders or direct from file
// for inclusion into the result
// (static code analysis for requires and other stuff)
// [this step is cached]
function processJs(resultBuffers, cacheable) {
var source = resultBuffers[0].toString("utf-8")
var deps;
try {
deps = parse(source, options.parse);
} catch(e) {
callback("File \"" + filenameWithLoaders + "\" parsing failed: " + e);
return;
}
if(cacheable && cacheEntry) {
modu.toCache = true;
cacheEntry.save(JSON.stringify({
deps: deps,
source: source
}));
}
return processParsedJs(source, deps);
function seperateBuildModule(parameters, callback) {
var opt = {};
Object.keys(parameters.options).forEach(function(name) {
if(name == "internal") return;
if(name == "events") return;
if(name == "resolve") return;
opt[name] = parameters.options[name];
});
parameters.options = opt;
options.workers.run(parameters, function(err, source, deps, cacheInfo, profileBuild) {
if(err) err = {
message: err.message,
stack: err.stack,
_toString: err._toString,
toString: function() {
return this._toString
}
}
callback(err, source, deps, cacheInfo, profileBuild);
});
}
// process the final parsed javascript code
function processParsedJs(source, deps) {
modu.requires = deps.requires || [];
@ -306,6 +351,7 @@ function addModule(depTree, context, modu, options, reason, finalCallback) {
if(errors.length) {
callback(errors.join("\n"));
} else {
options.events.emit("module", modu, filename);
callback(null, modu.id);
}
}
@ -349,7 +395,7 @@ function addContextModule(depTree, context, contextModuleName, options, reason,
var contextModuleNameWithLoaders = dirname;
var loaders = dirname.split(/!/g);
dirname = loaders.pop();
options.events.emit("context", contextModule, dirname);
options.events.emit("context-enum", contextModule, dirname);
var prependLoaders = loaders.length === 0 ? "" : loaders.join("!") + "!";
var extensions = (options.resolve && options.resolve.extensions) || [".web.js", ".js"];
@ -434,6 +480,7 @@ function addContextModule(depTree, context, contextModuleName, options, reason,
callback(err);
return;
}
options.events.emit("context", contextModule, dirname);
callback(null, contextModule.id);
});
}

72
lib/buildModule.js Normal file
View File

@ -0,0 +1,72 @@
var fs = require("fs");
var parse = require("./parse");
var execLoaders = require("enhanced-require/lib/execLoaders");
function buildModule(parameters, callback) {
var context = parameters.context;
var filenameWithLoaders = parameters.filenameWithLoaders;
var preLoaders = parameters.preLoaders;
var loaders = parameters.loaders;
var postLoaders = parameters.postLoaders;
var filename = parameters.filename;
var options = parameters.options;
var dependencyInfo = {
cacheable: true,
files: [filename]
}
var cacheEntry = parameters.cacheEntry || {
add: function(file) {
dependencyInfo.files.push(file);
},
clear: function() {
dependencyInfo.files = [];
}
};
fs.readFile(filename, function(err, content) {
if(err) return callback(err);
var loaderContext = {
loaders: loaders,
preLoaders: preLoaders,
postLoaders: postLoaders,
loaderType: null
};
loaderContext.loaderType = "preLoader";
execLoaders(context, filenameWithLoaders, preLoaders, [filename], [content], loaderContext, dependencyInfo, options,
function(err, result) {
if(err) return callback(err);
loaderContext.loaderType = "loader";
execLoaders(context, filenameWithLoaders, loaders, [filename], result, loaderContext, dependencyInfo, options,
function(err, result) {
if(err) return callback(err);
loaderContext.loaderType = "postLoader";
execLoaders(context, filenameWithLoaders, postLoaders, [filename], result, loaderContext, dependencyInfo, options,
function(err, result) {
if(err) return callback(err);
return processJs(result)
});
});
});
});
// process the result delivered from loaders or direct from file
// for inclusion into the result
// (static code analysis for requires and other stuff)
// [this step is cached]
function processJs(resultBuffers) {
var source = resultBuffers[0].toString("utf-8");
var deps;
try {
deps = parse(source, options.parse);
} catch(e) {
callback("File \"" + filenameWithLoaders + "\" parsing failed: " + e);
return;
}
return callback(null, source, deps, dependencyInfo);
}
}
module.exports = buildModule;

15
lib/buildModuleFork.js Normal file
View File

@ -0,0 +1,15 @@
var buildModule = require("./buildModule");
process.on("message", function(arr) {
var id = arr[0]
var parameters = arr[1];
try {
buildModule(parameters, function(err, source, deps, cacheInfo, profile) {
if(err) err = { message: err.message, stack: err.stack, _toString: err.toString() };
process.send([id, err, source, deps, cacheInfo, profile]);
});
} catch(e) {
process.send([id, e]);
}
process.send(null);
});

View File

@ -17,30 +17,69 @@ module.exports = function(stats, options) {
buf.push("Chunks: "+c("\033[1m") + stats.chunkCount + c("\033[22m"));
buf.push("Modules: "+c("\033[1m") + stats.modulesCount + c("\033[22m"));
buf.push("Modules including duplicates: "+c("\033[1m") + stats.modulesIncludingDuplicates + c("\033[22m"));
buf.push("Modules per chunk: "+c("\033[1m") + stats.modulesPerChunk + c("\033[22m"));
buf.push("Modules first chunk: "+c("\033[1m") + stats.modulesFirstChunk + c("\033[22m"));
if(stats.fileSizes)
if(stats.fileSizes) {
var maxLenFilename = 0;
var maxLenChunkname = 0;
Object.keys(stats.fileSizes).forEach(function(file) {
buf.push(c("\033[1m") + sprintf("%" + (3 + Object.keys(stats.fileSizes)[0].length) + "s", file) + c("\033[22m")+": "+c("\033[1m") + sprintf("%8d", stats.fileSizes[file]) + c("\033[22m") + " characters");
if(file.length > maxLenFilename) maxLenFilename = file.length;
});
var fileChunkNames = {};
Object.keys(stats.chunkNameFiles).forEach(function(name) {
if(name.length > maxLenChunkname) maxLenChunkname = name.length;
fileChunkNames[stats.chunkNameFiles[name]] = name;
});
Object.keys(stats.fileSizes).forEach(function(file) {
var name = fileChunkNames[file] || "";
var fileLine = sprintf("%" + maxLenChunkname + "s", name) + c("\033[1m") + sprintf("%" + (3 + maxLenFilename) + "s", file) + c("\033[22m")+": "+c("\033[1m") + sprintf("%8d", stats.fileSizes[file]) + c("\033[22m") + " chars/bytes ";
buf.push(fileLine);
});
}
var compressFilename = options.context ? createFilenameShortener(options.context) : function(f) { return f };
if(stats.fileModules) {
buf.push("");
buf.push(" <id> <size> <filename>");
if(options.verbose)
buf.push(" <reason> from <filename>");
var maxTime = 0;
Object.keys(stats.fileModules).forEach(function(file) {
stats.fileModules[file].forEach(function(module) {
if(!module.profile) return;
var time = module.profile.time - module.profile.timeChildren;
if(maxTime < time) maxTime = time;
});
});
var middleTime = maxTime * 0.7;
maxTime *= 0.9;
Object.keys(stats.fileModules).forEach(function(file) {
buf.push(c("\033[1m\033[32m") + file + c("\033[39m\033[22m"));
var modules = stats.fileModules[file];
if(options["by-size"])
modules.sort(function(a, b) {
return b.size - a.size;
});
if(options["by-size"]) {
modules.sort(function(a, b) {
return b.size - a.size;
});
}
modules.forEach(function(module) {
buf.push(" "+c("\033[1m") + sprintf("%3s", module.id+"") + " " + (typeof module.size === "number" ? sprintf("%9s", Math.round(module.size)+"") : " ") + " " +
var moduleLine = " "+(module.fromCache?"":c("\033[1m")) + sprintf("%3s", module.id+"") + " " + (typeof module.size === "number" ? sprintf("%9s", Math.round(module.size)+"") : " ") + " " +
(compressFilename(module.filename) ||
(module.dirname && ("[context] " + compressFilename(module.dirname))) ||
"[unknown]") + c("\033[22m") + (module.fromCache ? " [from cache]" : module.toCache ? " [caching]" : ""));
"[unknown]") + (module.fromCache?"":c("\033[22m"))
if(module.fromCache) moduleLine += c("\033[1m\033[32m") + " [from cache]" + c("\033[39m\033[22m");
else if(module.toCache) moduleLine += c("\033[1m\033[33m") + " [caching]" + c("\033[39m\033[22m");
if(module.seperate) moduleLine += " [seperate]";
if(module.profile) {
var valueTime = module.profile.time - module.profile.timeChildren;
if(valueTime > maxTime) moduleLine += c("\033[1m\033[31m");
else if(valueTime > middleTime) moduleLine += c("\033[1m\033[33m");
var buildProfile = module.profile.buildModule;
if(buildProfile) buildProfile = " (" +
(buildProfile.loaders && buildProfile.loaders.length > 0 ? "loaders: " + buildProfile.loaders.join("ms, ") + "ms" : "") +
")";
else buildProfile = "";
moduleLine += " [" + module.profile.time + "ms: " + (module.profile.timeResolve + module.profile.timeResolvePrePostLoaders) + "ms resolving, " + module.profile.timeBuildModule + "ms build" + buildProfile + ", " + module.profile.timeChildren + "ms children]";
if(valueTime > middleTime) moduleLine += c("\033[39m\033[22m");
}
buf.push(moduleLine);
if(options.verbose) {
module.reasons.forEach(function(reason) {
switch(reason.type) {

View File

@ -6,157 +6,157 @@ var esprima = require("esprima");
// Syntax: https://developer.mozilla.org/en/SpiderMonkey/Parser_API
function walkStatements(context, statements) {
function walkStatements(options, context, statements) {
statements.forEach(function(statement) {
walkStatement(context, statement);
walkStatement(options, context, statement);
});
}
function walkStatement(context, statement) {
function walkStatement(options, context, statement) {
switch(statement.type) {
// Real Statements
case "BlockStatement":
walkStatements(context, statement.body);
walkStatements(options, context, statement.body);
break;
case "ExpressionStatement":
walkExpression(context, statement.expression);
walkExpression(options, context, statement.expression);
break;
case "IfStatement":
walkExpression(context, statement.test);
walkStatement(context, statement.consequent);
walkExpression(options, context, statement.test);
walkStatement(options, context, statement.consequent);
if(statement.alternate)
walkStatement(context, statement.alternate);
walkStatement(options, context, statement.alternate);
break;
case "LabeledStatement":
walkStatement(context, statement.body);
walkStatement(options, context, statement.body);
break;
case "WithStatement":
walkExpression(context, statement.object);
walkStatement(context, statement.body);
walkExpression(options, context, statement.object);
walkStatement(options, context, statement.body);
break;
case "SwitchStatement":
walkExpression(context, statement.discriminant);
walkSwitchCases(context, statement.cases);
walkExpression(options, context, statement.discriminant);
walkSwitchCases(options, context, statement.cases);
break;
case "ReturnStatement":
case "ThrowStatement":
if(statement.argument)
walkExpression(context, statement.argument);
walkExpression(options, context, statement.argument);
break;
case "TryStatement":
walkStatement(context, statement.block);
walkCatchClauses(context, statement.handlers);
walkStatement(options, context, statement.block);
walkCatchClauses(options, context, statement.handlers);
if(statement.finalizer)
walkStatement(context, statement.finalizer);
walkStatement(options, context, statement.finalizer);
break;
case "WhileStatement":
case "DoWhileStatement":
walkExpression(context, statement.test);
walkStatement(context, statement.body);
walkExpression(options, context, statement.test);
walkStatement(options, context, statement.body);
break;
case "ForStatement":
if(statement.init) {
if(statement.init.type === "VariableDeclaration")
walkStatement(context, statement.init);
walkStatement(options, context, statement.init);
else
walkExpression(context, statement.init);
walkExpression(options, context, statement.init);
}
if(statement.test)
walkExpression(context, statement.test);
walkExpression(options, context, statement.test);
if(statement.update)
walkExpression(context, statement.update);
walkStatement(context, statement.body);
walkExpression(options, context, statement.update);
walkStatement(options, context, statement.body);
break;
case "ForInStatement":
if(statement.left.type === "VariableDeclaration")
walkStatement(context, statement.left);
walkStatement(options, context, statement.left);
else
walkExpression(context, statement.left);
walkExpression(context, statement.right);
walkStatement(context, statement.body);
walkExpression(options, context, statement.left);
walkExpression(options, context, statement.right);
walkStatement(options, context, statement.body);
break;
// Declarations
case "FunctionDeclaration":
if(context.options.overwrites.hasOwnProperty(statement.name)) {
if(options.overwrites.hasOwnProperty(statement.name)) {
context.overwrite.push(statement.name);
}
var old = addOverwrites(context, statement.params);
var old = addOverwrites(options, context, statement.params);
if(statement.body.type === "BlockStatement")
walkStatement(context, statement.body);
walkStatement(options, context, statement.body);
else
walkExpression(context, statement.body);
walkExpression(options, context, statement.body);
context.overwrite.length = old;
break;
case "VariableDeclaration":
if(statement.declarations)
walkVariableDeclarators(context, statement.declarations);
walkVariableDeclarators(options, context, statement.declarations);
break;
}
}
function walkSwitchCases(context, switchCases) {
function walkSwitchCases(options, context, switchCases) {
switchCases.forEach(function(switchCase) {
if(switchCase.test)
walkExpression(context, switchCase.test);
walkStatements(context, switchCase.consequent);
walkExpression(options, context, switchCase.test);
walkStatements(options, context, switchCase.consequent);
});
}
function walkCatchClauses(context, catchClauses) {
function walkCatchClauses(options, context, catchClauses) {
catchClauses.forEach(function(catchClause) {
if(catchClause.guard)
walkExpression(context, catchClause.guard);
walkStatement(context, catchClause.body);
walkExpression(options, context, catchClause.guard);
walkStatement(options, context, catchClause.body);
});
}
function walkVariableDeclarators(context, declarators) {
function walkVariableDeclarators(options, context, declarators) {
declarators.forEach(function(declarator) {
switch(declarator.type) {
case "VariableDeclarator":
if(declarator.id.type === "Identifier" &&
context.options.overwrites.hasOwnProperty(declarator.id.name)) {
options.overwrites.hasOwnProperty(declarator.id.name)) {
context.overwrite.push(declarator.id.name);
}
if(declarator.init)
walkExpression(context, declarator.init);
walkExpression(options, context, declarator.init);
break;
}
});
}
function walkExpressions(context, expressions) {
function walkExpressions(options, context, expressions) {
expressions.forEach(function(expression) {
walkExpression(context, expression);
walkExpression(options, context, expression);
});
}
function walkExpression(context, expression) {
function walkExpression(options, context, expression) {
switch(expression.type) {
case "ArrayExpression":
if(expression.elements)
walkExpressions(context, expression.elements);
walkExpressions(options, context, expression.elements);
break;
case "ObjectExpression":
expression.properties.forEach(function(prop) {
walkExpression(context, prop.value);
walkExpression(options, context, prop.value);
});
break;
case "FunctionExpression":
var old = addOverwrites(context, expression.params);
var old = addOverwrites(options, context, expression.params);
if(expression.body.type === "BlockStatement")
walkStatement(context, expression.body);
walkStatement(options, context, expression.body);
else
walkExpression(context, expression.body);
walkExpression(options, context, expression.body);
context.overwrite.length = old;
break;
case "SequenceExpression":
if(expression.expressions)
walkExpressions(context, expression.expressions);
walkExpressions(options, context, expression.expressions);
break;
case "UpdateExpression":
walkExpression(context, expression.argument);
walkExpression(options, context, expression.argument);
break;
case "UnaryExpression":
if(expression.operator === "typeof" &&
@ -164,28 +164,28 @@ function walkExpression(context, expression) {
expression.argument.type === "Identifier" &&
expression.argument.name === "require")
break;
walkExpression(context, expression.argument);
walkExpression(options, context, expression.argument);
break;
case "BinaryExpression":
case "LogicalExpression":
walkExpression(context, expression.left);
walkExpression(context, expression.right);
walkExpression(options, context, expression.left);
walkExpression(options, context, expression.right);
break;
case "AssignmentExpression":
if(expression.left.type !== "Identifier" ||
expression.left.name !== "require")
walkExpression(context, expression.left);
walkExpression(context, expression.right);
walkExpression(options, context, expression.left);
walkExpression(options, context, expression.right);
break;
case "ConditionalExpression":
walkExpression(context, expression.test);
walkExpression(context, expression.alternate);
walkExpression(context, expression.consequent);
walkExpression(options, context, expression.test);
walkExpression(options, context, expression.alternate);
walkExpression(options, context, expression.consequent);
break;
case "NewExpression":
walkExpression(context, expression.callee);
walkExpression(options, context, expression.callee);
if(expression.arguments)
walkExpressions(context, expression.arguments);
walkExpressions(options, context, expression.arguments);
break;
case "CallExpression":
var noCallee = false;
@ -287,7 +287,7 @@ function walkExpression(context, expression) {
column: expression.loc.start.column,
ignoreOverride: true,
overwrite: context.overwrite.slice(),
options: context.options
options: options
};
if(expression.arguments.length >= 2 &&
expression.arguments[1].type === "FunctionExpression" &&
@ -448,7 +448,7 @@ function walkExpression(context, expression) {
column: expression.loc.start.column,
ignoreOverride: true,
overwrite: context.overwrite.slice(),
options: context.options
options: options
};
param.forEach(function(r) {
newContext.requires.push({name: r});
@ -531,9 +531,9 @@ function walkExpression(context, expression) {
}
if(expression.callee && !noCallee)
walkExpression(context, expression.callee);
walkExpression(options, context, expression.callee);
if(expression.arguments)
walkExpressions(context, expression.arguments);
walkExpressions(options, context, expression.arguments);
break;
case "MemberExpression":
if(expression.object.type === "Identifier" &&
@ -545,9 +545,9 @@ function walkExpression(context, expression) {
expression.object.name === "require" &&
expression.property.type === "Identifier")
break;
walkExpression(context, expression.object);
walkExpression(options, context, expression.object);
if(expression.property.type !== "Identifier")
walkExpression(context, expression.property);
walkExpression(options, context, expression.property);
break;
case "Identifier":
if(context.overwrite.indexOf("require") === -1 &&
@ -563,9 +563,9 @@ function walkExpression(context, expression) {
};
context.contexts.push(newContext);
} else if(context.overwrite.indexOf(expression.name) === -1 &&
context.options.overwrites.hasOwnProperty(expression.name)) {
options.overwrites.hasOwnProperty(expression.name)) {
context.requires = context.requires || [];
var overwrite = context.options.overwrites[expression.name];
var overwrite = options.overwrites[expression.name];
var append = undefined;
if(overwrite.indexOf("+") !== -1) {
append = overwrite.substr(overwrite.indexOf("+")+1);
@ -583,7 +583,7 @@ function walkExpression(context, expression) {
}
}
function addOverwrites(context, params) {
function addOverwrites(options, context, params) {
var l = context.overwrite.length;
if(!params) return l;
params.forEach(function(param) {
@ -592,7 +592,7 @@ function addOverwrites(context, params) {
return;
}
if(param.type === "Identifier" &&
context.options.overwrites.hasOwnProperty(param.name))
options.overwrites.hasOwnProperty(param.name))
context.overwrite.push(param.name);
});
return l;
@ -681,10 +681,9 @@ module.exports = function parse(source, options) {
options.overwrites = options.overwrites || {};
options.overwrites.require = true;
var context = {
options: options,
overwrite: []
};
walkStatements(context, ast.body);
delete context.options;
walkStatements(options, context, ast.body);
JSON.stringify(context);
return context;
}

View File

@ -19,42 +19,9 @@ var HASH_REGEXP = /\[hash\]/i;
source if options.output is not set
else stats json
options:
- outputJsonpFunction
JSONP function used to load chunks
- publicPrefix
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
- watch
recompile on file change
- watchDelay
delay before recompile after the last file change
- minimize
minimize outputs with uglify-js
- includeFilenames
add absolute filenames of input files as comments
- resolve.alias (object)
replace a module. ex {"old-module": "new-module"}
- resolve.extensions (object)
possible extensions for files
- resolve.paths (array)
search paths
- 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
- parse.overwrites (object)
free module varables which are replaced with a module
ex. { "$": "jquery" }
options: see README.md
*/
module.exports = function(context, moduleName, options, callback) {
module.exports = function webpackMain(context, moduleName, options, callback) {
// Support multiple call signitures
if(typeof moduleName === "object") {
callback = options;
@ -67,6 +34,10 @@ module.exports = function(context, moduleName, options, callback) {
}
// Defaults
// Create a options.events as EventEmitter
if(!options.events) options.events = new (require("events").EventEmitter)();
if(!options.outputJsonpFunction)
options.outputJsonpFunction = "webpackJsonp" + (options.libary || "");
options.publicPrefix = options.publicPrefix || "";
@ -77,6 +48,26 @@ module.exports = function(context, moduleName, options, callback) {
options.internal.fileWrites[toFront?"unshift":"push"]([path.join(options.outputDirectory, filename), content]);
}
if(options.workers === true) {
options.workers = require("os").cpus().length;
}
if(typeof options.workers == "number") {
options.workers = new (require("./Workers"))(path.join(__dirname, "buildModuleFork.js"), options.workers);
}
if(options.workers && options.closeWorkers !== false) {
if(options.watch) {
options.events.on("watch-end", function() {
if(options.closeWorkers === false) return;
options.workers.close();
});
} else {
options.events.on("bundle", function() {
if(options.closeWorkers === false) return;
options.workers.close();
});
}
}
if(options.output) {
if(!options.outputDirectory) {
options.outputDirectory = path.dirname(options.output);
@ -140,9 +131,6 @@ module.exports = function(context, moduleName, options, callback) {
options.internal.subStats.push(stats);
}
// Create a options.events as EventEmitter
if(!options.events) options.events = new (require("events").EventEmitter)();
// Register listeners to support watch mode
if(options.watch) {
var fs = require("fs");
@ -166,10 +154,12 @@ module.exports = function(context, moduleName, options, callback) {
});
watchers.length = 0;
if(staticChanges.length > 0)
return callback(new Error(
if(staticChanges.length > 0) {
callback(new Error(
"Files (" + staticChanges.join(", ") +
") changed. Webpack cannot recompile in this watch step."));
return options.events.emit("watch-end");
}
runAgain = false;
isRunning = true;
@ -186,61 +176,56 @@ module.exports = function(context, moduleName, options, callback) {
runAgain = true;
else
startAgain()
}
// on before a module is read
options.events.on("module", function(module, filename) {
if(!filename) return;
watchers.push(fs.watch(filename, function() {
change();
}));
});
// on before a loader dependency is read
options.events.on("dependency", function(filename) {
if(!filename) return;
watchers.push(fs.watch(filename, function() {
change();
}));
});
function staticChange(filename) {
if(staticChanges.indexOf(filename) == -1)
staticChanges.push(filename);
change();
}
// on before a context is enumerated
options.events.on("context", function(module, dirname) {
options.events.on("context-enum", function(module, dirname) {
if(!dirname) return;
watchers.push(fs.watch(dirname, function() {
change();
}));
});
// on before a loader is read
options.events.on("loader", function(filename) {
if(!filename) return;
watchers.push(fs.watch(filename, function() {
change();
}));
});
// on before a static dependency is read
options.events.on("static-dependency", function(filename) {
if(!filename) return;
watchers.push(fs.watch(filename, function() {
if(staticChanges.indexOf(filename) == -1)
staticChanges.push(filename);
change();
}));
});
// on user defines the bundle as invalid
options.events.on("invalid", function() {
change();
});
// on bundle finished compiling
var bundleToken = null;
options.events.on("bundle", function(stats) {
isRunning = false;
bundleToken = "" + Math.random();
if(runAgain)
startAgain();
else {
// Bind watchers
var token = bundleToken;
stats.dependencies.forEach(function(dep) {
watchers.push(fs.watch(dep, change));
fs.stat(dep, function(err, stat) {
if(bundleToken != token) return;
if(err) return change();
if(stat.mtime.getTime() > stats.startTime)
change();
});
});
stats.loaders.forEach(function(dep) {
watchers.push(fs.watch(dep, staticChange.bind(null, dep)));
fs.stat(dep, function(err, stat) {
if(bundleToken != token) return;
if(err) return staticChange(dep);
if(stat.mtime.getTime() > stats.startTime)
staticChange(dep);
});
});
}
});
if(!options.cache)
@ -274,9 +259,6 @@ function webpack(context, moduleName, options, callback) {
}
var buffer = [];
// collect which module is in which file
var fileModulesMap = {};
// collect which chunks exists
var chunksCount = 0;
@ -449,6 +431,13 @@ function webpack(context, moduleName, options, callback) {
buffer.chunkCount = chunksCount;
buffer.modulesCount = Object.keys(depTree.modules).length;
var sum = 0;
// collect which module is in which file
var fileModulesMap = {};
// collect named chunks filenames
var chunkNameMap = {};
chunkIds.reverse().forEach(function(chunkId) {
var chunk = depTree.chunks[chunkId]
if(!chunk.filename) return;
@ -463,28 +452,49 @@ function webpack(context, moduleName, options, callback) {
dirname: modu.dirname,
fromCache: modu.fromCache,
toCache: modu.toCache,
seperate: modu.seperate,
profile: modu.profile,
reasons: modu.reasons});
sum++;
}
}
modulesArray.sort(function(a, b) {
return a.id - b.id;
});
fileModulesMap[path.basename(chunk.filename)] = modulesArray;
chunkNameMap[chunkId] = path.basename(chunk.filename);
});
buffer.modulesIncludingDuplicates = sum;
buffer.modulesPerChunk = Math.round(sum / chunksCount*10)/10; // DEPRECATED: useless info
sum = 0;
for(var moduleId in depTree.chunks.main.modules) {
if(depTree.chunks.main.modules[moduleId] === "include")
sum++;
}
var dependencies = {};
var loaders = {};
var contexts = [];
Object.keys(depTree.modules).forEach(function(moduleId) {
var module = depTree.modules[moduleId];
if(module.dependencies) module.dependencies.forEach(function(dep) {
dependencies[dep] = true;
});
if(module.loaders) module.loaders.forEach(function(loader) {
loaders[loader] = true;
});
if(module.dirname) contexts.push(module.dirname);
});
buffer.dependencies = Object.keys(dependencies);
buffer.loaders = Object.keys(loaders);
buffer.contexts = contexts;
buffer.modulesFirstChunk = sum;
buffer.fileSizes = fileSizeMap;
buffer.warnings = depTree.warnings;
buffer.errors = depTree.errors;
buffer.fileModules = fileModulesMap;
buffer.chunkNameFiles = chunkNameMap;
buffer.subStats = subStats;
buffer.startTime = startTime.getTime();
buffer.time = new Date() - startTime;
options.events.emit("task-end", "statistics");
options.events.emit("bundle", buffer);

View File

@ -1,6 +1,6 @@
{
"name": "webpack",
"version": "0.6.2",
"version": "0.7.0-beta",
"author": "Tobias Koppers @sokra",
"description": "Packs CommonJs/AMD 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": {
@ -10,15 +10,15 @@
"sprintf": "0.1.x",
"enhanced-require": "0.2.x",
"enhanced-resolve": "0.2.x",
"raw-loader": "0.1.x",
"json-loader": "0.1.x",
"jade-loader": "0.1.x",
"coffee-loader": "0.1.x",
"raw-loader": "0.2.x",
"json-loader": "0.2.x",
"jade-loader": "0.2.x",
"coffee-loader": "0.2.x",
"css-loader": "0.2.x",
"less-loader": "0.2.x",
"style-loader": "0.1.x",
"script-loader": "0.1.x",
"bundle-loader": "0.1.x",
"script-loader": "0.2.x",
"bundle-loader": "0.2.x",
"file-loader": "0.1.x",
"val-loader": "0.1.x"
},

View File

@ -33,7 +33,7 @@ var libary1 = cp.spawn("node", join(["../../bin/webpack.js", "--colors", "--sing
bindOutput(libary1);
libary1.on("exit", function(code) {
if(code === 0) {
var main = cp.spawn("node", join(["../../bin/webpack.js", "--colors", "--alias", "vm=vm-browserify",
var main = cp.spawn("node", join(["../../bin/webpack.js", "--colors", "--alias", "vm=vm-browserify", "--workers",
"--public-prefix", "js/", "lib/index", "js/web.js"], extraArgs));
bindOutput(main);
}