From d2c9ccbfddb6c089db26c9566bb6e6ca540d066e Mon Sep 17 00:00:00 2001 From: Nick O'Leary Date: Sun, 14 Feb 2021 00:02:08 +0000 Subject: [PATCH] Detect externalModule dependencies inside subflow modules Not sure this is 100% the right approach. If a subflow module has a dependency it should be in the subflow's package.json and therefore installed next to the subflow module in ~/.node-red/node_modules. By treating it as a 'normal' external module, it will be dynamically installed in ~/.node-red/externalModules. That then exposes the module to the user who won't know why its there and may remove it. It would be better to allow nodes inside a subflow module to require from ~/.node-red/node_modules and not limit it to the externalModules dir. The hard part is knowing when to do that. --- .../@node-red/registry/lib/externalModules.js | 22 ++++++++++++++---- .../@node-red/registry/lib/registry.js | 3 +++ test/resources/subflow/package/package.json | 5 ++-- test/resources/subflow/package/subflow.json | 1 + .../subflow/test-subflow-mod-1.0.1.tgz | Bin 1682 -> 0 bytes .../subflow/test-subflow-mod-1.0.2.tgz | Bin 0 -> 1709 bytes 6 files changed, 24 insertions(+), 7 deletions(-) delete mode 100644 test/resources/subflow/test-subflow-mod-1.0.1.tgz create mode 100644 test/resources/subflow/test-subflow-mod-1.0.2.tgz diff --git a/packages/node_modules/@node-red/registry/lib/externalModules.js b/packages/node_modules/@node-red/registry/lib/externalModules.js index f794f1fc6..1e9ec4ebe 100644 --- a/packages/node_modules/@node-red/registry/lib/externalModules.js +++ b/packages/node_modules/@node-red/registry/lib/externalModules.js @@ -6,6 +6,7 @@ const fs = require("fs-extra"); const registryUtil = require("./util"); const path = require("path"); +const clone = require("clone"); const exec = require("@node-red/util").exec; const log = require("@node-red/util").log; @@ -16,6 +17,7 @@ const EXTERNAL_MODULES_DIR = "externalModules"; const NPM_COMMAND = (process.platform === "win32") ? "npm.cmd" : "npm"; let registeredTypes = {}; +let subflowTypes = {}; let settings; let knownExternalModules = {}; @@ -58,6 +60,10 @@ function register(type, dynamicModuleListProperty) { registeredTypes[type] = dynamicModuleListProperty; } +function registerSubflow(type, subflowConfig) { + subflowTypes[type] = subflowConfig; +} + function requireModule(module) { if (!registryUtil.checkModuleAllowed( module, null,installAllowList,installDenyList)) { const e = new Error("Module not allowed"); @@ -95,15 +101,21 @@ function isInstalled(moduleDetails) { return moduleDetails.builtin || moduleDetails.known; } + async function checkFlowDependencies(flowConfig) { + let nodes = clone(flowConfig); await refreshExternalModules(); const checkedModules = {}; const promises = []; const errors = []; - - flowConfig.forEach(n => { - if (registeredTypes[n.type]) { + const checkedSubflows = {}; + while (nodes.length > 0) { + let n = nodes.shift(); + if (subflowTypes[n.type] && !checkedSubflows[n.type]) { + checkedSubflows[n.type] = true; + nodes = nodes.concat(subflowTypes[n.type].flow) + } else if (registeredTypes[n.type]) { let nodeModules = n[registeredTypes[n.type]] || []; if (!Array.isArray(nodeModules)) { nodeModules = [nodeModules] @@ -135,8 +147,7 @@ async function checkFlowDependencies(flowConfig) { } }) } - }) - + } return Promise.all(promises).then(refreshExternalModules).then(() => { if (errors.length > 0) { throw errors; @@ -205,6 +216,7 @@ async function installModule(moduleDetails) { module.exports = { init: init, register: register, + registerSubflow: registerSubflow, checkFlowDependencies: checkFlowDependencies, require: requireModule } \ No newline at end of file diff --git a/packages/node_modules/@node-red/registry/lib/registry.js b/packages/node_modules/@node-red/registry/lib/registry.js index a4d504fac..82de4e442 100644 --- a/packages/node_modules/@node-red/registry/lib/registry.js +++ b/packages/node_modules/@node-red/registry/lib/registry.js @@ -456,6 +456,9 @@ function registerSubflow(nodeSet, subflow) { nodeSetInfo.config = result.config; } subflowModules[result.type] = result; + externalModules.registerSubflow(result.type,subflow); + + events.emit("type-registered",result.type); return result; } diff --git a/test/resources/subflow/package/package.json b/test/resources/subflow/package/package.json index 92ff33e2b..831428c6a 100644 --- a/test/resources/subflow/package/package.json +++ b/test/resources/subflow/package/package.json @@ -1,6 +1,6 @@ { "name": "test-subflow-mod", - "version": "1.0.1", + "version": "1.0.2", "description": "", "keywords": [], "license": "ISC", @@ -13,6 +13,7 @@ ] }, "dependencies": { - "node-red-node-random": "*" + "node-red-node-random": "*", + "cowsay2": "*" } } diff --git a/test/resources/subflow/package/subflow.json b/test/resources/subflow/package/subflow.json index e5c6b25ab..db43cdff4 100644 --- a/test/resources/subflow/package/subflow.json +++ b/test/resources/subflow/package/subflow.json @@ -189,6 +189,7 @@ "noerr": 0, "initialize": "", "finalize": "", + "libs": [ {"var":"cowsay2","module":"cowsay2"}], "x": 240, "y": 100, "wires": [ diff --git a/test/resources/subflow/test-subflow-mod-1.0.1.tgz b/test/resources/subflow/test-subflow-mod-1.0.1.tgz deleted file mode 100644 index daa53b77a4d73bc6ca062f72c7559485d30bc892..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1682 zcmV;D25tEtiwFP!000006YUynZ`(GopZzO1?L(5yk|kNT6BqrEwkg)3U4}TtP}D)e zP?V)bCN+|Z;}-t!BPGkRUQS{=>DrkvVCn93ygS~5uLz(7=QC;TW_Xm zb{z*)!Bw-{7I@a~bWO|Zc1=kqAM9RR{%>EDa*MfuOp3B8pHvsH`2S8?V@%yR#0I{J zC=(pK0A5Ux2q@VQ95B4U z=kdJ*NeJCHL5K%af)*$(g2LAhjK66>_CJ4i14iH&H?>>uqeMH9tug!7hWVegXdeIb z|6~NV-NzmA-%&ANi2sh$*~R};G!@^P3PP<9G=aI$=dq~E#Go}5_8K!T^MN9;4AZbu zs7wZk1(BFzQs5PyPAGG^gnYV4AYp(o;R*M*gI6h)$|O3&t^}s(We(#@fCRWKkK%Bf zCbT)(teG)zafFGB2?`QEpO*P#9qQ==BrY9i94;DjE=3MG(Mn%SX?{^GW{p|nTaV!; zEh^RjCysxsV>#vc?>g2l{-2_yUakcy7&QbvyK{(;;b0p%hguq3nwUm-Df}{UmNm|0 zl!8GX7Z8tXeeK*2IFP^b6r>Xjco|}lPC3Aw-H;5)Tj0|O1EI*dPbVBe4tyL&KV=LN z6nI40G!c`I$1>|Iy2^$7asrXq8;P`Rvzdn~HkMh0PUIA&m^-SNE0tIYZ%4}&f2E%L~#;P#+DtZ+90Lx!&=rkGEDRj10^R-W=Fs(XN2W8t9Ho+yX(}rwL~kve;j@6 zE$Fj?$CK;R-hw_Wc+4s>7bWOy?SQ2OPXggXIZ>a6l#CMi8WF5Lpih}3&OX+4a!F}e z+lydvNxDr^g|jMEARnWvOM0_@HvH@DyZ6U+ijsI-pAXH=w7jI_%avI$3JAe$ea+xx zw2oezjvaJeS;)CAo#aZVkF24$Rvg?@!|$#h5&WZ+A~B&qN)-}5!`m2_Z(4%jics5H zHP~KKm#HuPJwDs2sx)$4?@tAGq~`=GHs1^1>Xi1rINr>QIkDnCU#r7VtEr zOi}!N+MV!y5>B4w5o!iJJDw@xztnHwENC*HD#8`d)Kgt- zV8$rhA7;BfAbAHv?bk6M84;8-Oz1Y{^TG2WSptC<;Omemc5;>52XaMhjIfY=BZ+#hc9$3yF(W_< z`@E<*jYSkERoE)pib}=R5EtM92}A(H;7he715%4qj%qub?%-6<7D{_JC8~V3zt85( zJ@!v-Q!*6D-=<4)L=(;=4#Q#^BM8PLU&WsXLyoJ=T)E@-kUgauGZ!+$)~viT<17_E zNJgb{xrv!s96oGQO{6ucYPOr;BZZ%*gj$+}i44a&0znl)l6Z`=G_Ne&^w3sJ!iHyc>~3vQpV!*{9Y#$I z?{>`|!tJ)ur-x8FyDX{>v>p|2;c7e*ON$7`u*D8j4v6^xuH?O0K`KT!Nj;l+*_>io!rn>STp%C6}^*uB%I2 z>Zte6)4@5A?=BP&?0wZF2d%HQK5)!LqBXVjs93TuT}_$o0bhuy-)e;v!O*8%^sU1~GwF{v8mVFnp+px?v0kI`o4Fb7;Z;=2@w?SV+u)mHX1kWI@aSAEb3A%#A~$k;{m2 zDbP#g#nh1jqgzJ@Z?@4@2SI~JjWU-AjgxU8B;m)Iy=?t?eE63ZVJ^rPC;!F)CtJ@w z@q8OPV)tzjlH(b5mMA5{(pR>%uPmTjU%t2jr+7lT%C+?|s%%5+%)hr{{wFP3`2XTR zoq-p5O8wpO>#SE2NmNc;0A}H30TTs43M}%IP`C~g z%A(u6n-g$JM5s%s6Qp!L7wO~!s@VgkE}P^OPCE-KRSG56YF`64zbVVX9X1tsW@gZY zU}xU>-ZOi<7S;0qBm2M6H|=`Z* zg8Sv*JZhYPm!jh`E}$M$_LLJp5D0#g8OjO_d=?Uvl^ke6ZbV1)9r9U3kW9J@ADjtc zfqW81zvKiREXkPhS;{7xOd#qkhbp;yF@ON}5|D09qfq9igz{|5vc0~i*}Hn**_=$Jxg+#q)!gNt^7ins!obqEoFpGsaB)yc zh`C@+mUBHurHZFSunFmTSd~8E6L^O5T-D2FIKRbY&Hv<988?XZlJX&`+Jh59UHbezK1@L=T*V?vNP z`qVba8DnAVD3ZrD?(P#R>{X${@riSO#xB>7hX1^K|6#vPP#RC#6#f8CHYJ=zsTogzzca6JB zwj)*m%}*;ICsuCS_uc+!E;VW!Keb-vRB@Y~om*_aT*q@P9e8RilbRd13-7Wa)uQG~SlO7CSB9%X*2HvN%eD7WGY6Z!(~g|X5?WKKoZ_Pd zzlbNTeNrOe%WWMbOFE=cOPjz#1J{HubX^j=p%$vTBX&v$Uq${n*dCX&#^}JalcU-c zu}1Ym)^E2`tKVd$)?KW0U3*)-PrNj^#Yx!)4Q_D^SlemzgR$Sda=6fi`g;V2m5?f7q5?J^$7Fw*GYf`xwoMd5F|M zkn$SVUj&w5ClFQk5RRfSfVvElTra(pg;X`U#FZTN{(X3Sg5cf75~Jr=3Q&N`wf9jF zE_#%OJqJqhUVdxh`XRZ^(SzW?V(t);-#F-1FP*5#B@b*plft2k#F~4px0qW8??9!SU3d+EaUK>$U#?{@$sV04@Lk DJFiW$ literal 0 HcmV?d00001