npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@drewjbartlett/tiny-ioc

v0.0.16

Published

`tiny-ioc` is a lightweight (less than 1kB) dependency injection / IOC container for TypeScript.

Downloads

43

Readme

Tiny IOC

tiny-ioc is a lightweight (less than 1kB) dependency injection / IOC container for TypeScript.

Core CI

Features

  • ✅ 100% TypeScript
  • ✅ lightweight - < 1kB
  • ✅ bind & unbind dependencies
  • ✅ bind singletons and reset if necessary
  • ✅ bind factories
  • ✅ support for swapping dependencies
  • ✅ simplfies testing / mocking

Installation

npm i @drewjbartlett/tiny-ioc --save

Usage

Create a container.ts file that creates, binds, and then exports the container.

// container.ts
import { createContainer, Scope } from '@drewjbartlett/tiny-ioc';

const container = createContainer();

container.bind(MyClass, () => new MyClass(), Scope.Singleton);
container.bindFactory(DataSource, () => new DataSource(container.get(HttpClient)));
container.bindSingleton(HttpClient, () => new HttpClient());

export { container }
// some-other-file.ts

import { container } from 'path/to/container';
import { DataSource } from 'path/to/data-source';

export async function makeRequest() {
  try {
    const dataSource = container.get(DataSource);

    return await dataSource.get('/foo/bar');
  } catch (e) {
    //
  }
}

API

bind<T>(binding: Binding<T>, value: FactoryFunction<T>, scope: Scope): void

Bind a dependency with a given scope.

import { createContainer, Scope } from '@drewjbartlett/tiny-ioc';

const container = createContainer();

container.bind(SomeClass, () => new SomeClass(container.get(AnotherClass)), Scope.Singleton);
container.bind(AnotherClass, () => new AnotherClass(), Scope.Factory);

bindSingleton<T>(binding: Binding<T>, factory: FactoryFunction<T>): void

Bind a dependency to the container as a singleton.

class Total {
  constructor(public readonly count: number) {}
}

let count = 0;

container.bindSingleton(
  Total,
  () => {
    count++;

    return new Total(count);
  },
);

container.get(Total).count; // 1
container.get(Total).count; // 1
container.get(Total).count; // 1
container.get(Total).count; // 1

bindFactory<T>(binding: Binding<T>, factory: FactoryFunction<T>): void

Bind a dependency to the container as a factory. Each time the dependency is resolved the container will call the factory function.

class Total {
  constructor(public readonly count: number) {}
}

let count = 0;

container.bindFactory(
  Total,
  () => {
    count++;

    return new Total(count);
  },
);

container.get(Total).count; // 1
container.get(Total).count; // 2
container.get(Total).count; // 3
container.get(Total).count; // 4

bindOnce<T>(binding: Binding<T>, value: FactoryFunction<T>, scope: Scope): void

Only bind the given value if there is not already a binding.

container.bindFactory(HttpClient, () => new HttpClient({ baseURL: 'baseURL 1' }));
container.bindOnce(HttpClient, () => new HttpClient({ baseURL: 'baseURL 2' }), Scope.Singleton);

container.get(HttpClient).baseURL // 'baseURL 1'

get<T>(binding: Binding<T>): T

Attempt to resolve a given binding. Will throw a NotBoundException if there is no binding found.

container.get(SomeDependency);

resetSingleton<T>(binding: Binding<T>): void

Reset a singleton value.

If a value has been previously resolved and is bound as a singleton, this will keep the binding but reset the singleton value until the next resolve. Take the example below. Each time the singleton dependency is built the count will increase. Since it's a singleton count will always be 1. After resetting the singleton the new value is 2 since the factory function is called again.

let count = 0;

container.bindSingleton(
  Total,
  () => {
    count++;

    return new Total(count);
  },
);

container.get(Total).count // 1
container.get(Total).count // 1

container.resetSingleton(Total);

container.get(Total).count // 2

bound<T>(binding: Binding<T>): boolean

Determine if a binding exists or not.

container.bound(HttpClient); // false

container.bindFactory(HttpClient, () => new HttpClient());

container.bound(HttpClient); // true

unbind<T>(binding: Binding<T>): void

Remove the given binding from the container entirely.

container.bindFactory(HttpClient, () => new HttpClient());

container.get(HttpClient); // HttpClient

container.unbind(HttpClient); 

container.get(HttpClient); // throws NotBoundException

swap<T>(oldBinding: Binding<T>, newBinding: FactoryFunction<T>): void

Swap the old binding's value with the new value. This is useful when testing.

There may be times where swapping a dependency is necessary. Especially when testing. swap allows for swapping out a dependency by a given class name.

class Tesla extends Car {

}

class Rivian extends Car {

}

container.bindFactory(Car, () => new Tesla());

container.get(Car); // Tesla

container.swap(Car, () => new Rivian());

container.get(Car); // Rivian

Using in tests

Unit testing is made very simple when using tiny-ioc. You can simply swap out the real dependency for any mock dependency and the tests will reference your mock instead of the real thing.

// make-request.ts

import { container } from 'path/to/container';
import { HttpClient } from 'path/to/http-client';

export async function makeRequest() {
  try {
    const dataSource = container.get(HttpClient);

    return await dataSource.get('/foo/bar');
  } catch (e) {
    //
  }
}
// make-request.test.ts

import { container } from 'path/to/container';
import { HttpClient } from 'path/to/http-client';
import { makeRequest } from 'path/top/make-request';

class DummyHttpClient {
  get(url: string) {
    return dummyData;
  }
}

it('should make the request', () => {
  container.swap(HttpClient, () => new DummyHttpClient());

  await myRequest(); // calls .get() on DummyHttpClient instead of HttpClient
})