Skip to content

Commit

Permalink
feat: new command parse
Browse files Browse the repository at this point in the history
  • Loading branch information
BIYUEHU committed Jan 31, 2024
1 parent 0966b25 commit 85f1ea0
Show file tree
Hide file tree
Showing 42 changed files with 1,631 additions and 799 deletions.
158 changes: 158 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<script>
Function.prototype.before = function (fn) {
const self = this;
return function () {
fn();
self.apply(this, arguments);
};
};
Function.prototype.after = function (fn) {
const self = this;
return function () {
self.apply(this, arguments);
fn();
};
};

class Symbols {
static container = Symbol.for('kotori.context.container');
static containerKey = (prop) => Symbol.for(`kotori.context.container.${prop}`);
}

class Context {
static handler() {
return {
get(target, prop, receiver) {
if (prop === 'identity') return Reflect.get(target, Symbols.set).get(target);
return Reflect.get(target, prop, receiver);
}
};
}

[Symbols.container] = new Map();
[Symbols.set] = new WeakMap();

root;

constructor(config = {}) {
this.root = config.root || this;
}

get(prop) {
return this[Symbols.container].get(prop);
}

inject(prop, value) {
const key = value ? Symbols.containerKey(prop) : prop;
if (value) {
if (this[Symbols.container].has(key)) return;
this.provide(key, value);
} else {
if (!this[Symbols.container].has(key)) return;
}
if (key in this) return;
this[prop] = this.get(key);
}

provide(prop, value) {
if (this[Symbols.container].has(prop)) return;
this[Symbols.container].set(prop, value);
}

mixin(prop, keys) {
if (!this[Symbols.container].has(prop)) return;
keys.forEach((key) => {
if (key in this) return;
const instance = this.get(prop);
if (!instance) return;
if (!(key in instance)) return;
if (typeof this.get(prop)[key] === 'function') {
this[key] = this.get(prop)[key].bind(this.get(prop));
return;
}
this[key] = this.get(prop)[key];
});
}

extends(meta = {}, identity = 'sub') {
const metaHandle = meta;
Object.keys(metaHandle).forEach((key) => {
if (typeof this[key] === 'function') delete metaHandle[key];
});
const ctx = Object.assign(new Context(this.root), this, meta, { identity });
ctx[Symbols.container].forEach((value, key) => {
if (!('ctx' in value)) {
ctx[Symbols.container].set(key, value);
return;
}
const instance = Object.assign(value, { ctx });
ctx[Symbols.container].set(key, instance);
});
return ctx;
}

proxy() {
return new Proxy(this, Context.handler);
}
}
class Event {
#stack = new Set();
heart = 114514;
ctx;
constructor(ctx) {
this.ctx = ctx;
}
on(callback) {
this.#stack.add(callback);
}
emit() {
// console.log(this.heart);
console.log(`identity ->`, this.ctx.identity);
this.#stack.forEach((callback) => callback());
}
}
class Kotori extends Context {
options;
constructor(options) {
super();
const event = new Event(this);
this.provide('event', event);
this.mixin('event', ['emit', 'on']);
this.options = options;
}
}
const ctx = new Kotori({ v: 1 });
// event.emit();
// ctx.inject('event').emit();
ctx.emit();
// console.log(ctx.options);
ctx.on(() => console.log(''));
// ctx.emit();
const ctx2 = ctx.extends({ options: 2 });
// console.log(ctx.options, ctx2.options);
// ctx2.inject('event').on(() => console.log('nb from ctx2 with inject'));
ctx2.emit();
ctx2.extends({}, 23).on(() => console.log('by ctx2'));
ctx2.extends({}, 233).get('event').emit();
ctx2.extends({}, 2333).emit();
// const ctx3 = ctx2.extends();
// const ctx4 = ctx.extends();
// ctx2.inject('event').emit();
// console.log(ctx3.options, ctx4.options, ctx4);
console.log(ctx.identity, ctx2.get('event'), ctx2[Symbols.container]);

/* ctx2.provide('cc', { f: () => console.log('cc') }); */
const ctx3 = ctx2.extends({ options: 3, emit: 1 }, 'ccc');
ctx2.inject('cc', () => console.log('cc'));
console.log(ctx2, ctx, ctx3, ctx2.extends());
</script>
</body>
</html>
5 changes: 5 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,5 +49,10 @@
"packageManager": "pnpm@8.7.4",
"engines": {
"node": ">=17.9.0"
},
"dependencies": {
"@types/minimist": "^1.2.5",
"minimist": "^1.2.8",
"tsukiko": "^1.2.1"
}
}
2 changes: 2 additions & 0 deletions packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
"@kotori-bot/i18n": "workspace:^",
"@kotori-bot/logger": "workspace:^",
"@kotori-bot/tools": "workspace:^",
"@types/minimist": "^1.2.5",
"minimist": "^1.2.8",
"tsukiko": "^1.2.1"
}
}
31 changes: 31 additions & 0 deletions packages/core/src/base/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { join } from 'path';
import { loadConfig } from '@kotori-bot/tools';
import { KotoriConfig as KotoriConfigType, PackageInfo, kotoriConfigSchema, packageInfoSchema } from '../types';
import { CoreError } from '../utils/errror';

type ConfigType = KotoriConfigType & { pkg: PackageInfo };

export class KotoriConfig implements ConfigType {
readonly pkg: PackageInfo;

readonly baseDir: KotoriConfigType['baseDir'];

readonly config: KotoriConfigType['config'];

readonly options: KotoriConfigType['options'];

constructor(config?: KotoriConfigType) {
const info = loadConfig(join(__dirname, '../../package.json')) as unknown;
if (!info || Object.values(info).length === 0) throw new CoreError('Cannot find kotori-bot package.json');
const result = packageInfoSchema.parseSafe(info);
if (!result.value) throw new CoreError(`File package.json format error: ${result.error.message}`);
this.pkg = result.data;
const handle = kotoriConfigSchema.parseSafe(config);
if (!handle.value) throw new CoreError('Unexpected error in parsing config (baseDir,kotori,options)');
this.baseDir = handle.data.baseDir;
this.config = handle.data.config;
this.options = handle.data.options;
}
}

export default KotoriConfig;
73 changes: 15 additions & 58 deletions packages/core/src/base/core.ts
Original file line number Diff line number Diff line change
@@ -1,63 +1,20 @@
import { loadConfig, obj } from '@kotori-bot/tools';
import path from 'path';
import { Parser } from 'tsukiko';
import { DEFAULT_LANG } from '@kotori-bot/i18n';
import {
type BaseDir,
type GlobalConfig,
type GlobalOptions,
type KotoriConfig,
type PackageInfo,
packageInfoSchema,
kotoriConfigSchema,
type ServiceConstructor
} from '../types';
import type Api from '../components/api';
import { CoreError } from '../utils/errror';
import { DEFAULT_COMMAND_PREFIX, DEFAULT_ENV, DEFAULT_MODULES_DIR, DEFAULT_ROOT_DIR } from '../consts';
import { Context } from '../context';
import KotoriConfig from './config';
import Events from './events';
import Modules from './modules';

export const defaultConfig = {
baseDir: {
root: path.resolve(DEFAULT_ROOT_DIR),
modules: path.resolve(DEFAULT_MODULES_DIR)
},
config: {
global: {
lang: DEFAULT_LANG,
'command-prefix': DEFAULT_COMMAND_PREFIX
},
adapter: {}
},
options: {
env: DEFAULT_ENV
}
};

export class Core {
protected readonly serviceStack: obj<[ServiceConstructor, Parser<unknown>?]> = {};

protected readonly botStack: obj<Api[]> = {};

readonly baseDir: BaseDir;

readonly config: GlobalConfig;

readonly options: GlobalOptions;

readonly package: PackageInfo;
declare module '../context' {
interface Context extends KotoriConfig {}
}

export default class Kotori extends Context {
constructor(config?: KotoriConfig) {
const info = loadConfig(path.join(__dirname, '../../package.json')) as unknown;
if (!info || Object.values(info).length === 0) throw new CoreError('Cannot find kotori-bot package.json');
const result = packageInfoSchema.parseSafe(info);
if (!result.value) throw new CoreError(`File package.json format error: ${result.error.message}`);
this.package = result.data;
const handle = kotoriConfigSchema.parseSafe(config);
if (!handle.value) throw new CoreError('Unexpected error in parsing config (basedir,kotori,options)');
this.baseDir = handle.data!.baseDir;
this.config = handle.data!.config;
this.options = handle.data!.options;
super();
this.provide('config', new KotoriConfig(config));
this.mixin('config', ['pkg', 'baseDir', 'config', 'options']);
this.provide('events', new Events());
this.mixin('events', ['emit', 'on', 'once', 'off', 'offAll']);
this.provide('modules', new Modules(this));
this.mixin('modules', ['load', 'dispose']);
}
}

export default Core;
9 changes: 9 additions & 0 deletions packages/core/src/base/events.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { Events as EventsOrigin } from '@kotori-bot/tools';
import { EventsList } from '../types';

declare module '../context' {
interface Context extends EventsOrigin<EventsList> {}
}

export const Events = EventsOrigin<EventsList>;
export default Events;

0 comments on commit 85f1ea0

Please sign in to comment.