JSPM

  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 11
  • Score
    100M100P100Q30172F
  • License MIT

IoC library mirroring Java Spring Framework's implementation

Package Exports

  • ecmascript-ioc

Readme

ecmascript-ioc

This is a zero-dependency vanila TypeScript IoC library that mirrors the implementation of the Java Spring Framework IoC. You can use it anywhere: Node.js backend, Electron.js, IoT apps, React/ReactNative, Vue, Iframe embeded widgets, etc...

Features

  • Dependency Injection: Automatic dependency resolution and injection with scopes, circular dependencies handling and lazy initializations.
  • Annotations: Use decorators like @component, @repository, @service, @controller, and @autowired.
  • New ECMAScript Decorators: Use the native TypeScript5.0 decorators without reflect-metadata.

Installation

npm install ecmascript-ioc

Dependency settings

All dependency definition decorators have a common signature: you define the required dependency name and optional settings.

type DependencySettings = {
  lazy: boolean;
  scope: "Singleton" | "Prototype";
};

function component(name: string | symbol, settings?: Partial<DependencySettings>);
const defaultSettings: DependencySettings = {
  lazy: false,
  scope: "Singleton";
};

Usage guides

Create and init ApplicationContext before usage:

import { ApplicationContext } from 'ecmascript-ioc';

const applicationContext = new ApplicationContext({...configuration});
applicationContext.init();

In the test env:

import { TestApplicationContext } from 'ecmascript-ioc';

describe('TestCases', () => {
  const applicationContext = new TestApplicationContext();
  applicationContext.init();

  beforeEach(() => {
    context.diContainer.clear();
  });
});
import { TestApplicationContext } from 'ecmascript-ioc';

describe('TestCases', () => {
  const applicationContext = new TestApplicationContext();
  applicationContext.init();

  it('TestCase', () => {
    context.diContainer.overrideDependency("name", Clazz, {
      lazy: false,
      scope: "Singleton",
    });
  });
});

Backend Three-tier architecture example:

import { autowired,
         component,
         repository,
         service,
         controller,
         postConstruct } from 'ecmascript-ioc';


@component(ReportGenerator.di_token, { lazy: true })
class ReportGenerator {
  static readonly di_token = Symbol.for("ReportGenerator");

  public generateTaxReport(username: string): void {
    console.log(`Prepare tax report for user: ${username}.`);
  }
}


@repository("UsersRepository")
class UsersRepository extends Repository {
  public delete(username: string): void {
    console.log(`Delete user: ${username} from DataBase.`);
  }

  @postConstruct
  private warmUpCache(): void {
    console.log('Fetching data from DataBase...');
  }
}


@service("UsersService")
class UsersService implements Service {
  @autowired("UsersRepository")
  private readonly repository!: UsersRepository;

  @autowired(ReportGenerator.di_token)
  private readonly reportGenerator!: ReportGenerator;

  public deleteUser(username: string): void {
    this.repository.delete(username);
    this.reportGenerator.generateTaxReport(username);
  }
}


@controller("UsersController")
class UsersController {
  @autowired("UsersService")
  private readonly service!: UsersService;

  public deleteUser(req: Request, res: Response) {
    this.service.deleteUser(req.query.username);
  }
}

Frontend Three-tier architecture example:

import React from "react";
import { AxiosInstance } from "axios";
import { observable, action } from "mobx";
import { autowired, component, repository, service } from 'ecmascript-ioc';


@component("RestHttpClient")
export class RestHttpClient extends HttpClient {
  protected readonly http: AxiosInstance;

  public delete(url: string): void {
    console.log(`RESTful: ${url}.`);
  }
}


@repository("UsersRepository")
export class UsersRepository extends Repository {
  @autowired("RestHttpClient")
  private readonly http!: RestHttpClient;

  public deleteUser(username: string): void {
    return this.http.delete(`/users?username=${username}`);
  }
}


@service("UsersService", { scope: 'Prototype' })
export class UsersService extends Service {
  @autowired("UsersRepository")
  private readonly repository!: UsersRepository;

  @observable accessor username: string = "";

  @action
  public setUsername(username: string): void {
    this.username = username;
  }

  public deleteUser(): void {
    return this.repository.delete(this.username);
  }
}


export function View(): JSX.Element {
  const service = useDependency<UsersService>("UsersService");
  return <div>Loading..<div/>;
}

React.js hook

import { useMemo } from "react";
import { Container } from "ecmascript-ioc";

export function useDependency<T>(dependencyName: string | symbol): T {
  return useMemo(() => {
    return globalThis
    .ecmascript_ioc_application_context
    .diContainer
    .getDependency<T>(dependencyName);
  }, [dependencyName]);
}