Skip to content

Creating plugins

Liam edited this page Nov 2, 2016 · 12 revisions

Creating Matter.js plugins

  1. Using plugins
  2. Creating plugins
    1. Example
    2. Versioning
    3. Registering
    4. Patching
    5. Using other plugins as dependencies
  3. Guidelines for plugins
  4. Documentation
  5. Examples

Notice

This is a pre-release feature
Branch: plugins
Issue and discussion: #213

The following information may be subject to change

Using plugins

To find out how to use and install plugins, see the wiki page on using plugins. This article explains how you can make your own.

Creating plugins

Example

Here is a basic Matter.js plugin boilerplate (a repository will be available soon):

var MyPlugin = {
    name: 'matter-my-plugin',

    version: '0.1.0',

    for: 'matter-js@^0.10.0',

    uses: [
        'matter-some-dependency@^0.1.0',
        'matter-another-dependency@^0.1.0'
    ],

    options: {
        something: true
    },

    install: function(base) {
        // patch the Matter namespace here
    },

    // implement your plugin functions etc...
};

Matter.Plugin.register(MyPlugin);

A description of the required properties:

  • name string, required The unique name of the plugin (e.g. same as package.json)
  • version string, required The version of the plugin (a limited subset of semver)
  • install function, required The install function, it will be called only once and is passed a reference to the module it is being installed on as the first argument

There are some additional optional properties:

  • for string, optional The name and optionally version of module this plugin should be installed on
  • uses array, optional The names and optionally versions of any other plugins this plugin requires to be installed before itself It is also possible to directly pass references to plugin objects here, but it is not recommended
  • options object, optional Any global options for the plugin

While the above boilerplate is given as a guide, any equivalent representations exposing these properties should also work (such as CommonJS or ES6 modules).

Versioning

Plugins are versioned using the semver approach, making it easier to specify compatibility. Versions may be specified for plugins themselves, the version of Matter.js they are recommended for and the versions of their dependencies.

Versions are strictly of the format x.y.z (as in semver). Versions may optionally have a prerelease tag in the format x.y.z-alpha. Ranges are a strict subset of npm ranges. Only the following range types are supported:

  • Tilde ranges e.g. ~1.2.3
  • Caret ranges e.g. ^1.2.3
  • Exact version e.g. 1.2.3
  • Any version *

If a version or range is not specified, then any version (*) is assumed to satisfy.

Registering

It is very important to call Plugin.register after your plugin definition. This allows it to be resolved by name inside the plugin system, which is the recommended way to specify dependencies.

Matter.Plugin.register(MyPlugin);

Patching

A plugin's install function is where it should apply patches that implement the plugin's features on Matter.* modules.

Included in the library is a powerful function for patching called Common.chain that you should use in most cases. This utility returns a new function that executes all chained functions in order, returning the last value that was returned inside the chain.

Using this will also help ensure that you do not break the original function or any other plugins that may also patch it.
Here is an example of the recommended approach:

var MyPlugin = {
    // ...

    install: function(base) {
        base.Engine.create = Matter.Common.chain(
            base.Engine.create,
            function() {
                MyPlugin.Engine.init(this);
            }
        );
    },

    Engine: {
        init: function(engine) {
            // do something with engine
            console.log('MyPlugin.Engine.init:', engine);
        }
    }
};

// ...

When this plugin is installed, it will log to the console 'MyPlugin.Engine.init: ... whenever Matter.Engine.create is called.

Note that by using Common.chain you can also:

  • chain before or after the original method
  • chain as many functions as you need
  • override the returned value of the chain by using return
  • inspect the contents of a chain in the property chain._chained
  • join chains to keep them flat automatically

Be careful and respectful when patching:

  • don't change the original function signature
  • don't return inside a Common.chain unless you intend to change the original value
  • when returning, ensure the same type as the patched function

Using other plugins as dependencies

Plugins may be broken down in to smaller parts, shared and combined. Dependencies can be specified inside the uses property, and these will be installed before the plugin that specifies them.

var MyPlugin = {
    // ...

    uses: [
        'matter-some-dependency@^0.1.0',
        'matter-another-dependency@^0.1.0'
    ],

    // ...
};

// ...

Note that these dependencies will only ever be installed once, no matter how many plugins use them. If a plugin's code is loaded multiple times, the one with the highest version will be used.

Guidelines for plugins

Some general guidelines for plugins:

  • consider whether to implement as a plugin at all (a pull request might be more appropriate)
  • version everything
  • document everything (code and readme)
  • build plugins with sharing and reuse in mind
  • don't add new functions to modules or namespaces you don't maintain (only patch existing functions)
  • follow the same namespacing structure as the core (e.g. MyPlugin.Body.init, MyPlugin.Engine.update)
  • expose and implement your plugin's functions so that they can be called manually
  • avoid relying a particular order of execution where possible
  • the smaller the better
  • but avoid unnecessary dependencies
  • don't modify options or settings in unexpected or undocumented ways
  • use conditionals before operating on possibly undefined properties
  • don't bundle dependencies (document them)

Documentation

Check the documentation for a full description of the Matter.Plugin API.

Examples

See the list of plugins for more code examples of how plugins are implemented.

Clone this wiki locally