First check-in of the code from the Obsidian Prj project.

This commit is contained in:
2024-08-14 19:40:52 +02:00
parent 1341427590
commit 6c4db19926
14 changed files with 495 additions and 0 deletions

76
src/decorators/Inject.ts Normal file
View File

@@ -0,0 +1,76 @@
import { DIContainer } from '../DIContainer';
import { InitDelegate } from '../types/InitDelegate';
/**
* A decorator to inject a dependency from a DI (Dependency Injection) container.
* The dependency is lazily evaluated when the property is accessed for the first time.
* This can help avoid issues like circular dependencies and not-found dependencies.
* @template ClassType The type of the property to be injected.
* @param identifier The identifier used to resolve the dependency from the DI container.
* @param init An optional initializer function to transform the dependency before injection.
* @param necessary Indicates if the dependency is necessary.
* - If `true`, an error will be thrown if the dependency cannot be resolved.
* - If `false`, `undefined` will be returned if the dependency cannot be resolved.
* @returns A decorator function to be applied on the class property.
* @see {@link DIContainer}
* @example
* ```ts
* class MyClass {
* \@Inject<MyDependency>('MyDependencyIdentifier')
* private myDependency!: MyDependency;
* }
* ```
* @example
* ```ts
* class MyClass {
* \@Inject('ILogger_', (x: ILogger_) => x.getLogger('Tags'), false)
* private _logger?: ILogger;
* }
* ```
*/
export function Inject<T, U>(
identifier: string,
init?: InitDelegate<T, U>,
necessary = true,
) {
return function (target: unknown, propertyKey: string | symbol): void {
// Unique symbol to store the private property
const privatePropertyKey: unique symbol = Symbol();
// Get the DI container instance
const diContainer = DIContainer.getInstance();
// Function to evaluate the dependency lazily
// to avoid circular dependencies, not found dependencies, etc.
const evaluate = (): T | undefined => {
return diContainer.resolve<T>(identifier, necessary);
};
// Define the property
Object.defineProperty(target, propertyKey, {
get() {
// If the property is not defined, evaluate the dependency
if (!this.hasOwnProperty(privatePropertyKey)) {
if (init) {
try {
this[privatePropertyKey] = init(evaluate() as T);
} catch (error) {
if (necessary) {
throw error;
}
}
} else {
this[privatePropertyKey] = evaluate();
}
}
return this[privatePropertyKey];
},
// Not necessary to set the property
// set(value: PropertieType) {
// this[privatePropertyKey] = value;
// },
enumerable: true,
configurable: false,
});
};
}

View File

@@ -0,0 +1,28 @@
import { DIContainer } from '../DIContainer';
/**
* A decorator to register a class in the DI (Dependency Injection) container.
* @template TargetType The type of the class to be registered.
* @param identifier The identifier used to register the class in the DI container.
* @param deprecated If true, the dependency is deprecated => a warning
* is logged when the dependency is resolved.
* @returns A function that is applied as a decorator to the class.
* @example
* ```ts
* \@Register('MyClassIdentifier')
* class MyClass {
* // ...
* }
* ```
*/
export function Register<
TargetType extends new (...args: unknown[]) => InstanceType<TargetType>,
>(identifier: string, deprecated?: boolean) {
return function (constructor: TargetType, ...args: unknown[]): void {
// Get the instance of the DI container
const diContainer = DIContainer.getInstance();
// Register the class in the DI container
diContainer.register(identifier, constructor, deprecated);
};
}

View File

@@ -0,0 +1,70 @@
import { DIContainer } from '../DIContainer';
import { InitDelegate } from '../types/InitDelegate';
/**
* A decorator to register an instance of a class in the DI (Dependency Injection) container.
* The instance is created only when it is first needed (Lazy Initialization).
* @template TargetType The type of the class whose instance is to be registered.
* @param identifier The identifier used to register the instance in the DI container.
* @param init An optional initializer function which get the constructor of the class
* as input and returns an instance of the class.
* @returns A function that is applied as a decorator to the class.
* @example
* ```ts
* \@RegisterInstance('MyClassInstanceIdentifier', arg1, arg2)
* class MyClass {
* // ...
* }
* ```
*/
export function RegisterInstance<
TargetType extends new (..._args: unknown[]) => InstanceType<TargetType>,
>(
identifier: string,
init?: InitDelegate<
TargetType & { new (..._args: unknown[]): InstanceType<TargetType> },
InstanceType<TargetType>
>,
) {
return function (constructor: TargetType, ...args: unknown[]): void {
// Get the instance of the DI container
const diContainer = DIContainer.getInstance();
// Create a proxy to instantiate the class when needed (Lazy Initialization)
let lazyProxy: unknown = new Proxy(
{},
{
get(target, prop, receiver) {
let instance: InstanceType<TargetType>;
if (init) {
instance = init(constructor);
} else {
instance = new constructor(...args);
}
lazyProxy = instance;
// Return the requested property of the instance
return instance[prop as keyof InstanceType<TargetType>];
},
set(target, prop, value, receiver) {
let instance: InstanceType<TargetType>;
if (init) {
instance = init(constructor);
} else {
instance = new constructor(...args);
}
lazyProxy = instance;
// Set the requested property of the instance
return (instance[prop as keyof InstanceType<TargetType>] =
value);
},
},
);
// Register the lazy proxy in the DI container
diContainer.register(identifier, lazyProxy);
};
}