mirror of https://gitee.com/bigwinds/arangodb
1172 lines
34 KiB
JavaScript
1172 lines
34 KiB
JavaScript
/*jslint indent: 2, nomen: true, maxlen: 120, sloppy: true, vars: true, white: true, plusplus: true, continue: true */
|
|
/*global require, module: true, PACKAGE_PATH */
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief JavaScript server functions
|
|
///
|
|
/// @file
|
|
///
|
|
/// DISCLAIMER
|
|
///
|
|
/// Copyright 2004-2013 triAGENS 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 Dr. Frank Celler
|
|
/// @author Copyright 2011-2013, triAGENS GmbH, Cologne, Germany
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// --SECTION-- global variables
|
|
// -----------------------------------------------------------------------------
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @addtogroup ArangoDB
|
|
/// @{
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief top-level module
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
module = null;
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @}
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// --SECTION-- global functions
|
|
// -----------------------------------------------------------------------------
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @addtogroup ArangoDB
|
|
/// @{
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief global require function
|
|
///
|
|
/// @FUN{require(@FA{path})}
|
|
///
|
|
/// @FN{require} checks if the file specified by @FA{path} has already been
|
|
/// loaded. If not, the content of the file is executed in a new
|
|
/// context. Within the context you can use the global variable @LIT{exports}
|
|
/// in order to export variables and functions. This variable is returned by
|
|
/// @FN{require}.
|
|
///
|
|
/// Assume that your module file is @LIT{test1.js} and contains
|
|
///
|
|
/// @verbinclude modules-require-1
|
|
///
|
|
/// Then you can use @FN{require} to load the file and access the exports.
|
|
///
|
|
/// @verbinclude modules-require-2
|
|
///
|
|
/// @FN{require} follows the specification
|
|
/// <a href="http://wiki.commonjs.org/wiki/Modules/1.1.1">Modules/1.1.1</a>.
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
function require (path) {
|
|
return module.require(path);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief global print function
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
function print () {
|
|
var internal = require("internal");
|
|
internal.print.apply(internal.print, arguments);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief global printf function
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
function printf () {
|
|
var internal = require("internal");
|
|
internal.printf.apply(internal.printf, arguments);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief turn off pretty printing
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
function print_plain () {
|
|
var internal = require("internal");
|
|
|
|
var p = internal.PRETTY_PRINT;
|
|
internal.PRETTY_PRINT = false;
|
|
|
|
var c = internal.COLOR_OUTPUT;
|
|
internal.COLOR_OUTPUT = false;
|
|
|
|
try {
|
|
internal.print.apply(internal.print, arguments);
|
|
|
|
internal.PRETTY_PRINT = p;
|
|
internal.COLOR_OUTPUT = c;
|
|
}
|
|
catch (e) {
|
|
internal.PRETTY_PRINT = p;
|
|
internal.COLOR_OUTPUT = c;
|
|
|
|
throw e.message;
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief start pretty printing
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
function start_pretty_print () {
|
|
require("internal").startPrettyPrint();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief stop pretty printing
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
function stop_pretty_print () {
|
|
require("internal").stopPrettyPrint();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief start pretty printing with optional color
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
function start_color_print (color) {
|
|
require("internal").startColorPrint(color, false);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief stop pretty printing
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
function stop_color_print () {
|
|
require("internal").stopColorPrint();
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @}
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// --SECTION-- constructors and destructors
|
|
// -----------------------------------------------------------------------------
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @addtogroup ArangoDB
|
|
/// @{
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
(function () {
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief module constructor
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
function Module (id, type, pkg) {
|
|
this.id = id; // commonjs Module/1.1.1
|
|
this.exports = {}; // commonjs Module/1.1.1
|
|
|
|
this._type = type; // module type: 'system', 'user'
|
|
this._origin = 'unknown'; // 'file:///{path}'
|
|
// 'database:///_document/{collection}/{key}'
|
|
|
|
this._package = pkg; // package to which this module belongs
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief package constructor
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
function Package (id, description, parent, paths) {
|
|
var i;
|
|
|
|
this.id = id; // same of the corresponding module
|
|
this._description = description; // the package.json file
|
|
this._parent = parent; // parent package
|
|
this._moduleCache = {}; // module cache for package modules
|
|
|
|
this._paths = paths; // path to the package
|
|
}
|
|
|
|
var GlobalPackage = new Package("/", {name: "ArangoDB"}, undefined, PACKAGE_PATH);
|
|
|
|
Package.prototype.defineSystemModule = function (path) {
|
|
var result = this._moduleCache[path] = new Module(path, 'system', GlobalPackage);
|
|
|
|
return result;
|
|
};
|
|
|
|
Package.prototype.defineModule = function (path, module) {
|
|
this._moduleCache[path] = module;
|
|
|
|
return module;
|
|
};
|
|
|
|
Package.prototype.clearModule = function (path) {
|
|
delete this._moduleCache[path];
|
|
};
|
|
|
|
Package.prototype.module = function (path) {
|
|
if (this._moduleCache.hasOwnProperty(path)) {
|
|
return this._moduleCache[path];
|
|
}
|
|
|
|
return null;
|
|
};
|
|
|
|
Package.prototype.moduleNames = function () {
|
|
var name;
|
|
var names = [];
|
|
|
|
for (name in this._moduleCache) {
|
|
if (this._moduleCache.hasOwnProperty(name)) {
|
|
names.push(name);
|
|
}
|
|
}
|
|
|
|
return names;
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief app constructor
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
function ArangoApp (name, version, manifest, path) {
|
|
this._id = "app:" + name + ":" + version;
|
|
this._name = name;
|
|
this._version = version;
|
|
this._manifest = manifest;
|
|
this._path = path;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @}
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// --SECTION-- private variables
|
|
// -----------------------------------------------------------------------------
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @addtogroup ArangoDB
|
|
/// @{
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief global module cache
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
var ModuleExistsCache = {};
|
|
|
|
GlobalPackage.defineSystemModule("/");
|
|
GlobalPackage.defineSystemModule("/internal");
|
|
GlobalPackage.defineSystemModule("/fs");
|
|
GlobalPackage.defineSystemModule("/console");
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief top-level-module
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
module = Module.prototype.root = GlobalPackage.module("/");
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief global package
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
var internal = GlobalPackage.module("/internal").exports;
|
|
internal.GlobalPackage = GlobalPackage;
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @}
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// --SECTION-- module "internal"
|
|
// -----------------------------------------------------------------------------
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @addtogroup ArangoDB
|
|
/// @{
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
var fs = GlobalPackage.module("/fs").exports;
|
|
var console = GlobalPackage.module("/console").exports;
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief normalizes an URL
|
|
///
|
|
/// If @FA{path} starts with "." or "..", then it is a relative path.
|
|
/// Otherwise it is an absolute path. Normalizing will remove `//`,
|
|
/// `/./`, `/../` from the url - expect in the beginning, where it keeps
|
|
/// `../` and or at most one `./`.
|
|
///
|
|
/// If @FA{path} is empty, the url `./` will be returned.
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
internal.normalizeURL = function (path) {
|
|
var i;
|
|
var n;
|
|
var p;
|
|
var q;
|
|
var r;
|
|
var x;
|
|
|
|
if (path === "") {
|
|
return "./";
|
|
}
|
|
|
|
p = path.split('/');
|
|
|
|
// relative path
|
|
if (p[0] === "." || p[0] === "..") {
|
|
r = p[0] + "/";
|
|
p.shift();
|
|
q = p;
|
|
}
|
|
|
|
// absolute path
|
|
else if (p[0] === "") {
|
|
r = "/";
|
|
p.shift();
|
|
q = p;
|
|
}
|
|
|
|
// assume that the path is relative
|
|
else {
|
|
r = "./";
|
|
q = p;
|
|
}
|
|
|
|
// normalize path
|
|
n = [];
|
|
|
|
for (i = 0; i < q.length; ++i) {
|
|
x = q[i];
|
|
|
|
if (x === "..") {
|
|
if (n.length === 0) {
|
|
if (r === "../") {
|
|
n.push(x);
|
|
}
|
|
else if (r === "./") {
|
|
r = "../";
|
|
}
|
|
else {
|
|
throw "cannot use '..' to escape top-level-directory";
|
|
}
|
|
}
|
|
else if (n[n.length - 1] === "..") {
|
|
n.push(x);
|
|
}
|
|
else {
|
|
n.pop();
|
|
}
|
|
}
|
|
else if (x !== "" && x !== ".") {
|
|
n.push(x);
|
|
}
|
|
}
|
|
|
|
return r + n.join('/');
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief normalizes a module name
|
|
///
|
|
/// If @FA{path} starts with "." or "..", then it is a relative path.
|
|
/// Otherwise it is an absolute path.
|
|
///
|
|
/// @FA{prefix} must not end in `/` unless it is equal to `"/"`.
|
|
///
|
|
/// The normalized name will start with a `/`, but not end in `/' unless it
|
|
/// is equal to `"/"`.
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
internal.normalizeModuleName = function (prefix, path) {
|
|
var i;
|
|
var n;
|
|
var p;
|
|
var q;
|
|
var x;
|
|
|
|
if (path === "") {
|
|
return prefix;
|
|
}
|
|
|
|
p = path.split('/');
|
|
|
|
// relative path
|
|
if (p[0] === "." || p[0] === "..") {
|
|
q = prefix.split('/');
|
|
q.pop();
|
|
q = q.concat(p);
|
|
}
|
|
|
|
// absolute path
|
|
else {
|
|
q = p;
|
|
}
|
|
|
|
// normalize path
|
|
n = [];
|
|
|
|
for (i = 0; i < q.length; ++i) {
|
|
x = q[i];
|
|
|
|
if (x === "..") {
|
|
if (n.length === 0) {
|
|
throw "cannot use '..' to escape top-level-directory";
|
|
}
|
|
|
|
n.pop();
|
|
}
|
|
else if (x !== "" && x !== ".") {
|
|
n.push(x);
|
|
}
|
|
}
|
|
|
|
return "/" + n.join('/');
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief reads a module package description file
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
internal.loadPackageDescription = function (main, pkg) {
|
|
var paths;
|
|
var i;
|
|
|
|
paths = pkg._paths;
|
|
|
|
for (i = 0; i < paths.length; ++i) {
|
|
var p = paths[i];
|
|
var n;
|
|
var m;
|
|
|
|
if (p === "") {
|
|
m = "./node_modules" + main;
|
|
}
|
|
else if (p[p.length - 1] === '/') {
|
|
m = p + "node_modules" + main;
|
|
}
|
|
else {
|
|
m = p + "/node_modules" + main;
|
|
}
|
|
|
|
n = m + "/package.json";
|
|
|
|
if (internal.exists(n)) {
|
|
try {
|
|
var desc = JSON.parse(internal.read(n));
|
|
var mainfile = m + internal.normalizeModuleName("", desc.main) + ".js";
|
|
|
|
if (internal.exists(mainfile)) {
|
|
var content = internal.read(mainfile);
|
|
var mypaths;
|
|
|
|
if (typeof desc.directories !== "undefined" && typeof desc.directories.lib !== "undefined") {
|
|
var full = m + internal.normalizeModuleName("", desc.directories.lib);
|
|
|
|
mypaths = [ full ];
|
|
}
|
|
else {
|
|
mypaths = [ m ];
|
|
}
|
|
|
|
return { name: main,
|
|
description: desc,
|
|
packagePath: m,
|
|
packageLib: mypaths,
|
|
path: 'file://' + mainfile,
|
|
content: content };
|
|
}
|
|
}
|
|
catch (err) {
|
|
if (console.hasOwnProperty('error')) {
|
|
console.error("cannot load package '%s': %s - %s",
|
|
main,
|
|
String(err),
|
|
String(err.stack));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return null;
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief reads a file from the package path
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
internal.loadPackageFile = function (main, pkg) {
|
|
var n;
|
|
var i;
|
|
var mc;
|
|
var paths;
|
|
|
|
paths = pkg._paths;
|
|
|
|
// .............................................................................
|
|
// normal modules, file based
|
|
// .............................................................................
|
|
|
|
// try to load the file
|
|
for (i = 0; i < paths.length; ++i) {
|
|
var p = paths[i];
|
|
|
|
if (p === "") {
|
|
n = "." + main + ".js";
|
|
}
|
|
else if (p[p.length - 1] === '/') {
|
|
n = p + main.substr(1) + ".js";
|
|
}
|
|
else {
|
|
n = p + main + ".js";
|
|
}
|
|
|
|
if (internal.exists(n)) {
|
|
return { name: main,
|
|
path: 'file://' + n,
|
|
content: internal.read(n) };
|
|
}
|
|
}
|
|
|
|
return null;
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief reads a file from the module path or the database
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
internal.loadModuleFile = function (main) {
|
|
var n;
|
|
var i;
|
|
var mc;
|
|
var paths;
|
|
|
|
paths = internal.MODULES_PATH;
|
|
|
|
// .............................................................................
|
|
// normal modules, file based
|
|
// .............................................................................
|
|
|
|
// try to load the file
|
|
for (i = 0; i < paths.length; ++i) {
|
|
var p = paths[i];
|
|
|
|
if (p === "") {
|
|
n = "." + main + ".js";
|
|
}
|
|
else if (p[p.length - 1] === '/') {
|
|
n = p + main.substr(1) + ".js";
|
|
}
|
|
else {
|
|
n = p + main + ".js";
|
|
}
|
|
|
|
if (internal.exists(n)) {
|
|
return { name: main,
|
|
path: 'file://' + n,
|
|
content: internal.read(n) };
|
|
}
|
|
}
|
|
|
|
// .............................................................................
|
|
// normal modules, database based
|
|
// .............................................................................
|
|
|
|
if (internal.db !== undefined) {
|
|
mc = internal.db._collection("_modules");
|
|
|
|
if (mc !== null && typeof mc.firstExample === "function") {
|
|
n = mc.firstExample({ path: main });
|
|
|
|
if (n !== null) {
|
|
if (n.hasOwnProperty('content')) {
|
|
return { name: main,
|
|
path: "database:///_document/" + n._id,
|
|
content: n.content };
|
|
}
|
|
|
|
if (console.hasOwnProperty('error')) {
|
|
console.error("found empty content in '%s'", JSON.stringify(n));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return null;
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief returns the app path and manifest
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
internal.appDescription = function (name, version) {
|
|
var apps = internal.APP_PATH;
|
|
var i;
|
|
|
|
for (i = 0; i < apps.length; ++i) {
|
|
var path = apps[i];
|
|
var file = path + "/" + name + "/" + "manifest.json";
|
|
var content;
|
|
var manifest;
|
|
|
|
if (internal.exists(file)) {
|
|
try {
|
|
content = internal.read(file);
|
|
manifest = JSON.parse(content);
|
|
}
|
|
catch (err) {
|
|
console.error("cannot load manifest file '%s': %s - %s",
|
|
file,
|
|
String(err),
|
|
String(err.stack));
|
|
continue;
|
|
}
|
|
|
|
if (! manifest.hasOwnProperty("name")) {
|
|
console.error("cannot manifest file is missing a name '%s'", file);
|
|
continue;
|
|
}
|
|
|
|
if (! manifest.hasOwnProperty("version")) {
|
|
console.error("cannot manifest file is missing a version '%s'", file);
|
|
continue;
|
|
}
|
|
|
|
return {
|
|
path: path + "/" + name,
|
|
manifest: manifest
|
|
};
|
|
}
|
|
}
|
|
|
|
return null;
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @}
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// --SECTION-- private module methods
|
|
// -----------------------------------------------------------------------------
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @addtogroup ArangoDB
|
|
/// @{
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief loads a module
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
Module.prototype.createModule = function (description, type, pkg) {
|
|
var content;
|
|
var module;
|
|
var env;
|
|
var key;
|
|
var sandbox;
|
|
|
|
// mark that we have seen the definition, used for debugging only
|
|
ModuleExistsCache[description.name] = true;
|
|
|
|
// test for parse errors first and fail early if a parse error detected
|
|
if (! internal.parse(description.content)) {
|
|
throw "Javascript parse error in file '" + description.path + "'";
|
|
}
|
|
|
|
// create a new sandbox and execute
|
|
module = new Module(description.name, type, pkg);
|
|
module._origin = description.path;
|
|
|
|
pkg.defineModule(description.name, module);
|
|
|
|
// setup a sandbox
|
|
env = pkg._environment;
|
|
|
|
sandbox = {};
|
|
sandbox.print = internal.print;
|
|
|
|
if (env !== undefined) {
|
|
for (key in env) {
|
|
if (env.hasOwnProperty(key) && key !== "__myenv__") {
|
|
sandbox[key] = env[key];
|
|
}
|
|
}
|
|
}
|
|
|
|
sandbox.module = module;
|
|
sandbox.exports = module.exports;
|
|
sandbox.require = function(path) { return module.require(path); };
|
|
|
|
// try to execute the module source code
|
|
content = "(function (__myenv__) {";
|
|
|
|
for (key in sandbox) {
|
|
if (sandbox.hasOwnProperty(key)) {
|
|
content += "var " + key + " = __myenv__['" + key + "'];";
|
|
}
|
|
}
|
|
|
|
content += "delete __myenv__;"
|
|
+ description.content
|
|
+ "\n});";
|
|
|
|
try {
|
|
var fun = internal.executeScript(content, undefined, description.name);
|
|
|
|
if (fun === undefined) {
|
|
throw "cannot create module context function for: " + content;
|
|
}
|
|
|
|
fun(sandbox);
|
|
}
|
|
catch (err) {
|
|
pkg.clearModule(description.name);
|
|
throw "Javascript exception in file '" + description.name + "': " + err + " - " + err.stack;
|
|
}
|
|
|
|
return module;
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief creates an application
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
Module.prototype.createApp = function (name, version) {
|
|
description = internal.appDescription(name, version);
|
|
|
|
if (description === null) {
|
|
return description;
|
|
}
|
|
|
|
return new ArangoApp(
|
|
description.manifest.name,
|
|
description.manifest.version,
|
|
description.manifest,
|
|
description.path
|
|
);
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief loads a package
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
Module.prototype.createPackage = function (parent, description) {
|
|
var path;
|
|
var module;
|
|
var pkg;
|
|
|
|
path = description.name;
|
|
|
|
pkg = new Package(path,
|
|
description.description,
|
|
parent,
|
|
description.packageLib);
|
|
|
|
module = this.createModule(description, 'package', pkg);
|
|
|
|
if (module !== null) {
|
|
parent.defineModule(path, module);
|
|
}
|
|
|
|
return module;
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief requires a package
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
Module.prototype.requirePackage = function (unormalizedPath) {
|
|
var current;
|
|
var module;
|
|
var path;
|
|
|
|
// first get rid of any ".." and "."
|
|
path = this.normalize(unormalizedPath);
|
|
|
|
if (this._package.id === "/" && path === "/internal") {
|
|
return null;
|
|
}
|
|
|
|
// try to locate the package file starting with the current package
|
|
current = this._package;
|
|
|
|
while (current !== undefined) {
|
|
|
|
// check if already know a package with that name
|
|
module = current.module(path);
|
|
|
|
if (module !== null && module._type === 'package') {
|
|
return module;
|
|
}
|
|
|
|
var description = internal.loadPackageDescription(path, current);
|
|
|
|
if (description !== null) {
|
|
module = this.createPackage(current, description);
|
|
|
|
if (module !== null) {
|
|
return module;
|
|
}
|
|
}
|
|
|
|
current = current._parent;
|
|
}
|
|
|
|
return null;
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief requires a module
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
Module.prototype.requireModule = function (unormalizedPath) {
|
|
var description;
|
|
var module;
|
|
var path;
|
|
|
|
// first get rid of any ".." and "."
|
|
path = this.normalize(unormalizedPath);
|
|
|
|
// check if already know a module with that name
|
|
module = this._package.module(path);
|
|
|
|
if (module) {
|
|
if (module.type === 'package') {
|
|
module = null;
|
|
}
|
|
return module;
|
|
}
|
|
|
|
// first check: we are talking about module within a package
|
|
description = internal.loadPackageFile(path, this._package);
|
|
|
|
if (description !== null) {
|
|
module = this.createModule(description, 'module', this._package);
|
|
|
|
if (module !== null) {
|
|
this._package.defineModule(path, module);
|
|
return module;
|
|
}
|
|
}
|
|
|
|
// check if already know a module with that name
|
|
module = GlobalPackage.module(path);
|
|
|
|
if (module) {
|
|
if (module.type === 'package') {
|
|
module = null;
|
|
}
|
|
return module;
|
|
}
|
|
|
|
// second check: we are talking about a global module
|
|
description = internal.loadModuleFile(path);
|
|
|
|
if (description !== null) {
|
|
module = this.createModule(description, 'module', GlobalPackage);
|
|
|
|
if (module !== null) {
|
|
GlobalPackage.defineModule(path, module);
|
|
return module;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief loads a file and creates a new module descriptor
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
Module.prototype.require = function (unormalizedPath) {
|
|
var module;
|
|
var path;
|
|
|
|
// check if path points to a package or a module in a package
|
|
module = this.requirePackage(unormalizedPath);
|
|
|
|
if (module !== null) {
|
|
return module.exports;
|
|
}
|
|
|
|
// try to load a global module into the current package
|
|
module = this.requireModule(unormalizedPath);
|
|
|
|
if (module !== null) {
|
|
return module.exports;
|
|
}
|
|
|
|
throw "cannot locate module '" + unormalizedPath + "'"
|
|
+ " for package '" + this._package.id + "'"
|
|
+ " using module path '" + internal.MODULES_PATH + "'"
|
|
+ " and package path '" + this._package._paths + "'";
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief returns true if require found a file
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
Module.prototype.exists = function (path) {
|
|
return ModuleExistsCache[path];
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief normalizes a path
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
Module.prototype.normalize = function (path) {
|
|
return internal.normalizeModuleName(this.id, path);
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief unloads module
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
Module.prototype.unload = function (path) {
|
|
if (! path) {
|
|
return;
|
|
}
|
|
|
|
var norm = module.normalize(path);
|
|
var m = GlobalPackage.module(norm);
|
|
|
|
if (m === null) {
|
|
return;
|
|
}
|
|
|
|
if (m._type === 'system') {
|
|
return;
|
|
}
|
|
|
|
if ( norm === "/org/arangodb"
|
|
|| norm === "/org/arangodb/actions"
|
|
|| norm === "/org/arangodb/arango-collection"
|
|
|| norm === "/org/arangodb/arango-database"
|
|
|| norm === "/org/arangodb/arango-error"
|
|
|| norm === "/org/arangodb/arango-statement"
|
|
|| norm === "/org/arangodb/shaped-json") {
|
|
return;
|
|
}
|
|
|
|
GlobalPackage.clearModule(norm);
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief unloads module
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
Module.prototype.unloadAll = function () {
|
|
var i;
|
|
var names;
|
|
|
|
names = GlobalPackage.moduleNames();
|
|
|
|
for (i = 0; i < names.length; ++i) {
|
|
this.unload(names[i]);
|
|
}
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief prints a module
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
Module.prototype._PRINT = function () {
|
|
var parent = "";
|
|
|
|
if (this._package._parent !== undefined) {
|
|
parent = ', parent package "' + this._package._parent.id + '"';
|
|
}
|
|
|
|
internal.output('[module "' + this.id + '"'
|
|
+ ', type "' + this._type + '"'
|
|
+ ', package "' + this._package.id + '"'
|
|
+ parent
|
|
+ ', origin "' + this._origin + '"'
|
|
+ ']');
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @}
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// --SECTION-- private package methods
|
|
// -----------------------------------------------------------------------------
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @addtogroup ArangoDB
|
|
/// @{
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief prints a package
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
Package.prototype._PRINT = function () {
|
|
var parent = "";
|
|
|
|
if (this._parent !== undefined) {
|
|
parent = ', parent "' + this._package._parent.id + '"';
|
|
}
|
|
|
|
internal.output('[module "' + this.id + '"'
|
|
+ ', path "' + this._path + '"'
|
|
+ parent
|
|
+ ']');
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @}
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// --SECTION-- private app methods
|
|
// -----------------------------------------------------------------------------
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @addtogroup ArangoDB
|
|
/// @{
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief prints a package
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
ArangoApp.prototype._PRINT = function () {
|
|
var parent = "";
|
|
|
|
if (this._parent !== undefined) {
|
|
parent = ', parent "' + this._package._parent.id + '"';
|
|
}
|
|
|
|
internal.output('[app "' + this._name + ' (' + this._version + ')]');
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief returns the app root module
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
ArangoApp.prototype.createAppModule = function (type, rootPackage) {
|
|
var libpath;
|
|
var pkg;
|
|
|
|
if (type === undefined) {
|
|
type = 'lib';
|
|
}
|
|
|
|
if (this._manifest.hasOwnProperty(type)) {
|
|
libpath = this._path + "/" + this._manifest[type];
|
|
}
|
|
else {
|
|
libpath = this._path;
|
|
}
|
|
|
|
pkg = new Package("application",
|
|
{name: "application '" + this._name + "'"},
|
|
rootPackage,
|
|
[ libpath ]);
|
|
|
|
return new Module("application", 'application', pkg);
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @brief loads an init file from an application path
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
ArangoApp.prototype.loadAppScript = function (appModule, file, appContext, context) {
|
|
var sandbox = {};
|
|
var fileContent;
|
|
var content;
|
|
var appPath;
|
|
var full;
|
|
var key;
|
|
|
|
appPath = this._path;
|
|
|
|
try {
|
|
full = appPath + "/" + file;
|
|
fileContent = internal.read(full);
|
|
}
|
|
catch (err1) {
|
|
throw "cannot read file '" + full + "': " + err1 + " - " + err1.stack;
|
|
}
|
|
|
|
if (context !== undefined) {
|
|
for (key in context) {
|
|
if (context.hasOwnProperty(key) && key !== "__myenv__") {
|
|
sandbox[key] = context[key];
|
|
}
|
|
}
|
|
}
|
|
|
|
sandbox.module = appModule;
|
|
sandbox.applicationContext = appContext;
|
|
|
|
sandbox.require = function (path) {
|
|
return appModule.require(path);
|
|
};
|
|
|
|
content = "(function (__myenv__) {";
|
|
|
|
for (key in sandbox) {
|
|
if (sandbox.hasOwnProperty(key)) {
|
|
content += "var " + key + " = __myenv__['" + key + "'];";
|
|
}
|
|
}
|
|
|
|
content += "delete __myenv__;"
|
|
+ fileContent
|
|
+ "\n});";
|
|
|
|
try {
|
|
var fun = internal.executeScript(content, undefined, full);
|
|
|
|
if (fun === undefined) {
|
|
throw "cannot create application script: " + content;
|
|
}
|
|
|
|
fun(sandbox);
|
|
}
|
|
catch (err2) {
|
|
throw "JavaScript exception in application file '"
|
|
+ full + "': " + err2+ " - " + err2.stack;
|
|
}
|
|
};
|
|
|
|
}());
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
/// @}
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// --SECTION-- END-OF-FILE
|
|
// -----------------------------------------------------------------------------
|
|
|
|
// Local Variables:
|
|
// mode: outline-minor
|
|
// outline-regexp: "/// @brief\\|/// @addtogroup\\|/// @page\\|// --SECTION--\\|/// @\\}\\|/\\*jslint"
|
|
// End:
|