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:
parent
ab357031ba
commit
2eeb4923e1
77
README.md
77
README.md
|
@ -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
|
||||
|
||||
|
|
|
@ -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
33
bm.js
|
@ -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 */
|
||||
|
||||
|
|
51
lib/Cache.js
51
lib/Cache.js
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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 {};
|
||||
}
|
149
lib/buildDeps.js
149
lib/buildDeps.js
|
@ -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);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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;
|
|
@ -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);
|
||||
});
|
|
@ -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) {
|
||||
|
|
149
lib/parse.js
149
lib/parse.js
|
@ -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;
|
||||
}
|
168
lib/webpack.js
168
lib/webpack.js
|
@ -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);
|
||||
|
|
14
package.json
14
package.json
|
@ -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"
|
||||
},
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue