From ddd33885340fc319d295bd1b074c9dab569376a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Sat, 9 Dec 2023 02:06:43 +0100 Subject: [PATCH] Project build --- bun.lockb | Bin 0 -> 25470 bytes package.json | 32 +++++++++++++++++ rollup.config.js | 22 ++++++++++++ src/class.ts | 68 ++++++++++++++++++++++++++++++++++++ src/index.ts | 3 ++ src/tests.ts | 29 ++++++++++++++++ src/trait.ts | 87 +++++++++++++++++++++++++++++++++++++++++++++++ src/utils.ts | 1 + tsconfig.json | 24 +++++++++++++ 9 files changed, 266 insertions(+) create mode 100755 bun.lockb create mode 100644 package.json create mode 100644 rollup.config.js create mode 100644 src/class.ts create mode 100644 src/index.ts create mode 100644 src/tests.ts create mode 100644 src/trait.ts create mode 100644 src/utils.ts create mode 100644 tsconfig.json diff --git a/bun.lockb b/bun.lockb new file mode 100755 index 0000000000000000000000000000000000000000..2d025d288aef085c8e8875461f05687ca23df81f GIT binary patch literal 25470 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, + to: AbstractClass, +) { + Object.getOwnPropertyNames(from).forEach(name => { + if (name === "length" + || name === "prototype" + ) + return + + Object.defineProperty( + to, + name, + Object.getOwnPropertyDescriptor(from, name) || Object.create(null), + ) + }) + + Object.getOwnPropertyNames(from.prototype).forEach(name => { + Object.defineProperty( + to.prototype, + name, + Object.getOwnPropertyDescriptor(from.prototype, name) || Object.create(null), + ) + }) +} + +export const flattenClass = < + C extends AbstractClass +>(class_: C) => + getInheritanceHierarchy(class_) + .reduce((flattened, current) => { + copyClassProperties(current, flattened) + return flattened + }, class {}) as C + +/** + * Returns an array of classes representing the inheritance hierarchy of a given class constructor. The array includes the given class constructor itself and all its parent classes in the order of inheritance. + * + * @param class_ The class constructor for which to generate the inheritance hierarchy. + * @returns An array of class constructors representing the inheritance hierarchy of `Class`. + */ +export function getInheritanceHierarchy( + class_: AbstractClass +): AbstractClass[] { + const parent = Object.getPrototypeOf(class_) + + return isClass(parent) + ? [...getInheritanceHierarchy(parent), class_] + : [class_] +} + +/** + * Determines whether a given value is a class constructor or not by checking if its `toString` method returns a string matching the pattern of a class definition. + * + * @param el The value to check for class constructor status. + * @returns A boolean indicating whether `el` is a class constructor or not. + */ +export const isClass = (el: { toString: () => string }) => + Boolean(el.toString().match(/^class(?: [.\S]+)?(?: extends [.\S]+)? {[\s\S]*}$/)) diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..46c8360 --- /dev/null +++ b/src/index.ts @@ -0,0 +1,3 @@ +export * from "./class" +export * from "./trait" +export * from "./utils" diff --git a/src/tests.ts b/src/tests.ts new file mode 100644 index 0000000..4520fd6 --- /dev/null +++ b/src/tests.ts @@ -0,0 +1,29 @@ +import { mixTraits } from "./trait" + + +abstract class Identified { + abstract id: ID + + equals(el: Identified) { + return this.id === el.id + } +} + +abstract class ProvideIdentified extends Identified { + id!: ID +} + +abstract class Permissible { + protected permissions: string[] = [] +} + + +class User extends mixTraits( + Identified, + // Identified, + Permissible, +) { + id: bigint = BigInt(-1) +} + +const user = new User() diff --git a/src/trait.ts b/src/trait.ts new file mode 100644 index 0000000..021d69a --- /dev/null +++ b/src/trait.ts @@ -0,0 +1,87 @@ +import { AbstractClass, Class, UnionToIntersection } from "type-fest" +import { copyClassProperties, flattenClass } from "./class" +import { StaticMembers } from "./utils" + + +export type Trait = + AbstractClass + + +// export function applyTrait< +// C extends Class | AbstractClass, +// TraitC extends Trait, +// >( +// class_: C, +// trait: TraitC, +// ) { +// copyClassProperties(trait, class_) + +// return class_ as ( +// (C extends Class +// ? Class< +// InstanceType & InstanceType, +// ConstructorParameters +// > +// : AbstractClass< +// InstanceType & InstanceType, +// ConstructorParameters +// > +// ) & +// StaticMembers & +// StaticMembers +// ) +// } + + +export const extendAndApplyTraits = < + C extends Class | AbstractClass, + Traits extends readonly Trait[], +>( + classToExtend: C, + traits: Traits, +) => + traits.reduce((class_, trait) => { + copyClassProperties(flattenClass(trait), class_) + return class_ + }, class extends classToExtend {}) as ( + AbstractClass< + InstanceType & + UnionToIntersection< + InstanceType + >, + + ConstructorParameters + > & + + StaticMembers< + C & + UnionToIntersection< + Traits[number] + > + > + ) + + +export const mixTraits = < + Traits extends readonly Trait[] +>( + ...traits: Traits +) => + traits.reduce((class_, trait) => { + copyClassProperties(flattenClass(trait), class_) + return class_ + }, class {}) as ( + Trait< + UnionToIntersection< + InstanceType< + Traits[number] + > + > + > & + + StaticMembers< + UnionToIntersection< + Traits[number] + > + > + ) diff --git a/src/utils.ts b/src/utils.ts new file mode 100644 index 0000000..87f5f52 --- /dev/null +++ b/src/utils.ts @@ -0,0 +1 @@ +export type StaticMembers = Pick diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..5470793 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "lib": ["ESNext"], + "module": "esnext", + "target": "esnext", + "moduleResolution": "bundler", + "moduleDetection": "force", + // "allowImportingTsExtensions": true, + // "noEmit": true, + "declaration": true, + "composite": true, + "strict": true, + "downlevelIteration": true, + "skipLibCheck": true, + "jsx": "react-jsx", + "allowSyntheticDefaultImports": true, + "forceConsistentCasingInFileNames": true, + "allowJs": true, + "types": [ + "bun-types" + ] + }, + "include": ["src"] +}