@yumerijs/loader 1.1.0 → 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 +54 -95
- 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,59 +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
|
-
if (this.pluginCache[pluginName]) {
|
|
75
|
-
this.logger.info(`Returning cached plugin: ${pluginName}`);
|
|
62
|
+
if (this.pluginCache[pluginName] && !this.isDev) {
|
|
76
63
|
return this.pluginCache[pluginName];
|
|
77
64
|
}
|
|
78
65
|
let pluginPath;
|
|
79
66
|
let isLocalPlugin = false;
|
|
80
|
-
|
|
81
|
-
const
|
|
82
|
-
const localPluginPath1 = path.resolve(pluginName);
|
|
67
|
+
const localPluginPath = path.resolve(process.cwd(), this.pluginsDir, pluginName);
|
|
68
|
+
const localPluginPathAlt = path.resolve(process.cwd(), pluginName);
|
|
83
69
|
if (fs.existsSync(localPluginPath)) {
|
|
84
70
|
pluginPath = localPluginPath;
|
|
85
71
|
isLocalPlugin = true;
|
|
86
72
|
}
|
|
87
|
-
else if (fs.existsSync(
|
|
88
|
-
pluginPath =
|
|
73
|
+
else if (fs.existsSync(localPluginPathAlt)) {
|
|
74
|
+
pluginPath = localPluginPathAlt;
|
|
89
75
|
isLocalPlugin = true;
|
|
90
76
|
}
|
|
91
|
-
// 2. 检查是否是绝对路径或相对路径
|
|
92
77
|
else if (path.isAbsolute(pluginName) || pluginName.startsWith('.')) {
|
|
93
78
|
pluginPath = path.resolve(pluginName);
|
|
94
79
|
isLocalPlugin = true;
|
|
95
80
|
}
|
|
96
|
-
// 3. 尝试作为 npm 包加载
|
|
97
81
|
else {
|
|
98
82
|
try {
|
|
99
|
-
|
|
100
|
-
require.resolve(pluginName); // 检查包是否已安装
|
|
101
|
-
pluginPath = pluginName;
|
|
83
|
+
pluginPath = require.resolve(pluginName);
|
|
102
84
|
}
|
|
103
85
|
catch (e) {
|
|
104
|
-
// 如果 require.resolve 失败,则抛出错误
|
|
105
86
|
if (e.code === 'MODULE_NOT_FOUND') {
|
|
106
87
|
throw new Error(`Plugin ${pluginName} not found. Please install it first.`);
|
|
107
88
|
}
|
|
108
|
-
throw e;
|
|
89
|
+
throw e;
|
|
109
90
|
}
|
|
110
91
|
}
|
|
111
92
|
try {
|
|
@@ -118,99 +99,77 @@ class PluginLoader {
|
|
|
118
99
|
throw e;
|
|
119
100
|
}
|
|
120
101
|
}
|
|
121
|
-
/**
|
|
122
|
-
* 从指定路径加载插件.
|
|
123
|
-
* @param pluginPath 插件路径.
|
|
124
|
-
* @param isLocalPlugin 是否是本地插件
|
|
125
|
-
* @returns 加载的插件对象.
|
|
126
|
-
* @throws 如果插件加载失败.
|
|
127
|
-
*/
|
|
128
102
|
async loadPluginFromPath(pluginPath, isLocalPlugin) {
|
|
129
103
|
try {
|
|
130
|
-
let jsonPath;
|
|
131
104
|
let targetPath = pluginPath;
|
|
132
105
|
if (isLocalPlugin && this.isDev) {
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
}
|
|
140
|
-
// 检查目标文件是否是 TypeScript 文件
|
|
141
|
-
if (targetPath.endsWith('.ts')) {
|
|
142
|
-
// 清除该文件的缓存,确保获取最新版本
|
|
143
|
-
const resolvedPath = require.resolve(targetPath);
|
|
144
|
-
if (require.cache[resolvedPath]) {
|
|
145
|
-
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);
|
|
146
112
|
}
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
return module;
|
|
150
|
-
}
|
|
151
|
-
else {
|
|
152
|
-
// 清除该文件的缓存,确保获取最新版本
|
|
153
|
-
const resolvedPath = require.resolve(targetPath);
|
|
154
|
-
if (require.cache[resolvedPath]) {
|
|
155
|
-
delete require.cache[resolvedPath];
|
|
113
|
+
else if (pkg.main) {
|
|
114
|
+
targetPath = path.join(pluginPath, pkg.main);
|
|
156
115
|
}
|
|
157
|
-
// 使用 require 加载 js 模块
|
|
158
|
-
const module = require(targetPath);
|
|
159
|
-
return module;
|
|
160
116
|
}
|
|
161
117
|
}
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
//
|
|
165
|
-
|
|
166
|
-
|
|
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());
|
|
167
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;
|
|
168
134
|
}
|
|
169
|
-
// 使用require加载
|
|
170
|
-
const pluginModule = require(targetPath);
|
|
171
|
-
return pluginModule;
|
|
172
135
|
}
|
|
173
136
|
catch (e) {
|
|
174
137
|
this.logger.error(`Error loading plugin from path ${pluginPath}:`, e);
|
|
175
138
|
throw e;
|
|
176
139
|
}
|
|
177
140
|
}
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
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
|
+
}
|
|
182
158
|
async unloadPlugin(pluginName) {
|
|
183
159
|
if (!this.pluginCache[pluginName]) {
|
|
184
160
|
return; // Plugin not loaded
|
|
185
161
|
}
|
|
186
162
|
delete this.pluginCache[pluginName];
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
}
|
|
193
|
-
});
|
|
194
|
-
// this.logger.info(`Plugin unloaded: ${pluginName}`);
|
|
195
|
-
this.core?.unregall(pluginName);
|
|
196
|
-
}
|
|
197
|
-
catch (error) {
|
|
198
|
-
this.logger.error(`Error unloading plugin ${pluginName}:`, error);
|
|
199
|
-
}
|
|
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}`);
|
|
200
168
|
}
|
|
201
|
-
/**
|
|
202
|
-
* 检查插件的依赖是否满足.
|
|
203
|
-
* @param pluginPath 插件路径.
|
|
204
|
-
* @returns 如果依赖满足,则返回 true,否则返回 false.
|
|
205
|
-
*/
|
|
206
169
|
async checkPluginDependencies(pluginPath) {
|
|
207
|
-
// TODO:
|
|
170
|
+
// TODO: Implement dependency checking from package.json
|
|
208
171
|
return true;
|
|
209
172
|
}
|
|
210
|
-
/**
|
|
211
|
-
* 安装插件的依赖.
|
|
212
|
-
* @param pluginPath 插件路径.
|
|
213
|
-
*/
|
|
214
173
|
async installPluginDependencies(pluginName) {
|
|
215
174
|
try {
|
|
216
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
|
}
|