Skip to content

Instantly share code, notes, and snippets.

@ravihara
Created January 13, 2016 13:23
Show Gist options
  • Save ravihara/9bc6908741db1fc9d732 to your computer and use it in GitHub Desktop.
Save ravihara/9bc6908741db1fc9d732 to your computer and use it in GitHub Desktop.
CommonJS require function implementation for Nashorn / Java8
// Based on https://github.com/nodyn/jvm-npm
"use strict";
(function() {
var System = java.lang.System,
File = java.io.File,
FileInputStream = java.io.FileInputStream,
StringBuilder = java.lang.StringBuilder,
BufferedReader = java.io.BufferedReader,
InputStreamReader = java.io.InputStreamReader;
/* Helper function until ECMAScript 6 is complete */
if (typeof String.prototype.endsWith !== 'function') {
String.prototype.endsWith = function(suffix) {
if (!suffix) { return false; }
return (this.indexOf(suffix, (this.length - suffix.length)) !== -1);
};
}
/* Define ModuleError to throw exception */
var ModuleError = function(message, code, cause) {
this.code = code || "MODULE_ERROR";
this.message = message || "Error loading module";
this.cause = cause;
};
ModuleError.prototype = new Error();
ModuleError.prototype.constructor = ModuleError;
/* Define the actual Module function */
var Module = function(id, parent, core) {
this.id = id;
this.core = core;
this.parent = parent;
this.children = [];
this.filename = id;
this.loaded = false;
Object.defineProperty(this, 'exports', {
get: function() {
return this._exports;
}.bind(this),
set: function(val) {
Require.cache[this.filename] = val;
this._exports = val;
}.bind(this)
});
this.exports = {};
if (parent && parent.children) { parent.children.push(this); }
this.require = function(id) {
return Require(id, this);
}.bind(this);
};
Module.load = function(file, parent, core, main) {
var module = new Module(file, parent, core);
var __FILENAME__ = module.filename;
var body = readFile(module.filename, module.core),
dir = new File(module.filename).getParent(),
args = ['exports', 'module', 'require', '__filename', '__dirname'],
func = new Function(args, body);
func.apply(module, [module.exports, module, module.require, module.filename, dir]);
module.loaded = true;
module.main = main;
return module.exports;
};
/* Define the actual Require function */
var Require = function(id, parent) {
var core, file = Require.resolve(id, parent);
if (!file) {
System.err.println("Cannot find module " + id);
throw new ModuleError("Cannot find module " + id, "MODULE_NOT_FOUND");
}
if (file.core) {
file = file.path;
core = true;
}
try {
if (Require.cache[file]) {
return Require.cache[file];
} else if (file.endsWith('.js')) {
return Module.load(file, parent, core);
} else if (file.endsWith('.json')) {
return loadJSON(file);
}
} catch(ex) {
if (ex instanceof java.lang.Exception) {
throw new ModuleError("Cannot load module " + id, "LOAD_ERROR", ex);
} else {
System.out.println("Cannot load module " + id + " LOAD_ERROR");
throw ex;
}
}
};
Require.resolve = function(id, parent) {
var roots = findRoots(parent);
for (var i = 0 ; i < roots.length ; ++i) {
var root = roots[i];
var result = resolveCoreModule(id, root) ||
resolveAsFile(id, root, '.js') ||
resolveAsFile(id, root, '.json') ||
resolveAsDirectory(id, root) ||
resolveAsJjsModule(id, root);
if (result) { return result; }
}
return false;
};
Require.paths = function() {
var r = [];
r.push(java.lang.System.getProperty( "user.home" ) + "/.jjs_mods");
r.push(java.lang.System.getProperty( "user.home" ) + "/.jjs_libs");
if (Require.JJS_MODS_PATH) {
r = r.concat(parsePaths(Require.JJS_MODS_PATH));
} else {
var JJS_MODS_PATH = java.lang.System.getenv.JJS_MODS_PATH;
if (JJS_MODS_PATH) {
r = r.concat(parsePaths(JJS_MODS_PATH));
}
}
return r;
};
/*** internal functions to setup and load the module ***/
var findRoots = function(parent) {
var r = [];
r.push(findRoot(parent));
return r.concat(Require.paths());
};
var parsePaths = function(paths) {
if (! paths) { return []; }
if (paths === '') { return []; }
var osName = java.lang.System.getProperty("os.name").toLowerCase();
var separator;
if (osName.indexOf('win') >= 0 ) {
separator = ';';
} else {
separator = ':';
}
return paths.split(separator);
};
var findRoot = function(parent) {
if (!parent || !parent.id) { return Require.root; }
var pathParts = parent.id.split(/[\/|\\,]+/g);
pathParts.pop();
return pathParts.join('/');
};
var loadJSON = function(file) {
var json = JSON.parse(readFile(file));
Require.cache[file] = json;
return json;
};
var resolveAsJjsModule = function(id, root) {
var base = [root, 'jjs_mods'].join('/');
return resolveAsFile(id, base) ||
resolveAsDirectory(id, base) ||
(root ? resolveAsJjsModule(id, new File(root).getParent()) : false);
};
var resolveAsDirectory = function(id, root) {
var base = [root, id].join('/'),
file = new File([base, 'jjspkg.json'].join('/'));
if (file.exists()) {
try {
var body = readFile(file.getCanonicalPath()),
jjspkg = JSON.parse(body);
if (jjspkg.main) {
return (resolveAsFile(jjspkg.main, base) || resolveAsDirectory(jjspkg.main, base));
}
// if no jjspkg.main exists, look for index.js
return resolveAsFile('index.js', base);
} catch(ex) {
throw new ModuleError("Cannot load JSON file", "PARSE_ERROR", ex);
}
}
return resolveAsFile('index.js', base);
};
var resolveAsFile = function(id, root, ext) {
var file;
if ( id.length > 0 && id[0] === '/' ) {
file = new File(normalizeName(id, ext || '.js'));
if (!file.exists()) {
return resolveAsDirectory(id);
}
} else {
file = new File([root, normalizeName(id, ext || '.js')].join('/'));
}
if (file.exists()) {
return file.getCanonicalPath();
}
};
var resolveCoreModule = function(id, root) {
var name = normalizeName(id);
var classloader = java.lang.Thread.currentThread().getContextClassLoader();
if (classloader.getResource(name))
return { path: name, core: true };
};
var normalizeName = function(fileName, ext) {
var extension = ext || '.js';
if (fileName.endsWith(extension)) {
return fileName;
}
return fileName + extension;
};
var readFile = function(filename, core) {
var inStream = null,
buffReader = null,
strBuilder = new StringBuilder();
try {
if (core) {
var classloader = java.lang.Thread.currentThread().getContextClassLoader();
inStream = classloader.getResourceAsStream(filename);
} else {
inStream = new FileInputStream(filename);
}
buffReader = new BufferedReader(new InputStreamReader(inStream));
var line = buffReader.readLine();
while (line != null) {
strBuilder.append(line);
line = buffReader.readLine();
}
} catch(e) {
throw new ModuleError("Cannot read file [" + filename + "]: ", "IO_ERROR", e);
} finally {
if (null != buffReader) {
try {
buffReader.close();
} catch (e) {
throw new ModuleError("Unable to close buffer-reader for '" + filename + "'");
}
}
}
return strBuilder.toString();
};
/*** Final, export configuration ***/
Require.root = System.getProperty('user.dir');
Require.JJS_MODS_PATH = undefined;
Require.debug = true;
Require.cache = {};
Require.extensions = {};
return Require;
}());
@ravihara
Copy link
Author

// Example usage: app.js

var require = load("./jjs-require.js"); // This line is to be added in every file, where 'require' function is needed.
var myDep = require("./my-dep");

myDep.printMe();

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment