mirror of
https://github.com/node-red/node-red.git
synced 2023-10-10 13:36:53 +02:00
Merge pull request #3064 from node-red/revert-external-modules-dir
Move externalModules back into the user dir
This commit is contained in:
commit
eb4625a0b9
@ -7,36 +7,53 @@
|
|||||||
padding: 0px;
|
padding: 0px;
|
||||||
}
|
}
|
||||||
#node-input-libs-container-row .red-ui-editableList-container li {
|
#node-input-libs-container-row .red-ui-editableList-container li {
|
||||||
padding:5px;
|
padding:0px;
|
||||||
}
|
}
|
||||||
#node-input-libs-container-row .red-ui-editableList-item-remove {
|
#node-input-libs-container-row .red-ui-editableList-item-remove {
|
||||||
right: 5px;
|
right: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#node-input-libs-container-row .red-ui-editableList-header {
|
||||||
|
display: flex;
|
||||||
|
background: var(--red-ui-tertiary-background);
|
||||||
|
padding-right: 75px;
|
||||||
|
}
|
||||||
|
#node-input-libs-container-row .red-ui-editableList-header > div {
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
.node-libs-entry {
|
.node-libs-entry {
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
.node-libs-entry .node-input-libs-var, .node-libs-entry .red-ui-typedInput-container {
|
|
||||||
flex-grow: 1;
|
|
||||||
}
|
|
||||||
.node-libs-entry > code,.node-libs-entry > span {
|
|
||||||
line-height: 30px;
|
|
||||||
}
|
|
||||||
.node-libs-entry > input[type=text] {
|
|
||||||
border-radius: 0;
|
|
||||||
border-left: none;
|
|
||||||
border-top: none;
|
|
||||||
border-right: none;
|
|
||||||
padding-top: 2px;
|
|
||||||
padding-bottom: 2px;
|
|
||||||
margin-top: 4px;
|
|
||||||
margin-bottom: 2px;
|
|
||||||
height: 26px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.node-libs-entry > span > i {
|
.node-libs-entry .red-ui-typedInput-container {
|
||||||
|
border-radius: 0;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
.node-libs-entry .red-ui-typedInput-type-select {
|
||||||
|
border-radius: 0 !important;
|
||||||
|
height: 34px;
|
||||||
|
}
|
||||||
|
.node-libs-entry > span > input[type=text] {
|
||||||
|
border-radius: 0;
|
||||||
|
border-top-color: var(--red-ui-form-background);
|
||||||
|
border-bottom-color: var(--red-ui-form-background);
|
||||||
|
border-right-color: var(--red-ui-form-background);
|
||||||
|
}
|
||||||
|
.node-libs-entry > span > input[type=text].input-error {
|
||||||
|
}
|
||||||
|
.node-libs-entry > span {
|
||||||
|
flex-grow: 1;
|
||||||
|
width: 50%;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
.node-libs-entry span .node-input-libs-var, .node-libs-entry span .red-ui-typedInput-container {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.node-libs-entry > span > span > i {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
.node-libs-entry > span.input-error > i {
|
.node-libs-entry > span > span.input-error > i {
|
||||||
display: inline;
|
display: inline;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -209,47 +226,24 @@
|
|||||||
})
|
})
|
||||||
|
|
||||||
var libList = $("#node-input-libs-container").css('min-height','100px').css('min-width','450px').editableList({
|
var libList = $("#node-input-libs-container").css('min-height','100px').css('min-width','450px').editableList({
|
||||||
|
header: $('<div><div data-i18n="node-red:function.require.moduleName"></div><div data-i18n="node-red:function.require.importAs"></div></div>'),
|
||||||
addItem: function(container,i,opt) {
|
addItem: function(container,i,opt) {
|
||||||
var parent = container.parent();
|
var parent = container.parent();
|
||||||
var row0 = $("<div/>").addClass("node-libs-entry").appendTo(container);
|
var row0 = $("<div/>").addClass("node-libs-entry").appendTo(container);
|
||||||
var fieldWidth = "260px";
|
var fmoduleSpan = $("<span>").appendTo(row0);
|
||||||
$('<code>const </code>').appendTo(row0);
|
|
||||||
var fvar = $("<input/>", {
|
|
||||||
class: "node-input-libs-var red-ui-font-code",
|
|
||||||
placeholder: RED._("node-red:function.require.var"),
|
|
||||||
type: "text"
|
|
||||||
}).css({
|
|
||||||
width: "120px",
|
|
||||||
"margin-left": "5px"
|
|
||||||
}).appendTo(row0).val(opt.var);
|
|
||||||
var vnameWarning = $('<span style="display:inline-block; width: 16px;"><i class="fa fa-warning"></i></span>').appendTo(row0);
|
|
||||||
RED.popover.tooltip(vnameWarning.find("i"),function() {
|
|
||||||
var val = fvar.val();
|
|
||||||
if (invalidModuleVNames.indexOf(val) !== -1) {
|
|
||||||
return RED._("node-red:function.error.moduleNameReserved",{name:val})
|
|
||||||
} else {
|
|
||||||
return RED._("node-red:function.error.moduleNameError",{name:val})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
$('<code> = require(</code>').appendTo(row0);
|
|
||||||
var fmodule = $("<input/>", {
|
var fmodule = $("<input/>", {
|
||||||
class: "node-input-libs-val",
|
class: "node-input-libs-val",
|
||||||
placeholder: RED._("node-red:function.require.module"),
|
placeholder: RED._("node-red:function.require.module"),
|
||||||
type: "text"
|
type: "text"
|
||||||
}).css({
|
}).css({
|
||||||
width: "180px",
|
}).appendTo(fmoduleSpan).typedInput({
|
||||||
}).appendTo(row0).typedInput({
|
|
||||||
types: typedModules,
|
types: typedModules,
|
||||||
default: usedModules.indexOf(opt.module) > -1 ? opt.module : "_custom_"
|
default: usedModules.indexOf(opt.module) > -1 ? opt.module : "_custom_"
|
||||||
});
|
});
|
||||||
if (usedModules.indexOf(opt.module) === -1) {
|
if (usedModules.indexOf(opt.module) === -1) {
|
||||||
fmodule.typedInput('value', opt.module);
|
fmodule.typedInput('value', opt.module);
|
||||||
}
|
}
|
||||||
|
var moduleWarning = $('<span style="position: absolute;right:2px;top:7px; display:inline-block; width: 16px;"><i class="fa fa-warning"></i></span>').appendTo(fmoduleSpan);
|
||||||
$('<code>)</code>').appendTo(row0);
|
|
||||||
|
|
||||||
var moduleWarning = $('<span style="display:inline-block; width: 16px;"><i class="fa fa-warning"></i></span>').appendTo(row0);
|
|
||||||
RED.popover.tooltip(moduleWarning.find("i"),function() {
|
RED.popover.tooltip(moduleWarning.find("i"),function() {
|
||||||
var val = fmodule.typedInput("type");
|
var val = fmodule.typedInput("type");
|
||||||
if (val === "_custom_") {
|
if (val === "_custom_") {
|
||||||
@ -264,6 +258,26 @@
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
var fvarSpan = $("<span>").appendTo(row0);
|
||||||
|
|
||||||
|
var fvar = $("<input/>", {
|
||||||
|
class: "node-input-libs-var red-ui-font-code",
|
||||||
|
placeholder: RED._("node-red:function.require.var"),
|
||||||
|
type: "text"
|
||||||
|
}).css({
|
||||||
|
}).appendTo(fvarSpan).val(opt.var);
|
||||||
|
var vnameWarning = $('<span style="position: absolute; right:2px;top:7px;display:inline-block; width: 16px;"><i class="fa fa-warning"></i></span>').appendTo(fvarSpan);
|
||||||
|
RED.popover.tooltip(vnameWarning.find("i"),function() {
|
||||||
|
var val = fvar.val();
|
||||||
|
if (invalidModuleVNames.indexOf(val) !== -1) {
|
||||||
|
return RED._("node-red:function.error.moduleNameReserved",{name:val})
|
||||||
|
} else {
|
||||||
|
return RED._("node-red:function.error.moduleNameError",{name:val})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
fvar.on("change keyup paste", function (e) {
|
fvar.on("change keyup paste", function (e) {
|
||||||
var v = $(this).val().trim();
|
var v = $(this).val().trim();
|
||||||
if (v === "" || / /.test(v) || invalidModuleVNames.indexOf(v) !== -1) {
|
if (v === "" || / /.test(v) || invalidModuleVNames.indexOf(v) !== -1) {
|
||||||
@ -280,7 +294,7 @@
|
|||||||
if (val === "_custom_") {
|
if (val === "_custom_") {
|
||||||
val = $(this).val();
|
val = $(this).val();
|
||||||
}
|
}
|
||||||
var varName = val.trim().replace(/^@/,"").replace(/@.*$/,"").replace(/[-_/]./g, function(v) { return v[1].toUpperCase() });
|
var varName = val.trim().replace(/^@/,"").replace(/@.*$/,"").replace(/[-_/].?/g, function(v) { return v[1]?v[1].toUpperCase():"" });
|
||||||
fvar.val(varName);
|
fvar.val(varName);
|
||||||
fvar.trigger("change");
|
fvar.trigger("change");
|
||||||
|
|
||||||
|
@ -290,6 +290,7 @@ module.exports = function(RED) {
|
|||||||
};
|
};
|
||||||
sandbox.promisify = util.promisify;
|
sandbox.promisify = util.promisify;
|
||||||
}
|
}
|
||||||
|
const moduleLoadPromises = [];
|
||||||
|
|
||||||
if (node.hasOwnProperty("libs")) {
|
if (node.hasOwnProperty("libs")) {
|
||||||
let moduleErrors = false;
|
let moduleErrors = false;
|
||||||
@ -303,25 +304,21 @@ module.exports = function(RED) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
sandbox[vname] = null;
|
sandbox[vname] = null;
|
||||||
try {
|
var spec = module.module;
|
||||||
var spec = module.module;
|
if (spec && (spec !== "")) {
|
||||||
if (spec && (spec !== "")) {
|
moduleLoadPromises.push(RED.import(module.module).then(lib => {
|
||||||
var lib = RED.require(module.module);
|
|
||||||
sandbox[vname] = lib;
|
sandbox[vname] = lib;
|
||||||
}
|
}).catch(err => {
|
||||||
} catch (e) {
|
node.error(RED._("function.error.moduleLoadError",{module:module.spec, error:err.toString()}))
|
||||||
//TODO: NLS error message
|
throw err;
|
||||||
node.error(RED._("function.error.moduleLoadError",{module:module.spec, error:e.toString()}))
|
}));
|
||||||
moduleErrors = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (moduleErrors) {
|
if (moduleErrors) {
|
||||||
throw new Error(RED._("function.error.externalModuleLoadError"));
|
throw new Error(RED._("function.error.externalModuleLoadError"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const RESOLVING = 0;
|
const RESOLVING = 0;
|
||||||
const RESOLVED = 1;
|
const RESOLVED = 1;
|
||||||
const ERROR = 2;
|
const ERROR = 2;
|
||||||
@ -337,165 +334,168 @@ module.exports = function(RED) {
|
|||||||
processMessage(msg, send, done);
|
processMessage(msg, send, done);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
Promise.all(moduleLoadPromises).then(() => {
|
||||||
var context = vm.createContext(sandbox);
|
var context = vm.createContext(sandbox);
|
||||||
try {
|
try {
|
||||||
var iniScript = null;
|
var iniScript = null;
|
||||||
var iniOpt = null;
|
var iniOpt = null;
|
||||||
if (node.ini && (node.ini !== "")) {
|
if (node.ini && (node.ini !== "")) {
|
||||||
var iniText = `
|
var iniText = `
|
||||||
(async function(__send__) {
|
(async function(__send__) {
|
||||||
var node = {
|
var node = {
|
||||||
id:__node__.id,
|
id:__node__.id,
|
||||||
name:__node__.name,
|
name:__node__.name,
|
||||||
outputCount:__node__.outputCount,
|
outputCount:__node__.outputCount,
|
||||||
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,
|
||||||
status:__node__.status,
|
status:__node__.status,
|
||||||
send: function(msgs, cloneMsg) {
|
send: function(msgs, cloneMsg) {
|
||||||
__node__.send(__send__, RED.util.generateId(), msgs, cloneMsg);
|
__node__.send(__send__, RED.util.generateId(), msgs, cloneMsg);
|
||||||
}
|
|
||||||
};
|
|
||||||
`+ node.ini +`
|
|
||||||
})(__initSend__);`;
|
|
||||||
iniOpt = createVMOpt(node, " setup");
|
|
||||||
iniScript = new vm.Script(iniText, iniOpt);
|
|
||||||
}
|
|
||||||
node.script = vm.createScript(functionText, createVMOpt(node, ""));
|
|
||||||
if (node.fin && (node.fin !== "")) {
|
|
||||||
var finText = `(function () {
|
|
||||||
var node = {
|
|
||||||
id:__node__.id,
|
|
||||||
name:__node__.name,
|
|
||||||
outputCount:__node__.outputCount,
|
|
||||||
log:__node__.log,
|
|
||||||
error:__node__.error,
|
|
||||||
warn:__node__.warn,
|
|
||||||
debug:__node__.debug,
|
|
||||||
trace:__node__.trace,
|
|
||||||
status:__node__.status,
|
|
||||||
send: function(msgs, cloneMsg) {
|
|
||||||
__node__.error("Cannot send from close function");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
`+node.fin +`
|
|
||||||
})();`;
|
|
||||||
finOpt = createVMOpt(node, " cleanup");
|
|
||||||
finScript = new vm.Script(finText, finOpt);
|
|
||||||
}
|
|
||||||
var promise = Promise.resolve();
|
|
||||||
if (iniScript) {
|
|
||||||
context.__initSend__ = function(msgs) { node.send(msgs); };
|
|
||||||
promise = iniScript.runInContext(context, iniOpt);
|
|
||||||
}
|
|
||||||
|
|
||||||
processMessage = function (msg, send, done) {
|
|
||||||
var start = process.hrtime();
|
|
||||||
context.msg = msg;
|
|
||||||
context.__send__ = send;
|
|
||||||
context.__done__ = done;
|
|
||||||
|
|
||||||
node.script.runInContext(context);
|
|
||||||
context.results.then(function(results) {
|
|
||||||
sendResults(node,send,msg._msgid,results,false);
|
|
||||||
if (handleNodeDoneCall) {
|
|
||||||
done();
|
|
||||||
}
|
|
||||||
|
|
||||||
var duration = process.hrtime(start);
|
|
||||||
var converted = Math.floor((duration[0] * 1e9 + duration[1])/10000)/100;
|
|
||||||
node.metric("duration", msg, converted);
|
|
||||||
if (process.env.NODE_RED_FUNCTION_TIME) {
|
|
||||||
node.status({fill:"yellow",shape:"dot",text:""+converted});
|
|
||||||
}
|
|
||||||
}).catch(err => {
|
|
||||||
if ((typeof err === "object") && err.hasOwnProperty("stack")) {
|
|
||||||
//remove unwanted part
|
|
||||||
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');
|
|
||||||
var stack = err.stack.split(/\r?\n/);
|
|
||||||
|
|
||||||
//store the error in msg to be used in flows
|
|
||||||
msg.error = err;
|
|
||||||
|
|
||||||
var line = 0;
|
|
||||||
var errorMessage;
|
|
||||||
if (stack.length > 0) {
|
|
||||||
while (line < stack.length && stack[line].indexOf("ReferenceError") !== 0) {
|
|
||||||
line++;
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
`+ node.ini +`
|
||||||
|
})(__initSend__);`;
|
||||||
|
iniOpt = createVMOpt(node, " setup");
|
||||||
|
iniScript = new vm.Script(iniText, iniOpt);
|
||||||
|
}
|
||||||
|
node.script = vm.createScript(functionText, createVMOpt(node, ""));
|
||||||
|
if (node.fin && (node.fin !== "")) {
|
||||||
|
var finText = `(function () {
|
||||||
|
var node = {
|
||||||
|
id:__node__.id,
|
||||||
|
name:__node__.name,
|
||||||
|
outputCount:__node__.outputCount,
|
||||||
|
log:__node__.log,
|
||||||
|
error:__node__.error,
|
||||||
|
warn:__node__.warn,
|
||||||
|
debug:__node__.debug,
|
||||||
|
trace:__node__.trace,
|
||||||
|
status:__node__.status,
|
||||||
|
send: function(msgs, cloneMsg) {
|
||||||
|
__node__.error("Cannot send from close function");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
`+node.fin +`
|
||||||
|
})();`;
|
||||||
|
finOpt = createVMOpt(node, " cleanup");
|
||||||
|
finScript = new vm.Script(finText, finOpt);
|
||||||
|
}
|
||||||
|
var promise = Promise.resolve();
|
||||||
|
if (iniScript) {
|
||||||
|
context.__initSend__ = function(msgs) { node.send(msgs); };
|
||||||
|
promise = iniScript.runInContext(context, iniOpt);
|
||||||
|
}
|
||||||
|
|
||||||
if (line < stack.length) {
|
processMessage = function (msg, send, done) {
|
||||||
errorMessage = stack[line];
|
var start = process.hrtime();
|
||||||
var m = /:(\d+):(\d+)$/.exec(stack[line+1]);
|
context.msg = msg;
|
||||||
if (m) {
|
context.__send__ = send;
|
||||||
var lineno = Number(m[1])-1;
|
context.__done__ = done;
|
||||||
var cha = m[2];
|
|
||||||
errorMessage += " (line "+lineno+", col "+cha+")";
|
node.script.runInContext(context);
|
||||||
|
context.results.then(function(results) {
|
||||||
|
sendResults(node,send,msg._msgid,results,false);
|
||||||
|
if (handleNodeDoneCall) {
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
|
||||||
|
var duration = process.hrtime(start);
|
||||||
|
var converted = Math.floor((duration[0] * 1e9 + duration[1])/10000)/100;
|
||||||
|
node.metric("duration", msg, converted);
|
||||||
|
if (process.env.NODE_RED_FUNCTION_TIME) {
|
||||||
|
node.status({fill:"yellow",shape:"dot",text:""+converted});
|
||||||
|
}
|
||||||
|
}).catch(err => {
|
||||||
|
if ((typeof err === "object") && err.hasOwnProperty("stack")) {
|
||||||
|
//remove unwanted part
|
||||||
|
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');
|
||||||
|
var stack = err.stack.split(/\r?\n/);
|
||||||
|
|
||||||
|
//store the error in msg to be used in flows
|
||||||
|
msg.error = err;
|
||||||
|
|
||||||
|
var line = 0;
|
||||||
|
var errorMessage;
|
||||||
|
if (stack.length > 0) {
|
||||||
|
while (line < stack.length && stack[line].indexOf("ReferenceError") !== 0) {
|
||||||
|
line++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (line < stack.length) {
|
||||||
|
errorMessage = stack[line];
|
||||||
|
var m = /:(\d+):(\d+)$/.exec(stack[line+1]);
|
||||||
|
if (m) {
|
||||||
|
var lineno = Number(m[1])-1;
|
||||||
|
var cha = m[2];
|
||||||
|
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({});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
node.on("close", function() {
|
promise.then(function (v) {
|
||||||
if (finScript) {
|
var msgs = messages;
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
}).catch(err => {
|
||||||
|
throw new Error(RED._("function.error.externalModuleLoadError"));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
RED.nodes.registerType("function",FunctionNode, {
|
RED.nodes.registerType("function",FunctionNode, {
|
||||||
dynamicModuleList: "libs",
|
dynamicModuleList: "libs",
|
||||||
|
@ -226,7 +226,9 @@
|
|||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
"var": "variable",
|
"var": "variable",
|
||||||
"module": "module"
|
"module": "module",
|
||||||
|
"moduleName": "Module name",
|
||||||
|
"importAs": "Import as"
|
||||||
},
|
},
|
||||||
"error": {
|
"error": {
|
||||||
"externalModuleNotAllowed": "Function node not allowed to load external modules",
|
"externalModuleNotAllowed": "Function node not allowed to load external modules",
|
||||||
|
@ -12,7 +12,6 @@ const log = require("@node-red/util").log;
|
|||||||
const hooks = require("@node-red/util").hooks;
|
const hooks = require("@node-red/util").hooks;
|
||||||
|
|
||||||
const BUILTIN_MODULES = require('module').builtinModules;
|
const BUILTIN_MODULES = require('module').builtinModules;
|
||||||
const EXTERNAL_MODULES_DIR = "externalModules";
|
|
||||||
|
|
||||||
// TODO: outsource running npm to a plugin
|
// TODO: outsource running npm to a plugin
|
||||||
const NPM_COMMAND = (process.platform === "win32") ? "npm.cmd" : "npm";
|
const NPM_COMMAND = (process.platform === "win32") ? "npm.cmd" : "npm";
|
||||||
@ -28,15 +27,30 @@ let installAllowList = ['*'];
|
|||||||
let installDenyList = [];
|
let installDenyList = [];
|
||||||
|
|
||||||
function getInstallDir() {
|
function getInstallDir() {
|
||||||
return path.resolve(path.join(settings.userDir || process.env.NODE_RED_HOME || ".", "externalModules"));
|
return path.resolve(settings.userDir || process.env.NODE_RED_HOME || ".");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let loggedLegacyWarning = false;
|
||||||
async function refreshExternalModules() {
|
async function refreshExternalModules() {
|
||||||
const externalModuleDir = path.resolve(path.join(settings.userDir || process.env.NODE_RED_HOME || ".", EXTERNAL_MODULES_DIR));
|
|
||||||
|
if (!loggedLegacyWarning) {
|
||||||
|
loggedLegacyWarning = true;
|
||||||
|
const oldExternalModulesDir = path.join(path.resolve(settings.userDir || process.env.NODE_RED_HOME || "."),"externalModules");
|
||||||
|
if (fs.existsSync(oldExternalModulesDir)) {
|
||||||
|
try {
|
||||||
|
log.warn(log._("server.install.old-ext-mod-dir-warning",{oldDir:oldExternalModulesDir, newDir:getInstallDir()}))
|
||||||
|
} catch(err) {console.log(err)}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const externalModuleDir = getInstallDir();
|
||||||
try {
|
try {
|
||||||
const pkgFile = JSON.parse(await fs.readFile(path.join(externalModuleDir,"package.json"),"utf-8"));
|
const pkgFile = JSON.parse(await fs.readFile(path.join(externalModuleDir,"package.json"),"utf-8"));
|
||||||
knownExternalModules = pkgFile.dependencies;
|
knownExternalModules = pkgFile.dependencies || {};
|
||||||
} catch(err) {
|
} catch(err) {
|
||||||
|
knownExternalModules = {};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -44,6 +58,7 @@ function init(_settings) {
|
|||||||
settings = _settings;
|
settings = _settings;
|
||||||
knownExternalModules = {};
|
knownExternalModules = {};
|
||||||
installEnabled = true;
|
installEnabled = true;
|
||||||
|
|
||||||
if (settings.externalModules && settings.externalModules.modules) {
|
if (settings.externalModules && settings.externalModules.modules) {
|
||||||
if (settings.externalModules.modules.allowList || settings.externalModules.modules.denyList) {
|
if (settings.externalModules.modules.allowList || settings.externalModules.modules.denyList) {
|
||||||
installAllowList = settings.externalModules.modules.allowList;
|
installAllowList = settings.externalModules.modules.allowList;
|
||||||
@ -82,10 +97,33 @@ function requireModule(module) {
|
|||||||
e.code = "module_not_allowed";
|
e.code = "module_not_allowed";
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
const externalModuleDir = path.resolve(path.join(settings.userDir || process.env.NODE_RED_HOME || ".", EXTERNAL_MODULES_DIR));
|
const externalModuleDir = getInstallDir();
|
||||||
const moduleDir = path.join(externalModuleDir,"node_modules",module);
|
const moduleDir = path.join(externalModuleDir,"node_modules",module);
|
||||||
return require(moduleDir);
|
return require(moduleDir);
|
||||||
}
|
}
|
||||||
|
function importModule(module) {
|
||||||
|
if (!registryUtil.checkModuleAllowed( module, null,installAllowList,installDenyList)) {
|
||||||
|
const e = new Error("Module not allowed");
|
||||||
|
e.code = "module_not_allowed";
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
|
||||||
|
const parsedModule = parseModuleName(module);
|
||||||
|
|
||||||
|
if (BUILTIN_MODULES.indexOf(parsedModule.module) !== -1) {
|
||||||
|
return import(parsedModule.module);
|
||||||
|
}
|
||||||
|
if (!knownExternalModules[parsedModule.module]) {
|
||||||
|
const e = new Error("Module not allowed");
|
||||||
|
e.code = "module_not_allowed";
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
const externalModuleDir = getInstallDir();
|
||||||
|
const moduleDir = path.join(externalModuleDir,"node_modules",module);
|
||||||
|
// Import needs the full path to the module's main .js file
|
||||||
|
const moduleFile = require.resolve(moduleDir);
|
||||||
|
return import(moduleFile);
|
||||||
|
}
|
||||||
|
|
||||||
function parseModuleName(module) {
|
function parseModuleName(module) {
|
||||||
var match = /((?:@[^/]+\/)?[^/@]+)(?:@([\s\S]+))?/.exec(module);
|
var match = /((?:@[^/]+\/)?[^/@]+)(?:@([\s\S]+))?/.exec(module);
|
||||||
@ -214,6 +252,9 @@ async function installModule(moduleDetails) {
|
|||||||
return hooks.trigger("postInstall", triggerPayload)
|
return hooks.trigger("postInstall", triggerPayload)
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
log.info(log._("server.install.installed", { name: installSpec }));
|
log.info(log._("server.install.installed", { name: installSpec }));
|
||||||
|
const runtimeInstalledModules = settings.get("modules") || {};
|
||||||
|
runtimeInstalledModules[moduleDetails.module] = moduleDetails;
|
||||||
|
settings.set("modules",runtimeInstalledModules)
|
||||||
}).catch(result => {
|
}).catch(result => {
|
||||||
var output = result.stderr || result.toString();
|
var output = result.stderr || result.toString();
|
||||||
var e;
|
var e;
|
||||||
@ -235,9 +276,10 @@ async function installModule(moduleDetails) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
init: init,
|
init,
|
||||||
register: register,
|
register,
|
||||||
registerSubflow: registerSubflow,
|
registerSubflow,
|
||||||
checkFlowDependencies: checkFlowDependencies,
|
checkFlowDependencies,
|
||||||
require: requireModule
|
require: requireModule,
|
||||||
|
import: importModule
|
||||||
}
|
}
|
||||||
|
@ -50,6 +50,16 @@ function requireModule(name) {
|
|||||||
return require("./externalModules").require(name);
|
return require("./externalModules").require(name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
function importModule(name) {
|
||||||
|
var moduleInfo = require("./index").getModuleInfo(name);
|
||||||
|
if (moduleInfo && moduleInfo.path) {
|
||||||
|
var relPath = path.relative(__dirname, moduleInfo.path);
|
||||||
|
return import(relPath);
|
||||||
|
} else {
|
||||||
|
// Require it here to avoid the circular dependency
|
||||||
|
return require("./externalModules").import(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function createNodeApi(node) {
|
function createNodeApi(node) {
|
||||||
var red = {
|
var red = {
|
||||||
@ -61,6 +71,7 @@ function createNodeApi(node) {
|
|||||||
util: runtime.util,
|
util: runtime.util,
|
||||||
version: runtime.version,
|
version: runtime.version,
|
||||||
require: requireModule,
|
require: requireModule,
|
||||||
|
import: importModule,
|
||||||
comms: {
|
comms: {
|
||||||
publish: function(topic,data,retain) {
|
publish: function(topic,data,retain) {
|
||||||
events.emit("comms",{
|
events.emit("comms",{
|
||||||
|
@ -20,7 +20,7 @@ const fspath = require("path");
|
|||||||
const log = require("@node-red/util").log;
|
const log = require("@node-red/util").log;
|
||||||
const util = require("./util");
|
const util = require("./util");
|
||||||
|
|
||||||
const configSections = ['nodes','users','projects'];
|
const configSections = ['nodes','users','projects','modules'];
|
||||||
|
|
||||||
const settingsCache = {};
|
const settingsCache = {};
|
||||||
|
|
||||||
@ -59,6 +59,7 @@ async function migrateToMultipleConfigFiles() {
|
|||||||
* - .config.nodes.json - the node registry
|
* - .config.nodes.json - the node registry
|
||||||
* - .config.users.json - user specific settings (eg editor settings)
|
* - .config.users.json - user specific settings (eg editor settings)
|
||||||
* - .config.projects.json - project settings, including the active project
|
* - .config.projects.json - project settings, including the active project
|
||||||
|
* - .config.modules.json - external modules installed by the runtime
|
||||||
* - .config.runtime.json - everything else - most notable _credentialSecret
|
* - .config.runtime.json - everything else - most notable _credentialSecret
|
||||||
*/
|
*/
|
||||||
function writeSettings(data) {
|
function writeSettings(data) {
|
||||||
|
@ -41,7 +41,8 @@
|
|||||||
"uninstalling": "Uninstalling module: __name__",
|
"uninstalling": "Uninstalling module: __name__",
|
||||||
"uninstall-failed": "Uninstall failed",
|
"uninstall-failed": "Uninstall failed",
|
||||||
"uninstall-failed-long": "Uninstall of module __name__ failed:",
|
"uninstall-failed-long": "Uninstall of module __name__ failed:",
|
||||||
"uninstalled": "Uninstalled module: __name__"
|
"uninstalled": "Uninstalled module: __name__",
|
||||||
|
"old-ext-mod-dir-warning": "\n\n---------------------------------------------------------------------\nNode-RED 1.3 external modules directory detected:\n __oldDir__\nThis directory is no longer used. External Modules will be\nreinstalled in your Node-RED user directory:\n __newDir__\nDelete the old externalModules directory to stop this message.\n---------------------------------------------------------------------\n"
|
||||||
},
|
},
|
||||||
"deprecatedOption": "Use of __old__ is DEPRECATED. Use __new__ instead",
|
"deprecatedOption": "Use of __old__ is DEPRECATED. Use __new__ instead",
|
||||||
"unable-to-listen": "Unable to listen on __listenpath__",
|
"unable-to-listen": "Unable to listen on __listenpath__",
|
||||||
|
@ -26,8 +26,7 @@ async function createUserDir() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function setupExternalModulesPackage(dependencies) {
|
async function setupExternalModulesPackage(dependencies) {
|
||||||
await fs.ensureDir(path.join(homeDir,"externalModules"))
|
await fs.writeFile(path.join(homeDir,"package.json"),`{
|
||||||
await fs.writeFile(path.join(homeDir,"externalModules","package.json"),`{
|
|
||||||
"name": "Node-RED-External-Modules",
|
"name": "Node-RED-External-Modules",
|
||||||
"description": "These modules are automatically installed by Node-RED to use in Function nodes.",
|
"description": "These modules are automatically installed by Node-RED to use in Function nodes.",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
@ -68,7 +67,7 @@ describe("externalModules api", function() {
|
|||||||
exec.run.restore();
|
exec.run.restore();
|
||||||
})
|
})
|
||||||
it("does nothing when no types are registered",async function() {
|
it("does nothing when no types are registered",async function() {
|
||||||
externalModules.init({userDir: homeDir});
|
externalModules.init({userDir: homeDir, get:()=>{}, set:()=>{}});
|
||||||
await externalModules.checkFlowDependencies([
|
await externalModules.checkFlowDependencies([
|
||||||
{type: "function", libs:[{module: "foo"}]}
|
{type: "function", libs:[{module: "foo"}]}
|
||||||
])
|
])
|
||||||
@ -76,7 +75,7 @@ describe("externalModules api", function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("skips install for modules already installed", async function() {
|
it("skips install for modules already installed", async function() {
|
||||||
externalModules.init({userDir: homeDir});
|
externalModules.init({userDir: homeDir, get:()=>{}, set:()=>{}});
|
||||||
externalModules.register("function", "libs");
|
externalModules.register("function", "libs");
|
||||||
await setupExternalModulesPackage({"foo": "1.2.3", "bar":"2.3.4"});
|
await setupExternalModulesPackage({"foo": "1.2.3", "bar":"2.3.4"});
|
||||||
await externalModules.checkFlowDependencies([
|
await externalModules.checkFlowDependencies([
|
||||||
@ -86,7 +85,7 @@ describe("externalModules api", function() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it("skips install for built-in modules", async function() {
|
it("skips install for built-in modules", async function() {
|
||||||
externalModules.init({userDir: homeDir});
|
externalModules.init({userDir: homeDir, get:()=>{}, set:()=>{}});
|
||||||
externalModules.register("function", "libs");
|
externalModules.register("function", "libs");
|
||||||
await externalModules.checkFlowDependencies([
|
await externalModules.checkFlowDependencies([
|
||||||
{type: "function", libs:[{module: "fs"}]}
|
{type: "function", libs:[{module: "fs"}]}
|
||||||
@ -95,19 +94,17 @@ describe("externalModules api", function() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it("installs missing modules", async function() {
|
it("installs missing modules", async function() {
|
||||||
externalModules.init({userDir: homeDir});
|
externalModules.init({userDir: homeDir, get:()=>{}, set:()=>{}});
|
||||||
externalModules.register("function", "libs");
|
externalModules.register("function", "libs");
|
||||||
fs.existsSync(path.join(homeDir,"externalModules")).should.be.false();
|
|
||||||
await externalModules.checkFlowDependencies([
|
await externalModules.checkFlowDependencies([
|
||||||
{type: "function", libs:[{module: "foo"}]}
|
{type: "function", libs:[{module: "foo"}]}
|
||||||
])
|
])
|
||||||
exec.run.called.should.be.true();
|
exec.run.called.should.be.true();
|
||||||
fs.existsSync(path.join(homeDir,"externalModules")).should.be.true();
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
it("calls pre/postInstall hooks", async function() {
|
it("calls pre/postInstall hooks", async function() {
|
||||||
externalModules.init({userDir: homeDir});
|
externalModules.init({userDir: homeDir, get:()=>{}, set:()=>{}});
|
||||||
externalModules.register("function", "libs");
|
externalModules.register("function", "libs");
|
||||||
let receivedPreEvent,receivedPostEvent;
|
let receivedPreEvent,receivedPostEvent;
|
||||||
hooks.add("preInstall", function(event) { event.args = ["a"]; receivedPreEvent = event; })
|
hooks.add("preInstall", function(event) { event.args = ["a"]; receivedPreEvent = event; })
|
||||||
@ -122,11 +119,10 @@ describe("externalModules api", function() {
|
|||||||
receivedPreEvent.should.have.property("version")
|
receivedPreEvent.should.have.property("version")
|
||||||
receivedPreEvent.should.have.property("dir")
|
receivedPreEvent.should.have.property("dir")
|
||||||
receivedPreEvent.should.eql(receivedPostEvent)
|
receivedPreEvent.should.eql(receivedPostEvent)
|
||||||
fs.existsSync(path.join(homeDir,"externalModules")).should.be.true();
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it("skips npm install if preInstall returns false", async function() {
|
it("skips npm install if preInstall returns false", async function() {
|
||||||
externalModules.init({userDir: homeDir});
|
externalModules.init({userDir: homeDir, get:()=>{}, set:()=>{}});
|
||||||
externalModules.register("function", "libs");
|
externalModules.register("function", "libs");
|
||||||
let receivedPreEvent,receivedPostEvent;
|
let receivedPreEvent,receivedPostEvent;
|
||||||
hooks.add("preInstall", function(event) { receivedPreEvent = event; return false })
|
hooks.add("preInstall", function(event) { receivedPreEvent = event; return false })
|
||||||
@ -140,12 +136,11 @@ describe("externalModules api", function() {
|
|||||||
receivedPreEvent.should.have.property("version")
|
receivedPreEvent.should.have.property("version")
|
||||||
receivedPreEvent.should.have.property("dir")
|
receivedPreEvent.should.have.property("dir")
|
||||||
receivedPreEvent.should.eql(receivedPostEvent)
|
receivedPreEvent.should.eql(receivedPostEvent)
|
||||||
fs.existsSync(path.join(homeDir,"externalModules")).should.be.true();
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
it("installs missing modules from inside subflow module", async function() {
|
it("installs missing modules from inside subflow module", async function() {
|
||||||
externalModules.init({userDir: homeDir});
|
externalModules.init({userDir: homeDir, get:()=>{}, set:()=>{}});
|
||||||
externalModules.register("function", "libs");
|
externalModules.register("function", "libs");
|
||||||
externalModules.registerSubflow("sf", {"flow":[{type: "function", libs:[{module: "foo"}]}]});
|
externalModules.registerSubflow("sf", {"flow":[{type: "function", libs:[{module: "foo"}]}]});
|
||||||
await externalModules.checkFlowDependencies([
|
await externalModules.checkFlowDependencies([
|
||||||
@ -155,7 +150,7 @@ describe("externalModules api", function() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it("reports install fail - 404", async function() {
|
it("reports install fail - 404", async function() {
|
||||||
externalModules.init({userDir: homeDir});
|
externalModules.init({userDir: homeDir, get:()=>{}, set:()=>{}});
|
||||||
externalModules.register("function", "libs");
|
externalModules.register("function", "libs");
|
||||||
try {
|
try {
|
||||||
await externalModules.checkFlowDependencies([
|
await externalModules.checkFlowDependencies([
|
||||||
@ -174,7 +169,7 @@ describe("externalModules api", function() {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
it("reports install fail - target", async function() {
|
it("reports install fail - target", async function() {
|
||||||
externalModules.init({userDir: homeDir});
|
externalModules.init({userDir: homeDir, get:()=>{}, set:()=>{}});
|
||||||
externalModules.register("function", "libs");
|
externalModules.register("function", "libs");
|
||||||
try {
|
try {
|
||||||
await externalModules.checkFlowDependencies([
|
await externalModules.checkFlowDependencies([
|
||||||
@ -193,7 +188,7 @@ describe("externalModules api", function() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it("reports install fail - unexpected", async function() {
|
it("reports install fail - unexpected", async function() {
|
||||||
externalModules.init({userDir: homeDir});
|
externalModules.init({userDir: homeDir, get:()=>{}, set:()=>{}});
|
||||||
externalModules.register("function", "libs");
|
externalModules.register("function", "libs");
|
||||||
try {
|
try {
|
||||||
await externalModules.checkFlowDependencies([
|
await externalModules.checkFlowDependencies([
|
||||||
@ -211,7 +206,7 @@ describe("externalModules api", function() {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
it("reports install fail - multiple", async function() {
|
it("reports install fail - multiple", async function() {
|
||||||
externalModules.init({userDir: homeDir});
|
externalModules.init({userDir: homeDir, get:()=>{}, set:()=>{}});
|
||||||
externalModules.register("function", "libs");
|
externalModules.register("function", "libs");
|
||||||
try {
|
try {
|
||||||
await externalModules.checkFlowDependencies([
|
await externalModules.checkFlowDependencies([
|
||||||
@ -238,7 +233,7 @@ describe("externalModules api", function() {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
it("reports install fail - install disabled", async function() {
|
it("reports install fail - install disabled", async function() {
|
||||||
externalModules.init({userDir: homeDir, externalModules: {
|
externalModules.init({userDir: homeDir, get:()=>{}, set:()=>{}, externalModules: {
|
||||||
modules: {
|
modules: {
|
||||||
allowInstall: false
|
allowInstall: false
|
||||||
}
|
}
|
||||||
@ -262,7 +257,7 @@ describe("externalModules api", function() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it("reports install fail - module disallowed", async function() {
|
it("reports install fail - module disallowed", async function() {
|
||||||
externalModules.init({userDir: homeDir, externalModules: {
|
externalModules.init({userDir: homeDir, get:()=>{}, set:()=>{}, externalModules: {
|
||||||
modules: {
|
modules: {
|
||||||
denyList: ['foo']
|
denyList: ['foo']
|
||||||
}
|
}
|
||||||
@ -287,7 +282,7 @@ describe("externalModules api", function() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it("reports install fail - built-in module disallowed", async function() {
|
it("reports install fail - built-in module disallowed", async function() {
|
||||||
externalModules.init({userDir: homeDir, externalModules: {
|
externalModules.init({userDir: homeDir, get:()=>{}, set:()=>{}, externalModules: {
|
||||||
modules: {
|
modules: {
|
||||||
denyList: ['fs']
|
denyList: ['fs']
|
||||||
}
|
}
|
||||||
@ -313,12 +308,12 @@ describe("externalModules api", function() {
|
|||||||
})
|
})
|
||||||
describe("require", async function() {
|
describe("require", async function() {
|
||||||
it("requires built-in modules", async function() {
|
it("requires built-in modules", async function() {
|
||||||
externalModules.init({userDir: homeDir});
|
externalModules.init({userDir: homeDir, get:()=>{}, set:()=>{}});
|
||||||
const result = externalModules.require("fs")
|
const result = externalModules.require("fs")
|
||||||
result.should.eql(require("fs"));
|
result.should.eql(require("fs"));
|
||||||
})
|
})
|
||||||
it("rejects unknown modules", async function() {
|
it("rejects unknown modules", async function() {
|
||||||
externalModules.init({userDir: homeDir});
|
externalModules.init({userDir: homeDir, get:()=>{}, set:()=>{}});
|
||||||
try {
|
try {
|
||||||
externalModules.require("foo")
|
externalModules.require("foo")
|
||||||
throw new Error("require did not reject after fail")
|
throw new Error("require did not reject after fail")
|
||||||
@ -328,7 +323,7 @@ describe("externalModules api", function() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it("rejects disallowed modules", async function() {
|
it("rejects disallowed modules", async function() {
|
||||||
externalModules.init({userDir: homeDir, externalModules: {
|
externalModules.init({userDir: homeDir, get:()=>{}, set:()=>{}, externalModules: {
|
||||||
modules: {
|
modules: {
|
||||||
denyList: ['fs']
|
denyList: ['fs']
|
||||||
}
|
}
|
||||||
@ -341,4 +336,36 @@ describe("externalModules api", function() {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
describe("import", async function() {
|
||||||
|
it("import built-in modules", async function() {
|
||||||
|
externalModules.init({userDir: homeDir, get:()=>{}, set:()=>{}});
|
||||||
|
const result = await externalModules.import("fs")
|
||||||
|
// `result` won't have the `should` property
|
||||||
|
should.exist(result);
|
||||||
|
should.exist(result.existsSync);
|
||||||
|
})
|
||||||
|
it("rejects unknown modules", async function() {
|
||||||
|
externalModules.init({userDir: homeDir, get:()=>{}, set:()=>{}});
|
||||||
|
try {
|
||||||
|
await externalModules.import("foo")
|
||||||
|
throw new Error("import did not reject after fail")
|
||||||
|
} catch(err) {
|
||||||
|
err.should.have.property("code","module_not_allowed");
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
it("rejects disallowed modules", async function() {
|
||||||
|
externalModules.init({userDir: homeDir, get:()=>{}, set:()=>{}, externalModules: {
|
||||||
|
modules: {
|
||||||
|
denyList: ['fs']
|
||||||
|
}
|
||||||
|
}});
|
||||||
|
try {
|
||||||
|
await externalModules.import("fs")
|
||||||
|
throw new Error("import did not reject after fail")
|
||||||
|
} catch(err) {
|
||||||
|
err.should.have.property("code","module_not_allowed");
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user