Commit babcff1e authored by nanahira's avatar nanahira

Merge branch 'koishi-beta4'

parents 46b4cf2d 2d0a1d9d
stages: stages:
- install
- build - build
- deploy - deploy
variables: variables:
GIT_DEPTH: "1" GIT_DEPTH: "1"
build: npm_ci:
stage: build stage: install
tags: tags:
- linux - linux
script: script:
- npm ci - npm ci
artifacts:
paths:
- node_modules
.build_base:
stage: build
tags:
- linux
dependencies:
- npm_ci
build:
extends:
- .build_base
script:
- npm run build - npm run build
artifacts: artifacts:
paths: paths:
- dist/ - dist/
unit-test:
extends:
- .build_base
script:
- npm run test
deploy_npm: deploy_npm:
stage: deploy stage: deploy
dependencies: dependencies:
......
...@@ -8,4 +8,7 @@ ...@@ -8,4 +8,7 @@
Dockerfile Dockerfile
/test /test
/dist/test /dist/test
/src /src
\ No newline at end of file /coverage
/tests
/dist/tests
\ No newline at end of file
This diff is collapsed.
...@@ -6,7 +6,8 @@ ...@@ -6,7 +6,8 @@
"typings": "dist/index.d.ts", "typings": "dist/index.d.ts",
"scripts": { "scripts": {
"lint": "eslint --fix .", "lint": "eslint --fix .",
"build": "tsc" "build": "tsc",
"test": "jest --passWithNoTests"
}, },
"repository": { "repository": {
"type": "git", "type": "git",
...@@ -29,24 +30,46 @@ ...@@ -29,24 +30,46 @@
"license": "MIT", "license": "MIT",
"devDependencies": { "devDependencies": {
"@koishijs/plugin-adapter-onebot": "^4.0.0-beta.2", "@koishijs/plugin-adapter-onebot": "^4.0.0-beta.2",
"@types/jest": "^27.0.3",
"@types/lodash": "^4.14.177",
"@types/node": "^16.11.9", "@types/node": "^16.11.9",
"@typescript-eslint/eslint-plugin": "^4.33.0", "@typescript-eslint/eslint-plugin": "^4.33.0",
"@typescript-eslint/parser": "^4.33.0", "@typescript-eslint/parser": "^4.33.0",
"eslint": "^7.32.0", "eslint": "^7.32.0",
"eslint-config-prettier": "^8.3.0", "eslint-config-prettier": "^8.3.0",
"eslint-plugin-prettier": "^3.4.1", "eslint-plugin-prettier": "^3.4.1",
"koishi": "^4.0.0-beta.3", "jest": "^27.4.3",
"koishi": "^4.0.0-beta.4",
"prettier": "^2.4.1", "prettier": "^2.4.1",
"ts-jest": "^27.0.7",
"typescript": "^4.5.2", "typescript": "^4.5.2",
"ws": "^8.2.3" "ws": "^8.2.3"
}, },
"peerDependencies": { "peerDependencies": {
"koishi": "^4.0.0-beta.3" "koishi": "^4.0.0-beta.4"
}, },
"dependencies": { "dependencies": {
"lodash": "^4.17.21",
"reflect-metadata": "^0.1.13", "reflect-metadata": "^0.1.13",
"schemastery": "^2.0.0", "schemastery": "^2.1.0",
"schemastery-gen": "2.0.2", "schemastery-gen": "^2.2.4",
"typed-reflector": "^1.0.5" "typed-reflector": "^1.0.8"
},
"jest": {
"moduleFileExtensions": [
"js",
"json",
"ts"
],
"rootDir": "tests",
"testRegex": ".*\\.spec\\.ts$",
"transform": {
"^.+\\.(t|j)s$": "ts-jest"
},
"collectCoverageFrom": [
"**/*.(t|j)s"
],
"coverageDirectory": "../coverage",
"testEnvironment": "node"
} }
} }
...@@ -5,6 +5,7 @@ import { ...@@ -5,6 +5,7 @@ import {
DoRegisterConfig, DoRegisterConfig,
EventName, EventName,
GenerateMappingStruct, GenerateMappingStruct,
KoishiAddUsingList,
KoishiCommandDefinition, KoishiCommandDefinition,
KoishiCommandPutDef, KoishiCommandPutDef,
KoishiDoRegister, KoishiDoRegister,
...@@ -17,6 +18,7 @@ import { ...@@ -17,6 +18,7 @@ import {
KoishiSystemInjectSymKeys, KoishiSystemInjectSymKeys,
MetadataMap, MetadataMap,
OnContextFunction, OnContextFunction,
ProvideOptions,
Selection, Selection,
SystemInjectFun, SystemInjectFun,
} from './def'; } from './def';
...@@ -181,7 +183,26 @@ export const PutBot = () => PutSession('bot'); ...@@ -181,7 +183,26 @@ export const PutBot = () => PutSession('bot');
// Service // Service
export function Inject(name?: keyof Context.Services): PropertyDecorator { export function Inject(
name?: keyof Context.Services,
addUsing?: boolean,
): PropertyDecorator;
export function Inject(addUsing?: boolean): PropertyDecorator;
export function Inject(
...args: [(keyof Context.Services | boolean)?, boolean?]
): PropertyDecorator {
let name: keyof Context.Services;
let addUsing = false;
if (args.length === 1) {
if (typeof args[0] === 'boolean') {
addUsing = args[0];
} else {
name = args[0];
}
} else if (args.length >= 2) {
name = args[0] as keyof Context.Services;
addUsing = args[1];
}
return (obj, key) => { return (obj, key) => {
if (!name) { if (!name) {
const functionType = Reflect.getMetadata('design:type', obj, key); const functionType = Reflect.getMetadata('design:type', obj, key);
...@@ -196,6 +217,9 @@ export function Inject(name?: keyof Context.Services): PropertyDecorator { ...@@ -196,6 +217,9 @@ export function Inject(name?: keyof Context.Services): PropertyDecorator {
} }
} }
const serviceName = name || (key as keyof Context.Services); const serviceName = name || (key as keyof Context.Services);
if (addUsing) {
Metadata.appendUnique(KoishiAddUsingList, serviceName)(obj.constructor);
}
const dec = Metadata.set( const dec = Metadata.set(
KoishiServiceInjectSym, KoishiServiceInjectSym,
serviceName, serviceName,
...@@ -207,10 +231,13 @@ export function Inject(name?: keyof Context.Services): PropertyDecorator { ...@@ -207,10 +231,13 @@ export function Inject(name?: keyof Context.Services): PropertyDecorator {
export function Provide( export function Provide(
name: keyof Context.Services, name: keyof Context.Services,
options?: Context.Options, options?: ProvideOptions,
): ClassDecorator { ): ClassDecorator {
Context.service(name, options); Context.service(name);
return Metadata.appendUnique(KoishiServiceProvideSym, name); return Metadata.append(KoishiServiceProvideSym, {
...options,
serviceName: name,
});
} }
const InjectSystem = (fun: SystemInjectFun) => const InjectSystem = (fun: SystemInjectFun) =>
......
...@@ -4,6 +4,7 @@ import { ...@@ -4,6 +4,7 @@ import {
CommandDefinitionFun, CommandDefinitionFun,
DoRegisterConfig, DoRegisterConfig,
OnContextFunction, OnContextFunction,
ProvideDefinition,
SystemInjectFun, SystemInjectFun,
} from './interfaces'; } from './interfaces';
...@@ -18,16 +19,18 @@ export const KoishiServiceInjectSymKeys = 'KoishiServiceInjectSymKeys'; ...@@ -18,16 +19,18 @@ export const KoishiServiceInjectSymKeys = 'KoishiServiceInjectSymKeys';
export const KoishiServiceProvideSym = 'KoishiServiceProvideSym'; export const KoishiServiceProvideSym = 'KoishiServiceProvideSym';
export const KoishiSystemInjectSym = 'KoishiSystemInjectSym'; export const KoishiSystemInjectSym = 'KoishiSystemInjectSym';
export const KoishiSystemInjectSymKeys = 'KoishiSystemInjectSymKeys'; export const KoishiSystemInjectSymKeys = 'KoishiSystemInjectSymKeys';
export const KoishiAddUsingList = 'KoishiAddUsingList';
// metadata map // metadata map
export interface MetadataArrayMap { export interface MetadataArrayMap {
KoishiOnContextScope: OnContextFunction; KoishiOnContextScope: OnContextFunction;
KoishiCommandDefinition: CommandDefinitionFun; KoishiCommandDefinition: CommandDefinitionFun;
KoishiServiceProvideSym: keyof Context.Services; KoishiServiceProvideSym: ProvideDefinition;
KoishiDoRegisterKeys: string; KoishiDoRegisterKeys: string;
KoishiServiceInjectSymKeys: string; KoishiServiceInjectSymKeys: string;
KoishiSystemInjectSymKeys: string; KoishiSystemInjectSymKeys: string;
KoishiAddUsingList: keyof Context.Services;
} }
export interface MetadataMap { export interface MetadataMap {
......
...@@ -147,3 +147,11 @@ export type SystemInjectFun = <T = any>( ...@@ -147,3 +147,11 @@ export type SystemInjectFun = <T = any>(
obj: PluginClass<T>, obj: PluginClass<T>,
pluginMeta: KoishiPluginRegistrationOptions<T>, pluginMeta: KoishiPluginRegistrationOptions<T>,
) => any; ) => any;
export interface ProvideOptions {
immediate?: boolean;
}
export interface ProvideDefinition extends ProvideOptions {
serviceName: keyof Context.Services;
}
...@@ -2,6 +2,7 @@ import { Argv, Command, Context, Schema, User } from 'koishi'; ...@@ -2,6 +2,7 @@ import { Argv, Command, Context, Schema, User } from 'koishi';
import { import {
CommandPutConfig, CommandPutConfig,
DoRegisterConfig, DoRegisterConfig,
KoishiAddUsingList,
KoishiCommandDefinition, KoishiCommandDefinition,
KoishiDoRegister, KoishiDoRegister,
KoishiDoRegisterKeys, KoishiDoRegisterKeys,
...@@ -18,10 +19,12 @@ import { ...@@ -18,10 +19,12 @@ import {
import { reflector } from './meta/meta-fetch'; import { reflector } from './meta/meta-fetch';
import { applySelector } from './utility/utility'; import { applySelector } from './utility/utility';
import { ClassType, SchemaClass } from 'schemastery-gen'; import { ClassType, SchemaClass } from 'schemastery-gen';
import _ from 'lodash';
export interface KoishiPluginRegistrationOptions<T = any> { export interface KoishiPluginRegistrationOptions<T = any> {
name?: string; name?: string;
schema?: Schema<any, T> | Type<T>; schema?: Schema<any, T> | Type<T>;
using?: (keyof Context.Services)[];
} }
export interface PluginClass<T = any> { export interface PluginClass<T = any> {
...@@ -54,14 +57,18 @@ export function KoishiPlugin<T = any>( ...@@ -54,14 +57,18 @@ export function KoishiPlugin<T = any>(
options: KoishiPluginRegistrationOptions<T> = {}, options: KoishiPluginRegistrationOptions<T> = {},
) { ) {
return function < return function <
C extends { new (...args: any[]): any; schema?: Schema; name?: string } C extends {
new (...args: any[]): any;
} & KoishiPluginRegistrationOptions<any>
>(originalClass: C) { >(originalClass: C) {
const addUsingList = reflector.getArray(KoishiAddUsingList, originalClass);
const newClass = class extends originalClass implements PluginClass { const newClass = class extends originalClass implements PluginClass {
static schema = static schema =
options.schema && options.schema &&
((options.schema as Schema).type ((options.schema as Schema).type
? (options.schema as Schema<Partial<T>, T>) ? (options.schema as Schema<Partial<T>, T>)
: SchemaClass(options.schema as ClassType<T>)); : SchemaClass(options.schema as ClassType<T>));
static using = _.uniq([...(options.using || []), ...addUsingList]);
__ctx: Context; __ctx: Context;
__config: T; __config: T;
__pluginOptions: KoishiPluginRegistrationOptions<T>; __pluginOptions: KoishiPluginRegistrationOptions<T>;
...@@ -273,15 +280,15 @@ export function KoishiPlugin<T = any>( ...@@ -273,15 +280,15 @@ export function KoishiPlugin<T = any>(
); );
} }
_handleServiceProvide(connect = true) { _handleServiceProvide(immediate: boolean) {
// console.log(`Handling service provide`); // console.log(`Handling service provide`);
const providingServices = [ const providingServices = [
...reflector.getArray(KoishiServiceProvideSym, originalClass), ...reflector.getArray(KoishiServiceProvideSym, originalClass),
...reflector.getArray(KoishiServiceProvideSym, this), ...reflector.getArray(KoishiServiceProvideSym, this),
]; ].filter((serviceDef) => !serviceDef.immediate === !immediate);
for (const key of providingServices) { for (const key of providingServices) {
// console.log(`Processing ${key}`); // console.log(`Processing ${key}`);
this.__ctx[key] = connect ? (this as any) : null; this.__ctx[key.serviceName] = this as any;
} }
} }
...@@ -291,17 +298,18 @@ export function KoishiPlugin<T = any>( ...@@ -291,17 +298,18 @@ export function KoishiPlugin<T = any>(
if (typeof this.onConnect === 'function') { if (typeof this.onConnect === 'function') {
await this.onConnect(); await this.onConnect();
} }
this._handleServiceProvide(true); this._handleServiceProvide(false);
}); });
this.__ctx.on('disconnect', async () => { this.__ctx.on('disconnect', async () => {
if (typeof this.onDisconnect === 'function') { if (typeof this.onDisconnect === 'function') {
await this.onDisconnect(); await this.onDisconnect();
} }
this._handleServiceProvide(false); // this._handleServiceProvide(false);
}); });
} }
async _initializePluginClass() { async _initializePluginClass() {
this._handleServiceProvide(true);
this._handleSystemInjections(); this._handleSystemInjections();
this._handleServiceInjections(); this._handleServiceInjections();
this._registerAfterInit(); this._registerAfterInit();
......
import { Inject, KoishiPlugin } from '..';
import { Cache, Assets, Bot, Context } from 'koishi';
describe('InjectUsing', () => {
@KoishiPlugin({ using: ['database'] })
class MyPlugin {
@Inject(true)
cache: Cache;
@Inject('assets', true)
assets: Assets;
@Inject('bots')
bots: Bot[];
}
it('Should include injected using services', () => {
const usingList = (MyPlugin as any).using as (keyof Context.Services)[];
expect(usingList).toBeInstanceOf(Array);
expect(usingList.length).toEqual(3);
expect(usingList.includes('database')).toEqual(true);
expect(usingList.includes('assets')).toEqual(true);
expect(usingList.includes('cache')).toEqual(true);
expect(usingList.includes('bots')).toEqual(false);
});
});
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
"include": [ "include": [
"*.ts", "*.ts",
"src/**/*.ts", "src/**/*.ts",
"test/**/*.ts" "test/**/*.ts",
"tests/**/*.ts"
] ]
} }
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