diff --git a/src/deps/mixer.ts b/src/deps/mixer.ts index 5254878..9149300 100644 --- a/src/deps/mixer.ts +++ b/src/deps/mixer.ts @@ -1,75 +1,30 @@ -/** - * Gets the descriptors of a class. - * @param c The class to get the descriptors of. - * @returns The descriptors of the class. - */ -function getDenoDescriptors(c: TypeClass) { - const protos = [c.prototype]; - - let v = c; - while ((v = Object.getPrototypeOf(v))) { - if (v.prototype) protos.push(v.prototype); +const IgnoredProps = ['constructor', 'prototype', 'name']; +export function copyProperties(target: InstanceType, source: TypeClass) { + const keys = Reflect.ownKeys(source); + for (const key of keys) { + if (IgnoredProps.includes(key as string)) continue; + if (key in target) continue; + const descriptor = Object.getOwnPropertyDescriptor(source, key); + if (descriptor) { + Object.defineProperty(target, key, descriptor); + } } - - return protos.map(x => Object.getOwnPropertyDescriptors(x)); } -/** - * Gets the descriptors of a class. - * @param c The class to get the descriptors of. - * @returns The descriptors of the class. - */ -function getNodeDescriptors(c: TypeClass) { - let proto = c.prototype; - const result: Record | PropertyDescriptor>[] = []; - while (proto) { - const descriptors = Object.getOwnPropertyDescriptors(proto); - // @ts-expect-error this is not a function in all cases - if (descriptors.valueOf.configurable) break; - result.push(descriptors); - proto = Object.getPrototypeOf(proto); - } - return result; -} - -function getDescriptors(c: TypeClass) { - //@ts-expect-error - // biome-ignore lint/correctness/noUndeclaredVariables: - return typeof Deno === 'undefined' ? getNodeDescriptors(c) : getDenoDescriptors(c); -} - -/** - * Mixes a class with other classes. - * @param args The classes to mix. - * @returns The mixed class. - */ -export function Mixin(...args: C): C[number] & T { - const Base = args[0]; +export function Mixin(...mixins: C): C[number] & T { + const Base = mixins[0]; class MixedClass extends Base { - constructor(...constructorArgs: any[]) { - super(...constructorArgs); - - for (const mixin of args.slice(1)) { - const descriptors = getDescriptors(mixin).reverse(); - for (const desc of descriptors) { - // @ts-expect-error - Object.assign(this, new desc.constructor.value(...constructorArgs)); - for (const key in desc) { - if (key === 'constructor') continue; - if (key in MixedClass.prototype) continue; - const descriptor = desc[key]; - - if (descriptor.value) { - // @ts-expect-error - MixedClass.prototype[key] = descriptor.value; - } else if (descriptor.get || descriptor.set) { - Object.defineProperty(MixedClass.prototype, key, { - get: descriptor.get, - set: descriptor.set, - }); - } - } + constructor(...args: any[]) { + super(...args); + for (const mixin of mixins.slice(1)) { + // @ts-expect-error + const mixinInstance = new mixin(...args); + copyProperties(this, mixinInstance); + let proto = Object.getPrototypeOf(mixinInstance); + while (proto && proto !== Object.prototype) { + copyProperties(this, proto); + proto = Object.getPrototypeOf(proto); } } } diff --git a/src/structures/extra/Base.ts b/src/structures/extra/Base.ts index d919c66..1961e5c 100644 --- a/src/structures/extra/Base.ts +++ b/src/structures/extra/Base.ts @@ -3,7 +3,7 @@ import type { UsingClient } from '../../commands'; import { toCamelCase } from '../../common'; /** */ -export abstract class Base { +export class Base { constructor(client: UsingClient) { Object.assign(this, { client }); } diff --git a/tests/mixer.test.mts b/tests/mixer.test.mts index 7bc75a2..cdf21b1 100644 --- a/tests/mixer.test.mts +++ b/tests/mixer.test.mts @@ -138,7 +138,10 @@ describe('mix decorator', () => { it('should handle constructor arguments', () => { class MixinWithConstructor { - constructor(public name: string) {} + name: string; + constructor(name: string) { + this.name = name.slice(0, 2); + } getName() { return this.name;