Skip to content

Repository files navigation

Table of Contents

Zephyr

Build Statusnpm versionCoverage Status.

Plugin functionality for modular libraries.

For an implementation using this module see air.

Install

npm i zephyr --save

Usage

varplug=require('zephyr')// create the plugin system,sys=plug({});// load pluginssys.plugin([require('plugin-file')]);// create a componentvarcomponent=sys();// do something with the plugin functionality

Options

  • proto: A reference to the prototype object.
  • type: A reference to the class to instantiate.
  • main: An alternative main function (factory).
  • plugin: Override the default plugin function.
  • hooks: Array of functions invoked as constructor hooks.
  • field: String name of field for plugin function.

Plugins

Plugins are functions invoked in the scope of a class prototype that typically decorate the prototype object (using this) but may also add static methods or load other plugins.

Loading Plugins

To load plugin(s) call the plugin function passing an array of plugin functions:

varplug=require('zephyr'),sys=plug();sys.plugin([require('plugin-file')]);

It is possible to pass a configuration object at runtime to a plugin by using an object with a plugin function and a conf object:

varplug=require('zephyr'),sys=plug();varplugins=[{plugin: function(conf){// do something with the runtime configuration// initialize the plugin},conf: {foo: 'bar'}}];sys.plugin(plugins);

Creating Plugins

The most common use case for plugins is to decorate the class prototype with functions that are available on instances returned by the main function, these are referred to as instance plugins. Plugins may also decorate the main function these are referred to as static plugins.

Plugin implementations may mix functionality, for clarity the examples show the distinct styles.

Instance Plugins

To create an instance plugin just assign a function to this within the plugin function:

module.exports=functionplugin(){// decorate class prototypethis.chain=function(){// return this to allow chaining on this functionreturnthis;}}

Now load the plugin and invoke the instance method:

varcomp,plug=require('zephyr')// create the plugin system,sys=plug();// load the pluginsys.plugin([require('instance-plugin')]);// get the instance from the main functioncomp=sys();// invoke the plugin methodcomp.chain();

Static Plugins

To decorate the main function with static functions assign to this.main.

module.exports=functionplugin(){this.main.method=function(){// implement method functionality}}

You can then invoke the function on the plugin system:

varplug=require('zephyr'),sys=plug();sys.plugin([require('static-plugin')]);sys.method();

Composite Plugins

You can depend upon other plugins by calling this.plugin within the plugin function. This allows plugins to composite other plugins in order to resolve plugin dependencies or provide plugin groups (related plugins).

module.exports=functionplugin(){this.plugin([require('plugin-dependency')]);}

By convention plugins are singular and plugin groups are plural.

Named Plugin

Typically a plugin will be a single module (file) and the plugin function is exported, however sometimes you may prefer to export a class or other function; in this case the plugin initialization function may be assigned to the exported object and referenced using the field option.

Consider a module that exports a class but also wishes to expose a plugin function:

functionComponent(){}Component.init=function(){// implement plugin functionality}module.exports=Component;

We can then configure the plugin system by specifying the field option with the name of the function, in this case init:

varzephyr=require('zephyr'),main=zephyr({field: 'init'});module.exports=main;

Then we can require the file when loading the plugin and the init function will be invoked for plugin initialization:

main.plugin([require('./component-module')]);

Configuration

Plugins accept a single argument which is a configuration object optionally passed when loading the plugin. Useful when a plugin wishes to add functionality conditionally. For example:

module.exports=functionplugin(conf){conf=conf||{};// implement default logicif(conf.ext){// implement extended logic}}

Then a consumer of the plugin system could enable the extended logic:

sys.plugin({plugin: require('conf-plugin-file'),conf: {ext: true}})

Hooks

For some plugin systems it is useful to be able to add functionality in the scope of the component instance rather than the prototype. For example to add a default listener for an event, set properties on the instance or start running logic on component creation (or based on the plugin configuration).

Pass an array as the hooks option:

varplug=require('zephyr'),sys=plug({hooks: []});

And an additional register method is available on plugin:

functionhook(){// do something on component instantiation}module.exports=functionplugin(){// register the constructor hookthis.plugin.register(hook);}

Note that hooks are only applied when the component is created with the main function:

varplug=require('zephyr'),sys=plug({hooks: []});sys.plugin([require('plugin-with-hook')]);// constructor hooks are appliedvarcomp=sys();// bypass constructor hooks, probably not desirablecomp=newsys.Type();

Systems

A plugin system is the result of invoking the zephyr function:

varplug=require('zephyr'),sys=plug();module.exports=sys;

Which allows the ability to mix multiple components using plugins in the same code base. Typically you would export the main function returned as the plugin system.

Extend

Pass the proto and type options to extend the plugin system:

varplug=require('zephyr');// custom constructorfunctionPluginSystem(){}varproto=PluginSystem.prototype// extend the prototype with base functionality// available to all pluginsvarsys=plug({proto: proto,type: PluginSystem});module.exports=sys;

For an example implementation see air.js.

Source

;(function(){'use strict'functionplug(opts){opts=opts||{};/** * Default plugin class. */functionComponent(){}varmain,hooks=opts.hooks,proto=opts.proto||Component.prototype;/** * Plugin method. * * @param plugins Array of plugin functions. */functionplugin(plugins){varz,method,conf;for(zinplugins){if(typeofplugins[z]==='function'){method=plugins[z];}else{method=plugins[z].plugin;conf=plugins[z].conf;}if(opts.field&&typeofmethod[opts.field]==='function'){method=method[opts.field];}method.call(proto,conf);}returnmain;}/** * Create an instance of the class represented by *Type* and proxy * all arguments to the constructor. */functionconstruct(){varargs=Array.prototype.slice.call(arguments);functionFn(){returnmain.Type.apply(this,args);}Fn.prototype=main.Type.prototype;returnnewFn();}/** * Invoke constructor hooks by proxying to the main construct * function and invoking registered hook functions in the scope * of the created component. */functionhook(){varcomp=hook.proxy.apply(null,arguments);for(vari=0;i<hooks.length;i++){hooks[i].apply(comp,arguments);}returncomp;}/** * Register a constructor hook function. * * @param fn The constructor hook. */functionregister(fn){if(typeoffn==='function'&&!~hooks.indexOf(fn)){hooks.push(fn);}}main=opts.main||construct;// hooks enabled, wrap main function aop styleif(Array.isArray(hooks)){hook.proxy=main;main=hook;}// class to constructmain.Type=opts.type||Component;// static and instance plugin methodmain.plugin=proto.plugin=opts.plugin||plugin;// hooks enabled, decorate with register functionif(Array.isArray(hooks)){main.plugin.register=register;}// reference to the main function for static assignmentproto.main=main;returnmain;}module.exports=plug;})();

Developer

Developer workflow is via gulp but should be executed as npm scripts to enable shell execution where necessary.

Test

Run the headless test suite using phantomjs:

npm test

To run the tests in a browser context open test/index.html or use the server npm start.

Start

Serve the test files from a web server with:

npm start

Cover

Run the test suite and generate code coverage:

npm run cover

Lint

Run the source tree through eslint:

npm run lint

Clean

Remove generated files:

npm run clean

Spec

Compile the test specifications:

npm run spec

Instrument

Generate instrumented code from lib in instrument:

npm run instrument

Readme

Generate the project readme file (requires mdp):

npm run readme

License

Everything is MIT. Read the license if you feel inclined.

Generated by mdp(1).

About

Plugin facade

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages