@vdwpsmt/node-red-contrib-flow-splitter-extended 1.0.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -8
- package/functions-templates-handler.js +11 -72
- package/index.js +155 -15
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -22,13 +22,18 @@ It will make the diffs of your version control much more controlled and readable
|
|
|
22
22
|
- Supports both YAML and JSON formats
|
|
23
23
|
- Maintains tab order through configuration
|
|
24
24
|
|
|
25
|
-
### Function & Template Extraction (NEW)
|
|
26
|
-
- Automatically extracts
|
|
27
|
-
- Extracts `
|
|
28
|
-
-
|
|
29
|
-
-
|
|
25
|
+
### Optional Function & Template Extraction and Restore (NEW)
|
|
26
|
+
- Automatically extracts into seperate files per function/ui-template
|
|
27
|
+
- Extracts code from `function` nodes into `.js` files
|
|
28
|
+
- Extracts `ui-template` (Dashboard 2.0) content into `.vue` files
|
|
29
|
+
- Supports function `initialize` and `finalize` code in separate files
|
|
30
|
+
- Extracts node `info` documentation into `.md` files
|
|
30
31
|
- Organizes extracted files in subdirectories alongside their parent tab/subflow
|
|
31
|
-
-
|
|
32
|
+
- Restores changes back to Node-RED
|
|
33
|
+
- Automatically on startup
|
|
34
|
+
- Manually reload using endpoint
|
|
35
|
+
- Both extraction (default true) and restoring (default false) can be changed in `.config.flow-splitter.json`
|
|
36
|
+
|
|
32
37
|
|
|
33
38
|
## Functioning
|
|
34
39
|
|
|
@@ -81,7 +86,7 @@ fetch('http://localhost:1880/flow-splitter/reload', {method: 'POST'})
|
|
|
81
86
|
```
|
|
82
87
|
|
|
83
88
|
This allows you to:
|
|
84
|
-
1. Edit function/template files in VS Code
|
|
89
|
+
1. Edit function/template files in VS Code or any other IDE
|
|
85
90
|
2. Save changes
|
|
86
91
|
3. Run the reload command
|
|
87
92
|
4. See changes immediately in Node-RED (without deploy/restart)
|
|
@@ -124,7 +129,8 @@ Default configuration file =
|
|
|
124
129
|
"fileFormat": "yaml",
|
|
125
130
|
"destinationFolder": "src",
|
|
126
131
|
"tabsOrder": [],
|
|
127
|
-
"extractFunctionsTemplates": true
|
|
132
|
+
"extractFunctionsTemplates": true,
|
|
133
|
+
"restoreFunctionsTemplates": false
|
|
128
134
|
}
|
|
129
135
|
```
|
|
130
136
|
|
|
@@ -4,7 +4,7 @@ const fs = require('fs-extra')
|
|
|
4
4
|
/**
|
|
5
5
|
* Functions and Templates nodes Handler
|
|
6
6
|
* Extracts function and ui-template node code into separate files
|
|
7
|
-
* and
|
|
7
|
+
* and restores them back when rebuilding flows
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
/**
|
|
@@ -18,6 +18,12 @@ function extractFunctionsAndTemplates(flowNodes, flowName, flowDir, RED) {
|
|
|
18
18
|
if (!flowNodes || flowNodes.length === 0) return
|
|
19
19
|
|
|
20
20
|
const extractedDir = path.join(flowDir, flowName)
|
|
21
|
+
|
|
22
|
+
// Delete entire extracted directory to ensure fresh state
|
|
23
|
+
if (fs.existsSync(extractedDir)) {
|
|
24
|
+
fs.removeSync(extractedDir)
|
|
25
|
+
}
|
|
26
|
+
|
|
21
27
|
const manifest = {}
|
|
22
28
|
const fileNames = []
|
|
23
29
|
let count = 0
|
|
@@ -124,20 +130,17 @@ function extractFunctionsAndTemplates(flowNodes, flowName, flowDir, RED) {
|
|
|
124
130
|
|
|
125
131
|
RED.log.info(`[node-red-contrib-flow-splitter] Extracted ${count} functions/templates for "${flowName}"`)
|
|
126
132
|
}
|
|
127
|
-
|
|
128
|
-
// Clean up unused files
|
|
129
|
-
cleanupUnusedFiles(extractedDir, manifest, RED)
|
|
130
133
|
}
|
|
131
134
|
|
|
132
135
|
/**
|
|
133
|
-
*
|
|
136
|
+
* Restore functions and templates from separate files back into flow nodes
|
|
134
137
|
* @param {Array} flowNodes - Array of nodes from a tab or subflow
|
|
135
138
|
* @param {string} flowName - Name of the tab or subflow
|
|
136
139
|
* @param {string} flowDir - Directory where the flow file is stored
|
|
137
140
|
* @param {object} RED - Node-RED runtime
|
|
138
141
|
* @returns {Array} - Updated flow nodes
|
|
139
142
|
*/
|
|
140
|
-
function
|
|
143
|
+
function restoreFunctionsAndTemplates(flowNodes, flowName, flowDir, RED) {
|
|
141
144
|
if (!flowNodes || flowNodes.length === 0) return flowNodes
|
|
142
145
|
|
|
143
146
|
const extractedDir = path.join(flowDir, flowName)
|
|
@@ -225,77 +228,13 @@ function collectFunctionsAndTemplates(flowNodes, flowName, flowDir, RED) {
|
|
|
225
228
|
})
|
|
226
229
|
|
|
227
230
|
if (updatedCount > 0) {
|
|
228
|
-
RED.log.info(`[node-red-contrib-flow-splitter]
|
|
231
|
+
RED.log.info(`[node-red-contrib-flow-splitter] Restored ${updatedCount} functions/templates for "${flowName}"`)
|
|
229
232
|
}
|
|
230
233
|
|
|
231
234
|
return flowNodes
|
|
232
235
|
}
|
|
233
236
|
|
|
234
|
-
/**
|
|
235
|
-
* Clean up files that are no longer in the manifest
|
|
236
|
-
* @param {string} extractedDir - Directory containing extracted files
|
|
237
|
-
* @param {object} manifest - Manifest object
|
|
238
|
-
* @param {object} RED - Node-RED runtime
|
|
239
|
-
*/
|
|
240
|
-
function cleanupUnusedFiles(extractedDir, manifest, RED) {
|
|
241
|
-
if (!fs.existsSync(extractedDir)) return
|
|
242
|
-
|
|
243
|
-
const validExtensions = ['.vue', '.js', '.md']
|
|
244
|
-
const files = getAllFiles(extractedDir, validExtensions)
|
|
245
|
-
|
|
246
|
-
files.forEach(file => {
|
|
247
|
-
// Skip manifest file
|
|
248
|
-
if (file.endsWith('.manifest.json')) return
|
|
249
|
-
|
|
250
|
-
let found = false
|
|
251
|
-
Object.keys(manifest).forEach((id) => {
|
|
252
|
-
const item = manifest[id]
|
|
253
|
-
if (file.indexOf(item.fileName) > -1) {
|
|
254
|
-
found = true
|
|
255
|
-
}
|
|
256
|
-
})
|
|
257
|
-
|
|
258
|
-
if (!found) {
|
|
259
|
-
const filePath = path.join(extractedDir, file)
|
|
260
|
-
try {
|
|
261
|
-
fs.removeSync(filePath)
|
|
262
|
-
RED.log.info(`[node-red-contrib-flow-splitter] Removed unused file: ${file}`)
|
|
263
|
-
} catch (error) {
|
|
264
|
-
RED.log.warn(`[node-red-contrib-flow-splitter] Could not remove file ${file}: ${error.message}`)
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
})
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
/**
|
|
271
|
-
* Get all files with specified extensions from a directory
|
|
272
|
-
* @param {string} dir - Directory to search
|
|
273
|
-
* @param {Array<string>} exts - Array of extensions to match
|
|
274
|
-
* @param {Array<string>} fileList - Accumulated file list
|
|
275
|
-
* @param {string} relDir - Relative directory path
|
|
276
|
-
* @returns {Array<string>} - List of file paths
|
|
277
|
-
*/
|
|
278
|
-
function getAllFiles(dir, exts, fileList = [], relDir = '') {
|
|
279
|
-
if (!fs.existsSync(dir)) return fileList
|
|
280
|
-
|
|
281
|
-
const files = fs.readdirSync(dir)
|
|
282
|
-
|
|
283
|
-
files.forEach(file => {
|
|
284
|
-
const filePath = path.join(dir, file)
|
|
285
|
-
const relPath = path.join(relDir, file)
|
|
286
|
-
const stat = fs.statSync(filePath)
|
|
287
|
-
|
|
288
|
-
if (stat.isDirectory()) {
|
|
289
|
-
getAllFiles(filePath, exts, fileList, relPath)
|
|
290
|
-
} else if (exts.some(ext => file.endsWith(ext))) {
|
|
291
|
-
fileList.push(relPath)
|
|
292
|
-
}
|
|
293
|
-
})
|
|
294
|
-
|
|
295
|
-
return fileList
|
|
296
|
-
}
|
|
297
|
-
|
|
298
237
|
module.exports = {
|
|
299
238
|
extractFunctionsAndTemplates,
|
|
300
|
-
|
|
239
|
+
restoreFunctionsAndTemplates
|
|
301
240
|
}
|
package/index.js
CHANGED
|
@@ -30,7 +30,8 @@ const DEFAULT_CFG = {
|
|
|
30
30
|
destinationFolder: 'src',
|
|
31
31
|
tabsOrder: [],
|
|
32
32
|
monolithFilename: "flows.json",
|
|
33
|
-
extractFunctionsTemplates: true
|
|
33
|
+
extractFunctionsTemplates: true,
|
|
34
|
+
restoreFunctionsTemplates: false
|
|
34
35
|
}
|
|
35
36
|
|
|
36
37
|
/**
|
|
@@ -113,6 +114,142 @@ function extractFunctionsTemplatesFromSplitFiles(cfg, projectPath) {
|
|
|
113
114
|
|
|
114
115
|
processFlowDirectory(tabsDir, cfg.fileFormat, 'tab')
|
|
115
116
|
processFlowDirectory(subflowsDir, cfg.fileFormat, 'subflow')
|
|
117
|
+
|
|
118
|
+
// Clean up orphaned directories from renamed/deleted flows
|
|
119
|
+
cleanupOrphanedDirectories(tabsDir, cfg.fileFormat)
|
|
120
|
+
cleanupOrphanedDirectories(subflowsDir, cfg.fileFormat)
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Remove subdirectories that don't have a corresponding flow file
|
|
125
|
+
* @param {string} dir - Directory to clean (tabs or subflows)
|
|
126
|
+
* @param {string} fileFormat - File format (yaml or json)
|
|
127
|
+
*/
|
|
128
|
+
function cleanupOrphanedDirectories(dir, fileFormat) {
|
|
129
|
+
if (!fs.existsSync(dir)) {
|
|
130
|
+
return
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const extension = fileFormat === 'yaml' ? '.yaml' : '.json'
|
|
134
|
+
|
|
135
|
+
// Get all flow files
|
|
136
|
+
const flowFiles = fs.readdirSync(dir)
|
|
137
|
+
.filter(f => f.endsWith(extension))
|
|
138
|
+
.map(f => path.basename(f, extension))
|
|
139
|
+
|
|
140
|
+
// Get all subdirectories
|
|
141
|
+
const subdirs = fs.readdirSync(dir)
|
|
142
|
+
.filter(f => {
|
|
143
|
+
const fullPath = path.join(dir, f)
|
|
144
|
+
return fs.statSync(fullPath).isDirectory()
|
|
145
|
+
})
|
|
146
|
+
|
|
147
|
+
// Remove orphaned subdirectories
|
|
148
|
+
subdirs.forEach(subdir => {
|
|
149
|
+
if (!flowFiles.includes(subdir)) {
|
|
150
|
+
const subdirPath = path.join(dir, subdir)
|
|
151
|
+
try {
|
|
152
|
+
fs.rmSync(subdirPath, { recursive: true, force: true })
|
|
153
|
+
RED.log.info(`[node-red-contrib-flow-splitter-extended] Removed orphaned directory: ${subdir}`)
|
|
154
|
+
} catch (error) {
|
|
155
|
+
RED.log.warn(`[node-red-contrib-flow-splitter-extended] Could not remove orphaned directory ${subdir}: ${error.message}`)
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
})
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Clean up old flow files when a tab or subflow has been renamed.
|
|
163
|
+
* Scans existing files and removes those with IDs that match current flows but have different filenames.
|
|
164
|
+
* @param {Array} flowNodes - Array of all flow nodes from Node-RED
|
|
165
|
+
* @param {object} cfg - Splitter configuration
|
|
166
|
+
* @param {string} projectPath - Path to the project
|
|
167
|
+
*/
|
|
168
|
+
function cleanupRenamedFlows(flowNodes, cfg, projectPath) {
|
|
169
|
+
const srcDir = path.join(projectPath, cfg.destinationFolder || 'src')
|
|
170
|
+
const tabsDir = path.join(srcDir, 'tabs')
|
|
171
|
+
const subflowsDir = path.join(srcDir, 'subflows')
|
|
172
|
+
const extension = cfg.fileFormat === 'yaml' ? '.yaml' : '.json'
|
|
173
|
+
|
|
174
|
+
// Build maps of ID -> expected filename from the current flow nodes
|
|
175
|
+
const tabsIdToFilename = new Map()
|
|
176
|
+
const subflowsIdToFilename = new Map()
|
|
177
|
+
|
|
178
|
+
flowNodes.forEach(node => {
|
|
179
|
+
if (node.type === 'tab' && node.id) {
|
|
180
|
+
// Use normalizedLabel if available, otherwise compute from label
|
|
181
|
+
const label = node.label || node.id
|
|
182
|
+
const expectedFilename = node.normalizedLabel ||
|
|
183
|
+
label.replace(/[\/\\:*?"<>|]/g, '-').toLowerCase().replace(/\s+/g, '-')
|
|
184
|
+
tabsIdToFilename.set(node.id, expectedFilename)
|
|
185
|
+
} else if (node.type === 'subflow' && node.id) {
|
|
186
|
+
const name = node.name || node.id
|
|
187
|
+
const expectedFilename = name.replace(/[\/\\:*?"<>|]/g, '-').toLowerCase().replace(/\s+/g, '-')
|
|
188
|
+
subflowsIdToFilename.set(node.id, expectedFilename)
|
|
189
|
+
}
|
|
190
|
+
})
|
|
191
|
+
|
|
192
|
+
// Clean up tabs directory
|
|
193
|
+
cleanupRenamedFlowsInDir(tabsDir, tabsIdToFilename, extension, 'tab')
|
|
194
|
+
|
|
195
|
+
// Clean up subflows directory
|
|
196
|
+
cleanupRenamedFlowsInDir(subflowsDir, subflowsIdToFilename, extension, 'subflow')
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Clean up renamed flows in a specific directory.
|
|
201
|
+
* Removes old files when the same ID exists but with a different filename.
|
|
202
|
+
* @param {string} dir - Directory to scan
|
|
203
|
+
* @param {Map} idToFilename - Map of ID to expected filename
|
|
204
|
+
* @param {string} extension - File extension (.yaml or .json)
|
|
205
|
+
* @param {string} flowType - Type of flow (tab or subflow)
|
|
206
|
+
*/
|
|
207
|
+
function cleanupRenamedFlowsInDir(dir, idToFilename, extension, flowType) {
|
|
208
|
+
if (!fs.existsSync(dir)) {
|
|
209
|
+
return
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const files = fs.readdirSync(dir).filter(f => f.endsWith(extension))
|
|
213
|
+
|
|
214
|
+
files.forEach(file => {
|
|
215
|
+
const filePath = path.join(dir, file)
|
|
216
|
+
const filename = path.basename(file, extension)
|
|
217
|
+
|
|
218
|
+
try {
|
|
219
|
+
let flowData
|
|
220
|
+
const fileContent = fs.readFileSync(filePath, 'utf8')
|
|
221
|
+
|
|
222
|
+
if (extension === '.yaml') {
|
|
223
|
+
flowData = yaml.load(fileContent)
|
|
224
|
+
} else {
|
|
225
|
+
flowData = JSON.parse(fileContent)
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
const flowDataArray = Array.isArray(flowData) ? flowData : [flowData]
|
|
229
|
+
const flowNode = flowDataArray.find(n => n.type === flowType)
|
|
230
|
+
|
|
231
|
+
if (flowNode && flowNode.id) {
|
|
232
|
+
const expectedFilename = idToFilename.get(flowNode.id)
|
|
233
|
+
|
|
234
|
+
// If this ID exists in current flows but with a different filename, this is an old renamed file
|
|
235
|
+
if (expectedFilename && expectedFilename !== filename) {
|
|
236
|
+
RED.log.info(`[node-red-contrib-flow-splitter-extended] Removing old ${flowType} file "${file}" (renamed to "${expectedFilename}${extension}")`)
|
|
237
|
+
|
|
238
|
+
// Remove the old flow file
|
|
239
|
+
fs.unlinkSync(filePath)
|
|
240
|
+
|
|
241
|
+
// Remove the corresponding subdirectory if it exists
|
|
242
|
+
const subdirPath = path.join(dir, filename)
|
|
243
|
+
if (fs.existsSync(subdirPath) && fs.statSync(subdirPath).isDirectory()) {
|
|
244
|
+
fs.rmSync(subdirPath, { recursive: true, force: true })
|
|
245
|
+
RED.log.info(`[node-red-contrib-flow-splitter-extended] Removed old ${flowType} directory "${filename}"`)
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
} catch (error) {
|
|
250
|
+
RED.log.warn(`[node-red-contrib-flow-splitter-extended] Error checking ${flowType} file ${file}: ${error.message}`)
|
|
251
|
+
}
|
|
252
|
+
})
|
|
116
253
|
}
|
|
117
254
|
|
|
118
255
|
/**
|
|
@@ -153,12 +290,12 @@ function processFlowDirectory(dir, fileFormat, flowType) {
|
|
|
153
290
|
}
|
|
154
291
|
|
|
155
292
|
/**
|
|
156
|
-
*
|
|
293
|
+
* Restore functions and templates back into split flow files before rebuilding single flows.json file
|
|
157
294
|
* @param {object} cfg - Splitter configuration
|
|
158
295
|
* @param {string} projectPath - Path to the project
|
|
159
296
|
*/
|
|
160
|
-
function
|
|
161
|
-
if (cfg.
|
|
297
|
+
function restoreFunctionsTemplatesIntoSplitFiles(cfg, projectPath) {
|
|
298
|
+
if (cfg.restoreFunctionsTemplates === false) {
|
|
162
299
|
return
|
|
163
300
|
}
|
|
164
301
|
|
|
@@ -166,19 +303,19 @@ function collectFunctionsTemplatesIntoSplitFiles(cfg, projectPath) {
|
|
|
166
303
|
const tabsDir = path.join(srcDir, 'tabs')
|
|
167
304
|
const subflowsDir = path.join(srcDir, 'subflows')
|
|
168
305
|
|
|
169
|
-
RED.log.info("[node-red-contrib-flow-splitter-extended]
|
|
306
|
+
RED.log.info("[node-red-contrib-flow-splitter-extended] Restoring functions and templates...")
|
|
170
307
|
|
|
171
|
-
|
|
172
|
-
|
|
308
|
+
restoreIntoFlowDirectory(tabsDir, cfg.fileFormat, 'tab')
|
|
309
|
+
restoreIntoFlowDirectory(subflowsDir, cfg.fileFormat, 'subflow')
|
|
173
310
|
}
|
|
174
311
|
|
|
175
312
|
/**
|
|
176
|
-
* Process a directory of flow files to
|
|
313
|
+
* Process a directory of flow files to restore functions/templates
|
|
177
314
|
* @param {string} dir - Directory to process
|
|
178
315
|
* @param {string} fileFormat - File format (yaml or json)
|
|
179
316
|
* @param {string} flowType - Type of flow (tab or subflow)
|
|
180
317
|
*/
|
|
181
|
-
function
|
|
318
|
+
function restoreIntoFlowDirectory(dir, fileFormat, flowType) {
|
|
182
319
|
if (!fs.existsSync(dir)) {
|
|
183
320
|
return
|
|
184
321
|
}
|
|
@@ -201,7 +338,7 @@ function collectFromFlowDirectory(dir, fileFormat, flowType) {
|
|
|
201
338
|
}
|
|
202
339
|
|
|
203
340
|
let flowNodes = Array.isArray(flowData) ? flowData : [flowData]
|
|
204
|
-
flowNodes = functionsTemplatesHandler.
|
|
341
|
+
flowNodes = functionsTemplatesHandler.restoreFunctionsAndTemplates(flowNodes, flowName, dir, RED)
|
|
205
342
|
|
|
206
343
|
if (fileFormat === 'yaml') {
|
|
207
344
|
const yamlContent = yaml.dump(flowNodes, {
|
|
@@ -216,14 +353,14 @@ function collectFromFlowDirectory(dir, fileFormat, flowType) {
|
|
|
216
353
|
}
|
|
217
354
|
|
|
218
355
|
} catch (error) {
|
|
219
|
-
RED.log.warn(`[node-red-contrib-flow-splitter-extended] Error
|
|
356
|
+
RED.log.warn(`[node-red-contrib-flow-splitter-extended] Error restoring ${flowType} ${flowName}: ${error.message}`)
|
|
220
357
|
}
|
|
221
358
|
})
|
|
222
359
|
}
|
|
223
360
|
|
|
224
361
|
/**
|
|
225
362
|
* Manual reload endpoint handler
|
|
226
|
-
*
|
|
363
|
+
* Restores functions/templates from files and reloads flows
|
|
227
364
|
*/
|
|
228
365
|
async function manualReload(req, res) {
|
|
229
366
|
try {
|
|
@@ -232,7 +369,7 @@ async function manualReload(req, res) {
|
|
|
232
369
|
const projectPath = getProjectPath()
|
|
233
370
|
const cfg = loadSplitterConfig(projectPath)
|
|
234
371
|
|
|
235
|
-
|
|
372
|
+
restoreFunctionsTemplatesIntoSplitFiles(cfg, projectPath)
|
|
236
373
|
|
|
237
374
|
const flowSet = manager.constructFlowSetFromTreeFiles(cfg, projectPath)
|
|
238
375
|
|
|
@@ -278,9 +415,9 @@ async function onFlowReload(flowEventData) {
|
|
|
278
415
|
|
|
279
416
|
if (flowEventData.config.flows.length === 0) {
|
|
280
417
|
// The flow file does not exist or is empty - rebuild from split files
|
|
281
|
-
RED.log.info("[node-red-contrib-flow-splitter-extended] Rebuilding
|
|
418
|
+
RED.log.info("[node-red-contrib-flow-splitter-extended] Rebuilding single flows.json file from source files")
|
|
282
419
|
|
|
283
|
-
|
|
420
|
+
restoreFunctionsTemplatesIntoSplitFiles(cfg, projectPath)
|
|
284
421
|
|
|
285
422
|
const flowSet = manager.constructFlowSetFromTreeFiles(cfg, projectPath)
|
|
286
423
|
|
|
@@ -303,6 +440,9 @@ async function onFlowReload(flowEventData) {
|
|
|
303
440
|
}
|
|
304
441
|
|
|
305
442
|
// Flows exist - split into source files
|
|
443
|
+
// First, clean up any old files from renamed tabs/subflows
|
|
444
|
+
cleanupRenamedFlows(flowEventData.config.flows, cfg, projectPath)
|
|
445
|
+
|
|
306
446
|
const flowSet = manager.constructFlowSetFromMonolithObject(flowEventData.config.flows)
|
|
307
447
|
|
|
308
448
|
const updatedCfg = manager.constructTreeFilesFromFlowSet(flowSet, cfg, projectPath)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vdwpsmt/node-red-contrib-flow-splitter-extended",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"description": "Split your flows.json file in individual YAML or JSON files (per tab, subflow and config-node) with optional function and ui-template node code extraction.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"keywords": [
|