mirror of
https://github.com/tiramisulabs/seyfert.git
synced 2025-07-01 20:46:08 +00:00
fix: mixer, idk, runtimes sucks (#331)
* fix: declared properties * fix: idk mixer
This commit is contained in:
parent
272ddac08b
commit
749ba3e6be
12
package.json
12
package.json
@ -22,14 +22,14 @@
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@biomejs/biome": "1.9.4",
|
||||
"@changesets/cli": "^2.27.11",
|
||||
"@commitlint/cli": "^19.6.1",
|
||||
"@commitlint/config-conventional": "^19.6.0",
|
||||
"@types/node": "^22.10.7",
|
||||
"@changesets/cli": "^2.28.1",
|
||||
"@commitlint/cli": "^19.7.1",
|
||||
"@commitlint/config-conventional": "^19.7.1",
|
||||
"@types/node": "^22.13.5",
|
||||
"husky": "^9.1.7",
|
||||
"lint-staged": "^15.4.1",
|
||||
"lint-staged": "^15.4.3",
|
||||
"typescript": "^5.7.3",
|
||||
"vitest": "^3.0.3"
|
||||
"vitest": "^3.0.6"
|
||||
},
|
||||
"homepage": "https://seyfert.dev",
|
||||
"repository": {
|
||||
|
602
pnpm-lock.yaml
generated
602
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@ -24,6 +24,8 @@ function getNodeDescriptors(c: TypeClass) {
|
||||
const result: Record<string, TypedPropertyDescriptor<unknown> | 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);
|
||||
}
|
||||
@ -51,6 +53,8 @@ export function Mixin<T, C extends TypeClass[]>(...args: C): C[number] & T {
|
||||
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;
|
||||
|
183
tests/mixer.test.mts
Normal file
183
tests/mixer.test.mts
Normal file
@ -0,0 +1,183 @@
|
||||
// test written by claude 🩻
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { mix } from '../lib/deps/mixer';
|
||||
|
||||
describe('mix decorator', () => {
|
||||
// Helper classes for testing
|
||||
class BaseClass {
|
||||
baseProperty = 'baseValue';
|
||||
baseMethod() {
|
||||
return 'base';
|
||||
}
|
||||
|
||||
toString() {
|
||||
return 'from base';
|
||||
}
|
||||
}
|
||||
|
||||
class MixinOne {
|
||||
propertyOne = 1;
|
||||
methodOne() {
|
||||
return 'one';
|
||||
}
|
||||
|
||||
toString() {
|
||||
return 'from one';
|
||||
}
|
||||
}
|
||||
|
||||
class MixinTwo {
|
||||
propertyTwo = 'two';
|
||||
methodTwo() {
|
||||
return 'two';
|
||||
}
|
||||
}
|
||||
|
||||
it('should correctly mix classes and preserve properties', () => {
|
||||
@mix(MixinOne, MixinTwo)
|
||||
class TestClass extends BaseClass {
|
||||
testProperty = 'test';
|
||||
testMethod() {
|
||||
return 'test';
|
||||
}
|
||||
}
|
||||
interface TestClass extends MixinOne, MixinTwo {}
|
||||
const instance = new TestClass();
|
||||
|
||||
// Check properties
|
||||
expect(instance.baseProperty).toBe('baseValue');
|
||||
expect(instance.propertyOne).toBe(1);
|
||||
expect(instance.propertyTwo).toBe('two');
|
||||
expect(instance.testProperty).toBe('test');
|
||||
|
||||
// Check methods
|
||||
expect(instance.baseMethod()).toBe('base');
|
||||
expect(instance.methodOne()).toBe('one');
|
||||
expect(instance.methodTwo()).toBe('two');
|
||||
expect(instance.testMethod()).toBe('test');
|
||||
expect(instance.toString()).toBe('from base');
|
||||
});
|
||||
|
||||
it('should correctly mix classes and preserve the original class name', () => {
|
||||
@mix(MixinOne, MixinTwo)
|
||||
class TestClass extends BaseClass {
|
||||
testMethod() {
|
||||
return 'test';
|
||||
}
|
||||
}
|
||||
interface TestClass extends MixinOne, MixinTwo {}
|
||||
const instance = new TestClass();
|
||||
|
||||
// Check if the class name is preserved
|
||||
expect(TestClass.name).toBe('TestClass');
|
||||
|
||||
// Check if methods from all classes are available
|
||||
expect(instance.baseMethod()).toBe('base');
|
||||
expect(instance.methodOne()).toBe('one');
|
||||
expect(instance.methodTwo()).toBe('two');
|
||||
expect(instance.testMethod()).toBe('test');
|
||||
});
|
||||
|
||||
it('should handle mixing with no additional mixins', () => {
|
||||
@mix()
|
||||
class TestClass extends BaseClass {
|
||||
testMethod() {
|
||||
return 'test';
|
||||
}
|
||||
}
|
||||
|
||||
const instance = new TestClass();
|
||||
|
||||
expect(TestClass.name).toBe('TestClass');
|
||||
expect(instance.baseMethod()).toBe('base');
|
||||
expect(instance.testMethod()).toBe('test');
|
||||
});
|
||||
|
||||
it('should handle mixing with getters and setters', () => {
|
||||
class MixinWithAccessors {
|
||||
private _value = '';
|
||||
|
||||
get value(): string {
|
||||
return this._value;
|
||||
}
|
||||
|
||||
set value(val: string) {
|
||||
this._value = val;
|
||||
}
|
||||
}
|
||||
|
||||
interface TestClass extends MixinWithAccessors {}
|
||||
|
||||
@mix(MixinWithAccessors)
|
||||
class TestClass extends BaseClass {}
|
||||
|
||||
const instance = new TestClass();
|
||||
|
||||
instance.value = 'test';
|
||||
|
||||
expect(instance.value).toBe('test');
|
||||
});
|
||||
|
||||
it('should not override existing methods in the target class', () => {
|
||||
class MixinWithConflict {
|
||||
baseMethod() {
|
||||
return 'mixin';
|
||||
}
|
||||
}
|
||||
|
||||
@mix(MixinWithConflict)
|
||||
class TestClass extends BaseClass {
|
||||
baseMethod() {
|
||||
return 'override';
|
||||
}
|
||||
}
|
||||
|
||||
const instance = new TestClass();
|
||||
expect(instance.baseMethod()).toBe('override');
|
||||
});
|
||||
|
||||
it('should handle constructor arguments', () => {
|
||||
class MixinWithConstructor {
|
||||
constructor(public name: string) {}
|
||||
|
||||
getName() {
|
||||
return this.name;
|
||||
}
|
||||
}
|
||||
interface TestClass extends MixinWithConstructor {}
|
||||
@mix(MixinWithConstructor)
|
||||
class TestClass extends BaseClass {
|
||||
constructor(name: string) {
|
||||
super();
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
name: string;
|
||||
}
|
||||
|
||||
const instance = new TestClass('test');
|
||||
expect(instance.getName()).toBe('test');
|
||||
});
|
||||
|
||||
it('should handle multiple levels of inheritance', () => {
|
||||
class Level1 {
|
||||
level1() {
|
||||
return 'level1';
|
||||
}
|
||||
}
|
||||
|
||||
class Level2 extends Level1 {
|
||||
level2() {
|
||||
return 'level2';
|
||||
}
|
||||
}
|
||||
interface TestClass extends Level1, Level2 {}
|
||||
@mix(Level2)
|
||||
class TestClass extends BaseClass {}
|
||||
|
||||
const instance = new TestClass();
|
||||
|
||||
expect(instance.level1()).toBe('level1');
|
||||
expect(instance.level2()).toBe('level2');
|
||||
});
|
||||
});
|
Loading…
x
Reference in New Issue
Block a user