Commit 0bcc28c9 authored by nanahira's avatar nanahira

add template render and @For

parent 19fceeb4
......@@ -11,7 +11,7 @@
"dependencies": {
"@types/koa": "^2.13.4",
"@types/koa__router": "^8.0.11",
"koishi-decorators": "^2.0.1",
"koishi-decorators": "^2.0.2",
"lodash": "^4.17.21",
"minato-decorators": "^2.0.6",
"reflect-metadata": "^0.1.13",
......@@ -4773,18 +4773,19 @@
}
},
"node_modules/koishi-decorators": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/koishi-decorators/-/koishi-decorators-2.0.1.tgz",
"integrity": "sha512-dDrBS4Su1NtmrkIeCdcCKsC6uaBYTRTspDdeeoPLOMuSJf6fqqXwxa2qEosvG5475MXs9SaNQDaKjh0gq/NY6A==",
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/koishi-decorators/-/koishi-decorators-2.0.2.tgz",
"integrity": "sha512-a7n/nVq693krWaYsdz85b/cLZXQUb+/BhVAVAQFlGult27ub9PhonG4qXpDwRD5RIIQoSJfca4YTrNHpzL84cA==",
"dependencies": {
"@types/koa": "^2.13.4",
"@types/koa__router": "^8.0.11",
"lodash": "^4.17.21",
"mustache": "^4.2.0",
"reflect-metadata": "^0.1.13",
"typed-reflector": "^1.0.10"
},
"peerDependencies": {
"koishi": "^4.6.0"
"koishi": "^4.7.0"
}
},
"node_modules/leven": {
......@@ -5025,6 +5026,14 @@
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
},
"node_modules/mustache": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/mustache/-/mustache-4.2.0.tgz",
"integrity": "sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==",
"bin": {
"mustache": "bin/mustache"
}
},
"node_modules/natural-compare": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
......@@ -10515,13 +10524,14 @@
}
},
"koishi-decorators": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/koishi-decorators/-/koishi-decorators-2.0.1.tgz",
"integrity": "sha512-dDrBS4Su1NtmrkIeCdcCKsC6uaBYTRTspDdeeoPLOMuSJf6fqqXwxa2qEosvG5475MXs9SaNQDaKjh0gq/NY6A==",
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/koishi-decorators/-/koishi-decorators-2.0.2.tgz",
"integrity": "sha512-a7n/nVq693krWaYsdz85b/cLZXQUb+/BhVAVAQFlGult27ub9PhonG4qXpDwRD5RIIQoSJfca4YTrNHpzL84cA==",
"requires": {
"@types/koa": "^2.13.4",
"@types/koa__router": "^8.0.11",
"lodash": "^4.17.21",
"mustache": "^4.2.0",
"reflect-metadata": "^0.1.13",
"typed-reflector": "^1.0.10"
}
......@@ -10712,6 +10722,11 @@
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
},
"mustache": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/mustache/-/mustache-4.2.0.tgz",
"integrity": "sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ=="
},
"natural-compare": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
......
......@@ -124,8 +124,13 @@ export const PluginSchema = (schema: Schema | ClassType<any>) =>
export const PluginName = (name: string) =>
Metadata.set('KoishiPredefineName', name);
export const If = <T>(func: Condition<boolean, T>): MethodDecorator =>
Metadata.append('KoishiIf', func);
export const If = <T>(
func: Condition<boolean, T, [Record<string, any>]>,
): MethodDecorator => Metadata.append('KoishiIf', func);
export const For = <T>(
func: Condition<Iterable<Record<string, any>>, T, [Record<string, any>]>,
): MethodDecorator => Metadata.append('KoishiFor', func);
export const UseModel = (...models: ModelClassType[]): ClassDecorator =>
TopLevelAction((ctx) => {
......
......@@ -19,7 +19,12 @@ export interface MetadataArrayMap {
KoishiSystemInjectSymKeys: string;
KoishiAddUsingList: keyof Context.Services;
KoishiPartialUsing: keyof Context.Services;
KoishiIf: Condition<boolean>;
KoishiIf: Condition<boolean, any, [Record<string, any>]>;
KoishiFor: Condition<
Iterable<Record<string, any>>,
any,
[Record<string, any>]
>;
}
export interface MetadataMap {
......
......@@ -15,10 +15,9 @@ export interface ProvideDefinition extends ProvideOptions {
serviceName: keyof Context.Services;
}
export type Condition<R, T = any> = (
export type Condition<R, T = any, Ext extends any[] = []> = (
o: T,
config: T extends { config: infer C } ? C : any,
ctx: Context,
...ext: Ext
) => R;
export interface Instances<T> {
......
import { Context, Plugin, Schema, WebSocketLayer } from 'koishi';
import {
Condition,
KoishiAddUsingList,
KoishiPartialUsing,
KoishiServiceInjectSym,
......@@ -121,8 +122,12 @@ export function DefinePlugin<T = any>(
}
}
_registerDeclarationsProcess(methodKey: keyof C & string, ctx: Context) {
const result = this.__registrar.register(ctx, methodKey, false);
_registerDeclarationsProcess(
methodKey: keyof C & string,
ctx: Context,
view: Record<string, any> = {},
) {
const result = this.__registrar.register(ctx, methodKey, false, view);
if (result?.type === 'ws') {
const layer = result.result as WebSocketLayer;
ctx.on('dispose', () => layer.close());
......@@ -134,19 +139,17 @@ export function DefinePlugin<T = any>(
}
}
_registerDeclarationsFor(methodKey: keyof C & string) {
_registerDeclarationsResolving(
methodKey: keyof C & string,
view: Record<string, any> = {},
) {
const conditions = reflector.getArray('KoishiIf', this, methodKey);
if (conditions.some((condition) => !condition(this, view))) return;
const ctx = this.__registrar.getScopeContext(
this.__ctx,
methodKey,
false,
);
const conditions = reflector.getArray('KoishiIf', this, methodKey);
if (
conditions.some(
(condition) => !condition(this, this.__config as any, this.__ctx),
)
)
return;
const partialUsing = reflector.getArray(
KoishiPartialUsing,
this,
......@@ -158,12 +161,38 @@ export function DefinePlugin<T = any>(
name,
using: partialUsing,
apply: (innerCtx) =>
this._registerDeclarationsProcess(methodKey, innerCtx),
this._registerDeclarationsProcess(methodKey, innerCtx, view),
};
ctx.plugin(innerPlugin);
} else {
this._registerDeclarationsProcess(methodKey, ctx);
this._registerDeclarationsProcess(methodKey, ctx, view);
}
}
_registerDeclarationsWithStack(
methodKey: keyof C & string,
stack: Condition<
Iterable<Record<string, any>>,
any,
[Record<string, any>]
>[],
existing: Record<string, any> = {},
) {
if (!stack.length) {
return this._registerDeclarationsResolving(methodKey, existing);
}
const [iter, ...rest] = stack;
for (const view of iter(this, existing)) {
this._registerDeclarationsWithStack(methodKey, rest, {
...existing,
...view,
});
}
}
_registerDeclarationsFor(methodKey: keyof C & string) {
const stack = reflector.getArray('KoishiFor', this, methodKey);
return this._registerDeclarationsWithStack(methodKey, stack);
}
_registerDeclarations() {
......@@ -240,7 +269,7 @@ export function DefinePlugin<T = any>(
this.__ctx = ctx;
this.__config = config;
this.__pluginOptions = options;
this.__registrar = new Registrar(this, originalClass);
this.__registrar = new Registrar(this, originalClass, config);
this.__pluginsToWaitFor = [];
this._initializePluginClass();
}
......
import { DefinePlugin } from '../src/register';
import { UseCommand } from 'koishi-decorators';
import { If } from '../src/decorators';
import { PutValue, UseCommand } from 'koishi-decorators';
import { For, If } from '../src/decorators';
import { App } from 'koishi';
import { BasePlugin } from '../src/base-plugin';
@DefinePlugin()
class MyPlugin extends BasePlugin<{ foo: boolean; bar: boolean }> {
@If<MyPlugin>((o, config, ctx) => config.foo)
@If<MyPlugin>((o) => o.config.foo)
@UseCommand('foo')
foo() {
return 'foo';
}
@If<MyPlugin>((o, config, ctx) => config.bar)
@If<MyPlugin>((o) => o.config.bar)
@UseCommand('bar')
bar() {
return 'bar';
}
}
@DefinePlugin()
class MyPlugin2 extends BasePlugin<{
prefix: string;
commands: { name: string; return: string }[];
}> {
@For<MyPlugin2>(({ config }) => config.commands)
@UseCommand('{{name}}')
onCommand(
@PutValue('{{return}}') returnValue: string,
@PutValue('{{prefix}}') prefix: string,
) {
return prefix + returnValue;
}
}
describe('It should register conditionally', () => {
it('register command on condition', async () => {
it('registers command on condition', async () => {
const app = new App();
app.plugin(MyPlugin, { foo: true, bar: false });
await app.start();
......@@ -29,4 +44,20 @@ describe('It should register conditionally', () => {
expect(await commandFoo.execute({})).toBe('foo');
expect(await commandBar.execute({})).toBeFalsy();
});
it('iterates commands on condition', async () => {
const app = new App();
app.plugin(MyPlugin2, {
commands: [
{ name: 'foo', return: 'bar' },
{ name: 'bar', return: 'baz' },
],
prefix: '> ',
});
await app.start();
const commandFoo = app.command('foo');
const commandBar = app.command('bar');
expect(await commandFoo.execute({})).toBe('> bar');
expect(await commandBar.execute({})).toBe('> baz');
});
});
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