diff --git a/Objective-J/Executable.js b/Objective-J/Executable.js
index f3c4423ac..6a1a2591c 100644
--- a/Objective-J/Executable.js
+++ b/Objective-J/Executable.js
@@ -35,7 +35,11 @@ function Executable(/*String*/ aCode, /*Array*/ fileDependencies, /*CFURL|String
this._function = aFunction || NULL;
this._URL = makeAbsoluteURL(aURL || new CFURL("(Anonymous" + (AnonymousExecutableCount++) + ")"));
- this._fileDependencies = fileDependencies;
+ this._fileDependencies = fileDependencies || [];
+
+#ifndef COMMONJS
+ this._fileDependencies.push.apply(this._fileDependencies, parseRequireFileDependencies(aCode));
+#endif
if (fileDependencies.length)
{
@@ -53,6 +57,23 @@ function Executable(/*String*/ aCode, /*Array*/ fileDependencies, /*CFURL|String
exports.Executable = Executable;
+function parseRequireFileDependencies(aCode)
+{
+ var dependencies = [];
+ if (aCode)
+ {
+ var pattern = /(?:^|[^\w.])require\s*\(\s*["']([^"']+)["']\s*\)/g;
+ while (match = pattern.exec(aCode))
+ {
+ var id = match[1];
+ if (!(/\.[^\/]+$/).test(id))
+ id += ".js";
+ dependencies.push(new FileDependency(new CFURL(id), (/^[.\/]/).test(id)));
+ }
+ }
+ return dependencies;
+}
+
Executable.prototype.path = function()
{
return this.URL().path();
@@ -73,6 +94,8 @@ Executable.prototype.functionParameters = function()
#ifdef COMMONJS
functionParameters = functionParameters.concat("require", "exports", "module", "system", "print", "window");
+#else
+ functionParameters = functionParameters.concat("require", "exports", "module");
#endif
return functionParameters;
@@ -86,11 +109,65 @@ Executable.prototype.functionArguments = function()
#ifdef COMMONJS
functionArguments = functionArguments.concat(Executable.commonJSArguments());
+#else
+ functionArguments = functionArguments.concat(this.getCommonJSRequire(), this.getCommonJSExports(), this.getCommonJSModule());
#endif
return functionArguments;
}
+Executable.prototype.getCommonJSRequire = function()
+{
+ if (!this._commonJSRequire)
+ {
+ var executor = this.fileExecuter();
+ var importer = this.fileImporter();
+
+ var req = function(id)
+ {
+ if (!(/\.[^\/]+$/).test(id))
+ id += ".js";
+
+ return executor(id, (id.charAt(0) === "." || id.charAt(0) === "/"), NO);
+ }
+ req.async = function(id, callback) {
+ if (!(/\.[^\/]+$/).test(id))
+ id += ".js";
+
+ importer(id, (id.charAt(0) === "." || id.charAt(0) === "/"), function() {
+ callback(req(id));
+ });
+ };
+ req.paths = OBJJ_INCLUDE_PATHS;
+ this._commonJSRequire = req;
+ }
+ return this._commonJSRequire;
+}
+
+Executable.prototype.getCommonJSExports = function()
+{
+ if (!this._commonJSExports)
+ this._commonJSExports = {};
+ return this._commonJSExports;
+}
+
+Executable.prototype.setCommonJSExports = function(commonJSExports)
+{
+ if (this._commonJSExports)
+ throw "CommonJS exports for " + this.URL() + " already set.";
+ this._commonJSExports = commonJSExports;
+}
+
+Executable.prototype.getCommonJSModule = function()
+{
+ if (!this._commonJSModule)
+ this._commonJSModule = {
+ path : this.path(),
+ url : this.URL().toString()
+ };
+ return this._commonJSModule;
+}
+
DISPLAY_NAME(Executable.prototype.functionArguments);
#ifdef COMMONJS
@@ -350,14 +427,36 @@ Executable.fileExecuterForURL = function(/*CFURL|String*/ aURL)
{
cachedFileExecuter = function(/*CFURL*/ aURL, /*BOOL*/ isQuoted, /*BOOL*/ shouldForce)
{
+ // CommonJS "exports" support:
+ // this is a little hacky. we use the internaly created "exports" if it executes synchronously,
+ // otherwise we have to create our own exports object that we can return immediately
+ // then set it on the executable right before it is eventually executed.
+ var commonJSExports = {},
+ hasSetExports = false,
+ hasReturnedExports = false;
+
Executable.fileExecutableSearcherForURL(referenceURL)(aURL, isQuoted,
function(/*FileExecutable*/ aFileExecutable)
{
if (!aFileExecutable.hasLoadedFileDependencies())
throw "No executable loaded for file at URL " + aURL;
+ if (hasReturnedExports && !hasSetExports)
+ {
+ aFileExecutable.setCommonJSExports(commonJSExports);
+ hasSetExports = true;
+ }
+
aFileExecutable.execute(shouldForce);
+
+ if (!hasReturnedExports)
+ {
+ commonJSExports = aFileExecutable.getCommonJSExports();
+ }
});
+
+ hasReturnedExports = true;
+ return commonJSExports;
}
cachedFileExecuters[referenceURLString] = cachedFileExecuter;
diff --git a/Tests/Manual/CommonJSModules/Info.plist b/Tests/Manual/CommonJSModules/Info.plist
new file mode 100644
index 000000000..666ac58f1
--- /dev/null
+++ b/Tests/Manual/CommonJSModules/Info.plist
@@ -0,0 +1,12 @@
+
+
+
+
+ CPApplicationDelegateClass
+ AppController
+ CPBundleName
+ TestApp
+ CPPrincipalClass
+ CPApplication
+
+
diff --git a/Tests/Manual/CommonJSModules/Resources/spinner.gif b/Tests/Manual/CommonJSModules/Resources/spinner.gif
new file mode 100644
index 000000000..06dbc2bc2
Binary files /dev/null and b/Tests/Manual/CommonJSModules/Resources/spinner.gif differ
diff --git a/Tests/Manual/CommonJSModules/index-debug.html b/Tests/Manual/CommonJSModules/index-debug.html
new file mode 100644
index 000000000..2b45851b0
--- /dev/null
+++ b/Tests/Manual/CommonJSModules/index-debug.html
@@ -0,0 +1,101 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ TestApp
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Tests/Manual/CommonJSModules/index.html b/Tests/Manual/CommonJSModules/index.html
new file mode 100644
index 000000000..fc1bac23c
--- /dev/null
+++ b/Tests/Manual/CommonJSModules/index.html
@@ -0,0 +1,79 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ TestApp
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Tests/Manual/CommonJSModules/lib/system.js b/Tests/Manual/CommonJSModules/lib/system.js
new file mode 100644
index 000000000..4a051e12e
--- /dev/null
+++ b/Tests/Manual/CommonJSModules/lib/system.js
@@ -0,0 +1,5 @@
+exports.stdio = {
+ print : function() {
+ throw "shouldn't be using system.stdio.print"
+ }
+}
diff --git a/Tests/Manual/CommonJSModules/main.j b/Tests/Manual/CommonJSModules/main.j
new file mode 100644
index 000000000..9b5595f84
--- /dev/null
+++ b/Tests/Manual/CommonJSModules/main.j
@@ -0,0 +1,68 @@
+/*
+ * AppController.j
+ * TestApp
+ *
+ * Created by You on May 11, 2010.
+ * Copyright 2010, Your Company All rights reserved.
+ */
+
+// @import
+
+var url = window.location.toString();
+var dir = url.substring(0, url.lastIndexOf("/"));
+
+var testNames = [
+ "absolute",
+ "cyclic",
+ "determinism", // failing
+ "exactExports",
+ "hasOwnProperty", // failing
+ "method",
+ "missing", // failing
+ "monkeys",
+ "nested",
+ "relative",
+ "transitive"
+];
+
+// ObjectiveJ.asyncLoader = false;
+
+print = function() {
+ console.warn.apply(console, arguments);
+}
+
+function main(args, namedArgs)
+{
+ var index = parseInt(window.location.hash.substring(1));
+ if (isNaN(index))
+ index = 0;
+
+ var testName = testNames[index];
+
+ function next() {
+ window.clearNativeTimeout(timeout);
+ if (index < testNames.length - 1) {
+ window.location.hash = "#" + (index+1);
+ window.location.reload();
+ }
+ }
+
+ var timeout = window.setNativeTimeout(function() {
+ alert(testName + ": timed out")
+ next();
+ }, 2000);
+
+ try {
+ console.log("running: " + testName);
+
+ var testDir = dir + "/tests/" + testName;
+ require.paths.unshift(dir + "/lib", testDir);
+ require.async(testDir + "/program", function() {
+ alert(testName + ": completed");
+ next();
+ });
+
+ } catch (e) {
+ console.error(testName+ ": exception=" + e);
+ }
+}
diff --git a/Tests/Manual/CommonJSModules/tests/absolute/b.js b/Tests/Manual/CommonJSModules/tests/absolute/b.js
new file mode 100644
index 000000000..da5bf4fd1
--- /dev/null
+++ b/Tests/Manual/CommonJSModules/tests/absolute/b.js
@@ -0,0 +1 @@
+exports.foo = function() {};
diff --git a/Tests/Manual/CommonJSModules/tests/absolute/program.js b/Tests/Manual/CommonJSModules/tests/absolute/program.js
new file mode 100644
index 000000000..7980ecf14
--- /dev/null
+++ b/Tests/Manual/CommonJSModules/tests/absolute/program.js
@@ -0,0 +1,5 @@
+var test = require('test');
+var a = require('submodule/a');
+var b = require('b');
+test.assert(a.foo().foo === b.foo, 'require works with absolute identifiers');
+test.print('DONE', 'info');
diff --git a/Tests/Manual/CommonJSModules/tests/absolute/submodule/a.js b/Tests/Manual/CommonJSModules/tests/absolute/submodule/a.js
new file mode 100644
index 000000000..bc138b87d
--- /dev/null
+++ b/Tests/Manual/CommonJSModules/tests/absolute/submodule/a.js
@@ -0,0 +1,3 @@
+exports.foo = function () {
+ return require('b');
+};
diff --git a/Tests/Manual/CommonJSModules/tests/absolute/test.js b/Tests/Manual/CommonJSModules/tests/absolute/test.js
new file mode 100644
index 000000000..5d0984eec
--- /dev/null
+++ b/Tests/Manual/CommonJSModules/tests/absolute/test.js
@@ -0,0 +1,15 @@
+
+exports.print = typeof print !== "undefined" ? print : function () {
+ var system = require("system");
+ var stdio = system.stdio;
+ stdio.print.apply(stdio, arguments);
+};
+
+exports.assert = function (guard, message) {
+ if (guard) {
+ exports.print('PASS ' + message, 'pass');
+ } else {
+ exports.print('FAIL ' + message, 'fail');
+ }
+};
+
diff --git a/Tests/Manual/CommonJSModules/tests/cyclic/a.js b/Tests/Manual/CommonJSModules/tests/cyclic/a.js
new file mode 100644
index 000000000..e0188fa08
--- /dev/null
+++ b/Tests/Manual/CommonJSModules/tests/cyclic/a.js
@@ -0,0 +1,4 @@
+exports.a = function () {
+ return b;
+};
+var b = require('b');
diff --git a/Tests/Manual/CommonJSModules/tests/cyclic/b.js b/Tests/Manual/CommonJSModules/tests/cyclic/b.js
new file mode 100644
index 000000000..873a30577
--- /dev/null
+++ b/Tests/Manual/CommonJSModules/tests/cyclic/b.js
@@ -0,0 +1,4 @@
+var a = require('a');
+exports.b = function () {
+ return a;
+};
diff --git a/Tests/Manual/CommonJSModules/tests/cyclic/program.js b/Tests/Manual/CommonJSModules/tests/cyclic/program.js
new file mode 100644
index 000000000..2ee4758db
--- /dev/null
+++ b/Tests/Manual/CommonJSModules/tests/cyclic/program.js
@@ -0,0 +1,10 @@
+var test = require('test');
+var a = require('a');
+var b = require('b');
+
+test.assert(a.a, 'a exists');
+test.assert(b.b, 'b exists')
+test.assert(a.a().b === b.b, 'a gets b');
+test.assert(b.b().a === a.a, 'b gets a');
+
+test.print('DONE', 'info');
diff --git a/Tests/Manual/CommonJSModules/tests/cyclic/test.js b/Tests/Manual/CommonJSModules/tests/cyclic/test.js
new file mode 100644
index 000000000..5d0984eec
--- /dev/null
+++ b/Tests/Manual/CommonJSModules/tests/cyclic/test.js
@@ -0,0 +1,15 @@
+
+exports.print = typeof print !== "undefined" ? print : function () {
+ var system = require("system");
+ var stdio = system.stdio;
+ stdio.print.apply(stdio, arguments);
+};
+
+exports.assert = function (guard, message) {
+ if (guard) {
+ exports.print('PASS ' + message, 'pass');
+ } else {
+ exports.print('FAIL ' + message, 'fail');
+ }
+};
+
diff --git a/Tests/Manual/CommonJSModules/tests/determinism/program.js b/Tests/Manual/CommonJSModules/tests/determinism/program.js
new file mode 100644
index 000000000..7c0e50b0b
--- /dev/null
+++ b/Tests/Manual/CommonJSModules/tests/determinism/program.js
@@ -0,0 +1,3 @@
+var test = require('test');
+require('submodule/a');
+test.print('DONE', 'info');
diff --git a/Tests/Manual/CommonJSModules/tests/determinism/submodule/a.js b/Tests/Manual/CommonJSModules/tests/determinism/submodule/a.js
new file mode 100644
index 000000000..215ea1d9a
--- /dev/null
+++ b/Tests/Manual/CommonJSModules/tests/determinism/submodule/a.js
@@ -0,0 +1,9 @@
+var test = require('test');
+var pass = false;
+var test = require('test');
+try {
+ require('a');
+} catch (exception) {
+ pass = true;
+}
+test.assert(pass, 'require does not fall back to relative modules when absolutes are not available.')
diff --git a/Tests/Manual/CommonJSModules/tests/determinism/submodule/b.js b/Tests/Manual/CommonJSModules/tests/determinism/submodule/b.js
new file mode 100644
index 000000000..139597f9c
--- /dev/null
+++ b/Tests/Manual/CommonJSModules/tests/determinism/submodule/b.js
@@ -0,0 +1,2 @@
+
+
diff --git a/Tests/Manual/CommonJSModules/tests/determinism/test.js b/Tests/Manual/CommonJSModules/tests/determinism/test.js
new file mode 100644
index 000000000..5d0984eec
--- /dev/null
+++ b/Tests/Manual/CommonJSModules/tests/determinism/test.js
@@ -0,0 +1,15 @@
+
+exports.print = typeof print !== "undefined" ? print : function () {
+ var system = require("system");
+ var stdio = system.stdio;
+ stdio.print.apply(stdio, arguments);
+};
+
+exports.assert = function (guard, message) {
+ if (guard) {
+ exports.print('PASS ' + message, 'pass');
+ } else {
+ exports.print('FAIL ' + message, 'fail');
+ }
+};
+
diff --git a/Tests/Manual/CommonJSModules/tests/exactExports/a.js b/Tests/Manual/CommonJSModules/tests/exactExports/a.js
new file mode 100644
index 000000000..99c929e44
--- /dev/null
+++ b/Tests/Manual/CommonJSModules/tests/exactExports/a.js
@@ -0,0 +1,3 @@
+exports.program = function () {
+ return require('program');
+};
diff --git a/Tests/Manual/CommonJSModules/tests/exactExports/program.js b/Tests/Manual/CommonJSModules/tests/exactExports/program.js
new file mode 100644
index 000000000..e82bd6db2
--- /dev/null
+++ b/Tests/Manual/CommonJSModules/tests/exactExports/program.js
@@ -0,0 +1,4 @@
+var test = require('test');
+var a = require('a');
+test.assert(a.program() === exports, 'exact exports');
+test.print('DONE', 'info');
diff --git a/Tests/Manual/CommonJSModules/tests/exactExports/test.js b/Tests/Manual/CommonJSModules/tests/exactExports/test.js
new file mode 100644
index 000000000..5d0984eec
--- /dev/null
+++ b/Tests/Manual/CommonJSModules/tests/exactExports/test.js
@@ -0,0 +1,15 @@
+
+exports.print = typeof print !== "undefined" ? print : function () {
+ var system = require("system");
+ var stdio = system.stdio;
+ stdio.print.apply(stdio, arguments);
+};
+
+exports.assert = function (guard, message) {
+ if (guard) {
+ exports.print('PASS ' + message, 'pass');
+ } else {
+ exports.print('FAIL ' + message, 'fail');
+ }
+};
+
diff --git a/Tests/Manual/CommonJSModules/tests/hasOwnProperty/hasOwnProperty.js b/Tests/Manual/CommonJSModules/tests/hasOwnProperty/hasOwnProperty.js
new file mode 100644
index 000000000..e69de29bb
diff --git a/Tests/Manual/CommonJSModules/tests/hasOwnProperty/program.js b/Tests/Manual/CommonJSModules/tests/hasOwnProperty/program.js
new file mode 100644
index 000000000..1653f1b86
--- /dev/null
+++ b/Tests/Manual/CommonJSModules/tests/hasOwnProperty/program.js
@@ -0,0 +1,4 @@
+var hasOwnProperty = require('hasOwnProperty');
+var toString = require('toString');
+var test = require('test');
+test.print('DONE', 'info');
diff --git a/Tests/Manual/CommonJSModules/tests/hasOwnProperty/test.js b/Tests/Manual/CommonJSModules/tests/hasOwnProperty/test.js
new file mode 100644
index 000000000..5d0984eec
--- /dev/null
+++ b/Tests/Manual/CommonJSModules/tests/hasOwnProperty/test.js
@@ -0,0 +1,15 @@
+
+exports.print = typeof print !== "undefined" ? print : function () {
+ var system = require("system");
+ var stdio = system.stdio;
+ stdio.print.apply(stdio, arguments);
+};
+
+exports.assert = function (guard, message) {
+ if (guard) {
+ exports.print('PASS ' + message, 'pass');
+ } else {
+ exports.print('FAIL ' + message, 'fail');
+ }
+};
+
diff --git a/Tests/Manual/CommonJSModules/tests/hasOwnProperty/toString.js b/Tests/Manual/CommonJSModules/tests/hasOwnProperty/toString.js
new file mode 100644
index 000000000..e69de29bb
diff --git a/Tests/Manual/CommonJSModules/tests/method/a.js b/Tests/Manual/CommonJSModules/tests/method/a.js
new file mode 100644
index 000000000..69c48af67
--- /dev/null
+++ b/Tests/Manual/CommonJSModules/tests/method/a.js
@@ -0,0 +1,12 @@
+exports.foo = function () {
+ return this;
+};
+exports.set = function (x) {
+ this.x = x;
+};
+exports.get = function () {
+ return this.x;
+};
+exports.getClosed = function () {
+ return exports.x;
+};
diff --git a/Tests/Manual/CommonJSModules/tests/method/program.js b/Tests/Manual/CommonJSModules/tests/method/program.js
new file mode 100644
index 000000000..192811c5a
--- /dev/null
+++ b/Tests/Manual/CommonJSModules/tests/method/program.js
@@ -0,0 +1,8 @@
+var test = require('test');
+var a = require('a');
+var foo = a.foo;
+test.assert(a.foo() == a, 'calling a module member');
+test.assert(foo() == (function (){return this})(), 'members not implicitly bound');
+a.set(10);
+test.assert(a.get() == 10, 'get and set')
+test.print('DONE', 'info');
diff --git a/Tests/Manual/CommonJSModules/tests/method/test.js b/Tests/Manual/CommonJSModules/tests/method/test.js
new file mode 100644
index 000000000..5d0984eec
--- /dev/null
+++ b/Tests/Manual/CommonJSModules/tests/method/test.js
@@ -0,0 +1,15 @@
+
+exports.print = typeof print !== "undefined" ? print : function () {
+ var system = require("system");
+ var stdio = system.stdio;
+ stdio.print.apply(stdio, arguments);
+};
+
+exports.assert = function (guard, message) {
+ if (guard) {
+ exports.print('PASS ' + message, 'pass');
+ } else {
+ exports.print('FAIL ' + message, 'fail');
+ }
+};
+
diff --git a/Tests/Manual/CommonJSModules/tests/missing/program.js b/Tests/Manual/CommonJSModules/tests/missing/program.js
new file mode 100644
index 000000000..c6b03aa28
--- /dev/null
+++ b/Tests/Manual/CommonJSModules/tests/missing/program.js
@@ -0,0 +1,8 @@
+var test = require('test');
+try {
+ require('bogus');
+ test.print('FAIL require throws error when module missing', 'fail');
+} catch (exception) {
+ test.print('PASS require throws error when module missing', 'pass');
+}
+test.print('DONE', 'info');
diff --git a/Tests/Manual/CommonJSModules/tests/missing/test.js b/Tests/Manual/CommonJSModules/tests/missing/test.js
new file mode 100644
index 000000000..5d0984eec
--- /dev/null
+++ b/Tests/Manual/CommonJSModules/tests/missing/test.js
@@ -0,0 +1,15 @@
+
+exports.print = typeof print !== "undefined" ? print : function () {
+ var system = require("system");
+ var stdio = system.stdio;
+ stdio.print.apply(stdio, arguments);
+};
+
+exports.assert = function (guard, message) {
+ if (guard) {
+ exports.print('PASS ' + message, 'pass');
+ } else {
+ exports.print('FAIL ' + message, 'fail');
+ }
+};
+
diff --git a/Tests/Manual/CommonJSModules/tests/monkeys/a.js b/Tests/Manual/CommonJSModules/tests/monkeys/a.js
new file mode 100644
index 000000000..a949e1dcf
--- /dev/null
+++ b/Tests/Manual/CommonJSModules/tests/monkeys/a.js
@@ -0,0 +1 @@
+require('program').monkey = 10;
diff --git a/Tests/Manual/CommonJSModules/tests/monkeys/program.js b/Tests/Manual/CommonJSModules/tests/monkeys/program.js
new file mode 100644
index 000000000..42d67112e
--- /dev/null
+++ b/Tests/Manual/CommonJSModules/tests/monkeys/program.js
@@ -0,0 +1,4 @@
+var a = require('a');
+var test = require('test');
+test.assert(exports.monkey == 10, 'monkeys permitted');
+test.print('DONE', 'info');
diff --git a/Tests/Manual/CommonJSModules/tests/monkeys/test.js b/Tests/Manual/CommonJSModules/tests/monkeys/test.js
new file mode 100644
index 000000000..5d0984eec
--- /dev/null
+++ b/Tests/Manual/CommonJSModules/tests/monkeys/test.js
@@ -0,0 +1,15 @@
+
+exports.print = typeof print !== "undefined" ? print : function () {
+ var system = require("system");
+ var stdio = system.stdio;
+ stdio.print.apply(stdio, arguments);
+};
+
+exports.assert = function (guard, message) {
+ if (guard) {
+ exports.print('PASS ' + message, 'pass');
+ } else {
+ exports.print('FAIL ' + message, 'fail');
+ }
+};
+
diff --git a/Tests/Manual/CommonJSModules/tests/nested/a/b/c/d.js b/Tests/Manual/CommonJSModules/tests/nested/a/b/c/d.js
new file mode 100644
index 000000000..69fd28228
--- /dev/null
+++ b/Tests/Manual/CommonJSModules/tests/nested/a/b/c/d.js
@@ -0,0 +1,3 @@
+exports.foo = function () {
+ return 1;
+};
diff --git a/Tests/Manual/CommonJSModules/tests/nested/program.js b/Tests/Manual/CommonJSModules/tests/nested/program.js
new file mode 100644
index 000000000..c014b5727
--- /dev/null
+++ b/Tests/Manual/CommonJSModules/tests/nested/program.js
@@ -0,0 +1,3 @@
+var test = require('test');
+test.assert(require('a/b/c/d').foo() == 1, 'nested module identifier');
+test.print('DONE', 'info');
diff --git a/Tests/Manual/CommonJSModules/tests/nested/test.js b/Tests/Manual/CommonJSModules/tests/nested/test.js
new file mode 100644
index 000000000..5d0984eec
--- /dev/null
+++ b/Tests/Manual/CommonJSModules/tests/nested/test.js
@@ -0,0 +1,15 @@
+
+exports.print = typeof print !== "undefined" ? print : function () {
+ var system = require("system");
+ var stdio = system.stdio;
+ stdio.print.apply(stdio, arguments);
+};
+
+exports.assert = function (guard, message) {
+ if (guard) {
+ exports.print('PASS ' + message, 'pass');
+ } else {
+ exports.print('FAIL ' + message, 'fail');
+ }
+};
+
diff --git a/Tests/Manual/CommonJSModules/tests/relative/program.js b/Tests/Manual/CommonJSModules/tests/relative/program.js
new file mode 100644
index 000000000..b3e4b6ed7
--- /dev/null
+++ b/Tests/Manual/CommonJSModules/tests/relative/program.js
@@ -0,0 +1,5 @@
+var test = require('test');
+var a = require('submodule/a');
+var b = require('submodule/b');
+test.assert(a.foo == b.foo, 'a and b share foo through a relative require');
+test.print('DONE', 'info');
diff --git a/Tests/Manual/CommonJSModules/tests/relative/submodule/a.js b/Tests/Manual/CommonJSModules/tests/relative/submodule/a.js
new file mode 100644
index 000000000..42e4ca086
--- /dev/null
+++ b/Tests/Manual/CommonJSModules/tests/relative/submodule/a.js
@@ -0,0 +1 @@
+exports.foo = require('./b').foo;
diff --git a/Tests/Manual/CommonJSModules/tests/relative/submodule/b.js b/Tests/Manual/CommonJSModules/tests/relative/submodule/b.js
new file mode 100644
index 000000000..9042c16b5
--- /dev/null
+++ b/Tests/Manual/CommonJSModules/tests/relative/submodule/b.js
@@ -0,0 +1,2 @@
+exports.foo = function () {
+};
diff --git a/Tests/Manual/CommonJSModules/tests/relative/test.js b/Tests/Manual/CommonJSModules/tests/relative/test.js
new file mode 100644
index 000000000..5d0984eec
--- /dev/null
+++ b/Tests/Manual/CommonJSModules/tests/relative/test.js
@@ -0,0 +1,15 @@
+
+exports.print = typeof print !== "undefined" ? print : function () {
+ var system = require("system");
+ var stdio = system.stdio;
+ stdio.print.apply(stdio, arguments);
+};
+
+exports.assert = function (guard, message) {
+ if (guard) {
+ exports.print('PASS ' + message, 'pass');
+ } else {
+ exports.print('FAIL ' + message, 'fail');
+ }
+};
+
diff --git a/Tests/Manual/CommonJSModules/tests/transitive/a.js b/Tests/Manual/CommonJSModules/tests/transitive/a.js
new file mode 100644
index 000000000..4df7bb8d2
--- /dev/null
+++ b/Tests/Manual/CommonJSModules/tests/transitive/a.js
@@ -0,0 +1 @@
+exports.foo = require('b').foo;
diff --git a/Tests/Manual/CommonJSModules/tests/transitive/b.js b/Tests/Manual/CommonJSModules/tests/transitive/b.js
new file mode 100644
index 000000000..30ea70dd1
--- /dev/null
+++ b/Tests/Manual/CommonJSModules/tests/transitive/b.js
@@ -0,0 +1 @@
+exports.foo = require('c').foo;
diff --git a/Tests/Manual/CommonJSModules/tests/transitive/c.js b/Tests/Manual/CommonJSModules/tests/transitive/c.js
new file mode 100644
index 000000000..69fd28228
--- /dev/null
+++ b/Tests/Manual/CommonJSModules/tests/transitive/c.js
@@ -0,0 +1,3 @@
+exports.foo = function () {
+ return 1;
+};
diff --git a/Tests/Manual/CommonJSModules/tests/transitive/program.js b/Tests/Manual/CommonJSModules/tests/transitive/program.js
new file mode 100644
index 000000000..98bb99644
--- /dev/null
+++ b/Tests/Manual/CommonJSModules/tests/transitive/program.js
@@ -0,0 +1,3 @@
+var test = require('test');
+test.assert(require('a').foo() == 1, 'transitive');
+test.print('DONE', 'info');
diff --git a/Tests/Manual/CommonJSModules/tests/transitive/test.js b/Tests/Manual/CommonJSModules/tests/transitive/test.js
new file mode 100644
index 000000000..5d0984eec
--- /dev/null
+++ b/Tests/Manual/CommonJSModules/tests/transitive/test.js
@@ -0,0 +1,15 @@
+
+exports.print = typeof print !== "undefined" ? print : function () {
+ var system = require("system");
+ var stdio = system.stdio;
+ stdio.print.apply(stdio, arguments);
+};
+
+exports.assert = function (guard, message) {
+ if (guard) {
+ exports.print('PASS ' + message, 'pass');
+ } else {
+ exports.print('FAIL ' + message, 'fail');
+ }
+};
+