diff --git a/docs/API-Reference.md b/docs/API-Reference.md index e407e9d..047aa10 100644 --- a/docs/API-Reference.md +++ b/docs/API-Reference.md @@ -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 ------- diff --git a/src/main/javascript/core/_require.js b/src/main/javascript/core/_require.js new file mode 100644 index 0000000..11501c0 --- /dev/null +++ b/src/main/javascript/core/_require.js @@ -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("./")); +}) diff --git a/src/main/javascript/core/_scriptcraft.js b/src/main/javascript/core/_scriptcraft.js index e7aecdd..b874a44 100644 --- a/src/main/javascript/core/_scriptcraft.js +++ b/src/main/javascript/core/_scriptcraft.js @@ -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.