mirror of https://github.com/nodejs/node.git
Module system refactor
There is one major API change in the refactor: filename extensions are now required when requiring or including modules. Added extra test to test-module-loading.js.v0.7.4-release
parent
887f056923
commit
b6fe4aec50
236
src/node.js
236
src/node.js
|
@ -1,16 +1,38 @@
|
|||
// module search paths
|
||||
node.includes = ["."];
|
||||
// Timers
|
||||
|
||||
function setTimeout (callback, delay) {
|
||||
var timer = new node.Timer(callback, delay, 0);
|
||||
timer.start();
|
||||
return timer;
|
||||
}
|
||||
|
||||
function setInterval (callback, delay) {
|
||||
var timer = new node.Timer(callback, delay, delay);
|
||||
timer.start();
|
||||
return timer;
|
||||
}
|
||||
|
||||
function clearTimeout (timer) {
|
||||
timer.stop();
|
||||
delete timer;
|
||||
}
|
||||
|
||||
clearInterval = clearTimeout;
|
||||
|
||||
// This is useful for dealing with raw encodings.
|
||||
Array.prototype.encodeUtf8 = function () {
|
||||
return String.fromCharCode.apply(String, this);
|
||||
}
|
||||
};
|
||||
|
||||
node.path = new function () {
|
||||
this.join = function () {
|
||||
var joined = "";
|
||||
for (var i = 0; i < arguments.length; i++) {
|
||||
var part = arguments[i].toString();
|
||||
|
||||
if (part === ".") continue;
|
||||
while (/^\.\//.exec(part)) part = part.replace(/^\.\//, "");
|
||||
|
||||
if (i === 0) {
|
||||
part = part.replace(/\/*$/, "/");
|
||||
} else if (i === arguments.length - 1) {
|
||||
|
@ -32,149 +54,93 @@ node.path = new function () {
|
|||
};
|
||||
};
|
||||
|
||||
// Timers
|
||||
// Module
|
||||
|
||||
function setTimeout (callback, delay) {
|
||||
var timer = new node.Timer(callback, delay, 0);
|
||||
timer.start();
|
||||
return timer;
|
||||
node.Module = function (o) {
|
||||
this.parent = o.parent;
|
||||
this.target = o.target || {};
|
||||
|
||||
if (!o.path) throw "path argument required";
|
||||
if (o.path.charAt(0) == "/")
|
||||
throw "Absolute module paths are not yet supported in Node";
|
||||
|
||||
var dir = o.base_directory || ".";
|
||||
this.filename = node.path.join(dir, o.path);
|
||||
|
||||
this.loaded = false;
|
||||
this.exited = false;
|
||||
this.children = [];
|
||||
};
|
||||
|
||||
function setInterval (callback, delay) {
|
||||
var timer = new node.Timer(callback, delay, delay);
|
||||
timer.start();
|
||||
return timer;
|
||||
};
|
||||
node.Module.prototype.load = function (callback) {
|
||||
var self = this;
|
||||
if (self.loaded)
|
||||
throw "Module '" + self.filename + "' is already loaded.";
|
||||
|
||||
function clearTimeout (timer) {
|
||||
timer.stop();
|
||||
delete timer;
|
||||
};
|
||||
clearInterval = clearTimeout;
|
||||
|
||||
// Modules
|
||||
|
||||
(function () {
|
||||
function findScript(base_directory, name, callback) {
|
||||
// in the future this function will be more complicated
|
||||
if (name.charAt(0) == "/")
|
||||
throw "absolute module paths are not yet supported.";
|
||||
|
||||
var filename = node.path.join(base_directory, name) + ".js";
|
||||
|
||||
node.fs.exists(filename, function (status) {
|
||||
callback(status ? filename : null);
|
||||
});
|
||||
}
|
||||
|
||||
// Constructor for submodule.
|
||||
// "name" is like a path but without .js. e.g. "database/mysql"
|
||||
// "target" is an object into which the submodule will be loaded.
|
||||
function Sub (name, target) {
|
||||
this.name = name;
|
||||
this.target = target;
|
||||
|
||||
this.load = function (base_directory, callback) {
|
||||
//node.debug("sub.load from <" + base_directory + "> " + this.toString());
|
||||
findScript(base_directory, name, function (filename) {
|
||||
if (filename === null) {
|
||||
stderr.puts("Cannot find a script matching: " + name);
|
||||
node.exit(1);
|
||||
}
|
||||
loadScript(filename, target, callback);
|
||||
});
|
||||
};
|
||||
|
||||
this.toString = function () {
|
||||
return "[sub name=" + name + " target=" + target.toString() + "]";
|
||||
}
|
||||
}
|
||||
|
||||
function Scaffold (source, filename, module) {
|
||||
// wrap the source in a strange function
|
||||
var source = "function (__filename) {"
|
||||
+ " var onLoad;"
|
||||
+ " var exports = this;"
|
||||
+ " var require = this.__require;"
|
||||
+ " var include = this.__include;"
|
||||
+ source
|
||||
+ " this.__onLoad = onLoad;"
|
||||
+ "};"
|
||||
;
|
||||
// returns the function
|
||||
var compiled = node.compile(source, filename);
|
||||
|
||||
if (module.__onLoad) {
|
||||
//node.debug("<"+ filename+"> has onload! this is bad");
|
||||
node.fs.cat(self.filename, "utf8", function (status, content) {
|
||||
if (status != 0) {
|
||||
stderr.puts("Error reading " + self.filename);
|
||||
node.exit(1);
|
||||
}
|
||||
|
||||
module.__subs = [];
|
||||
module.__require = function (name) {
|
||||
var target = {};
|
||||
module.__subs.push(new Sub(name, target));
|
||||
return target;
|
||||
}
|
||||
module.__include = function (name) {
|
||||
module.__subs.push(new Sub(name, module));
|
||||
}
|
||||
// execute the script of interest
|
||||
compiled.apply(module, [filename]);
|
||||
self.target.__require = function (path) { return self.newChild(path, {}); };
|
||||
self.target.__include = function (path) { self.newChild(path, self.target); };
|
||||
|
||||
// The module still needs to have its submodules loaded.
|
||||
this.filename = filename;
|
||||
this.module = module;
|
||||
this.subs = module.__subs;
|
||||
this.onLoad = module.__onLoad;
|
||||
|
||||
// remove these references so they don't get exported.
|
||||
delete module.__subs;
|
||||
delete module.__onLoad;
|
||||
delete module.__require;
|
||||
delete module.__include;
|
||||
}
|
||||
|
||||
function loadScript (filename, target, callback) {
|
||||
node.fs.cat(filename, "utf8", function (status, content) {
|
||||
if (status != 0) {
|
||||
stderr.puts("Error reading " + filename);
|
||||
node.exit(1);
|
||||
}
|
||||
// create wrapper function
|
||||
var wrapper = "function (__filename) {\n"
|
||||
+ " var onLoad;\n"
|
||||
+ " var onExit;\n"
|
||||
+ " var exports = this;\n"
|
||||
+ " var require = this.__require;\n"
|
||||
+ " var include = this.__include;\n"
|
||||
+ content
|
||||
+ "\n"
|
||||
+ " this.__onLoad = onLoad;\n"
|
||||
+ " this.__onExit = onExit;\n"
|
||||
+ "};\n"
|
||||
;
|
||||
var compiled_wrapper = node.compile(wrapper, self.filename);
|
||||
|
||||
var scaffold = new Scaffold(content, filename, target);
|
||||
// execute the script of interest
|
||||
compiled_wrapper.apply(self.target, [self.filename]);
|
||||
self.onLoad = self.target.__onLoad;
|
||||
self.onExit = self.target.__onExit;
|
||||
|
||||
//node.debug("after scaffold <" + filename + ">");
|
||||
self.loadChildren(function () {
|
||||
if (self.onLoad) self.onLoad();
|
||||
self.loaded = true;
|
||||
if (callback) callback();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
function finish() {
|
||||
//node.debug("finish 1 load <" + filename + ">");
|
||||
if (scaffold.onLoad instanceof Function) {
|
||||
//node.debug("calling onLoad for <" + filename + ">");
|
||||
scaffold.onLoad();
|
||||
}
|
||||
//node.debug("finish 2 load <" + filename + ">");
|
||||
node.Module.prototype.newChild = function (path, target) {
|
||||
var child = new node.Module({
|
||||
target: target,
|
||||
path: path,
|
||||
base_directory: node.path.dirname(this.filename),
|
||||
parent: this
|
||||
});
|
||||
this.children.push(child);
|
||||
return target;
|
||||
};
|
||||
|
||||
if (callback instanceof Function)
|
||||
callback();
|
||||
}
|
||||
|
||||
// Each time require() or include() was called inside the script
|
||||
// a key/value was added to scaffold.__subs.
|
||||
// Now we loop though each one and recursively load each.
|
||||
if (scaffold.subs.length == 0) {
|
||||
finish();
|
||||
} else {
|
||||
var ncomplete = 0;
|
||||
for (var i = 0; i < scaffold.subs.length; i++) {
|
||||
var sub = scaffold.subs[i];
|
||||
sub.load(node.path.dirname(filename), function () {
|
||||
ncomplete += 1;
|
||||
//node.debug("<" + filename + "> ncomplete = " + ncomplete.toString() + " scaffold.subs.length = " + scaffold.subs.length.toString());
|
||||
if (ncomplete === scaffold.subs.length)
|
||||
finish();
|
||||
});
|
||||
}
|
||||
}
|
||||
node.Module.prototype.loadChildren = function (callback) {
|
||||
var children = this.children;
|
||||
if (children.length == 0 && callback) callback();
|
||||
var nloaded = 0;
|
||||
for (var i = 0; i < children.length; i++) {
|
||||
var child = children[i];
|
||||
child.load(function () {
|
||||
nloaded += 1;
|
||||
if (nloaded == children.length && callback) callback();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
loadScript(ARGV[1], this);
|
||||
})();
|
||||
node.Module.prototype.exit = function (callback) {
|
||||
throw "not implemented";
|
||||
};
|
||||
|
||||
// Load the root module. I.E. the command line argument.
|
||||
(new node.Module({ path: ARGV[1], target: this })).load();
|
||||
|
|
|
@ -1,5 +1,10 @@
|
|||
var c = require("b/c");
|
||||
var c = require("b/c.js");
|
||||
exports.A = function () {
|
||||
return "A";
|
||||
}
|
||||
exports.C = function () { return c.C(); }
|
||||
};
|
||||
exports.C = function () {
|
||||
return c.C();
|
||||
};
|
||||
exports.D = function () {
|
||||
return c.D();
|
||||
};
|
||||
|
|
|
@ -1,3 +1,9 @@
|
|||
var d = require("d.js");
|
||||
|
||||
exports.C = function () {
|
||||
return "C";
|
||||
}
|
||||
};
|
||||
|
||||
exports.D = function () {
|
||||
return d.D();
|
||||
};
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
exports.D = function () {
|
||||
return "D";
|
||||
};
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
include("mjsunit");
|
||||
include("mjsunit.js");
|
||||
|
||||
function onLoad () {
|
||||
var dirname = node.path.dirname(__filename);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
include("mjsunit");
|
||||
include("mjsunit.js");
|
||||
var assert_count = 0;
|
||||
|
||||
function onLoad () {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
include("mjsunit");
|
||||
include("mjsunit.js");
|
||||
|
||||
var port = 8222;
|
||||
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
include("mjsunit.js");
|
||||
var a = require("fixtures/a.js");
|
||||
var d = require("fixtures/b/d.js");
|
||||
var d2 = require("fixtures/b/d.js");
|
||||
|
||||
function onLoad () {
|
||||
assertFalse(false, "testing the test program.");
|
||||
|
||||
assertInstanceof(a.A, Function);
|
||||
assertEquals("A", a.A());
|
||||
|
||||
assertInstanceof(a.C, Function);
|
||||
assertEquals("C", a.C());
|
||||
|
||||
assertInstanceof(a.D, Function);
|
||||
assertEquals("D", a.D());
|
||||
|
||||
assertInstanceof(d.D, Function);
|
||||
assertEquals("D", d.D());
|
||||
|
||||
assertInstanceof(d2.D, Function);
|
||||
assertEquals("D", d2.D());
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
include("mjsunit");
|
||||
include("mjsunit.js");
|
||||
|
||||
var port = 12123;
|
||||
var N = 1000;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
include("mjsunit");
|
||||
include("mjsunit.js");
|
||||
var port = 8921;
|
||||
|
||||
function onLoad () {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
include("mjsunit");
|
||||
include("mjsunit.js");
|
||||
|
||||
function onLoad () {
|
||||
assertInstanceof(setTimeout, Function);
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
include("mjsunit");
|
||||
var a = require("fixtures/a");
|
||||
|
||||
function onLoad () {
|
||||
assertFalse(false, "testing the test program.");
|
||||
|
||||
assertInstanceof(a.A, Function);
|
||||
assertEquals("A", a.A());
|
||||
|
||||
assertInstanceof(a.C, Function);
|
||||
assertEquals("C", a.C());
|
||||
}
|
|
@ -880,7 +880,7 @@ req.finish(function (res) {
|
|||
<p>The contents of <code>foo.js</code>:</p>
|
||||
|
||||
<pre>
|
||||
include("mjsunit");
|
||||
include("mjsunit.js");
|
||||
function onLoad () {
|
||||
assertEquals(1, 2);
|
||||
}</pre>
|
||||
|
@ -900,12 +900,11 @@ exports.assertEquals = function (expected, found, name_opt) {
|
|||
};</pre>
|
||||
|
||||
<p>
|
||||
The module <code>mjsunit</code> has exported a function
|
||||
The module <code>mjsunit.js</code> has exported a function
|
||||
<code>assertEquals()</code>. <code>mjsunit.js</code> must be
|
||||
in the same directory as <code>foo.js</code> for
|
||||
<code>include()</code> to find it. The module path is relative
|
||||
to the file calling <code>include()</code>. The module path does
|
||||
not include filename extensions like <code>.js</code>.
|
||||
to the file calling <code>include()</code>.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
|
@ -935,7 +934,7 @@ exports.assertEquals = function (expected, found, name_opt) {
|
|||
after the <code>onLoad()</code> callback is made. For example:
|
||||
</p>
|
||||
<pre>
|
||||
var mjsunit = require("mjsunit");
|
||||
var mjsunit = require("mjsunit.js");
|
||||
function onLoad () {
|
||||
mjsunit.assertEquals(1, 2);
|
||||
}</pre>
|
||||
|
|
Loading…
Reference in New Issue