| 149 | } |
| 150 | |
| 151 | transformFlows(flows, credentials) { |
| 152 | const imports = new Map([["nodered", ""]]); // ensure that globalThis.RED is available |
| 153 | |
| 154 | // must be an array with at least one element |
| 155 | if (!Array.isArray(flows)) |
| 156 | throw new Error("JSON input is not an array"); |
| 157 | |
| 158 | if (!flows.length) |
| 159 | throw new Error("no nodes"); |
| 160 | |
| 161 | // if no flows, create one. useful for running flow snippets (e.g. https://cookbook.nodered.org/). |
| 162 | if (!flows.some(config => ("tab" === config.type) && config.z)) { |
| 163 | const z = flows[0].z; |
| 164 | if (flows.every(config => z === config.z)) { |
| 165 | flows.unshift({ |
| 166 | type: "tab", |
| 167 | name: "auto-generated by nodered2mcu", |
| 168 | id: z |
| 169 | }); |
| 170 | } |
| 171 | } |
| 172 | |
| 173 | // add global configuration flow to start of list |
| 174 | flows.unshift({ |
| 175 | type: "tab", |
| 176 | id: "__config", |
| 177 | name: "global config" |
| 178 | }); |
| 179 | |
| 180 | // set z for each node in global configuration flow |
| 181 | flows.forEach(config => { |
| 182 | if (("tab" !== config.type) && !config.z) |
| 183 | config.z = "__config"; |
| 184 | }); |
| 185 | |
| 186 | // map from node ID to config and delete disabled and Comment nodes |
| 187 | const nodes = new Map; |
| 188 | for (let i = 0; i < flows.length; i++) { |
| 189 | const config = flows[i]; |
| 190 | if ("tab" === config.type) |
| 191 | continue; |
| 192 | if(("comment" === config.type)&&(config.name?.startsWith("moddable_import"))&&(config.info?.startsWith("{"))){ |
| 193 | const arr = JSON.parse(config.info); |
| 194 | for(let key in arr) { |
| 195 | imports.set(key, arr[key]); |
| 196 | } |
| 197 | } |
| 198 | if (config.d || ("comment" === config.type)) { |
| 199 | flows.splice(i, 1); |
| 200 | i -= 1; |
| 201 | } |
| 202 | else |
| 203 | nodes.set(config.id, config); |
| 204 | } |
| 205 | |
| 206 | // route around junction nodes |
| 207 | while (true) { |
| 208 | let changed; |