@yumerijs/loader 1.1.1 → 1.2.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/dist/index.d.ts +1 -7
- package/dist/index.js +53 -98
- package/package.json +6 -6
package/dist/index.d.ts
CHANGED
|
@@ -21,13 +21,7 @@ declare class PluginLoader {
|
|
|
21
21
|
logger: Logger;
|
|
22
22
|
constructor(core?: Core, config?: Config, pluginsDir?: string);
|
|
23
23
|
setCoreAndConfig(core: Core, config: Config): void;
|
|
24
|
-
/**
|
|
25
|
-
* 从指定路径加载插件.
|
|
26
|
-
* @param pluginPath 插件路径.
|
|
27
|
-
* @param isLocalPlugin 是否是本地插件
|
|
28
|
-
* @returns 加载的插件对象.
|
|
29
|
-
* @throws 如果插件加载失败.
|
|
30
|
-
*/
|
|
31
24
|
private loadPluginFromPath;
|
|
25
|
+
private clearRequireCache;
|
|
32
26
|
}
|
|
33
27
|
export default PluginLoader;
|
package/dist/index.js
CHANGED
|
@@ -38,7 +38,7 @@ const core_1 = require("@yumerijs/core");
|
|
|
38
38
|
const fs = __importStar(require("fs"));
|
|
39
39
|
const util_1 = require("util");
|
|
40
40
|
const child_process_1 = require("child_process");
|
|
41
|
-
const
|
|
41
|
+
const url_1 = require("url");
|
|
42
42
|
const execAsync = (0, util_1.promisify)(child_process_1.exec);
|
|
43
43
|
class PluginLoader {
|
|
44
44
|
pluginCache = {};
|
|
@@ -53,60 +53,40 @@ class PluginLoader {
|
|
|
53
53
|
this.config = config || null;
|
|
54
54
|
this.isDev = process.env.NODE_ENV === 'development';
|
|
55
55
|
this.pluginsDir = pluginsDir;
|
|
56
|
-
tsNode.register({
|
|
57
|
-
transpileOnly: true, // Use transpileOnly for faster compilation
|
|
58
|
-
compilerOptions: {
|
|
59
|
-
module: 'CommonJS' // Ensure CommonJS for compatibility with require
|
|
60
|
-
}
|
|
61
|
-
});
|
|
62
56
|
}
|
|
63
57
|
setCoreAndConfig(core, config) {
|
|
64
58
|
this.core = core;
|
|
65
59
|
this.config = config;
|
|
66
60
|
}
|
|
67
|
-
/**
|
|
68
|
-
* 从本地目录或 npm 包加载插件.
|
|
69
|
-
* @param pluginName 插件名称或路径.
|
|
70
|
-
* @returns 加载的插件对象.
|
|
71
|
-
* @throws 如果插件加载失败.
|
|
72
|
-
*/
|
|
73
61
|
async load(pluginName) {
|
|
74
62
|
if (this.pluginCache[pluginName] && !this.isDev) {
|
|
75
|
-
// this.logger.info(`Returning cached plugin: ${pluginName}`);
|
|
76
63
|
return this.pluginCache[pluginName];
|
|
77
64
|
}
|
|
78
|
-
// this.logger.info('test')
|
|
79
65
|
let pluginPath;
|
|
80
66
|
let isLocalPlugin = false;
|
|
81
|
-
|
|
82
|
-
const
|
|
83
|
-
const localPluginPath1 = path.resolve(pluginName);
|
|
67
|
+
const localPluginPath = path.resolve(process.cwd(), this.pluginsDir, pluginName);
|
|
68
|
+
const localPluginPathAlt = path.resolve(process.cwd(), pluginName);
|
|
84
69
|
if (fs.existsSync(localPluginPath)) {
|
|
85
70
|
pluginPath = localPluginPath;
|
|
86
71
|
isLocalPlugin = true;
|
|
87
72
|
}
|
|
88
|
-
else if (fs.existsSync(
|
|
89
|
-
pluginPath =
|
|
73
|
+
else if (fs.existsSync(localPluginPathAlt)) {
|
|
74
|
+
pluginPath = localPluginPathAlt;
|
|
90
75
|
isLocalPlugin = true;
|
|
91
76
|
}
|
|
92
|
-
// 2. 检查是否是绝对路径或相对路径
|
|
93
77
|
else if (path.isAbsolute(pluginName) || pluginName.startsWith('.')) {
|
|
94
78
|
pluginPath = path.resolve(pluginName);
|
|
95
79
|
isLocalPlugin = true;
|
|
96
80
|
}
|
|
97
|
-
// 3. 尝试作为 npm 包加载
|
|
98
81
|
else {
|
|
99
82
|
try {
|
|
100
|
-
|
|
101
|
-
require.resolve(pluginName); // 检查包是否已安装
|
|
102
|
-
pluginPath = pluginName;
|
|
83
|
+
pluginPath = require.resolve(pluginName);
|
|
103
84
|
}
|
|
104
85
|
catch (e) {
|
|
105
|
-
// 如果 require.resolve 失败,则抛出错误
|
|
106
86
|
if (e.code === 'MODULE_NOT_FOUND') {
|
|
107
87
|
throw new Error(`Plugin ${pluginName} not found. Please install it first.`);
|
|
108
88
|
}
|
|
109
|
-
throw e;
|
|
89
|
+
throw e;
|
|
110
90
|
}
|
|
111
91
|
}
|
|
112
92
|
try {
|
|
@@ -118,103 +98,78 @@ class PluginLoader {
|
|
|
118
98
|
this.logger.error(`Failed to load plugin from ${pluginPath}:`, e);
|
|
119
99
|
throw e;
|
|
120
100
|
}
|
|
121
|
-
// const plugin = require(pluginName);
|
|
122
|
-
// this.pluginCache[pluginName] = plugin;
|
|
123
|
-
// return plugin;
|
|
124
101
|
}
|
|
125
|
-
/**
|
|
126
|
-
* 从指定路径加载插件.
|
|
127
|
-
* @param pluginPath 插件路径.
|
|
128
|
-
* @param isLocalPlugin 是否是本地插件
|
|
129
|
-
* @returns 加载的插件对象.
|
|
130
|
-
* @throws 如果插件加载失败.
|
|
131
|
-
*/
|
|
132
102
|
async loadPluginFromPath(pluginPath, isLocalPlugin) {
|
|
133
103
|
try {
|
|
134
|
-
let jsonPath;
|
|
135
104
|
let targetPath = pluginPath;
|
|
136
105
|
if (isLocalPlugin && this.isDev) {
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
}
|
|
144
|
-
// 检查目标文件是否是 TypeScript 文件
|
|
145
|
-
if (targetPath.endsWith('.ts')) {
|
|
146
|
-
// 清除该文件的缓存,确保获取最新版本
|
|
147
|
-
const resolvedPath = require.resolve(targetPath);
|
|
148
|
-
if (require.cache[resolvedPath]) {
|
|
149
|
-
delete require.cache[resolvedPath];
|
|
106
|
+
const jsonPath = path.join(pluginPath, 'package.json');
|
|
107
|
+
if (fs.existsSync(jsonPath)) {
|
|
108
|
+
const jsonContent = fs.readFileSync(jsonPath, 'utf-8');
|
|
109
|
+
const pkg = JSON.parse(jsonContent);
|
|
110
|
+
if (pkg.dev) {
|
|
111
|
+
targetPath = path.join(pluginPath, pkg.dev);
|
|
150
112
|
}
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
return module;
|
|
154
|
-
}
|
|
155
|
-
else {
|
|
156
|
-
// 清除该文件的缓存,确保获取最新版本
|
|
157
|
-
const resolvedPath = require.resolve(targetPath);
|
|
158
|
-
if (require.cache[resolvedPath]) {
|
|
159
|
-
delete require.cache[resolvedPath];
|
|
113
|
+
else if (pkg.main) {
|
|
114
|
+
targetPath = path.join(pluginPath, pkg.main);
|
|
160
115
|
}
|
|
161
|
-
// 使用 require 加载 js 模块
|
|
162
|
-
const module = require(targetPath);
|
|
163
|
-
return module;
|
|
164
116
|
}
|
|
165
117
|
}
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
//
|
|
169
|
-
|
|
170
|
-
|
|
118
|
+
if (this.isDev) {
|
|
119
|
+
// In development, use require() to leverage esbuild-register
|
|
120
|
+
// and clear cache for HMR.
|
|
121
|
+
const resolvedPath = require.resolve(targetPath);
|
|
122
|
+
// Clean up the cache for the main module and its children
|
|
123
|
+
if (require.cache[resolvedPath]) {
|
|
124
|
+
this.clearRequireCache(resolvedPath, new Set());
|
|
171
125
|
}
|
|
126
|
+
const module = require(resolvedPath);
|
|
127
|
+
return module.default || module;
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
// In production, use dynamic import.
|
|
131
|
+
const url = (0, url_1.pathToFileURL)(targetPath).href;
|
|
132
|
+
const module = await Promise.resolve(`${url}`).then(s => __importStar(require(s)));
|
|
133
|
+
return module.default || module;
|
|
172
134
|
}
|
|
173
|
-
// 使用require加载
|
|
174
|
-
const pluginModule = require(targetPath);
|
|
175
|
-
return pluginModule;
|
|
176
135
|
}
|
|
177
136
|
catch (e) {
|
|
178
137
|
this.logger.error(`Error loading plugin from path ${pluginPath}:`, e);
|
|
179
138
|
throw e;
|
|
180
139
|
}
|
|
181
140
|
}
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
141
|
+
clearRequireCache(moduleId, visited) {
|
|
142
|
+
if (visited.has(moduleId)) {
|
|
143
|
+
return; // Cycle detected
|
|
144
|
+
}
|
|
145
|
+
visited.add(moduleId);
|
|
146
|
+
const module = require.cache[moduleId];
|
|
147
|
+
if (!module)
|
|
148
|
+
return;
|
|
149
|
+
// Recursively clear children's cache
|
|
150
|
+
if (module.children) {
|
|
151
|
+
for (const child of module.children) {
|
|
152
|
+
this.clearRequireCache(child.id, visited);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
// Delete the module from cache
|
|
156
|
+
delete require.cache[moduleId];
|
|
157
|
+
}
|
|
186
158
|
async unloadPlugin(pluginName) {
|
|
187
159
|
if (!this.pluginCache[pluginName]) {
|
|
188
160
|
return; // Plugin not loaded
|
|
189
161
|
}
|
|
190
162
|
delete this.pluginCache[pluginName];
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
}
|
|
197
|
-
});
|
|
198
|
-
// this.logger.info(`Plugin unloaded: ${pluginName}`);
|
|
199
|
-
this.core?.unregall(pluginName);
|
|
200
|
-
}
|
|
201
|
-
catch (error) {
|
|
202
|
-
this.logger.error(`Error unloading plugin ${pluginName}:`, error);
|
|
203
|
-
}
|
|
163
|
+
// With dynamic import, there's no direct equivalent of require.cache to clear.
|
|
164
|
+
// The timestamp query in dev mode handles fresh loading.
|
|
165
|
+
// For production, modules are cached intentionally.
|
|
166
|
+
this.core?.unregall(pluginName);
|
|
167
|
+
// this.logger.info(`Plugin unloaded: ${pluginName}`);
|
|
204
168
|
}
|
|
205
|
-
/**
|
|
206
|
-
* 检查插件的依赖是否满足.
|
|
207
|
-
* @param pluginPath 插件路径.
|
|
208
|
-
* @returns 如果依赖满足,则返回 true,否则返回 false.
|
|
209
|
-
*/
|
|
210
169
|
async checkPluginDependencies(pluginPath) {
|
|
211
|
-
// TODO:
|
|
170
|
+
// TODO: Implement dependency checking from package.json
|
|
212
171
|
return true;
|
|
213
172
|
}
|
|
214
|
-
/**
|
|
215
|
-
* 安装插件的依赖.
|
|
216
|
-
* @param pluginPath 插件路径.
|
|
217
|
-
*/
|
|
218
173
|
async installPluginDependencies(pluginName) {
|
|
219
174
|
try {
|
|
220
175
|
this.logger.info(`Installing dependencies for plugin: ${pluginName}`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yumerijs/loader",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "Module loader for yumeri",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -8,8 +8,7 @@
|
|
|
8
8
|
"dist"
|
|
9
9
|
],
|
|
10
10
|
"scripts": {
|
|
11
|
-
"build": "tsc"
|
|
12
|
-
"prepublishOnly": "yarn build"
|
|
11
|
+
"build": "tsc"
|
|
13
12
|
},
|
|
14
13
|
"repository": {
|
|
15
14
|
"type": "git",
|
|
@@ -28,12 +27,13 @@
|
|
|
28
27
|
"devDependencies": {
|
|
29
28
|
"@types/node": "^22.13.10",
|
|
30
29
|
"@types/semver": "^7.5.8",
|
|
30
|
+
"esbuild": "^0.25.9",
|
|
31
|
+
"esbuild-register": "^3.6.0",
|
|
31
32
|
"typescript": "^5.8.2"
|
|
32
33
|
},
|
|
33
34
|
"dependencies": {
|
|
34
|
-
"@yumerijs/core": "^1.
|
|
35
|
+
"@yumerijs/core": "^1.2.0",
|
|
35
36
|
"chokidar": "^4.0.3",
|
|
36
|
-
"semver": "^7.7.1"
|
|
37
|
-
"ts-node": "^10.9.2"
|
|
37
|
+
"semver": "^7.7.1"
|
|
38
38
|
}
|
|
39
39
|
}
|