@libria/plugin-loader 0.1.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.
- package/.clean-publish.hash +1 -0
- package/LICENSE +21 -0
- package/README.md +228 -0
- package/dist/index.cjs +2 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +44 -0
- package/dist/index.d.mts +44 -0
- package/dist/index.mjs +2 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +40 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
2c5f1c5099692fedf676cdc07ff278087d4924891f85b79c5b08647b51e7ed06
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 CinemaCove
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
# @libria/plugin-loader
|
|
2
|
+
|
|
3
|
+
A simple, type-safe plugin loader for Node.js applications. Supports both ESM and CommonJS plugins with glob pattern discovery.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @libria/plugin-loader
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quick Start
|
|
12
|
+
|
|
13
|
+
### 1. Create a Plugin
|
|
14
|
+
|
|
15
|
+
Create a `plugin.json` manifest in your plugin directory:
|
|
16
|
+
|
|
17
|
+
```json
|
|
18
|
+
{
|
|
19
|
+
"name": "my-plugin",
|
|
20
|
+
"pluginType": "greeting",
|
|
21
|
+
"module": "./dist/index.mjs"
|
|
22
|
+
}
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Create the plugin module:
|
|
26
|
+
|
|
27
|
+
```typescript
|
|
28
|
+
// src/index.ts
|
|
29
|
+
import { definePlugin } from '@libria/plugin-loader';
|
|
30
|
+
|
|
31
|
+
export default definePlugin('greeting', {
|
|
32
|
+
sayHello(name: string) {
|
|
33
|
+
return `Hello, ${name}!`;
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### 2. Load Plugins
|
|
39
|
+
|
|
40
|
+
```typescript
|
|
41
|
+
import { findPlugins, loadPlugin, loadAllPlugins } from '@libria/plugin-loader';
|
|
42
|
+
|
|
43
|
+
// Load all plugins from a directory
|
|
44
|
+
const plugins = await loadAllPlugins('./plugins', 'greeting');
|
|
45
|
+
|
|
46
|
+
for (const plugin of plugins) {
|
|
47
|
+
console.log(plugin.api.sayHello('World'));
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## API
|
|
52
|
+
|
|
53
|
+
### `definePlugin<T>(pluginType, api, name?)`
|
|
54
|
+
|
|
55
|
+
Helper function to create a type-safe plugin export.
|
|
56
|
+
|
|
57
|
+
```typescript
|
|
58
|
+
import { definePlugin } from '@libria/plugin-loader';
|
|
59
|
+
|
|
60
|
+
export default definePlugin('my-type', {
|
|
61
|
+
myMethod() {
|
|
62
|
+
return 'Hello!';
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### `findPlugins(pattern, pluginType?)`
|
|
68
|
+
|
|
69
|
+
Discovers plugins by scanning directories for `plugin.json` manifests.
|
|
70
|
+
|
|
71
|
+
```typescript
|
|
72
|
+
import { findPlugins } from '@libria/plugin-loader';
|
|
73
|
+
|
|
74
|
+
// Simple directory path (scans one level deep)
|
|
75
|
+
const manifests = await findPlugins('./plugins');
|
|
76
|
+
|
|
77
|
+
// Glob pattern
|
|
78
|
+
const manifests = await findPlugins('./plugins/*-plugin');
|
|
79
|
+
|
|
80
|
+
// Recursive glob
|
|
81
|
+
const manifests = await findPlugins('./plugins/**/dist');
|
|
82
|
+
|
|
83
|
+
// Filter by plugin type
|
|
84
|
+
const manifests = await findPlugins('./plugins', 'greeting');
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### `loadPlugin<T>(manifest)`
|
|
88
|
+
|
|
89
|
+
Loads a single plugin from its manifest. Supports both ESM (`module`) and CommonJS (`main`) entry points.
|
|
90
|
+
|
|
91
|
+
```typescript
|
|
92
|
+
import { findPlugins, loadPlugin } from '@libria/plugin-loader';
|
|
93
|
+
|
|
94
|
+
const [manifest] = await findPlugins('./plugins/my-plugin');
|
|
95
|
+
const plugin = await loadPlugin<{ sayHello: (name: string) => string }>(manifest);
|
|
96
|
+
|
|
97
|
+
console.log(plugin.api.sayHello('World'));
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### `loadAllPlugins<T>(pattern, pluginType?)`
|
|
101
|
+
|
|
102
|
+
Convenience function that combines `findPlugins` and `loadPlugin`. Discovers and loads all matching plugins.
|
|
103
|
+
|
|
104
|
+
```typescript
|
|
105
|
+
import { loadAllPlugins } from '@libria/plugin-loader';
|
|
106
|
+
|
|
107
|
+
const plugins = await loadAllPlugins<{ greet: () => string }>(
|
|
108
|
+
'./plugins/*-plugin',
|
|
109
|
+
'greeting'
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
for (const plugin of plugins) {
|
|
113
|
+
console.log(plugin.api.greet());
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## Plugin Manifest
|
|
118
|
+
|
|
119
|
+
The `plugin.json` file defines plugin metadata:
|
|
120
|
+
|
|
121
|
+
| Field | Type | Required | Description |
|
|
122
|
+
|--------------|--------|----------|--------------------------------------|
|
|
123
|
+
| `name` | string | Yes | Unique plugin identifier |
|
|
124
|
+
| `pluginType` | string | Yes | Plugin category/type for filtering |
|
|
125
|
+
| `module` | string | No | ESM entry point (relative path) |
|
|
126
|
+
| `main` | string | No | CommonJS entry point (relative path) |
|
|
127
|
+
| `types` | string | No | TypeScript declaration file |
|
|
128
|
+
|
|
129
|
+
At least one of `module` or `main` must be specified.
|
|
130
|
+
|
|
131
|
+
### ESM Plugin Example
|
|
132
|
+
|
|
133
|
+
```
|
|
134
|
+
my-plugin/
|
|
135
|
+
plugin.json
|
|
136
|
+
dist/
|
|
137
|
+
index.mjs
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
```json
|
|
141
|
+
{
|
|
142
|
+
"name": "my-plugin",
|
|
143
|
+
"pluginType": "feature",
|
|
144
|
+
"module": "./dist/index.mjs"
|
|
145
|
+
}
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### CommonJS Plugin Example
|
|
149
|
+
|
|
150
|
+
```
|
|
151
|
+
my-plugin/
|
|
152
|
+
plugin.json
|
|
153
|
+
dist/
|
|
154
|
+
index.cjs
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
```json
|
|
158
|
+
{
|
|
159
|
+
"name": "my-plugin",
|
|
160
|
+
"pluginType": "feature",
|
|
161
|
+
"main": "./dist/index.cjs"
|
|
162
|
+
}
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### Nested Manifest (dist folder)
|
|
166
|
+
|
|
167
|
+
You can place `plugin.json` inside the `dist` folder and use glob patterns to discover it:
|
|
168
|
+
|
|
169
|
+
```
|
|
170
|
+
my-plugin/
|
|
171
|
+
dist/
|
|
172
|
+
plugin.json
|
|
173
|
+
index.mjs
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
```typescript
|
|
177
|
+
// Discover plugins with manifest in dist/
|
|
178
|
+
const plugins = await loadAllPlugins('./plugins/*/dist');
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
## Types
|
|
182
|
+
|
|
183
|
+
### `LibriaPlugin<T>`
|
|
184
|
+
|
|
185
|
+
```typescript
|
|
186
|
+
interface LibriaPlugin<T = unknown> {
|
|
187
|
+
readonly pluginType: string;
|
|
188
|
+
readonly name?: string;
|
|
189
|
+
readonly api: T;
|
|
190
|
+
}
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
### `PluginManifest`
|
|
194
|
+
|
|
195
|
+
```typescript
|
|
196
|
+
interface PluginManifest {
|
|
197
|
+
readonly name: string;
|
|
198
|
+
readonly pluginType: string;
|
|
199
|
+
readonly main?: string;
|
|
200
|
+
readonly module?: string;
|
|
201
|
+
readonly types?: string;
|
|
202
|
+
readonly __dir: string; // Resolved absolute path
|
|
203
|
+
}
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
## Error Handling
|
|
207
|
+
|
|
208
|
+
The library throws specific errors for common issues:
|
|
209
|
+
|
|
210
|
+
- **`PluginLoadError`** - Failed to load the plugin module
|
|
211
|
+
- **`PluginInvalidExportError`** - Plugin export is not a valid object
|
|
212
|
+
- **`PluginTypeMismatchError`** - Plugin type doesn't match manifest
|
|
213
|
+
|
|
214
|
+
```typescript
|
|
215
|
+
import { loadPlugin, PluginTypeMismatchError } from '@libria/plugin-loader';
|
|
216
|
+
|
|
217
|
+
try {
|
|
218
|
+
const plugin = await loadPlugin(manifest);
|
|
219
|
+
} catch (error) {
|
|
220
|
+
if (error instanceof PluginTypeMismatchError) {
|
|
221
|
+
console.error(`Type mismatch: expected ${error.expected}, got ${error.actual}`);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
## License
|
|
227
|
+
|
|
228
|
+
MIT
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;l<u;l++)d=c[l],!a.call(e,d)&&d!==o&&t(e,d,{get:(e=>i[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},s=(n,r,a)=>(a=n==null?{}:e(i(n)),o(r||!n||!n.__esModule?t(a,`default`,{value:n,enumerable:!0}):a,n));let c=require(`fs-extra`);c=s(c);let l=require(`fast-glob`);l=s(l);let u=require(`path`);u=s(u);let d=require(`url`),f=require(`node:module`);function p(e,t,n){return{pluginType:e,name:n,api:t}}async function m(e,t){let n=[];if(e.includes(`*`)||e.includes(`{`)||e.includes(`?`)){let r=await(0,l.default)(e.replace(/\\/g,`/`),{onlyDirectories:!0,absolute:!0});for(let e of r){let r=u.default.join(e,`plugin.json`);if(!await c.default.pathExists(r))continue;let i=await c.default.readJson(r);t&&i.pluginType!==t||n.push({...i,__dir:e})}}else{if(!await c.default.pathExists(e))return[];let r=await c.default.readdir(e);for(let i of r){let r=u.default.join(e,i);if(!(await c.default.stat(r)).isDirectory())continue;let a=u.default.join(r,`plugin.json`);if(!await c.default.pathExists(a))continue;let o=await c.default.readJson(a);t&&o.pluginType!==t||n.push({...o,__dir:r})}}return n}var h=class extends Error{constructor(e,t){super(`Failed to load plugin "${e}".\n${String(t)}`),this.name=`PluginLoadError`,this.pluginName=e,this.cause=t}},g=class extends Error{constructor(e){super(`Plugin "${e}" has invalid export`),this.name=`PluginInvalidExportError`,this.pluginName=e}},_=class extends Error{constructor(e,t,n){super(`Plugin type mismatch for "${e}": "${n}" !== "${t}"`),this.name=`PluginTypeMismatchError`,this.pluginName=e,this.expected=t,this.actual=n}};const v=(0,f.createRequire)(require(`url`).pathToFileURL(__filename).href);async function y(e){let t,n;if(e.module)try{t=await import((0,d.pathToFileURL)(u.default.resolve(e.__dir,e.module)).href)}catch(e){n=e}if(!t&&e.main)try{t=v(u.default.resolve(e.__dir,e.main))}catch(e){n=e}if(!t)throw new h(e.name,n);let r=t.default??t;if(!r||typeof r!=`object`)throw new g(e.name);if(r.pluginType!==e.pluginType)throw new _(e.name,e.pluginType,r.pluginType);return r}async function b(e,t){let n=await m(e,t),r=[];for(let e of n){let t=await y(e);r.push(t)}return r}exports.PluginInvalidExportError=g,exports.PluginLoadError=h,exports.PluginTypeMismatchError=_,exports.definePlugin=p,exports.findPlugins=m,exports.loadAllPlugins=b,exports.loadPlugin=y;
|
|
2
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["fs","require"],"sources":["../src/define-plugin.ts","../src/find-plugins.ts","../src/types.ts","../src/load-plugin.ts","../src/load-all-plugins.ts"],"sourcesContent":["import {LibriaPlugin} from \"./types\";\r\n\r\nexport function definePlugin<T>(\r\n pluginType: string,\r\n api: T,\r\n name: string\r\n): LibriaPlugin<T> {\r\n return {\r\n pluginType: pluginType,\r\n name,\r\n api\r\n };\r\n}","import {PluginManifest} from \"./types\";\r\nimport fs from \"fs-extra\";\r\nimport fg from \"fast-glob\";\r\nimport path from \"path\";\r\n\r\nexport async function findPlugins(\r\n pattern: string,\r\n pluginType?: string\r\n): Promise<PluginManifest[]> {\r\n const manifests: PluginManifest[] = [];\r\n\r\n // Check if pattern is a glob pattern or a simple path\r\n const isGlob = pattern.includes('*') || pattern.includes('{') || pattern.includes('?');\r\n\r\n if (isGlob) {\r\n // Normalize path separators for fast-glob (it expects forward slashes)\r\n const normalizedPattern = pattern.replace(/\\\\/g, '/');\r\n\r\n // Use fast-glob to find all matching directories\r\n const pluginDirs = await fg(normalizedPattern, {\r\n onlyDirectories: true,\r\n absolute: true,\r\n });\r\n\r\n for (const dir of pluginDirs) {\r\n const manifestPath = path.join(dir, 'plugin.json');\r\n if (!(await fs.pathExists(manifestPath))) continue;\r\n\r\n const raw = await fs.readJson(manifestPath);\r\n\r\n if (pluginType && raw.pluginType !== pluginType) continue;\r\n\r\n manifests.push({\r\n ...raw,\r\n __dir: dir\r\n });\r\n }\r\n } else {\r\n // Original behavior for simple directory paths\r\n if (!(await fs.pathExists(pattern))) return [];\r\n\r\n const entries = await fs.readdir(pattern);\r\n\r\n for (const entry of entries) {\r\n const fullPath = path.join(pattern, entry);\r\n const stat = await fs.stat(fullPath);\r\n\r\n if (!stat.isDirectory()) continue;\r\n\r\n const manifestPath = path.join(fullPath, 'plugin.json');\r\n if (!(await fs.pathExists(manifestPath))) continue;\r\n\r\n const raw = await fs.readJson(manifestPath);\r\n\r\n if (pluginType && raw.pluginType !== pluginType) continue;\r\n\r\n manifests.push({\r\n ...raw,\r\n __dir: fullPath\r\n });\r\n }\r\n }\r\n\r\n return manifests;\r\n}","export interface LibriaPlugin<T = unknown> {\r\n readonly pluginType: string;\r\n readonly name?: string;\r\n readonly api: T;\r\n}\r\n\r\nexport interface PluginManifest {\r\n readonly name: string;\r\n readonly pluginType: string;\r\n\r\n readonly main?: string; // cjs\r\n readonly module?: string; // esm\r\n readonly types?: string;\r\n\r\n // resolved absolute path to the plugin folder\r\n readonly __dir: string;\r\n}\r\n\r\n// Plugin Errors\r\n\r\nexport class PluginLoadError extends Error {\r\n readonly pluginName: string;\r\n readonly cause: unknown;\r\n\r\n constructor(pluginName: string, cause: unknown) {\r\n super(`Failed to load plugin \"${pluginName}\".\\n${String(cause)}`);\r\n this.name = 'PluginLoadError';\r\n this.pluginName = pluginName;\r\n this.cause = cause;\r\n }\r\n}\r\n\r\nexport class PluginInvalidExportError extends Error {\r\n readonly pluginName: string;\r\n\r\n constructor(pluginName: string) {\r\n super(`Plugin \"${pluginName}\" has invalid export`);\r\n this.name = 'PluginInvalidExportError';\r\n this.pluginName = pluginName;\r\n }\r\n}\r\n\r\nexport class PluginTypeMismatchError extends Error {\r\n readonly pluginName: string;\r\n readonly expected: string;\r\n readonly actual: string;\r\n\r\n constructor(pluginName: string, expected: string, actual: string) {\r\n super(\r\n `Plugin type mismatch for \"${pluginName}\": ` +\r\n `\"${actual}\" !== \"${expected}\"`\r\n );\r\n this.name = 'PluginTypeMismatchError';\r\n this.pluginName = pluginName;\r\n this.expected = expected;\r\n this.actual = actual;\r\n }\r\n}","import {\r\n LibriaPlugin,\r\n PluginManifest,\r\n PluginLoadError,\r\n PluginInvalidExportError,\r\n PluginTypeMismatchError\r\n} from \"./types\";\r\nimport path from \"path\";\r\nimport {pathToFileURL} from \"url\";\r\nimport {createRequire} from \"node:module\";\r\n\r\nconst require = createRequire(import.meta.url);\r\n\r\nexport async function loadPlugin<T = unknown>(\r\n manifest: PluginManifest\r\n): Promise<LibriaPlugin<T>> {\r\n let mod: any;\r\n let lastError: unknown;\r\n\r\n // 1 Try ESM first\r\n if (manifest.module) {\r\n try {\r\n const esmPath = path.resolve(manifest.__dir, manifest.module);\r\n mod = await import(pathToFileURL(esmPath).href);\r\n } catch (err) {\r\n lastError = err;\r\n }\r\n }\r\n\r\n // 2 Fallback to CJS\r\n if (!mod && manifest.main) {\r\n try {\r\n const cjsPath = path.resolve(manifest.__dir, manifest.main);\r\n mod = require(cjsPath);\r\n } catch (err) {\r\n lastError = err;\r\n }\r\n }\r\n\r\n if (!mod) {\r\n throw new PluginLoadError(manifest.name, lastError);\r\n }\r\n\r\n // 3️⃣ Normalize export\r\n const plugin = (mod.default ?? mod) as LibriaPlugin<T>;\r\n\r\n if (!plugin || typeof plugin !== 'object') {\r\n throw new PluginInvalidExportError(manifest.name);\r\n }\r\n\r\n if (plugin.pluginType !== manifest.pluginType) {\r\n throw new PluginTypeMismatchError(\r\n manifest.name,\r\n manifest.pluginType,\r\n plugin.pluginType\r\n );\r\n }\r\n\r\n return plugin;\r\n}","import {LibriaPlugin} from \"./types\";\r\nimport {findPlugins} from \"./find-plugins\";\r\nimport {loadPlugin} from \"./load-plugin\";\r\n\r\nexport async function loadAllPlugins<T = unknown>(\r\n pattern: string,\r\n pluginType?: string\r\n): Promise<LibriaPlugin<T>[]> {\r\n const manifests = await findPlugins(pattern, pluginType);\r\n const plugins: LibriaPlugin<T>[] = [];\r\n\r\n for (const manifest of manifests) {\r\n const plugin = await loadPlugin<T>(manifest);\r\n plugins.push(plugin);\r\n }\r\n\r\n return plugins;\r\n}\r\n"],"mappings":"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"}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
//#region src/types.d.ts
|
|
2
|
+
interface LibriaPlugin<T = unknown> {
|
|
3
|
+
readonly pluginType: string;
|
|
4
|
+
readonly name?: string;
|
|
5
|
+
readonly api: T;
|
|
6
|
+
}
|
|
7
|
+
interface PluginManifest {
|
|
8
|
+
readonly name: string;
|
|
9
|
+
readonly pluginType: string;
|
|
10
|
+
readonly main?: string;
|
|
11
|
+
readonly module?: string;
|
|
12
|
+
readonly types?: string;
|
|
13
|
+
readonly __dir: string;
|
|
14
|
+
}
|
|
15
|
+
declare class PluginLoadError extends Error {
|
|
16
|
+
readonly pluginName: string;
|
|
17
|
+
readonly cause: unknown;
|
|
18
|
+
constructor(pluginName: string, cause: unknown);
|
|
19
|
+
}
|
|
20
|
+
declare class PluginInvalidExportError extends Error {
|
|
21
|
+
readonly pluginName: string;
|
|
22
|
+
constructor(pluginName: string);
|
|
23
|
+
}
|
|
24
|
+
declare class PluginTypeMismatchError extends Error {
|
|
25
|
+
readonly pluginName: string;
|
|
26
|
+
readonly expected: string;
|
|
27
|
+
readonly actual: string;
|
|
28
|
+
constructor(pluginName: string, expected: string, actual: string);
|
|
29
|
+
}
|
|
30
|
+
//#endregion
|
|
31
|
+
//#region src/define-plugin.d.ts
|
|
32
|
+
declare function definePlugin<T>(pluginType: string, api: T, name: string): LibriaPlugin<T>;
|
|
33
|
+
//#endregion
|
|
34
|
+
//#region src/find-plugins.d.ts
|
|
35
|
+
declare function findPlugins(pattern: string, pluginType?: string): Promise<PluginManifest[]>;
|
|
36
|
+
//#endregion
|
|
37
|
+
//#region src/load-plugin.d.ts
|
|
38
|
+
declare function loadPlugin<T = unknown>(manifest: PluginManifest): Promise<LibriaPlugin<T>>;
|
|
39
|
+
//#endregion
|
|
40
|
+
//#region src/load-all-plugins.d.ts
|
|
41
|
+
declare function loadAllPlugins<T = unknown>(pattern: string, pluginType?: string): Promise<LibriaPlugin<T>[]>;
|
|
42
|
+
//#endregion
|
|
43
|
+
export { LibriaPlugin, PluginInvalidExportError, PluginLoadError, PluginManifest, PluginTypeMismatchError, definePlugin, findPlugins, loadAllPlugins, loadPlugin };
|
|
44
|
+
//# sourceMappingURL=index.d.cts.map
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
//#region src/types.d.ts
|
|
2
|
+
interface LibriaPlugin<T = unknown> {
|
|
3
|
+
readonly pluginType: string;
|
|
4
|
+
readonly name?: string;
|
|
5
|
+
readonly api: T;
|
|
6
|
+
}
|
|
7
|
+
interface PluginManifest {
|
|
8
|
+
readonly name: string;
|
|
9
|
+
readonly pluginType: string;
|
|
10
|
+
readonly main?: string;
|
|
11
|
+
readonly module?: string;
|
|
12
|
+
readonly types?: string;
|
|
13
|
+
readonly __dir: string;
|
|
14
|
+
}
|
|
15
|
+
declare class PluginLoadError extends Error {
|
|
16
|
+
readonly pluginName: string;
|
|
17
|
+
readonly cause: unknown;
|
|
18
|
+
constructor(pluginName: string, cause: unknown);
|
|
19
|
+
}
|
|
20
|
+
declare class PluginInvalidExportError extends Error {
|
|
21
|
+
readonly pluginName: string;
|
|
22
|
+
constructor(pluginName: string);
|
|
23
|
+
}
|
|
24
|
+
declare class PluginTypeMismatchError extends Error {
|
|
25
|
+
readonly pluginName: string;
|
|
26
|
+
readonly expected: string;
|
|
27
|
+
readonly actual: string;
|
|
28
|
+
constructor(pluginName: string, expected: string, actual: string);
|
|
29
|
+
}
|
|
30
|
+
//#endregion
|
|
31
|
+
//#region src/define-plugin.d.ts
|
|
32
|
+
declare function definePlugin<T>(pluginType: string, api: T, name: string): LibriaPlugin<T>;
|
|
33
|
+
//#endregion
|
|
34
|
+
//#region src/find-plugins.d.ts
|
|
35
|
+
declare function findPlugins(pattern: string, pluginType?: string): Promise<PluginManifest[]>;
|
|
36
|
+
//#endregion
|
|
37
|
+
//#region src/load-plugin.d.ts
|
|
38
|
+
declare function loadPlugin<T = unknown>(manifest: PluginManifest): Promise<LibriaPlugin<T>>;
|
|
39
|
+
//#endregion
|
|
40
|
+
//#region src/load-all-plugins.d.ts
|
|
41
|
+
declare function loadAllPlugins<T = unknown>(pattern: string, pluginType?: string): Promise<LibriaPlugin<T>[]>;
|
|
42
|
+
//#endregion
|
|
43
|
+
export { LibriaPlugin, PluginInvalidExportError, PluginLoadError, PluginManifest, PluginTypeMismatchError, definePlugin, findPlugins, loadAllPlugins, loadPlugin };
|
|
44
|
+
//# sourceMappingURL=index.d.mts.map
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{createRequire as e}from"node:module";import t from"fs-extra";import n from"fast-glob";import r from"path";import{pathToFileURL as i}from"url";function a(e,t,n){return{pluginType:e,name:n,api:t}}async function o(e,i){let a=[];if(e.includes(`*`)||e.includes(`{`)||e.includes(`?`)){let o=await n(e.replace(/\\/g,`/`),{onlyDirectories:!0,absolute:!0});for(let e of o){let n=r.join(e,`plugin.json`);if(!await t.pathExists(n))continue;let o=await t.readJson(n);i&&o.pluginType!==i||a.push({...o,__dir:e})}}else{if(!await t.pathExists(e))return[];let n=await t.readdir(e);for(let o of n){let n=r.join(e,o);if(!(await t.stat(n)).isDirectory())continue;let s=r.join(n,`plugin.json`);if(!await t.pathExists(s))continue;let c=await t.readJson(s);i&&c.pluginType!==i||a.push({...c,__dir:n})}}return a}var s=class extends Error{constructor(e,t){super(`Failed to load plugin "${e}".\n${String(t)}`),this.name=`PluginLoadError`,this.pluginName=e,this.cause=t}},c=class extends Error{constructor(e){super(`Plugin "${e}" has invalid export`),this.name=`PluginInvalidExportError`,this.pluginName=e}},l=class extends Error{constructor(e,t,n){super(`Plugin type mismatch for "${e}": "${n}" !== "${t}"`),this.name=`PluginTypeMismatchError`,this.pluginName=e,this.expected=t,this.actual=n}};const u=e(import.meta.url);async function d(e){let t,n;if(e.module)try{t=await import(i(r.resolve(e.__dir,e.module)).href)}catch(e){n=e}if(!t&&e.main)try{t=u(r.resolve(e.__dir,e.main))}catch(e){n=e}if(!t)throw new s(e.name,n);let a=t.default??t;if(!a||typeof a!=`object`)throw new c(e.name);if(a.pluginType!==e.pluginType)throw new l(e.name,e.pluginType,a.pluginType);return a}async function f(e,t){let n=await o(e,t),r=[];for(let e of n){let t=await d(e);r.push(t)}return r}export{c as PluginInvalidExportError,s as PluginLoadError,l as PluginTypeMismatchError,a as definePlugin,o as findPlugins,f as loadAllPlugins,d as loadPlugin};
|
|
2
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../src/define-plugin.ts","../src/find-plugins.ts","../src/types.ts","../src/load-plugin.ts","../src/load-all-plugins.ts"],"sourcesContent":["import {LibriaPlugin} from \"./types\";\r\n\r\nexport function definePlugin<T>(\r\n pluginType: string,\r\n api: T,\r\n name: string\r\n): LibriaPlugin<T> {\r\n return {\r\n pluginType: pluginType,\r\n name,\r\n api\r\n };\r\n}","import {PluginManifest} from \"./types\";\r\nimport fs from \"fs-extra\";\r\nimport fg from \"fast-glob\";\r\nimport path from \"path\";\r\n\r\nexport async function findPlugins(\r\n pattern: string,\r\n pluginType?: string\r\n): Promise<PluginManifest[]> {\r\n const manifests: PluginManifest[] = [];\r\n\r\n // Check if pattern is a glob pattern or a simple path\r\n const isGlob = pattern.includes('*') || pattern.includes('{') || pattern.includes('?');\r\n\r\n if (isGlob) {\r\n // Normalize path separators for fast-glob (it expects forward slashes)\r\n const normalizedPattern = pattern.replace(/\\\\/g, '/');\r\n\r\n // Use fast-glob to find all matching directories\r\n const pluginDirs = await fg(normalizedPattern, {\r\n onlyDirectories: true,\r\n absolute: true,\r\n });\r\n\r\n for (const dir of pluginDirs) {\r\n const manifestPath = path.join(dir, 'plugin.json');\r\n if (!(await fs.pathExists(manifestPath))) continue;\r\n\r\n const raw = await fs.readJson(manifestPath);\r\n\r\n if (pluginType && raw.pluginType !== pluginType) continue;\r\n\r\n manifests.push({\r\n ...raw,\r\n __dir: dir\r\n });\r\n }\r\n } else {\r\n // Original behavior for simple directory paths\r\n if (!(await fs.pathExists(pattern))) return [];\r\n\r\n const entries = await fs.readdir(pattern);\r\n\r\n for (const entry of entries) {\r\n const fullPath = path.join(pattern, entry);\r\n const stat = await fs.stat(fullPath);\r\n\r\n if (!stat.isDirectory()) continue;\r\n\r\n const manifestPath = path.join(fullPath, 'plugin.json');\r\n if (!(await fs.pathExists(manifestPath))) continue;\r\n\r\n const raw = await fs.readJson(manifestPath);\r\n\r\n if (pluginType && raw.pluginType !== pluginType) continue;\r\n\r\n manifests.push({\r\n ...raw,\r\n __dir: fullPath\r\n });\r\n }\r\n }\r\n\r\n return manifests;\r\n}","export interface LibriaPlugin<T = unknown> {\r\n readonly pluginType: string;\r\n readonly name?: string;\r\n readonly api: T;\r\n}\r\n\r\nexport interface PluginManifest {\r\n readonly name: string;\r\n readonly pluginType: string;\r\n\r\n readonly main?: string; // cjs\r\n readonly module?: string; // esm\r\n readonly types?: string;\r\n\r\n // resolved absolute path to the plugin folder\r\n readonly __dir: string;\r\n}\r\n\r\n// Plugin Errors\r\n\r\nexport class PluginLoadError extends Error {\r\n readonly pluginName: string;\r\n readonly cause: unknown;\r\n\r\n constructor(pluginName: string, cause: unknown) {\r\n super(`Failed to load plugin \"${pluginName}\".\\n${String(cause)}`);\r\n this.name = 'PluginLoadError';\r\n this.pluginName = pluginName;\r\n this.cause = cause;\r\n }\r\n}\r\n\r\nexport class PluginInvalidExportError extends Error {\r\n readonly pluginName: string;\r\n\r\n constructor(pluginName: string) {\r\n super(`Plugin \"${pluginName}\" has invalid export`);\r\n this.name = 'PluginInvalidExportError';\r\n this.pluginName = pluginName;\r\n }\r\n}\r\n\r\nexport class PluginTypeMismatchError extends Error {\r\n readonly pluginName: string;\r\n readonly expected: string;\r\n readonly actual: string;\r\n\r\n constructor(pluginName: string, expected: string, actual: string) {\r\n super(\r\n `Plugin type mismatch for \"${pluginName}\": ` +\r\n `\"${actual}\" !== \"${expected}\"`\r\n );\r\n this.name = 'PluginTypeMismatchError';\r\n this.pluginName = pluginName;\r\n this.expected = expected;\r\n this.actual = actual;\r\n }\r\n}","import {\r\n LibriaPlugin,\r\n PluginManifest,\r\n PluginLoadError,\r\n PluginInvalidExportError,\r\n PluginTypeMismatchError\r\n} from \"./types\";\r\nimport path from \"path\";\r\nimport {pathToFileURL} from \"url\";\r\nimport {createRequire} from \"node:module\";\r\n\r\nconst require = createRequire(import.meta.url);\r\n\r\nexport async function loadPlugin<T = unknown>(\r\n manifest: PluginManifest\r\n): Promise<LibriaPlugin<T>> {\r\n let mod: any;\r\n let lastError: unknown;\r\n\r\n // 1 Try ESM first\r\n if (manifest.module) {\r\n try {\r\n const esmPath = path.resolve(manifest.__dir, manifest.module);\r\n mod = await import(pathToFileURL(esmPath).href);\r\n } catch (err) {\r\n lastError = err;\r\n }\r\n }\r\n\r\n // 2 Fallback to CJS\r\n if (!mod && manifest.main) {\r\n try {\r\n const cjsPath = path.resolve(manifest.__dir, manifest.main);\r\n mod = require(cjsPath);\r\n } catch (err) {\r\n lastError = err;\r\n }\r\n }\r\n\r\n if (!mod) {\r\n throw new PluginLoadError(manifest.name, lastError);\r\n }\r\n\r\n // 3️⃣ Normalize export\r\n const plugin = (mod.default ?? mod) as LibriaPlugin<T>;\r\n\r\n if (!plugin || typeof plugin !== 'object') {\r\n throw new PluginInvalidExportError(manifest.name);\r\n }\r\n\r\n if (plugin.pluginType !== manifest.pluginType) {\r\n throw new PluginTypeMismatchError(\r\n manifest.name,\r\n manifest.pluginType,\r\n plugin.pluginType\r\n );\r\n }\r\n\r\n return plugin;\r\n}","import {LibriaPlugin} from \"./types\";\r\nimport {findPlugins} from \"./find-plugins\";\r\nimport {loadPlugin} from \"./load-plugin\";\r\n\r\nexport async function loadAllPlugins<T = unknown>(\r\n pattern: string,\r\n pluginType?: string\r\n): Promise<LibriaPlugin<T>[]> {\r\n const manifests = await findPlugins(pattern, pluginType);\r\n const plugins: LibriaPlugin<T>[] = [];\r\n\r\n for (const manifest of manifests) {\r\n const plugin = await loadPlugin<T>(manifest);\r\n plugins.push(plugin);\r\n }\r\n\r\n return plugins;\r\n}\r\n"],"mappings":"qJAEA,SAAgB,EACZ,EACA,EACA,EACe,CACf,MAAO,CACS,aACZ,OACA,MACH,CCNL,eAAsB,EAClB,EACA,EACyB,CACzB,IAAM,EAA8B,EAAE,CAKtC,GAFe,EAAQ,SAAS,IAAI,EAAI,EAAQ,SAAS,IAAI,EAAI,EAAQ,SAAS,IAAI,CAE1E,CAKR,IAAM,EAAa,MAAM,EAHC,EAAQ,QAAQ,MAAO,IAAI,CAGN,CAC3C,gBAAiB,GACjB,SAAU,GACb,CAAC,CAEF,IAAK,IAAM,KAAO,EAAY,CAC1B,IAAM,EAAe,EAAK,KAAK,EAAK,cAAc,CAClD,GAAI,CAAE,MAAM,EAAG,WAAW,EAAa,CAAG,SAE1C,IAAM,EAAM,MAAM,EAAG,SAAS,EAAa,CAEvC,GAAc,EAAI,aAAe,GAErC,EAAU,KAAK,CACX,GAAG,EACH,MAAO,EACV,CAAC,MAEH,CAEH,GAAI,CAAE,MAAM,EAAG,WAAW,EAAQ,CAAG,MAAO,EAAE,CAE9C,IAAM,EAAU,MAAM,EAAG,QAAQ,EAAQ,CAEzC,IAAK,IAAM,KAAS,EAAS,CACzB,IAAM,EAAW,EAAK,KAAK,EAAS,EAAM,CAG1C,GAAI,EAFS,MAAM,EAAG,KAAK,EAAS,EAE1B,aAAa,CAAE,SAEzB,IAAM,EAAe,EAAK,KAAK,EAAU,cAAc,CACvD,GAAI,CAAE,MAAM,EAAG,WAAW,EAAa,CAAG,SAE1C,IAAM,EAAM,MAAM,EAAG,SAAS,EAAa,CAEvC,GAAc,EAAI,aAAe,GAErC,EAAU,KAAK,CACX,GAAG,EACH,MAAO,EACV,CAAC,EAIV,OAAO,EC3CX,IAAa,EAAb,cAAqC,KAAM,CAIvC,YAAY,EAAoB,EAAgB,CAC5C,MAAM,0BAA0B,EAAW,MAAM,OAAO,EAAM,GAAG,CACjE,KAAK,KAAO,kBACZ,KAAK,WAAa,EAClB,KAAK,MAAQ,IAIR,EAAb,cAA8C,KAAM,CAGhD,YAAY,EAAoB,CAC5B,MAAM,WAAW,EAAW,sBAAsB,CAClD,KAAK,KAAO,2BACZ,KAAK,WAAa,IAIb,EAAb,cAA6C,KAAM,CAK/C,YAAY,EAAoB,EAAkB,EAAgB,CAC9D,MACI,6BAA6B,EAAW,MACpC,EAAO,SAAS,EAAS,GAChC,CACD,KAAK,KAAO,0BACZ,KAAK,WAAa,EAClB,KAAK,SAAW,EAChB,KAAK,OAAS,IC5CtB,MAAM,EAAU,EAAc,OAAO,KAAK,IAAI,CAE9C,eAAsB,EAClB,EACwB,CACxB,IAAI,EACA,EAGJ,GAAI,EAAS,OACT,GAAI,CAEA,EAAM,MAAM,OAAO,EADH,EAAK,QAAQ,EAAS,MAAO,EAAS,OAAO,CACpB,CAAC,YACrC,EAAK,CACV,EAAY,EAKpB,GAAI,CAAC,GAAO,EAAS,KACjB,GAAI,CAEA,EAAM,EADU,EAAK,QAAQ,EAAS,MAAO,EAAS,KAAK,CACrC,OACjB,EAAK,CACV,EAAY,EAIpB,GAAI,CAAC,EACD,MAAM,IAAI,EAAgB,EAAS,KAAM,EAAU,CAIvD,IAAM,EAAU,EAAI,SAAW,EAE/B,GAAI,CAAC,GAAU,OAAO,GAAW,SAC7B,MAAM,IAAI,EAAyB,EAAS,KAAK,CAGrD,GAAI,EAAO,aAAe,EAAS,WAC/B,MAAM,IAAI,EACN,EAAS,KACT,EAAS,WACT,EAAO,WACV,CAGL,OAAO,ECtDX,eAAsB,EAClB,EACA,EAC0B,CAC1B,IAAM,EAAY,MAAM,EAAY,EAAS,EAAW,CAClD,EAA6B,EAAE,CAErC,IAAK,IAAM,KAAY,EAAW,CAC9B,IAAM,EAAS,MAAM,EAAc,EAAS,CAC5C,EAAQ,KAAK,EAAO,CAGxB,OAAO"}
|
package/package.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@libria/plugin-loader",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Simple plugin loader for Node.js applications",
|
|
5
|
+
"main": "dist/index.cjs",
|
|
6
|
+
"module": "dist/index.mjs",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"require": {
|
|
10
|
+
"types": "./dist/index.d.cts",
|
|
11
|
+
"default": "./dist/index.cjs"
|
|
12
|
+
},
|
|
13
|
+
"import": {
|
|
14
|
+
"types": "./dist/index.d.mts",
|
|
15
|
+
"default": "./dist/index.mjs"
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"type": "module",
|
|
20
|
+
"repository": {
|
|
21
|
+
"type": "git",
|
|
22
|
+
"url": "git+https://github.com/LibriaForge/plugin-loader.git"
|
|
23
|
+
},
|
|
24
|
+
"keywords": [
|
|
25
|
+
"plugin",
|
|
26
|
+
"loader",
|
|
27
|
+
"plugin loader",
|
|
28
|
+
"typescript"
|
|
29
|
+
],
|
|
30
|
+
"author": "LibriaForge",
|
|
31
|
+
"license": "MIT",
|
|
32
|
+
"bugs": {
|
|
33
|
+
"url": "https://github.com/LibriaForge/plugin-loader/issues"
|
|
34
|
+
},
|
|
35
|
+
"homepage": "https://github.com/LibriaForge/plugin-loader#readme",
|
|
36
|
+
"dependencies": {
|
|
37
|
+
"fast-glob": "^3.3.3",
|
|
38
|
+
"fs-extra": "^11.3.3"
|
|
39
|
+
}
|
|
40
|
+
}
|