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

polite-plugin-manager

v0.0.6

Published

Stuning plugin manager for NodeJS

Downloads

86

Readme

polite-plugin-manager

A cool plugin manager for your NodeJS projects!

Polite Plugin Manager is an atomic library with which add plugin capabilities to any NodeJS project enhancing behavior scalability.

Key points during the developent of this library were:

  • to be ridicolous easy to declare hooks
  • to be ridicolous easy to register plugin packages
  • to be ridicolous easy to handle asynchronous plugins

I really think all these points have been reached very well.
I've also added a really fine grained control on:

  • plugins initialization cycle
  • series / parallel / waterfall behaviors
  • stoppable plugin chain

What's Next

I want to better explore plugins package organization to let a plugin package to spread on multiple files.

I also want to explore in-plugin package test support to let every plugin to be fully testable before it's release.

Tests

To grant code quality I wrote many test for this module!

After run npm install to load all dev-dependencies you should run:

// run tests
grunt

// run tests and show a test coverage report
grunt specs

Install

The latest version of this package is always published to the NPM repository so it easy to install it by:

npm install polite-plugin-manager --save

After that you can load an instance into your modules to be able to declare hooks or to load packages:

var PluginManager = require('polite-plugin-manager');

// register all plugins within a folder then start the manager
PluginManager.registerMany('/path/to/plugins/').start(function() {
    
    ... your app code ...
    
    // run all hook's registered callbacks
    PluginManager.run('hook-name');
    
    ... your app code ...
    
});

Hooks

Register Callbacks to Hooks

PluginManager.registerHook('hook-name', function(arg1) {
    arg1 += 1;
});

Run Hook's Callbacks

var num = 0;
PluginManager.run('hook-name', num, function() {
    console.log(num); // 1
});

Packages

Register a Package

PluginManager.registerPackage('/path/to/plugin-package/');

Register Many Packages

PluginManager.registerPackage('/path/to/plugins/');

Inside that folders you can place all you plugin-package folders. Polite Plugin Manager will load'em all!

Package Structure

/path/to/plugin-package/index.js
module.exports = function(packageContext) {
    return {
        // package identity properties
        name: 'optional package name',
        priority: 500, // default 100
        init: function() {
            ... initialization stuff ...
        },
        
        // register hooks callbacks
        'hook-name': function() {
            ... hook callback stuff ...
        },
        'another-hook-name': function() {
            ... hook callback stuff ...
        }
    };
};

Package Initialization

You plugin's package init() function executes when PluginManager.start().

If you need to run asynchronous logic then you need to reference to a done() callback to use when your stuff are done:

init: function() {
    var done = this.async();
    ... async stuff ...
    done();
}

Hook Types

run('hook-name', [arg1], [...], [argN], callback)

Run registered callbacks in series.

Execution order is granted to be the exact registration order (priority package property). Each callback starts only after the previous one ends.

It support both sinchronous and asynchronous callbacks(see below how to implement an asynchronous callback).

parallel('hook-name', [arg1], [...], [argN], callback)

Run registered callbacks in parallel.

It support both sinchronous and asyncronous callbacks(see below how to implement an asynchronous callback) but it is highly recommend to avoid sinchronous callbacks in this hook types because they would slow down execution!

waterfall('hook-name')

// use a waterfall hook as normal function:
var sum = PluginManager.waterfall('sum', 0);

A waterfall callback must always return a value. That value will become the first argument of the following registered callback.

Waterfall's callbacks are always synchronous!

PluginManager.registerHook('sum', function(tot) {
    tot += 1;
    return tot;
});

Advanced Topics

Asynchronous Callbacks

Asynchronous callbacks needs to explicitly communicate the end of their stuff.

PluginManager.registerHook('hook-name', function() {
    var done = this.async();
    doAsyncFunction(function() {
        done();
    });
});

If your callback do not uses any asynchronous APIs but it is meant to run by a parallel hook then it is highly recommend to make it asynchronous anyway to avoid block other callbacks startup!

A very simple way to obtain this behavior is the setTimeout function:

PluginManager.registerHook('hook-name', function() {
    var done = this.async();
    setTimeout(function() {
        done();
    }, 0);
});

This callback will release control immediately letting other callbacks to begin their execution.

If you want to learn more about Asynchronous Javascript programmin you can click here!
(temporary link)

Prevent Following Callbacks Execution

synchronous

PluginManager.registerHook('hook-name', function() {
    ... do something ...
    this.stop();
});

asynchronous

PluginManager.registerHook('hook-name', function() {
    var done = this.async();
    fs.exists('/test/path', function(exists) {
        if (exists) {
            
            // prevent other callbacks to run:
            done(true);
            
        } else {
            // allow other callbacks to run
            done();
        }
    });
});

you can detect if an asyncronous callback queque have been stopped:

PluginManager.run('foo', function(err) {
    if (err === true) {
        // stopped with standard stop API
    } else if (err instanceof Error) {
        // stopped by some callback's error
    }
});

Detect Empty Hook

If you feel important to know if some plugins have been executed under certain hooks you can:

/**
 * explicitly detection:
 */
if (PluginManager.isEmpty('hook-name')) {
    // yes it is!
} else {
    // no, something will be executed if you run it!
}

/**
 * after execution detection:
 */
var hasExecutedCallbacks = PluginManager.run('foo', function(err) {
    if (err === valse) {
        // no callbacks were executed
    }
});

if (hasExecutedCallbacks === false) {
    // no callbacks were executed
}