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 🙏

© 2026 – Pkg Stats / Ryan Hefner

sokkit

v0.4.6

Published

an npm module based plugin handler

Readme

Sokkit

Build Status - NPM version

Its where yer plugins go, innit?

A simple, unopinionated plugin handler for npm modules.

Discover, load, instantiate and/or execute methods on all of your plugins with ease - Just decide on a suitable file pattern, or use the mymodule-pluginname default, and away you go!

Installation

Install via npm, and save it as a dependency for your project.

npm install sokkit --save

Testing

Install the dev dependencies:

cd node_modules/sokkit/
npm install

Then, run the included test suite:

npm test

Configuration

Require the Sokkit class, and create an instance.

var Sokkit = require('sokkit');

var sokkit = new Sokkit();

The owning module name will automatically be detected, or you may override this by supplying a module option.

var sokkit = new Sokkit({ module: 'mymodule' });

Call the load() method to discover, and load any plugins.

var plugins = sokkit.load();

Or call it asynchronously, by supplying a callback:

sokkit.load(function(error, plugins) {
	// do important stuff.
});

Failures to detect plugins, or file system failures throw an Error (in synchronous mode), or pass the error parameter to the callback supplied (in asynchronous mode).

Actual plugin load failures, however, populate the failed property, an Array containing plain objects with name and error properties.

if (sokkit.failed.length) {
	console.log("The following plugins failed to load:\n",
		sokkit.failed.map(function(fail) {
			return '   ' + fail.name + ': ' + fail.error;
		}).join('\n')
	);
}

By default Sokkit will search the same node_modules directory that your module resides in for any other modules named yourmodule-*, and attempt to require them.

You can override the search directory by supplying a path option:

var dirname = require('path').dirname;

var sokkit = new Sokkit({
	path: dirname(require.main.filename) + '/plugins/appname-*'
});

Additionally, you can specify an array of alternate paths, and even include the default by specifying $DEFAULT within the array:

var sokkit = new Sokkit({
	path: [
		dirname(require.main.filename) + '/plugins/appname-*',
		dirname(require.main.filename) + '/extras/*.js',
		'$DEFAULT'
	]
});

Note: Overriding the path automatically overrides the pattern as well (since you may well need different patterns for different paths).

If you'd prefer to use the default path, but want a different file matching pattern, just supply a glob compatible pattern.

var sokkit = new Sokkit({
	pattern: 'mymodule-*-{plugin,extension}'
});

Additionally, plugins can be prevented from loading at all, by supplying a disable array during construction, and supplying the names of plugins to prevent:

var sokkit = new Sokkit({
	disable: ['plugin1', 'plugin2']
});

Usage

The Sokkit instance is actually an Array, well, sort of. Once load has returned (in synchronous mode), or the supplied callback has been called (in asynchronous mode), it will contain the module.exports from each of the plugins discovered, and loaded.

From there, you can iterate over the loaded plugins, as you would with any other Array:

sokkit.forEach(function(plugin) {
	// ... do something with plugin ...
});

Or use Array functions, such as map, filter, join, to perform operations or aggregate information about your loaded plugins.

If you need access to the actual plugin names, or want to reference a plugin by name, you can use the plugins property:

var plugins = sokkit.plugins;
for(var name in plugins) {
	if (!plugins.hasOwnProperty(name)) { continue; }
	var plugin = plugins[name];
	// ... do something with name and plugin ...
}

Sokkit is not opinionated, there is no enforced design on your plugin structure. How your plugins interact with your application is left to the developer's discretion, but several methods are supplied to assist in those interactions.

The call method invokes the method supplied on every succesfully loaded plugin with the remaining parameters:

sokkit.call('init', this, config.plugin);

// For all plugins, call: plugin.init(this, config.plugin)

The call method will return an array, containing the return values of every successful call made.

An additional property on the returned array, errors will contain objects with name and error properties, describing details of any plugins that threw exceptions while processing the request.

The apply method works exactly the same as call, except the arguments are passed as an array:

sokkit.apply('init', [this, config.plugin]);

Plugin naming

Plugins have a name associated, which can be used to reference them when validating dependencies, enabling, disabling, or accessing via sokkit.plugins[name].

The name is automatically assigned during discovery from the module directory name, or direct script filename matched by the discovery pattern.

In order to maintain consistency, and prevent app-name-soup, the following transformations occur when a plugin is discovered.

If the plugin is contained in a single script file, the .js extension is removed.

If the plugin is discovered in a module called application-pluginname, or a file named application-plugin.js then the application- prefix is removed, so an application using the following plugins:

	node_modules/application-plugin1/
	node_modules/application-plugin2/

	./components/feature1.js
	./components/application-feature2.js

Will have the following plugins:

	plugin1
	plugin2
	feature1
	feature2

Subsets

Although the Sokkit instance is a subclass of Array, you can not manipulate the contents directly, or use slice() to obtain a subset of plugins (it will return an Array, not a Sokkit instance). Instead, use subset(), and supply an optional function to filter that set, which will result in a Sokkit instance containing the required subset of plugins.

var group = sokkit.subset(function(name, plugin) {
	return plugin.isGroupMember || name.indexOf('group_name');
});

Subsets do not retain the failed properties of their parents.

Subsets are also independent plugin lists, they reference the same exports, but are unique sets in their own right. This means you can load plugins, create two subsets, run instantiate on each, and maintain two completely independent sets of plugin instances.

var plugins = new Sokkit().load();

var listA = plugins.subset();
	listB = plugins.subset();

listA.instantiate();
listB.instantiate();

console.log(listA[0] === listB[0]); // false

Instanced plugins

If you prefer modular plugins, the helper function, instantiate can be used to treat each plugin as a constructor, and instantiate those objects with the parameters supplied.

var errors = sokkit.instantiate(this);

// Performs the equivalent of:
sokkit[0] = new sokkit[0](this);
sokkit[1] = new sokkit[1](this);
// ...

The returned array will contain objects with name and error properties, listing any plugins that threw exceptions while being instantiated.

Enabling and disabling

If a plugin misbehaves, or is not required, it can be disabled:

sokkit.disable('bad-plugin');

And later re-enabled:

sokkit.enable('good-plugin');

Disabled plugins will no longer appear in the sokkit array, the plugins property, appear in any subsets or be affected by call, apply, or instantiate.

Plugins disabled during construction, and thereby never actually loaded can be enabled, just like any other plugin.

Plugin dependency management

In order to remain unopinionated, there is no enforced method of dependency management between plugins, but there is a helper function to simplify the task, should you need it.

Create a retrieve callback to return an array of dependencies for any given plugin, and pass it to the depend method to automatically verify dependencies, disabling any plugins that have not had their dependencies satisfied.

Additionally, this method will return the, now familiar, error array describing any plugins that have been disabled due to a dependency failure.

var errors = sokkit.depend(function(name, plugin) {
	return plugin.requires;
});

So, how does this help me?

Well, it means you can pick a naming scheme, and automatically discover and work with plugins, using whatever actual plugin API you prefer.

This, in turn, means that your users can, for instance:

npm install yourmodule
npm install yourmodule-plugin1
npm install yourmodule-plugin2

and have those plugins working automatically, right out of the box. No configuration, or editing of package.json files to enable them, or ensure they are loaded/linked correctly.

Alternatively, developers that depend on your module can do exactly the same, but by specifying plugins as dependencies too, in their package.json:

{
	"name": "superapplication",
	"version": "0.1.0",
	"author": "Bob",
	"description": "the very best application",

	"dependencies": {
		"yourmodule": "^0.1.0",
		"yourmodule-plugin1": "^0.1.0",
		"yourmodule-plugin2": "^0.2.0"
	}
}

And, when they access your module, the plugins will be available too - all automatically discovered, loaded and ready to use.

Personally, I like to use this as an automatic aggregator for components within my own software too - use an array of paths, including some way of picking up internal plugins.

	function MyAPI() {
		this.plugins = new Sokkit({
			path: [
				// Internal component path
				dirname(require.main.filename) . '/components/**/*.js',
				// External plugin path
				'$DEFAULT'
			]
		}).load();
	}

This style not only allows you to make every component of your system an effective example plugin, but also forces you, the developer, to consider encapsulation and separation of concerns within your architecture - and in turn consider your plugin API, its usability, scalability, etc, from the outset.

Finally

Have fun!