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

hollywood-di

v0.5.2

Published

📦 Type safe Dependency Injection for Typescript with zero decorators

Downloads

22

Readme

Hollywood DI

📦 Typesafe Dependency Injection for typescript with zero decorators.

Hollywood is desgined to be simple. A class / factory function determines its dependencies and it can only be resolved by a container if that container holds all it's required dependencies.

As long as this requirement is met, the container will take control of creating the instance. Dependencies are statically checked with typescript at compile time.

Installation

npm i hollywood-di

Usage

import { factory, Hollywood } from "hollywood-di";

class Example {
  public greet() {
    console.log("hello world");
  }
}

const container = Hollywood.create({
  classExample: Example,
  factoryExample: factory(() => "ping pong"),
});

// resolve registered token by name
const classExample = container.resolve("classExample");

// resolve registered token using constructor
const classExample2 = container.resolve(Example);

// access instance directly from the container
const factoryExample = container.instances.factoryExample;

Containers

A container is responsible for registering and resolving tokens. Containers may create child containers which can register their own tokens.

import { Hollywood } from "hollywood-di";

const container = Hollywood.create({
  /// ...register tokens here
});

const childContainer = container.createChild({
  // ...register child tokens here
});

// NOTE: a child container can resolve every token it's parent container can resolve

Lazy Containers

By default, registered tokens are eagerly resolved before they are first accessed. To change this behaviour create the container as lazy. Containers will inherit their lazy option form parent containers.

NOTE: Token lazy option superceeds the container option.

// lazy container
const container = Hollywood.create(
  {
    // ...register tokens
  },
  { lazy: true }
);

// this container is lazy, as inherited from its parent
const childContainer = Hollywood.createWithParent(container, {
  // ...register tokens
});

Tokens

Registration

Tokens are registered on a container after which they can be resolved using their name, class constructor or factory function.

NOTE: When resolving a class constructor or factory function, the container will only return an existing instance if it was directly registered on the container, otherwise a new instance will be created and returned. Resolving by name does not share this behaviour.

Example using a factory:

const userFactory = factory(() => ({ role: "user" }));

const container = Hollywood.create({
  user: userFactory,
});

const user1 = container.resolve("user");
const user2 = container.resolve(userFactory);
// user1 === user2

Example using a class:

class Car {}

const container = Hollywood.create({
  car: Car,
});

const car1 = container.resolve("car");
const car2 = container.resolve(Car);
// car1 === car2

A class can only be used as a token if it has exactly zero constructor parameters or a static init function that returns an instance of the class.

class Car {}

class Color {
  constructor(public hex: string) {}
}

class Person {
  public static init(container: { car: Car }) {
    return new Person(container.car);
  }
  constructor(public car: Car) {}
}

const container = Hollywood.create({
  car: Car, // valid ✅
  color: Color, // invalid ❌
  person: Person, // valid ✅
});

Dependencies

Registered tokens describe their dependencies and the container will ensure those dependencies are provided.

class Chef {
  public cook(meal: string) {}
}

class Waiter {
  public serve(meal: string) {}
}

class Restaurant {
  public static init(container: { chef: Chef; waiter: Waiter }) {
    return new Restaurant(container.chef, container.waiter);
  }

  constructor(private chef: Chef, private waiter: Waiter) {}

  public orderMeal(meal: string) {
    this.chef.cook(meal);
    this.waiter.serve(meal);
  }
}

const container = Hollywood.create({
  chef: Chef,
  waiter: Waiter,
  restaurant: Restaurant,
});

const restaurant = container.resolve("restaurant");
restaurant.orderMeal("🍜");

In the example above, the container automatically injects the chef and waiter to the restaurant.

Define Init (class helper)

In the previous example, the restaurant initializer can be simplified as below:

import { defineInit } from "hollywood-di";

// other code here

class Restaurant {
  public static init = defineInit(Restaurant).args("chef", "waiter");

  constructor(private chef: Chef, private waiter: Waiter) {}

  // other methods here
}

this is a shorthand for specifying the name of the token in the container which will hold the value for each argument in the class constructor.

Lazy Tokens

By default a token is eagerly resolved, except overriden on the container. To change this behaviour register the token as lazy.

NOTE: Token lazy option superceeds the container option.

const container = Hollywood.create({
  // this token will be lazily resolved
  example: factory(() => "hello world", { lazy: true }),
});

// lazy container
const container = Hollywood.create(
  {
    // this token will be eagerly resolved
    example: factory(() => "hello world", { lazy: false }),
  },
  { lazy: true }
);

NOTE: when resolving a token, all dependencies of the token will also be resolved, hence, an eagerly resolved token in a lazy container may also eagerly resolve other tokens.

Init Hooks

Registered tokens can have init hooks that are called before and after an instance of that token is created.

const container = Hollywood.create({
  example: factory(() => "hello world", {
    beforeInit() {
      console.log("before example init");
    },
    afterInit(instance) {
      console.log(instance); // instance === "hello world"
    },
  }),
});

Scope

Tokens registered in containers can have one of three scopes:

type Scope = "singleton" | "scoped" | "transient";
  • Scoped: the same instance is shared in each container.

  • Singleton: a single instance is shared across all containers.

  • Transient: a new instance is created everytime.

import {
  factory,
  Hollywood,
  scoped,
  scopedFactory,
  singleton,
  singletonFactory,
  transient,
  transientFactory,
} from "hollywood-di";

class Instance {}

const container = Hollywood.create({
  // scoped
  example: Instance, // scoped by default ie same as scoped(...)
  exampleScopedFactory: factory(() => new Instance()), // scoped by default ie same as scopedFactory(...)
  exampleScoped: scoped(Instance),
  exampleScopedFactory: scopedFactory(() => new Instance()),

  // singleton
  exampleSingleton: singleton(Instance),
  exampleSingletonFactory: singletonFactory(() => new Instance()),

  // transient
  exampleTransient: transient(Instance),
  exampleTransientfactory: transientFactory(() => new Instance()),
});