Added Rhino-Require subtree.

This commit is contained in:
Michael Mathews 2011-05-07 15:14:34 +01:00
commit 2d7f690096
13 changed files with 2527 additions and 0 deletions

View File

@ -0,0 +1,9 @@
Rhino-Require
====
This is require() for rhino.
A nodejs-compatible implementation of the commonjs require function, implemented
in code compatible with the Mozilla Rhino JavaScript engine.
Written by Michael Mathews. Licensed as public domain.

View File

@ -0,0 +1,253 @@
/*
Rhino-Require is Public Domain
<http://en.wikipedia.org/wiki/Public_Domain>
The author or authors of this code dedicate any and all copyright interest
in this code to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and successors. We
intend this dedication to be an overt act of relinquishment in perpetuity of
all present and future rights to this code under copyright law.
*/
(function(global) {
var require = global.require = function(id) { /*debug*///console.log('require('+id+')');
var moduleContent = '',
moduleUri;
moduleUri = require.resolve(id);
moduleContent = '';
var file = new java.io.File(moduleUri);
try {
var scanner = new java.util.Scanner(file).useDelimiter("\\Z");
moduleContent = String( scanner.next() );
}
catch(e) {
throw 'Unable to read file at: '+moduleUri+', '+e;
}
if (moduleContent) {
try {
var f = new Function('require', 'exports', 'module', moduleContent),
exports = require.cache[moduleUri] || {},
module = { id: id, uri: moduleUri, exports: exports };
require._root.unshift(moduleUri);
f.call({}, require, exports, module);
require._root.shift();
}
catch(e) {
throw 'Unable to require source code from "' + moduleUri + '": ' + e.toSource();
}
exports = module.exports || exports;
require.cache[id] = exports;
}
else {
throw 'The requested module cannot be returned: no content for id: "' + id + '" in paths: ' + require.paths.join(', ');
}
return exports;
}
require._root = [''];
require.paths = [];
require.cache = {}; // cache module exports. Like: {id: exported}
/** Given a module id, try to find the path to the associated module.
*/
require.resolve = function(id) {
// TODO: 1. load node core modules
// 2. dot-relative module id, like './foo/bar'
var parts = id.match(/^(\.?\.\/|\/)(.+)$/),
isRelative = false,
isAbsolute = false,
basename = id;
if (parts) {
isRelative = parts[1] === './' || parts[1] === '../';
isAbsolute = parts[1] === '/';
basename = parts[2];
}
if (typeof basename !== 'undefined') {
if (isAbsolute) {
rootedId = id;
}
else {
var root = (isRelative? toDir(require._root[0] || '.') : '.'),
rootedId = (root + '/' + id).replace(/\/[^\/]+\/\.\.\//g, '/').replace(/\/\.\//g, '/'),
uri = '';
}
if ( uri = loadAsFile(rootedId) ) { }
else if ( uri = loadAsDir(rootedId) ) { }
else if ( uri = loadNodeModules(rootedId) ) { }
else if ( uri = nodeModulesPaths(rootedId, 'rhino_modules') ) { }
else if ( uri = nodeModulesPaths(rootedId, 'node_modules') ) { }
if (uri !== '') return toAbsolute(uri);
throw 'Require Error: Not found.';
}
}
/** Given a path, return the base directory of that path.
@example toDir('/foo/bar/somefile.js'); => '/foo/bar'
*/
function toDir(path) {
var file = new java.io.File(path);
if (file.isDirectory()) {
return path;
}
var parts = path.split(/[\\\/]/);
parts.pop();
return parts.join('/');
}
/** Returns true if the given path exists and is a file.
*/
function isFile(path) {
var file = new java.io.File(path);
if (file.isFile()) {
return true;
}
return false;
}
/** Returns true if the given path exists and is a directory.
*/
function isDir(path) {
var file = new java.io.File(path);
if (file.isDirectory()) {
return true;
}
return false;
}
/** Get the path of the current working directory
*/
function getCwd() {
return toDir( ''+new java.io.File('.').getAbsolutePath() ).replace(/\/\.$/, '');
}
function toAbsolute(relPath) {
absPath = ''+new java.io.File(relPath).getAbsolutePath();
absPath = absPath.replace(/\/[^\/]+\/\.\.\//g, '/').replace(/\/\.\//g, '/');
return absPath;
}
/** Assume the id is a file, try to find it.
*/
function loadAsFile(id) {
if ( isFile(id) ) { return id; }
if ( isFile(id+'.js') ) { return id+'.js'; }
if ( isFile(id+'.node') ) { throw 'Require Error: .node files not supported'; }
}
/** Assume the id is a directory, try to find a module file within it.
*/
function loadAsDir(id) {
if (!isDir(id)) {
return;
}
// look for the "main" property of the package.json file
if ( isFile(id+'/package.json') ) {
var packageJson = readFileSync(id+'/package.json', 'utf-8');
eval( 'packageJson = '+ packageJson);
if (packageJson.hasOwnProperty('main')) {
var main = (id + '/' + packageJson.main).replace(/\/\.?\//g, '/');
return require.resolve(main);
}
}
if ( isFile(id+'/index.js') ) {
return id+'/index.js';
}
}
function loadNodeModules(id) {
var path,
uri;
for (var i = 0, len = require.paths.length; i < len; i++) {
path = require.paths[i];
if (isDir(path)) {
path = (path + '/' + id).replace(/\/\.?\//g, '/');
uri = loadAsFile(path);
if (typeof uri !== 'undefined') {
return uri;
}
uri = loadAsDir(path);
if (typeof uri !== 'undefined') {
return uri;
}
}
}
}
function nodeModulesPaths(id, moduleFolder) {
var cwd = getCwd(),
dirs = cwd.split('/'),
dir,
path,
filename,
uri;
while (dirs.length) {
dir = dirs.join('/');
path = dir+'/'+moduleFolder;
if ( isDir(path) ) {
filename = (path+'/'+id).replace(/\/\.?\//g, '/');
if ( uri = loadAsFile(filename) ) {
uri = uri.replace(cwd, '.');
return uri;
}
if ( uri = loadAsDir(filename) ) {
uri = uri.replace(cwd, '.');
return uri;
}
}
dirs.pop();
}
}
function readFileSync(filename, encoding, callback) {
if (typeof arguments[1] === 'function') {
encoding = null;
callback = arguments[1];
}
encoding = encoding || java.lang.System.getProperty('file.encoding');
try {
var content = new java.util.Scanner(
new java.io.File(filename),
encoding
).useDelimiter("\\Z");
return String( content.next() );
}
catch (e) {
return '';
}
}
})(this);

View File

@ -0,0 +1,5 @@
To run the tests, change your current working directory to this test folder.
Then:
java -classpath js.jar org.mozilla.javascript.tools.shell.Main run.js

View File

@ -0,0 +1,377 @@
/*
* JSMock 1.2.2, a mock object library for JavaScript
* Copyright (C) 2006 Justin DeWind
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
JSMock = {
extend: function(object) {
var mockControl = new MockControl();
object.createMock = function(objectToMock) {return mockControl.createMock(objectToMock)};
object.resetMocks = function() {mockControl.reset()};
object.verifyMocks = function() {mockControl.verify()};
if(!object.tearDown) {
object.tearDown = function() {
object.verifyMocks();
}
}
else if(object.tearDown.constructor == Function) {
object.__oldTearDown__ = object.tearDown;
object.tearDown = function() {
object.__oldTearDown__();
object.verifyMocks();
}
}
}
}
function MockControl() {
this.__expectationMatcher = new ExpectationMatcher();
this.__lastMock = null;
this.__lastCallName = null;
}
MockControl.prototype = {
createMock: function(objectToMock) {
var mock = { calls: [], expects: function() {this.__recording = true; return this}, __recording: false};
mock.expect = mock.expects;
if(objectToMock != null) {
if( typeof(objectToMock) == 'function' ) {
this.__createMethods(objectToMock, mock);
this.__createMethods(new objectToMock(), mock);
}
else if( typeof(objectToMock) == 'object') {
this.__createMethods(objectToMock, mock);
}
else {
throw new Error("Cannot mock out a " + typeof(objectToMock));
}
}
var self = this;
mock.addMockMethod = function(method) { self.__createMethod(self, mock, method); }
return mock;
},
andReturn: function(returnValue) {
this.__verifyLastMockNotNull("Cannot set return value without an expectation");
this.__initializeReturnExpectationForMock();
this.__lastMock.calls[this.__lastCallName].push( function() { return returnValue; });
},
andThrow: function(throwMsg) {
this.__verifyLastMockNotNull("Cannot throw error without an expectation");
this.__initializeReturnExpectationForMock();
this.__lastMock.calls[this.__lastCallName].push( function() { throw new Error(throwMsg); });
},
andStub: function(block) {
this.__verifyLastMockNotNull("Cannot stub without an expectation");
if( typeof(block) != 'function') {
throw new Error("Stub must be a function");
}
this.__initializeReturnExpectationForMock();
this.__lastMock.calls[this.__lastCallName].push( function() { return block.apply(this, arguments); });
},
reset: function() {
this.__expectationMatcher.reset();
},
verify: function() {
if(!this.__expectationMatcher.matches())
{
discrepancy = this.__expectationMatcher.discrepancy();
message = discrepancy.message;
method = discrepancy.behavior.method
formattedArgs = ArgumentFormatter.format(discrepancy.behavior.methodArguments);
this.__expectationMatcher.reset();
throw new Error(message + ": " + method + "(" + formattedArgs + ")");
}
else {
this.__expectationMatcher.reset();
}
},
__createMethods: function(object, mock) {
for( property in object ) {
if( this.__isPublicMethod(object, property) ) {
this.__createMethod( this, mock, property );
}
}
},
__createMethod: function(control, mock, method) {
mock[method] =
function() {
if( mock.__recording ) {
control.__lastMock = mock;
control.__lastCallName = method;
control.__expectationMatcher.addExpectedMethodCall( mock, method, arguments );
mock.__recording = false;
return control;
}
else {
control.__expectationMatcher.addActualMethodCall( mock, method, arguments );
if( mock.calls[method] != null) {
returnValue = mock.calls[method].shift();
if( typeof(returnValue) == 'function') {
return returnValue.apply(this, arguments);
}
}
}
}
},
__isPublicMethod: function(object, property) {
try {
return typeof(object[property]) == 'function' && property.charAt(0) != "_";
} catch(e) {
return false;
}
},
__verifyLastMockNotNull: function(throwMsg) {
if(this.__lastMock == null) {
throw new Error(throwMsg);
}
},
__initializeReturnExpectationForMock: function() {
if(typeof(this.__lastMock.calls[this.__lastCallName]) == 'undefined') {
this.__lastMock.calls[this.__lastCallName] = [];
}
}
}
function ExpectationMatcher() {
this.__expectationBehaviorList = [];
this.__actualBehaviorList = [];
this.__discrepancy = null;
}
ExpectationMatcher.prototype = {
addExpectedMethodCall: function(caller, method, methodArguments ) {
this.__expectationBehaviorList.push(new InvocationBehavior(caller, method, methodArguments));
},
addActualMethodCall: function(caller, method, methodArguments ) {
this.__actualBehaviorList.push(new InvocationBehavior(caller, method, methodArguments));
},
matches: function() {
var self = this;
var matches = true;
this.__expectationBehaviorList.eachIndexForJsMock(function(index, expectedBehavior) {
var actualBehavior = (self.__actualBehaviorList.length > index) ? self.__actualBehaviorList[index] : null;
if(matches) {
if( actualBehavior === null ) {
self.__discrepancy = new Discrepancy("Expected function not called", expectedBehavior);
matches = false;
}
else if( expectedBehavior.method != actualBehavior.method ) {
self.__discrepancy = new Discrepancy("Surprise call", actualBehavior);
matches = false;
}
else if( expectedBehavior.caller != actualBehavior.caller ) {
self.__discrepancy = new Discrepancy("Surprise call from unexpected caller", actualBehavior);
matches = false;
}
else if( !self.__matchArguments(expectedBehavior.methodArguments, actualBehavior.methodArguments) ) {
self.__discrepancy = new Discrepancy("Unexpected Arguments", actualBehavior);
matches = false;
}
}
});
if( this.__actualBehaviorList.length > this.__expectationBehaviorList.length && matches ) {
this.__discrepancy = new Discrepancy("Surprise call", this.__actualBehaviorList[this.__expectationBehaviorList.length]);
matches = false
}
return matches;
},
reset: function() {
this.__expectationBehaviorList = [];
this.__actualBehaviorList = [];
this.__discrepancy = null;
},
discrepancy: function() {
return this.__discrepancy;
},
__matchArguments: function(expectedArgs, actualArgs) {
var expectedArray = this.__convertArgumentsToArray(expectedArgs);
var actualArray = this.__convertArgumentsToArray(actualArgs);
return ArgumentMatcher.matches(expectedArray, actualArray);
},
__convertArgumentsToArray: function(args) {
var convertedArguments = [];
for(var i = 0; i < args.length; i++) {
convertedArguments[i] = args[i];
}
return convertedArguments;
}
}
function InvocationBehavior(caller, method, methodArguments) {
this.caller = caller;
this.method = method;
this.methodArguments = methodArguments;
}
function TypeOf(type) {
if(typeof(type) != 'function')
throw new Error("Can only take constructors");
this.type = type;
}
TypeOf.isA = function(type) { return new TypeOf(type); };
ArgumentMatcher = {
matches: function(expected, actual) {
return this.__delegateMatching(expected, actual);
},
__delegateMatching: function(expected, actual) {
if( expected == null ) {
return this.__match( expected, actual );
}
else if( expected.constructor == TypeOf ) {
return this.__match(expected.type, actual.constructor);
}
else if( expected.constructor == Array ) {
return this.__matchArrays(expected, actual);
}
else {
return this.__match(expected, actual);
}
},
__match: function(expected, actual) {
return ( expected == actual );
},
__matchArrays: function(expected, actual) {
if ( actual == null)
return false;
if( actual.constructor != Array)
return false;
if( expected.length != actual.length )
return false;
for(var i = 0; i < expected.length; i++ ) {
if( !this.__delegateMatching(expected[i], actual[i]) )
return false;
}
return true;
}
}
function Discrepancy(message, behavior) {
if(behavior.constructor != InvocationBehavior)
throw new Error("The behavior can only be an InvocationBehavior object");
this.message = message;
this.behavior = behavior;
}
ArgumentFormatter = {
format: function(args) {
var formattedArgs = "";
for(var i = 0; i < args.length; i++) {
if( args[i] == null ) {
formattedArgs += ( formattedArgs == "" ) ? "null" : ", " + "null";
}
else if( args[i].constructor == TypeOf || args[i].constructor == Function) {
var func = ( args[i].constructor == TypeOf ) ? args[i].type : args[i];
formattedArgs += ( formattedArgs == "" ) ? this.__formatFunction(func) : ", " + this.__formatFunction(func);
}
else if( typeof(args[i]) == "string" ) {
formattedArgs += ( formattedArgs == "" ) ? "\"" + args[i].toString() + "\"" : ", \"" + args[i].toString() + "\""
}
else if( args[i].constructor == Array ) {
formattedArgs += ( formattedArgs == "" ) ? "[" + this.format(args[i]) + "]" : ", [" + this.format(args[i]) + "]";
}
else {
formattedArgs += ( formattedArgs == "" ) ? args[i].toString() : ", " + args[i].toString();
}
}
return formattedArgs;
},
__formatFunction: function(func) {
// Manual checking is done for internal/native functions
// since Safari will not display them correctly
// for the intended regex parsing.
if(func == Array) {
return "Array";
} else if(func == Date) {
return "Date";
} else if(func == Object) {
return "Object";
} else if(func == String) {
return "String";
} else if(func == Function) {
return "Function";
} else if(func == RegExp) {
return "RegExp";
} else if(func == Error) {
return "Error";
} else if(func == Number) {
return "Number";
} else if(func == Boolean) {
return "Boolean";
}
var formattedFunc = func.toString().match(/function (\w+)/);
return ( formattedFunc == null ) ? "{{Closure}}" : formattedFunc[1];
}
}
/* Helpers */
// Implemented each method with a unique name to avoid conflicting
// with other libraries that implement it.
Array.prototype.eachIndexForJsMock = function(block) {
for(var index = 0; index < this.length; index++)
{
block(index, this[index]);
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,2 @@
exports.name = 'bar';
exports.extras = require('./extras');

View File

@ -0,0 +1 @@
exports.name = 'extras';

View File

@ -0,0 +1,10 @@
{
"name": "my-test-package",
"version": "1.2.3",
"description": "Blah blah blah",
"keywords": [
"package",
"example"
],
"main": "./myModuleLib/bar.js"
}

View File

View File

0
lib/Rhino-Require/test/node_modules/baz/index.js generated vendored Normal file
View File

0
lib/Rhino-Require/test/node_modules/foobar.js generated vendored Normal file
View File

View File

@ -0,0 +1,108 @@
// USAGE: java -classpath ~/Scripts/js.jar org.mozilla.javascript.tools.shell.Main test.js
load('./lib/nodeunit.js');
//load('lib/jsmock.js');
load('../src/require.js');
module = typeof module === 'undefined'? {} : module;
var test = module.exports = {
'Basic tests.': {
'The require function should be defined.': function(t) {
t.expect(1);
t.equal( typeof require, 'function' );
t.done();
}
},
'The require.resolve function.': {
'The require.resolve function should be defined.': function(t) {
t.expect(1);
t.equal( typeof require.resolve, 'function' );
t.done();
},
'When an id starts with "./" it should resolve relative to the current working directory.': function(t) {
t.expect(1);
t.equal( require.resolve( './mock/foo'), toAbsolute('./mock/foo.js') );
t.done();
},
'When an id starts with "./" it should resolve relative to the current running module.': function(t) {
t.expect(1);
require._root.unshift('./mock/bar.js');
t.equal( require.resolve('./foo'), toAbsolute('./mock/foo.js') );
require._root.shift();
t.done();
},
'When an id does not start with "./" it should resolve relative to the cwd.': function(t) {
t.expect(1);
require._root.unshift('blah/one/two.js');
t.equal( require.resolve('mock/foo'), toAbsolute('./mock/foo.js') );
require._root.shift();
t.done();
}
},
'Resolve from package.json.': {
'The require.resolve function should use the "main" property from package.json.': function(t) {
t.expect(1);
t.equal( require.resolve('./mock/bar'), toAbsolute('./mock/bar/myModuleLib/bar.js') );
t.done();
}
},
'Resolve from index file.': {
'The require.resolve function should use the "index.js" file.': function(t) {
t.expect(1);
t.equal( require.resolve('./mock/zop'), toAbsolute('./mock/zop/index.js') );
t.done();
}
},
'Resolve from require.paths.': {
'The require.resolve function should use the require.paths values.': function(t) {
t.expect(1);
require.paths.push('./mock');
t.equal( require.resolve('foo'), toAbsolute('./mock/foo.js') );
require.paths.pop();
t.done();
}
},
'Resolve from node_modules.': {
'The require.resolve function should use the node_modules dir.': function(t) {
t.expect(1);
t.equal( require.resolve('foobar'), toAbsolute('./node_modules/foobar.js') );
t.done();
},
'The require.resolve function should look for index in node_modules dir.': function(t) {
t.expect(1);
t.equal( require.resolve('baz'), toAbsolute('./node_modules/baz/index.js') );
t.done();
}
},
'Require from package.json.': {
'The required module should be returned when it is listed in package.json.': function(t) {
t.expect(3);
var bar = require('mock/bar');
t.equal( typeof bar, 'object' );
t.equal( bar.name, 'bar' );
t.equal( bar.extras.name, 'extras' );
t.done();
},
'The required dot-relative module should be returned when it is listed in package.json.': function(t) {
t.expect(2);
var bar = require('./mock/bar');
t.equal( typeof bar, 'object' );
t.equal( bar.name, 'bar' );
t.done();
}
}
};
var cwd = (typeof __dirname === 'undefined')? ''+new java.io.File('.').getAbsolutePath() : __dirname;
function toAbsolute(relPath) {
if ( /^\//.test(relPath) ) return relPath;
relPath = relPath.replace(/^\.\//, '');
var absPath = cwd + '/' + relPath;
absPath = absPath.replace(/\/[^\/]+\/\.\.\//g, '/').replace(/\/\.\//g, '/');
return absPath;
}
nodeunit.run(test);