0.3.0 • Published 4 months ago

athlete-core v0.3.0

Weekly downloads
-
License
MIT
Repository
github
Last release
4 months ago

Athlete Documentation

A lightweight DI framework.
No decorators or string tokens.
JS/TS friendly.

Usage

import { Athlete } from 'athlete-core';

const framework = Athlete(); // or const framework = new Athlete();
interface IFramework {
  inject<T>(token: Token<T, []>): IFramework;
  inject<T, A extends any[]>(token: Token<T, A>, dependencies: Dependencies<A>): IFramework;
  injectFactory<T>(token: Token<T, []>): IFramework;
  injectFactory<T, A extends any[]>(token: Token<T, A>, dependencies: Dependencies<A>): IFramework;
  injectModule<T extends IModule>(token: Token<T, []>): IFramework;
  injectModule<T extends IModule, A extends any[]>(
    token: Token<T, A>,
    dependencies: PartialDependencies<A>
  ): IFramework;
  buildContainer(): IContainer;
}

framework is used to configure dependencies.

import { Athlete } from 'athlete-core';

const container = Athlete().buildContainer();
interface IContainer {
  resolveInstance<T>(token: Token<T>): T;
  executeCommand<T extends ICommand>(token: Token<T, []>): IContainer;
  executeCommand<T extends ICommand, A extends any[]>(
    token: Token<T, A>,
    dependencies: PartialDependencies<A>
  ): IContainer;
}

container is used to get instances.

Methods

  • inject: Inject a class or function as a singleton.
class ServiceA {}

function ServiceB(serviceA: ServiceA) {}

Athlete().inject(ServiceB, [ServiceA]).inject(ServiceA).buildContainer();

You can call injects in any order.

  • injectFactory: Inject a class or function as a factory (new instance each time).
class Logger {}

class ServiceA {
  constructor(readonly logger: Logger) {}
}

function ServiceB(serviceA: ServiceA, logger: Logger) {}

Athlete()
  .inject(ServiceA, [Logger])
  .inject(ServiceB, [ServiceA, Logger])
  .injectFactory(Logger)
  .buildContainer();

Logger will be instantiated twice. A new instance for each service.

  • ResolveInstance: Method for getting an instance.
class ServiceA {}

function ServiceB(serviceA: ServiceA) {}

const serviceBInstance = Athlete()
  .inject(ServiceB, [ServiceA])
  .inject(ServiceA)
  .buildContainer()
  .resolveInstance(ServiceB);
  • injectModule: Inject a module into the framework.

The IModule interface defines a method export that is called to inject a module into the framework. The method takes an instance of IModuleFramework, which contains methods for injecting dependencies.

interface IModule {
  export(injector: IInjector): void;
}
class ServiceA {}

function ServiceB(serviceA: ServiceA) {}

class ServiceAModule implements IModule {
  readonly SERVICE_A_TOKEN = ServiceA;

  export(injector: IInjector): void {
    injector.inject(this.SERVICE_A_TOKEN);
  }
}

class ServiceBModule implements IModule {
  constructor(readonly serviceA: ServiceA) {}

  readonly SERVICE_B_TOKEN = ServiceB;

  export(injector: IInjector): void {
    injector.inject(this.SERVICE_B_TOKEN, [this.serviceA.SERVICE_A_TOKEN]);
  }
}

Athlete().injectModule(ServiceAModule).injectModule(ServiceBModule, [ServiceAModule]);
  • executeCommand: Method to set a command that will run after the container is created.

If there are multiple commands, they will execute sequentially in the set order.

interface ICommand {
  execute(locator: ILocator): void;
}
class ServiceA {}

function ServiceB(serviceA: ServiceA) {}

class ServiceAModule implements IModule {
  readonly SERVICE_A_TOKEN = ServiceA;

  export(injector: IInjector): void {
    injector.inject(this.SERVICE_A_TOKEN);
  }
}

class ServiceBModule implements IModule {
  constructor(readonly serviceA: ServiceA) {}

  readonly SERVICE_B_TOKEN = ServiceB;

  export(injector: IInjector): void {
    injector.inject(this.SERVICE_B_TOKEN, [this.serviceA.SERVICE_A_TOKEN]);
  }
}

class ReturnServiceBInstance implements ICommand {
  constructor(readonly serviceBModule: ServiceBModule) {}

  execute(locator: ILocator): void {
    const serviceBInstance = locator.resolveInstance(this.serviceBModule.SERVICE_B_TOKEN);
  }
}

Athlete()
  .injectModule(ServiceAModule)
  .injectModule(ServiceBModule, [ServiceAModule])
  .buildContainer()
  .executeCommand(ReturnServiceBInstance, [ServiceBModule]);
  • canBeResolved: Method to check whether a given candidate can be resolved as a valid token.

If the candidate is a valid token, it can be used for dependency resolution.

const isToken = Athlete().buildContainer().canBeResolved(Athlete.LOCATOR_TOKEN); // isToken = true
  • Inject objects and primitives
    To specify objects or primitives that should not be instantiated, wrap the value in a tuple.
class ServiceA {
  constructor(readonly num: number, readonly obj: object) {}
}

Athlete().inject(ServiceA, [[42], [{}]]);
  • Inject container locator
    You can inject the instance resolver as a dependency.
class ServiceA {
  constructor(readonly locator: ILocator) {}

  test() {
    const serviceA = this.locator.resolveInstance(ServiceA);
  }
}

Athlete().inject(ServiceA, [Athlete.LOCATOR_TOKEN]);

Enjoy programming!

0.3.0

4 months ago

0.2.1

5 months ago

0.2.0

5 months ago

0.0.1

5 months ago