Commit fa24b321 authored by nanahira's avatar nanahira

add @Accept and @Reactive

parent b71fc081
......@@ -33,7 +33,7 @@
"typescript": "^4.7.4"
},
"peerDependencies": {
"cordis": "^2.1.0",
"cordis": "^2.4.4",
"schemastery": "^3.5.1"
}
},
......@@ -1853,18 +1853,18 @@
}
},
"node_modules/cordis": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/cordis/-/cordis-2.1.0.tgz",
"integrity": "sha512-GPdkYAEIRzBQlTW4eHoKgD+efXCiubujIC2MoqXpEwwaW1Y2ifxWoPNT+JSYrSH3fbtA70QV05rIP+L0lkB9CQ==",
"version": "2.4.4",
"resolved": "https://registry.npmjs.org/cordis/-/cordis-2.4.4.tgz",
"integrity": "sha512-mXkb7jTEpAwqY5zbE03Tmm2QN5oQs96NJ7VSYWv4yZZyFnkv+GuYwt5s58u97OXDxHKiqbj59iHDW8LIAf/M4A==",
"peer": true,
"dependencies": {
"cosmokit": "^1.2.1"
"cosmokit": "^1.3.3"
}
},
"node_modules/cosmokit": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/cosmokit/-/cosmokit-1.2.1.tgz",
"integrity": "sha512-BTn7vRr31WUwX7Tq8Q/r+Qz+LPKTE3vA0d7xzVaYNes2NPvGPmIWiljYP0m/PIrdpqLLtdHpY1zGNr+OwDhA7A==",
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/cosmokit/-/cosmokit-1.3.3.tgz",
"integrity": "sha512-jpEp5zDrGbycu8AdaCjmNNr2J617NAFvAU6GM8o/dJP2/pe80qTbPoflqyJzAuMhiaC6QCXlBP0KCusTsDemPQ==",
"peer": true
},
"node_modules/cross-spawn": {
......@@ -6204,18 +6204,18 @@
}
},
"cordis": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/cordis/-/cordis-2.1.0.tgz",
"integrity": "sha512-GPdkYAEIRzBQlTW4eHoKgD+efXCiubujIC2MoqXpEwwaW1Y2ifxWoPNT+JSYrSH3fbtA70QV05rIP+L0lkB9CQ==",
"version": "2.4.4",
"resolved": "https://registry.npmjs.org/cordis/-/cordis-2.4.4.tgz",
"integrity": "sha512-mXkb7jTEpAwqY5zbE03Tmm2QN5oQs96NJ7VSYWv4yZZyFnkv+GuYwt5s58u97OXDxHKiqbj59iHDW8LIAf/M4A==",
"peer": true,
"requires": {
"cosmokit": "^1.2.1"
"cosmokit": "^1.3.3"
}
},
"cosmokit": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/cosmokit/-/cosmokit-1.2.1.tgz",
"integrity": "sha512-BTn7vRr31WUwX7Tq8Q/r+Qz+LPKTE3vA0d7xzVaYNes2NPvGPmIWiljYP0m/PIrdpqLLtdHpY1zGNr+OwDhA7A==",
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/cosmokit/-/cosmokit-1.3.3.tgz",
"integrity": "sha512-jpEp5zDrGbycu8AdaCjmNNr2J617NAFvAU6GM8o/dJP2/pe80qTbPoflqyJzAuMhiaC6QCXlBP0KCusTsDemPQ==",
"peer": true
},
"cross-spawn": {
......
......@@ -55,7 +55,7 @@
"typescript": "^4.7.4"
},
"peerDependencies": {
"cordis": "^2.1.0",
"cordis": "^2.4.4",
"schemastery": "^3.5.1"
},
"dependencies": {
......
import { defaultRegistrar } from '../registrar';
import { Context } from 'cordis';
import { AcceptOptions, Context } from 'cordis';
import { PluginRegistrar } from '../plugin-def';
import { TypedMethodDecorator } from '../def';
const pluginDecorators = defaultRegistrar.pluginDecorators();
......@@ -8,11 +9,13 @@ export const {
PluginName,
PluginSchema,
Reusable,
Reactive,
Provide,
InjectContext,
InjectConfig,
InjectParent,
Caller,
AcceptWhen,
} = pluginDecorators;
export const Fork = <Ctx extends Context>(
......@@ -35,3 +38,28 @@ export function Internal(): MethodDecorator & PropertyDecorator {
cls[field].push(key);
};
}
export function Accept(
options?: AcceptOptions,
): ClassDecorator &
TypedMethodDecorator<(...args: any[]) => boolean> &
PropertyDecorator {
return (obj, key?, des?) => {
if (!key) {
// Class decorator
return defaultRegistrar.metadata.append('CordisConfigAcceptors', (ctx) =>
ctx.accept(undefined, options),
)(obj);
}
if (des) {
// Method decorator
return defaultRegistrar.metadata.append('CordisConfigAcceptors', (ctx) =>
ctx.accept((c) => c[key](), options),
)(obj.constructor);
}
// Property decorator
return defaultRegistrar.metadata.append('CordisConfigAcceptors', (ctx) =>
ctx.accept([key as string], undefined, options),
)(obj.constructor);
};
}
......@@ -25,7 +25,6 @@ export namespace PluginRegistrar {
export interface PluginMeta<Ctx extends Context, T = any> {
__ctx: Ctx;
__config: T;
__pluginOptions: PluginRegistrationOptions<Ctx, T>;
__promisesToWaitFor: Promise<void>[];
__disposables: (() => void)[];
......
import 'reflect-metadata';
import { Context, GetEvents } from 'cordis';
import { AcceptOptions, Context, GetEvents } from 'cordis';
import { ClassType, SchemaClass } from 'schemastery-gen';
import { MetadataSetter, Reflector } from 'typed-reflector';
import type {
......@@ -33,6 +33,7 @@ export namespace Registrar {
CordisPluginProvide: ProvideDefinition<Context>;
CordisPluginInjectKeys: string;
CordisPluginSystemKeys: string;
CordisConfigAcceptors: (ctx: Context) => any;
}
export interface MetadataMap {
CordisRegister: MethodMeta<Context>;
......@@ -42,6 +43,7 @@ export namespace Registrar {
CordisPluginPredefineName: string;
CordisPluginFork: PluginRegistrar.PluginClass<Context>;
CordisPluginReusable: boolean;
CordisPluginReactive: boolean;
}
export type DecorateFunctionParam<
......@@ -186,6 +188,13 @@ export class Registrar<Ctx extends Context> {
// for override
}
getConfigAcceptors(configClass: any) {
if (!configClass) {
return [];
}
return this.reflector.getArray('CordisConfigAcceptors', configClass);
}
plugin<T>(
options?: PluginRegistrar.PluginRegistrationOptions<Ctx, T>,
): <C extends PluginRegistrar.PluginClass<Ctx, T>>(
......@@ -239,8 +248,11 @@ export class Registrar<Ctx extends Context> {
return reflector.get('CordisPluginReusable', newClass);
}
static get reactive() {
return reflector.get('CordisPluginReactive', newClass);
}
__ctx: Ctx;
__config: T;
__pluginOptions: PluginRegistrar.PluginRegistrationOptions<Ctx, T>;
__registrar: RegistrarAspect<Ctx>;
__promisesToWaitFor: Promise<any>[];
......@@ -302,6 +314,11 @@ export class Registrar<Ctx extends Context> {
});
}
_handleConfigAcceptors() {
const acceptors = _this.getConfigAcceptors(newClass.Config);
acceptors.forEach((acceptor) => acceptor(this.__ctx));
}
_getProvidingServices() {
return reflector.getArray('CordisPluginProvide', this);
}
......@@ -366,7 +383,6 @@ export class Registrar<Ctx extends Context> {
}
this.__disposables.forEach((dispose) => dispose());
delete this.__ctx;
delete this.__config;
delete this.__pluginOptions;
delete this.__registrar;
delete this.__promisesToWaitFor;
......@@ -379,6 +395,7 @@ export class Registrar<Ctx extends Context> {
this._handleServiceInjections();
this.__registrar.performTopActions(this.__ctx);
this._registerDeclarations();
this._handleConfigAcceptors();
if (typeof this.onApply === 'function') {
this.onApply();
}
......@@ -389,13 +406,12 @@ export class Registrar<Ctx extends Context> {
constructor(...args: any[]) {
const originalCtx: Ctx = args[0];
const config = args[1];
const config = originalCtx.config;
const ctx = _this
.aspect(newClass, config)
.getScopeContext(originalCtx);
super(ctx, config, ...args.slice(2));
this.__ctx = ctx;
this.__config = config;
this.__pluginOptions = options;
this.__registrar = _this.aspect(this, config);
this.__promisesToWaitFor = [];
......@@ -513,6 +529,8 @@ export class Registrar<Ctx extends Context> {
this.metadata.set('CordisPluginPredefineSchema', schema),
Reusable: (reusable = true) =>
this.metadata.set('CordisPluginReusable', reusable),
Reactive: (reactive = true) =>
this.metadata.set('CordisPluginReactive', reactive),
Fork: (fork: PluginRegistrar.PluginClass<Ctx>) =>
this.metadata.set('CordisPluginFork', fork),
Provide: (
......@@ -561,7 +579,7 @@ export class Registrar<Ctx extends Context> {
},
InjectSystem,
InjectContext: () => InjectSystem((obj) => obj.__ctx),
InjectConfig: () => InjectSystem((obj) => obj.__config),
InjectConfig: () => InjectSystem((obj) => obj.__ctx.config),
InjectParent: () => InjectSystem((obj) => obj.__ctx.__parent),
Caller: () =>
InjectSystem((obj) => {
......@@ -571,6 +589,21 @@ export class Registrar<Ctx extends Context> {
DefinePlugin: <T>(
options?: PluginRegistrar.PluginRegistrationOptions<Ctx, T>,
) => this.plugin(options),
AcceptWhen: this.decorateMethod(
'accept',
(
{ ctx },
fun: (config: any) => boolean,
keys?: string[],
options?: AcceptOptions,
) => {
if (keys?.length) {
ctx.accept(keys, fun, options);
} else {
ctx.accept(fun, options);
}
},
),
};
}
......
import { RegisterSchema, SchemaProperty } from 'schemastery-gen';
import { Accept } from '../src/decorators';
import { DefinePlugin, StarterPlugin, UseEvent } from './utility/decorators';
import { Context } from 'cordis';
import { defaultRegistrar } from '../src/registrar';
@RegisterSchema()
class Config {
updated = false;
@SchemaProperty()
@Accept()
name: string;
@SchemaProperty()
age: number;
@Accept()
onUpdate() {
this.updated = true;
return false;
}
}
@DefinePlugin()
class MyPlugin extends StarterPlugin(Config) {
@UseEvent('hi')
onHi() {
return `My name is ${this.config.name} and I'm ${this.config.age} years old`;
}
}
describe('accept', () => {
it('should have all types of acceptors', async () => {
const acceptors = defaultRegistrar.getConfigAcceptors(Config);
expect(acceptors.length).toBe(2);
});
it('should handle reactive plugin', async () => {
const app = new Context();
const state = app.plugin(MyPlugin, {
name: 'Yuzu',
age: 18,
updated: false,
});
await app.start();
expect(app.bail('hi')).toBe(`My name is Yuzu and I'm 18 years old`);
expect(state.config.updated).toBe(false);
state.update({ name: 'Miya', age: 19 });
expect(state.config.updated).toBe(true);
expect(app.bail('hi')).toBe(`My name is Miya and I'm 19 years old`);
});
});
import { Context } from 'cordis';
import { RegisterSchema, SchemaProperty } from 'schemastery-gen';
import { Reactive } from '../src/decorators';
import { DefinePlugin, StarterPlugin, UseEvent } from './utility/decorators';
declare module 'cordis' {
interface Events {
hello(): string;
hi(): string;
}
}
@RegisterSchema()
class Config {
@SchemaProperty()
name: string;
getName() {
return this.name;
}
}
@Reactive()
@DefinePlugin()
class MyPlugin extends StarterPlugin(Config) {
@UseEvent('hello')
onHello() {
return `My name is ${this.config.getName()}`;
}
@UseEvent('hi')
onHi() {
return `My name is ${this.config.name}`;
}
}
describe('reactive', () => {
it('should handle reactive plugin', async () => {
const app = new Context();
app.plugin(MyPlugin, { name: 'Yuzu' });
await app.start();
expect(app.bail('hello')).toBe('My name is Yuzu');
expect(app.bail('hi')).toBe('My name is Yuzu');
});
});
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