Commit 3203665d authored by nanahira's avatar nanahira

add @ProvideMixin() and support mixin in @Inject()

parent 6ca0c8f0
...@@ -31,7 +31,7 @@ ...@@ -31,7 +31,7 @@
"typescript": "^4.7.4" "typescript": "^4.7.4"
}, },
"peerDependencies": { "peerDependencies": {
"cordis": "^2.6.0", "cordis": "^2.7.2",
"schemastery": "^3.5.1" "schemastery": "^3.5.1"
} }
}, },
...@@ -1845,12 +1845,12 @@ ...@@ -1845,12 +1845,12 @@
} }
}, },
"node_modules/cordis": { "node_modules/cordis": {
"version": "2.6.0", "version": "2.7.2",
"resolved": "https://registry.npmjs.org/cordis/-/cordis-2.6.0.tgz", "resolved": "https://registry.npmjs.org/cordis/-/cordis-2.7.2.tgz",
"integrity": "sha512-4VUY2x6ufctBr1zYAML3c+b1eXwgY94nkqLP7/icb3QVGXMBJuH4Nztakf6ADVonN5MvgJ06RRawRvmWdx3LxA==", "integrity": "sha512-bM+4P1bhIawrXRLvBMoxfWwSHebm0B3n0sM+T/FCED/iMUnEs6N9Y7lwJ2gDo2fm0wvu4JzU8cuMKrxqroq16Q==",
"peer": true, "peer": true,
"dependencies": { "dependencies": {
"cosmokit": "^1.3.3" "cosmokit": "^1.4.0"
} }
}, },
"node_modules/cosmokit": { "node_modules/cosmokit": {
...@@ -6184,12 +6184,12 @@ ...@@ -6184,12 +6184,12 @@
} }
}, },
"cordis": { "cordis": {
"version": "2.6.0", "version": "2.7.2",
"resolved": "https://registry.npmjs.org/cordis/-/cordis-2.6.0.tgz", "resolved": "https://registry.npmjs.org/cordis/-/cordis-2.7.2.tgz",
"integrity": "sha512-4VUY2x6ufctBr1zYAML3c+b1eXwgY94nkqLP7/icb3QVGXMBJuH4Nztakf6ADVonN5MvgJ06RRawRvmWdx3LxA==", "integrity": "sha512-bM+4P1bhIawrXRLvBMoxfWwSHebm0B3n0sM+T/FCED/iMUnEs6N9Y7lwJ2gDo2fm0wvu4JzU8cuMKrxqroq16Q==",
"peer": true, "peer": true,
"requires": { "requires": {
"cosmokit": "^1.3.3" "cosmokit": "^1.4.0"
} }
}, },
"cosmokit": { "cosmokit": {
......
...@@ -54,7 +54,7 @@ ...@@ -54,7 +54,7 @@
"typescript": "^4.7.4" "typescript": "^4.7.4"
}, },
"peerDependencies": { "peerDependencies": {
"cordis": "^2.6.0", "cordis": "^2.7.2",
"schemastery": "^3.5.1" "schemastery": "^3.5.1"
}, },
"dependencies": { "dependencies": {
......
...@@ -28,16 +28,19 @@ export function Inject(...args: [(string | boolean)?, boolean?]) { ...@@ -28,16 +28,19 @@ export function Inject(...args: [(string | boolean)?, boolean?]) {
return pluginDecorators.Inject(...args); return pluginDecorators.Inject(...args);
} }
export function Internal(): MethodDecorator & PropertyDecorator { export const ProvideMixin = (): MethodDecorator & PropertyDecorator => {
return (obj, key, des?) => { return (obj, key, des?) => {
const cls = obj.constructor as Context.MixinOptions;
const field = des ? 'methods' : 'properties'; const field = des ? 'methods' : 'properties';
if (!cls[field]) { defaultRegistrar.metadata.set('CordisMixin', field, 'CordisMixinKeys')(
cls[field] = []; obj,
} key,
cls[field].push(key); des,
);
}; };
} };
// for backward compatibility
export const Internal = ProvideMixin;
export function Accept( export function Accept(
options?: AcceptOptions, options?: AcceptOptions,
......
...@@ -14,6 +14,7 @@ import Schema from 'schemastery'; ...@@ -14,6 +14,7 @@ import Schema from 'schemastery';
import { PluginRegistrar } from './plugin-def'; import { PluginRegistrar } from './plugin-def';
import { RegisterMeta } from './utility/register-meta'; import { RegisterMeta } from './utility/register-meta';
import { uniq } from './utility/utility'; import { uniq } from './utility/utility';
import MixinOptions = Context.MixinOptions;
declare module 'cordis' { declare module 'cordis' {
interface Context { interface Context {
...@@ -23,6 +24,8 @@ declare module 'cordis' { ...@@ -23,6 +24,8 @@ declare module 'cordis' {
// eslint-disable-next-line @typescript-eslint/no-namespace // eslint-disable-next-line @typescript-eslint/no-namespace
export namespace Registrar { export namespace Registrar {
import MixinOptions = Context.MixinOptions;
export interface MetadataArrayMap { export interface MetadataArrayMap {
CordisRegisterKeys: string; CordisRegisterKeys: string;
CordisContextTransformer: RegisterMeta<ContextTransformer<Context>>; CordisContextTransformer: RegisterMeta<ContextTransformer<Context>>;
...@@ -34,6 +37,7 @@ export namespace Registrar { ...@@ -34,6 +37,7 @@ export namespace Registrar {
CordisPluginInjectKeys: string; CordisPluginInjectKeys: string;
CordisPluginSystemKeys: string; CordisPluginSystemKeys: string;
CordisConfigAcceptors: (ctx: Context) => any; CordisConfigAcceptors: (ctx: Context) => any;
CordisMixinKeys: string;
} }
export interface MetadataMap { export interface MetadataMap {
CordisRegister: MethodMeta<Context>; CordisRegister: MethodMeta<Context>;
...@@ -44,6 +48,7 @@ export namespace Registrar { ...@@ -44,6 +48,7 @@ export namespace Registrar {
CordisPluginFork: PluginRegistrar.PluginClass<Context>; CordisPluginFork: PluginRegistrar.PluginClass<Context>;
CordisPluginReusable: boolean; CordisPluginReusable: boolean;
CordisPluginReactive: boolean; CordisPluginReactive: boolean;
CordisMixin: keyof MixinOptions;
} }
export type DecorateFunctionParam< export type DecorateFunctionParam<
...@@ -288,12 +293,15 @@ export class Registrar<Ctx extends Context> { ...@@ -288,12 +293,15 @@ export class Registrar<Ctx extends Context> {
enumerable: true, enumerable: true,
configurable: true, configurable: true,
get: () => { get: () => {
return this.__ctx[name]; const val = this.__ctx[name];
if (typeof val === 'function') {
return (...args: any[]) => this.__ctx[name](...args);
} else {
return val;
}
}, },
set: (val: any) => { set: (val: any) => {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment // do nothing
// @ts-ignore
this.__ctx[name] = val;
}, },
}); });
} }
...@@ -537,16 +545,34 @@ export class Registrar<Ctx extends Context> { ...@@ -537,16 +545,34 @@ export class Registrar<Ctx extends Context> {
name: string, name: string,
options?: Registrar.ProvideOptions, options?: Registrar.ProvideOptions,
): ClassDecorator => { ): ClassDecorator => {
if (options?.internal) { return (cls) => {
return (cls) => { const mixinSet: { [K in keyof MixinOptions]: Set<string> } = {
Context.service(name, cls); methods: new Set(),
properties: new Set(),
}; };
} const mixinKeys = this.reflector.getArray('CordisMixinKeys', cls);
Context.service(name); for (const key of mixinKeys) {
return this.metadata.append('CordisPluginProvide', { const mixinType = this.reflector.get('CordisMixin', cls, key);
...options, mixinSet[mixinType].add(key);
serviceName: name, }
}); const mixin: MixinOptions = {};
for (const mixinType of ['methods', 'properties'] as const) {
const set = mixinSet[mixinType];
if (set.size) {
mixin[mixinType] = [...set];
}
}
if (options?.internal) {
Object.assign(cls, mixin);
Context.service(name, cls);
return;
}
Context.service(name, mixin);
this.metadata.append('CordisPluginProvide', {
...options,
serviceName: name,
})(cls);
};
}, },
Inject: (...args: [(string | boolean)?, boolean?]): PropertyDecorator => { Inject: (...args: [(string | boolean)?, boolean?]): PropertyDecorator => {
let name: string; let name: string;
......
import { Inject, Internal, Provide } from '../src/decorators'; import { Internal, Provide } from '../src/decorators';
import { DefinePlugin, StarterPlugin } from './utility/decorators'; import { DefinePlugin, StarterPlugin } from './utility/decorators';
import { LifecycleEvents } from '../src/plugin-def';
import { Context } from 'cordis'; import { Context } from 'cordis';
declare module 'cordis' { declare module 'cordis' {
...@@ -23,7 +22,7 @@ class MyInternalService extends StarterPlugin() { ...@@ -23,7 +22,7 @@ class MyInternalService extends StarterPlugin() {
} }
} }
describe('Apply and Connect in koishi-thirdeye', () => { describe('Internal service', () => {
let app: Context; let app: Context;
beforeEach(() => { beforeEach(() => {
app = new Context(); app = new Context();
......
import { Inject, Internal, Provide } from '../src/decorators';
import { DefinePlugin, StarterPlugin } from './utility/decorators';
import { Context } from 'cordis';
declare module 'cordis' {
// eslint-disable-next-line @typescript-eslint/no-namespace
interface Context {
foo: string;
bar(): number;
bar2(): number;
bazz: MyProvider;
myBarConsumer: MyConsumer;
}
}
@Provide('bazz', { immediate: true })
@DefinePlugin()
class MyProvider extends StarterPlugin() {
barValue = 5;
@Internal()
foo: string;
@Internal()
bar() {
return this.barValue;
}
}
@Provide('myBarConsumer', { immediate: true })
@DefinePlugin()
class MyConsumer extends StarterPlugin() {
@Inject()
bar: Context['bar'];
}
describe('Mixin', () => {
let app: Context;
beforeEach(() => {
app = new Context();
app.plugin(MyProvider);
app.plugin(MyConsumer);
});
it('should load provide mixin', () => {
app.foo = 'msg';
expect(app.foo).toBe('msg');
expect(app.bar()).toBe(5);
expect(app.bazz.foo).toBe('msg');
expect(app.bazz.bar()).toBe(5);
expect(app.myBarConsumer.bar()).toBe(5);
});
});
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