Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/nikku/didi
Dependency Injection for JavaScript
https://github.com/nikku/didi
dependency-injection ioc javascript nodejs
Last synced: about 20 hours ago
JSON representation
Dependency Injection for JavaScript
- Host: GitHub
- URL: https://github.com/nikku/didi
- Owner: nikku
- License: mit
- Created: 2014-03-19T15:46:11.000Z (almost 11 years ago)
- Default Branch: main
- Last Pushed: 2024-12-02T17:51:29.000Z (20 days ago)
- Last Synced: 2024-12-15T03:02:15.593Z (8 days ago)
- Topics: dependency-injection, ioc, javascript, nodejs
- Language: JavaScript
- Homepage:
- Size: 683 KB
- Stars: 95
- Watchers: 3
- Forks: 17
- Open Issues: 3
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE
Awesome Lists containing this project
README
# `didi`
[![CI](https://github.com/nikku/didi/actions/workflows/CI.yml/badge.svg)](https://github.com/nikku/didi/actions/workflows/CI.yml)
A tiny [inversion of control](https://en.wikipedia.org/wiki/Inversion_of_control) container for JavaScript.
## About
Using [`didi`](https://github.com/nikku/didi) you follow the [dependency injection](https://en.wikipedia.org/wiki/Dependency_injection) / [inversion of control](https://en.wikipedia.org/wiki/Inversion_of_control) pattern, decoupling component declaration from instantiation. Once declared, `didi` instantiates components as needed, transitively resolves their dependencies, and caches instances for re-use.
## Example
```ts
import { Injector } from 'didi';function Car(engine) {
this.start = function() {
engine.start();
};
}function createPetrolEngine(power) {
return {
start: function() {
console.log('Starting engine with ' + power + 'hp');
}
};
}// define a (didi) module - it declares available
// components by name and specifies how these are provided
const carModule = {// asked for 'car', the injector will call new Car(...) to produce it
'car': ['type', Car],// asked for 'engine', the injector will call createPetrolEngine(...) to produce it
'engine': ['factory', createPetrolEngine],// asked for 'power', the injector will give it number 1184
'power': ['value', 1184] // probably Bugatti Veyron
};// instantiate an injector with a set of (didi) modules
const injector = new Injector([
carModule
]);// use the injector API to retrieve components
injector.get('car').start();// alternatively invoke a function, injecting the arguments
injector.invoke(function(car) {
console.log('started', car);
});// if you work with a TypeScript code base, retrieve
// a typed instance of a component
const car: Car = injector.get('car');car.start();
```For real-world examples, check out [Karma](https://github.com/karma-runner/karma) or [diagram-js](https://github.com/bpmn-io/diagram-js), two libraries that heavily use dependency injection at their core. You can also check out [the tests](https://github.com/nikku/didi/blob/master/test/injector.spec.js) to learn about all supported use cases.
## Usage
Learn how to [declare](#declaring-components), [inject](#injecting-components) and [initialize](#initializing-components) your components.
### Declaring Components
By declaring a component as part of a `didi` module, you make it available to other components.
#### `type(token, Constructor)`
`Constructor` will be called with `new` operator to produce the instance:
```js
const module = {
'engine': ['type', DieselEngine]
};
```#### `factory(token, factoryFn)`
The injector produces the instance by calling `factoryFn` without any context. It uses the factory's return value:
```js
const module = {
'engine': ['factory', createDieselEngine]
};
```#### `value(token, value)`
Register a static value:
```js
const module = {
'power': ['value', 1184]
};
```### Injecting Components
The injector looks up dependencies based on explicit annotations, comments, or function argument names.
#### Argument Names
If no further details are provided the injector parses dependency names from function arguments:
```js
function Car(engine, license) {
// will inject components bound to 'engine' and 'license'
}
```#### Function Comments
You can use comments to encode names:
```js
function Car(/* engine */ e, /* x._weird */ x) {
// will inject components bound to 'engine' and 'x._weird'
}
```#### `$inject` Annotation
You can use a static `$inject` annotation to declare dependencies in a minification safe manner:
```js
function Car(e, license) {
// will inject components bound to 'engine' and 'license'
}Car.$inject = [ 'engine', 'license' ];
```#### Array Notation
You can also the minification save array notation known from [AngularJS][AngularJS]:
```js
const Car = [ 'engine', 'trunk', function(e, t) {
// will inject components bound to 'engine' and 'trunk'
}];
```#### Partial Injection
Sometimes it is helpful to inject only a specific property of some object:
```js
function Engine(/* config.engine.power */ power) {
// will inject 1184 (config.engine.power),
// assuming there is no direct binding for 'config.engine.power' token
}const engineModule = {
'config': ['value', {engine: {power: 1184}, other : {}}]
};
```### Initializing Components
Modules can use an `__init__` hook to declare components that shall eagerly load or functions to be invoked, i.e., trigger side-effects during initialization:
```javascript
import { Injector } from 'didi';function HifiComponent(events) {
events.on('toggleHifi', this.toggle.bind(this));this.toggle = function(mode) {
console.log(`Toggled Hifi ${mode ? 'ON' : 'OFF'}`);
};
}const injector = new Injector([
{
__init__: [ 'hifiComponent' ],
hifiComponent: [ 'type', HifiComponent ]
},
...
]);// initializes all modules as defined
injector.init();
```### Overriding Components
You can override components by name. That can be beneficial for testing but also for customizing:
```js
import { Injector } from 'didi';import coreModule from './core';
import HttpBackend from './test/mocks';const injector = new Injector([
coreModule,
{
// overrides already declared `httpBackend`
httpBackend: [ 'type', HttpBackend ]
}
]);
```### Type-safety
[`didi`](https://github.com/nikku/didi) ships type declarations that allow you to use it in a type safe manner.
#### Explicit Typing
Pass a type attribute to `Injector#get` to retrieve a service as a known type:
```typescript
const hifiComponent = injector.get('hifiComponent');// typed as
hifiComponent.toggle();
```#### Implicit Typing
Configure the `Injector` through a service map and automatically cast services
to known types:```typescript
type ServiceMap = {
'hifiComponent': HifiComponent
};const injector = new Injector(...);
const hifiComponent = injector.get('hifiComponent');
// typed as
```## Credits
This library builds on top of the (now unmaintained) [node-di][node-di] library. `didi` is a maintained fork that adds support for ES6, the minification safe array notation, and other features.
## Differences to [node-di][node-di]
- supports array notation
- supports [ES2015](http://babeljs.io/learn-es2015/)
- bundles type definitions
- module initialization + module dependencies## License
MIT
[AngularJS]: http://angularjs.org/
[node-di]: https://github.com/vojtajina/node-di