Commit babcff1e authored by nanahira's avatar nanahira

Merge branch 'koishi-beta4'

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