@wener/console
Version:
Base console UI toolkit
64 lines (55 loc) • 1.72 kB
text/typescript
import type { RouteObject } from 'react-router';
import { createStore, type StoreApi } from 'zustand';
import { immer } from 'zustand/middleware/immer';
import { DynamicStore } from './DynamicStore';
import type { DynamicModule } from './types';
class DynamicModuleContext extends DynamicStore {
constructor(
private readonly module: DynamicModule,
store: StoreApi<any>,
) {
super(store);
}
}
export class ModuleService {
readonly store: StoreApi<Record<string, unknown>> = createStore(
immer(() => {
return {};
}),
);
private instances: ModuleInstance[] = [];
get modules() {
return this.instances.map((v) => v.module);
}
constructor(public loader: (name: string) => Promise<DynamicModule>) {}
async loadModules(names: string[]) {
const last = this.instances.map((v) => v.name);
names = names.filter((v) => last.indexOf(v) < 0);
const loaded = await Promise.all(
names.map(async (name) => {
const module = await this.loader(name);
return { module, name, context: new DynamicModuleContext(module, this.store) };
}),
);
// ensure order
await Promise.all(
loaded.map(async ({ module, context }) => {
if ('onModuleInit' in module) {
await module.onModuleInit?.(context);
}
}),
);
this.instances = [...this.instances, ...loaded];
// wait to next tick, ensure the SetState take effects
await Promise.resolve();
}
async createRoutes(): Promise<RouteObject[]> {
const all = await Promise.all(this.instances.map((v) => v.module?.createRoutes?.(v.context)));
return all.flat().filter((x): x is NonNullable<typeof x> => Boolean(x));
}
}
interface ModuleInstance {
context: DynamicModuleContext;
module: DynamicModule;
name: string;
}