From c6de7004d28412caa1b9dff03cc5278b1ffeaa17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Valverd=C3=A9?= Date: Sat, 9 Dec 2023 02:22:38 +0100 Subject: [PATCH] Project setup (#1) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sets up project build and CI Co-authored-by: Julien Valverdé Reviewed-on: https://git.jvalver.de/Thilawyn/thilatrait/pulls/1 --- .drone.jsonnet | 93 +++++++++++++++++++++++++++++++++++++++++++++++ bun.lockb | Bin 0 -> 20853 bytes package.json | 33 +++++++++++++++++ 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 ++++++++++++ 10 files changed, 360 insertions(+) create mode 100644 .drone.jsonnet 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/.drone.jsonnet b/.drone.jsonnet new file mode 100644 index 0000000..8b33506 --- /dev/null +++ b/.drone.jsonnet @@ -0,0 +1,93 @@ +local bun_image = "oven/bun:1"; + + +local fetch_step = { + name: "fetch", + image: "alpine/git", + commands: ["git fetch --tags"], +}; + +local install_step = { + name: "install", + image: bun_image, + commands: ["bun install --frozen-lockfile"], +}; + +local lint_step = { + name: "lint", + image: bun_image, + commands: ["bun lint:tsc"], +}; + +local generate_docker_tags_step = { + name: "generate-docker-tags", + image: "git.jvalver.de/thilawyn/drone-better-docker-autotag", +}; + + +[ + // Lint the whole project when not in master, not in a PR nor on a tag + { + kind: "pipeline", + type: "docker", + name: "lint", + + trigger: { + ref: { + exclude: [ + "refs/heads/master", + "refs/pull/**", + "refs/tags/**", + ] + } + }, + + steps: [ + install_step, + lint_step, + ], + }, + + // Build the server and legacy API docker images without publishing them for pull requests + // { + // kind: "pipeline", + // type: "docker", + // name: "build-docker", + + // trigger: { + // ref: { + // include: ["refs/pull/**"] + // } + // }, + + // steps: [ + // fetch_step, + // generate_docker_tags_step, + // build_website_docker_step(false), + // build_legacy_api_docker_step(false), + // ], + // }, + + // Build the server and legacy API docker images and publish them for master and tags + // { + // kind: "pipeline", + // type: "docker", + // name: "build-publish-docker", + + // trigger: { + // ref: { + // include: [ + // "refs/heads/master", + // "refs/tags/**", + // ] + // } + // }, + + // steps: [ + // fetch_step, + // generate_docker_tags_step, + // build_website_docker_step(true), + // build_legacy_api_docker_step(true), + // ], + // }, +] diff --git a/bun.lockb b/bun.lockb new file mode 100755 index 0000000000000000000000000000000000000000..ada479ec1da18794383ac5e5eddefbc73b9585e2 GIT binary patch literal 20853 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 NAu2uE`k(jrKLF@be%Am1 literal 0 HcmV?d00001 diff --git a/package.json b/package.json new file mode 100644 index 0000000..54a9941 --- /dev/null +++ b/package.json @@ -0,0 +1,33 @@ +{ + "name": "thilatrait", + "version": "20231208.0.0", + "type": "module", + "exports": { + ".": { + "import": { + "types": "./dist/lib.d.mts", + "default": "./dist/lib.mjs" + }, + "require": { + "types": "./dist/lib.d.cts", + "default": "./dist/lib.cjs" + } + } + }, + "scripts": { + "build": "rollup -c", + "lint:tsc": "tsc --noEmit", + "clean:cache": "rm -f tsconfig.tsbuildinfo", + "clean:dist": "rm -rf dist", + "clean:node": "rm -rf node_modules" + }, + "dependencies": { + "type-fest": "^4.8.3" + }, + "devDependencies": { + "bun-types": "latest", + "rollup": "^4.7.0", + "rollup-plugin-ts": "^3.4.5", + "typescript": "^5.3.3" + } +} diff --git a/rollup.config.js b/rollup.config.js new file mode 100644 index 0000000..87e9671 --- /dev/null +++ b/rollup.config.js @@ -0,0 +1,22 @@ +import { defineConfig } from "rollup" +import ts from "rollup-plugin-ts" +import pkg from "./package.json" assert { type: "json" } + + +export default defineConfig({ + input: "src/index.ts", + + plugins: [ ts() ], + + output: [ + { + file: pkg.exports["."].import.default, + format: "esm", + }, + + { + file: pkg.exports["."].require.default, + format: "cjs", + }, + ], +}) diff --git a/src/class.ts b/src/class.ts new file mode 100644 index 0000000..8cff9e9 --- /dev/null +++ b/src/class.ts @@ -0,0 +1,68 @@ +import { AbstractClass } from "type-fest" + + +/** + * Copies all own properties and methods (excluding "length" and "prototype") of one class to another class. + * + * @param from The class whose properties and methods are to be copied. + * @param to The class to which the properties and methods are to be copied. + */ +export function copyClassProperties( + from: AbstractClass, + 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"] +}