Provide more helpful errors when trying to require non-existent modules

This commit is contained in:
walterhiggins 2015-05-31 16:44:42 +01:00
parent 9eb95113c2
commit 4a1c1b7b01
4 changed files with 131 additions and 115 deletions

29
src/main/js/lib/find.js Normal file
View file

@ -0,0 +1,29 @@
'use strict';
var File = java.io.File;
module.exports = function find(dir, filter) {
var result = [];
function recurse( dir, store ) {
var files,
len,
i,
file,
dirfile = new File( dir );
if ( typeof filter == 'undefined' ) {
files = dirfile.list();
} else {
files = dirfile.list(filter);
}
len = files.length; i = 0;
for (; i < len; i++){
file = new File( dir + '/' + files[i] );
if ( file.isDirectory() ) {
recurse( file.canonicalPath, store );
} else {
store.push( ('' + file.canonicalPath).replace(/\\\\/g,'/') );
}
}
}
recurse( dir, result );
return result;
};

View file

@ -1,14 +1,15 @@
'use strict'; 'use strict';
/*global persist,exports,config,__plugin,require*/ /*global persist,exports,config,__plugin,require*/
var File = java.io.File, var File = java.io.File,
FileWriter = java.io.FileWriter, FileWriter = java.io.FileWriter,
PrintWriter = java.io.PrintWriter; PrintWriter = java.io.PrintWriter,
find = require('./find');
/* /*
plugin management plugin management
*/ */
var _plugins = {}; var _plugins = {};
var _plugin = function(/* String */ moduleName, /* Object */ moduleObject, isPersistent ) { function _plugin(/* String */ moduleName, /* Object */ moduleObject, isPersistent ) {
// //
// don't load plugin more than once // don't load plugin more than once
// //
@ -26,67 +27,46 @@ var _plugin = function(/* String */ moduleName, /* Object */ moduleObject, isPer
moduleObject.store = persist( moduleName, moduleObject.store ); moduleObject.store = persist( moduleName, moduleObject.store );
} }
return moduleObject; return moduleObject;
}; }
exports.plugin = _plugin; function _autoload( context, pluginDir, options ) {
exports.autoload = function( context, pluginDir, options ) {
var _canonize = function( file ) {
return '' + file.canonicalPath.replaceAll('\\\\','/');
};
/*
recursively walk the given directory and return a list of all .js files
*/
var _listSourceFiles = function( store, dir ) {
var files = dir.listFiles(),
file;
if ( !files ) {
return;
}
for ( var i = 0; i < files.length; i++ ) {
file = files[i];
if ( file.isDirectory( ) ) {
_listSourceFiles( store, file );
}else{
if ( file.canonicalPath.endsWith( '.js' ) ) {
store.push( file );
}
}
}
};
/* /*
Reload all of the .js files in the given directory Reload all of the .js files in the given directory
*/ */
(function( pluginDir ) { var sourceFiles = [],
var sourceFiles = [],
property, property,
module, module,
pluginPath; pluginPath;
_listSourceFiles( sourceFiles, pluginDir ); sourceFiles = find(pluginDir);
var len = sourceFiles.length; var len = sourceFiles.length;
if ( config && config.verbose ) { if ( config && config.verbose ) {
console.info( len + ' scriptcraft plugins found in ' + pluginDir ); console.info( len + ' scriptcraft plugins found in ' + pluginDir );
}
for ( var i = 0; i < len; i++ ) {
pluginPath = sourceFiles[i];
if (!pluginPath.match(/\.js$/)){
continue;
} }
module = {};
for ( var i = 0; i < len; i++ ) { try {
module = require( pluginPath , options);
pluginPath = _canonize( sourceFiles[i] ); for ( property in module ) {
module = {}; /*
all exports in plugins become members of context object
try { */
module = require( pluginPath , options); context[property] = module[property];
for ( property in module ) {
/*
all exports in plugins become members of context object
*/
context[property] = module[property];
}
} catch ( e ) {
var msg = 'Plugin ' + pluginPath + ' ' + e ;
console.error( msg );
} }
} catch ( e ) {
var msg = 'Plugin ' + pluginPath + ' ' + e ;
console.error( msg );
} }
}(pluginDir)); }
};
}
exports.plugin = _plugin;
exports.autoload = _autoload;

View file

@ -65,8 +65,20 @@ module specification, the '.js' suffix is optional.
var File = java.io.File, var File = java.io.File,
FileReader = java.io.FileReader, FileReader = java.io.FileReader,
BufferedReader = java.io.BufferedReader; BufferedReader = java.io.BufferedReader;
function fileExists( file ) {
if ( file.isDirectory() ) {
return readModuleFromDirectory( file );
} else {
return file;
}
}
function _canonize(file){
return "" + file.canonicalPath.replaceAll("\\\\","/");
}
var readModuleFromDirectory = function( dir ) { function readModuleFromDirectory( dir ) {
// look for a package.json file // look for a package.json file
var pkgJsonFile = new File( dir, './package.json' ); var pkgJsonFile = new File( dir, './package.json' );
@ -87,19 +99,8 @@ module specification, the '.js' suffix is optional.
return null; return null;
} }
} }
}; }
var fileExists = function( file ) {
if ( file.isDirectory() ) {
return readModuleFromDirectory( file );
} else {
return file;
}
};
var _canonize = function(file){
return "" + file.canonicalPath.replaceAll("\\\\","/");
};
/********************************************************************** /**********************************************************************
### module name resolution ### module name resolution
@ -135,7 +136,7 @@ When resolving module names to file paths, ScriptCraft uses the following rules.
3.2 if no package.json file exists then look for an index.js file in the directory 3.2 if no package.json file exists then look for an index.js file in the directory
***/ ***/
var resolveModuleToFile = function ( moduleName, parentDir ) { function resolveModuleToFile( moduleName, parentDir ) {
var file = new File(moduleName), var file = new File(moduleName),
i = 0, i = 0,
pathWithJSExt, pathWithJSExt,
@ -179,13 +180,11 @@ When resolving module names to file paths, ScriptCraft uses the following rules.
} }
} }
return null; return null;
}; }
var _loadedModules = {};
var _format = java.lang.String.format;
/* /*
require() function implementation require() function implementation
*/ */
var _require = function( parentFile, path, options ) { function _require( parentFile, path, options ) {
var file, var file,
canonizedFilename, canonizedFilename,
moduleInfo, moduleInfo,
@ -209,6 +208,29 @@ When resolving module names to file paths, ScriptCraft uses the following rules.
if (! ( (''+path).match( /^\./ ) ) ) { if (! ( (''+path).match( /^\./ ) ) ) {
errMsg = errMsg + ' and not found in paths ' + JSON.stringify(modulePaths); errMsg = errMsg + ' and not found in paths ' + JSON.stringify(modulePaths);
} }
var find = _require(parentFile, 'find').exports;
var allJS = [];
for (var i = 0;i < modulePaths.length; i++){
var js = find( modulePaths[i] );
for (var j = 0;j < js.length; j++){
if (js[j].match(/\.js$/)){
allJS.push( js[j].replace(modulePaths[i],'') );
}
}
}
var pathL = path.toLowerCase();
var candidates = [];
for (i = 0;i < allJS.length;i++){
var filenameparts = allJS[i];
var candidate = filenameparts.replace(/\.js/,'') ;
var lastpart = candidate.toLowerCase();
if (pathL.indexOf(lastpart) > -1 || lastpart.indexOf(pathL) > -1){
candidates.push(candidate);
}
}
if (candidates.length > 0){
errMsg += '\nBut found module/s named: ' + candidates.join(',') + ' - is this what you meant?';
}
throw new Error(errMsg); throw new Error(errMsg);
} }
canonizedFilename = _canonize(file); canonizedFilename = _canonize(file);
@ -285,14 +307,16 @@ When resolving module names to file paths, ScriptCraft uses the following rules.
} }
moduleInfo.loaded = true; moduleInfo.loaded = true;
return moduleInfo; return moduleInfo;
}; }
var _requireClosure = function( parent ) { function _requireClosure( parent ) {
return function( path, options ) { return function requireBoundToParent( path, options ) {
var module = _require( parent, path , options); var module = _require( parent, path , options);
return module.exports; return module.exports;
}; };
}; }
var _loadedModules = {};
var _format = java.lang.String.format;
return _requireClosure( new java.io.File(rootDir) ); return _requireClosure( new java.io.File(rootDir) );
// last line deliberately has no semicolon! // last line deliberately has no semicolon!
}) })

View file

@ -143,7 +143,7 @@ This can be useful if you write a plugin that needs to store location data since
A JSON object in the above form. A JSON object in the above form.
***/ ***/
var _locationToJSON = function( location ) { function _locationToJSON( location ) {
var yaw = __plugin.bukkit ? location.yaw : (__plugin.canary ? location.rotation : 0); var yaw = __plugin.bukkit ? location.yaw : (__plugin.canary ? location.rotation : 0);
return { return {
world: ''+location.world.name, world: ''+location.world.name,
@ -175,7 +175,7 @@ lookupTable[key] = player.name;
``` ```
***/ ***/
exports.locationToString = function( location ) { exports.locationToString = function locationToString( location ) {
return JSON.stringify( _locationToJSON( location ) ); return JSON.stringify( _locationToJSON( location ) );
}; };
exports.locationToJSON = _locationToJSON; exports.locationToJSON = _locationToJSON;
@ -190,7 +190,7 @@ returned by locationToJSON() and reconstructs and returns a bukkit
Location object. Location object.
***/ ***/
exports.locationFromJSON = function( json ) { exports.locationFromJSON = function locationFromJSON( json ) {
var world; var world;
if ( json.constructor == Array ) { if ( json.constructor == Array ) {
// for support of legacy format // for support of legacy format
@ -210,7 +210,7 @@ exports.locationFromJSON = function( json ) {
exports.player = _player; exports.player = _player;
exports.getPlayerObject = function( player ) { exports.getPlayerObject = function getPlayerObject( player ) {
console.warn( 'utils.getPlayerObject() is deprecated. Use utils.player() instead.' ); console.warn( 'utils.getPlayerObject() is deprecated. Use utils.player() instead.' );
return _player(player); return _player(player);
}; };
@ -281,7 +281,7 @@ if (targetPos){
``` ```
***/ ***/
exports.getMousePos = function( player ) { exports.getMousePos = function getMousePos( player ) {
player = _player(player); player = _player(player);
if ( !player ) { if ( !player ) {
@ -366,20 +366,20 @@ utils.foreach (players, function( player ) {
Java-style collection. This is important because many objects in the Java-style collection. This is important because many objects in the
CanaryMod and Bukkit APIs use Java-style collections. CanaryMod and Bukkit APIs use Java-style collections.
***/ ***/
var _foreach = function( array, callback, context, delay, onCompletion ) { function _foreach( array, callback, context, delay, onCompletion ) {
if ( array instanceof java.util.Collection ) { if ( array instanceof java.util.Collection ) {
array = array.toArray(); array = array.toArray();
} }
var i = 0; var i = 0;
var len = array.length; var len = array.length;
function next() {
callback(array[i], i, context, array);
i++;
}
function hasNext() {
return i < len;
}
if ( delay ) { if ( delay ) {
var next = function( ) {
callback(array[i], i, context, array);
i++;
};
var hasNext = function( ) {
return i < len;
};
_nicely( next, hasNext, onCompletion, delay ); _nicely( next, hasNext, onCompletion, delay );
} else { } else {
for ( ;i < len; i++ ) { for ( ;i < len; i++ ) {
@ -412,7 +412,7 @@ function and the start of the next `next()` function.
See the source code to utils.foreach for an example of how utils.nicely is used. See the source code to utils.foreach for an example of how utils.nicely is used.
***/ ***/
var _nicely = function( next, hasNext, onDone, delay ) { function _nicely( next, hasNext, onDone, delay ) {
if ( hasNext() ){ if ( hasNext() ){
next(); next();
setTimeout( function() { setTimeout( function() {
@ -426,11 +426,12 @@ var _nicely = function( next, hasNext, onDone, delay ) {
}; };
exports.nicely = _nicely; exports.nicely = _nicely;
exports.at = function( time24hr, callback, pWorlds, repeat ) { function _at( time24hr, callback, pWorlds, repeat ) {
console.warn("utils.at() is deprecated, use require('at') instead"); console.warn("utils.at() is deprecated, use require('at') instead");
var at = require('at'); var at = require('at');
return at( time24hr, callback, pWorlds, repeat); return at( time24hr, callback, pWorlds, repeat);
}; }
exports.at = _at;
/************************************************************************* /*************************************************************************
### utils.time( world ) function ### utils.time( world ) function
@ -501,27 +502,9 @@ var jsFiles = utils.find('./', function(dir,name){
}); });
``` ```
***/ ***/
exports.find = function( dir , filter ) { exports.find = function( path, filter){
var result = []; console.warn("utils.find() is deprecated, use require('find') instead");
var recurse = function( dir, store ) { return require('find')(path, filter);
var files, dirfile = new File( dir );
if ( typeof filter == 'undefined' ) {
files = dirfile.list();
} else {
files = dirfile.list(filter);
}
_foreach( files, function( file ) {
file = new File( dir + '/' + file );
if ( file.isDirectory() ) {
recurse( file.canonicalPath, store );
} else {
store.push( file.canonicalPath );
}
});
};
recurse( dir, result );
return result;
}; };
/************************************************************************ /************************************************************************
### utils.serverAddress() function ### utils.serverAddress() function
@ -534,7 +517,7 @@ var serverAddress = utils.serverAddress();
console.log(serverAddress); console.log(serverAddress);
``` ```
***/ ***/
exports.serverAddress = function() { exports.serverAddress = function serverAddress() {
var interfaces = java.net.NetworkInterface.getNetworkInterfaces(); var interfaces = java.net.NetworkInterface.getNetworkInterfaces();
var current, var current,
addresses, addresses,
@ -682,7 +665,7 @@ if (__plugin.canary){
function getPlayerNames(){ function getPlayerNames(){
return getPlayers().map(function(p){ return p.name; }); return getPlayers().map(function(p){ return p.name; });
} }
exports.players = function(fn){ exports.players = function players(fn){
var result = getPlayers(); var result = getPlayers();
if (fn){ if (fn){
result.forEach(fn); result.forEach(fn);