UNPKG

do-red

Version:

A do-node and corresponding return-node for creating loops and task-lists.

143 lines (135 loc) 5.41 kB
const doHelper = require('./utils/doHelper') module.exports = function (RED) { function DoNode (config) { RED.nodes.createNode(this, config) const node = this node.tasks = config.tasks // Add event-handling const event = 'do:' + node.id const handler = function (msg) { msg._event = node.event node.receive(msg) } RED.events.on(event, handler) // Clean up event handler on close node.on('close', function () { RED.events.removeListener(event, handler) }) node.on('input', function (msg, send, done) { // Fallback for older versions of Node-RED send = send || function () { node.send.apply(node, arguments) } // Fallback older do node if (typeof config.doneOutput === 'undefined') config.doneOutput = true if (typeof config.mode === 'undefined') config.mode = 'msg' const handleDo = (hasTasksOrMoreValues) => { if (hasTasksOrMoreValues) { let outputMsgIndex = node.tasks.indexOf(_do.stack[_do.stack.length - 1]) if (config.mode === 'each') { // consider possible firstValueOutput if (config.firstValueOutput) { outputMsg.push(undefined) outputMsgIndex++ } if (_do.isFirstMsg && config.firstValueOutput) { return send(msg) // go to first output } else if (_do.isLastMsg && config.lastValueOutput) { outputMsg.push(msg) // separate output after all tasks (+ possible first output) return send(outputMsg) } } outputMsg[outputMsgIndex] = msg return send(outputMsg) } else { return doHelper.handleDone(RED, msg, config, send, done) } } // no-op if (node.tasks.length === 0) { send(msg) done() return } // first time do node or nested do node if (!msg._do || msg._do.returnTo !== node.id) { const nestedDo = msg._do msg._do = { returnTo: node.id, stack: [...node.tasks].reverse() } if (nestedDo) msg._do._do = nestedDo } const outputMsg = new Array(node.tasks.length) let _do = msg._do try { if (config.mode === 'msg') { handleDo(_do.stack.length > 0) done() } else if (config.mode === 'each') { // first each or first nested each if (typeof _do.index === 'undefined') { _do = doHelper.initEachDo(RED, msg, node, config, _do) } _do.collectionType = doHelper.checkCollectionType(config, _do.collection) let hasMoreValues switch (_do.collectionType) { case 'Array': if (_do.yieldType !== 'value' && _do.yieldType !== 'indexValue') { throw new Error(`Collection is an ${msg._do.collectionType} but should return ${config.yield}!`) } // We will only handle the collection length which exists when the each node started first time! if (typeof _do.collectionLength === 'undefined') { _do.collectionLength = [..._do.collection].length } if (_do.stack.length === 0 || (_do.index === -1 && !_do.valueDeleted)) { msg = doHelper.initNextValue(msg, config, node.tasks) } if (_do.collectionLength === _do.index + 1) { _do.isLastMsg = true } hasMoreValues = (_do.collectionLength >= _do.index + 1) break case 'Set': if (_do.yieldType !== 'value' && _do.yieldType !== 'entries') { throw new Error(`Collection is an ${msg._do.collectionType} but should return ${config.yield}!`) } // init on first run if (!_do.setIterator) { _do.setIterator = _do.collection.values() _do.collectionLength = _do.collection.size } if (_do.collectionLength === _do.index + 1) { _do.isLastMsg = true } if (_do.stack.length === 0 || _do.index === -1) { msg = doHelper.initNextValue(msg, config, node.tasks, RED) } // if (_do.yieldType === 'entries') msg.payload = [msg.payload, msg.payload] hasMoreValues = (_do.collectionLength >= _do.index + 1) break case 'Object': case 'Map': if (!_do.keys) { if (_do.collectionType === 'Object') _do.keys = Object.keys(_do.collection) if (_do.collectionType === 'Map') _do.keys = [..._do.collection.keys()] } if (_do.stack.length === 0 || _do.index === -1) { msg = doHelper.initNextValue(msg, config, node.tasks) } if (_do.index + 1 === _do.keys.length) { _do.isLastMsg = true } // only keys from first run will be iterated hasMoreValues = (_do.keys.length && _do.keys.length !== _do.index) break default: throw new Error('No valid collection type') } handleDo(hasMoreValues) } } catch (e) { done(e) } }) } RED.nodes.registerType('do', DoNode) }