Skip to content

Commit 0ddf304

Browse files
committed
added caching for watch mode
1 parent c30095a commit 0ddf304

5 files changed

Lines changed: 329 additions & 221 deletions

File tree

lib/Cache.js

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/*
2+
MIT License http://www.opensource.org/licenses/mit-license.php
3+
Author Tobias Koppers @sokra
4+
*/
5+
var fs = require("fs");
6+
7+
function Cache(options) {
8+
this.options = options = options || {};
9+
this.map = {};
10+
}
11+
var $ = Cache.prototype;
12+
module.exports = Cache;
13+
14+
$.get = function(request, callback) {
15+
var self = this;
16+
var item = self.map[request];
17+
if(!item) return callback(new Error("No Entry"));
18+
var count = item.files.length;
19+
var invalid = false;
20+
item.files.forEach(function(file) {
21+
fs.stat(file.path, function(err, stat) {
22+
if(err) return invalidate(err.toString());
23+
if(!stat) return invalidate("Cannot read stat");
24+
if(stat.mtime.getTime() !== file.time)
25+
return invalidate("File " + file.path + " has changed");
26+
valid();
27+
});
28+
});
29+
if(item.files.length === 0) valid();
30+
function invalidate(msg) {
31+
if(invalid) return;
32+
invalid = true;
33+
callback(new Error(msg));
34+
}
35+
function valid() {
36+
if(invalid) return;
37+
count--;
38+
if(count <= 0)
39+
callback(null, item.value);
40+
}
41+
}
42+
43+
$.create = function(request) {
44+
var self = this;
45+
var item = {
46+
files: []
47+
};
48+
return {
49+
add: function(path) {
50+
item.files.push({
51+
path: path,
52+
// TODO do it asynchronly
53+
time: fs.statSync(path).mtime.getTime()
54+
});
55+
},
56+
clear: function() {
57+
item.files.length = 0;
58+
},
59+
save: function(value) {
60+
item.value = value;
61+
self.map[request] = item;
62+
}
63+
}
64+
}

lib/buildDeps.js

Lines changed: 126 additions & 112 deletions
Original file line numberDiff line numberDiff line change
@@ -126,132 +126,146 @@ function addModule(depTree, context, modu, options, reason, finalCallback) {
126126
filename = loaders.pop();
127127
options.events.emit("module", modu, filename);
128128

129-
// read file content
130-
fs.readFile(filename, function(err, content) {
131-
if(err) {
132-
callback(err);
133-
return;
134-
}
129+
if(options.cache) {
130+
options.cache.get(filenameWithLoaders, function(err, source) {
131+
if(err) return readFile();
132+
processJs(null, source);
133+
});
134+
} else
135+
readFile();
135136

136-
// exec the loaders
137-
execLoaders(context, filenameWithLoaders, loaders, [filename], [content], options, processJs);
137+
// Read the file and process it with loaders
138+
function readFile() {
139+
var cacheEntry = options.cache && options.cache.create(filenameWithLoaders);
138140

139-
// process the finished javascript code
140-
// for inclusion into the result
141-
// (static code analysis for requires and other stuff)
142-
function processJs(err, source) {
141+
// read file content
142+
if(cacheEntry) cacheEntry.add(filename);
143+
fs.readFile(filename, function(err, content) {
143144
if(err) {
144145
callback(err);
145146
return;
146147
}
147-
var deps;
148-
try {
149-
deps = parse(source, options.parse);
150-
} catch(e) {
151-
callback("File \"" + filenameWithLoaders + "\" parsing failed: " + e);
152-
return;
153-
}
154-
modu.requires = deps.requires || [];
155-
modu.asyncs = deps.asyncs || [];
156-
modu.contexts = deps.contexts || [];
157-
modu.source = source;
158-
159-
var requires = {}, directRequire = {};
160-
var contexts = [], directContexts = {};
161-
function add(r) {
162-
requires[r.name] = requires[r.name] || [];
163-
requires[r.name].push(r);
164-
}
165-
function addContext(m) {
166-
return function(c) {
167-
contexts.push({context: c, module: m});
168-
}
169-
}
170-
if(modu.requires) {
171-
modu.requires.forEach(add);
172-
modu.requires.forEach(function(r) {
173-
directRequire[r.name] = true;
174-
});
175-
}
176-
if(modu.contexts) {
177-
modu.contexts.forEach(addContext(modu));
178-
modu.contexts.forEach(function(c) {
179-
directContexts[c.name] = true;
180-
});
148+
149+
// exec the loaders
150+
execLoaders(context, filenameWithLoaders, loaders, [filename], [content], cacheEntry, options, processJs);
151+
});
152+
}
153+
154+
// process the finished javascript code
155+
// for inclusion into the result
156+
// (static code analysis for requires and other stuff)
157+
function processJs(err, source) {
158+
if(err) {
159+
callback(err);
160+
return;
161+
}
162+
var deps;
163+
try {
164+
deps = parse(source, options.parse);
165+
} catch(e) {
166+
callback("File \"" + filenameWithLoaders + "\" parsing failed: " + e);
167+
return;
168+
}
169+
modu.requires = deps.requires || [];
170+
modu.asyncs = deps.asyncs || [];
171+
modu.contexts = deps.contexts || [];
172+
modu.source = source;
173+
174+
var requires = {}, directRequire = {};
175+
var contexts = [], directContexts = {};
176+
function add(r) {
177+
requires[r.name] = requires[r.name] || [];
178+
requires[r.name].push(r);
179+
}
180+
function addContext(m) {
181+
return function(c) {
182+
contexts.push({context: c, module: m});
181183
}
182-
if(modu.asyncs)
183-
modu.asyncs.forEach(function addAsync(c) {
184-
if(c.requires)
185-
c.requires.forEach(add);
186-
if(c.asyncs)
187-
c.asyncs.forEach(addAsync);
188-
if(c.contexts)
189-
c.contexts.forEach(addContext(c));
190-
});
191-
requiresNames = Object.keys(requires);
192-
var count = requiresNames.length + contexts.length + 1;
193-
var errors = [];
194-
if(requiresNames.length)
195-
requiresNames.forEach(function(moduleName) {
196-
var reason = {
197-
type: directRequire[moduleName] ? "require" : "async require",
198-
count: requires[moduleName].length,
199-
filename: filename
200-
};
201-
202-
// create or get the module for each require
203-
addModule(depTree, path.dirname(filename), moduleName, options, reason, function(err, moduleId) {
204-
if(err) {
205-
depTree.errors.push("Cannot find module '" + moduleName + "'\n " + err +
206-
"\n @ " + filename + " (line " + requires[moduleName][0].line + ", column " + requires[moduleName][0].column + ")");
207-
} else {
208-
requires[moduleName].forEach(function(requireItem) {
209-
requireItem.id = moduleId;
210-
});
211-
}
212-
endOne();
213-
});
214-
});
215-
if(contexts) {
216-
contexts.forEach(function(contextObj) {
217-
var context = contextObj.context;
218-
var module = contextObj.module;
219-
var reason = {
220-
type: directContexts[context.name] ? "context" : "async context",
221-
filename: filename
222-
};
223-
224-
// create of get the context module for each require.context
225-
addContextModule(depTree, path.dirname(filename), context.name, options, reason, function(err, contextModuleId) {
226-
if(err) {
227-
depTree.errors.push("Cannot find context '"+context.name+"'\n " + err +
228-
"\n @ " + filename + " (line " + context.line + ", column " + context.column + ")");
229-
} else {
230-
context.id = contextModuleId;
231-
module.requires.push({id: context.id});
232-
}
233-
endOne();
234-
});
235-
if(context.warn) {
236-
depTree.warnings.push(filename + " (line " + context.line + ", column " + context.column + "): " +
237-
"implicit use of require.context(\".\") is not recommended.");
184+
}
185+
if(modu.requires) {
186+
modu.requires.forEach(add);
187+
modu.requires.forEach(function(r) {
188+
directRequire[r.name] = true;
189+
});
190+
}
191+
if(modu.contexts) {
192+
modu.contexts.forEach(addContext(modu));
193+
modu.contexts.forEach(function(c) {
194+
directContexts[c.name] = true;
195+
});
196+
}
197+
if(modu.asyncs)
198+
modu.asyncs.forEach(function addAsync(c) {
199+
if(c.requires)
200+
c.requires.forEach(add);
201+
if(c.asyncs)
202+
c.asyncs.forEach(addAsync);
203+
if(c.contexts)
204+
c.contexts.forEach(addContext(c));
205+
});
206+
requiresNames = Object.keys(requires);
207+
var count = requiresNames.length + contexts.length + 1;
208+
var errors = [];
209+
if(requiresNames.length)
210+
requiresNames.forEach(function(moduleName) {
211+
var reason = {
212+
type: directRequire[moduleName] ? "require" : "async require",
213+
count: requires[moduleName].length,
214+
filename: filename
215+
};
216+
217+
// create or get the module for each require
218+
addModule(depTree, path.dirname(filename), moduleName, options, reason, function(err, moduleId) {
219+
if(err) {
220+
depTree.errors.push("Cannot find module '" + moduleName + "'\n " + err +
221+
"\n @ " + filename + " (line " + requires[moduleName][0].line + ", column " + requires[moduleName][0].column + ")");
222+
} else {
223+
requires[moduleName].forEach(function(requireItem) {
224+
requireItem.id = moduleId;
225+
});
238226
}
227+
endOne();
239228
});
240-
}
241-
endOne();
242-
function endOne() {
243-
count--;
244-
assert(count >= 0);
245-
if(count === 0) {
246-
if(errors.length) {
247-
callback(errors.join("\n"));
229+
});
230+
if(contexts) {
231+
contexts.forEach(function(contextObj) {
232+
var context = contextObj.context;
233+
var module = contextObj.module;
234+
var reason = {
235+
type: directContexts[context.name] ? "context" : "async context",
236+
filename: filename
237+
};
238+
239+
// create of get the context module for each require.context
240+
addContextModule(depTree, path.dirname(filename), context.name, options, reason, function(err, contextModuleId) {
241+
if(err) {
242+
depTree.errors.push("Cannot find context '"+context.name+"'\n " + err +
243+
"\n @ " + filename + " (line " + context.line + ", column " + context.column + ")");
248244
} else {
249-
callback(null, modu.id);
245+
context.id = contextModuleId;
246+
module.requires.push({id: context.id});
250247
}
248+
endOne();
249+
});
250+
if(context.warn) {
251+
depTree.warnings.push(filename + " (line " + context.line + ", column " + context.column + "): " +
252+
"implicit use of require.context(\".\") is not recommended.");
253+
}
254+
});
255+
}
256+
endOne();
257+
function endOne() {
258+
count--;
259+
assert(count >= 0);
260+
if(count === 0) {
261+
if(errors.length) {
262+
callback(errors.join("\n"));
263+
} else {
264+
callback(null, modu.id);
251265
}
252266
}
253267
}
254-
});
268+
}
255269
}
256270
}
257271
}

0 commit comments

Comments
 (0)