Skip to content

Commit

Permalink
README
Browse files Browse the repository at this point in the history
  • Loading branch information
overlookmotel committed Jul 18, 2018
1 parent 6fa98d5 commit 581b8b3
Showing 1 changed file with 141 additions and 0 deletions.
141 changes: 141 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,149 @@
[![Greenkeeper badge](https://badges.greenkeeper.io/overlookmotel/pluggi.svg)](https://greenkeeper.io/)
[![Coverage Status](https://img.shields.io/coveralls/overlookmotel/pluggi/master.svg)](https://coveralls.io/r/overlookmotel/pluggi)

## What's it for?

An easy way create apps which accept plugins.

A plugin is just a function which is executed and passed the app instance and options.

It's dead simple but powerful.

## Usage

```js
const Pluggi = require('pluggi');

const app = new Pluggi( {
myPlugin: { a: 1 }
} );

app.plugin( 'myPlugin', function(options) {
assert(this == app); // Plugin called with app instance
assert(options.a == 1); // Options keyed with the name of the plugin are passed
} );
```

### Plugin loading methods

**API: `.plugin( [name], [plugin], [options] )`**

#### Name + plugin function

```js
app.plugin( 'myPlugin', function(options) {
/* ... */
} );
```

#### Named function

If function is named, plugin name is taken from the function name. This is equivalent to the above example:

```js
app.plugin( function myPlugin(options) {
/* ... */
} );
```

#### Module name

If just name is provided, the plugin is loaded as a Node module.

```js
app.plugin( 'my-amazing-plugin' );

assert(app.plugins.myAmazingPlugin);
```

This calls `require('my-amazing-plugin')` internally. Note that the plugin name is converted to camel case.

### Plugin registry

The app has a property `.plugins` which contains a registry of all loaded plugins.

The value for each property of `.plugins` is the return value from the plugin function.

```js
app.plugin( 'myPlugin', () => ({ aProp: 123 }) );

assert(app.plugins.myPlugin.aProp == 123);
```

Return values must be truthy, or an empty object `{}` is used.

### Subclassing

Let's say you're making a web framework called "monkey". You want users to be able to develop plugins for this framework.

Subclass `Pluggi` and set `[PLUGIN_PREFIX]` to `'monkey'`.

```js
const {PLUGIN_PREFIX} = Pluggi;

class Monkey extends Pluggi {
constructor(options) {
super(options);
this[PLUGIN_PREFIX] = 'monkey';
}
}
```

Now users can develop plugins published on npm as 'monkey-routes', 'monkey-express' etc. When a plugin is loaded by name only, 'monkey-' is added to the start of the module name before it is `require()`-ed.

```js
const app = new Monkey();
app.plugin('router')
.plugin('express');

assert(app.plugins.router);
assert(app.plugins.express);
```

The plugins are registered as `router` and `express`, but the npm modules which were required were called 'monkey-router' and 'monkey-express'.

NB `PLUGIN_PREFIX` is currently a string, but it may be changed to a `Symbol` in a future version. This will not be considered a semver major change, so always use it via `Pluggi.PLUGIN_PREFIX`.

### Options

Options are set for a plugin in 2 places - local and global. The two are merged when passed to the plugin.

```js
const app = new Pluggi( {
router: { globalOpt: 123 }
} );

// Here is our plugin function
function router(options) {
console.log(options);
}

app.plugin( router, { localOpt: 456 } );

// Logs { globalOpt: 123, localOpt: 456 }
```

Global options are recorded on the app as `app.options`.

### Namespacing

Plugins will typically alter properties on the app.

To ensure two plugins do not clash, it is recommended that they respect a namespace defined by the name of the plugin. They should only make changes in two ways:

1. Set property on app named with plugin name
2. Return a value to be stored in the `app.plugins` object

```js
// Example plugin
function router(options) {
this.router = ...
return { bindToExpress: function() { ... } };
}
```

The `bindToExpress()` method can now be accessed at `app.plugins.router.bindToExpress()`.

## Tests

Use `npm test` to run the tests. Use `npm run cover` to check coverage.
Expand Down

0 comments on commit 581b8b3

Please sign in to comment.