@serenityjs/plugins 0.1.0-beta.10

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 ADDED
@@ -0,0 +1,2 @@
1
+ # Plugins
2
+
@@ -0,0 +1,70 @@
1
+ import { Logger } from '@serenityjs/logger';
2
+
3
+ /**
4
+ * Represents a base plugin that can be loaded into the Serenity server.
5
+ */
6
+ declare abstract class BasePlugin<T = unknown> {
7
+ /**
8
+ * The serenity instance.
9
+ */
10
+ readonly serenity: T;
11
+ /**
12
+ * The logger instance.
13
+ */
14
+ readonly logger: Logger;
15
+ /**
16
+ * Constructs a new base plugin instance.
17
+ *
18
+ * @param serenity - The serenity instance.
19
+ * @param logger - The logger instance.
20
+ */
21
+ constructor(serenity: T, logger: Logger);
22
+ /**
23
+ * Called when the plugin is started.
24
+ */
25
+ startup(): void;
26
+ /**
27
+ * Called when the plugin is stopped.
28
+ */
29
+ shutdown(): void;
30
+ }
31
+
32
+ interface Plugin {
33
+ instance: BasePlugin;
34
+ package: Record<string, unknown>;
35
+ plugin: unknown;
36
+ }
37
+ interface tsconfig {
38
+ compilerOptions: {
39
+ outDir: string;
40
+ };
41
+ }
42
+ declare class Plugins<T> {
43
+ /**
44
+ * The serenity instance.
45
+ */
46
+ protected readonly serenity: T;
47
+ /**
48
+ * The logger instance.
49
+ */
50
+ readonly logger: Logger;
51
+ /**
52
+ * The path to the plugins folder.
53
+ */
54
+ readonly path: string;
55
+ /**
56
+ * The plugins map.
57
+ */
58
+ readonly plugins: Map<string, Plugin>;
59
+ /**
60
+ * Constructs a new plugins instance.
61
+ *
62
+ * @param serenity - The serenity instance.
63
+ */
64
+ constructor(serenity: T, path: string, enabled?: boolean);
65
+ loadNew(): Promise<void>;
66
+ validatePackage(path: string, pack: Record<string, unknown>): boolean;
67
+ validateTsconfig(path: string, tsconfig: tsconfig): boolean;
68
+ }
69
+
70
+ export { BasePlugin, Plugins };
package/dist/index.js ADDED
@@ -0,0 +1,240 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.ts
31
+ var src_exports = {};
32
+ __export(src_exports, {
33
+ BasePlugin: () => BasePlugin,
34
+ Plugins: () => Plugins
35
+ });
36
+ module.exports = __toCommonJS(src_exports);
37
+
38
+ // src/base.ts
39
+ var BasePlugin = class {
40
+ /**
41
+ * The serenity instance.
42
+ */
43
+ serenity;
44
+ /**
45
+ * The logger instance.
46
+ */
47
+ logger;
48
+ /**
49
+ * Constructs a new base plugin instance.
50
+ *
51
+ * @param serenity - The serenity instance.
52
+ * @param logger - The logger instance.
53
+ */
54
+ constructor(serenity, logger) {
55
+ this.serenity = serenity;
56
+ this.logger = logger;
57
+ this.startup();
58
+ }
59
+ /**
60
+ * Called when the plugin is started.
61
+ */
62
+ startup() {
63
+ }
64
+ /**
65
+ * Called when the plugin is stopped.
66
+ */
67
+ shutdown() {
68
+ }
69
+ };
70
+
71
+ // src/plugins.ts
72
+ var import_node_child_process = require("child_process");
73
+ var import_node_fs = require("fs");
74
+ var import_node_path = require("path");
75
+ var import_node_process = __toESM(require("process"));
76
+ var import_logger = require("@serenityjs/logger");
77
+ var Plugins = class {
78
+ /**
79
+ * The serenity instance.
80
+ */
81
+ serenity;
82
+ /**
83
+ * The logger instance.
84
+ */
85
+ logger;
86
+ /**
87
+ * The path to the plugins folder.
88
+ */
89
+ path;
90
+ /**
91
+ * The plugins map.
92
+ */
93
+ plugins;
94
+ /**
95
+ * Constructs a new plugins instance.
96
+ *
97
+ * @param serenity - The serenity instance.
98
+ */
99
+ constructor(serenity, path, enabled = true) {
100
+ this.serenity = serenity;
101
+ this.logger = new import_logger.Logger("Plugins", import_logger.LoggerColors.CyanBright);
102
+ this.path = (0, import_node_path.resolve)(import_node_process.default.cwd(), path);
103
+ this.plugins = /* @__PURE__ */ new Map();
104
+ if (enabled) {
105
+ if (!(0, import_node_fs.existsSync)(this.path)) {
106
+ (0, import_node_fs.mkdirSync)(this.path);
107
+ this.logger.success(`Created plugins folder at "${this.path}"`);
108
+ }
109
+ void this.loadNew();
110
+ }
111
+ }
112
+ async loadNew() {
113
+ const files = (0, import_node_fs.readdirSync)(this.path, { withFileTypes: true });
114
+ for (const file of files) {
115
+ if (!file.isDirectory())
116
+ continue;
117
+ const files2 = (0, import_node_fs.readdirSync)((0, import_node_path.resolve)(file.path, file.name), {
118
+ withFileTypes: true
119
+ });
120
+ const packExsist = files2.find((file2) => file2.name === "package.json");
121
+ if (!packExsist) {
122
+ this.logger.error(
123
+ `Plugin "${file.name}" does not contain a package.json file.`
124
+ );
125
+ continue;
126
+ }
127
+ const pack = JSON.parse(
128
+ (0, import_node_fs.readFileSync)((0, import_node_path.resolve)(packExsist.path, packExsist.name), "utf8")
129
+ );
130
+ if (!this.validatePackage(file.name, pack))
131
+ continue;
132
+ const needModules = (0, import_node_fs.existsSync)(
133
+ (0, import_node_path.resolve)(file.path, file.name, "node_modules")
134
+ );
135
+ if (!needModules) {
136
+ try {
137
+ (0, import_node_child_process.execSync)("npm install", { cwd: (0, import_node_path.resolve)(file.path, file.name) });
138
+ } catch (reason) {
139
+ this.logger.error(
140
+ `Failed to install node_modules for plugin "${file.name}".`,
141
+ reason
142
+ );
143
+ continue;
144
+ }
145
+ this.logger.success(
146
+ `Installed node_modules for plugin "${file.name}".`
147
+ );
148
+ }
149
+ const typescript = files2.find((file2) => file2.name === "tsconfig.json");
150
+ const tsconfig = typescript ? JSON.parse(
151
+ (0, import_node_fs.readFileSync)((0, import_node_path.resolve)(typescript.path, typescript.name), "utf8")
152
+ ) : null;
153
+ if (typescript && !this.validateTsconfig(file.name, tsconfig))
154
+ continue;
155
+ const needToCompile = Boolean(
156
+ typescript && !(0, import_node_fs.existsSync)(
157
+ (0, import_node_path.resolve)(file.path, file.name, tsconfig.compilerOptions.outDir)
158
+ ) || Boolean(pack?.development ?? false) === true
159
+ );
160
+ if (needToCompile) {
161
+ try {
162
+ if (pack?.scripts?.build) {
163
+ (0, import_node_child_process.execSync)("npm run build", { cwd: (0, import_node_path.resolve)(file.path, file.name) });
164
+ } else {
165
+ (0, import_node_child_process.execSync)("tsc", { cwd: (0, import_node_path.resolve)(file.path, file.name) });
166
+ }
167
+ this.logger.success(`Compiled plugin "${file.name}".`);
168
+ } catch (reason) {
169
+ this.logger.error(`Failed to compile plugin "${file.name}".`, reason);
170
+ continue;
171
+ }
172
+ }
173
+ try {
174
+ const plugin = await import(`file://${(0, import_node_path.resolve)(file.path, file.name, pack.main)}`);
175
+ if (!plugin.default)
176
+ continue;
177
+ const instance = new plugin.default(
178
+ this.serenity,
179
+ new import_logger.Logger(`${file.name}@${pack.version}`, import_logger.LoggerColors.Blue)
180
+ );
181
+ this.plugins.set(file.name, { instance, package: pack, plugin });
182
+ } catch (reason) {
183
+ this.logger.error(`Failed to import plugin "${file.name}".`, reason);
184
+ continue;
185
+ }
186
+ }
187
+ }
188
+ validatePackage(path, pack) {
189
+ if (!pack?.name) {
190
+ this.logger.error(
191
+ `Plugin package.json "${path}" does not contain a "name" property.`
192
+ );
193
+ return false;
194
+ }
195
+ if (!pack?.version) {
196
+ this.logger.error(
197
+ `Plugin package.json "${path}" does not contain a "version" property.`
198
+ );
199
+ return false;
200
+ }
201
+ if (!pack?.type) {
202
+ this.logger.error(
203
+ `Plugin package.json "${path}" does not contain a "type" property.`
204
+ );
205
+ return false;
206
+ } else if (pack.type !== "module") {
207
+ this.logger.error(
208
+ `Plugin package.json "${path}" type is not set to "module".`
209
+ );
210
+ return false;
211
+ }
212
+ if (!pack?.main) {
213
+ this.logger.error(
214
+ `Plugin package.json "${path}" does not contain a "main" property.`
215
+ );
216
+ return false;
217
+ }
218
+ return true;
219
+ }
220
+ validateTsconfig(path, tsconfig) {
221
+ if (!tsconfig?.compilerOptions) {
222
+ this.logger.error(
223
+ `Plugin tsconfig.json "${path}" does not contain a "compilerOptions" property.`
224
+ );
225
+ return false;
226
+ }
227
+ if (!tsconfig?.compilerOptions?.outDir) {
228
+ this.logger.error(
229
+ `Plugin tsconfig.json "${path}" does not contain a "outDir" property.`
230
+ );
231
+ return false;
232
+ }
233
+ return true;
234
+ }
235
+ };
236
+ // Annotate the CommonJS export names for ESM import in node:
237
+ 0 && (module.exports = {
238
+ BasePlugin,
239
+ Plugins
240
+ });
package/package.json ADDED
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "@serenityjs/plugins",
3
+ "version": "0.1.0-beta.10",
4
+ "main": "./dist/index.js",
5
+ "types": "./dist/index.d.ts",
6
+ "repository": "https://github.com/SerenityJS/serenity",
7
+ "files": [
8
+ "dist/**"
9
+ ],
10
+ "scripts": {
11
+ "build": "tsup",
12
+ "watch": "tsup --watch",
13
+ "lint": "eslint src/",
14
+ "typecheck": "tsc --noEmit",
15
+ "test": "jest"
16
+ },
17
+ "jest": {
18
+ "preset": "@serenityjs/jest-presets/jest/node"
19
+ },
20
+ "devDependencies": {
21
+ "@serenityjs/eslint-config": "0.1.0-beta.10",
22
+ "@serenityjs/jest-presets": "0.1.0-beta.10",
23
+ "@serenityjs/typescript-config": "0.1.0-beta.10",
24
+ "@types/jest": "^29.5.12",
25
+ "@types/node": "^20.11.24",
26
+ "jest": "^29.7.0",
27
+ "tsup": "^8.0.2",
28
+ "typescript": "^5.4.2"
29
+ },
30
+ "dependencies": {
31
+ "@serenityjs/logger": "0.1.0-beta.10"
32
+ }
33
+ }