1
0
mirror of https://github.com/node-red/node-red.git synced 2023-10-10 13:36:53 +02:00

Merge pull request #3676 from Steve-Mcl/nodesdir-feature-parity

Scanning of `nodesdir` to be more aligned (feature wise) with `coreNodeDir` and `userDir`
This commit is contained in:
Nick O'Leary 2022-06-16 13:11:36 +01:00 committed by GitHub
commit 77c4423059
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
35 changed files with 587 additions and 98 deletions

View File

@ -88,9 +88,10 @@ function getLocalFile(file) {
/**
* Synchronously walks the directory looking for node files.
* @param dir the directory to search
* @param skipValidNodeRedModules a flag to skip lading icons & files if the directory a valid node-red module
* @return an array of fully-qualified paths to .js files
*/
function getLocalNodeFiles(dir) {
function getLocalNodeFiles(dir, skipValidNodeRedModules) {
dir = path.resolve(dir);
var result = [];
@ -102,6 +103,14 @@ function getLocalNodeFiles(dir) {
return {files: [], icons: []};
}
files.sort();
// when loading local files, if the path is a valid node-red module
// dont include it (will be picked up in scanTreeForNodesModules)
if(skipValidNodeRedModules && files.indexOf("package.json") >= 0) {
const package = getPackageDetails(dir)
if(package.isNodeRedModule) {
return {files: [], icons: []};
}
}
files.forEach(function(fn) {
var stats = fs.statSync(path.join(dir,fn));
if (stats.isFile()) {
@ -114,7 +123,7 @@ function getLocalNodeFiles(dir) {
} else if (stats.isDirectory()) {
// Ignore /.dirs/, /lib/ /node_modules/
if (!/^(\..*|lib|icons|node_modules|test|locales)$/.test(fn)) {
var subDirResults = getLocalNodeFiles(path.join(dir,fn));
var subDirResults = getLocalNodeFiles(path.join(dir,fn), skipValidNodeRedModules);
result = result.concat(subDirResults.files);
icons = icons.concat(subDirResults.icons);
} else if (fn === "icons") {
@ -126,21 +135,30 @@ function getLocalNodeFiles(dir) {
return {files: result, icons: icons}
}
function scanDirForNodesModules(dir,moduleName) {
var results = [];
var scopeName;
function scanDirForNodesModules(dir,moduleName,package) {
let results = [];
let scopeName;
let files
try {
var files = fs.readdirSync(dir);
if (moduleName) {
var m = /^(?:(@[^/]+)[/])?([^@/]+)/.exec(moduleName);
if (m) {
scopeName = m[1];
moduleName = m[2];
let isNodeRedModule = false
if(package) {
dir = path.join(package.moduleDir,'..')
files = [path.basename(package.moduleDir)]
moduleName = (package.package ? package.package.name : null) || moduleName
isNodeRedModule = package.isNodeRedModule
} else {
files = fs.readdirSync(dir);
if (moduleName) {
var m = /^(?:(@[^/]+)[/])?([^@/]+)/.exec(moduleName);
if (m) {
scopeName = m[1];
moduleName = m[2];
}
}
}
for (var i=0;i<files.length;i++) {
var fn = files[i];
if (/^@/.test(fn)) {
for (let i=0;i<files.length;i++) {
let fn = files[i];
if (!isNodeRedModule && /^@/.test(fn)) {
if (scopeName && scopeName === fn) {
// Looking for a specific scope/module
results = results.concat(scanDirForNodesModules(path.join(dir,fn),moduleName));
@ -149,16 +167,18 @@ function scanDirForNodesModules(dir,moduleName) {
results = results.concat(scanDirForNodesModules(path.join(dir,fn),moduleName));
}
} else {
if (isIncluded(fn) && !isExcluded(fn) && (!moduleName || fn == moduleName)) {
var pkgfn = path.join(dir,fn,"package.json");
if ((isNodeRedModule || (!moduleName || fn == moduleName)) && (isIncluded(fn) && !isExcluded(fn))) {
try {
var pkg = require(pkgfn);
if (pkg['node-red']) {
if (!registryUtil.checkModuleAllowed(pkg.name,pkg.version,loadAllowList,loadDenyList)) {
log.debug("! Module: "+pkg.name+" "+pkg.version+ " *ignored due to denyList*");
const moduleDir = isNodeRedModule ? package.moduleDir : path.join(dir,fn);
const pkg = package || getPackageDetails(moduleDir)
if(pkg.error) {
throw pkg.error
}
if (pkg.isNodeRedModule) {
if (!pkg.allowed) {
log.debug("! Module: "+pkg.package.name+" "+pkg.package.version+ " *ignored due to denyList*");
} else {
var moduleDir = path.join(dir,fn);
results.push({dir:moduleDir,package:pkg});
results.push({dir:moduleDir,package:pkg.package});
}
}
} catch(err) {
@ -182,11 +202,14 @@ function scanDirForNodesModules(dir,moduleName) {
* @param moduleName the name of the module to be found
* @return a list of node modules: {dir,package}
*/
function scanTreeForNodesModules(moduleName) {
var dir = settings.coreNodesDir;
var results = [];
var userDir;
function scanTreeForNodesModules(moduleName) {
let coreNodesDir = settings.coreNodesDir;
let results = [];
let userDir;
let nodesDir;
if(settings.nodesDir) {
nodesDir = Array.isArray(settings.nodesDir) ? settings.nodesDir : [settings.nodesDir]
}
if (settings.userDir) {
packageList = getPackageList();
userDir = path.join(settings.userDir,"node_modules");
@ -201,15 +224,46 @@ function scanTreeForNodesModules(moduleName) {
});
}
if (dir) {
var up = path.resolve(path.join(dir,".."));
while (up !== dir) {
var pm = path.join(dir,"node_modules");
if (coreNodesDir) {
var up = path.resolve(path.join(coreNodesDir,".."));
while (up !== coreNodesDir) {
var pm = path.join(coreNodesDir,"node_modules");
if (pm != userDir) {
results = results.concat(scanDirForNodesModules(pm,moduleName));
}
dir = up;
up = path.resolve(path.join(dir,".."));
coreNodesDir = up;
up = path.resolve(path.join(coreNodesDir,".."));
}
}
// scan nodesDir for any node-red modules
/*
1. if !exist(package.json) || !package.json.has(node-red) => look for node_modules
2. exist(package.json) && package.json.has(node-red) => load this only
3. in original scan of nodesDir, ignore if:(exist(package.json) && package.json.has(node-red))
*/
if (nodesDir) {
for (let dirIndex = 0; dirIndex < nodesDir.length; dirIndex++) {
const nodeDir = nodesDir[dirIndex];
const packageDetails = getPackageDetails(nodeDir)
if(packageDetails.isNodeRedModule) {
//we have found a node-red module, scan it
const nrModules = scanDirForNodesModules(nodeDir, packageDetails.package.name, packageDetails);
results = results.concat(nrModules);
} else if (packageDetails.has_node_modules) {
//If this dir has a `node_modues` dir, scan it
const nodeModulesDir = path.join(nodeDir, 'node_modules')
const nrModules = scanDirForNodesModules(nodeModulesDir, moduleName );
results = results.concat(nrModules);
} else {
//If this is not a node-red module AND it does NOT have a node_modules dir,
//it may be a directory of project directories or a node_modules dir?
//scan this instead
const nrModules = scanDirForNodesModules(nodeDir, moduleName);
results = results.concat(nrModules);
}
}
}
return results;
@ -274,24 +328,26 @@ function getModuleNodeFiles(module) {
}
function getNodeFiles(disableNodePathScan) {
var dir;
// Find all of the nodes to load
var nodeFiles = [];
var results;
var dir;
var iconList = [];
let results;
let nodesDir;
if(settings.nodesDir) {
nodesDir = Array.isArray(settings.nodesDir) ? settings.nodesDir : [settings.nodesDir]
}
let dir;
let nodeFiles = [];
let iconList = [];
if (settings.coreNodesDir) {
results = getLocalNodeFiles(path.resolve(settings.coreNodesDir));
nodeFiles = nodeFiles.concat(results.files);
iconList = iconList.concat(results.icons);
var defaultLocalesPath = path.join(settings.coreNodesDir,"locales");
let defaultLocalesPath = path.join(settings.coreNodesDir,"locales");
i18n.registerMessageCatalog("node-red",defaultLocalesPath,"messages.json");
}
if (settings.userDir) {
dir = path.join(settings.userDir,"lib","icons");
var icons = scanIconDir(dir);
let icons = scanIconDir(dir);
if (icons.length > 0) {
iconList.push({path:dir,icons:icons});
}
@ -301,13 +357,9 @@ function getNodeFiles(disableNodePathScan) {
nodeFiles = nodeFiles.concat(results.files);
iconList = iconList.concat(results.icons);
}
if (settings.nodesDir) {
dir = settings.nodesDir;
if (typeof settings.nodesDir == "string") {
dir = [dir];
}
for (var i=0;i<dir.length;i++) {
results = getLocalNodeFiles(dir[i]);
if (nodesDir) {
for (let i = 0; i < nodesDir.length; i++) {
results = getLocalNodeFiles(nodesDir[i], true);
nodeFiles = nodeFiles.concat(results.files);
iconList = iconList.concat(results.icons);
}
@ -479,7 +531,52 @@ function getPackageList() {
}
return list;
}
/**
* Gets the package json object for the supplied `dir`.
* If there is no package.json or the `node-red` section is missing, `result.isNodeRedModule` will be `false`.
* If there is no package.json `isPackage` will be `false`.
* If an error occurs, `result.error` will contain the error.
* @param {string} dir The directory to inspect
*/
function getPackageDetails(dir) {
const result = {
/** @type {string} The package directory */
moduleDir: dir,
/** @type {string} The full file path of package.json for this package */
packageFile: null,
/** @type {boolean} True if this is a valid node-red module */
isNodeRedModule: false,
/** @type {boolean} True if a package.json file is present */
isPackage: false,
/** @type {boolean} True if this a node-red module and passes the checks */
allowed: false,
/** @type {object} The contents of package.json */
package: null,
}
if (!dir) { return result }
try {
const packagefile = path.join(dir,'package.json')
result.has_node_modules = fs.existsSync(path.join(dir,'node_modules'))
if(!fs.existsSync(packagefile)) {
return result
}
result.packageFile = packagefile
const pkg = require(packagefile)
result.package = pkg
if(result.package) {
result.allowed = true
result.isPackage = true
result.isNodeRedModule = typeof result.package['node-red'] === 'object'
if(result.isNodeRedModule) {
result.isNodeRedModule = true;
result.allowed = registryUtil.checkModuleAllowed(pkg.name,pkg.version,loadAllowList,loadDenyList)
}
}
} catch(err) {
result.error = err; // this is not a package we are interested in!
}
return result || result;
}
module.exports = {
init: init,
getNodeFiles: getNodeFiles,

View File

@ -14,26 +14,41 @@
* limitations under the License.
**/
var should = require("should");
var sinon = require("sinon");
var path = require("path");
const should = require("should");
const sinon = require("sinon");
const path = require("path");
var NR_TEST_UTILS = require("nr-test-utils");
const NR_TEST_UTILS = require("nr-test-utils");
var localfilesystem = NR_TEST_UTILS.require("@node-red/registry/lib/localfilesystem");
const localfilesystem = NR_TEST_UTILS.require("@node-red/registry/lib/localfilesystem");
var resourcesDir = path.resolve(path.join(__dirname,"resources","local"));
var userDir = path.resolve(path.join(__dirname,"resources","userDir"));
var moduleDir = path.resolve(path.join(__dirname,"resources","local","TestNodeModule"));
const resourcesDir = path.resolve(path.join(__dirname,"resources","local"));
const userDir = path.resolve(path.join(__dirname,"resources","userDir"));
var i18n = NR_TEST_UTILS.require("@node-red/util").i18n;
const nodesDir1 = path.resolve(path.join(__dirname,"resources","nodesDir1"))
const nodesDir2 = path.resolve(path.join(__dirname,"resources","nodesDir2"))
const nodesDir3 =path.resolve(path.join(__dirname,"resources","nodesDir3"))
const moduleDir = path.resolve(path.join(__dirname,"resources","local","TestNodeModule"));
const i18n = NR_TEST_UTILS.require("@node-red/util").i18n;
describe("red/nodes/registry/localfilesystem",function() {
var stubs = [];
function stubPathJoin() {
var _join = path.join;
stubs.push(sinon.stub(path,"join").callsFake(function() {
if (arguments[0] == resourcesDir) {
// This stops the module tree scan from going any higher
// up the tree than resourcesDir.
return arguments[0];
}
return _join.apply(null,arguments);
}));
}
beforeEach(function() {
stubs.push(sinon.stub(i18n,"registerMessageCatalog").callsFake(function() { return Promise.resolve(); }));
})
var stubs = [];
afterEach(function() {
while(stubs.length) {
stubs.pop().restore();
@ -129,16 +144,76 @@ describe("red/nodes/registry/localfilesystem",function() {
checkNodes(nm.nodes,['TestNode5'],['TestNode1']);
done();
});
it("Finds nodes and icons only in nodesDir with files, icons and valid node-red packages",function(done) {
localfilesystem.init({nodesDir:nodesDir1});
const nodeList = localfilesystem.getNodeFiles(true);
nodeList.should.have.a.property("node-red");
const nm = nodeList['node-red'];
nm.should.have.a.property('name','node-red');
nm.should.have.a.property("nodes");
nm.should.have.a.property("icons");
checkNodes(nm.nodes,['loose1', 'loose2'], []);
//1 icon in nodesDir1/icons/ - should be found
//2 icons in nodesDir1/loose2/icons/ - should be found
//1 icons in nodesDir1/node-red-node-testnode/icons/ - should be found
//1 icons in nodesDir1/regular_module/icons/ - should NOT be found
//total icon sets 3, total icons 4
nm.icons.should.have.a.property("length", 3);
nm.icons[0].should.have.a.property("path")
nm.icons[0].should.have.a.property("icons", ['loose1.svg'])
nm.icons[1].should.have.a.property("path")
nm.icons[1].should.have.a.property("icons", ['loose2.svg', 'loose2b.svg'])
nm.icons[2].should.have.a.property("path")
nm.icons[2].should.have.a.property("icons", ['test.svg'])
done();
});
it("Should not find node-red node in nodesDir with files, icons and valid node-red packages",function(done) {
// path contains a regular node module and a node-red node module
localfilesystem.init({nodesDir:path.join(nodesDir1)});
const nodeList = localfilesystem.getNodeFiles(true);
nodeList.should.have.a.property("node-red");
const nm = nodeList['node-red'];
nm.should.have.a.property('name','node-red');
nm.should.have.a.property("nodes");
nm.nodes.should.have.a.property("loose1");
nm.nodes.should.have.a.property("loose2");
nm.nodes.should.not.have.a.property("regular_module");
nm.nodes.should.not.have.a.property("node-red-node-testnode");
for (let key of Object.keys(nm.nodes)) {
const n = nm.nodes[key];
n.file.indexOf("regular_module").should.eql(-1, `found icons in a node-red module`)
n.file.indexOf("node-red-node-testnode").should.eql(-1, `found icons in a node-red module`)
}
//1 icon in nodesDir1/icons/ - should be found
//2 icons in nodesDir1/loose2/icons/ - should be found
//1 icons in nodesDir1/node-red-node-testnode/icons/ - should be found
//1 icons in nodesDir1/regular_module/icons/ - should NOT be found
//total icon sets 3, total icons 4
nm.should.have.a.property("icons");
nm.icons.should.have.a.property("length", 3);
let iconCount = 0;
for (let index = 0; index < nm.icons.length; index++) {
const iconDir = nm.icons[index];
iconCount += iconDir.icons.length
iconDir.path.indexOf("node-red-node-testnode").should.eql(-1, `should not find icons in a node-red module`)
}
should(iconCount).eql(4, "Should find only 4 icons")
done();
});
it("Should not find node-red node in nodesDir when regular package and valid node-red packages",function(done) {
localfilesystem.init({nodesDir:path.join(nodesDir1,"regular_module")});
const nodeList = localfilesystem.getNodeFiles(true);
nodeList.should.have.a.property("node-red");
const nm = nodeList['node-red'];
nm.should.have.a.property('name','node-red');
nm.should.have.a.property("nodes", {});
nm.should.have.a.property("icons");
nm.icons.should.have.a.property("length", 1); //should find 1 icons folder
nm.icons[0].should.have.a.property("icons", [ 'test.svg' ]); //should find 1 icon in regular package
done();
});
it("Finds nodes module path",function(done) {
var _join = path.join;
stubs.push(sinon.stub(path,"join").callsFake(function() {
if (arguments[0] == resourcesDir) {
// This stops the module tree scan from going any higher
// up the tree than resourcesDir.
return arguments[0];
}
return _join.apply(null,arguments);
}));
stubPathJoin()
localfilesystem.init({coreNodesDir:moduleDir});
var nodeList = localfilesystem.getNodeFiles();
nodeList.should.have.a.property("node-red");
@ -166,8 +241,6 @@ describe("red/nodes/registry/localfilesystem",function() {
i18n.registerMessageCatalog.lastCall.args[1].should.eql(path.resolve(path.join(moduleDir,"locales")));
i18n.registerMessageCatalog.lastCall.args[2].should.eql('messages.json');
done();
});
it.skip("finds locales directory");
@ -205,15 +278,7 @@ describe("red/nodes/registry/localfilesystem",function() {
});
describe("#getModuleFiles",function() {
it("gets a nodes module files",function(done) {
var _join = path.join;
stubs.push(sinon.stub(path,"join").callsFake(function() {
if (arguments[0] == resourcesDir) {
// This stops the module tree scan from going any higher
// up the tree than resourcesDir.
return arguments[0];
}
return _join.apply(null,arguments);
}));
stubPathJoin()
localfilesystem.init({coreNodesDir:moduleDir});
var nodeModule = localfilesystem.getModuleFiles('TestNodeModule');
nodeModule.should.have.a.property('TestNodeModule');
@ -230,16 +295,87 @@ describe("red/nodes/registry/localfilesystem",function() {
done();
});
it("Finds only 1 node-red node in nodesDir amongst legacy nodes and regular nodes",function(done) {
stubPathJoin()
localfilesystem.init({nodesDir:[path.join(nodesDir1,"node-red-node-testnode")]});
const nodeModule = localfilesystem.getModuleFiles();
const loaded = Object.keys(nodeModule)
loaded.should.have.a.property("length", 1)
loaded.indexOf('node-red-node-testnode').should.greaterThan(-1, "Should load node-red-node-testnode")
nodeModule['node-red-node-testnode'].should.have.a.property('name','node-red-node-testnode');
nodeModule['node-red-node-testnode'].should.have.a.property('version','1.0.0');
nodeModule['node-red-node-testnode'].should.have.a.property('nodes');
nodeModule['node-red-node-testnode'].should.have.a.property('path');
nodeModule['node-red-node-testnode'].should.have.a.property('user', false);
checkNodes(nodeModule['node-red-node-testnode'].nodes,['testnode'],[],'node-red-node-testnode');
done();
});
it("Finds a node-red node in nodesDir with a sub dir containing valid node-red package",function(done) {
stubPathJoin()
localfilesystem.init({nodesDir:[path.join(nodesDir1,"node-red-node-testnode")]});
const nodeModule = localfilesystem.getModuleFiles();
const loaded = Object.keys(nodeModule)
nodeModule['node-red-node-testnode'].should.have.a.property('name','node-red-node-testnode');
nodeModule['node-red-node-testnode'].should.have.a.property('version','1.0.0');
nodeModule['node-red-node-testnode'].should.have.a.property('nodes');
nodeModule['node-red-node-testnode'].should.have.a.property('path');
nodeModule['node-red-node-testnode'].should.have.a.property('user', false);
checkNodes(nodeModule['node-red-node-testnode'].nodes,['testnode'],[],'node-red-node-testnode');
done();
});
it("Finds 2 node-red modules and 1 plugin in nodesDir (in root of dir)",function(done) {
stubPathJoin()
localfilesystem.init({nodesDir:[nodesDir2]});
const nodeModule = localfilesystem.getModuleFiles();
const loaded = Object.keys(nodeModule)
loaded.should.have.a.property("length", 3)
loaded.indexOf('@test/testnode').should.greaterThan(-1, "Should load @test/testnode")
loaded.indexOf('testnode2').should.greaterThan(-1, "Should load testnode2")
loaded.indexOf('test-theme2').should.greaterThan(-1, "Should load test-theme2")
nodeModule['@test/testnode'].should.have.a.property('name','@test/testnode');
nodeModule['@test/testnode'].should.have.a.property('version','1.0.0');
nodeModule['@test/testnode'].should.have.a.property('nodes');
nodeModule['@test/testnode'].should.have.a.property('path');
nodeModule['@test/testnode'].should.have.a.property('user', false);
nodeModule['testnode2'].should.have.a.property('name','testnode2');
nodeModule['testnode2'].should.have.a.property('version','1.0.0');
nodeModule['testnode2'].should.have.a.property('nodes');
nodeModule['testnode2'].should.have.a.property('path');
nodeModule['testnode2'].should.have.a.property('user', false);
nodeModule['test-theme2'].should.have.a.property('name','test-theme2');
nodeModule['test-theme2'].should.have.a.property('version','0.0.1');
nodeModule['test-theme2'].should.have.a.property('nodes', {});
nodeModule['test-theme2'].should.have.a.property('path');
nodeModule['test-theme2'].should.have.a.property('user', false);
nodeModule['test-theme2'].should.have.a.property('plugins');
nodeModule['test-theme2'].plugins.should.have.a.property('test-theme2');
nodeModule['test-theme2'].plugins['test-theme2'].should.have.a.property('name','test-theme2');
nodeModule['test-theme2'].plugins['test-theme2'].should.have.a.property('module','test-theme2');
nodeModule['test-theme2'].plugins['test-theme2'].should.have.a.property('version', '0.0.1');
nodeModule['test-theme2'].plugins['test-theme2'].should.have.a.property('file');
nodeModule['test-theme2'].plugins['test-theme2'].should.have.a.property('local', false);
done();
});
it("Finds 2 node-red modules and 1 plugin in nodesDir pointing to a node_modules dir",function(done) {
stubPathJoin()
localfilesystem.init({nodesDir:[path.join(nodesDir3, "node_modules")]});
const nodeModule = localfilesystem.getModuleFiles();
const loaded = Object.keys(nodeModule)
loaded.should.have.a.property("length", 3)
loaded.indexOf('@test/testnode').should.greaterThan(-1, "Should load @test/testnode")
loaded.indexOf('@test/test-theme3').should.greaterThan(-1, "Should load test-theme3")
loaded.indexOf('testnode3').should.greaterThan(-1, "Should load testnode3")
done();
});
it("throws an error if a node isn't found",function(done) {
var _join = path.join;
stubs.push(sinon.stub(path,"join").callsFake(function() {
if (arguments[0] == resourcesDir) {
// This stops the module tree scan from going any higher
// up the tree than resourcesDir.
return arguments[0];
}
return _join.apply(null,arguments);
}));
stubPathJoin()
localfilesystem.init({coreNodesDir:moduleDir});
/*jshint immed: false */
(function(){
@ -250,15 +386,7 @@ describe("red/nodes/registry/localfilesystem",function() {
it.skip("finds locales directory");
it.skip("finds icon path directory");
it("scans icon files with a module file",function(done) {
var _join = path.join;
stubs.push(sinon.stub(path,"join").callsFake(function() {
if (arguments[0] == resourcesDir) {
// This stops the module tree scan from going any higher
// up the tree than resourcesDir.
return arguments[0];
}
return _join.apply(null,arguments);
}));
stubPathJoin()
localfilesystem.init({
coreNodesDir: moduleDir
});

View File

@ -0,0 +1 @@
<svg width="40" height="60" viewBox="0, 0, 40, 60" xmlns="http://www.w3.org/2000/svg"><path d="M10.004 14.499h20M10.004 46.503h20M10.004 22.5h20M10.004 30.501h20M10.004 38.502h20" stroke="#fff" stroke-width="2.9997000000000003"/></svg>

After

Width:  |  Height:  |  Size: 236 B

View File

@ -0,0 +1,5 @@
<script>
(function() {
console.log("hello from loose1.html")
})()
</script>

View File

@ -0,0 +1,4 @@
(function() {
console.log("hello from loose1.js")
})()

View File

@ -0,0 +1 @@
<svg width="40" height="60" viewBox="0, 0, 40, 60" xmlns="http://www.w3.org/2000/svg"><path d="M30.999 31.005v-3h-6.762s.812-12.397 1.162-14 .597-3.35 2.628-3.103 1.971 3.103 1.971 3.103l4.862-.016s-.783-3.984-2.783-5.984-7.946-1.7-9.633.03c-1.687 1.73-2.302 5.065-2.597 6.422-.588 4.5-.854 9.027-1.248 13.547h-8.6v3H18.1s-.812 12.398-1.162 14-.597 3.35-2.628 3.103-1.972-3.102-1.972-3.102l-4.862.015s.783 3.985 2.783 5.985c2 2 7.946 1.699 9.634-.031 1.687-1.73 2.302-5.065 2.597-6.422.587-4.5.854-9.027 1.248-13.547z" fill="#fff"/></svg>

After

Width:  |  Height:  |  Size: 539 B

View File

@ -0,0 +1 @@
<svg width="40" height="60" viewBox="0, 0, 40, 60" xmlns="http://www.w3.org/2000/svg"><path d="M7 38.98v3.983h11v12l13-23H19l-.463.017c-1.28 4.048-5.066 6.983-9.537 6.983zm12-11.017h12l-13-23v12H7V20.9l2 .064c4.467 0 8.25 2.93 9.534 6.972zM6.95 24.22a6 6 0 1 1-.083 11.456" fill="#fff" style="isolation:auto;mix-blend-mode:normal"/></svg>

After

Width:  |  Height:  |  Size: 339 B

View File

@ -0,0 +1,5 @@
<script>
(function() {
console.log("hello from loose2.html")
})()
</script>

View File

@ -0,0 +1,4 @@
(function() {
console.log("hello from loose2.js")
})()

View File

@ -0,0 +1 @@
<svg width="40" height="60" viewBox="0, 0, 40, 60" xmlns="http://www.w3.org/2000/svg"><path d="M30.999 31.005v-3h-6.762s.812-12.397 1.162-14 .597-3.35 2.628-3.103 1.971 3.103 1.971 3.103l4.862-.016s-.783-3.984-2.783-5.984-7.946-1.7-9.633.03c-1.687 1.73-2.302 5.065-2.597 6.422-.588 4.5-.854 9.027-1.248 13.547h-8.6v3H18.1s-.812 12.398-1.162 14-.597 3.35-2.628 3.103-1.972-3.102-1.972-3.102l-4.862.015s.783 3.985 2.783 5.985c2 2 7.946 1.699 9.634-.031 1.687-1.73 2.302-5.065 2.597-6.422.587-4.5.854-9.027 1.248-13.547z" fill="#fff"/></svg>

After

Width:  |  Height:  |  Size: 539 B

View File

@ -0,0 +1,4 @@
(function() {
console.log("hello from regular module main.js")
})()

View File

@ -0,0 +1,19 @@
{
"name": "node-red-node-testnode",
"version": "1.0.0",
"description": "A node-red node that does nothing other than exist",
"main": "main.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [
"node-red"
],
"node-red": {
"nodes": {
"testnode": "index.js"
}
},
"author": "@testyMcTersterson",
"license": "MIT"
}

View File

@ -0,0 +1 @@
<svg width="40" height="60" viewBox="0, 0, 40, 60" xmlns="http://www.w3.org/2000/svg"><path d="M30.999 31.005v-3h-6.762s.812-12.397 1.162-14 .597-3.35 2.628-3.103 1.971 3.103 1.971 3.103l4.862-.016s-.783-3.984-2.783-5.984-7.946-1.7-9.633.03c-1.687 1.73-2.302 5.065-2.597 6.422-.588 4.5-.854 9.027-1.248 13.547h-8.6v3H18.1s-.812 12.398-1.162 14-.597 3.35-2.628 3.103-1.972-3.102-1.972-3.102l-4.862.015s.783 3.985 2.783 5.985c2 2 7.946 1.699 9.634-.031 1.687-1.73 2.302-5.065 2.597-6.422.587-4.5.854-9.027 1.248-13.547z" fill="#fff"/></svg>

After

Width:  |  Height:  |  Size: 539 B

View File

@ -0,0 +1,4 @@
(function() {
console.log("hello from regular module main.js")
})()

View File

@ -0,0 +1,14 @@
{
"name": "regular_node",
"version": "1.0.0",
"description": "A regular node that does nothing other than exist",
"main": "main.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [
"test"
],
"author": "@testyMcTersterson",
"license": "MIT"
}

View File

@ -0,0 +1,5 @@
<script>
(function() {
console.log("hello from @test/testnode index.html")
})()
</script>

View File

@ -0,0 +1,4 @@
(function() {
console.log("hello from @test/testnode index.js")
})()

View File

@ -0,0 +1,20 @@
{
"name": "@test/testnode",
"version": "1.0.0",
"description": "A test node that does nothing other than exist",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [
"node-red",
"test"
],
"node-red": {
"nodes": {
"testnode": "index.js"
}
},
"author": "@testyMcTersterson",
"license": "MIT"
}

View File

@ -0,0 +1,5 @@
<script>
(function() {
console.log("hello from testnode2 index.js")
})()
</script>

View File

@ -0,0 +1,4 @@
(function() {
console.log("hello from testnode2 index.js")
})()

View File

@ -0,0 +1,20 @@
{
"name": "testnode2",
"version": "1.0.0",
"description": "A test node that does nothing other than exist",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [
"node-red",
"test"
],
"node-red": {
"nodes": {
"testnode2": "index.js"
}
},
"author": "@testyMcTersterson",
"license": "MIT"
}

View File

@ -0,0 +1,3 @@
(function() {
console.log("Hi from test plugin client side")
})()

View File

@ -0,0 +1,14 @@
module.exports = function (RED) {
RED.plugins.registerPlugin('test-theme', {
type: 'node-red-theme',
scripts: [
'files/clientside.js'
],
css: [
'files/theme.css',
],
monacoOptions: {
theme: "vs"
}
})
}

View File

@ -0,0 +1 @@
:root{--red-ui-primary-background: #f2f3fb;}

View File

@ -0,0 +1,24 @@
{
"name": "test-theme2",
"version": "0.0.1",
"description": "test theme for Node-RED",
"keywords": [
"node-red",
"plugin",
"theme"
],
"author": {
"name": "testy-McTesterson"
},
"license": "MIT",
"node-red": {
"version": ">=2.2.0",
"plugins": {
"test-theme2": "files/plugin.js"
}
},
"engines": {
"node": ">=12.x"
}
}

View File

@ -0,0 +1,5 @@
<script>
(function() {
console.log("hello from @test/testnode index.html")
})()
</script>

View File

@ -0,0 +1,4 @@
(function() {
console.log("hello from @test/testnode index.js")
})()

View File

@ -0,0 +1,20 @@
{
"name": "@test/testnode",
"version": "1.0.0",
"description": "A test node that does nothing other than exist",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [
"node-red",
"test"
],
"node-red": {
"nodes": {
"testnode": "index.js"
}
},
"author": "@testyMcTersterson",
"license": "MIT"
}

View File

@ -0,0 +1,3 @@
(function() {
console.log("Hi from test plugin client side")
})()

View File

@ -0,0 +1,14 @@
module.exports = function (RED) {
RED.plugins.registerPlugin('test-theme', {
type: 'node-red-theme',
scripts: [
'files/clientside.js'
],
css: [
'files/theme.css',
],
monacoOptions: {
theme: "vs"
}
})
}

View File

@ -0,0 +1 @@
:root{--red-ui-primary-background: #f2f3fb;}

View File

@ -0,0 +1,24 @@
{
"name": "@test/test-theme3",
"version": "0.0.1",
"description": "test theme for Node-RED",
"keywords": [
"node-red",
"plugin",
"theme"
],
"author": {
"name": "testy-McTesterson"
},
"license": "MIT",
"node-red": {
"version": ">=2.2.0",
"plugins": {
"test-theme3": "files/plugin.js"
}
},
"engines": {
"node": ">=12.x"
}
}

View File

@ -0,0 +1,5 @@
<script>
(function() {
console.log("hello from testnode3 index.js")
})()
</script>

View File

@ -0,0 +1,4 @@
(function() {
console.log("hello from testnode3 index.js")
})()

View File

@ -0,0 +1,20 @@
{
"name": "testnode3",
"version": "1.0.0",
"description": "A test node that does nothing other than exist",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [
"node-red",
"test"
],
"node-red": {
"nodes": {
"testnode3": "index.js"
}
},
"author": "@testyMcTersterson",
"license": "MIT"
}