@dliv/minject
v0.1.2
Published
A Javascript Dependency Injection library built with an emphasis on constructor injection and ES6 classes.
Downloads
2
Readme
Minject
Minject is a Javascript Dependency Injection library built with an emphasis on constructor injection and ES6 classes. The 'M' is for 'map' and/or 'minimal'. Map because constructor arguments are injected as a single object for easy destructuring. Minimal because it has (a lot) fewer features than other libraries (Angular's module system, BottleJS, etc).
Minject is also tiny: 2.5KB before gzip.
Getting Started
The Minject module can be loaded as:
- A CommonJS / Node module available as the npm package @dliv/minject
- A
script
tag (creates the global variableMinject
when dist/index.js is included on the page)
API and Examples
The most comprehensive documentation is the set of spec files in the src folder and the ESDocs associated with each public method.
Minject (constructor)
The constructor does not accept any options. That should probably change.
ES6 module
Install with npm i @dliv/minject
. Then:
import Minject from '@dliv/minject';
const services = new Minject();
Minject is a scoped npm module; ostensibly because of the left-pad issue (but really because some Haxe thing already has the name).
script tag
Include dist/index.js on your page. Then:
var services = new window.Minject();
chaining
Methods for adding values / services are chainable.
services
.value('debugMode', false)
.eagerService('Water', Water)
.lazyService('ColdBrew', ColdBrew, { water: 'Water', coffee: 'Coffee', brewTime: 'BrewTime' })
.service('Coffee', Coffee)
.service('BrewTime', BrewTime);
const coldBrew = services.get('ColdBrew'); // `services.get(name)` can't return `services` for obvious reasons
Minject#value(serviceName, serviceValue)
services.value('name', value);
Registers a value that is already constructed as an injectible dependency for services when requested by
@param name. The @param value will not be created with new
, invoked, or mutated - it is passed as is when asked
for by a service.
Minject#service(serviceName, serviceConstructor[, dependencies])
Registers a service name and constructor pair, with optional dependencies for the constructor. The created service is a cached singleton (it can be retrieved multiple times but only one instance will be created).
class Water {
constructor () {
// ...
}
}
class Coffee {
constructor ({ water, coffee, time }) {
// ...
}
brew () {
const { water, coffee, time } = this._services;
// ...
}
}
class AngularCoffee {
constructor ({ water, coffee, time }) {
// ...
}
}
AngularCoffee.$inject = { water: 'ColdWater', coffee: 'CoarseGroundCoffee', time: 'OverNight '};
// or `AngularCoffee.$inject = ['water', 'coffee', 'time'];`
class ImplicitCoffee {
constructor ({ water, coffee, time }) {
// ...
}
}
ImplicitCoffee._inject = { water: 'ColdWater', coffee: 'CoarseGroundCoffee', time: 'OverNight '};
// or `ImplicitCoffee._inject = ['water', 'coffee', 'time'];`
services
.service('water', Water)
.service('ColdBrew', Coffee, { water: 'ColdWater', coffee: 'CoarseGroundCoffee', time: 'OverNight '})
.service('BasicCoffee', Coffee, ['water', 'coffee', 'time'])
.service('AngularCoffee', AngularCoffee)
.service('ImplicitCoffee', ImplicitCoffee);
The dependencies param can be an array of registered service names or an object mapping constructor
param name to registered dependency name; when using the array syntax, the constructor param name will
be inferred (as equal to) the dependency's name. If the dependency param is omitted, the constructor will
also be checked for a (static) property named $inject
or _inject
.
As a side effect, after the constructor is invoked, a _services
property is added to the constructed
service; this convenience means the constructor does not have to save every service it is passed.
Dependencies are looked up when the service is created; lazy services can specify dependencies that have
not been registered yet, while eager services can only use already registered services as dependencies.
It is unspecified whether Minject#service
is constructed lazily or eagerly. The current default is eager
(to surface errors sooner, at the expense of potentially creating services that are never injected).
WARNING: may register a lazy service in a future version.
Minject#eagerService(serviceName, serviceConstructor[, dependencies])
Registers a dependency name and value; immediately creates the value. See Minject#service
.
class Eager {
constructor () {
console.log('Eager service created.');
}
}
services.eagerService('Eager', Eager);
// immediately logs 'Eager service created.'
// but service may never be injected or retrieved with `Minject#get`
Minject#lazyService(serviceName, serviceConstructor[, dependencies])
Registers a dependency name and value; delays creating the value until it is needed. See Minject#service
.
class LazyPi {
constructor () {
console.log('LazyPi created.');
this.value = this._calculatePi();
}
}
class CircleAreaCalculator {
constructor ({ pi }) {
console.log('CircleAreaCalculator created.');
// ...
}
}
services.lazyService('pi', LazyPi);
// <nothing logged>
services.eagerService('Calculator', CircleAreaCalculator, ['pi']);
// logs 'LazyPi created.'
// then 'CircleAreaCalculator created.'
Minject#get(serviceName)
Returns the specified service; will create (with dependencies) and cache the service if needed. If you can organize your app as a single (eager) service, you don't need this method. Overuse of this method will lead to the Service Locator pattern and is discouraged.
WARNING: may be renamed, removed, or limited in a future version.
// detect-env.js
services.value('$window', typeof window === 'undefined' ? mockWindow : window);
// controller1.js
const $window = services.get('$window');
Status
Very beta. Besides the limited functionality, lots of error checking is missing (e.g. checking for circular dependencies).