Commit 3d377b5a authored by nanahira's avatar nanahira

add parent plugin

parent 9184765c
......@@ -5,5 +5,6 @@ export * from './src/registrar';
export * from './src/registrar-aspect';
export * from './src/plugin-factory';
export * from './src/utility/get-schema';
export * from './src/utility/parent-plugin';
export * from 'schemastery-gen';
import { PluginRegistrar } from '../plugin-def';
import PluginClass = PluginRegistrar.PluginClass;
import { Context } from 'cordis';
export type Awaitable<T> = [T] extends [Promise<unknown>] ? T : T | Promise<T>;
export type TypedMethodDecorator<F extends (...args: any[]) => any> = <
......
......@@ -50,6 +50,17 @@ export namespace PluginRegistrar {
export type PluginDefinition<Ctx extends Context, T extends Plugin = any> =
| PluginDefinitionExact<Ctx, T>
| PluginDefinitionName;
export type ClassPluginConfig<P extends PluginClass<Context>> =
P extends PluginClass<Context, infer C> ? C : never;
export type ExactClassPluginConfig<P extends PluginClass<Context>> =
P extends PluginClass<Context, any, { config: infer IC }>
? IC
: ClassPluginConfig<P>;
export type ContextFromPlugin<P extends PluginClass<Context>> =
P extends PluginClass<infer Ctx> ? Ctx : never;
}
export function PluginDef(
......
import { Context } from 'cordis';
import { LifecycleEvents, PluginRegistrar } from '../plugin-def';
import { TypeFromClass } from '../def';
import { BasePlugin } from '../registrar';
import { Fork, PluginSchema } from '../decorators';
import { ClassType } from 'schemastery-gen';
export function ParentPlugin<PC extends PluginRegistrar.PluginClass<Context>>(
child: PC,
) {
const plugin = class ParentPluginBase
extends BasePlugin<
PluginRegistrar.ContextFromPlugin<PC>,
PluginRegistrar.ExactClassPluginConfig<PC>,
PluginRegistrar.ClassPluginConfig<PC>
>
implements LifecycleEvents
{
onFork(instance: TypeFromClass<PC>): void | Promise<void> {}
onForkDisconnect(instance: TypeFromClass<PC>): void | Promise<void> {}
};
const schema = child['Config'] as ClassType<any>;
if (schema) {
PluginSchema(schema)(plugin);
}
Fork(child)(plugin);
return plugin;
}
export function ParentPluginSet<
PC extends PluginRegistrar.PluginClass<Context>,
>(child: PC) {
return class ParentPluginSet extends ParentPlugin(child) {
instances = new Set<TypeFromClass<PC>>();
onFork(instance: TypeFromClass<PC>) {
this.instances.add(instance);
}
onForkDisconnect(instance: TypeFromClass<PC>) {
this.instances.delete(instance);
}
};
}
export function ParentPluginMap<
PC extends PluginRegistrar.PluginClass<Context>,
R,
>(child: PC, classifyFunction: (instance: TypeFromClass<PC>) => R) {
return class ParentPluginMapBase extends ParentPlugin(child) {
instances = new Map<R, TypeFromClass<PC>>();
onFork(instance: TypeFromClass<PC>) {
this.instances.set(classifyFunction(instance), instance);
}
onForkDisconnect(instance: TypeFromClass<PC>) {
this.instances.delete(classifyFunction(instance));
}
};
}
import { RegisterSchema, SchemaProperty } from 'schemastery-gen';
import { DefinePlugin, StarterPlugin } from './utility/decorators';
import { ParentPluginMap } from '../src/utility/parent-plugin';
import { Prop } from '../src/def';
import { Apply, InjectParent, Provide, Reusable } from '../src/decorators';
import { Context } from 'cordis';
declare module 'cordis' {
interface Context {
forkTest: MyPlugin;
}
}
@RegisterSchema()
class Config {
@SchemaProperty()
name: string;
getName() {
return this.name;
}
}
@DefinePlugin()
class ChildPlugin extends StarterPlugin(Config) {
@InjectParent()
parent: Prop<MyPlugin>;
@Apply()
increase() {
this.parent.loadCount++;
// console.log('fork loaded: ', this.parent.loadCount);
}
}
@Provide('forkTest', { immediate: true })
@DefinePlugin()
class MyPlugin extends ParentPluginMap(ChildPlugin, (p) => p.config.getName()) {
loadCount = 0;
isParent = true;
@Apply()
onLoad() {
// console.log('load', this.config);
}
}
@Reusable()
@DefinePlugin()
class MyReusablePlugin extends StarterPlugin(Config) {
@Apply()
onLoad() {
this.ctx.root['count']++;
}
}
describe('Fork', () => {
let app: Context;
beforeEach(async () => {
app = new Context();
await app.start();
app['count'] = 0;
});
it('should fork a plugin', async () => {
// console.log('before 1');
app.plugin(MyPlugin, { name: 'a' });
// console.log('after 1: ' + app.forkTest.loadCount);
const myPlugin = app.forkTest;
expect(app.forkTest.config.getName()).toEqual('a');
expect(app.forkTest.instances.get('a').config.getName()).toEqual('a');
// console.log(myPlugin.instances.get('a').parent);
// console.log(myPlugin);
expect(myPlugin.instances.get('a').parent === myPlugin).toBe(true);
expect(app.forkTest.instances.get('b')).toBeUndefined();
expect(app.forkTest.loadCount).toBe(1);
// console.log('before 2: ' + app.forkTest.loadCount);
app.plugin(MyPlugin, { name: 'b' });
// console.log('after 2: ' + app.forkTest.loadCount);
expect(app.forkTest.instances.get('b').config.getName()).toEqual('b');
// console.log(myPlugin.instances.get('b').parent);
// console.log(myPlugin);
expect(myPlugin.instances.get('b').parent === myPlugin).toBe(true);
expect(app.forkTest.loadCount).toBe(2);
// console.log('before 3: ' + app.forkTest.loadCount);
app.plugin(MyPlugin, { name: 'c' });
// console.log('after 3: ' + app.forkTest.loadCount);
expect(app.forkTest.instances.get('c').config.getName()).toEqual('c');
// console.log(myPlugin.instances.get('c').parent);
// console.log(myPlugin);
expect(myPlugin.instances.get('c').parent === myPlugin).toBe(true);
expect(app.forkTest.loadCount).toBe(3);
});
it('it should work on reusable', async () => {
expect(app['count']).toBe(0);
app.plugin(MyReusablePlugin, { name: 'a' });
expect(app['count']).toBe(1);
app.plugin(MyReusablePlugin, { name: 'b' });
expect(app['count']).toBe(2);
});
});
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment