mirror of
https://github.com/node-red/node-red.git
synced 2025-03-01 10:36:34 +00:00
Merge pull request #2936 from node-red/npm-install-hooks
Add pre/postInstall hooks to npm install handling
This commit is contained in:
10
packages/node_modules/@node-red/util/index.js
vendored
10
packages/node_modules/@node-red/util/index.js
vendored
@@ -19,6 +19,7 @@ const i18n = require("./lib/i18n");
|
||||
const util = require("./lib/util");
|
||||
const events = require("./lib/events");
|
||||
const exec = require("./lib/exec");
|
||||
const hooks = require("./lib/hooks");
|
||||
|
||||
/**
|
||||
* This module provides common utilities for the Node-RED runtime and editor
|
||||
@@ -69,5 +70,12 @@ module.exports = {
|
||||
* @mixes @node-red/util_exec
|
||||
* @memberof @node-red/util
|
||||
*/
|
||||
exec: exec
|
||||
exec: exec,
|
||||
|
||||
/**
|
||||
* Runtime hooks
|
||||
* @mixes @node-red/util_hooks
|
||||
* @memberof @node-red/util
|
||||
*/
|
||||
hooks: hooks
|
||||
}
|
||||
|
243
packages/node_modules/@node-red/util/lib/hooks.js
vendored
Normal file
243
packages/node_modules/@node-red/util/lib/hooks.js
vendored
Normal file
@@ -0,0 +1,243 @@
|
||||
const Log = require("./log.js");
|
||||
|
||||
const VALID_HOOKS = [
|
||||
// Message Routing Path
|
||||
"onSend",
|
||||
"preRoute",
|
||||
"preDeliver",
|
||||
"postDeliver",
|
||||
"onReceive",
|
||||
"postReceive",
|
||||
"onComplete",
|
||||
// Module install hooks
|
||||
"preInstall",
|
||||
"postInstall",
|
||||
"preUninstall",
|
||||
"postUninstall"
|
||||
]
|
||||
|
||||
|
||||
// Flags for what hooks have handlers registered
|
||||
let states = { }
|
||||
|
||||
// Doubly-LinkedList of hooks by id.
|
||||
// - hooks[id] points to head of list
|
||||
// - each list item looks like:
|
||||
// {
|
||||
// cb: the callback function
|
||||
// location: filename/line of code that added the hook
|
||||
// previousHook: reference to previous hook in list
|
||||
// nextHook: reference to next hook in list
|
||||
// removed: a flag that is set if the item was removed
|
||||
// }
|
||||
let hooks = { }
|
||||
|
||||
// Hooks by label
|
||||
let labelledHooks = { }
|
||||
|
||||
/**
|
||||
* Runtime hooks engine
|
||||
*
|
||||
* The following hooks can be used:
|
||||
*
|
||||
* Message sending
|
||||
* - `onSend` - passed an array of `SendEvent` objects. The messages inside these objects are exactly what the node has passed to `node.send` - meaning there could be duplicate references to the same message object.
|
||||
* - `preRoute` - passed a `SendEvent`
|
||||
* - `preDeliver` - passed a `SendEvent`. The local router has identified the node it is going to send to. At this point, the message has been cloned if needed.
|
||||
* - `postDeliver` - passed a `SendEvent`. The message has been dispatched to be delivered asynchronously (unless the sync delivery flag is set, in which case it would be continue as synchronous delivery)
|
||||
* - `onReceive` - passed a `ReceiveEvent` when a node is about to receive a message
|
||||
* - `postReceive` - passed a `ReceiveEvent` when the message has been given to the node's `input` handler(s)
|
||||
* - `onComplete` - passed a `CompleteEvent` when the node has completed with a message or logged an error
|
||||
*
|
||||
* @mixin @node-red/util_hooks
|
||||
*/
|
||||
|
||||
/**
|
||||
* Register a handler to a named hook
|
||||
* @memberof @node-red/util_hooks
|
||||
* @param {String} hookId - the name of the hook to attach to
|
||||
* @param {Function} callback - the callback function for the hook
|
||||
*/
|
||||
function add(hookId, callback) {
|
||||
let [id, label] = hookId.split(".");
|
||||
if (VALID_HOOKS.indexOf(id) === -1) {
|
||||
throw new Error(`Invalid hook '${id}'`);
|
||||
}
|
||||
if (label && labelledHooks[label] && labelledHooks[label][id]) {
|
||||
throw new Error("Hook "+hookId+" already registered")
|
||||
}
|
||||
// Get location of calling code
|
||||
const stack = new Error().stack;
|
||||
const callModule = stack.split("\n")[2].split("(")[1].slice(0,-1);
|
||||
Log.debug(`Adding hook '${hookId}' from ${callModule}`);
|
||||
|
||||
const hookItem = {cb:callback, location: callModule, previousHook: null, nextHook: null }
|
||||
|
||||
let tailItem = hooks[id];
|
||||
if (tailItem === undefined) {
|
||||
hooks[id] = hookItem;
|
||||
} else {
|
||||
while(tailItem.nextHook !== null) {
|
||||
tailItem = tailItem.nextHook
|
||||
}
|
||||
tailItem.nextHook = hookItem;
|
||||
hookItem.previousHook = tailItem;
|
||||
}
|
||||
|
||||
if (label) {
|
||||
labelledHooks[label] = labelledHooks[label]||{};
|
||||
labelledHooks[label][id] = hookItem;
|
||||
}
|
||||
|
||||
// TODO: get rid of this;
|
||||
states[id] = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a handled from a named hook
|
||||
* @memberof @node-red/util_hooks
|
||||
* @param {String} hookId - the name of the hook event to remove - must be `name.label`
|
||||
*/
|
||||
function remove(hookId) {
|
||||
let [id,label] = hookId.split(".");
|
||||
if ( !label) {
|
||||
throw new Error("Cannot remove hook without label: ",hookId)
|
||||
}
|
||||
Log.debug(`Removing hook '${hookId}'`);
|
||||
if (labelledHooks[label]) {
|
||||
if (id === "*") {
|
||||
// Remove all hooks for this label
|
||||
let hookList = Object.keys(labelledHooks[label]);
|
||||
for (let i=0;i<hookList.length;i++) {
|
||||
removeHook(hookList[i],labelledHooks[label][hookList[i]])
|
||||
}
|
||||
delete labelledHooks[label];
|
||||
} else if (labelledHooks[label][id]) {
|
||||
removeHook(id,labelledHooks[label][id])
|
||||
delete labelledHooks[label][id];
|
||||
if (Object.keys(labelledHooks[label]).length === 0){
|
||||
delete labelledHooks[label];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function removeHook(id,hookItem) {
|
||||
let previousHook = hookItem.previousHook;
|
||||
let nextHook = hookItem.nextHook;
|
||||
|
||||
if (previousHook) {
|
||||
previousHook.nextHook = nextHook;
|
||||
} else {
|
||||
hooks[id] = nextHook;
|
||||
}
|
||||
if (nextHook) {
|
||||
nextHook.previousHook = previousHook;
|
||||
}
|
||||
hookItem.removed = true;
|
||||
if (!previousHook && !nextHook) {
|
||||
delete hooks[id];
|
||||
delete states[id];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function trigger(hookId, payload, done) {
|
||||
let hookItem = hooks[hookId];
|
||||
if (!hookItem) {
|
||||
if (done) {
|
||||
done();
|
||||
return;
|
||||
} else {
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
if (!done) {
|
||||
return new Promise((resolve,reject) => {
|
||||
invokeStack(hookItem,payload,function(err) {
|
||||
if (err !== undefined && err !== false) {
|
||||
if (!(err instanceof Error)) {
|
||||
err = new Error(err);
|
||||
}
|
||||
err.hook = hookId
|
||||
reject(err);
|
||||
} else {
|
||||
resolve(err);
|
||||
}
|
||||
})
|
||||
});
|
||||
} else {
|
||||
invokeStack(hookItem,payload,done)
|
||||
}
|
||||
}
|
||||
function invokeStack(hookItem,payload,done) {
|
||||
function callNextHook(err) {
|
||||
if (!hookItem || err) {
|
||||
done(err);
|
||||
return;
|
||||
}
|
||||
if (hookItem.removed) {
|
||||
hookItem = hookItem.nextHook;
|
||||
callNextHook();
|
||||
return;
|
||||
}
|
||||
const callback = hookItem.cb;
|
||||
if (callback.length === 1) {
|
||||
try {
|
||||
let result = callback(payload);
|
||||
if (result === false) {
|
||||
// Halting the flow
|
||||
done(false);
|
||||
return
|
||||
}
|
||||
if (result && typeof result.then === 'function') {
|
||||
result.then(handleResolve, callNextHook)
|
||||
return;
|
||||
}
|
||||
hookItem = hookItem.nextHook;
|
||||
callNextHook();
|
||||
} catch(err) {
|
||||
done(err);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
callback(payload,handleResolve)
|
||||
} catch(err) {
|
||||
done(err);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
function handleResolve(result) {
|
||||
if (result === undefined) {
|
||||
hookItem = hookItem.nextHook;
|
||||
callNextHook();
|
||||
} else {
|
||||
done(result);
|
||||
}
|
||||
}
|
||||
callNextHook();
|
||||
}
|
||||
|
||||
function clear() {
|
||||
hooks = {}
|
||||
labelledHooks = {}
|
||||
states = {}
|
||||
}
|
||||
|
||||
function has(hookId) {
|
||||
let [id, label] = hookId.split(".");
|
||||
if (label) {
|
||||
return !!(labelledHooks[label] && labelledHooks[label][id])
|
||||
}
|
||||
return !!states[id]
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
has,
|
||||
clear,
|
||||
add,
|
||||
remove,
|
||||
trigger
|
||||
}
|
Reference in New Issue
Block a user