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

@mitmaro/dependency-manager

v0.1.0

Published

A node dependency loader and manager

Downloads

4

Readme

Node Dependency Manager

Dependency Status Build Status Coverage Status NPM version GitHub license Known Vulnerabilities

Motivation

Wiring together dependant pieces of a JavaScript project can be difficult and tedious. This library aims to provide a mechanism to manage and provide the dependant parts of a JavaScript project.

Install

npm install --save @mitmaro/dependency-manager

Documentation

Usage

Creating an instance

Creating a dependency manager instance is pretty straight forward.

const {DependencyManager} = require('@mitmaro/dependancy-manager');
const dependencyManager = new DependencyManager({
    // options
});

Options

|Name |Type |Description |Default | |------------------|----------|--------------------------------------------------------------|--------| |argumentInjection |boolean |Inject dependencies as arguments instead as an object |false | |snakeCase |boolean |Use snake case instead of camel case for creating identifiers |false |

argumentInjection

By default the dependencies are injected as an object with keys based on the dependency name. This options will instead provide the dependencies as arguments to the factory function based on the order of the dependency list. The option snakeCase has no visible effect when using this option.

const serviceFactory = (config, database) => {
	// ...
}
dependencyManager.register('service', serviceFactory, ['config', 'database']);
snakeCase

Generally dependencies names are converted to the parameter name in the dependency object using camel case, by providing this option they will instead be snake cased.

const serviceFactory = ({database_connection}) => {
	// ...
}
dependencyManager.register('service', serviceFactory, ['DatabaseConnection']);

Set a static value

The most simple type of dependency is a constant static value. You can provide a constant value using the set method.

dependencyManager.set('configuration', {
    databaseConnectionUrl: 'postgresql://localhost:5432/',
});

Static values should only exist for truly static values. If you are using static with a result of a function call or with an instance created with new you should be using a factory function.

Register a Factory functions

The way to define an item, that may requires other dependencies, is with a factory function. The factory function takes the optional dependencies and returns an instance of the dependency. For example to create a database connection that requires a database connection configuration you would have the factory function:

// `configuration` is the constant value set in the previous section
const databaseFactory = ({configuration, database}) => database.connect(configuration.databaseConnectionUrl);

const serviceFactory = ({database}) => (id) => {
    return database.query('SELECT * FROM foo WHERE id = ${id}', {id});
};

dependencyManager.register('database', databaseFactory, ['config']);
dependencyManager.register('service', serviceFactory, ['database']);

Aliasing

Sometimes it is useful to provide an alternative name, an alias, to a dependency. This works with constant and factory dependencies. Aliases follow the same naming rules as dependencies.

dependencyManager.alias('database', 'db');
dependencyManager.alias('configuration', 'config');

Naming

A dependency name must only contain alphanumeric characters, $, _ and - characters; and it must not must begin with a number. The name will be converted to camel cases (or snake case if the option is selected) when it is injected into a factory function with object injection.

Namespaces

Most large projects will have dependencies that can be categorized. For example there could be a number of libraries, utilities or services. In these cases an optional namespace can be prepended to the name to group these common dependencies. When registering or setting a dependency use he naming format of namespace:name. A namespaced dependency has a slightly different object injection format of nameNamespace with namespace following the same rules defined in above for dependency names.

const serviceFactory = ({databaseLib}) => (id) => {
    return databaseLib.query('SELECT * FROM foo WHERE id = ${id}', {id});
};

dependencyManager.register('lib:database', databaseFactory, ['config']);
dependencyManager.register('service', serviceFactory, ['lib:database']);

Loading

Once all the dependencies are registered, the next step is to load the dependencies. This is achieved using the load method and it's usage is pretty straight forward:

dependencyManager.load()
    .then(() => {
        console.log('Dependencies are all loaded');
        const service = depdenencyManager.get('service');
        return service('my_id');
    })
    .catch((err) => {
        console.error('An error occurred while loading dependencies');
        console.error(err);
    });

Best Practices

Be immutable

When ever possible the injected dependencies should be immutable. in that once the dependency is set, it should not be possible to change that dependency. For example:

function myService({dependency}) {
    let myDependency = dependency;
    return {
        // this is a bad function
        setDependency(newDependency) {
            myDependency = newDependency;
        }
    }
}

Inject direct dependencies only

While it might be tempting, avoid using the dependencies of a dependencies as this creates a tight coupling. For example do not do:

function myService({dependency}) {
    let mySubDependency = dependency.subDependency;
    // ...
}

The alternative is to instead directly inject the dependency:

function myService({dependency, subDependency}) {
    // ...
}

Resolving cyclic dependencies

Sometimes you will have one dependency, say FooService that has a dependency on BarService. BarService in turn has a direct, or indirect, dependency on FooService. This will result in an error:

Cycle detected in dependencies: FooService > BarService > FooService

Assuming the cycle is not an error, there are a couple ways to resolve the cycle.

Remove the cycle

Most often a cycle is a sign that the a dependency is performing an action that it should not. To resolve the cycle extract the dependant functionality into a separate shared dependency.

Inject a provider function

Sometimes it is not possible to remove the dependency cycle, in this case you can use a factory function to create a dependency. For example:

function createFooService({anotherDependency, barService}) {
	// ...
}

function fooServiceProvider({anotherDependency}) {
    let fooService;
    return {
        create(barService) {
            fooService = createFooService({anotherDependency, barService});
            return fooService;
        },
        get() {
            return fooService;
        }
    }
}

function createBarService({fooServiceProvider}) {
    let fooService;
    const bar = {
        callFooService() {
            fooService()
        }
    };
    fooService = fooServiceProvider.create(bar);
    return bar;
}
dependencyManager.register('fooServiceProvider', fooServiceProvider, ['anotherDependency']);
dependencyManager.register('barService', fooServiceProvider, ['fooServiceProvider']);

The service provider above creates a singleton instance of fooService, but this is optional if multiple instances of fooService are desired.

Avoid side effects

Often you will have a dependency that will make an interaction to an external system, such as opening a database connection, loading a file for writing, or starting a HTTP server. Avoid performing these, and similar, actions in a factory function. Instead create a service interface that exposes a function to start and optionally stop the action. Then call the start and stop functions as part of your application bootstrap and shutdown. For example:

function httpService({http}) {
	let httpConnection;
	const service = {
		start() {
			return http
				.start()
				.then((connection) => {
					httpConnection = connection;
				});
		},
		stop() {
			return httpConnection.end();
		}
	}
}

Development

Development is done using Node 8 and NPM 5, and tested against both Node 6 and Node 8. To get started

  • Install Node 8 from NodeJS.org or using nvm
  • Clone the repository using git clone [email protected]:MitMaro/node-dependency-manager.git
  • cd node-dependency-manager
  • Install the dependencies npm install
  • Make changes, add tests, etc.
  • Run linting and test suite using npm run test

License

This project is released under the ISC license. See LICENSE.