š§āāļø Lightweight and flexible dependency injection library for JavaScript and TypeScript, w/wo ECMAScript decorators.
Installations
npm install di-wise
Developer Guide
Typescript
Yes
Module System
CommonJS, ESM
Node Version
22.11.0
NPM Version
10.9.0
Releases
Contributors
Languages
TypeScript (94.49%)
JavaScript (5.51%)
Developer
Download Statistics
Total Downloads
1,477
Last Day
4
Last Week
17
Last Month
248
Last Year
1,477
GitHub Statistics
85 Stars
182 Commits
2 Forks
1 Watching
1 Branches
1 Contributors
Package Meta Information
Latest Version
0.2.8
Package Id
di-wise@0.2.8
Unpacked Size
220.85 kB
Size
47.80 kB
File Count
39
NPM Version
10.9.0
Node Version
22.11.0
Publised On
07 Dec 2024
Total Downloads
Cumulative downloads
Total Downloads
1,477
Last day
0%
4
Compared to previous day
Last week
-39.3%
17
Compared to previous week
Last month
-65.7%
248
Compared to previous month
Last year
0%
1,477
Compared to previous year
Daily Downloads
Weekly Downloads
Monthly Downloads
Yearly Downloads
di-wise š§āāļø
Lightweight and flexible dependency injection library for JavaScript and TypeScript, w/wo ECMAScript decorators.
Table of Contents
- di-wise š§āāļø
- Table of Contents
- Installation
- Features
- Zero dependencies
- Modern decorator implementation
- Context-based DI system
- Multiple provider types
- Hierarchical injection
- Full control over registration and caching
- Various injection scopes
- Flexible token-based injection
- Automatic circular dependency resolution
- Dynamic injection
- Constructor Injection
- Middleware
- Usage
- API
- Credits
- License
Installation
1npm install di-wise 2 3pnpm add di-wise 4 5yarn add di-wise
Also available on JSR:
1deno add jsr:@exuanbo/di-wise
Features
Zero dependencies
- No need for
reflect-metadata
- No TypeScript legacy
experimentalDecorators
required
Modern decorator implementation
- Built on ECMAScript Stage 3 Decorators: tc39/proposal-decorators
- Native support in TypeScript 5.0+, swc 1.3.47+, and esbuild 0.21.0+
Context-based DI system
- Flexible decorator-based or function-based injection
- Full type inference support āØ
- Optional decorators with equivalent function alternatives
Example:
1import {createContainer, Inject, inject, Injectable, Scope, Scoped, Type} from "di-wise"; 2 3interface Spell { 4 cast(): void; 5} 6const Spell = Type<Spell>("Spell"); 7 8@Scoped(Scope.Container) 9@Injectable(Spell) 10class Fireball implements Spell { 11 cast() { 12 console.log("š„"); 13 } 14} 15 16class Wizard { 17 @Inject(Wand) 18 wand!: Wand; 19 20 // Equivalent to 21 wand = inject(Wand); 22 23 constructor(spell = inject(Spell)) { 24 // inject() can be used anywhere during construction 25 this.wand.store(spell); 26 } 27} 28 29const container = createContainer(); 30container.register(Fireball); 31 32// Under the hood 33[Fireball, Spell].forEach((token) => { 34 container.register( 35 token, 36 {useClass: Fireball}, 37 {scope: Scope.Container}, 38 ); 39}); 40 41const wizard = container.resolve(Wizard); 42wizard.wand.activate(); // => š„
Multiple provider types
- Class, Factory, and Value providers
- Built-in helpers for one-off providers:
Build()
,Value()
- Seamless integration with existing classes
Example:
1import {Build, createContainer, inject, Value} from "di-wise"; 2 3class Wizard { 4 equipment = inject( 5 Cloak, 6 // Provide a default value 7 Value({ 8 activate() { 9 console.log("š»"); 10 }, 11 }), 12 ); 13 14 wand: Wand; 15 16 constructor(wand: Wand) { 17 this.wand = wand; 18 } 19} 20 21const container = createContainer(); 22 23const wizard = container.resolve( 24 Build(() => { 25 // inject() can be used in factory functions 26 const wand = inject(Wand); 27 return new Wizard(wand); 28 }), 29); 30 31wizard.equipment.activate(); // => š»
Hierarchical injection
- Parent-child container relationships
- Automatic token resolution through the container hierarchy
- Isolated registration with shared dependencies
Example:
1import {createContainer, inject, Injectable, Type} from "di-wise"; 2 3const MagicSchool = Type<string>("MagicSchool"); 4const Spell = Type<{cast(): void}>("Spell"); 5 6// Parent container with shared config 7const hogwarts = createContainer(); 8hogwarts.register(MagicSchool, {useValue: "Hogwarts"}); 9 10@Injectable(Spell) 11class Fireball { 12 school = inject(MagicSchool); 13 cast() { 14 console.log(`š„ from ${this.school}`); 15 } 16} 17 18// Child containers with isolated spells 19const gryffindor = hogwarts.createChild(); 20gryffindor.register(Fireball); 21 22const slytherin = hogwarts.createChild(); 23slytherin.register(Spell, { 24 useValue: {cast: () => console.log("š")}, 25}); 26 27gryffindor.resolve(Spell).cast(); // => š„ from Hogwarts 28slytherin.resolve(Spell).cast(); // => š
Full control over registration and caching
- Explicit container management without global state
- Fine-grained control over instance lifecycle
- Transparent registry access for testing
Various injection scopes
- Flexible scoping system:
Inherited
(default),Transient
,Resolution
,Container
- Smart scope resolution for dependencies
- Configurable default scopes per container
Example for singleton pattern:
1import {createContainer, Scope} from "di-wise"; 2 3export const singletons = createContainer({ 4 defaultScope: Scope.Container, 5 autoRegister: true, 6}); 7 8// Always resolves to the same instance 9const wizard = singletons.resolve(Wizard);
Inherited (default)
Inherits the scope from its dependent. If there is no dependent (top-level resolution), behaves like Transient
.
Example
1import {createContainer, Scope, Scoped} from "di-wise"; 2 3@Scoped(Scope.Container) 4class Wizard { 5 wand = inject(Wand); 6} 7 8const container = createContainer(); 9container.register( 10 Wand, 11 {useClass: Wand}, 12 {scope: Scope.Inherited}, 13); 14container.register(Wizard); 15 16// Dependency Wand will be resolved with "Container" scope 17const wizard = container.resolve(Wizard);
Transient
Creates a new instance every time the dependency is requested. No caching occurs.
Resolution
Creates one instance per resolution graph. The same instance will be reused within a single dependency resolution, but new instances are created for separate resolutions.
Example
1@Scoped(Scope.Resolution) 2class Wand {} 3 4class Inventory { 5 wand = inject(Wand); 6} 7 8class Wizard { 9 inventory = inject(Inventory); 10 wand = inject(Wand); 11} 12 13const container = createContainer(); 14const wizard = container.resolve(Wizard); 15 16expect(wizard.inventory.wand).toBe(wizard.wand);
Container
Creates one instance per container (singleton pattern). The instance is cached and reused for all subsequent resolutions within the same container.
Flexible token-based injection
- Multiple token resolution with union type inference āØ
- Support for optional dependencies via
Type.Null
andType.Undefined
- Interface-based token system
Example:
1import {inject, Type} from "di-wise"; 2 3class Wizard { 4 wand = inject(Wand, Type.Null); 5 // ^? (property) Wizard.wand: Wand | null 6 7 spells = injectAll(Spell, Type.Null); 8 // ^? (property) Wizard.spells: Spell[] 9 // => [] 10}
Automatic circular dependency resolution
- Smart handling of circular dependencies
- Multiple resolution strategies (
@Inject()
orinject.by()
) - Maintains type safety
Example:
1import {createContainer, Inject, inject} from "di-wise"; 2 3class Wand { 4 owner = inject(Wizard); 5} 6 7class Wizard { 8 @Inject(Wand) 9 wand!: Wand; 10 11 // Equivalent to 12 wand = inject.by(this, Wand); 13} 14 15const container = createContainer(); 16const wizard = container.resolve(Wizard); 17 18expect(wizard.wand.owner).toBe(wizard);
Dynamic injection
- On-demand dependency resolution via
Injector
- Context-aware lazy loading
- Preserves proper scoping and circular dependency handling
Example:
1import {createContainer, inject, Injector} from "di-wise"; 2 3class Wizard { 4 private injector = inject(Injector); 5 private wand?: Wand; 6 7 getWand() { 8 // Lazy load wand only when needed 9 return (this.wand ??= this.injector.inject(Wand)); 10 } 11 12 castAllSpells() { 13 // Get all registered spells 14 const spells = this.injector.injectAll(Spell); 15 spells.forEach((spell) => spell.cast()); 16 } 17} 18 19const container = createContainer(); 20const wizard = container.resolve(Wizard); 21 22wizard.getWand(); // => Wand
The injector maintains the same resolution context as its injection point, allowing proper handling of scopes and circular dependencies:
1import {createContainer, inject, Injector} from "di-wise"; 2 3class Wand { 4 owner = inject(Wizard); 5} 6 7class Wizard { 8 private injector = inject.by(this, Injector); 9 10 getWand() { 11 return this.injector.inject(Wand); 12 } 13} 14 15const container = createContainer(); 16const wizard = container.resolve(Wizard); 17 18const wand = wizard.getWand(); 19expect(wand.owner).toBe(wizard);
Constructor Injection
See discussion Does di-wise support constructor injection? #12
Middleware
- Extensible container behavior through middleware
- Composable middleware chain with predictable execution order
- Full access to container lifecycle
Example:
1import {applyMiddleware, createContainer, type Middleware} from "di-wise"; 2 3const logger: Middleware = (composer, _api) => { 4 composer 5 .use("resolve", (next) => (token) => { 6 console.log("Resolving:", token.name); 7 const result = next(token); 8 console.log("Resolved:", token.name); 9 return result; 10 }) 11 .use("resolveAll", (next) => (token) => { 12 console.log("Resolving all:", token.name); 13 const result = next(token); 14 console.log("Resolved all:", token.name); 15 return result; 16 }); 17}; 18 19const performanceTracker: Middleware = (composer, _api) => { 20 composer.use("resolve", (next) => (token) => { 21 const start = performance.now(); 22 const result = next(token); 23 const end = performance.now(); 24 console.log(`Resolution time for ${token.name}: ${end - start}ms`); 25 return result; 26 }); 27}; 28 29const container = applyMiddleware(createContainer(), [logger, performanceTracker]); 30 31// Use the container with applied middlewares 32const wizard = container.resolve(Wizard);
Middlewares are applied in array order but execute in reverse order, allowing outer middlewares to wrap and control the behavior of inner middlewares.
Usage
šļø WIP (PR welcome)
API
See API documentation.
Credits
Inspired by:
License
MIT License @ 2024-Present Xuanbo Cheng
No vulnerabilities found.
No security vulnerabilities found.