1
0
Fork 0
arangodb/js/common/bootstrap/modules.js

652 lines
20 KiB
JavaScript

/*jshint ignore:start */
/*eslint no-unused-vars:0, quotes:0 */
/*eslint-disable */
;(function () {
'use strict'
/*eslint-enable */
// //////////////////////////////////////////////////////////////////////////////
// / DISCLAIMER
// /
// / Copyright 2016 ArangoDB GmbH, Cologne, Germany
// /
// / Licensed under the Apache License, Version 2.0 (the "License")
// / you may not use this file except in compliance with the License.
// / You may obtain a copy of the License at
// /
// / http://www.apache.org/licenses/LICENSE-2.0
// /
// / Unless required by applicable law or agreed to in writing, software
// / distributed under the License is distributed on an "AS IS" BASIS,
// / WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// / See the License for the specific language governing permissions and
// / limitations under the License.
// /
// / Copyright holder is triAGENS GmbH, Cologne, Germany
// /
// / @author Alan Plum
// / @author Copyright 2015-2016, ArangoDB GmbH, Cologne, Germany
// /
// / Based on Node.js 4.1.0 /lib/module.js:
// /
// / Copyright Joyent, Inc. and other Node contributors.
// /
// / Permission is hereby granted, free of charge, to any person obtaining a
// / copy of this software and associated documentation files (the
// / "Software"), to deal in the Software without restriction, including
// / without limitation the rights to use, copy, modify, merge, publish,
// / distribute, sublicense, and/or sell copies of the Software, and to permit
// / persons to whom the Software is furnished to do so, subject to the
// / following conditions:
// /
// / The above copyright notice and this permission notice shall be included
// / in all copies or substantial portions of the Software.
// /
// / THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// / OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// / MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// / NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// / DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// / OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// / USE OR OTHER DEALINGS IN THE SOFTWARE.
// /
// //////////////////////////////////////////////////////////////////////////////
const NATIVE_MODULES = global.SCAFFOLDING_MODULES;
const fs = NATIVE_MODULES.fs;
const path = NATIVE_MODULES.path;
const assert = NATIVE_MODULES.assert;
const internal = NATIVE_MODULES.internal;
const console = NATIVE_MODULES.console;
delete global.SCAFFOLDING_MODULES;
delete global.DEFINE_MODULE;
const LOADING = [];
const $_MODULE_ROOT = Symbol.for('@arangodb/module.root');
const $_MODULE_CONTEXT = Symbol.for('@arangodb/module.context');
const GLOBAL_PATHS = [];
const ROOT_PATH = fs.normalize(fs.makeAbsolute(internal.startupPath));
global.MODULES_PATH.forEach(function (p) {
p = fs.normalize(fs.makeAbsolute(p));
GLOBAL_PATHS.push(p);
if (p.match(/[\/\\]node[\/\\]?$/)) {
GLOBAL_PATHS.push(path.join(p, 'node_modules'));
}
});
delete global.MODULES_PATH;
function internalModuleStat (path) {
if (fs.isDirectory(path)) return 1;
else if (fs.isFile(path)) return 0;
else return -1;
}
// If obj.hasOwnProperty has been overridden, then calling
// obj.hasOwnProperty(prop) will break.
// See: https://github.com/joyent/node/issues/1707
function hasOwnProperty (obj, prop) {
return Object.prototype.hasOwnProperty.call(obj, prop);
}
function createRequire (module) {
function require (path) {
assert(path, 'missing path');
assert(typeof path === 'string', 'path must be a string');
if (path === '@arangodb/locals') {
return {context: module.context};
}
return Module._load(path, module);
}
require.resolve = function (request) {
return Module._resolveFilename(request, module);
};
// Enable support to add extra extension types
require.extensions = Module._extensions;
require.cache = Module._cache;
require.aliases = new Map();
return require;
}
function Module (id, parent) {
this.id = id;
this.exports = {};
this.require = createRequire(this);
this.parent = parent;
if (parent && parent.children) {
parent.children.push(this);
}
Object.defineProperty(this, $_MODULE_CONTEXT, {
value: {
print: internal.print,
process: NATIVE_MODULES.process,
console: NATIVE_MODULES.console,
module: this,
exports: this.exports,
require: this.require,
__filename: null,
__dirname: null
}
});
if (parent) {
this.context = parent.context;
this.require.cache = parent.require.cache;
this.require.aliases = parent.require.aliases;
this[$_MODULE_ROOT] = parent[$_MODULE_ROOT];
Object.keys(parent[$_MODULE_CONTEXT]).forEach(function (key) {
if (!hasOwnProperty(this[$_MODULE_CONTEXT], key)) {
this[$_MODULE_CONTEXT][key] = parent[$_MODULE_CONTEXT][key];
}
}.bind(this));
}
Object.defineProperty(this, 'filename', {
configurable: true,
enumerable: true,
get() {
return this[$_MODULE_CONTEXT].__filename;
},
set(filename) {
this[$_MODULE_CONTEXT].__filename = filename;
this[$_MODULE_CONTEXT].__dirname = filename === null ? null : path.dirname(filename);
}
});
this.filename = null;
this.loaded = false;
this.children = [];
}
NATIVE_MODULES.module = Module;
Module._cache = {};
Module._pathCache = {};
Module._dbCache = {};
Module._extensions = {};
var modulePaths = GLOBAL_PATHS;
Module.globalPaths = modulePaths;
// given a module name, and a list of paths to test, returns the first
// matching file in the following precedence.
//
// require("a.<ext>")
// -> a.<ext>
//
// require("a")
// -> a
// -> a.<ext>
// -> a/index.<ext>
// check if the directory is a package.json dir
const packageMainCache = {};
function readPackage (requestPath) {
if (hasOwnProperty(packageMainCache, requestPath)) {
return packageMainCache[requestPath];
}
var jsonPath = path.resolve(requestPath, 'package.json');
var longPath = path._makeLong(jsonPath);
if (!fs.exists(longPath)) {
return false;
}
var json = fs.read(longPath);
try {
var pkg = JSON.parse(json).main;
packageMainCache[requestPath] = pkg;
} catch (e) {
e.path = jsonPath;
e.message = 'Error parsing ' + jsonPath + ': ' + e.message;
throw e;
}
return pkg;
}
function tryPackage (requestPath, exts) {
var pkg = readPackage(requestPath);
if (!pkg) return false;
var filename = path.resolve(requestPath, pkg);
return tryFile(filename) || tryExtensions(filename, exts) ||
tryExtensions(path.resolve(filename, 'index'), exts);
}
// check if the file exists and is not a directory
function tryFile (requestPath) {
const rc = internalModuleStat(path._makeLong(requestPath));
return rc === 0 && requestPath;
}
// given a path check a the file exists with any of the set extensions
function tryExtensions (p, exts) {
for (var i = 0, EL = exts.length; i < EL; i++) {
var filename = tryFile(p + exts[i]);
if (filename) {
return filename;
}
}
return false;
}
var warned = false;
Module._findPath = function (request, paths) {
var exts = Object.keys(Module._extensions);
if (path.isAbsolute(request)) {
paths = [''];
}
var s = request.slice(-1);
var trailingSlash = (s === '/' || s === '\\');
var cacheKey = JSON.stringify({request: request, paths: paths});
if (Module._pathCache[cacheKey]) {
return Module._pathCache[cacheKey];
}
// For each path
for (var i = 0, PL = paths.length; i < PL; i++) {
// Don't search further if path doesn't exist
if (paths[i] && internalModuleStat(path._makeLong(paths[i])) < 1) continue;
var basePath = path.resolve(paths[i], request);
var filename;
if (!trailingSlash) {
const rc = internalModuleStat(path._makeLong(basePath));
if (rc === 0) { // File.
filename = basePath;
} else if (rc === 1) { // Directory.
filename = tryPackage(basePath, exts);
}
if (!filename) {
// try it with each of the extensions
filename = tryExtensions(basePath, exts);
}
}
if (!filename) {
filename = tryPackage(basePath, exts);
}
if (!filename) {
// try it with each of the extensions at "index"
filename = tryExtensions(path.resolve(basePath, 'index'), exts);
}
if (filename) {
// Warn once if '.' resolved outside the module dir
if (request === '.' && i > 0 && !warned) {
warned = true;
console.warnLines(
"warning: require('.') resolved outside the package " +
'directory. This functionality is deprecated and will be removed ' +
'soon.'
);
}
Module._pathCache[cacheKey] = filename;
return filename;
}
}
return false;
};
// 'from' is the __dirname of the module.
Module._nodeModulePaths = function (from, root) {
// guarantee that 'from' is absolute.
from = path.resolve(from);
// note: this approach *only* works when the path is guaranteed
// to be absolute. Doing a fully-edge-case-correct path.split
// that works on both Windows and Posix is non-trivial.
var splitRe = (internal.platform === 'win32' || internal.platform === 'win64') ? /[\/\\]/ : /\//;
var paths = [];
var parts = from.split(splitRe);
var inRoot = root && from.indexOf(root) === 0;
if (!inRoot) {
root = ROOT_PATH;
inRoot = from.indexOf(root) === 0;
}
for (var tip = parts.length - 1; tip >= 0; tip--) {
// don't search in .../node_modules/node_modules
if (parts[tip] === 'node_modules') continue;
var dir = parts.slice(0, tip + 1).concat('node_modules').join(path.sep);
if (inRoot && dir.indexOf(root) !== 0) break;
paths.push(dir);
}
return paths;
};
Module._resolveLookupPaths = function (request, parent) {
if (hasOwnProperty(NATIVE_MODULES, request)) {
return [request, []];
}
var start = request.substring(0, 2);
if (start !== './' && start !== '..') {
var paths = modulePaths.slice(0);
if (parent) {
if (!parent.paths) parent.paths = [];
paths = parent.paths.concat(paths);
}
// Maintain backwards compat with certain broken uses of require('.')
// by putting the module's directory in front of the lookup paths.
if (request === '.') {
if (parent && parent.filename) {
paths.splice(0, 0, path.dirname(parent.filename));
} else {
paths.splice(0, 0, path.resolve(request));
}
}
return [request, paths];
}
// with --eval, parent.id is not set and parent.filename is null
if (!parent || !parent.id || !parent.filename) {
// make require('./path/to/foo') work - normally the path is taken
// from realpath(__filename) but with eval there is no filename
var mainPaths = ['.'].concat(modulePaths);
mainPaths = Module._nodeModulePaths('.').concat(mainPaths);
return [request, mainPaths];
}
// Is the parent an index module?
// We can assume the parent has a valid extension,
// as it already has been accepted as a module.
var isIndex = /^index\.\w+?$/.test(path.basename(parent.filename));
var parentIdPath = isIndex ? parent.id : path.dirname(parent.id);
var id = path.resolve(parentIdPath, request);
// make sure require('./path') and require('path') get distinct ids, even
// when called from the toplevel js file
if (parentIdPath === '.' && id.indexOf('/') === -1) {
id = './' + id;
}
return [id, [path.dirname(parent.filename)]];
};
Module._resolveDbModule = function (request) {
if (request.charAt(0) !== '/') {
request = '/' + request;
}
try {
var dbModule = Module._dbCache[request];
// _modules is an optional collection. only use it if it is present.
if (!dbModule && internal.db !== undefined && internal.db._modules) {
dbModule = internal.db._modules.firstExample({path: request});
if (!dbModule) {
// try again, but prefix module with '/db' as some modules seem
// to have been saved with that prefix...
dbModule = internal.db._modules.firstExample({path: '/db:' + request});
if (!dbModule) {
return null;
}
}
Module._dbCache[request] = dbModule;
}
return dbModule;
} catch (err) {
// something went wrong while accessing _modules collection
// ArangoDB will continue to run without it, so just log a message and
// continue operations
console.debug("unable to load module '%s': %s", request, String(err));
return null;
}
};
function isGlobalModule (filename) {
return modulePaths.some(function (modulePath) {
return filename.indexOf(modulePath) === 0;
});
}
// Check the cache for the requested file.
// 1. If a module already exists in the cache: return its exports object.
// 2. If the module is native: call `NativeModule.require()` with the
// filename and return the result.
// 3. Otherwise, create a new module for the file and save it to the cache.
// Then have it load the file contents before returning its exports
// object.
Module._load = function (request, parent, isMain) {
request = request.replace(/^org\/arangodb/, '@arangodb');
request = (parent && parent.require.aliases.get(request)) || request;
var filename = request;
var dbModule = false;
var match = request.match(/^\/?db:(\/(\/_modules)?)?(\/.+)/);
if (match) {
dbModule = Module._resolveDbModule(match[3]);
if (!dbModule) {
throw new internal.ArangoError({
errorNum: internal.errors.ERROR_MODULE_NOT_FOUND.code,
errorMessage: internal.errors.ERROR_MODULE_NOT_FOUND.message
+ '\nFile: ' + request
});
}
} else {
try {
filename = Module._resolveFilename(request, parent);
} catch (e) {
dbModule = Module._resolveDbModule(request);
if (!dbModule) {
throw e;
}
}
}
if (dbModule) {
filename = dbModule.path;
}
var cache;
if (parent && (dbModule || !isGlobalModule(filename))) {
cache = parent.require.cache;
} else {
cache = Module._cache;
}
var cachedModule = cache[filename];
if (cachedModule) {
return cachedModule.exports;
}
if (hasOwnProperty(NATIVE_MODULES, filename)) {
return NATIVE_MODULES[filename];
}
var module = new Module(filename, parent);
if (isMain) {
module.id = '.';
}
cache[filename] = module;
var loading = {cache, filename};
LOADING.push(loading);
var hadException = true;
try {
if (dbModule) {
module._loadDbModule(dbModule);
} else {
module.load(filename);
}
hadException = false;
} finally {
if (hadException) {
delete cache[filename];
}
var i = LOADING.indexOf(loading);
if (i !== -1) {
LOADING.splice(i, 1);
}
}
return module.exports;
};
Module._cleanupCancelation = function () {
while (LOADING.length) {
let loading = LOADING.pop();
delete loading.cache[loading.filename];
}
};
Module._resolveFilename = function (request, parent) {
if (hasOwnProperty(NATIVE_MODULES, request)) {
return request;
}
var resolvedModule = Module._resolveLookupPaths(request, parent);
var id = resolvedModule[0];
var paths = resolvedModule[1];
// look up the filename first, since that's the cache key.
var filename = Module._findPath(request, paths);
if (!filename) {
throw new internal.ArangoError({
errorNum: internal.errors.ERROR_MODULE_NOT_FOUND.code,
errorMessage: internal.errors.ERROR_MODULE_NOT_FOUND.message
+ '\nFile: ' + request
});
}
return filename;
};
// Given a file name, pass it to the proper extension handler.
Module.prototype.load = function (filename) {
assert(!this.loaded);
this.filename = filename;
this.paths = Module._nodeModulePaths(path.dirname(filename), this[$_MODULE_ROOT]);
var extension = path.extname(filename) || '.js';
if (!Module._extensions[extension]) extension = '.js';
try {
Module._extensions[extension](this, filename);
} catch (e) {
if (e.errorNum !== internal.errors.ERROR_MODULE_FAILURE.code) {
let msg = `${internal.errors.ERROR_MODULE_FAILURE.message}\nReason: ${e}`;
if (e.fileName !== undefined) {
msg += `\nFile: ${e.fileName}`;
} else {
msg += `\nFile: ${filename}`;
}
if (e.lineNumber !== undefined) {
msg += `\nLine: ${e.lineNumber}`;
}
if (e.columnNumber !== undefined) {
msg += `\nColumn: ${e.columnNumber}`;
}
throw Object.assign(
new internal.ArangoError({
errorNum: internal.errors.ERROR_MODULE_FAILURE.code,
errorMessage: msg
}),
{cause: e}
);
}
throw e;
}
this.loaded = true;
};
Module.prototype._loadDbModule = function (dbModule) {
assert(!this.loaded);
const filename = `db://_modules${dbModule.path}`;
this.filename = filename;
this._compile(dbModule.content, filename);
this.loaded = true;
};
// Run the file contents in the correct scope or sandbox. Expose
// the correct helper variables (require, module, exports) to
// the file.
// Returns exception, if any.
Module.prototype._compile = function (content, filename) {
// remove shebang
content = content.replace(/^\#\!.*/, '');
this.filename = filename;
var args = this[$_MODULE_CONTEXT];
var keys = Object.keys(args);
// Do not use Function constructor or line numbers will be wrong
var wrapper = `(function (${keys.join(', ')}) {${content}\n})`;
var fn = internal.executeScript(wrapper, undefined, filename);
if (typeof fn !== 'function') {
throw new TypeError(`Expected internal.executeScript to return a function, not ${typeof fn}`);
}
fn.apply(args.exports, keys.map(function (key) {
return args[key];
}));
};
function stripBOM (content) {
// Remove byte order marker. This catches EF BB BF (the UTF-8 BOM)
// because the buffer-to-string conversion in `fs.readFileSync()`
// translates it to FEFF, the UTF-16 BOM.
if (content.charCodeAt(0) === 0xFEFF) {
content = content.slice(1);
}
return content;
}
// Native extension for .js
Module._extensions['.js'] = function (module, filename) {
var content = fs.readFileSync(filename, 'utf8');
module._compile(stripBOM(content), filename);
};
// Native extension for .json
Module._extensions['.json'] = function (module, filename) {
var content = fs.readFileSync(filename, 'utf8');
try {
module.exports = JSON.parse(stripBOM(content));
} catch (err) {
err.message = filename + ': ' + err.message;
throw err;
}
};
// backwards compatibility
Module.Module = Module;
global.require = function (request) {
if (request === '@arangodb/locals') {
return {};
}
return Module._load(request);
};
}());