Commit fa24b321 authored by nanahira's avatar nanahira

add @Accept and @Reactive

parent b71fc081
...@@ -33,7 +33,7 @@ ...@@ -33,7 +33,7 @@
"typescript": "^4.7.4" "typescript": "^4.7.4"
}, },
"peerDependencies": { "peerDependencies": {
"cordis": "^2.1.0", "cordis": "^2.4.4",
"schemastery": "^3.5.1" "schemastery": "^3.5.1"
} }
}, },
...@@ -1853,18 +1853,18 @@ ...@@ -1853,18 +1853,18 @@
} }
}, },
"node_modules/cordis": { "node_modules/cordis": {
"version": "2.1.0", "version": "2.4.4",
"resolved": "https://registry.npmjs.org/cordis/-/cordis-2.1.0.tgz", "resolved": "https://registry.npmjs.org/cordis/-/cordis-2.4.4.tgz",
"integrity": "sha512-GPdkYAEIRzBQlTW4eHoKgD+efXCiubujIC2MoqXpEwwaW1Y2ifxWoPNT+JSYrSH3fbtA70QV05rIP+L0lkB9CQ==", "integrity": "sha512-mXkb7jTEpAwqY5zbE03Tmm2QN5oQs96NJ7VSYWv4yZZyFnkv+GuYwt5s58u97OXDxHKiqbj59iHDW8LIAf/M4A==",
"peer": true, "peer": true,
"dependencies": { "dependencies": {
"cosmokit": "^1.2.1" "cosmokit": "^1.3.3"
} }
}, },
"node_modules/cosmokit": { "node_modules/cosmokit": {
"version": "1.2.1", "version": "1.3.3",
"resolved": "https://registry.npmjs.org/cosmokit/-/cosmokit-1.2.1.tgz", "resolved": "https://registry.npmjs.org/cosmokit/-/cosmokit-1.3.3.tgz",
"integrity": "sha512-BTn7vRr31WUwX7Tq8Q/r+Qz+LPKTE3vA0d7xzVaYNes2NPvGPmIWiljYP0m/PIrdpqLLtdHpY1zGNr+OwDhA7A==", "integrity": "sha512-jpEp5zDrGbycu8AdaCjmNNr2J617NAFvAU6GM8o/dJP2/pe80qTbPoflqyJzAuMhiaC6QCXlBP0KCusTsDemPQ==",
"peer": true "peer": true
}, },
"node_modules/cross-spawn": { "node_modules/cross-spawn": {
...@@ -6204,18 +6204,18 @@ ...@@ -6204,18 +6204,18 @@
} }
}, },
"cordis": { "cordis": {
"version": "2.1.0", "version": "2.4.4",
"resolved": "https://registry.npmjs.org/cordis/-/cordis-2.1.0.tgz", "resolved": "https://registry.npmjs.org/cordis/-/cordis-2.4.4.tgz",
"integrity": "sha512-GPdkYAEIRzBQlTW4eHoKgD+efXCiubujIC2MoqXpEwwaW1Y2ifxWoPNT+JSYrSH3fbtA70QV05rIP+L0lkB9CQ==", "integrity": "sha512-mXkb7jTEpAwqY5zbE03Tmm2QN5oQs96NJ7VSYWv4yZZyFnkv+GuYwt5s58u97OXDxHKiqbj59iHDW8LIAf/M4A==",
"peer": true, "peer": true,
"requires": { "requires": {
"cosmokit": "^1.2.1" "cosmokit": "^1.3.3"
} }
}, },
"cosmokit": { "cosmokit": {
"version": "1.2.1", "version": "1.3.3",
"resolved": "https://registry.npmjs.org/cosmokit/-/cosmokit-1.2.1.tgz", "resolved": "https://registry.npmjs.org/cosmokit/-/cosmokit-1.3.3.tgz",
"integrity": "sha512-BTn7vRr31WUwX7Tq8Q/r+Qz+LPKTE3vA0d7xzVaYNes2NPvGPmIWiljYP0m/PIrdpqLLtdHpY1zGNr+OwDhA7A==", "integrity": "sha512-jpEp5zDrGbycu8AdaCjmNNr2J617NAFvAU6GM8o/dJP2/pe80qTbPoflqyJzAuMhiaC6QCXlBP0KCusTsDemPQ==",
"peer": true "peer": true
}, },
"cross-spawn": { "cross-spawn": {
......
...@@ -55,7 +55,7 @@ ...@@ -55,7 +55,7 @@
"typescript": "^4.7.4" "typescript": "^4.7.4"
}, },
"peerDependencies": { "peerDependencies": {
"cordis": "^2.1.0", "cordis": "^2.4.4",
"schemastery": "^3.5.1" "schemastery": "^3.5.1"
}, },
"dependencies": { "dependencies": {
......
import { defaultRegistrar } from '../registrar'; import { defaultRegistrar } from '../registrar';
import { Context } from 'cordis'; import { AcceptOptions, Context } from 'cordis';
import { PluginRegistrar } from '../plugin-def'; import { PluginRegistrar } from '../plugin-def';
import { TypedMethodDecorator } from '../def';
const pluginDecorators = defaultRegistrar.pluginDecorators(); const pluginDecorators = defaultRegistrar.pluginDecorators();
...@@ -8,11 +9,13 @@ export const { ...@@ -8,11 +9,13 @@ export const {
PluginName, PluginName,
PluginSchema, PluginSchema,
Reusable, Reusable,
Reactive,
Provide, Provide,
InjectContext, InjectContext,
InjectConfig, InjectConfig,
InjectParent, InjectParent,
Caller, Caller,
AcceptWhen,
} = pluginDecorators; } = pluginDecorators;
export const Fork = <Ctx extends Context>( export const Fork = <Ctx extends Context>(
...@@ -35,3 +38,28 @@ export function Internal(): MethodDecorator & PropertyDecorator { ...@@ -35,3 +38,28 @@ export function Internal(): MethodDecorator & PropertyDecorator {
cls[field].push(key); 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 { ...@@ -25,7 +25,6 @@ export namespace PluginRegistrar {
export interface PluginMeta<Ctx extends Context, T = any> { export interface PluginMeta<Ctx extends Context, T = any> {
__ctx: Ctx; __ctx: Ctx;
__config: T;
__pluginOptions: PluginRegistrationOptions<Ctx, T>; __pluginOptions: PluginRegistrationOptions<Ctx, T>;
__promisesToWaitFor: Promise<void>[]; __promisesToWaitFor: Promise<void>[];
__disposables: (() => void)[]; __disposables: (() => void)[];
......
import 'reflect-metadata'; import 'reflect-metadata';
import { Context, GetEvents } from 'cordis'; import { AcceptOptions, Context, GetEvents } from 'cordis';
import { ClassType, SchemaClass } from 'schemastery-gen'; import { ClassType, SchemaClass } from 'schemastery-gen';
import { MetadataSetter, Reflector } from 'typed-reflector'; import { MetadataSetter, Reflector } from 'typed-reflector';
import type { import type {
...@@ -33,6 +33,7 @@ export namespace Registrar { ...@@ -33,6 +33,7 @@ export namespace Registrar {
CordisPluginProvide: ProvideDefinition<Context>; CordisPluginProvide: ProvideDefinition<Context>;
CordisPluginInjectKeys: string; CordisPluginInjectKeys: string;
CordisPluginSystemKeys: string; CordisPluginSystemKeys: string;
CordisConfigAcceptors: (ctx: Context) => any;
} }
export interface MetadataMap { export interface MetadataMap {
CordisRegister: MethodMeta<Context>; CordisRegister: MethodMeta<Context>;
...@@ -42,6 +43,7 @@ export namespace Registrar { ...@@ -42,6 +43,7 @@ export namespace Registrar {
CordisPluginPredefineName: string; CordisPluginPredefineName: string;
CordisPluginFork: PluginRegistrar.PluginClass<Context>; CordisPluginFork: PluginRegistrar.PluginClass<Context>;
CordisPluginReusable: boolean; CordisPluginReusable: boolean;
CordisPluginReactive: boolean;
} }
export type DecorateFunctionParam< export type DecorateFunctionParam<
...@@ -186,6 +188,13 @@ export class Registrar<Ctx extends Context> { ...@@ -186,6 +188,13 @@ export class Registrar<Ctx extends Context> {
// for override // for override
} }
getConfigAcceptors(configClass: any) {
if (!configClass) {
return [];
}
return this.reflector.getArray('CordisConfigAcceptors', configClass);
}
plugin<T>( plugin<T>(
options?: PluginRegistrar.PluginRegistrationOptions<Ctx, T>, options?: PluginRegistrar.PluginRegistrationOptions<Ctx, T>,
): <C extends PluginRegistrar.PluginClass<Ctx, T>>( ): <C extends PluginRegistrar.PluginClass<Ctx, T>>(
...@@ -239,8 +248,11 @@ export class Registrar<Ctx extends Context> { ...@@ -239,8 +248,11 @@ export class Registrar<Ctx extends Context> {
return reflector.get('CordisPluginReusable', newClass); return reflector.get('CordisPluginReusable', newClass);
} }
static get reactive() {
return reflector.get('CordisPluginReactive', newClass);
}
__ctx: Ctx; __ctx: Ctx;
__config: T;
__pluginOptions: PluginRegistrar.PluginRegistrationOptions<Ctx, T>; __pluginOptions: PluginRegistrar.PluginRegistrationOptions<Ctx, T>;
__registrar: RegistrarAspect<Ctx>; __registrar: RegistrarAspect<Ctx>;
__promisesToWaitFor: Promise<any>[]; __promisesToWaitFor: Promise<any>[];
...@@ -302,6 +314,11 @@ export class Registrar<Ctx extends Context> { ...@@ -302,6 +314,11 @@ export class Registrar<Ctx extends Context> {
}); });
} }
_handleConfigAcceptors() {
const acceptors = _this.getConfigAcceptors(newClass.Config);
acceptors.forEach((acceptor) => acceptor(this.__ctx));
}
_getProvidingServices() { _getProvidingServices() {
return reflector.getArray('CordisPluginProvide', this); return reflector.getArray('CordisPluginProvide', this);
} }
...@@ -366,7 +383,6 @@ export class Registrar<Ctx extends Context> { ...@@ -366,7 +383,6 @@ export class Registrar<Ctx extends Context> {
} }
this.__disposables.forEach((dispose) => dispose()); this.__disposables.forEach((dispose) => dispose());
delete this.__ctx; delete this.__ctx;
delete this.__config;
delete this.__pluginOptions; delete this.__pluginOptions;
delete this.__registrar; delete this.__registrar;
delete this.__promisesToWaitFor; delete this.__promisesToWaitFor;
...@@ -379,6 +395,7 @@ export class Registrar<Ctx extends Context> { ...@@ -379,6 +395,7 @@ export class Registrar<Ctx extends Context> {
this._handleServiceInjections(); this._handleServiceInjections();
this.__registrar.performTopActions(this.__ctx); this.__registrar.performTopActions(this.__ctx);
this._registerDeclarations(); this._registerDeclarations();
this._handleConfigAcceptors();
if (typeof this.onApply === 'function') { if (typeof this.onApply === 'function') {
this.onApply(); this.onApply();
} }
...@@ -389,13 +406,12 @@ export class Registrar<Ctx extends Context> { ...@@ -389,13 +406,12 @@ export class Registrar<Ctx extends Context> {
constructor(...args: any[]) { constructor(...args: any[]) {
const originalCtx: Ctx = args[0]; const originalCtx: Ctx = args[0];
const config = args[1]; const config = originalCtx.config;
const ctx = _this const ctx = _this
.aspect(newClass, config) .aspect(newClass, config)
.getScopeContext(originalCtx); .getScopeContext(originalCtx);
super(ctx, config, ...args.slice(2)); super(ctx, config, ...args.slice(2));
this.__ctx = ctx; this.__ctx = ctx;
this.__config = config;
this.__pluginOptions = options; this.__pluginOptions = options;
this.__registrar = _this.aspect(this, config); this.__registrar = _this.aspect(this, config);
this.__promisesToWaitFor = []; this.__promisesToWaitFor = [];
...@@ -513,6 +529,8 @@ export class Registrar<Ctx extends Context> { ...@@ -513,6 +529,8 @@ export class Registrar<Ctx extends Context> {
this.metadata.set('CordisPluginPredefineSchema', schema), this.metadata.set('CordisPluginPredefineSchema', schema),
Reusable: (reusable = true) => Reusable: (reusable = true) =>
this.metadata.set('CordisPluginReusable', reusable), this.metadata.set('CordisPluginReusable', reusable),
Reactive: (reactive = true) =>
this.metadata.set('CordisPluginReactive', reactive),
Fork: (fork: PluginRegistrar.PluginClass<Ctx>) => Fork: (fork: PluginRegistrar.PluginClass<Ctx>) =>
this.metadata.set('CordisPluginFork', fork), this.metadata.set('CordisPluginFork', fork),
Provide: ( Provide: (
...@@ -561,7 +579,7 @@ export class Registrar<Ctx extends Context> { ...@@ -561,7 +579,7 @@ export class Registrar<Ctx extends Context> {
}, },
InjectSystem, InjectSystem,
InjectContext: () => InjectSystem((obj) => obj.__ctx), InjectContext: () => InjectSystem((obj) => obj.__ctx),
InjectConfig: () => InjectSystem((obj) => obj.__config), InjectConfig: () => InjectSystem((obj) => obj.__ctx.config),
InjectParent: () => InjectSystem((obj) => obj.__ctx.__parent), InjectParent: () => InjectSystem((obj) => obj.__ctx.__parent),
Caller: () => Caller: () =>
InjectSystem((obj) => { InjectSystem((obj) => {
...@@ -571,6 +589,21 @@ export class Registrar<Ctx extends Context> { ...@@ -571,6 +589,21 @@ export class Registrar<Ctx extends Context> {
DefinePlugin: <T>( DefinePlugin: <T>(
options?: PluginRegistrar.PluginRegistrationOptions<Ctx, T>, options?: PluginRegistrar.PluginRegistrationOptions<Ctx, T>,
) => this.plugin(options), ) => 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