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

sl-module-loader

v0.0.3

Published

separate your app into modules loaded by config files

Downloads

4

Readme

sl-module-loader

v0.0.1

Purpose

The sl-module-loader allows your program to register classes (or types) that are instantiated via configuration files. Configuration files point to an implementation module constructor. The module's job is to construct a useful instance with the given configuration options. This allows programs to be free from bootstrapping code and manageable via config.

Install

slnode install sl-module-loader

Example

Given a simple Dog module:

function Dog(options) {
  this.options = options;
}

Dog.prototype.speak = function() {
  console.log('roof', 'my name is', this.options.name);
}

module.exports = Dog;

And a set of config.json files:

/my-app
  /fido
    config.json
  /santas-little-helper
    config.json
  /rex
    config.json
  /node_modules
    /dog
      index.js
  package.json

Where a config.json looks like this:

{
  "module": "dog", // the "dog" module
  "options": {
    "name": "fido"
  }
}

We can load up all the dogs like so (app.js):

var moduleLoader = require('sl-module-loader').create('my-app');

moduleLoader.load(function (err, modules) {
  if(err) throw err;

  moduleLoader
    .instanceOf('dog') // a module in node_modules or declared as a dependency in package.json
    .forEach(function (m) {
      m.speak();
    });
});

The above calls a method on all module instances that inherit from Dog and outputs:

roof my name is fido
roof my name is santa's little helper
roof my name is rex

Creating Module Classes

The purpose of a module class is to take meaningful input (configuration, options, dependencies) and create a useful output: a module instance.

Module Classes

A module class is a node_module that exports a constructor inheriting from the Module class.

var inherits = require('util').inherits;
var Module = require('sl-module-loader').Module;

module.exports = Dog;

function Dog(options) {
  this.options = options;
}

inherits(Dog, Module);

Dog.prototype.speak = function() {
  console.log('roof', 'my name is', this.options.name);
}

Module classes may define dependency contracts that tell the module loader to provide dependencies of a given module class during construction.

function MyComplexModule() {
  Module.apply(this, arguments);
  console.log('loaded dependencies', this.dependencies); // {'my-dependency': <module instance>}
}

MyComplexModule.dependencies = {
  'my-dependency': 'another-module-class'
}

Module Class Options

Module classes may also describe the options they accept. This will validate the configuration of module instance and guarantee the module class constructor has enough information to construct an instance.

Here is an example options description for a database connection module class.

DatabaseConnection.options = {
  'hostname': {type: 'string', required: true},
  'port': {type: 'number', min: 10, max: 99999},
  'username': {type: 'string'},
  'password': {type: 'string'}
};

key the option name given in config.json.

type must be one of:

  • string
  • boolean
  • number
  • array

min/max depend on the option type

{
  min: 10, // minimum length or value
  max: 100, // max length or value
}

Module Events

Module classes may also emit and listen to events. By default a Module will emit the following events:

destroy

Emitted when a module instance is being destroyed during a moduleLoader.reset(). Modules should cleanup any connections and unbind all event listeners.

Configuration

Each module instance is defined by creating a config.json file in a directory with the module's name.

/my-module-instance
  config.json
  other-files.txt
  index.js
  

This directory should contain files related to the module instance. For example it might contain a script that require()s the module instance.

config.module

The node module name that exports the module class that constructs the config's module instance.

{
  "module": "my-module-class"
}

config.options

Defines arbitrary options. A file-upload module might have an uploads option.

{
  "module": "file-upload",
  "options": {
    "uploads": "/tmp"
  }
}

config.dependencies

Defines other module instances the configured instance depends on.

{
  "module": "collection"
  "dependencies": {
    "db": "my-db-module"
  },
  "options": {
    "collection-name": "my-collection"
  }
}

Where my-db-module's config looks like this:

{
  "module": "couchdb-connector",
  "options": {
    "database": "my-db"
  }
}

config.env.json

Separate file that overrides config.json depending on the current NODE_ENV. Useful for including config information that is environment specific or should not be committed to source control.

{
  // overrides for all envs
  "*": {
    "options": {
      "upload-dir": "/uploads"
    }
  },
  "dev": {
    "options": {
      "upload-dir": "/dev-uploads"
    }
  }
}

Requiring Modules

To use module instances, you can require() them anywhere in your program like you normally would require a node module. For example, you can get a reference to the fido object like this:

var fido = require('fido'); // `fido` is the directory name containing the fido module config

Require Behavior

After your program runs require('sl-module-loader') the require() function's behavior will change slightly to make referencing module instances simpler. Since some module instances may not have any program specific code, they can't be require()d with node's existing require() implementation.

Config Loader

sl-module-loader inherits from sl-config-loader.

Bundled Modules / Aliasing

Some modules need to be distributed together. For example, you have a set of related modules that all live under a single version number since they depend on features from each other. In this case you should bundle your sub modules using the package.json bundledDependencies array.

Reference bundled modules by relative location (just like require).

// config.json
{
  "module": "myBundle/node_modules/foo"
}

You may also provide aliases to any module path when creating a ModuleLoader.

var moduleLoader = require('sl-module-loader').create('my-app', {alias: {'foo': 'myBundle/node_modules/foo'}});

Now the config can reference foo instead of the qualified path.

// config.json
{
  "module": "foo"
}