Adding support for 'require()' and node.js-style modules (not node modules per se - just support for the same module semantics)

This commit is contained in:
walterhiggins 2013-12-21 08:58:40 +00:00
parent 77e9d59e9b
commit f35a729ceb
3 changed files with 203 additions and 73 deletions

View file

@ -272,6 +272,53 @@ See [issue #69][issue69] for more information.
[issue69]: https://github.com/walterhiggins/ScriptCraft/issues/69
# Require - Node.js-style module loading in ScriptCraft
### (Experimental as of 2013-12-21)
Node.js is a server-side javascript environment with an excellent
module loading system based on CommonJS. Modules in Node.js are really
simple. Each module is in its own javascript file and all variables
and functions within the file are private to that file/module only. There is a very concise explanation of CommonJS modules at
http://wiki.commonjs.org/wiki/Modules/1.1.1.
If you want to export a variable or function you use the module.export
property.
For example imagine you have 3 files program.js, inc.js and math.js ...
## math.js
exports.add = function(a,b){
return a + b;
}
## inc.js
var math = require('./math');
exports.increment = function(n){
return math.add(n, 1);
}
## program.js
var inc = require('./inc').increment;
var a = 7;
a = inc(a);
print(a);
You can see from the above sample code that programs can use modules
and modules themeselves can use other modules. Modules have full
control over what functions and properties they want to provide to
others.
## Important
Although ScriptCraft now supports Node.js style modules, it does not
support node modules. Node.js and Rhino are two very different
Javascript environments. ScriptCraft uses Rhino Javascript, not
Node.js.
Drone Module
============
The Drone is a convenience class for building. It can be used for...
@ -1451,30 +1498,34 @@ String class extensions
-----------------------
The following chat-formatting methods are added to the javascript String class..
* black()
* darkblue()
* blue()
* darkgreen()
* darkaqua()
* darkred()
* purple()
* gold()
* gray()
* darkgray()
* indigo()
* brightgreen()
* green()
* aqua()
* red()
* pink()
* yellow()
* white()
* bold()
* random()
* strike()
* underline()
* italic()
* reset()
* aqua()
* black()
* blue()
* bold()
* brightgreen()
* darkaqua()
* darkblue()
* darkgray()
* darkgreen()
* purple()
* darkpurple()
* darkred()
* gold()
* gray()
* green()
* italic()
* lightpurple()
* indigo()
* green()
* red()
* pink()
* yellow()
* white()
* strike()
* random()
* magic()
* underline()
* reset()
Example
-------

View file

@ -0,0 +1,123 @@
/*************************************************************************
# Require - Node.js-style module loading in ScriptCraft
### (Experimental as of 2013-12-21)
Node.js is a server-side javascript environment with an excellent
module loading system based on CommonJS. Modules in Node.js are really
simple. Each module is in its own javascript file and all variables
and functions within the file are private to that file/module only. There is a very concise explanation of CommonJS modules at
http://wiki.commonjs.org/wiki/Modules/1.1.1.
If you want to export a variable or function you use the module.export
property.
For example imagine you have 3 files program.js, inc.js and math.js ...
## math.js
exports.add = function(a,b){
return a + b;
}
## inc.js
var math = require('./math');
exports.increment = function(n){
return math.add(n, 1);
}
## program.js
var inc = require('./inc').increment;
var a = 7;
a = inc(a);
print(a);
You can see from the above sample code that programs can use modules
and modules themeselves can use other modules. Modules have full
control over what functions and properties they want to provide to
others.
## Important
Although ScriptCraft now supports Node.js style modules, it does not
support node modules. Node.js and Rhino are two very different
Javascript environments. ScriptCraft uses Rhino Javascript, not
Node.js.
***/
(function(__plugin, __engine, verbose){
/*
wph 20131215 Experimental
*/
var _loadedModules = {};
var _require = function(parentFile, path)
{
var _canonize = function(file){
return "" + file.canonicalPath.replaceAll("\\\\","/");
};
var file = new java.io.File(parentFile, path);
if (!file.exists())
{
if (path.match(/\.js$/i)){
__plugin.logger.warning('require("' + path + '") failed. File [' + file.canonicalPath + '] not found');
return;
}else{
path = path + '.js';
file = new java.io.File(parentFile, path);
if (!file.exists()){
__plugin.logger.warning('require("' + path + '") failed. File [' + file.canonicalPath + '] not found');
return;
}
}
}
if (file.isDirectory()){
__plugin.logger.warning('require("' + path + '") directories not yet supported. ' + file.canonicalPath);
return;
}
var canonizedFilename = _canonize(file);
var moduleInfo = _loadedModules[canonizedFilename];
if (moduleInfo){
return moduleInfo;
}
if (verbose){
print("loading module " + canonizedFilename);
}
var reader = new java.io.FileReader(file);
var br = new java.io.BufferedReader(reader);
var code = "";
while ((r = br.readLine()) !== null) code += r + "\n";
var head = "(function(exports,module,require,__filename,__dirname){ ";
moduleInfo = {
loaded: false,
id: canonizedFilename,
exports: {}
};
var tail = "})";
code = head + code + tail;
_loadedModules[canonizedFilename] = moduleInfo;
moduleInfo.main = __engine.eval(code);
moduleInfo.main(moduleInfo.exports,
moduleInfo,
_requireClosure(file.parentFile),
canonizedFilename,
"" + parentFile?parentFile.canonicalPath:"");
moduleInfo.loaded = true;
return moduleInfo;
};
var _requireClosure = function(parent){
return function(path){
var module = _require(parent, path);
return module.exports;
};
};
return _requireClosure(new java.io.File("./"));
})

View file

@ -240,59 +240,15 @@ var server = org.bukkit.Bukkit.server;
if (typeof load == "function")
return ;
var _canonize = function(file){ return file.getCanonicalPath().replaceAll("\\\\","/"); };
var _canonize = function(file){
return "" + file.getCanonicalPath().replaceAll("\\\\","/");
};
var _originalScript = __script;
var parentFileObj = new java.io.File(__script).getParentFile();
var jsPluginsRootDir = parentFileObj.getParentFile();
var jsPluginsRootDirName = _canonize(jsPluginsRootDir);
/*
wph 20131215 Experimental
*/
var _loadedModules = {};
var _require = function(path)
{
var file = new java.io.File(path);
if (!file.exists()){
if (path.match(/\.js$/i)){
__plugin.logger.warning('require("' + path + '") failed. File not found');
return;
}else{
path = path + '.js';
file = new java.io.File(path);
if (!file.exists()){
__plugin.logger.warning('require("' + path + '") failed. File not found');
return;
}
}
}
if (file.isDirectory()){
__plugin.logger.warning('require("' + path + '") directories not yet supported.');
return;
}
var canonizedFilename = _canonize(file);
if (_loadedModules[canonizedFilename]){
return _loadedModules[canonizedFilename];
}
if (verbose){
print("loading module " + canonizedFilename);
}
var reader = new java.io.FileReader(file);
var br = new java.io.BufferedReader(reader);
var code = "";
var module = {id: canonizedFilename};
while ((r = br.readLine()) !== null) code += r + "\n";
var head = "var result = {};(function(exports,module){ ";
var tail = "; return exports;}(result," + JSON.stringify(module) + "))";
code = head + code + tail;
_loadedModules[canonizedFilename] = __engine.eval(code);
return _loadedModules[canonizedFilename];
};
global.require = _require;
var _loaded = {};
@ -331,7 +287,6 @@ var server = org.bukkit.Bukkit.server;
} else {
while ((r = br.readLine()) !== null) code += r + "\n";
}
result = __engine.eval(code);
_loaded[canonizedFilename] = result || true;
}catch (e){
@ -829,7 +784,8 @@ See [issue #69][issue69] for more information.
global._onTabComplete = __onTabCompleteJS;
global.addUnloadHandler = _addUnloadHandler;
var fnRequire = load(jsPluginsRootDirName + '/core/_require.js',true);
global.require = fnRequire(__plugin, __engine, verbose);
//
// assumes this was loaded from js-plugins/core/
// load all of the plugins.