From c8b143bf30178bd8549af8a88f6a6f841fc0ae71 Mon Sep 17 00:00:00 2001 From: Ryan Dahl Date: Fri, 18 Sep 2009 15:45:47 +0200 Subject: [PATCH] Absolute path names for require() refer to node.libraryPaths --- src/node.js | 200 +++++++++++------- src/util.js | 14 +- .../test-remote-module-loading.js | 0 test/mjsunit/fixtures/a.js | 3 + test/mjsunit/fixtures/b/c.js | 3 + test/mjsunit/fixtures/b/d.js | 2 + test/mjsunit/test-module-loading.js | 3 + 7 files changed, 144 insertions(+), 81 deletions(-) rename test/mjsunit/{ => disabled}/test-remote-module-loading.js (100%) diff --git a/src/node.js b/src/node.js index 19b4e263fa..b65b9ccef0 100644 --- a/src/node.js +++ b/src/node.js @@ -71,6 +71,16 @@ clearInterval = clearTimeout; // Module +node.libraryPaths = [ node.path.join(ENV["HOME"], ".node_libraries") + , node.path.join(node.installPrefix, "lib/node_libraries") + , "/" + ]; + +if (ENV["NODE_LIBRARY_PATHS"]) { + node.libraryPaths = + ENV["NODE_LIBRARY_PATHS"].split(":").concat(node.libraryPaths); +} + node.loadingModules = []; function require_async (url) { @@ -83,30 +93,25 @@ function require (url) { } function include_async (url) { - var currentModule = node.loadingModules[0]; - return currentModule.newChild(url, currentModule.target); + var promise = require_async(url) + promise.addCallback(function (t) { + // copy properties into global namespace. + for (var prop in t) { + if (t.hasOwnProperty(prop)) process[prop] = t[prop]; + } + }); + return promise; } function include (url) { include_async(url).wait(); } -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 by Node"; - } - - if (o.path.match(/:\/\//)) { - this.filename = o.path; - } else { - var dir = o.base_directory || "."; - this.filename = node.path.join(dir, o.path); - } +node.Module = function (filename, parent) { + node.assert(filename.charAt(0) == "/"); + this.filename = filename; + this.target = {}; + this.parent = parent; this.loaded = false; this.loadPromise = null; @@ -114,18 +119,99 @@ node.Module = function (o) { this.children = []; }; -node.Module.prototype.load = function (callback) { +node.Module.cache = {}; + +(function () { + function retrieveFromCache (loadPromise, fullPath, parent) { + var module; + if (fullPath in node.Module.cache) { + module = node.Module.cache[fullPath]; + setTimeout(function () { + loadPromise.emitSuccess(module.target); + }, 0); + } else { + module = new node.Module(fullPath, parent); + node.Module.cache[fullPath] = module; + module.load(loadPromise); + } + } + + function findPath (path, dirs, callback) { + node.assert(path.charAt(0) == "/"); + node.assert(dirs.constructor == Array); + + if (dirs.length == 0) { + callback(); + } else { + var dir = dirs[0]; + var rest = dirs.slice(1, dirs.length); + + var fullPath = node.path.join(dir, path); + node.fs.exists(fullPath, function (doesExist) { + if (doesExist) { + callback(fullPath); + } else { + findPath(path, rest, callback); + } + }); + } + } + + node.loadModule = function (requestedPath, target, parent) { + var loadPromise = new node.Promise(); + + // On success copy the loaded properties into the target + loadPromise.addCallback(function (t) { + for (var prop in t) { + if (t.hasOwnProperty(prop)) target[prop] = t[prop]; + } + }); + + if (!parent) { + // root module + node.assert(requestedPath.charAt(0) == "/"); + retrieveFromCache(loadPromise, requestedPath); + + } else { + if (requestedPath.charAt(0) == "/") { + // Need to find the module in node.libraryPaths + findPath(requestedPath, node.libraryPaths, function (fullPath) { + if (fullPath) { + retrieveFromCache(loadPromise, fullPath, parent); + } else { + loadPromise.emitError(); + } + }); + + } else { + // Relative file load + var fullPath = node.path.join(node.path.dirname(parent.filename), + requestedPath); + retrieveFromCache(loadPromise, fullPath, parent); + } + } + + return loadPromise; + }; +}()); + +node.Module.prototype.load = function (loadPromise) { + if (this.loaded) { + loadPromise.emitError(new Error("Module '" + self.filename + "' is already loaded.")); + return; + } + node.assert(!node.loadPromise); + this.loadPromise = loadPromise; + if (this.filename.match(/\.node$/)) { - return this.loadObject(callback); + this.loadObject(loadPromise); } else { - return this.loadScript(callback); + this.loadScript(loadPromise); } }; -node.Module.prototype.loadObject = function (callback) { +node.Module.prototype.loadObject = function (loadPromise) { var self = this; - var loadPromise = new node.Promise(); - self.loadPromise = loadPromise; // XXX Not yet supporting loading from HTTP. would need to download the // file, store it to tmp then run dlopen on it. node.fs.exists(self.filename, function (does_exist) { @@ -139,57 +225,28 @@ node.Module.prototype.loadObject = function (callback) { node.exit(1); } }); - return loadPromise; }; -node.Module.prototype.loadScript = function (callback) { +node.Module.prototype.loadScript = function (loadPromise) { var self = this; - if (self.loaded) { - throw "Module '" + self.filename + "' is already loaded."; - } + var catPromise = node.cat(self.filename); - var loadPromise = new node.Promise(); - node.assert(self.loadPromise === null); - self.loadPromise = loadPromise; - - var cat_promise = node.cat(self.filename); - - cat_promise.addErrback(function () { - node.stdio.writeError("Error reading " + self.filename + "\n"); - loadPromise.emitError(); - node.exit(1); + catPromise.addErrback(function () { + loadPromise.emitError(new Error("Error reading " + self.filename + "\n")); }); - cat_promise.addCallback(function (content) { + catPromise.addCallback(function (content) { // remove shebang content = content.replace(/^\#\!.*/, ''); // create wrapper function - var wrapper = "function (__filename) { "+ - " var onLoad; "+ - " var onExit; "+ - " var exports = this; "+ - content+ - "\n"+ - " this.__onLoad = onLoad;\n"+ - " this.__onExit = onExit;\n"+ - "};\n"; + var wrapper = "function (__filename, exports) { " + content + "\n};"; var compiled_wrapper = node.compile(wrapper, self.filename); node.loadingModules.unshift(self); - compiled_wrapper.apply(self.target, [self.filename]); + compiled_wrapper.apply(self.target, [self.filename, self.target]); node.loadingModules.shift(); - self.onLoad = self.target.__onLoad; - self.onExit = self.target.__onExit; - if (self.onLoad || self.onExit) { - node.stdio.writeError( "(node) onLoad and onExit have been removed. " - + " module load is synchronous so onLoad is unnecessary" - + " Use process.addListener('exit') for onExit. " - ); - node.exit(1); - } - self.waitChildrenLoad(function () { self.loaded = true; loadPromise.emitSuccess(self.target); @@ -198,15 +255,7 @@ node.Module.prototype.loadScript = function (callback) { }; 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); - child.load(); - return child.loadPromise; + return node.loadModule(path, target, this); }; node.Module.prototype.waitChildrenLoad = function (callback) { @@ -227,22 +276,19 @@ node.Module.prototype.waitChildrenLoad = function (callback) { }; (function () { + var cwd = node.cwd(); + // Make ARGV[0] and ARGV[1] into full paths. if (ARGV[0].charAt(0) != "/") { - ARGV[0] = node.path.join(node.cwd(), ARGV[0]); + ARGV[0] = node.path.join(cwd, ARGV[0]); } if (ARGV[1].charAt(0) != "/") { - ARGV[1] = node.path.join(node.cwd(), ARGV[1]); + ARGV[1] = node.path.join(cwd, ARGV[1]); } // Load the root module--the command line argument. - var root_module = new node.Module({ - path: node.path.filename(ARGV[1]), - base_directory: node.path.dirname(ARGV[1]), - target: this - }); - root_module.load(); + node.loadModule(ARGV[1], process); node.exit = function (code) { process.emit("exit"); diff --git a/src/util.js b/src/util.js index f1ad501c2f..66ce2e85bd 100644 --- a/src/util.js +++ b/src/util.js @@ -92,17 +92,23 @@ node.path = new function () { }; print = function (x) { - return node.stdio.write(x); + node.stdio.write(x); }; puts = function (x) { - return print(x.toString() + "\n"); + print(x.toString() + "\n"); }; p = function (x) { - return puts(JSON.stringify(x)); + if (x === null) { + puts("null"); + } else if (x === NaN) { + puts("NaN"); + } else { + puts(JSON.stringify(x) || "undefined"); + } }; node.debug = function (x) { - return node.stdio.writeError("DEBUG: " + x.toString() + "\n"); + node.stdio.writeError("DEBUG: " + x.toString() + "\n"); }; diff --git a/test/mjsunit/test-remote-module-loading.js b/test/mjsunit/disabled/test-remote-module-loading.js similarity index 100% rename from test/mjsunit/test-remote-module-loading.js rename to test/mjsunit/disabled/test-remote-module-loading.js diff --git a/test/mjsunit/fixtures/a.js b/test/mjsunit/fixtures/a.js index e94ac175fc..80a079845e 100644 --- a/test/mjsunit/fixtures/a.js +++ b/test/mjsunit/fixtures/a.js @@ -1,6 +1,9 @@ +node.debug("load fixtures/a.js"); + var c = require("b/c.js"); var string = "A"; + exports.A = function () { return string; }; diff --git a/test/mjsunit/fixtures/b/c.js b/test/mjsunit/fixtures/b/c.js index 4959fbc08c..a90ea9eddd 100644 --- a/test/mjsunit/fixtures/b/c.js +++ b/test/mjsunit/fixtures/b/c.js @@ -1,3 +1,5 @@ +node.debug("load fixtures/b/c.js"); + var d = require("d.js"); var string = "C"; @@ -12,4 +14,5 @@ exports.D = function () { process.addListener("exit", function () { string = "C done"; + puts("b/c.js exit"); }); diff --git a/test/mjsunit/fixtures/b/d.js b/test/mjsunit/fixtures/b/d.js index 447f912395..c9e0e7bc8c 100644 --- a/test/mjsunit/fixtures/b/d.js +++ b/test/mjsunit/fixtures/b/d.js @@ -1,3 +1,5 @@ +node.debug("load fixtures/b/d.js"); + var string = "D"; exports.D = function () { diff --git a/test/mjsunit/test-module-loading.js b/test/mjsunit/test-module-loading.js index 342b45014e..1bb2908879 100644 --- a/test/mjsunit/test-module-loading.js +++ b/test/mjsunit/test-module-loading.js @@ -1,3 +1,4 @@ +node.debug("load test-module-loading.js"); include("mjsunit.js"); var a = require("fixtures/a.js"); var d = require("fixtures/b/d.js"); @@ -35,4 +36,6 @@ process.addListener("exit", function () { assertInstanceof(d2.D, Function); assertEquals("D done", d2.D()); + + puts("exit"); });