Simplest and straightforward implementation of IoC container in JavaScript
npx @tessl/cli install tessl/npm-adonisjs--fold@10.2.0AdonisJS Fold is a lightweight and straightforward implementation of an Inversion of Control (IoC) container specifically designed for JavaScript and TypeScript applications. It focuses on simplicity and readability while enabling dependency injection patterns that help create loosely coupled systems.
npm install @adonisjs/foldimport {
Container,
inject,
ContainerResolver,
moduleCaller,
moduleImporter,
moduleExpression,
parseBindingReference
} from "@adonisjs/fold";For CommonJS:
const {
Container,
inject,
ContainerResolver,
moduleCaller,
moduleImporter,
moduleExpression,
parseBindingReference
} = require("@adonisjs/fold");import { Container, inject } from "@adonisjs/fold";
// Create a container
const container = new Container();
// Register bindings
container.bindValue("database_url", "postgresql://localhost:5432/mydb");
container.singleton("logger", () => new Logger());
// Use dependency injection with decorators
@inject()
class UserService {
constructor(
private logger: Logger,
private databaseUrl: string
) {}
async getUsers() {
this.logger.info("Fetching users from", this.databaseUrl);
// Implementation
}
}
// Resolve dependencies
const userService = await container.make(UserService);AdonisJS Fold is built around several key components:
Core IoC container functionality for registering dependencies, creating instances, and managing the dependency lifecycle.
class Container<KnownBindings extends Record<any, any>> {
constructor(options?: ContainerOptions);
make<Binding>(binding: Binding, runtimeValues?: any[], createError?: ErrorCreator): Promise<Make<Binding>>;
createResolver(): ContainerResolver<KnownBindings>;
}Automatic dependency injection using TypeScript decorators and reflection metadata for seamless constructor and method parameter resolution.
function inject(): <C extends Function>(target: C) => void;
function inject(): (target: any, propertyKey: string | symbol) => void;Multiple binding types for registering dependencies including direct values, factory functions, singletons, and contextual bindings.
class Container<KnownBindings extends Record<any, any>> {
bind<Binding>(binding: Binding, resolver: BindingResolver<KnownBindings, any>): void;
bindValue<Binding>(binding: Binding, value: any): void;
singleton<Binding>(binding: Binding, resolver: BindingResolver<KnownBindings, any>): void;
}Isolated resolver instances that can resolve dependencies and create class instances without modifying the parent container.
class ContainerResolver<KnownBindings extends Record<any, any>> {
make<Binding>(binding: Binding, runtimeValues?: any[], createError?: ErrorCreator): Promise<Make<Binding>>;
call<Value, Method>(value: Value, method: Method, runtimeValues?: any[], createError?: ErrorCreator): Promise<ReturnType<Value[Method]>>;
bindValue<Binding>(binding: Binding, value: any): void;
}Helper functions for working with dynamic imports, string-based module references, and creating callable functions from class constructors.
function moduleCaller(target: Constructor<any>, method: string): {
toCallable<T>(containerOrResolver: Container<any> | ContainerResolver<any>): ModuleCallable<T, any[]>;
toHandleMethod<T>(containerOrResolver: Container<any> | ContainerResolver<any>): ModuleHandler<T, any[]>;
};
function moduleImporter(importFn: () => Promise<{ default: Constructor<any> }>, method: string): {
toCallable<T>(containerOrResolver: Container<any> | ContainerResolver<any>): ModuleCallable<T, any[]>;
toHandleMethod<T>(containerOrResolver: Container<any> | ContainerResolver<any>): ModuleHandler<T, any[]>;
};Swap and mock functionality for replacing dependencies during testing, with easy restoration capabilities.
class Container<KnownBindings extends Record<any, any>> {
swap<Binding>(binding: AbstractConstructor<any>, resolver: BindingResolver<KnownBindings, any>): void;
restore(binding: AbstractConstructor<any>): void;
restoreAll(bindings?: AbstractConstructor<any>[]): void;
}Utility functions for parsing binding references from magic strings, class references, and lazy import references.
function parseBindingReference(
binding: string | [LazyImport<Constructor<any>> | Constructor<any>, any?]
): Promise<{ moduleNameOrPath: string; method: string }>;type BindingKey = string | symbol | AbstractConstructor<any>;
type BindingResolver<KnownBindings extends Record<any, any>, Value> = (
resolver: ContainerResolver<KnownBindings>,
runtimeValues?: any[]
) => Value | Promise<Value>;
type Make<T> = T extends AbstractConstructor<infer A> ? A : never;
type ErrorCreator = (message: string) => Exception;
type HookCallback<KnownBindings extends Record<any, any>, Value> = (
value: Value,
resolver: ContainerResolver<KnownBindings>
) => void | Promise<void>;
interface ContainerOptions {
emitter?: {
emit(event: string | symbol, ...values: any[]): any;
};
}
type ModuleCallable<T, Args extends any[]> = T extends undefined
? (resolver: ContainerResolver<any> | Container<any>, ...args: Args) => Promise<any>
: (...args: Args) => Promise<any>;
type ModuleHandler<T, Args extends any[]> = T extends undefined
? {
name?: string;
handle(resolver: ContainerResolver<any> | Container<any>, ...args: Args): Promise<any>;
}
: {
name?: string;
handle(...args: Args): Promise<any>;
};