@adonisjs/fold

  • Version 10.1.2
  • Published
  • 173 kB
  • 1 dependency
  • MIT license

Install

npm i @adonisjs/fold
yarn add @adonisjs/fold
pnpm add @adonisjs/fold

Overview

A simple and straight forward implementation for IoC container in JavaScript

Index

Functions

function inject

inject: () => {
<C extends Function>(target: C): void;
(target: any, propertyKey: string | symbol): void;
};
  • The "@inject" decorator uses Reflection to inspect the dependencies of a class or a method and defines them as metaData on the class for the container to discover them.

function moduleCaller

moduleCaller: (
target: Constructor<any>,
method: string
) => {
toCallable<
T extends ContainerResolver<any> | Container<any> = undefined,
Args extends any[] = any[]
>(
container?: T
): ModuleCallable<T, Args>;
toHandleMethod<
T_1 extends ContainerResolver<any> | Container<any> = undefined,
Args_1 extends any[] = any[]
>(
container?: T_1
): ModuleHandler<T_1, Args_1>;
};
  • The moduleCaller works around a very specific pattern we use with AdonisJS, ie to construct classes and call methods using the container.

    For example: Controllers of AdonisJS allows defining a controller as follows

    route.get('/', [HomeController, 'index'])

    Behind the scenes, we have to run following operations in order to call the handle method on the defined middleware.

    - Create an instance of the controller class using the container. - Call the method using the container. Hence having the ability to use DI

function moduleExpression

moduleExpression: (
expression: string,
parentURL: URL | string
) => {
parse(): [string, string];
toCallable<
T extends ContainerResolver<any> | Container<any> = undefined,
Args extends any[] = any[]
>(
container?: T
): ModuleCallable<T, Args>;
toHandleMethod<
T_1 extends ContainerResolver<any> | Container<any> = undefined,
Args_1 extends any[] = any[]
>(
container?: T_1
): ModuleHandler<T_1, Args_1>;
};
  • The moduleExpression module works around a very specific pattern we use with AdonisJS, ie to bind modules as string.

    For example: With the router of AdonisJS, we can bind a controller to a route as follows.

    Route.get('users', '#controllers/users_controller.index')

    Behind the scenes, we have to run following operations in order to call a method on the users_controller class.

    - Dynamic import #controllers/users_controller module - Check if the module has a default export. - Create an instance of the default export class using the container. - Call the index method on the controller class using the container.

    Router is just one example, we do this with event listeners, redis pub/sub and so on.

    So, instead of writing all this parsing logic, we encapsulate it inside the "moduleExpression" module.

function moduleImporter

moduleImporter: (
importFn: () => Promise<{ default: Constructor<any> }>,
method: string
) => {
toCallable<
T extends ContainerResolver<any> | Container<any> = undefined,
Args extends any[] = any[]
>(
container?: T
): ModuleCallable<T, Args>;
toHandleMethod<
T_1 extends ContainerResolver<any> | Container<any> = undefined,
Args_1 extends any[] = any[]
>(
container?: T_1
): ModuleHandler<T_1, Args_1>;
};
  • The moduleImporter module works around a very specific pattern we use with AdonisJS, ie to lazy load modules by wrapping import calls inside a callback.

    For example: Middleware of AdonisJS allows registering middleware as an array of import calls.

    defineMiddleware([
    () => import('#middleware/silent_auth')
    ])
    defineMiddleware({
    auth: () => import('#middleware/auth')
    })

    Behind the scenes, we have to run following operations in order to call the handle method on the defined middleware.

    - Lazily call the registered callbacks to import the middleware. - Check if the module has a default export. - Create an instance of the default export class using the container. - Call the handle method on the middleware class using the container.

Classes

class Container

class Container<KnownBindings extends Record<any, any>> {}
  • The container class exposes the API to register bindings, values and resolve them.

    Known bindings types can be defined at the time of the constructing the container.

    new Container<{ 'route': Route, encryption: Encryption }>()

    You can resolve bindings and construct classes as follows

    await container.make(BINDING_NAME)
    await container.make(CLASS_CONSTRUCTOR)

constructor

constructor(options?: ContainerOptions);

    method alias

    alias: <Alias extends keyof KnownBindings>(
    alias: Alias extends string | symbol ? Alias : never,
    value:
    | AbstractConstructor<KnownBindings[Alias]>
    | Exclude<
    {
    [K in keyof KnownBindings]: KnownBindings[K] extends KnownBindings[Alias]
    ? K
    : never;
    }[keyof KnownBindings],
    Alias
    >
    ) => void;
    • Register an alias for a binding. The value can be a reference to an existing binding or to a class constructor that will instantiate to the same value as the alias.

    method bind

    bind: {
    <Binding extends keyof KnownBindings>(
    binding: Binding extends string | symbol ? Binding : never,
    resolver: BindingResolver<KnownBindings, KnownBindings[Binding]>
    ): void;
    <Binding extends AbstractConstructor<any>>(
    binding: Binding,
    resolver: BindingResolver<KnownBindings, InstanceType<Binding>>
    ): void;
    };
    • Register a binding inside the container. The method receives a key-value pair.

      - Key can be a string, symbol or a constructor. - The value is always a factory function to construct the dependency.

      container.bind('route', () => new Route())
      await container.make('route')
      container.bind(Route, () => new Route())
      await container.make(Route)
      const routeSymbol = Symbol('route')
      container.bind(routeSymbol, () => new Route())
      await container.make(routeSymbol)

    method bindValue

    bindValue: {
    <Binding extends keyof KnownBindings>(
    binding: Binding extends string | symbol ? Binding : never,
    value: KnownBindings[Binding]
    ): void;
    <Binding extends AbstractConstructor<any>>(
    binding: Binding,
    value: InstanceType<Binding>
    ): void;
    };
    • Register a binding as a value

      container.bindValue(Route, new Route())

    method call

    call: <Value extends Record<any, any>, Method extends ExtractFunctions<Value>>(
    value: Value,
    method: Method,
    runtimeValues?: any[],
    createError?: ErrorCreator
    ) => Promise<ReturnType<Value[Method]>>;
    • Call a method on an object by injecting its dependencies. The method dependencies are resolved in the same manner as a class constructor dependencies.

      await container.call(await container.make(UsersController), 'index')

    method contextualBinding

    contextualBinding: <Binding extends AbstractConstructor<any>>(
    parent: Constructor<any>,
    binding: Binding,
    resolver: BindingResolver<KnownBindings, Make<Binding>>
    ) => void;
    • Add a contextual binding for a given class constructor. A contextual takes a parent, parent's dependency and a callback to self resolve the dependency.

      For example: - When "UsersController" - Asks for "Hash class" - Provide "Argon2" implementation

    method createResolver

    createResolver: () => ContainerResolver<KnownBindings>;
    • Create a container resolver to resolve bindings, or make classes.

      const resolver = container.createResolver()
      await resolver.make(CLASS_CONSTRUCTOR)

      Bind values with the resolver. Resolver values are isolated from the container.

      resolver.bindValue(HttpContext, new HttpContext())
      await resolver.make(UsersController)

    method hasAllBindings

    hasAllBindings: {
    <Binding extends keyof KnownBindings>(bindings: Binding[]): boolean;
    (binding: BindingKey[]): boolean;
    };
    • Find if the container has all the bindings registered using the "bind", the "singleton", or the "bindValue" methods.

    method hasBinding

    hasBinding: {
    <Binding extends keyof KnownBindings>(binding: Binding): boolean;
    (binding: BindingKey): boolean;
    };
    • Find if the container has a binding registered using the "bind", the "singleton", or the "bindValue" methods.

    method make

    make: {
    <Binding extends keyof KnownBindings>(
    binding: Binding,
    runtimeValues?: any[],
    createError?: ErrorCreator
    ): Promise<
    Binding extends string | symbol ? KnownBindings[Binding] : Make<Binding>
    >;
    <Binding>(
    binding: Binding,
    runtimeValues?: any[],
    createError?: ErrorCreator
    ): Promise<Make<Binding>>;
    };
    • Resolves the binding or constructor a class instance as follows.

      - Resolve the binding from the values (if registered) - Resolve the binding from the bindings (if registered) - If binding is a class, then create a instance of it. The constructor dependencies are further resolved as well. - All other values are returned as it is.

      await container.make('route')
      await container.make(Database)

    method resolving

    resolving: {
    <Binding extends keyof KnownBindings>(
    binding: Binding extends string | symbol ? Binding : never,
    callback: HookCallback<KnownBindings, KnownBindings[Binding]>
    ): void;
    <Binding extends AbstractConstructor<any>>(
    binding: Binding,
    callback: HookCallback<KnownBindings, InstanceType<Binding>>
    ): void;
    };
    • Define hooks to be executed after a binding has been resolved from the container.

      The hooks are executed for

      - Bindings - Only once for singletons - And class constructor

      In other words, the hooks are not executed for direct values registered with the container

    method restore

    restore: (binding: AbstractConstructor<any>) => void;
    • Restore binding by removing its swap

    method restoreAll

    restoreAll: (bindings?: AbstractConstructor<any>[]) => void;
    • Restore mentioned or all bindings by removing their swaps

    method singleton

    singleton: {
    <Binding extends keyof KnownBindings>(
    binding: Binding extends string | symbol ? Binding : never,
    resolver: BindingResolver<KnownBindings, KnownBindings[Binding]>
    ): void;
    <Binding extends AbstractConstructor<any>>(
    binding: Binding,
    resolver: BindingResolver<KnownBindings, InstanceType<Binding>>
    ): void;
    };
    • Register a binding as a single. The singleton method is same as the bind method, but the factory function is invoked only once.

      container.singleton('route', () => new Route())
      await container.make('route')
      container.singleton(Route, () => new Route())
      await container.make(Route)
      const routeSymbol = Symbol('route')
      container.singleton(routeSymbol, () => new Route())
      await container.make(routeSymbol)

    method swap

    swap: <Binding extends AbstractConstructor<any>>(
    binding: Binding,
    resolver: BindingResolver<KnownBindings, InstanceType<Binding>>
    ) => void;
    • Define a fake implementation for a binding or a class constructor. Fakes have the highest priority when resolving dependencies from the container.

    method useEmitter

    useEmitter: (emitter: Exclude<ContainerOptions['emitter'], undefined>) => this;
    • Define an emitter instance to use

    method when

    when: (
    parent: Constructor<any>
    ) => ContextBindingsBuilder<KnownBindings, AbstractConstructor<any>>;
    • Create a contextual builder to define contextual bindings

    class ContainerResolver

    class ContainerResolver<KnownBindings extends Record<any, any>> {}
    • Container resolver exposes the APIs to resolve bindings. You can think of resolver as an isolated container instance, with only the APIs to resolve bindings.

      const container = new Container()
      const resolver = container.createResolver()
      await resolver.make(BINDING_NAME)
      await resolver.make(CLASS_CONSTRUCTOR)

    constructor

    constructor(
    container: {
    bindings: Bindings;
    bindingValues: BindingValues;
    swaps: Swaps;
    hooks: Hooks;
    aliases: Map<
    Partial<keyof KnownBindings>,
    keyof KnownBindings | AbstractConstructor<any>
    >;
    contextualBindings: Map<Constructor<any>, ContextualBindings>;
    },
    options: ContainerOptions
    );

      method bindValue

      bindValue: {
      <Binding extends keyof KnownBindings>(
      binding: Binding extends string | symbol ? Binding : never,
      value: KnownBindings[Binding]
      ): void;
      <Binding extends AbstractConstructor<any>>(
      binding: Binding,
      value: InstanceType<Binding>
      ): void;
      };
      • Register a binding as a value

        container.bindValue(Route, new Route())

      method call

      call: <Value extends Record<any, any>, Method extends ExtractFunctions<Value>>(
      value: Value,
      method: Method,
      runtimeValues?: any[],
      createError?: ErrorCreator
      ) => Promise<ReturnType<Value[Method]>>;
      • Call a method on an object by injecting its dependencies. The method dependencies are resolved in the same manner as a class constructor dependencies.

        await resolver.call(await resolver.make(UsersController), 'index')

      method hasAllBindings

      hasAllBindings: {
      <Binding extends keyof KnownBindings>(bindings: Binding[]): boolean;
      (bindings: BindingKey[]): boolean;
      };
      • Find if the resolver has all the bindings registered using the "bind", the "singleton", or the "bindValue" methods.

      method hasBinding

      hasBinding: {
      <Binding extends keyof KnownBindings>(binding: Binding): boolean;
      (binding: BindingKey): boolean;
      };
      • Find if the resolver has a binding registered using the "bind", the "singleton", or the "bindValue" methods.

      method make

      make: {
      <Binding extends keyof KnownBindings>(
      binding: Binding,
      runtimeValues?: any[],
      createError?: ErrorCreator
      ): Promise<
      Binding extends string | symbol ? KnownBindings[Binding] : Make<Binding>
      >;
      <Binding>(
      binding: Binding,
      runtimeValues?: any[],
      createError?: ErrorCreator
      ): Promise<Make<Binding>>;
      };
      • Resolves the binding or constructor a class instance as follows.

        - Resolve the binding from the values (if registered) - Resolve the binding from the bindings (if registered) - If binding is a class, then create a instance of it. The constructor dependencies are further resolved as well. - All other values are returned as it is.

        await resolver.make('route')
        await resolver.make(Database)

      method resolveFor

      resolveFor: <Binding>(
      parent: unknown,
      binding: Binding,
      runtimeValues?: any[],
      createError?: ErrorCreator
      ) => Promise<Make<Binding>>;
      • Resolves binding in context of a parent. The method is same as the "make" method, but instead takes a parent class constructor.

      Package Files (7)

      Dependencies (1)

      Dev Dependencies (25)

      Peer Dependencies (0)

      No peer dependencies.

      Badge

      To add a badge like this onejsDocs.io badgeto your package's README, use the codes available below.

      You may also use Shields.io to create a custom badge linking to https://www.jsdocs.io/package/@adonisjs/fold.

      • Markdown
        [![jsDocs.io](https://img.shields.io/badge/jsDocs.io-reference-blue)](https://www.jsdocs.io/package/@adonisjs/fold)
      • HTML
        <a href="https://www.jsdocs.io/package/@adonisjs/fold"><img src="https://img.shields.io/badge/jsDocs.io-reference-blue" alt="jsDocs.io"></a>