@node-red/registry 3.0.0-beta.2 → 3.0.0

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.
@@ -11,6 +11,7 @@ const exec = require("@node-red/util").exec;
11
11
  const log = require("@node-red/util").log;
12
12
  const hooks = require("@node-red/util").hooks;
13
13
  const url = require("url");
14
+ const { createRequire } = require("module");
14
15
 
15
16
  const BUILTIN_MODULES = require('module').builtinModules;
16
17
 
@@ -139,10 +140,15 @@ function importModule(module) {
139
140
  }
140
141
  const externalModuleDir = getInstallDir();
141
142
  const moduleDir = path.join(externalModuleDir,"node_modules",module);
143
+ // To handle both CJS and ESM we need to resolve the module to the
144
+ // specific file that is loaded when the module is required/imported
145
+ // As this won't be on the natural module search path, we use createRequire
146
+ // to access the module
147
+ const modulePath = createRequire(moduleDir).resolve(module)
142
148
  // Import needs the full path to the module's main .js file
143
149
  // It also needs to be a file:// url for Windows
144
- const moduleFile = url.pathToFileURL(require.resolve(moduleDir));
145
- return import(moduleFile);
150
+ const moduleUrl = url.pathToFileURL(modulePath);
151
+ return import(moduleUrl);
146
152
  }
147
153
 
148
154
  function parseModuleName(module) {
package/lib/loader.js CHANGED
@@ -359,6 +359,7 @@ function loadNodeSet(node) {
359
359
  try {
360
360
  var loadPromise = null;
361
361
  var r = require(node.file);
362
+ r = r.__esModule ? r.default : r
362
363
  if (typeof r === "function") {
363
364
 
364
365
  var red = registryUtil.createNodeApi(node);
@@ -88,9 +88,10 @@ function getLocalFile(file) {
88
88
  /**
89
89
  * Synchronously walks the directory looking for node files.
90
90
  * @param dir the directory to search
91
+ * @param skipValidNodeRedModules a flag to skip lading icons & files if the directory a valid node-red module
91
92
  * @return an array of fully-qualified paths to .js files
92
93
  */
93
- function getLocalNodeFiles(dir) {
94
+ function getLocalNodeFiles(dir, skipValidNodeRedModules) {
94
95
  dir = path.resolve(dir);
95
96
 
96
97
  var result = [];
@@ -102,6 +103,14 @@ function getLocalNodeFiles(dir) {
102
103
  return {files: [], icons: []};
103
104
  }
104
105
  files.sort();
106
+ // when loading local files, if the path is a valid node-red module
107
+ // dont include it (will be picked up in scanTreeForNodesModules)
108
+ if(skipValidNodeRedModules && files.indexOf("package.json") >= 0) {
109
+ const package = getPackageDetails(dir)
110
+ if(package.isNodeRedModule) {
111
+ return {files: [], icons: []};
112
+ }
113
+ }
105
114
  files.forEach(function(fn) {
106
115
  var stats = fs.statSync(path.join(dir,fn));
107
116
  if (stats.isFile()) {
@@ -114,7 +123,7 @@ function getLocalNodeFiles(dir) {
114
123
  } else if (stats.isDirectory()) {
115
124
  // Ignore /.dirs/, /lib/ /node_modules/
116
125
  if (!/^(\..*|lib|icons|node_modules|test|locales)$/.test(fn)) {
117
- var subDirResults = getLocalNodeFiles(path.join(dir,fn));
126
+ var subDirResults = getLocalNodeFiles(path.join(dir,fn), skipValidNodeRedModules);
118
127
  result = result.concat(subDirResults.files);
119
128
  icons = icons.concat(subDirResults.icons);
120
129
  } else if (fn === "icons") {
@@ -126,21 +135,30 @@ function getLocalNodeFiles(dir) {
126
135
  return {files: result, icons: icons}
127
136
  }
128
137
 
129
- function scanDirForNodesModules(dir,moduleName) {
130
- var results = [];
131
- var scopeName;
138
+ function scanDirForNodesModules(dir,moduleName,package) {
139
+ let results = [];
140
+ let scopeName;
141
+ let files
132
142
  try {
133
- var files = fs.readdirSync(dir);
134
- if (moduleName) {
135
- var m = /^(?:(@[^/]+)[/])?([^@/]+)/.exec(moduleName);
136
- if (m) {
137
- scopeName = m[1];
138
- moduleName = m[2];
143
+ let isNodeRedModule = false
144
+ if(package) {
145
+ dir = path.join(package.moduleDir,'..')
146
+ files = [path.basename(package.moduleDir)]
147
+ moduleName = (package.package ? package.package.name : null) || moduleName
148
+ isNodeRedModule = package.isNodeRedModule
149
+ } else {
150
+ files = fs.readdirSync(dir);
151
+ if (moduleName) {
152
+ var m = /^(?:(@[^/]+)[/])?([^@/]+)/.exec(moduleName);
153
+ if (m) {
154
+ scopeName = m[1];
155
+ moduleName = m[2];
156
+ }
139
157
  }
140
158
  }
141
- for (var i=0;i<files.length;i++) {
142
- var fn = files[i];
143
- if (/^@/.test(fn)) {
159
+ for (let i=0;i<files.length;i++) {
160
+ let fn = files[i];
161
+ if (!isNodeRedModule && /^@/.test(fn)) {
144
162
  if (scopeName && scopeName === fn) {
145
163
  // Looking for a specific scope/module
146
164
  results = results.concat(scanDirForNodesModules(path.join(dir,fn),moduleName));
@@ -149,16 +167,18 @@ function scanDirForNodesModules(dir,moduleName) {
149
167
  results = results.concat(scanDirForNodesModules(path.join(dir,fn),moduleName));
150
168
  }
151
169
  } else {
152
- if (isIncluded(fn) && !isExcluded(fn) && (!moduleName || fn == moduleName)) {
153
- var pkgfn = path.join(dir,fn,"package.json");
170
+ if ((isNodeRedModule || (!moduleName || fn == moduleName)) && (isIncluded(fn) && !isExcluded(fn))) {
154
171
  try {
155
- var pkg = require(pkgfn);
156
- if (pkg['node-red']) {
157
- if (!registryUtil.checkModuleAllowed(pkg.name,pkg.version,loadAllowList,loadDenyList)) {
158
- log.debug("! Module: "+pkg.name+" "+pkg.version+ " *ignored due to denyList*");
172
+ const moduleDir = isNodeRedModule ? package.moduleDir : path.join(dir,fn);
173
+ const pkg = package || getPackageDetails(moduleDir)
174
+ if(pkg.error) {
175
+ throw pkg.error
176
+ }
177
+ if (pkg.isNodeRedModule) {
178
+ if (!pkg.allowed) {
179
+ log.debug("! Module: "+pkg.package.name+" "+pkg.package.version+ " *ignored due to denyList*");
159
180
  } else {
160
- var moduleDir = path.join(dir,fn);
161
- results.push({dir:moduleDir,package:pkg});
181
+ results.push({dir:moduleDir,package:pkg.package});
162
182
  }
163
183
  }
164
184
  } catch(err) {
@@ -182,11 +202,14 @@ function scanDirForNodesModules(dir,moduleName) {
182
202
  * @param moduleName the name of the module to be found
183
203
  * @return a list of node modules: {dir,package}
184
204
  */
185
- function scanTreeForNodesModules(moduleName) {
186
- var dir = settings.coreNodesDir;
187
- var results = [];
188
- var userDir;
189
-
205
+ function scanTreeForNodesModules(moduleName) {
206
+ let coreNodesDir = settings.coreNodesDir;
207
+ let results = [];
208
+ let userDir;
209
+ let nodesDir;
210
+ if(settings.nodesDir) {
211
+ nodesDir = Array.isArray(settings.nodesDir) ? settings.nodesDir : [settings.nodesDir]
212
+ }
190
213
  if (settings.userDir) {
191
214
  packageList = getPackageList();
192
215
  userDir = path.join(settings.userDir,"node_modules");
@@ -201,15 +224,46 @@ function scanTreeForNodesModules(moduleName) {
201
224
  });
202
225
  }
203
226
 
204
- if (dir) {
205
- var up = path.resolve(path.join(dir,".."));
206
- while (up !== dir) {
207
- var pm = path.join(dir,"node_modules");
227
+ if (coreNodesDir) {
228
+ var up = path.resolve(path.join(coreNodesDir,".."));
229
+ while (up !== coreNodesDir) {
230
+ var pm = path.join(coreNodesDir,"node_modules");
208
231
  if (pm != userDir) {
209
232
  results = results.concat(scanDirForNodesModules(pm,moduleName));
210
233
  }
211
- dir = up;
212
- up = path.resolve(path.join(dir,".."));
234
+ coreNodesDir = up;
235
+ up = path.resolve(path.join(coreNodesDir,".."));
236
+ }
237
+ }
238
+
239
+ // scan nodesDir for any node-red modules
240
+ /*
241
+ 1. if !exist(package.json) || !package.json.has(node-red) => look for node_modules
242
+ 2. exist(package.json) && package.json.has(node-red) => load this only
243
+ 3. in original scan of nodesDir, ignore if:(exist(package.json) && package.json.has(node-red))
244
+ */
245
+ if (nodesDir) {
246
+ for (let dirIndex = 0; dirIndex < nodesDir.length; dirIndex++) {
247
+ const nodeDir = nodesDir[dirIndex];
248
+ const packageDetails = getPackageDetails(nodeDir)
249
+ if(packageDetails.isNodeRedModule) {
250
+ //we have found a node-red module, scan it
251
+ const nrModules = scanDirForNodesModules(nodeDir, packageDetails.package.name, packageDetails);
252
+ results = results.concat(nrModules);
253
+
254
+ } else if (packageDetails.has_node_modules) {
255
+ //If this dir has a `node_modues` dir, scan it
256
+ const nodeModulesDir = path.join(nodeDir, 'node_modules')
257
+ const nrModules = scanDirForNodesModules(nodeModulesDir, moduleName );
258
+ results = results.concat(nrModules);
259
+
260
+ } else {
261
+ //If this is not a node-red module AND it does NOT have a node_modules dir,
262
+ //it may be a directory of project directories or a node_modules dir?
263
+ //scan this instead
264
+ const nrModules = scanDirForNodesModules(nodeDir, moduleName);
265
+ results = results.concat(nrModules);
266
+ }
213
267
  }
214
268
  }
215
269
  return results;
@@ -274,24 +328,26 @@ function getModuleNodeFiles(module) {
274
328
  }
275
329
 
276
330
  function getNodeFiles(disableNodePathScan) {
277
- var dir;
278
331
  // Find all of the nodes to load
279
- var nodeFiles = [];
280
- var results;
281
-
282
- var dir;
283
- var iconList = [];
332
+ let results;
333
+ let nodesDir;
334
+ if(settings.nodesDir) {
335
+ nodesDir = Array.isArray(settings.nodesDir) ? settings.nodesDir : [settings.nodesDir]
336
+ }
337
+ let dir;
338
+ let nodeFiles = [];
339
+ let iconList = [];
284
340
  if (settings.coreNodesDir) {
285
341
  results = getLocalNodeFiles(path.resolve(settings.coreNodesDir));
286
342
  nodeFiles = nodeFiles.concat(results.files);
287
343
  iconList = iconList.concat(results.icons);
288
- var defaultLocalesPath = path.join(settings.coreNodesDir,"locales");
344
+ let defaultLocalesPath = path.join(settings.coreNodesDir,"locales");
289
345
  i18n.registerMessageCatalog("node-red",defaultLocalesPath,"messages.json");
290
346
  }
291
347
 
292
348
  if (settings.userDir) {
293
349
  dir = path.join(settings.userDir,"lib","icons");
294
- var icons = scanIconDir(dir);
350
+ let icons = scanIconDir(dir);
295
351
  if (icons.length > 0) {
296
352
  iconList.push({path:dir,icons:icons});
297
353
  }
@@ -301,13 +357,9 @@ function getNodeFiles(disableNodePathScan) {
301
357
  nodeFiles = nodeFiles.concat(results.files);
302
358
  iconList = iconList.concat(results.icons);
303
359
  }
304
- if (settings.nodesDir) {
305
- dir = settings.nodesDir;
306
- if (typeof settings.nodesDir == "string") {
307
- dir = [dir];
308
- }
309
- for (var i=0;i<dir.length;i++) {
310
- results = getLocalNodeFiles(dir[i]);
360
+ if (nodesDir) {
361
+ for (let i = 0; i < nodesDir.length; i++) {
362
+ results = getLocalNodeFiles(nodesDir[i], true);
311
363
  nodeFiles = nodeFiles.concat(results.files);
312
364
  iconList = iconList.concat(results.icons);
313
365
  }
@@ -479,7 +531,52 @@ function getPackageList() {
479
531
  }
480
532
  return list;
481
533
  }
482
-
534
+ /**
535
+ * Gets the package json object for the supplied `dir`.
536
+ * If there is no package.json or the `node-red` section is missing, `result.isNodeRedModule` will be `false`.
537
+ * If there is no package.json `isPackage` will be `false`.
538
+ * If an error occurs, `result.error` will contain the error.
539
+ * @param {string} dir The directory to inspect
540
+ */
541
+ function getPackageDetails(dir) {
542
+ const result = {
543
+ /** @type {string} The package directory */
544
+ moduleDir: dir,
545
+ /** @type {string} The full file path of package.json for this package */
546
+ packageFile: null,
547
+ /** @type {boolean} True if this is a valid node-red module */
548
+ isNodeRedModule: false,
549
+ /** @type {boolean} True if a package.json file is present */
550
+ isPackage: false,
551
+ /** @type {boolean} True if this a node-red module and passes the checks */
552
+ allowed: false,
553
+ /** @type {object} The contents of package.json */
554
+ package: null,
555
+ }
556
+ if (!dir) { return result }
557
+ try {
558
+ const packagefile = path.join(dir,'package.json')
559
+ result.has_node_modules = fs.existsSync(path.join(dir,'node_modules'))
560
+ if(!fs.existsSync(packagefile)) {
561
+ return result
562
+ }
563
+ result.packageFile = packagefile
564
+ const pkg = require(packagefile)
565
+ result.package = pkg
566
+ if(result.package) {
567
+ result.allowed = true
568
+ result.isPackage = true
569
+ result.isNodeRedModule = typeof result.package['node-red'] === 'object'
570
+ if(result.isNodeRedModule) {
571
+ result.isNodeRedModule = true;
572
+ result.allowed = registryUtil.checkModuleAllowed(pkg.name,pkg.version,loadAllowList,loadDenyList)
573
+ }
574
+ }
575
+ } catch(err) {
576
+ result.error = err; // this is not a package we are interested in!
577
+ }
578
+ return result || result;
579
+ }
483
580
  module.exports = {
484
581
  init: init,
485
582
  getNodeFiles: getNodeFiles,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@node-red/registry",
3
- "version": "3.0.0-beta.2",
3
+ "version": "3.0.0",
4
4
  "license": "Apache-2.0",
5
5
  "main": "./lib/index.js",
6
6
  "repository": {
@@ -16,11 +16,11 @@
16
16
  }
17
17
  ],
18
18
  "dependencies": {
19
- "@node-red/util": "3.0.0-beta.2",
19
+ "@node-red/util": "3.0.0",
20
20
  "clone": "2.1.2",
21
21
  "fs-extra": "10.1.0",
22
22
  "semver": "7.3.7",
23
23
  "tar": "6.1.11",
24
- "uglify-js": "3.15.5"
24
+ "uglify-js": "3.16.2"
25
25
  }
26
26
  }