Skip to content

Commit

Permalink
Merge pull request #10 from codibre/decorator-helpers
Browse files Browse the repository at this point in the history
feat: creating decorator helpers
  • Loading branch information
Farenheith authored Sep 24, 2024
2 parents e45bfd1 + cfcaa9d commit f06d147
Show file tree
Hide file tree
Showing 7 changed files with 88 additions and 28 deletions.
43 changes: 43 additions & 0 deletions src/decorator-helpers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import { getProperMetadataTarget } from './internal/get-proper-metadata-target';
import { getClassMetadata } from './meta-info';
import { ClassType, Key } from './meta-type';

export const DEFAULT = Symbol('default');

export function applyPropertyAndMethodsDecorators<T extends object = object>(
cls: ClassType<T>,
propertyAndMethodsDecorators: Partial<
Record<Key<T> | typeof DEFAULT, (MethodDecorator | PropertyDecorator)[]>
>,
) {
const def = propertyAndMethodsDecorators[DEFAULT];
const meta = getClassMetadata(cls);
if (!meta) throw new Error('No class metadata found');
for (const prop of meta?.properties.values()) {
const decorators = propertyAndMethodsDecorators[prop.name] ?? def;
if (decorators) {
Reflect.decorate(
decorators,
getProperMetadataTarget(prop, cls),
prop.name,
);
}
}
for (const prop of meta?.methods.values()) {
const decorators = propertyAndMethodsDecorators[prop.name] ?? def;
if (decorators) {
Reflect.decorate(
decorators,
getProperMetadataTarget(prop, cls),
prop.name,
);
}
}
}

export function applyClassDecorators<T extends object = object>(
cls: ClassType<T>,
decorators: ClassDecorator[],
) {
Reflect.decorate(decorators, cls);
}
2 changes: 1 addition & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
export * from './decorator-helpers';
export * from './meta-info';
export * from './meta-type';

import './plugin/decorators';
import { blockAccess } from './internal';

blockAccess();
8 changes: 8 additions & 0 deletions src/internal/get-proper-metadata-target.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { PropertyMetadata, ClassType, MethodMetadata } from '../meta-type';

export function getProperMetadataTarget<T extends object = object>(
prop: PropertyMetadata<T> | MethodMetadata<T>,
cls: ClassType,
): Object {
return prop.modifiers.static ? cls : cls.prototype;
}
1 change: 1 addition & 0 deletions src/internal/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from './get-proper-metadata-target';
export * from './meta-storage';
31 changes: 18 additions & 13 deletions src/meta-type.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export type ClassType = abstract new (...args: any[]) => unknown;
export type Key = string | symbol;
export type ClassType<T extends object = object> = abstract new (
// eslint-disable-next-line @typescript-eslint/no-explicit-any
...args: any[]
) => T;
export type Key<T extends object = object> = keyof T | keyof ClassType<T>;

export interface ModifiersMetadata {
public: boolean;
Expand All @@ -20,23 +22,26 @@ export interface BaseMetadata {
modifiers: ModifiersMetadata;
}

export interface ConstructorMetadata extends BaseMetadata {
cls: ClassType;
export interface ConstructorMetadata<T extends object = object>
extends BaseMetadata {
cls: ClassType<T>;
args: unknown[];
}
export interface MethodMetadata extends BaseMetadata {
name: Key;
export interface MethodMetadata<T extends object = object>
extends BaseMetadata {
name: Key<T>;
args: unknown[];
returnType: unknown;
propertyDescriptor: PropertyDescriptor;
}
export interface PropertyMetadata extends BaseMetadata {
name: Key;
export interface PropertyMetadata<T extends object = object>
extends BaseMetadata {
name: Key<T>;
type: unknown;
}

export interface ClassMetadata {
ctor: ConstructorMetadata;
properties: Map<Key, PropertyMetadata>;
methods: Map<Key, MethodMetadata>;
export interface ClassMetadata<T extends object = object> {
ctor: ConstructorMetadata<T>;
properties: Map<Key<T>, PropertyMetadata<T>>;
methods: Map<Key<T>, MethodMetadata<T>>;
}
27 changes: 14 additions & 13 deletions src/plugin/decorators.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,25 @@
import { getMetadataStorage } from '../internal';
import {
ClassMetadata,
ClassType,
ConstructorMetadata,
Key,
MethodMetadata,
ModifiersMetadata,
PropertyMetadata,
} from '../meta-type';

const metadata = getMetadataStorage();

function getMeta(prototype: object) {
let ref = metadata.get(prototype);
function getMeta<T extends object = object>(prototype: T) {
let ref = metadata.get(prototype) as ClassMetadata<T> | undefined;
if (!ref) {
ref = {
ctor: undefined as unknown as ConstructorMetadata,
properties: new Map(),
methods: new Map(),
ctor: undefined as unknown as ConstructorMetadata<T>,
properties: new Map<Key<T>, PropertyMetadata>(),
methods: new Map<Key<T>, MethodMetadata>(),
};
metadata.set(prototype, ref);
metadata.set(prototype, ref as unknown as ClassMetadata<object>);
}
return ref;
}
Expand Down Expand Up @@ -45,13 +48,11 @@ export function registerPropertyMetadata(modifiers: ModifiersMetadata) {
};
}

export function registerMethodMetadata(modifiers: ModifiersMetadata) {
return (
prototype: object,
key: Key,
propertyDescriptor: PropertyDescriptor,
) => {
const ref = getMeta(prototype);
export function registerMethodMetadata<T extends object = object>(
modifiers: ModifiersMetadata,
) {
return (prototype: T, key: Key, propertyDescriptor: PropertyDescriptor) => {
const ref = getMeta<T>(prototype);
const returnType = Reflect.getMetadata('design:returntype', prototype, key);
ref.methods.set(key, {
name: key,
Expand Down
4 changes: 3 additions & 1 deletion src/plugin/emitter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ function addRef(
}
}

function* emitPropertyAssignments(obj: Record<Key, boolean>) {
function* emitPropertyAssignments<T extends object = object>(
obj: Record<Key<T>, boolean>,
) {
for (const k in obj) {
if (k in obj) {
yield tsBinary.factory.createPropertyAssignment(
Expand Down

0 comments on commit f06d147

Please sign in to comment.