node-pluginsmanager 3.5.0 → 3.6.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.
@@ -31,6 +31,7 @@ export default class PluginsManager extends EventEmitter {
31
31
  beforeInitAll(callback: tBeforeAllMethodCallback): Promise<void>;
32
32
  initAll(...data: unknown[]): Promise<void>;
33
33
  releaseAll(...data: unknown[]): Promise<void>;
34
+ getLatestGithubTag(user: string, repo: string): Promise<string>;
34
35
  installViaGithub(user: string, repo: string, ...data: unknown[]): Promise<Orchestrator>;
35
36
  updateViaGithub(plugin: Orchestrator, ...data: unknown[]): Promise<Orchestrator>;
36
37
  uninstall(plugin: Orchestrator, ...data: unknown[]): Promise<string>;
@@ -12,6 +12,7 @@ const promises_1 = require("node:fs/promises");
12
12
  // externals
13
13
  const check_version_modules_1 = __importDefault(require("check-version-modules"));
14
14
  const check_node_engine_1 = __importDefault(require("check-node-engine"));
15
+ const semver_1 = __importDefault(require("semver"));
15
16
  // locals
16
17
  const rmdirp_1 = __importDefault(require("./utils/rmdirp"));
17
18
  const checkAbsoluteDirectory_1 = __importDefault(require("./checkers/checkAbsoluteDirectory"));
@@ -24,7 +25,9 @@ const isFile_1 = __importDefault(require("./utils/isFile"));
24
25
  const createPluginByDirectory_1 = __importDefault(require("./createPluginByDirectory"));
25
26
  const loadSortedPlugins_1 = __importDefault(require("./loadSortedPlugins"));
26
27
  const initSortedPlugins_1 = __importDefault(require("./initSortedPlugins"));
27
- const extractGithub_1 = __importDefault(require("./extractGithub"));
28
+ const extractGithub_1 = __importDefault(require("./utils/extractGithub"));
29
+ const getLatestGithubTag_1 = __importDefault(require("./utils/getLatestGithubTag"));
30
+ const parseGithubUserRepo_1 = __importDefault(require("./utils/parseGithubUserRepo"));
28
31
  // git
29
32
  const gitInstall_1 = __importDefault(require("./cmd/git/gitInstall"));
30
33
  const gitUpdate_1 = __importDefault(require("./cmd/git/gitUpdate"));
@@ -267,6 +270,11 @@ class PluginsManager extends node_events_1.default {
267
270
  this.emit("allreleased", ...data);
268
271
  });
269
272
  }
273
+ getLatestGithubTag(user, repo) {
274
+ return (0, getLatestGithubTag_1.default)(user, repo).then((tag) => {
275
+ return tag.name;
276
+ });
277
+ }
270
278
  // install a plugin via github repo, using "data" in arguments for "install" and "init" plugin's Orchestrator methods
271
279
  installViaGithub(user, repo, ...data) {
272
280
  return (0, checkAbsoluteDirectory_1.default)("installViaGithub/directory", this.directory).then(() => {
@@ -285,7 +293,10 @@ class PluginsManager extends node_events_1.default {
285
293
  }).then((directory) => {
286
294
  // download plugin
287
295
  return Promise.resolve().then(() => {
288
- return (0, gitInstall_1.default)(directory, user, repo);
296
+ this._logger?.("info", "Downloading plugin...", false, repo);
297
+ return (0, gitInstall_1.default)(directory, user, repo).then(() => {
298
+ this._logger?.("success", "Download success", false, repo);
299
+ });
289
300
  // check if plugin directory is created
290
301
  }).then(() => {
291
302
  return (0, isDirectory_1.default)(directory).then((isPluginADirectory) => {
@@ -307,6 +318,11 @@ class PluginsManager extends node_events_1.default {
307
318
  return JSON.parse(content);
308
319
  });
309
320
  }).then((packageData) => {
321
+ // check if the plugin has a valid name
322
+ if ("string" !== typeof packageData.name || "" === packageData.name.trim()) {
323
+ throw new Error("\"" + repo + "\" plugin has no valid name");
324
+ }
325
+ const pluginName = packageData.name;
310
326
  // check if the plugin has a valid entry point
311
327
  if ("string" !== typeof packageData.main || "" === packageData.main.trim()) {
312
328
  throw new Error("\"" + repo + "\" plugin has no valid entry point");
@@ -326,18 +342,27 @@ class PluginsManager extends node_events_1.default {
326
342
  if ("string" !== typeof scripts.build || "" === scripts.build.trim()) {
327
343
  throw new Error("\"" + repo + "\" plugin has no build script registered");
328
344
  }
345
+ this._logger?.("info", "Installing dev dependencies...", false, pluginName);
329
346
  // install plugin with dependencies
330
347
  return (0, npmInstall_1.default)(directory, true).then(() => {
348
+ this._logger?.("success", "Plugin installed successfully", false, pluginName);
349
+ this._logger?.("info", "Building plugin...", false, pluginName);
331
350
  return (0, npmBuild_1.default)(directory);
332
351
  // remove dev dependencies
333
352
  }).then(() => {
334
- return (0, rmdirp_1.default)((0, node_path_1.join)(directory, "node_modules"));
353
+ this._logger?.("debug", "Removing dev dependencies...", false, pluginName);
354
+ return (0, rmdirp_1.default)((0, node_path_1.join)(directory, "node_modules")).then(() => {
355
+ this._logger?.("success", "Dev dependencies removed successfully", false, pluginName);
356
+ });
335
357
  });
336
358
  }).then(() => {
337
359
  if ("object" !== typeof packageData.dependencies || null === packageData.dependencies || 0 >= Object.keys(packageData.dependencies).length) {
338
360
  return Promise.resolve();
339
361
  }
340
- return (0, npmInstall_1.default)(directory);
362
+ this._logger?.("debug", "Installing dependencies...", false, pluginName);
363
+ return (0, npmInstall_1.default)(directory).then(() => {
364
+ this._logger?.("success", "Dependencies installed successfully", false, pluginName);
365
+ });
341
366
  });
342
367
  });
343
368
  // create plugin
@@ -383,24 +408,52 @@ class PluginsManager extends node_events_1.default {
383
408
  updateViaGithub(plugin, ...data) {
384
409
  let directory = "";
385
410
  let key = -1;
411
+ let githubUrl = "";
412
+ let latestTag = "";
386
413
  // check plugin
387
- return Promise.resolve().then(() => {
388
- return (0, checkOrchestrator_1.default)("updateViaGithub/plugin", plugin).then(() => {
389
- return (0, checkNonEmptyString_1.default)("updateViaGithub/github", (0, extractGithub_1.default)(plugin)).catch(() => {
390
- return Promise.reject(new ReferenceError("Plugin \"" + plugin.name + "\" must be linked in the package to a github project to be updated"));
391
- });
414
+ return (0, checkOrchestrator_1.default)("updateViaGithub/plugin", plugin).then(() => {
415
+ const github = (0, extractGithub_1.default)(plugin);
416
+ return (0, checkNonEmptyString_1.default)("updateViaGithub/github", github).catch(() => {
417
+ return Promise.reject(new ReferenceError("Plugin \"" + plugin.name + "\" must be linked in the package to a github project to be updated"));
392
418
  }).then(() => {
393
- key = this.getPluginsNames().findIndex((pluginName) => {
394
- return pluginName === plugin.name;
395
- });
396
- return -1 < key ? Promise.resolve() : Promise.reject(new Error("Plugin \"" + plugin.name + "\" is not registered"));
419
+ githubUrl = github;
397
420
  });
421
+ }).then(() => {
422
+ key = this.getPluginsNames().findIndex((pluginName) => {
423
+ return pluginName === plugin.name;
424
+ });
425
+ if (-1 >= key) {
426
+ throw new Error("Plugin \"" + plugin.name + "\" is not registered");
427
+ }
398
428
  // check plugin directory
399
429
  }).then(() => {
400
430
  return (0, checkAbsoluteDirectory_1.default)("updateViaGithub/directory", this.directory).then(() => {
401
431
  directory = (0, node_path_1.join)(this.directory, plugin.name);
402
432
  return (0, checkAbsoluteDirectory_1.default)("updateViaGithub/plugindirectory", directory);
403
433
  });
434
+ // check remote version
435
+ }).then(() => {
436
+ const githubUserRepo = (0, parseGithubUserRepo_1.default)(githubUrl);
437
+ if (!githubUserRepo) {
438
+ throw new Error("Plugin \"" + plugin.name + "\" has an invalid github project link");
439
+ }
440
+ const currentVersion = semver_1.default.coerce(plugin.version);
441
+ if (!currentVersion) {
442
+ throw new Error("Plugin \"" + plugin.name + "\" has no valid version (\"" + plugin.version + "\")");
443
+ }
444
+ this._logger?.("debug", "Get github latest tag", false, plugin.name);
445
+ return (0, getLatestGithubTag_1.default)(githubUserRepo.user, githubUserRepo.repo).then((tag) => {
446
+ const latestVersion = semver_1.default.coerce(tag.name);
447
+ if (!latestVersion) {
448
+ throw new Error("No valid version found for plugin \"" + plugin.name + "\" on github");
449
+ }
450
+ this._logger?.("debug", "Compare local version with github lastest tag", false, plugin.name);
451
+ if (!semver_1.default.gt(latestVersion, currentVersion)) {
452
+ throw new Error("Plugin \"" + plugin.name + "\" is already up to date (v" + plugin.version + ")");
453
+ }
454
+ latestTag = tag.name;
455
+ this._logger?.("success", "New version available", false, plugin.name);
456
+ });
404
457
  // release plugin
405
458
  }).then(() => {
406
459
  const pluginName = plugin.name;
@@ -411,15 +464,17 @@ class PluginsManager extends node_events_1.default {
411
464
  this.emit("destroyed", pluginName, ...data);
412
465
  this.plugins.splice(key, 1);
413
466
  });
414
- // download plugin
467
+ // update plugin
415
468
  }).then(() => {
416
- return (0, gitUpdate_1.default)(directory).then(() => {
469
+ this._logger?.("debug", "Update with new plugin version", false, plugin.name);
470
+ return (0, gitUpdate_1.default)(directory, latestTag).then(() => {
417
471
  return (0, createPluginByDirectory_1.default)(directory, this.externalResourcesDirectory, this._logger, ...data);
418
472
  });
419
473
  // check plugin modules versions
420
474
  }).then((_plugin) => {
475
+ this._logger?.("debug", "Check modules", false, plugin.name);
421
476
  return this.checkModules(_plugin).then(() => {
422
- return Promise.resolve(_plugin);
477
+ return _plugin;
423
478
  });
424
479
  }).then((_plugin) => {
425
480
  // update dependencies & execute update script
@@ -434,7 +489,7 @@ class PluginsManager extends node_events_1.default {
434
489
  }).then(() => {
435
490
  this.emit("initialized", _plugin, ...data);
436
491
  this.plugins[key] = _plugin;
437
- return Promise.resolve(_plugin);
492
+ return _plugin;
438
493
  });
439
494
  });
440
495
  }
@@ -9,6 +9,7 @@ exports.default = gitInstall;
9
9
  const node_path_1 = require("node:path");
10
10
  // locals
11
11
  const checkDirectory_1 = __importDefault(require("../../checkers/checkDirectory"));
12
+ const getLatestGithubTag_1 = __importDefault(require("../../utils/getLatestGithubTag"));
12
13
  const checkNonEmptyString_1 = __importDefault(require("../../checkers/checkNonEmptyString"));
13
14
  const cmd_1 = __importDefault(require("../cmd"));
14
15
  // module
@@ -26,16 +27,20 @@ function gitInstall(directory, user, repo) {
26
27
  }).then(() => {
27
28
  return (0, checkNonEmptyString_1.default)("cmd/git/install/repo", repo);
28
29
  }).then(() => {
29
- // git clone
30
- return (0, cmd_1.default)((0, node_path_1.dirname)(directory), "git", [
31
- "-c",
32
- "core.quotepath=false",
33
- "clone",
34
- "--recursive",
35
- "--depth",
36
- "1",
37
- "https://github.com/" + user + "/" + repo + "/",
38
- directory
39
- ]);
30
+ return (0, getLatestGithubTag_1.default)(user, repo).then((tag) => {
31
+ // git clone
32
+ return (0, cmd_1.default)((0, node_path_1.dirname)(directory), "git", [
33
+ "-c",
34
+ "core.quotepath=false",
35
+ "clone",
36
+ "--recursive",
37
+ "--depth",
38
+ "1",
39
+ "--branch",
40
+ tag.name,
41
+ "https://github.com/" + user + "/" + repo + "/",
42
+ directory
43
+ ]);
44
+ });
40
45
  });
41
46
  }
@@ -1 +1 @@
1
- export default function gitUpdate(directory: string): Promise<void>;
1
+ export default function gitUpdate(directory: string, tag?: string): Promise<void>;
@@ -7,11 +7,27 @@ Object.defineProperty(exports, "__esModule", { value: true });
7
7
  exports.default = gitUpdate;
8
8
  // locals
9
9
  const checkDirectory_1 = __importDefault(require("../../checkers/checkDirectory"));
10
+ const checkNonEmptyString_1 = __importDefault(require("../../checkers/checkNonEmptyString"));
10
11
  const cmd_1 = __importDefault(require("../cmd"));
11
12
  // module
12
- function gitUpdate(directory) {
13
+ function gitUpdate(directory, tag) {
13
14
  return (0, checkDirectory_1.default)("cmd/git/update/directory", directory).then(() => {
14
- // git update
15
- return (0, cmd_1.default)(directory, "git", ["pull"]);
15
+ // pull latest changes from remote repository
16
+ if ("string" !== typeof tag) {
17
+ return (0, cmd_1.default)(directory, "git", ["pull"]);
18
+ }
19
+ // OR fetch specific tag from remote repository
20
+ return (0, checkNonEmptyString_1.default)("cmd/git/update/tag", tag).then(() => {
21
+ return (0, cmd_1.default)(directory, "git", [
22
+ "fetch",
23
+ "origin",
24
+ "tag",
25
+ tag,
26
+ "--depth",
27
+ "1"
28
+ ]).then(() => {
29
+ return (0, cmd_1.default)(directory, "git", ["checkout", tag]);
30
+ });
31
+ });
16
32
  });
17
33
  }
@@ -0,0 +1,11 @@
1
+ export interface GithubTag {
2
+ "name": string;
3
+ "commit": {
4
+ "sha": string;
5
+ "url": string;
6
+ };
7
+ "tarball_url": string;
8
+ "zipball_url": string;
9
+ "node_id": string;
10
+ }
11
+ export default function getLatestGithubTag(user: string, repo: string): Promise<GithubTag>;
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+ // deps
3
+ var __importDefault = (this && this.__importDefault) || function (mod) {
4
+ return (mod && mod.__esModule) ? mod : { "default": mod };
5
+ };
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.default = getLatestGithubTag;
8
+ // locals
9
+ const checkNonEmptyString_1 = __importDefault(require("../checkers/checkNonEmptyString"));
10
+ // module
11
+ function getLatestGithubTag(user, repo) {
12
+ return (0, checkNonEmptyString_1.default)("utils/getLatestGithubTag/user", user).then(() => {
13
+ return (0, checkNonEmptyString_1.default)("utils/getLatestGithubTag/repo", repo);
14
+ }).then(() => {
15
+ const url = "https://api.github.com/repos/" + user + "/" + repo + "/tags";
16
+ const method = "GET";
17
+ return fetch(url, {
18
+ "method": method,
19
+ "headers": {
20
+ "Content-Type": "application/json"
21
+ }
22
+ }).then((res) => {
23
+ if (res.ok) {
24
+ return res.json();
25
+ }
26
+ else {
27
+ throw new Error("Failed to fetch '" + url + "': " + res.statusText);
28
+ }
29
+ }).then((data) => {
30
+ if (0 >= data.length) {
31
+ throw new Error("No tags found for '" + url + "'");
32
+ }
33
+ return data[0];
34
+ });
35
+ });
36
+ }
@@ -0,0 +1,5 @@
1
+ export interface GithubUserRepo {
2
+ "user": string;
3
+ "repo": string;
4
+ }
5
+ export default function parseGithubUserRepo(github: string): GithubUserRepo | null;
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ // types & interfaces
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.default = parseGithubUserRepo;
5
+ // module
6
+ function parseGithubUserRepo(github) {
7
+ if ("string" !== typeof github) {
8
+ return null;
9
+ }
10
+ const githubUrlPattern = /(?:github\.com[/:]|@github\.com:)([^/]+)\/([^/.]+)/i;
11
+ const userRepoPattern = /^([^/]+)\/([^/]+)$/;
12
+ let match = githubUrlPattern.exec(github);
13
+ match ??= userRepoPattern.exec(github);
14
+ if (!match) {
15
+ return null;
16
+ }
17
+ return {
18
+ "user": match[1],
19
+ "repo": match[2].replace(/\.git$/i, "")
20
+ };
21
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
 
3
3
  "name": "node-pluginsmanager",
4
- "version": "3.5.0",
4
+ "version": "3.6.0",
5
5
  "description": "A plugins manager.",
6
6
 
7
7
  "type": "commonjs",
@@ -43,23 +43,25 @@
43
43
  "/lib/cjs"
44
44
  ],
45
45
  "engines": {
46
- "node": ">=22.22.3"
46
+ "node": ">=22.23.1"
47
47
  },
48
48
 
49
49
  "dependencies": {
50
50
  "check-node-engine": "1.1.0",
51
- "check-version-modules": "2.5.0"
51
+ "check-version-modules": "2.5.0",
52
+ "semver": "7.8.5"
52
53
  },
53
54
  "devDependencies": {
54
55
  "@types/express": "5.0.6",
55
- "@types/node": "25.9.2",
56
+ "@types/node": "26.0.1",
57
+ "@types/semver": "7.7.1",
56
58
  "@types/socket.io": "3.0.2",
57
59
  "@types/ws": "8.18.1",
58
60
  "eslint-plugin-personnallinter": "git+ssh://git@github.com/Psychopoulet/eslint-plugin-personnallinter",
59
61
  "express": "5.2.1",
60
62
  "husky": "9.1.7",
61
63
  "mocha": "11.7.6",
62
- "node-pluginsmanager-plugin": "7.3.2",
64
+ "node-pluginsmanager-plugin": "7.4.0",
63
65
  "nyc": "18.0.0",
64
66
  "proxyquire": "2.1.3",
65
67
  "rimraf": "6.1.3",