mirror of
https://github.com/node-red/node-red.git
synced 2023-10-10 13:36:53 +02:00
initial support for npm module installation
This commit is contained in:
parent
c40412d7c6
commit
d51aefa156
@ -1,3 +1,279 @@
|
|||||||
|
<script type="text/html" data-template-name="library-config">
|
||||||
|
<div class="form-row">
|
||||||
|
<label for="node-config-input-name"><i class="fa fa-tag"></i> <span data-i18n="common.label.name"></span></label>
|
||||||
|
<div style="display: inline-block; width: calc(100% - 105px)">
|
||||||
|
<input type="text" id="node-config-input-name" data-i18n="[placeholder]common.label.name">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-row" style="margin-bottom: 2px; margin-top: 15px;">
|
||||||
|
<label style="width: 65%;"><i class="fa fa-cog"></i> <span>Import Libraries</span></label>
|
||||||
|
<input type="checkbox" id="node-config-input-showAll" style="display: inline-block; width: auto; vertical-align: top;">
|
||||||
|
<label for="node-config-input-showAll" style="width: auto;">Show All Libraries</label>
|
||||||
|
</div>
|
||||||
|
<div class="form-row "">
|
||||||
|
<ol id="node-input-libs-container"></ol>
|
||||||
|
</div>
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
<script type="text/javascript">
|
||||||
|
// object that maps from library name to its descriptor
|
||||||
|
var allLibs = {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add library descriptor
|
||||||
|
* @param {string} name - library name
|
||||||
|
*/
|
||||||
|
function addLib(name) {
|
||||||
|
var item = allLibs[name]
|
||||||
|
if (item) {
|
||||||
|
item.count++;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
allLibs[name] = {
|
||||||
|
name: name,
|
||||||
|
count: 1
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove library descriptor if reference count=0
|
||||||
|
* @param {string} name - library name
|
||||||
|
*/
|
||||||
|
function removeLib(name) {
|
||||||
|
var item = allLibs[name];
|
||||||
|
if (item) {
|
||||||
|
item.count--;
|
||||||
|
if (item.count === 0) {
|
||||||
|
delete allLibs[name];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function currentLibs() {
|
||||||
|
var result = [];
|
||||||
|
var libs = $("#node-input-libs-container").editableList("items");
|
||||||
|
libs.each(function(i) {
|
||||||
|
var item = $(this);
|
||||||
|
var n = item.find(".node-input-libs-val").val();
|
||||||
|
if (n && (n !== "")) {
|
||||||
|
result.push(n);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate library spec including conflicts with other specs
|
||||||
|
* @param {string} name - library name
|
||||||
|
*/
|
||||||
|
function checkLib(name) {
|
||||||
|
var m0 = name.match(/^([^@]+)(@.*)?$/);
|
||||||
|
if (m0) {
|
||||||
|
var lname0 = m0[1];
|
||||||
|
var ver0 = m0[2];
|
||||||
|
var ok = true;
|
||||||
|
var current = currentLibs(libs);
|
||||||
|
var libs = Object.keys(allLibs).concat(current);
|
||||||
|
libs.forEach(function(lib) {
|
||||||
|
if (name !== lib) {
|
||||||
|
var m1 = lib.match(/^([^@]+)(@.*)?$/);
|
||||||
|
if (m1) {
|
||||||
|
var lname1 = m1[1];
|
||||||
|
var ver1 = m1[2];
|
||||||
|
if ((lname1 === lname0) && (ver0 !== ver1)) {
|
||||||
|
ok = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update library list
|
||||||
|
*/
|
||||||
|
function updateLib(done) {
|
||||||
|
allLibs = {};
|
||||||
|
RED.nodes.eachConfig(function(n) {
|
||||||
|
if (n.type === "library-config") {
|
||||||
|
var libs = n.libs || [];
|
||||||
|
libs.forEach(function(lib) { addLib(lib.name); });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
var root = RED.settings.apiRootUrl || "";
|
||||||
|
$.ajax({
|
||||||
|
headers: {
|
||||||
|
"Accept":"application/json"
|
||||||
|
},
|
||||||
|
cache: false,
|
||||||
|
url: root + "/function/modules",
|
||||||
|
success: function(data) {
|
||||||
|
data.forEach(function(lib) {
|
||||||
|
var name = lib.name;
|
||||||
|
if (lib.version && (lib.version !== "")) {
|
||||||
|
name += "@" +lib.version;
|
||||||
|
}
|
||||||
|
addLib(name);
|
||||||
|
});
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register NPM library configuration node
|
||||||
|
*/
|
||||||
|
RED.nodes.registerType("library-config", {
|
||||||
|
category: "config",
|
||||||
|
defaults: {
|
||||||
|
name: {value: ""},
|
||||||
|
libs: {value: []},
|
||||||
|
showAll: {value: false}
|
||||||
|
},
|
||||||
|
label: function() {
|
||||||
|
if (this.name && (this.name !== "")) {
|
||||||
|
return this.name;
|
||||||
|
}
|
||||||
|
return "Library Set:"+this.id;
|
||||||
|
},
|
||||||
|
oneditprepare: function() {
|
||||||
|
var node = this;
|
||||||
|
var popovers = {};
|
||||||
|
$("#node-input-libs-container").css('min-height','250px').css('min-width','450px').editableList({
|
||||||
|
addItem: function(container,i,opt) {
|
||||||
|
var disabled = (opt && !!opt.unused);
|
||||||
|
var row = $("<div/>").appendTo(container);
|
||||||
|
var fvar = $("<input/>", {
|
||||||
|
class: "node-input-libs-var",
|
||||||
|
placeholder: RED._("node-red:function.require.var"),
|
||||||
|
type: "text",
|
||||||
|
disabled: disabled
|
||||||
|
}).css({
|
||||||
|
width: "90px",
|
||||||
|
"margin-left": "5px"
|
||||||
|
}).appendTo(row);
|
||||||
|
var fmodule = $("<input/>", {
|
||||||
|
class: "node-input-libs-val",
|
||||||
|
placeholder: RED._("node-red:function.require.module"),
|
||||||
|
type: "text",
|
||||||
|
disabled: disabled
|
||||||
|
}).css({
|
||||||
|
width: "280px",
|
||||||
|
"margin-left": "5px"
|
||||||
|
}).appendTo(row);
|
||||||
|
|
||||||
|
fvar.on("change", function (e) {
|
||||||
|
opt.vname = $(this).val();
|
||||||
|
});
|
||||||
|
|
||||||
|
fmodule.on("change", function (e) {
|
||||||
|
var val = $(this).val();
|
||||||
|
if (!checkLib(val)) {
|
||||||
|
$(this).addClass("input-error");
|
||||||
|
var popover = RED.popover.tooltip($(this), "conflict in module spec");
|
||||||
|
popovers[i] = popover;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$(this).removeClass("input-error");
|
||||||
|
var popover = popovers[i];
|
||||||
|
if (popover) {
|
||||||
|
popover.setContent(function () {
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
opt.name = val;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (opt) {
|
||||||
|
if (opt.vname) {
|
||||||
|
fvar.val(opt.vname);
|
||||||
|
}
|
||||||
|
if (opt.name) {
|
||||||
|
fmodule.val(opt.name);
|
||||||
|
addLib(opt.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
removeItem: function(item) {
|
||||||
|
removeLib(item.name);
|
||||||
|
},
|
||||||
|
removable: true,
|
||||||
|
sortable: true
|
||||||
|
});
|
||||||
|
var libs = node.libs ? node.libs : [];
|
||||||
|
libs.forEach(function(lib) {
|
||||||
|
var conf = {
|
||||||
|
vname: lib.vname,
|
||||||
|
name: lib.name
|
||||||
|
};
|
||||||
|
$("#node-input-libs-container").editableList("addItem", conf);
|
||||||
|
});
|
||||||
|
$("#node-config-input-showAll").on("change", function () {
|
||||||
|
var checked = $(this).prop("checked");
|
||||||
|
var fun = null;
|
||||||
|
if (checked) {
|
||||||
|
fun = null;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
fun = function(item) {
|
||||||
|
return (!item.unused) ||
|
||||||
|
((item.vname && (item.vname !== "")) &&
|
||||||
|
(item.name && (item.name !== "")));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
$("#node-input-libs-container").editableList("filter", fun);
|
||||||
|
});
|
||||||
|
updateLib(function () {
|
||||||
|
Object.values(allLibs).forEach(function(lib) {
|
||||||
|
function pred(x) {
|
||||||
|
return (x.name === lib.name);
|
||||||
|
}
|
||||||
|
if (libs.find(pred)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var conf = {
|
||||||
|
vname: "",
|
||||||
|
name: lib.name,
|
||||||
|
unused: true
|
||||||
|
};
|
||||||
|
$("#node-input-libs-container").editableList("addItem", conf);
|
||||||
|
});
|
||||||
|
$("#node-config-input-showAll").change();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
oneditsave: function() {
|
||||||
|
var node = this;
|
||||||
|
var libs = $("#node-input-libs-container").editableList("items");
|
||||||
|
var oldLibs = node.libs || [];
|
||||||
|
var newLibs = [];
|
||||||
|
node.libs = newLibs;
|
||||||
|
libs.each(function(i) {
|
||||||
|
var item = $(this);
|
||||||
|
var v = item.find(".node-input-libs-var").val();
|
||||||
|
var n = item.find(".node-input-libs-val").val();
|
||||||
|
if (!v || (v === "") ||
|
||||||
|
!n || (n === "")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
newLibs.push({
|
||||||
|
vname: v,
|
||||||
|
name: n
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
oneditresize: function(size) {
|
||||||
|
var height = size.height;
|
||||||
|
$("#node-input-libs-container").editableList("height", height -60);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
<script type="text/html" data-template-name="function">
|
<script type="text/html" data-template-name="function">
|
||||||
<div class="form-row">
|
<div class="form-row">
|
||||||
@ -50,6 +326,15 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div id="func-tab-config" style="display:none">
|
||||||
|
<div class="form-row">
|
||||||
|
<label><i class="fa fa-wrench"></i> <span>Use Library</span></label>
|
||||||
|
<input type="text" id="node-input-libs">
|
||||||
|
</div>
|
||||||
|
<div class="form-row" style="height: 250px; min-height: 150px">
|
||||||
|
<ol id="node-input-require-container"></ol>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
@ -64,7 +349,8 @@
|
|||||||
outputs: {value:1},
|
outputs: {value:1},
|
||||||
noerr: {value:0,required:true,validate:function(v) { return !v; }},
|
noerr: {value:0,required:true,validate:function(v) { return !v; }},
|
||||||
initialize: {value:""},
|
initialize: {value:""},
|
||||||
finalize: {value:""}
|
finalize: {value:""},
|
||||||
|
libs: {value:"", type:"library-config", required:false}
|
||||||
},
|
},
|
||||||
inputs:1,
|
inputs:1,
|
||||||
outputs:1,
|
outputs:1,
|
||||||
@ -97,6 +383,11 @@
|
|||||||
id: "func-tab-finalize",
|
id: "func-tab-finalize",
|
||||||
label: that._("function.label.finalize")
|
label: that._("function.label.finalize")
|
||||||
});
|
});
|
||||||
|
tabs.addTab({
|
||||||
|
id: "func-tab-config",
|
||||||
|
label: "Config"
|
||||||
|
});
|
||||||
|
|
||||||
tabs.activateTab("func-tab-body");
|
tabs.activateTab("func-tab-body");
|
||||||
|
|
||||||
$( "#node-input-outputs" ).spinner({
|
$( "#node-input-outputs" ).spinner({
|
||||||
@ -205,7 +496,56 @@
|
|||||||
RED.popover.tooltip($("#node-function-expand-js"), RED._("node-red:common.label.expand"));
|
RED.popover.tooltip($("#node-function-expand-js"), RED._("node-red:common.label.expand"));
|
||||||
RED.popover.tooltip($("#node-finalize-expand-js"), RED._("node-red:common.label.expand"));
|
RED.popover.tooltip($("#node-finalize-expand-js"), RED._("node-red:common.label.expand"));
|
||||||
|
|
||||||
|
$("#node-input-require-container").css('min-height','250px').css('min-width','450px').editableList({
|
||||||
|
addItem: function(container,i,opt) {
|
||||||
|
var row = $("<div/>").appendTo(container);
|
||||||
|
var fvar = $("<input/>", {
|
||||||
|
class: "node-input-require-var",
|
||||||
|
placeholder: RED._("node-red:function.require.var"),
|
||||||
|
type: "text",
|
||||||
|
disabled: true
|
||||||
|
}).css({
|
||||||
|
width: "130px",
|
||||||
|
"margin-left": "5px"
|
||||||
|
}).appendTo(row);
|
||||||
|
var fmodule = $("<input/>", {
|
||||||
|
class: "node-input-require-val",
|
||||||
|
placeholder: RED._("node-red:function.require.module"),
|
||||||
|
type: "text",
|
||||||
|
disabled: true
|
||||||
|
}).css({
|
||||||
|
width: "390px",
|
||||||
|
"margin-left": "5px"
|
||||||
|
}).appendTo(row);
|
||||||
|
if (opt) {
|
||||||
|
if (opt.vname) {
|
||||||
|
fvar.val(opt.vname);
|
||||||
|
}
|
||||||
|
if (opt.name) {
|
||||||
|
fmodule.val(opt.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
addButton: false,
|
||||||
|
removable: false
|
||||||
|
});
|
||||||
|
|
||||||
|
function updateLibs() {
|
||||||
|
var id = $("#node-input-libs").val();
|
||||||
|
var node = RED.nodes.node(id);
|
||||||
|
if (node && node.libs) {
|
||||||
|
var libs = node.libs;
|
||||||
|
$("#node-input-require-container").editableList("empty");
|
||||||
|
libs.forEach(function (r) {
|
||||||
|
$("#node-input-require-container").editableList("addItem", r);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateLibs();
|
||||||
|
$("#node-input-libs").on("change", function () {
|
||||||
|
updateLibs();
|
||||||
|
});
|
||||||
},
|
},
|
||||||
oneditsave: function() {
|
oneditsave: function() {
|
||||||
var node = this;
|
var node = this;
|
||||||
@ -271,6 +611,7 @@
|
|||||||
this.editor.resize();
|
this.editor.resize();
|
||||||
this.finalizeEditor.resize();
|
this.finalizeEditor.resize();
|
||||||
|
|
||||||
|
$("#node-input-require-container").css("height", (height -155)+"px");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
@ -16,6 +16,19 @@
|
|||||||
|
|
||||||
module.exports = function(RED) {
|
module.exports = function(RED) {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
|
function LibsConfigNode(n) {
|
||||||
|
RED.nodes.createNode(this, n);
|
||||||
|
this.name = n.name;
|
||||||
|
this.libs = n.libs;
|
||||||
|
}
|
||||||
|
RED.nodes.registerType("library-config", LibsConfigNode);
|
||||||
|
|
||||||
|
RED.httpNode.get("/function/modules", function (req, res) {
|
||||||
|
var list = RED.nodes.listNPMModules();
|
||||||
|
res.send(list);
|
||||||
|
});
|
||||||
|
|
||||||
var util = require("util");
|
var util = require("util");
|
||||||
var vm = require("vm");
|
var vm = require("vm");
|
||||||
|
|
||||||
@ -88,12 +101,16 @@ module.exports = function(RED) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function FunctionNode(n) {
|
function FunctionNode(n) {
|
||||||
RED.nodes.createNode(this,n);
|
var libConf = RED.nodes.getNode(n.libs);
|
||||||
|
var libs = libConf ? libConf.libs : [];
|
||||||
|
n.modules = libs.map(x => x.name).filter(x => (x && (x !== "")));
|
||||||
|
var loadPromise = RED.nodes.createNode(this,n);
|
||||||
var node = this;
|
var node = this;
|
||||||
node.name = n.name;
|
node.name = n.name;
|
||||||
node.func = n.func;
|
node.func = n.func;
|
||||||
node.ini = n.initialize ? n.initialize.trim() : "";
|
node.ini = n.initialize ? n.initialize.trim() : "";
|
||||||
node.fin = n.finalize ? n.finalize.trim() : "";
|
node.fin = n.finalize ? n.finalize.trim() : "";
|
||||||
|
node.libs = libs;
|
||||||
|
|
||||||
var handleNodeDoneCall = true;
|
var handleNodeDoneCall = true;
|
||||||
|
|
||||||
@ -105,23 +122,23 @@ module.exports = function(RED) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var functionText = "var results = null;"+
|
var functionText = "var results = null;"+
|
||||||
"results = (async function(msg,__send__,__done__){ "+
|
"results = (async function(msg,__send__,__done__){ "+
|
||||||
"var __msgid__ = msg._msgid;"+
|
"var __msgid__ = msg._msgid;"+
|
||||||
"var node = {"+
|
"var node = {"+
|
||||||
"id:__node__.id,"+
|
"id:__node__.id,"+
|
||||||
"name:__node__.name,"+
|
"name:__node__.name,"+
|
||||||
"log:__node__.log,"+
|
"log:__node__.log,"+
|
||||||
"error:__node__.error,"+
|
"error:__node__.error,"+
|
||||||
"warn:__node__.warn,"+
|
"warn:__node__.warn,"+
|
||||||
"debug:__node__.debug,"+
|
"debug:__node__.debug,"+
|
||||||
"trace:__node__.trace,"+
|
"trace:__node__.trace,"+
|
||||||
"on:__node__.on,"+
|
"on:__node__.on,"+
|
||||||
"status:__node__.status,"+
|
"status:__node__.status,"+
|
||||||
"send:function(msgs,cloneMsg){ __node__.send(__send__,__msgid__,msgs,cloneMsg);},"+
|
"send:function(msgs,cloneMsg){ __node__.send(__send__,__msgid__,msgs,cloneMsg);},"+
|
||||||
"done:__done__"+
|
"done:__done__"+
|
||||||
"};\n"+
|
"};\n"+
|
||||||
node.func+"\n"+
|
node.func+"\n"+
|
||||||
"})(msg,__send__,__done__);";
|
"})(msg,__send__,__done__);";
|
||||||
var finScript = null;
|
var finScript = null;
|
||||||
var finOpt = null;
|
var finOpt = null;
|
||||||
node.topic = n.topic;
|
node.topic = n.topic;
|
||||||
@ -266,12 +283,47 @@ module.exports = function(RED) {
|
|||||||
};
|
};
|
||||||
sandbox.promisify = util.promisify;
|
sandbox.promisify = util.promisify;
|
||||||
}
|
}
|
||||||
var context = vm.createContext(sandbox);
|
|
||||||
try {
|
const RESOLVING = 0;
|
||||||
var iniScript = null;
|
const RESOLVED = 1;
|
||||||
var iniOpt = null;
|
const ERROR = 2;
|
||||||
if (node.ini && (node.ini !== "")) {
|
var state = RESOLVING;
|
||||||
var iniText = `
|
var messages = [];
|
||||||
|
var processMessage = (() => {});
|
||||||
|
|
||||||
|
node.on("input", function(msg,send,done) {
|
||||||
|
if(state === RESOLVING) {
|
||||||
|
messages.push({msg:msg, send:send, done:done});
|
||||||
|
}
|
||||||
|
else if(state === RESOLVED) {
|
||||||
|
processMessage(msg, send, done);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// wait for module installation
|
||||||
|
loadPromise.then(function () {
|
||||||
|
if (node.hasOwnProperty("libs")) {
|
||||||
|
var modules = node.libs;
|
||||||
|
modules.forEach(module => {
|
||||||
|
var vname = module.hasOwnProperty("vname") ? module.vname : null;
|
||||||
|
if (vname && (vname !== "")) {
|
||||||
|
sandbox[vname] = null;
|
||||||
|
try {
|
||||||
|
var lib = RED.require(module.name);
|
||||||
|
sandbox[vname] = lib;
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
node.warn("failed to load library: "+ module.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
var context = vm.createContext(sandbox);
|
||||||
|
try {
|
||||||
|
var iniScript = null;
|
||||||
|
var iniOpt = null;
|
||||||
|
if (node.ini && (node.ini !== "")) {
|
||||||
|
var iniText = `
|
||||||
(async function(__send__) {
|
(async function(__send__) {
|
||||||
var node = {
|
var node = {
|
||||||
id:__node__.id,
|
id:__node__.id,
|
||||||
@ -288,141 +340,130 @@ module.exports = function(RED) {
|
|||||||
};
|
};
|
||||||
`+ node.ini +`
|
`+ node.ini +`
|
||||||
})(__initSend__);`;
|
})(__initSend__);`;
|
||||||
iniOpt = createVMOpt(node, " setup");
|
iniOpt = createVMOpt(node, " setup");
|
||||||
iniScript = new vm.Script(iniText, iniOpt);
|
iniScript = new vm.Script(iniText, iniOpt);
|
||||||
}
|
}
|
||||||
node.script = vm.createScript(functionText, createVMOpt(node, ""));
|
node.script = vm.createScript(functionText, createVMOpt(node, ""));
|
||||||
if (node.fin && (node.fin !== "")) {
|
if (node.fin && (node.fin !== "")) {
|
||||||
var finText = "(function () {\n"+node.fin +"\n})();";
|
var finText = "(function () {\n"+node.fin +"\n})();";
|
||||||
finOpt = createVMOpt(node, " cleanup");
|
finOpt = createVMOpt(node, " cleanup");
|
||||||
finScript = new vm.Script(finText, finOpt);
|
finScript = new vm.Script(finText, finOpt);
|
||||||
}
|
}
|
||||||
var promise = Promise.resolve();
|
var promise = Promise.resolve();
|
||||||
if (iniScript) {
|
if (iniScript) {
|
||||||
context.__initSend__ = function(msgs) { node.send(msgs); };
|
context.__initSend__ = function(msgs) { node.send(msgs); };
|
||||||
promise = iniScript.runInContext(context, iniOpt);
|
promise = iniScript.runInContext(context, iniOpt);
|
||||||
}
|
}
|
||||||
|
|
||||||
function processMessage(msg, send, done) {
|
processMessage = function (msg, send, done) {
|
||||||
var start = process.hrtime();
|
var start = process.hrtime();
|
||||||
context.msg = msg;
|
context.msg = msg;
|
||||||
context.__send__ = send;
|
context.__send__ = send;
|
||||||
context.__done__ = done;
|
context.__done__ = done;
|
||||||
|
|
||||||
node.script.runInContext(context);
|
node.script.runInContext(context);
|
||||||
context.results.then(function(results) {
|
context.results.then(function(results) {
|
||||||
sendResults(node,send,msg._msgid,results,false);
|
sendResults(node,send,msg._msgid,results,false);
|
||||||
if (handleNodeDoneCall) {
|
if (handleNodeDoneCall) {
|
||||||
done();
|
done();
|
||||||
}
|
}
|
||||||
|
|
||||||
var duration = process.hrtime(start);
|
var duration = process.hrtime(start);
|
||||||
var converted = Math.floor((duration[0] * 1e9 + duration[1])/10000)/100;
|
var converted = Math.floor((duration[0] * 1e9 + duration[1])/10000)/100;
|
||||||
node.metric("duration", msg, converted);
|
node.metric("duration", msg, converted);
|
||||||
if (process.env.NODE_RED_FUNCTION_TIME) {
|
if (process.env.NODE_RED_FUNCTION_TIME) {
|
||||||
node.status({fill:"yellow",shape:"dot",text:""+converted});
|
node.status({fill:"yellow",shape:"dot",text:""+converted});
|
||||||
}
|
}
|
||||||
}).catch(err => {
|
}).catch(err => {
|
||||||
if ((typeof err === "object") && err.hasOwnProperty("stack")) {
|
if ((typeof err === "object") && err.hasOwnProperty("stack")) {
|
||||||
//remove unwanted part
|
//remove unwanted part
|
||||||
var index = err.stack.search(/\n\s*at ContextifyScript.Script.runInContext/);
|
var index = err.stack.search(/\n\s*at ContextifyScript.Script.runInContext/);
|
||||||
err.stack = err.stack.slice(0, index).split('\n').slice(0,-1).join('\n');
|
err.stack = err.stack.slice(0, index).split('\n').slice(0,-1).join('\n');
|
||||||
var stack = err.stack.split(/\r?\n/);
|
var stack = err.stack.split(/\r?\n/);
|
||||||
|
|
||||||
//store the error in msg to be used in flows
|
//store the error in msg to be used in flows
|
||||||
msg.error = err;
|
msg.error = err;
|
||||||
|
|
||||||
var line = 0;
|
var line = 0;
|
||||||
var errorMessage;
|
var errorMessage;
|
||||||
if (stack.length > 0) {
|
if (stack.length > 0) {
|
||||||
while (line < stack.length && stack[line].indexOf("ReferenceError") !== 0) {
|
while (line < stack.length && stack[line].indexOf("ReferenceError") !== 0) {
|
||||||
line++;
|
line++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (line < stack.length) {
|
if (line < stack.length) {
|
||||||
errorMessage = stack[line];
|
errorMessage = stack[line];
|
||||||
var m = /:(\d+):(\d+)$/.exec(stack[line+1]);
|
var m = /:(\d+):(\d+)$/.exec(stack[line+1]);
|
||||||
if (m) {
|
if (m) {
|
||||||
var lineno = Number(m[1])-1;
|
var lineno = Number(m[1])-1;
|
||||||
var cha = m[2];
|
var cha = m[2];
|
||||||
errorMessage += " (line "+lineno+", col "+cha+")";
|
errorMessage += " (line "+lineno+", col "+cha+")";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!errorMessage) {
|
||||||
|
errorMessage = err.toString();
|
||||||
|
}
|
||||||
|
done(errorMessage);
|
||||||
}
|
}
|
||||||
if (!errorMessage) {
|
else if (typeof err === "string") {
|
||||||
errorMessage = err.toString();
|
done(err);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
done(JSON.stringify(err));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
node.on("close", function() {
|
||||||
|
if (finScript) {
|
||||||
|
try {
|
||||||
|
finScript.runInContext(context, finOpt);
|
||||||
|
}
|
||||||
|
catch (err) {
|
||||||
|
node.error(err);
|
||||||
}
|
}
|
||||||
done(errorMessage);
|
|
||||||
}
|
}
|
||||||
else if (typeof err === "string") {
|
while (node.outstandingTimers.length > 0) {
|
||||||
done(err);
|
clearTimeout(node.outstandingTimers.pop());
|
||||||
}
|
}
|
||||||
else {
|
while (node.outstandingIntervals.length > 0) {
|
||||||
done(JSON.stringify(err));
|
clearInterval(node.outstandingIntervals.pop());
|
||||||
|
}
|
||||||
|
if (node.clearStatus) {
|
||||||
|
node.status({});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
const RESOLVING = 0;
|
promise.then(function (v) {
|
||||||
const RESOLVED = 1;
|
var msgs = messages;
|
||||||
const ERROR = 2;
|
|
||||||
var state = RESOLVING;
|
|
||||||
var messages = [];
|
|
||||||
|
|
||||||
node.on("input", function(msg,send,done) {
|
|
||||||
if(state === RESOLVING) {
|
|
||||||
messages.push({msg:msg, send:send, done:done});
|
|
||||||
}
|
|
||||||
else if(state === RESOLVED) {
|
|
||||||
processMessage(msg, send, done);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
node.on("close", function() {
|
|
||||||
if (finScript) {
|
|
||||||
try {
|
|
||||||
finScript.runInContext(context, finOpt);
|
|
||||||
}
|
|
||||||
catch (err) {
|
|
||||||
node.error(err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
while (node.outstandingTimers.length > 0) {
|
|
||||||
clearTimeout(node.outstandingTimers.pop());
|
|
||||||
}
|
|
||||||
while (node.outstandingIntervals.length > 0) {
|
|
||||||
clearInterval(node.outstandingIntervals.pop());
|
|
||||||
}
|
|
||||||
if (node.clearStatus) {
|
|
||||||
node.status({});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
promise.then(function (v) {
|
|
||||||
var msgs = messages;
|
|
||||||
messages = [];
|
|
||||||
while (msgs.length > 0) {
|
|
||||||
msgs.forEach(function (s) {
|
|
||||||
processMessage(s.msg, s.send, s.done);
|
|
||||||
});
|
|
||||||
msgs = messages;
|
|
||||||
messages = [];
|
messages = [];
|
||||||
}
|
while (msgs.length > 0) {
|
||||||
state = RESOLVED;
|
msgs.forEach(function (s) {
|
||||||
}).catch((error) => {
|
processMessage(s.msg, s.send, s.done);
|
||||||
messages = [];
|
});
|
||||||
state = ERROR;
|
msgs = messages;
|
||||||
node.error(error);
|
messages = [];
|
||||||
});
|
}
|
||||||
|
state = RESOLVED;
|
||||||
|
}).catch((error) => {
|
||||||
|
messages = [];
|
||||||
|
state = ERROR;
|
||||||
|
node.error(error);
|
||||||
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
catch(err) {
|
catch(err) {
|
||||||
// eg SyntaxError - which v8 doesn't include line number information
|
// eg SyntaxError - which v8 doesn't include line number information
|
||||||
// so we can't do better than this
|
// so we can't do better than this
|
||||||
updateErrorInfo(err);
|
updateErrorInfo(err);
|
||||||
node.error(err);
|
node.error(err);
|
||||||
}
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
RED.nodes.registerType("function",FunctionNode);
|
RED.nodes.registerType("function",FunctionNode, {
|
||||||
|
dynamicModuleList: "modules"
|
||||||
|
});
|
||||||
RED.library.register("functions");
|
RED.library.register("functions");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -212,12 +212,17 @@
|
|||||||
"function": "Function",
|
"function": "Function",
|
||||||
"initialize": "Setup",
|
"initialize": "Setup",
|
||||||
"finalize": "Close",
|
"finalize": "Close",
|
||||||
"outputs": "Outputs"
|
"outputs": "Outputs",
|
||||||
|
"require": "Require"
|
||||||
},
|
},
|
||||||
"text": {
|
"text": {
|
||||||
"initialize": "// Code added here will be run once\n// whenever the node is deployed.\n",
|
"initialize": "// Code added here will be run once\n// whenever the node is deployed.\n",
|
||||||
"finalize": "// Code added here will be run when the\n// node is being stopped or re-deployed.\n"
|
"finalize": "// Code added here will be run when the\n// node is being stopped or re-deployed.\n"
|
||||||
},
|
},
|
||||||
|
"require": {
|
||||||
|
"var": "name",
|
||||||
|
"module": "module"
|
||||||
|
},
|
||||||
"error": {
|
"error": {
|
||||||
"inputListener":"Cannot add listener to 'input' event within Function",
|
"inputListener":"Cannot add listener to 'input' event within Function",
|
||||||
"non-message-returned":"Function tried to send a message of type __type__"
|
"non-message-returned":"Function tried to send a message of type __type__"
|
||||||
|
@ -45,6 +45,10 @@ function requireModule(name) {
|
|||||||
var relPath = path.relative(__dirname, moduleInfo.path);
|
var relPath = path.relative(__dirname, moduleInfo.path);
|
||||||
return require(relPath);
|
return require(relPath);
|
||||||
} else {
|
} else {
|
||||||
|
var npm = runtime.nodes.loadNPMModule(name);
|
||||||
|
if (npm) {
|
||||||
|
return npm;
|
||||||
|
}
|
||||||
var err = new Error(`Cannot find module '${name}'`);
|
var err = new Error(`Cannot find module '${name}'`);
|
||||||
err.code = "MODULE_NOT_FOUND";
|
err.code = "MODULE_NOT_FOUND";
|
||||||
throw err;
|
throw err;
|
||||||
@ -79,7 +83,7 @@ function createNodeApi(node) {
|
|||||||
httpAdmin: runtime.adminApp,
|
httpAdmin: runtime.adminApp,
|
||||||
server: runtime.server
|
server: runtime.server
|
||||||
}
|
}
|
||||||
copyObjectProperties(runtime.nodes,red.nodes,["createNode","getNode","eachNode","addCredentials","getCredentials","deleteCredentials" ]);
|
copyObjectProperties(runtime.nodes,red.nodes,["createNode","getNode","eachNode","addCredentials","getCredentials","deleteCredentials", "listNPMModules"]);
|
||||||
red.nodes.registerType = function(type,constructor,opts) {
|
red.nodes.registerType = function(type,constructor,opts) {
|
||||||
runtime.nodes.registerType(node.id,type,constructor,opts);
|
runtime.nodes.registerType(node.id,type,constructor,opts);
|
||||||
}
|
}
|
||||||
|
@ -26,6 +26,7 @@ var flows = require("../flows");
|
|||||||
var flowUtil = require("../flows/util")
|
var flowUtil = require("../flows/util")
|
||||||
var context = require("./context");
|
var context = require("./context");
|
||||||
var Node = require("./Node");
|
var Node = require("./Node");
|
||||||
|
var npmModule = require("./npmModule");
|
||||||
var log;
|
var log;
|
||||||
|
|
||||||
const events = require("@node-red/util").events;
|
const events = require("@node-red/util").events;
|
||||||
@ -49,6 +50,7 @@ function registerType(nodeSet,type,constructor,opts) {
|
|||||||
type = nodeSet;
|
type = nodeSet;
|
||||||
nodeSet = "";
|
nodeSet = "";
|
||||||
}
|
}
|
||||||
|
var dynModule = null;
|
||||||
if (opts) {
|
if (opts) {
|
||||||
if (opts.credentials) {
|
if (opts.credentials) {
|
||||||
credentials.register(type,opts.credentials);
|
credentials.register(type,opts.credentials);
|
||||||
@ -60,7 +62,11 @@ function registerType(nodeSet,type,constructor,opts) {
|
|||||||
log.warn("["+type+"] "+err.message);
|
log.warn("["+type+"] "+err.message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (opts.dynamicModuleList) {
|
||||||
|
dynModule = opts.dynamicModuleList;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
npmModule.register(type, dynModule);
|
||||||
if(!(constructor.prototype instanceof Node)) {
|
if(!(constructor.prototype instanceof Node)) {
|
||||||
if(Object.getPrototypeOf(constructor.prototype) === Object.prototype) {
|
if(Object.getPrototypeOf(constructor.prototype) === Object.prototype) {
|
||||||
util.inherits(constructor,Node);
|
util.inherits(constructor,Node);
|
||||||
@ -110,6 +116,7 @@ function createNode(node,def) {
|
|||||||
} else if (credentials.getDefinition(node.type)) {
|
} else if (credentials.getDefinition(node.type)) {
|
||||||
node.credentials = {};
|
node.credentials = {};
|
||||||
}
|
}
|
||||||
|
return npmModule.checkInstall(def);
|
||||||
}
|
}
|
||||||
|
|
||||||
function registerSubflow(nodeSet, subflow) {
|
function registerSubflow(nodeSet, subflow) {
|
||||||
@ -138,6 +145,7 @@ function init(runtime) {
|
|||||||
flows.init(runtime);
|
flows.init(runtime);
|
||||||
registry.init(runtime);
|
registry.init(runtime);
|
||||||
context.init(runtime.settings);
|
context.init(runtime.settings);
|
||||||
|
npmModule.init(runtime);
|
||||||
}
|
}
|
||||||
|
|
||||||
function disableNode(id) {
|
function disableNode(id) {
|
||||||
@ -261,5 +269,9 @@ module.exports = {
|
|||||||
// Contexts
|
// Contexts
|
||||||
loadContextsPlugin: context.load,
|
loadContextsPlugin: context.load,
|
||||||
closeContextsPlugin: context.close,
|
closeContextsPlugin: context.close,
|
||||||
listContextStores: context.listStores
|
listContextStores: context.listStores,
|
||||||
|
|
||||||
|
// NPM modules
|
||||||
|
listNPMModules: npmModule.list,
|
||||||
|
loadNPMModule: npmModule.load
|
||||||
};
|
};
|
||||||
|
281
packages/node_modules/@node-red/runtime/lib/nodes/npmModule.js
vendored
Normal file
281
packages/node_modules/@node-red/runtime/lib/nodes/npmModule.js
vendored
Normal file
@ -0,0 +1,281 @@
|
|||||||
|
/**
|
||||||
|
* Copyright JS Foundation and other contributors, http://js.foundation
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
**/
|
||||||
|
|
||||||
|
var path = require("path");
|
||||||
|
var fs = require("fs-extra");
|
||||||
|
var os = require("os");
|
||||||
|
var util = require("@node-red/registry/lib/util");
|
||||||
|
|
||||||
|
var api;
|
||||||
|
|
||||||
|
var runtime;
|
||||||
|
var settings;
|
||||||
|
var exec;
|
||||||
|
var log;
|
||||||
|
|
||||||
|
var npmCommand = (process.platform === "win32") ? "npm.cmd" : "npm";
|
||||||
|
|
||||||
|
var moduleProp = {};
|
||||||
|
var moduleBase = null;
|
||||||
|
|
||||||
|
var allowInstall = true;
|
||||||
|
var allowList = ["*"];
|
||||||
|
var denyList = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialise npm install module.
|
||||||
|
* @param {Object} _runtime - runtime object
|
||||||
|
*/
|
||||||
|
function init(_runtime) {
|
||||||
|
runtime = _runtime;
|
||||||
|
settings = _runtime.settings;
|
||||||
|
exec = _runtime.exec;
|
||||||
|
log = _runtime.log;
|
||||||
|
|
||||||
|
moduleProp = {};
|
||||||
|
moduleBase = settings.userDir || process.env.NODE_RED_HOME || ".";
|
||||||
|
|
||||||
|
if (settings.hasOwnProperty("externalModules")) {
|
||||||
|
var em = settings.externalModules;
|
||||||
|
if (em && em.hasOwnProperty("modules")) {
|
||||||
|
var mod = em.modules;
|
||||||
|
if (mod.hasOwnProperty("allowInstall")) {
|
||||||
|
allowInstall = mod.allowInstall;
|
||||||
|
}
|
||||||
|
if (mod.hasOwnProperty("allowList")) {
|
||||||
|
var alist = mod.allowList;
|
||||||
|
if (Array.isArray(alist)) {
|
||||||
|
allowList = alist;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
log.warn("unexpected value of externalModule.allowList in settings.js");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (mod.hasOwnProperty("denyList")) {
|
||||||
|
var dlist = mod.denyList;
|
||||||
|
if (Array.isArray(dlist)) {
|
||||||
|
denyList = dlist;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
log.warn("unexpected value of externalModule.denyList in settings.js");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register dynamic module installation property.
|
||||||
|
* @param {string} type - node type
|
||||||
|
* @param {string} prop - property name
|
||||||
|
*/
|
||||||
|
function register(type, prop) {
|
||||||
|
if (prop) {
|
||||||
|
moduleProp[type] = prop;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
delete moduleProp[prop]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get path to install modules
|
||||||
|
*/
|
||||||
|
function modulePath() { // takes variable length arguments in `arguments`
|
||||||
|
var result = moduleBase;
|
||||||
|
result = path.join(result, "lib", "node_modules");
|
||||||
|
for(var i = 0; i < arguments.length; i++) {
|
||||||
|
result = path.join(result, arguments[i]);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decompose NPM module specification string
|
||||||
|
* @param {string} module - module specification
|
||||||
|
*/
|
||||||
|
function moduleName(module) {
|
||||||
|
var match = /^([^@]+@.+)/.exec(module);
|
||||||
|
if (match) {
|
||||||
|
return [match[1], match[2]];
|
||||||
|
}
|
||||||
|
return [module, undefined];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get NPM module package info
|
||||||
|
* @param {string} name - module name
|
||||||
|
* @param {string} name - module version
|
||||||
|
*/
|
||||||
|
function infoNPM(name, ver) {
|
||||||
|
var path = modulePath(name, "package.json");
|
||||||
|
try {
|
||||||
|
var pkg = require(path);
|
||||||
|
return pkg;
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensure existance of module installation directory
|
||||||
|
*/
|
||||||
|
function ensureLibDirectory() {
|
||||||
|
var path = modulePath();
|
||||||
|
if (!fs.existsSync(path)) {
|
||||||
|
fs.mkdirSync(path, {
|
||||||
|
recursive: true
|
||||||
|
});
|
||||||
|
return fs.existsSync(path);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Install NPM module
|
||||||
|
* @param {string} module - module specification
|
||||||
|
*/
|
||||||
|
function installNPM(module) {
|
||||||
|
var [name, ver] = moduleName(module);
|
||||||
|
if (!ensureLibDirectory()) {
|
||||||
|
log.warn("failed to install: "+name);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var pkg = infoNPM(name, ver);
|
||||||
|
if (!pkg) {
|
||||||
|
var args = ["install", module];
|
||||||
|
var dir = modulePath();
|
||||||
|
return exec.run(npmCommand, args, {
|
||||||
|
cwd: dir
|
||||||
|
}, true).then(result => {
|
||||||
|
if (result && (result.code === 0)) {
|
||||||
|
log.info("successfully installed: "+name);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
log.warn("failed to install: "+name);
|
||||||
|
}
|
||||||
|
}).catch(e => {
|
||||||
|
var msg = e.hasOwnProperty("stderr") ? e.stderr : e;
|
||||||
|
log.warn("failed to install: "+name);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
log.info("already installed: "+name);
|
||||||
|
}
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check allowance of NPM module installation
|
||||||
|
* @param {string} name - module specification
|
||||||
|
*/
|
||||||
|
function isAllowed(name) {
|
||||||
|
if (!allowInstall) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
var [module, ver] = moduleName(name);
|
||||||
|
var aList = util.parseModuleList(allowList);
|
||||||
|
var dList = util.parseModuleList(denyList);
|
||||||
|
return util.checkModuleAllowed(module, ver, aList, dList);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check and install NPM module according to dynamic module specification
|
||||||
|
* @param {Object} node - node object
|
||||||
|
*/
|
||||||
|
function checkInstall(node) {
|
||||||
|
var name = null;
|
||||||
|
if(moduleProp.hasOwnProperty(node.type)) {
|
||||||
|
name = moduleProp[node.type];
|
||||||
|
}
|
||||||
|
var promises = [];
|
||||||
|
if (name && node.hasOwnProperty(name)) {
|
||||||
|
var modules = node[name];
|
||||||
|
modules.forEach(module => {
|
||||||
|
var name = module;
|
||||||
|
if ((typeof module === "object") &&
|
||||||
|
module &&
|
||||||
|
module.hasOwnProperty("name")) {
|
||||||
|
name = module.name;
|
||||||
|
}
|
||||||
|
if (isAllowed(name)) {
|
||||||
|
promises.push(installNPM(name));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
log.info("installation not allowed: "+name);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return Promise.all(promises);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load NPM module
|
||||||
|
* @param {string} module - module to load
|
||||||
|
*/
|
||||||
|
function load(module) {
|
||||||
|
try {
|
||||||
|
var [name, ver] = moduleName(module);
|
||||||
|
var path = modulePath(name);
|
||||||
|
var npm = require(path);
|
||||||
|
return npm;
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get list of installed modules
|
||||||
|
*/
|
||||||
|
function listModules() {
|
||||||
|
var modPath = modulePath();
|
||||||
|
if (!fs.existsSync(modPath)) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
var dir = fs.opendirSync(modPath);
|
||||||
|
var modules = [];
|
||||||
|
if (dir) {
|
||||||
|
var ent = null;
|
||||||
|
while (ent = dir.readSync()) {
|
||||||
|
var name = ent.name;
|
||||||
|
if (ent.isDirectory() &&
|
||||||
|
(name[0] !== ".")) {
|
||||||
|
var pkgPath = path.join(modPath, name, "package.json");
|
||||||
|
if (fs.existsSync(pkgPath)) {
|
||||||
|
var pkg = fs.readJSONSync(pkgPath);
|
||||||
|
var info = {
|
||||||
|
name: pkg.name,
|
||||||
|
version: pkg.version
|
||||||
|
};
|
||||||
|
modules.push(info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dir.closeSync();
|
||||||
|
}
|
||||||
|
return modules;
|
||||||
|
}
|
||||||
|
|
||||||
|
api = {
|
||||||
|
init: init,
|
||||||
|
register: register,
|
||||||
|
checkInstall: checkInstall,
|
||||||
|
load: load,
|
||||||
|
list: listModules
|
||||||
|
};
|
||||||
|
module.exports = api;
|
Loading…
x
Reference in New Issue
Block a user