Commit 0bcc28c9 authored by nanahira's avatar nanahira

add template render and @For

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