@yumerijs/loader 3.1.1 → 3.1.2

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 CHANGED
@@ -3,6 +3,7 @@ interface Plugin {
3
3
  apply: (ctx: Context, config: Config) => Promise<void>;
4
4
  disable: (ctx: Context) => Promise<void>;
5
5
  depend: Array<string>;
6
+ optional?: Array<string>;
6
7
  provide: Array<string>;
7
8
  render?: string;
8
9
  config?: Schema<any>;
@@ -23,6 +24,7 @@ export declare class PluginLoader {
23
24
  plugins: {
24
25
  [name: string]: Plugin & {
25
26
  depend?: string[];
27
+ optional?: string[];
26
28
  provide?: string[];
27
29
  };
28
30
  };
@@ -51,7 +53,7 @@ export declare class PluginLoader {
51
53
  unregall(pluginName: string): void;
52
54
  loadConfig(configPath: string): Promise<void>;
53
55
  loadPlugins(): Promise<void>;
54
- loadSinglePlugin(pluginName: string, triggerPendingCheck?: boolean, onlypending?: boolean): Promise<boolean>;
56
+ loadSinglePlugin(pluginName: string, triggerPendingCheck?: boolean, onlypending?: boolean, requireOptionalDependencies?: boolean): Promise<boolean>;
55
57
  private _loadPendingPlugins;
56
58
  unloadPlugin(pluginNameToUnload: string, ispending?: boolean): Promise<void>;
57
59
  private _unloadSinglePlugin;
package/dist/index.js CHANGED
@@ -153,26 +153,20 @@ export class PluginLoader {
153
153
  this.logger.info('No enabled plugins found in configuration.');
154
154
  return;
155
155
  }
156
- let loadedInLastPass = true;
157
- while (loadedInLastPass) {
158
- loadedInLastPass = false;
159
- for (const pluginName of enabledPlugins) {
160
- if (this.pluginStatus[pluginName] === "enabled" /* PluginStatus.ENABLED */) {
161
- continue;
162
- }
163
- const success = await this.loadSinglePlugin(pluginName, false);
164
- if (success) {
165
- loadedInLastPass = true;
166
- }
167
- }
156
+ // 第一阶段:将 optional 与 depend 一样处理,尽量让可选服务先完成加载并注入。
157
+ while (await this._loadPendingPlugins(true)) {
158
+ // Continue scanning until no plugin can satisfy all required and optional dependencies.
159
+ }
160
+ // 第二阶段:严格扫描无法推进后,忽略尚未提供的 optional;depend 仍必须满足。
161
+ while (await this._loadPendingPlugins(false)) {
162
+ // Continue scanning in relaxed mode until only genuinely missing dependencies remain.
168
163
  }
169
- await this._loadPendingPlugins();
170
164
  const pendingPlugins = Object.keys(this.pluginStatus).filter(p => this.pluginStatus[p] === "pending" /* PluginStatus.PENDING */);
171
165
  if (pendingPlugins.length > 0) {
172
- // this.logger.warn('Some plugins could not be loaded due to unresolved dependencies:', pendingPlugins);
166
+ this.logger.warn('Some plugins could not be loaded due to unresolved required dependencies:', pendingPlugins);
173
167
  }
174
168
  }
175
- async loadSinglePlugin(pluginName, triggerPendingCheck = true, onlypending = false) {
169
+ async loadSinglePlugin(pluginName, triggerPendingCheck = true, onlypending = false, requireOptionalDependencies = true) {
176
170
  if (!this.pluginStatus[pluginName]) {
177
171
  this.pluginStatus[pluginName] = "pending" /* PluginStatus.PENDING */;
178
172
  }
@@ -211,7 +205,10 @@ export class PluginLoader {
211
205
  }
212
206
  }
213
207
  }
214
- const deps = pluginInstance.depend || [];
208
+ const deps = [
209
+ ...(pluginInstance.depend || []),
210
+ ...(requireOptionalDependencies ? (pluginInstance.optional || []) : []),
211
+ ];
215
212
  const unmetDependencies = deps.filter(dep => !this.core.components[dep] && !this.core.services[dep]);
216
213
  if (unmetDependencies.length > 0) {
217
214
  return false;
@@ -231,7 +228,7 @@ export class PluginLoader {
231
228
  await this.core.plugin(pluginInstance, context, finalConfig);
232
229
  this.pluginStatus[pluginName] = "enabled" /* PluginStatus.ENABLED */;
233
230
  if (triggerPendingCheck) {
234
- await this._loadPendingPlugins();
231
+ await this._loadPendingPlugins(requireOptionalDependencies);
235
232
  }
236
233
  if (this.isDev) {
237
234
  let pluginPathToWatch = null;
@@ -265,13 +262,14 @@ export class PluginLoader {
265
262
  return false;
266
263
  }
267
264
  }
268
- async _loadPendingPlugins() {
265
+ async _loadPendingPlugins(requireOptionalDependencies = true) {
269
266
  const pendingPlugins = Object.keys(this.pluginStatus).filter(p => this.pluginStatus[p] === "pending" /* PluginStatus.PENDING */);
270
- if (pendingPlugins.length === 0)
271
- return;
267
+ let loadedAny = false;
272
268
  for (const pluginName of pendingPlugins) {
273
- await this.loadSinglePlugin(pluginName, false);
269
+ const loaded = await this.loadSinglePlugin(pluginName, false, true, requireOptionalDependencies);
270
+ loadedAny ||= loaded;
274
271
  }
272
+ return loadedAny;
275
273
  }
276
274
  async unloadPlugin(pluginNameToUnload, ispending = false) {
277
275
  const dependents = [];
@@ -325,7 +323,10 @@ export class PluginLoader {
325
323
  this.logger.info(`Reloading plugin: "${pluginName}"...`);
326
324
  await this.reloadConfigFile();
327
325
  await this.unloadPlugin(pluginName, true);
328
- const success = await this.loadSinglePlugin(pluginName, true, false);
326
+ let success = await this.loadSinglePlugin(pluginName, true, false, true);
327
+ if (!success) {
328
+ success = await this.loadSinglePlugin(pluginName, true, false, false);
329
+ }
329
330
  if (success) {
330
331
  this.logger.info(`Plugin "${pluginName}" reloaded successfully.`);
331
332
  this.core.emit('plugin-reloaded', pluginName);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yumerijs/loader",
3
- "version": "3.1.1",
3
+ "version": "3.1.2",
4
4
  "description": "Module loader for yumeri",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",