diff --git a/README.markdown b/README.markdown
index 70bd8fa..583865b 100644
--- a/README.markdown
+++ b/README.markdown
@@ -1,16 +1,16 @@
CommonJScript
=============
-CommonJScript is an implementation of the [CommonJS API](http://commonjs.org/) for Classic ASP and Windows Script Host.
+CommonJScript is an implementation of the [CommonJS API](http://commonjs.org/) for Classic ASP.
The [Modules/1.0](http://commonjs.org/specs/modules/1.0/) specification is currently implemented. Various others are planned for the future.
-A few rudimentary modules are included in the *lib* directory.
-
Usage
-----
-*require.js* can be included in a ASP page or WScript job to provide the `require` function.
+*require.js* can be included in a ASP page or to provide the `require` function.
+
+Some other basic modules (system, engine, file) are also included in that file.
Tests
-----
@@ -20,12 +20,12 @@ The [CommonJS modules test suite](http://github.com/commonjs/commonjs/tree/maste
git submodule init
git submodule update
-Run `cscript test.wsf` from the *test* directory for Windows Script Host (CLI), or load *test.asp* on ASP.
+Load *test/index.asp* on ASP to run the tests. `debug=1` can be passed in the query string to turn `require.debug` on.
Acknowledgements
----------------
-The module loading code is mostly taken from [Narwhal](http://narwhaljs.org).
+Most of the code is taken directly from [Narwhal](http://narwhaljs.org) and [narwhal-lib](http://github.com/kriskowal/narwhal-lib/), so thanks to everyone working on those.
License
-------
diff --git a/lib/browser/xhr.js b/lib/browser/xhr.js
deleted file mode 100644
index 9574c8e..0000000
--- a/lib/browser/xhr.js
+++ /dev/null
@@ -1,6 +0,0 @@
-// commonjscript XMLHttpRequest client
-// @see http://wiki.commonjs.org/wiki/HTTP_Client/B
-
-exports.XMLHttpRequest = function () {
- return new ActiveXObject("MSXML2.ServerXMLHTTP");
-};
diff --git a/lib/system.js b/lib/system.js
deleted file mode 100644
index e461a91..0000000
--- a/lib/system.js
+++ /dev/null
@@ -1,26 +0,0 @@
-// commonjscript system module
-
-/*global Response, WScript */
-
-var platform;
-
-exports.engine = "jscript";
-
-// The platform is either wscript (cli) or asp
-if (typeof WScript === "object") {
- platform = "wscript";
-} else if (typeof Response === "object" &&
- typeof Response.write !== "undefined") {
- platform = "asp";
-} else {
- platform = "unknown";
-}
-
-// print function
-exports.print = function () {
- var out = Array.prototype.slice.call(arguments).join(" ");
- if (platform === "wscript") { WScript.echo(out); }
- else if (platform === "asp") { Response.write(out + "
"); }
-};
-
-exports.stdio = { print: exports.print };
diff --git a/package.json b/package.json
index c39920b..974599b 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "commonjscript",
- "description": "An implementation of the CommonJS API for Classic ASP and Windows Script Host",
- "version": "1.0.0",
+ "description": "An implementation of the CommonJS API for Classic ASP",
+ "version": "0.1.0",
"maintainers": [{
"name": "Nathan L Smith",
"email": "nlloyds@gmail.com",
@@ -12,16 +12,21 @@
"url": "http://www.opensource.org/licenses/mit-license.php"
}],
"bugs": "http://github.com/smith/commonjscript/issues",
- "keywords": ["engine", "jscript", "windows", "asp", "wsh", "modules"],
+ "keywords": ["engine", "jscript", "windows", "asp", "modules"],
"repositories": [{
"type": "git",
"url": "git://github.com/smith/commonjscript.git"
}],
- "contributors": [{
- "name": "Nathan L Smith",
- "email": "nlloyds@gmail.com",
- "web": "http://nlsmith.com/"
- }],
+ "contributors": [
+ {
+ "name": "Nathan L Smith",
+ "email": "nlloyds@gmail.com",
+ "web": "http://nlsmith.com/"
+ },
+ { "name": "Kris Kowal" },
+ { "name": "Tom Robinson" },
+ { "name": "Christoph Dorn" }
+ ],
"os": ["windows"],
"engine": ["jscript"],
"implements": ["CommonJS/Modules/1.0"]
diff --git a/require.js b/require.js
index c9df5b9..80a1820 100644
--- a/require.js
+++ b/require.js
@@ -1,126 +1,1089 @@
-/**
- * @fileOverview An implentation of JavaScript modules for use in an ASP or
- * WScript environment. This implementation is based on the one used in
- * Narhwal
- *
- * @see ServerJS/Modules/SecurableModules
- * @see Narwhal
- * @author Nathan L Smith
- * @date February 21, 2009
- */
+// CommonJScript for ASP
-/*global ActiveXObject, Response, Server, WScript, exports, require */
+/*global require, exports, ActiveXObject, Response, Server */
/*jslint evil:true */
(function () {
+// Base setup
+// =============================================================================
+var modules = {}, paths = [], print, global;
+
// Don't do anything if require is already there
if (typeof require === "function") { return; }
-/** Global exports object */
-if (typeof exports === "undefined") { exports = {}; }
+// ASP Only
+if (typeof Server === "undefined" || typeof Response === "undefined") {
+ throw Error("This script only runs in ASP.");
+}
+
+// Stand-in for require
+require = function (id) { return modules[id]; }; // global
+
+// JScript does not support assigning properties explicitly on the global
+// object or a reference to it. Some included modules try to modify this, so
+// use an empty object in its place.
+global = {};
+
+// JScript's split method is not ECMA compliant. Replace it.
+// =============================================================================
+
+/* Cross-Browser Split 1.0.1
+(c) Steven Levithan ; MIT License
+An ECMA-compliant, uniform cross-browser split method */
-/** A print function for use in logging */
-function print() {
- var out = Array.prototype.slice.call(arguments).join(" ");
- if (typeof WScript === "object") {
- WScript.echo(out);
- } else if (Response && typeof Response.write !== "undefined") {
- Response.write(out + "
");
+var cbSplit;
+
+// avoid running twice, which would break `cbSplit._nativeSplit`'s reference to the native `split`
+if (!cbSplit) {
+
+cbSplit = function (str, separator, limit) {
+ // if `separator` is not a regex, use the native `split`
+ if (Object.prototype.toString.call(separator) !== "[object RegExp]") {
+ return cbSplit._nativeSplit.call(str, separator, limit);
}
-}
-////////////////////////////////////////////////////////////////////////////////
-// Internal functions for file manipulation
-////////////////////////////////////////////////////////////////////////////////
+ var output = [],
+ lastLastIndex = 0,
+ flags = (separator.ignoreCase ? "i" : "") +
+ (separator.multiline ? "m" : "") +
+ (separator.sticky ? "y" : ""),
+ separator = RegExp(separator.source, flags + "g"), // make `global` and avoid `lastIndex` issues by working with a copy
+ separator2, match, lastIndex, lastLength;
+
+ str = str + ""; // type conversion
+ if (!cbSplit._compliantExecNpcg) {
+ separator2 = RegExp("^" + separator.source + "$(?!\\s)", flags); // doesn't need /g or /y, but they don't hurt
+ }
+
+ /* behavior for `limit`: if it's...
+ - `undefined`: no limit.
+ - `NaN` or zero: return an empty array.
+ - a positive number: use `Math.floor(limit)`.
+ - a negative number: no limit.
+ - other: type-convert, then use the above rules. */
+ if (limit === undefined || +limit < 0) {
+ limit = Infinity;
+ } else {
+ limit = Math.floor(+limit);
+ if (!limit) {
+ return [];
+ }
+ }
+
+ while (match = separator.exec(str)) {
+ lastIndex = match.index + match[0].length; // `separator.lastIndex` is not reliable cross-browser
+
+ if (lastIndex > lastLastIndex) {
+ output.push(str.slice(lastLastIndex, match.index));
+
+ // fix browsers whose `exec` methods don't consistently return `undefined` for nonparticipating capturing groups
+ if (!cbSplit._compliantExecNpcg && match.length > 1) {
+ match[0].replace(separator2, function () {
+ for (var i = 1; i < arguments.length - 2; i++) {
+ if (arguments[i] === undefined) {
+ match[i] = undefined;
+ }
+ }
+ });
+ }
+
+ if (match.length > 1 && match.index < str.length) {
+ Array.prototype.push.apply(output, match.slice(1));
+ }
+
+ lastLength = match[0].length;
+ lastLastIndex = lastIndex;
+
+ if (output.length >= limit) {
+ break;
+ }
+ }
+
+ if (separator.lastIndex === match.index) {
+ separator.lastIndex++; // avoid an infinite loop
+ }
+ }
+
+ if (lastLastIndex === str.length) {
+ if (lastLength || !separator.test("")) {
+ output.push("");
+ }
+ } else {
+ output.push(str.slice(lastLastIndex));
+ }
+
+ return output.length > limit ? output.slice(0, limit) : output;
+};
+
+cbSplit._compliantExecNpcg = /()??/.exec("")[1] === undefined; // NPCG: nonparticipating capturing group
+cbSplit._nativeSplit = String.prototype.split;
+
+} // end `if (!cbSplit)`
+
+// for convenience...
+String.prototype.split = function (separator, limit) {
+ return cbSplit(this, separator, limit);
+};
+
+// Engine
+// =============================================================================
+(function (exports) {
+
+exports.engine = "jscript";
+exports.debug = require.debug;
+
+// We're treating paths on ASP like Unix paths, so misreport the os.
+// Hey, IE's been calling itself Mozilla all these years, so this is nothing.
+exports.os = "asp";
+
+exports.Module = function (text, path, line) {
+ // Return a function that takes an object, which returns another function
+ // that takes the inject properties
+ return function (inject) {
+ inject = inject || {};
+ var names = [], values = [], result, ee;
+
+ for (var name in inject) {
+ if (Object.prototype.hasOwnProperty.call(inject, name)) {
+ names.push(name);
+ values.push(inject[name]);
+ }
+ }
+
+ // JScript's eval is not ES3 compliant, so the function needs to be
+ // assigned to a variable.
+ // @see http://www.bigresource.com/ASP-JScript-eval-bug-6nZST3Bk.html
+ eval("result = function (" + names.join(", ") + ") { " + text + "};");
+
+ try {
+ return result.apply(null, values);
+ } catch (e) {
+ // rethrow error with path
+ ee = Error(e.number, e.description + " (in " + path + ")");
+ ee.name = e.name;
+ ee.message = e.message;
+ throw ee;
+ }
+ };
+};
+
+})(modules.engine = {});
+modules["narwhal/engine"] = modules.engine;
+
+// System
+// =============================================================================
+(function (exports) {
+
+exports.platform = "asp";
+
+exports.stdout = {
+ print: function () {
+ Response.write(Array.prototype.slice.call(arguments).join(" ") +
+ "
");
+ }
+};
+
+// system.stdio.print is not defined in any specs, but the Modules/1.0 test
+// suite uses it
+exports.stdio = { print: exports.stdout.print };
+
+exports.print = exports.stdout.print;
+
+// Convert a JScript/VBScript collection to a plain ol object
+// XXX: Not standard
+exports.collectionToObject = function (c) {
+ var o = {}, // new object
+ e = new Enumerator(c),
+ cnt = 0, // count
+ item; // item
+
+ // Iterate through collection
+ while (!e.atEnd()) {
+ item = e.item();
+ // null items have no count
+ try { cnt = c(item).count; }
+ catch (er) { cnt = -1; }
+
+ if (cnt > 1) { // multiple items
+ o[item] = [];
+ for (var i = 1; i < cnt + 1; i += 1) {
+ o[item].push(String(c(item)(i)));
+ }
+ } else if (cnt === 0) { o[item] = undefined; }
+ else { o[item] = c(item); }
+
+ e.moveNext();
+ }
+
+ for (i in o) {
+ // Stringify everything but null, undefined, and array
+ if (Object.prototype.hasOwnProperty.call(o, i) && o[i] !== null &&
+ typeof o[i] !== "undefined" && typeof o[i].length === "undefined") {
+ o[i] = String(o[i]);
+ }
+ }
+
+ return o;
+};
+
+exports.env = exports.collectionToObject(Request.ServerVariables);
+
+})(modules.system = {});
+
+print = require("system").print; // Add print function here
+
+// FS
+//
+// From narwhal-lib/lib/narwhal/fs-boot.js
+// =============================================================================
+(function (exports) {
+// -- kriskowal Kris Kowal Copyright (C) 2009-2010 MIT License
+// -- tlrobinson Tom Robinson TODO
/**
- * An implentation of readFile, which opens a file from the filesystem and
- * returns its contents as a string
- * @private
- */
-function readFile(fileName) {
- var contents,
- fileSystem = new ActiveXObject("Scripting.FileSystemObject"),
- mapPath = function mapPath(path) {
- var isASP = typeof Server === "object" &&
- typeof Server.mapPath !== "undefined";
- return isASP ? Server.mapPath(path) : path;
- };
- fileName = mapPath(fileName);
-
- if (fileSystem.fileExists(fileName)) {
- try { // JScript will throw an error if the file is empty
- contents = fileSystem.openTextFile(fileName).readAll();
- } catch (e) { contents = ""; }
- } else { throw new Error("File " + fileName + " does not exist"); }
- return contents;
-}
+ * Pure JavaScript implementations of file system path
+ * manipulation.
+ *
+ * This module depends on the non CommonJS "engine" module,
+ * particularly for an "os" property that has the words
+ * "windows" or "winnt" to distinguish Windows from Unix
+ * file systems.
+ */
+
+// NOTE: this file may be used is the engine bootstrapping
+// process, so any "requires" must be accounted for in
+// narwhal.js
+
+/*whatsupdoc*/
+/*markup markdown*/
+
+var ENGINE = require("engine");
+
+/**
+ * @name ROOT
+ * * `/` on Unix
+ * * `\` on Windows
+ */
-function dirname(path) {
- var raw = String(path),
- match = raw.match(/^(.*)\/[^\/]+\/?$/);
- if (match && match[1]) { return match[1]; }
- else if (raw.charAt(0) == "/") { return "/"; }
- else { return "."; }
+/**
+ * @name SEPARATOR
+ * * `/` on Unix
+ * * `\` on Windows
+ */
+
+/**
+ * @name ALT_SEPARATOR
+ * * undefined on Unix
+ * * `/` on Windows
+ */
+
+if (/\bwind(nt|ows)\b/i.test(ENGINE.os)) {
+ exports.ROOT = "\\";
+ exports.SEPARATOR = "\\";
+ exports.ALT_SEPARATOR = "/";
+} else {
+ exports.ROOT = "/";
+ exports.SEPARATOR = "/";
+ exports.ALT_SEPARATOR = undefined;
}
-function canonicalize(path) {
- return path.replace(/[^\/]+\/\.\.\//g, "").replace(/([^\.])\.\//g, "$1").
- replace(/^\.\//g, "").replace(/\/\/+/g, "/");
+// we need to make sure the separator regex is always in sync with the separators.
+// this caches the generated regex and rebuild if either separator changes.
+var separatorCached, altSeparatorCached, separatorReCached;
+/**
+ * @function
+ */
+exports.SEPARATORS_RE = function () {
+ if (
+ separatorCached !== exports.SEPARATOR ||
+ altSeparatorCached !== exports.ALT_SEPARATOR
+ ) {
+ separatorCached = exports.SEPARATOR;
+ altSeparatorCached = exports.ALT_SEPARATOR;
+ separatorReCached = new RegExp("[" +
+ (separatorCached || '').replace(/[-[\]{}()*+?.\\^$|,#\s]/g, "\\$&") +
+ (altSeparatorCached || '').replace(/[-[\]{}()*+?.\\^$|,#\s]/g, "\\$&") +
+ "]", "g");
+ }
+ return separatorReCached;
}
-////////////////////////////////////////////////////////////////////////////////
+/**
+ * separates a path into components. If the path is
+ * absolute, the first path component is the root of the
+ * file system, indicated by an empty string on Unix, and a
+ * drive letter followed by a colon on Windows.
+ * @returns {Array * String}
+ */
+exports.split = function (path) {
+ var parts;
+ try {
+ parts = String(path).split(exports.SEPARATORS_RE());
+ } catch (exception) {
+ throw new Error("Cannot split " + (typeof path) + ', "' + path + '"');
+ }
+ // this special case helps isAbsolute
+ // distinguish an empty path from an absolute path
+ // "" -> [] NOT [""]
+ if (parts.length == 1 && parts[0] == "")
+ return [];
+ // "a" -> ["a"]
+ // "/a" -> ["", "a"]
+ return parts;
+};
+
+/**
+ * Takes file system paths as variadic arguments and treats
+ * each as a file or directory path and returns the path
+ * arrived by traversing into the those paths. All
+ * arguments except for the last must be paths to
+ * directories for the result to be meaningful.
+ * @returns {String} path
+ */
+exports.join = function () {
+ if (arguments.length === 1 && typeof arguments[0] === "object")
+ return exports.normal.apply(exports, arguments[0]);
+ return exports.normal.apply(exports, arguments);
+};
+
+/**
+ * Takes file system paths as variadic arguments and treats
+ * each path as a location, in the URL sense, resolving each
+ * new location based on the previous. For example, if the
+ * first argument is the absolute path of a JSON file, and
+ * the second argument is a path mentioned in that JSON
+ * file, `resolve` returns the absolute path of the
+ * mentioned file.
+ * @returns {String} path
+ */
+exports.resolve = function () {
+ var root = "";
+ var parents = [];
+ var children = [];
+ var leaf = "";
+ for (var i = 0; i < arguments.length; i++) {
+ var path = String(arguments[i]);
+ if (path == "")
+ continue;
+ var parts = path.split(exports.SEPARATORS_RE());
+ if (exports.isAbsolute(path)) {
+ root = parts.shift() + exports.SEPARATOR;
+ parents = [];
+ children = [];
+ }
+ leaf = parts.pop();
+ if (leaf == "." || leaf == "..") {
+ parts.push(leaf);
+ leaf = "";
+ }
+ for (var j = 0; j < parts.length; j++) {
+ var part = parts[j];
+ if (part == "." || part == '') {
+ } else if (part == "..") {
+ if (children.length) {
+ children.pop();
+ } else {
+ if (root) {
+ } else {
+ parents.push("..");
+ }
+ }
+ } else {
+ children.push(part);
+ }
+ };
+ }
+ path = parents.concat(children).join(exports.SEPARATOR);
+ if (path) leaf = exports.SEPARATOR + leaf;
+ return root + path + leaf;
+};
+
+/**
+ * Takes paths as any number of arguments and reduces them
+ * into a single path in normal form, removing all "." path
+ * components, and reducing ".." path components by removing
+ * the previous path component if possible.
+ * @returns {String} path
+ */
+exports.normal = function () {
+ var root = "";
+ var parents = [];
+ var children = [];
+ for (var i = 0, ii = arguments.length; i < ii; i++) {
+ var path = String(arguments[i]);
+ // empty paths have no affect
+ if (path === "")
+ continue;
+ var parts = path.split(exports.SEPARATORS_RE());
+ if (exports.isAbsolute(path)) {
+ root = parts.shift() + exports.SEPARATOR;
+ parents = [];
+ children = [];
+ }
+ for (var j = 0, jj = parts.length; j < jj; j++) {
+ var part = parts[j];
+ if (part == "." || part == '') {
+ } else if (part == "..") {
+ if (children.length) {
+ children.pop();
+ } else {
+ if (root) {
+ } else {
+ parents.push("..");
+ }
+ }
+ } else {
+ children.push(part);
+ }
+ }
+ }
+ path = parents.concat(children).join(exports.SEPARATOR);
+ return root + path;
+};
+
+/***
+ * @returns {Boolean} whether the given path begins at the
+ * root of the file system or a drive letter.
+ */
+exports.isAbsolute = function (path) {
+ // for absolute paths on any operating system,
+ // the first path component always determines
+ // whether it is relative or absolute. On Unix,
+ // it is empty, so ['', 'foo'].join('/') == '/foo',
+ // '/foo'.split('/') == ['', 'foo'].
+ var parts = exports.split(path);
+ // split('') == []. '' is not absolute.
+ // split('/') == ['', ''] is absolute.
+ // split(?) == [''] does not occur.
+ if (parts.length == 0)
+ return false;
+ return exports.isRoot(parts[0]);
+};
-function _require(name, parentPath, loadOnce) {
- var result, pwd, extensions, paths, path, searchDirectory, ext;
- if (name.charAt(0) === "/") {
- result = _attemptLoad(name, name, loadOnce);
- if (result) { return result; }
+/**
+ * @returns {Boolean} whether the given path does not begin
+ * at the root of the file system or a drive letter.
+ */
+exports.isRelative = function (path) {
+ return !exports.isAbsolute(path);
+};
+
+/**
+ * @returns {Boolean} whether the given path component
+ * corresponds to the root of the file system or a drive
+ * letter, as applicable.
+ */
+exports.isRoot = function (first) {
+ if (/\bwind(nt|ows)\b/i.test(ENGINE.os)) {
+ return /:$/.test(first);
} else {
- pwd = dirname(parentPath);
- extensions = (/\.\w+$/).test(name) ? [""] : require.extensions;
- paths = ["."].concat(require.paths);
+ return first == "";
+ }
+};
+
+/**
+ * @returns {String} the Unix root path or corresponding
+ * Windows drive for a given path.
+ */
+exports.root = function (path) {
+ if (!exports.isAbsolute(path))
+ path = require("./fs").absolute(path);
+ var parts = exports.split(path);
+ return exports.join(parts[0], '');
+};
+
+/**
+ * @returns {String} the parent directory of the given path.
+ */
+exports.directory = function (path) {
+ var parts = exports.split(path);
+ // XXX needs to be sensitive to the root for
+ // Windows compatibility
+ parts.pop();
+ return parts.join(exports.SEPARATOR) || ".";
+};
+
+/**
+ * @returns {String} the last component of a path, without
+ * the given extension if the extension is provided and
+ * matches the given file.
+ * @param {String} path
+ * @param {String} extention an optional extention to detect
+ * and remove if it exists.
+ */
+exports.base = function (path, extension) {
+ var base = path.split(exports.SEPARATORS_RE()).pop();
+ if (extension)
+ base = base.replace(
+ new RegExp(RegExp.escape(extension) + '$'),
+ ''
+ );
+ return base;
+};
+
+/**
+ * @returns {String} the extension (e.g., `txt`) of the file
+ * at the given path.
+ */
+exports.extension = function (path) {
+ path = exports.base(path);
+ path = path.replace(/^\.*/, '');
+ var index = path.lastIndexOf(".");
+ return index <= 0 ? "" : path.substring(index);
+};
+})(modules["narwhal/fs"] = {});
+
+// FS - platform specific
+// =============================================================================
+(function (exports) {
+
+var fso = new ActiveXObject("Scripting.FileSystemObject"),
+ stream = new ActiveXObject("ADODB.Stream");
+
+// Wrap some calls in Server.mapPath on ASP
+function m(path) { return Server.mapPath(path); }
+
+exports.isFile = function (path) {
+ return fso.fileExists(m(path));
+};
+
+exports.read = function (path, options) {
+ var charset = (options || {}).charset || "utf-8",
+ adTypeText = 2, text = "";
+
+ stream.open();
+ stream.charset = charset;
+ stream.type = adTypeText;
+ stream.loadFromFile(m(path));
+ text = stream.readText();
+ stream.close();
+
+ return text;
+};
+})(modules.fs = modules["narwhal/fs"]);
+
+// Loader
+//
+// from narwhal-lib/lib/narwhal/loader.js
+// =============================================================================
+(function (exports) {
+// -- kriskowal Kris Kowal Copyright (C) 2009-2010 MIT License
+// -- cadorn Christoph Dorn
+
+// NOTE: when this file is being loaded as part of the
+// Narwhal bootstrapping process, all of the "requires" that
+// occur here have to be manually accounted for (who loads
+// the loader?)
+
+var ENGINE = require("narwhal/engine");
+// HACK: the stars prevent the file module from being sent to browser
+// clients with the regexen we're using. we need a real solution
+// for this.
+var FS = require(/**/"narwhal/fs"/**/);
+
+// this gets swapped out with a full fledged-read before
+// we're done using it
+var read = FS.read;
+
+exports.Loader = function (options) {
+ var loader = {};
+ var factories = options.factories || {};
+ var paths = options.paths;
+ var extensions = options.extensions || ["", ".js"];
+ var timestamps = {};
+ var debug = options.debug;
+
+ loader.resolve = exports.resolve;
+
+ loader.resolvePkg = function(id, baseId, pkg, basePkg) {
+ return exports.resolvePkg(loader, id, baseId, pkg, basePkg);
+ };
+
+ loader.find = function (topId) {
+ // if it's absolute only search the "root" directory.
+ // FS.join() must collapse multiple "/" into a single "/"
+ var searchPaths = FS.isAbsolute(topId) ? [""] : paths;
+
for (var j = 0; j < extensions.length; j++) {
- ext = extensions[j];
- for (var i = 0; i < paths.length; i++) {
- searchDirectory = (paths[i] === ".") ? pwd : paths[i];
- path = searchDirectory + "/" + name + ext;
- result = _attemptLoad(name, path, loadOnce);
- if (result) { return result; }
+ var extension = extensions[j];
+ for (var i = 0; i < searchPaths.length; i++) {
+ var path = FS.join(searchPaths[i], topId + extension);
+ if (FS.isFile(path))
+ return path;
+ }
+ }
+ throw new Error("require error: couldn't find \"" + topId + '"');
+ };
+
+ loader.fetch = function (topId, path) {
+ if (!path)
+ path = loader.find(topId);
+ if (typeof FS.lastModified === "function")
+ timestamps[path] = FS.lastModified(path);
+ if (debug)
+ print('loader: fetching ' + topId);
+ var text = read(path, {
+ 'charset': 'utf-8'
+ });
+ // we leave the endline so the error line numbers align
+ text = text.replace(/^#[^\n]+\n/, "\n");
+ return text;
+ };
+
+ loader.Module = function (text, topId, path) {
+ if (ENGINE.Module) {
+ if (!path)
+ path = loader.find(topId);
+ var factory = ENGINE.Module(text, path, 1);
+ factory.path = path;
+ return factory;
+ } else {
+ return function (inject) {
+ var keys = [], values = [];
+ for (var key in inject) {
+ if (Object.prototype.hasOwnProperty.call(inject, key)) {
+ keys.push(key);
+ values.push(inject[key]);
+ }
+ }
+ return Function.apply(null, keys).apply(this, values);
+ };
+ }
+ };
+
+ loader.load = function (topId, path) {
+ if (!Object.prototype.hasOwnProperty.call(factories, topId)) {
+ loader.reload(topId, path);
+ } else if (typeof FS.lastModified === "function") {
+ var path = loader.find(topId);
+ if (loader.hasChanged(topId, path))
+ loader.reload(topId, path);
+ }
+ return factories[topId];
+ };
+
+ loader.reload = function (topId, path) {
+ factories[topId] = loader.Module(loader.fetch(topId, path), topId, path);
+ };
+
+ loader.isLoaded = function (topId) {
+ return Object.prototype.hasOwnProperty.call(factories, topId);
+ };
+
+ loader.hasChanged = function (topId, path) {
+ if (!path)
+ path = loader.resolve(topId);
+ return (
+ !Object.prototype.hasOwnProperty.call(timestamps, path) ||
+ FS.lastModified(path) > timestamps[path]
+ );
+ };
+
+ loader.paths = paths;
+ loader.extensions = extensions;
+
+ return loader;
+};
+
+exports.resolve = function (id, baseId) {
+ id = String(id);
+ if (id.charAt(0) == ".") {
+ id = FS.directory(baseId) + "/" + id;
+ }
+ // module ids need to use forward slashes, despite what the OS might say
+ return FS.normal(id).replace(/\\/g, '/');
+};
+
+exports.resolvePkg = function(loader, id, baseId, pkg, basePkg) {
+ if(!loader.usingCatalog) {
+ // no usingCatalog - fall back to default
+ return [exports.resolve(id, baseId), null];
+ }
+ if(pkg) {
+ // locate id in pkg
+ if(basePkg && loader.usingCatalog[basePkg]) {
+ // see if pkg is an alias
+ var packages = loader.usingCatalog[basePkg].packages;
+ if(packages[pkg]) {
+ if(loader.usingCatalog[packages[pkg]]) {
+ var path = loader.usingCatalog[packages[pkg]].libPath;
+ return [exports.resolve("./" + id, path + "/"), packages[pkg]];
+ } else {
+ throw "Package '"+packages[pkg]+"' aliased with '"+pkg+"' in '"+basePkg+"' not found";
+ }
+ }
+ }
+ // see if pkg is a top-level ID
+ if(loader.usingCatalog[pkg]) {
+ var path = loader.usingCatalog[pkg].libPath;
+ return [exports.resolve("./" + id, path + "/"), pkg];
+ } else {
+ throw "Package '" + pkg + "' not aliased in '"+basePkg+"' nor a top-level ID";
+ }
+ } else {
+ // if id is relative we want a module relative to basePkg if it exists
+ if(id.charAt(0) == "." && basePkg) {
+ // if baseId is absolute we use it as a base and ignore basePkg
+ if (FS.isAbsolute(baseId)) {
+ path = FS.Path(baseId);
+ } else if (loader.usingCatalog[basePkg]) {
+ path = loader.usingCatalog[basePkg].libPath.join(baseId);
+ } else {
+ throw "basePkg '" + basePkg + "' not known";
}
+
+ // try and locate the path - at this stage it should be found
+ return [exports.resolve(id, path.valueOf()), basePkg];
+
+ } else {
+ // id is not relative - resolve against system modules
+ return [exports.resolve(id, baseId), undefined];
}
}
- throw new Error("couldn't find " + name);
-}
+};
+})(modules.loader = {});
+
+// Sandbox
+//
+// from narwhal-lib/lib/narwhal/sandbox.js
+// =============================================================================
+(function (exports) {
+// -- kriskowal Kris Kowal Copyright (C) 2009-2010 MIT License
-function _requireFactory(path, loadOnce) {
- return function(name) {
- return _require(name, path, loadOnce || false);
+// NOTE: this file is used is the bootstrapping process,
+// so any "requires" must be accounted for in narwhal.js
+
+var SYSTEM = require("system");
+var ENGINE = require("engine");
+
+exports.Sandbox = function (options) {
+ options = options || {};
+ var loader = options.loader;
+ var exportsMemo = options.modules || {};
+ var moduleMemo = {};
+ var debug = options.debug !== undefined ? !!options.debug : !!ENGINE.debug;
+ var debugDepth = 0;
+ var main;
+ var setDisplayName = (ENGINE.engine == "jsc");
+
+ // managed print free variable in the sandbox forwards
+ // to system.print in the sandbox
+ var subprint = options.print || function () {
+ var subsystem = sandbox("system");
+ return subsystem.print.apply(subsystem, arguments);
};
-}
-function _attemptLoad(name, path, loadOnce) {
- path = canonicalize(path);
- var module, moduleCode;
-
- // see if the module is already loaded
- if (require.loaded[path] && loadOnce) { return require.loaded[path]; }
- try { moduleCode = readFile(path); } catch (e) {}
- if (typeof moduleCode !== "undefined") {
- require.loaded[path] = {};
- module = new Function("require", "exports", moduleCode);
- module(_requireFactory(path, true), require.loaded[path]);
- return require.loaded[path];
- }
- return false;
-}
+ var sandbox = function (id, baseId, pkg, basePkg, options) {
+
+ if (!options)
+ options = {};
+
+ if (sandbox.debug)
+ SYSTEM.print("REQUIRE: id["+id+"] baseId["+baseId+"] pkg["+pkg+"] basePkg["+basePkg+"]");
+
+ if (loader.resolvePkg) {
+ var resolveInfo = loader.resolvePkg(id, baseId, pkg, basePkg);
+ id = resolveInfo[0];
+ pkg = resolveInfo[1];
+ } else {
+ id = loader.resolve(id, baseId);
+ }
+
+ if (sandbox.debug)
+ SYSTEM.print("USING: id["+id+"] pkg["+pkg+"]");
+
+ /* populate memo with module instance */
+ if (!Object.prototype.hasOwnProperty.call(exportsMemo, id) || options.force || options.once) {
+
+ if (sandbox.debug)
+ SYSTEM.print(new Array(++debugDepth + 1).join("\\") + " " + id, 'module');
+
+ var globals = {};
+ if (sandbox.debug) {
+ // record globals
+ for (var name in global)
+ globals[name] = true;
+ }
+
+ var exports;
+ if (options.once) {
+ exports = {};
+ } else {
+ if (!Object.prototype.hasOwnProperty.call(exportsMemo, id) || options.reload)
+ exports = exportsMemo[id] = {};
+ exports = exportsMemo[id];
+ }
-/** The global require function */
-require = function (name) { return _require(name, ".", true); };
+ if (options.reload)
+ loader.reload(id);
+
+ var factory;
+ try {
+ factory = loader.load(id);
+ } finally {
+ // poor man's catch and rethrow (Rhino sets file/line to where the exception is thrown, not created)
+ if (!factory) {
+ delete exportsMemo[id];
+ if (sandbox.debug)
+ debugDepth--;
+ }
+ }
+
+ var require = Require(id, pkg);
+ var load = Load(id, pkg);
+ var module
+ = moduleMemo[id]
+ = moduleMemo[id] || Module(id, factory.path);
+ if (pkg) {
+ module["package"] = pkg;
+ module.using = (
+ pkg && loader.usingCatalog && loader.usingCatalog[pkg] ?
+ loader.usingCatalog[pkg]["packages"] :
+ {}
+ );
+ }
+
+ var scope = {
+ "require": require,
+ "exports": exports,
+ "module": module,
+ "print": subprint
+ };
+
+ // require.once provides a scope of extra stuff to inject
+ if (options.scope) {
+ for (var name in options.scope) {
+ if (Object.prototype.hasOwnProperty.call(options.scope, name)) {
+ scope[name] = options.scope[name];
+ }
+ }
+ }
+
+ var completed;
+ try {
+ factory(scope);
+ completed = true;
+ } finally {
+ if (!completed) {
+ delete exportsMemo[id];
+ delete moduleMemo[id];
+ }
+ }
+
+ if (sandbox.debug) {
+ // check for new globals
+ for (var name in global)
+ if (!globals[name])
+ SYSTEM.print("NEW GLOBAL: " + name);
+ }
+
+ if (sandbox.debug)
+ SYSTEM.print(new Array(debugDepth-- + 1).join("/") + " " + id, 'module');
+
+ // set fn.displayName on exported functions for better debugging
+ if (setDisplayName) {
+ var displayID = id.replace(/[^\w]/g, "_").toUpperCase();
+ for (var name in exports) {
+ if (typeof exports[name] === "function" && !exports[name].displayName &&
+ Object.prototype.hasOwnProperty.call(exports, name)) {
+ exports[name].displayName = displayID+"."+name;
+ }
+ }
+ }
+
+ } else {
+ if (sandbox.debug > 1)
+ SYSTEM.print(new Array(debugDepth + 1).join("|") + " " + id, 'module');
+ exports = exportsMemo[id];
+ if (moduleMemo[id]) {
+ moduleMemo[id].setExports = function () {
+ throw new Error("Cannot set exports after a module has been required by another module.");
+ };
+ }
+ }
+
+ /* support curryId for modules in which it is requested */
+ var imports = {};
+ var importsUsed = false;
+ var curryUsed = false;
+ for (var name in exports) {
+ curryUsed = (
+ typeof exports[name] == "function" &&
+ // if it is Java class this will throw an exception, which is terribly annoying during debugging
+ Object.prototype.toString.call(exports[name]) !== "[object JavaClass]" &&
+ exports[name].xNarwhalCurry
+ );
+
+ if (curryUsed) {
+ importsUsed = true;
+ imports[name] = (function (block, module) {
+ return function () {
+ return block.apply(
+ this,
+ [module].concat(Array.prototype.slice.call(arguments))
+ );
+ };
+ })(exports[name], moduleMemo[baseId]);
+ } else {
+ imports[name] = exports[name];
+ }
+ }
+
+ if (!importsUsed)
+ imports = exports;
+
+ return imports;
+
+ };
+
+ /*
+ sandbox.async = function (id, baseId, pkg, basePkg, options) {
+ };
+ */
+
+ sandbox.load = function (id, baseId, pkg, basePkg) {
+ if (loader.resolvePkg) {
+ var resolveInfo = loader.resolvePkg(id, baseId, pkg, basePkg);
+ id = resolveInfo[0];
+ pkg = resolveInfo[1];
+ } else {
+ id = loader.resolve(id, baseId);
+ }
+ return loader.load(id);
+ };
+
+ sandbox.once = function (id, baseId, pkg, basePkg, scope) {
+ return sandbox(id, baseId, pkg, basePkg, {"scope": scope});
+ };
+
+ /*
+ sandbox.load.async = function (id, baseId, pkg, basePkg, options) {
+ };
+ */
+
+ sandbox.force = function (id) {
+ /* baseId, pkgId, basePkg , options */
+ return sandbox(id, undefined, undefined, undefined, {"force": true});
+ };
+
+ sandbox.main = function (id, path) {
+ if (!path && sandbox.loader.find)
+ path = sandbox.loader.find(id)[1];
+ id = sandbox.loader.resolve(id, "");
+ main = sandbox.main = moduleMemo[id] = moduleMemo[id] || Module(id, path);
+ sandbox(id);
+ return main;
+ };
+
+ sandbox.loader = loader;
+ sandbox.system = SYSTEM;
+ sandbox.paths = loader.paths;
+ sandbox.extensions = loader.extensions;
+ sandbox.debug = debug;
+
+ var Require = function (baseId, basePkg) {
+ // variations of require.* functions that close on a
+ // particular [package/]module
+ var require = function (id, pkg) {
+ return sandbox(id, baseId, pkg, basePkg);
+ };
+ require.async = function (id, pkg) {
+ return sandbox.async(id, baseId, pkg, basePkg);
+ };
+ require.once = function (id, scope) {
+ return sandbox.once(id, baseId, undefined, undefined, scope);
+ };
+ require.once.async = function (id, scope) {
+ return sandbox.once.async(id, baseId, undefined, undefined, scope);
+ };
+ require.load = function (id, pkg) {
+ return sandbox.load(id, baseId, pkg, basePkg);
+ };
+ require.load.async = function (id, pkg) {
+ return sandbox.load.async(id, baseId, pkg, basePkg);
+ };
+ require.force = function (id) {
+ return sandbox.force(id, baseId);
+ };
+ require.loader = loader;
+ require.main = main;
+ require.paths = loader.paths;
+ require.extensions = loader.extensions;
+ return require;
+ };
+
+ var Load = function (baseId, basePkg) {
+ var load = function (id, pkg) {
+ return sandbox.load(id, baseId, pkg, basePkg);
+ };
+ load.async = function (id) {
+ return sandbox.load.async(id, baseId, pkg, basePkg);
+ };
+ return load;
+ };
+
+ var Module = function (baseId, path) {
+ var module = {};
+ module.id = baseId;
+ module.path = path;
+ module.xNarwhalCurry = function (block) {
+ block.xNarwhalCurry = true;
+ return block;
+ };
+ module.setExports = function (exports) {
+ return exportsMemo[baseId] = exports;
+ };
+ return module;
+ };
+
+ return sandbox;
+};
+
+exports.sandbox = function(main, system, options) {
+ options = options || {};
+ var prefix = options['prefix'];
+ var loader = options['loader'] || require.loader;
+ var modules = options['modules'] || {};
+ var print = options['print'];
+ var debug = options['debug'];
+ if (!loader) throw new Error(
+ "sandbox cannot operate without a loader, either explicitly " +
+ "provided as an option, or implicitly provided by the current " +
+ "sandbox's 'loader' object."
+ );
+ if (prefix)
+ loader = require("narwhal/loader/prefix").PrefixLoader(prefix, loader);
+ var sandbox = exports.Sandbox({
+ modules: modules,
+ loader: loader,
+ print: print,
+ debug: debug
+ });
+
+ return sandbox.main(main);
+};
+})(modules.sandbox = {});
+
+// =============================================================================
+
+// Set up paths
+paths = ["", "lib", "engines/" + require("engine").engine + "/lib",
+ "engines/" + require("system").platform + "/lib"];
+
+// Create require
+require = require("sandbox").Sandbox({
+ loader: require("loader").Loader({ paths: paths, debug: require.debug }),
+ modules: modules,
+ debug: require.debug
+});
+
+// Add engine-specific system/file if available
+try {
+ require("system-engine");
+ require("fs-engine");
+} catch (e) {
+ if (e.description.indexOf("require error") === -1) { throw e; }
+}
-require.paths = ["lib"];
-require.loaded = {};
-require.extensions = [".js"];
-
-})();
+})(this);
diff --git a/test/asp/absolute.asp b/test/asp/absolute.asp
new file mode 100644
index 0000000..36fe802
--- /dev/null
+++ b/test/asp/absolute.asp
@@ -0,0 +1,7 @@
+
+
diff --git a/test/asp/cyclic.asp b/test/asp/cyclic.asp
new file mode 100644
index 0000000..d242915
--- /dev/null
+++ b/test/asp/cyclic.asp
@@ -0,0 +1,7 @@
+
+
diff --git a/test/asp/determinism.asp b/test/asp/determinism.asp
new file mode 100644
index 0000000..61491b2
--- /dev/null
+++ b/test/asp/determinism.asp
@@ -0,0 +1,7 @@
+
+
diff --git a/test/asp/exactExports.asp b/test/asp/exactExports.asp
new file mode 100644
index 0000000..a21d894
--- /dev/null
+++ b/test/asp/exactExports.asp
@@ -0,0 +1,7 @@
+
+
diff --git a/test/asp/hasOwnProperty.asp b/test/asp/hasOwnProperty.asp
new file mode 100644
index 0000000..525d986
--- /dev/null
+++ b/test/asp/hasOwnProperty.asp
@@ -0,0 +1,7 @@
+
+
diff --git a/test/asp/index.asp b/test/asp/index.asp
new file mode 100644
index 0000000..c347f92
--- /dev/null
+++ b/test/asp/index.asp
@@ -0,0 +1,19 @@
+
diff --git a/test/asp/method.asp b/test/asp/method.asp
new file mode 100644
index 0000000..83ffc0a
--- /dev/null
+++ b/test/asp/method.asp
@@ -0,0 +1,7 @@
+
+
diff --git a/test/asp/missing.asp b/test/asp/missing.asp
new file mode 100644
index 0000000..12f350b
--- /dev/null
+++ b/test/asp/missing.asp
@@ -0,0 +1,7 @@
+
+
diff --git a/test/asp/monkeys.asp b/test/asp/monkeys.asp
new file mode 100644
index 0000000..b1c9d0f
--- /dev/null
+++ b/test/asp/monkeys.asp
@@ -0,0 +1,7 @@
+
+
diff --git a/test/asp/nested.asp b/test/asp/nested.asp
new file mode 100644
index 0000000..186f413
--- /dev/null
+++ b/test/asp/nested.asp
@@ -0,0 +1,7 @@
+
+
diff --git a/test/asp/relative.asp b/test/asp/relative.asp
new file mode 100644
index 0000000..6ba6c11
--- /dev/null
+++ b/test/asp/relative.asp
@@ -0,0 +1,7 @@
+
+
diff --git a/test/asp/transitive.asp b/test/asp/transitive.asp
new file mode 100644
index 0000000..671a271
--- /dev/null
+++ b/test/asp/transitive.asp
@@ -0,0 +1,7 @@
+
+
diff --git a/test/test.asp b/test/test.asp
deleted file mode 100644
index 37b6332..0000000
--- a/test/test.asp
+++ /dev/null
@@ -1,2 +0,0 @@
-
-
diff --git a/test/test.js b/test/test.js
deleted file mode 100644
index 26d619e..0000000
--- a/test/test.js
+++ /dev/null
@@ -1,27 +0,0 @@
-// commonjscript test runner
-
-require.paths.push("../lib"); // to get system module
-
-var print = require("system").print,
- modulesSpecVersion = "1.0";
- tests = ["absolute",
- "cyclic",
- "determinism",
- "exactExports",
- "hasOwnProperty",
- "method",
- "missing",
- "monkeys",
- "nested",
- "relative",
- "transitive"];
-
-function run(test) {
- require.paths = ["commonjs/tests/modules/" + modulesSpecVersion + "/" +
- test];
- print("-- " + test + "--")
- require("program");
- print("");
-}
-
-for (var i = 0; i < tests.length; i += 1) { run(tests[i]); }
diff --git a/test/test.wsf b/test/test.wsf
deleted file mode 100644
index ceb42c8..0000000
--- a/test/test.wsf
+++ /dev/null
@@ -1,4 +0,0 @@
-
-
-
-