Fount provides synchronous and asynchronous dependency resolution.
npm i fount
const fount =
A note about style and safety
Fount supports two styles of identifying dependency: string array (like AMD) and by argument names (like Angular).
// argument namefount// string arrayfount
The second approach is easiest to work with, but keep in mind that if using this in the browser, a minifier will change the argument names and break resolution.
The key with any approach is always test your code.
Fount supports multiple containers. If you don't specify one when making calls, it will use the
default container. Most examples in this doc use the default container implicitly.
You can use a named container in two ways:
- pass the container name in parenthesis before making the call (all commands)
- use period delimited namespaces in your key names (all commands except purge)
// same as above, but terserfount
Scope & Lifecycles
Chances are you won't need this - you'll register dependencies and resolve them and never think about lifecycle or scope. When you do need more control, fount allows you to supply lifecycle arguments during registration and an optional scope name during resolution that give you more granular control over resolution.
A scope behaves like a second level of caching that gets written to/read from at resolution time (rather than at registration time). It can only affect the resolution of dependencies that are not determined at registration time (functions with a
factory lifecycle) and
factory lifecycles prevent fount from checking scope at during resolution.
When resolving a dependency a scope of
default is used if none is specified. You can purge a scope anytime with
// purges scope `myScope` out of `default` containerfount// purges all scopes in `default` containerfount// purge scope from a specific container// purge all scopes from a specific container
Lifecycle ( static, scoped or factory )
A lifecycle tells fount how long the result of a function dependency is good for. Static is the the default and, when possible, fount will resolve and store the value for static depenencies at registration time.
- static - once a value is returned, it will always be returned for future resolution
- scoped - like static but resolved once per scope (specified by name)
- factory - if the dependency is a function it will re-evaluated every time
It's worth reinforcing that
factory dependencies, because they can change at resolution time, are not evaluated eagerly at registration.
Registering is simple - provide a string name and then supply either a value, function or promise. See each section for more detail.
// lifeCycle is optional and 'static' by defaultfount
Once a value is registered, fount will always supply that value to any resolve call. It doesn't actually make sense to provide a lifecycle option with a value or promise since it has no effect, but fount doesn't freak out if you forget and do this by accident.
Registering a function with fount will cause it to invoke the function during resolution and return the result with two exceptions:
- The function is a stub or some other abstraction that cannot have dependencies resolved for it
- The function has dependencies which do not exist for fount
In these exceptional scenarios, fount will resolve the dependency with the function itself rather than calling it for you.
Registering functions with dependencies
If you want fount to inject dependencies into the function when calling it, you'll need to provide a string array of the dependencies in the order they should be supplied to the function:
// AMD users should feel right at home with this stylefount// Angular fans may enjoy this style// dependencies are 'read' out of the argument listfount
Registering functions as values
You may want to register a function as a value (confused yet?) so that fount returns the function as a dependency rather than executing it for you. If that's what you're looking for, try this:
// this really is just wrapping the value in a function like above, but it's easier to read// and hopefully less frustratingfount
Registering a promise looks almost identical to registering a function. From a consuming perspective, they're functionally equivalent since fount will wrap raw function execution in a promise anyway.
Fount will allow you to plug in an NPM module. If the module was previously loaded, it will grab it from the require cache, otherwise, it will attempt to load it from the modules folder:
Note: this method will register modules that return a function as a factory which will be invoked anytime the module is resolved as a dependency
In the example above, where
when is regsitered, fount will see that it is a function and register it as a factory. During resolve time, because fount cannot resolve the argument list for
when's function, it will simply provide the
when function as the value.
Synchronous vs. Asynchronous
Fount provides two sets of of functions for working with dependencies; one for when the dependencies are known to contain one or more promises and one for when the dependency chain is promise-free.
Each set consists of two functions; one for retrieving one or more dependencies directly and one for injecting dependencies into a function and calling it.
WARNING: Fount cannot guarantee a synchronous return or dependency chain. Using the synchronous methods for a dependency that contains a promise will result in unexpected results.
These methods return a promise that resolves once the dependency can be satisfied.
Resolving is pretty simple - fount will always return a promise to any request for a dependency.
fount// get the value for scope 'custom'fount// resolve multiple dependencies at once!fount// resolve multiple dependencies ACROSS containersfount
Injecting is how you get fount to apply resolved dependencies to a function. It can be used the same way as AMD's define, with the arguments specified as strings, or, with the argument names parsed from the call itself.
// where 'a' and 'b' have been registeredfount;fount;// within custom scope -- requires a and/or b to have been registered with 'scoped' lifecyclefount;fount;// using keys across multiple containersfount;// alternate support for multiple containersfount;
These methods return a value immediately and assume that the dependencies involved do not contain promises. Remember: if a promise is encountered, it will result in errors/nonsense.
As the name implies,
get simply returns the value of the key from the container. It does still work like resolve in terms of plugging in dependency chains, but it does not resolve promises between dependency levels.
// returns the value of gimme immediatelylet gimme = fount// the value for scope 'custom'let scoped = fount// resolve multiple dependencies at once!let one two = fount// resolve multiple dependencies ACROSS containerslet hash = fount
Invoking is how you get fount to apply dependencies to a function without promise resolution. It can be used the same way as AMD's define, with the arguments specified as strings, or, with the argument names parsed from the call itself.
Again: any promises encountered will result in odd behavior - write tests :)
// where 'a' and 'b' have been registeredfountfount// within custom scope -- requires a and/or b to have been registered with 'scoped' lifecyclefountfount// using keys across multiple containersfount// alternate support for multiple containersfount
To check whether fount is able to resolve a dependency ahead of time, use
Configuration of multiple containers, keys and values can be accomplished via a configuration hash passed to fount. The format of the hash is as follows:
containerName:keyName: valuekeyName:scope: value
Fount will return an array of all keys registered:
// top level includes all namespacesfount// limited by container
Fount provides three different ways to clean up:
- ejecting all keys and resolved scope values from all containers
- ejecting all keys and resolved scope values from one container
- removing all resolved scoped values from one container
Note: purge doesn't support the
.delimited syntax for containers. When purging non-default containers, select the container first like in the examples below:
// this is like starting from scratch:fount// remove all values for the default container's custom scope// this does not remove the keys, just their resolved valuesfount// eject all keys from the default containerfount// remove all values for the myContainer's custom scope// eject all keys from myContainer
Right now this is pretty weak, but if you call
log, Fount will dump the containers and scopes out so you can see what keys are present. Got ideas for more useful ways to troubleshoot? I'd love a PR 😄!
Tests & CI Mode
npm testruns the tests once
npm run coveragegets a coverage report
npm run continuousruns mocha in watch mode
npm run releasewill cut a new standard version, update the changelog and tag the last commit.