mirror of
https://github.com/node-red/node-red.git
synced 2025-03-01 10:36:34 +00:00
Handle subflow modules with their own npm dependencies
This commit is contained in:
@@ -28,6 +28,7 @@ let loadDenyList = [];
|
||||
var settings;
|
||||
var disableNodePathScan = false;
|
||||
var iconFileExtensions = [".png", ".gif", ".svg"];
|
||||
var packageList = {};
|
||||
|
||||
function init(_settings) {
|
||||
settings = _settings;
|
||||
@@ -187,9 +188,17 @@ function scanTreeForNodesModules(moduleName) {
|
||||
var userDir;
|
||||
|
||||
if (settings.userDir) {
|
||||
packageList = getPackageList();
|
||||
userDir = path.join(settings.userDir,"node_modules");
|
||||
results = scanDirForNodesModules(userDir,moduleName);
|
||||
results.forEach(function(r) { r.local = true; });
|
||||
results.forEach(function(r) {
|
||||
// If it was found in <userDir>/node_modules then it is considered
|
||||
// a local module.
|
||||
// Also check to see if it is listed in the package.json file as a user-installed
|
||||
// module. This distinguishes modules installed as a dependency
|
||||
r.local = true;
|
||||
r.user = !!packageList[r.package.name];
|
||||
});
|
||||
}
|
||||
|
||||
if (dir) {
|
||||
@@ -288,20 +297,19 @@ function getNodeFiles(disableNodePathScan) {
|
||||
}
|
||||
}
|
||||
|
||||
var nodeList = {
|
||||
"node-red": {
|
||||
name: "node-red",
|
||||
version: settings.version,
|
||||
nodes: {},
|
||||
icons: iconList
|
||||
}
|
||||
var nodeList = {};
|
||||
var coreNodeEntry = {
|
||||
name: "node-red",
|
||||
version: settings.version,
|
||||
nodes: {},
|
||||
icons: iconList
|
||||
}
|
||||
nodeFiles.forEach(function(node) {
|
||||
nodeList["node-red"].nodes[node.name] = node;
|
||||
coreNodeEntry.nodes[node.name] = node;
|
||||
});
|
||||
if (settings.coreNodesDir) {
|
||||
var examplesDir = path.join(settings.coreNodesDir,"examples");
|
||||
nodeList["node-red"].examples = {path: examplesDir};
|
||||
coreNodeEntry.examples = {path: examplesDir};
|
||||
}
|
||||
|
||||
if (!disableNodePathScan) {
|
||||
@@ -310,7 +318,6 @@ function getNodeFiles(disableNodePathScan) {
|
||||
// Filter the module list to ignore global modules
|
||||
// that have also been installed locally - allowing the user to
|
||||
// update a module they may not otherwise be able to touch
|
||||
|
||||
moduleFiles.sort(function(A,B) {
|
||||
if (A.local && !B.local) {
|
||||
return -1
|
||||
@@ -323,7 +330,7 @@ function getNodeFiles(disableNodePathScan) {
|
||||
moduleFiles = moduleFiles.filter(function(mod) {
|
||||
var result;
|
||||
if (!knownModules[mod.package.name]) {
|
||||
knownModules[mod.package.name] = true;
|
||||
knownModules[mod.package.name] = mod;
|
||||
result = true;
|
||||
} else {
|
||||
result = false;
|
||||
@@ -332,48 +339,62 @@ function getNodeFiles(disableNodePathScan) {
|
||||
return result;
|
||||
});
|
||||
|
||||
moduleFiles.forEach(function(moduleFile) {
|
||||
var nodeModuleFiles = getModuleNodeFiles(moduleFile);
|
||||
nodeList[moduleFile.package.name] = {
|
||||
name: moduleFile.package.name,
|
||||
version: moduleFile.package.version,
|
||||
path: moduleFile.dir,
|
||||
local: moduleFile.local||false,
|
||||
nodes: {},
|
||||
icons: nodeModuleFiles.icons,
|
||||
examples: nodeModuleFiles.examples
|
||||
};
|
||||
if (moduleFile.package['node-red'].version) {
|
||||
nodeList[moduleFile.package.name].redVersion = moduleFile.package['node-red'].version;
|
||||
// Do a second pass to check we have all the declared node dependencies
|
||||
// As this is only done as part of the initial palette load, `knownModules` will
|
||||
// contain a list of everything discovered during this phase. This means
|
||||
// we can check for missing dependencies here.
|
||||
moduleFiles = moduleFiles.filter(function(mod) {
|
||||
if (Array.isArray(mod.package["node-red"].dependencies)) {
|
||||
const deps = mod.package["node-red"].dependencies;
|
||||
const missingDeps = mod.package["node-red"].dependencies.filter(dep => {
|
||||
if (knownModules[dep]) {
|
||||
knownModules[dep].usedBy = knownModules[dep].usedBy || [];
|
||||
knownModules[dep].usedBy.push(mod.package.name)
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
})
|
||||
if (missingDeps.length > 0) {
|
||||
log.error(`Module: ${mod.package.name} missing dependencies:`);
|
||||
missingDeps.forEach(m => { log.error(` - ${m}`)});
|
||||
return false;
|
||||
}
|
||||
}
|
||||
nodeModuleFiles.files.forEach(function(node) {
|
||||
node.local = moduleFile.local||false;
|
||||
nodeList[moduleFile.package.name].nodes[node.name] = node;
|
||||
});
|
||||
nodeFiles = nodeFiles.concat(nodeModuleFiles.files);
|
||||
return true;
|
||||
});
|
||||
nodeList = convertModuleFileListToObject(moduleFiles);
|
||||
} else {
|
||||
// console.log("node path scan disabled");
|
||||
}
|
||||
nodeList["node-red"] = coreNodeEntry;
|
||||
return nodeList;
|
||||
}
|
||||
|
||||
function getModuleFiles(module) {
|
||||
var nodeList = {};
|
||||
|
||||
// Update the package list
|
||||
var moduleFiles = scanTreeForNodesModules(module);
|
||||
if (moduleFiles.length === 0) {
|
||||
var err = new Error(log._("nodes.registry.localfilesystem.module-not-found", {module:module}));
|
||||
err.code = 'MODULE_NOT_FOUND';
|
||||
throw err;
|
||||
}
|
||||
// Unlike when doing the initial palette load, this call cannot verify the
|
||||
// dependencies of the new module as it doesn't have visiblity of what
|
||||
// is in the registry. That will have to be done be the caller in loader.js
|
||||
return convertModuleFileListToObject(moduleFiles);
|
||||
}
|
||||
|
||||
function convertModuleFileListToObject(moduleFiles) {
|
||||
const nodeList = {};
|
||||
moduleFiles.forEach(function(moduleFile) {
|
||||
|
||||
var nodeModuleFiles = getModuleNodeFiles(moduleFile);
|
||||
nodeList[moduleFile.package.name] = {
|
||||
name: moduleFile.package.name,
|
||||
version: moduleFile.package.version,
|
||||
path: moduleFile.dir,
|
||||
local: moduleFile.local||false,
|
||||
user: moduleFile.user||false,
|
||||
nodes: {},
|
||||
icons: nodeModuleFiles.icons,
|
||||
examples: nodeModuleFiles.examples
|
||||
@@ -381,7 +402,14 @@ function getModuleFiles(module) {
|
||||
if (moduleFile.package['node-red'].version) {
|
||||
nodeList[moduleFile.package.name].redVersion = moduleFile.package['node-red'].version;
|
||||
}
|
||||
if (moduleFile.package['node-red'].dependencies) {
|
||||
nodeList[moduleFile.package.name].dependencies = moduleFile.package['node-red'].dependencies;
|
||||
}
|
||||
if (moduleFile.usedBy) {
|
||||
nodeList[moduleFile.package.name].usedBy = moduleFile.usedBy;
|
||||
}
|
||||
nodeModuleFiles.files.forEach(function(node) {
|
||||
node.local = moduleFile.local||false;
|
||||
nodeList[moduleFile.package.name].nodes[node.name] = node;
|
||||
nodeList[moduleFile.package.name].nodes[node.name].local = moduleFile.local || false;
|
||||
});
|
||||
@@ -412,6 +440,23 @@ function scanIconDir(dir) {
|
||||
})
|
||||
return iconList;
|
||||
}
|
||||
/**
|
||||
* Gets the list of modules installed in this runtime as reported by package.json
|
||||
* Note: these may include non-Node-RED modules
|
||||
*/
|
||||
function getPackageList() {
|
||||
var list = {};
|
||||
if (settings.userDir) {
|
||||
try {
|
||||
var userPackage = path.join(settings.userDir,"package.json");
|
||||
var pkg = JSON.parse(fs.readFileSync(userPackage,"utf-8"));
|
||||
return pkg.dependencies;
|
||||
} catch(err) {
|
||||
log.error(err);
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
init: init,
|
||||
|
Reference in New Issue
Block a user