Skip to content

Developing a Great Reem Plugin

andrejewski edited this page Oct 6, 2014 · 1 revision

The plugin and middleware architecture is what makes Reem so powerful. Instead of having to install a monstrous library with a kitchen sink included, Reem allows things to be added as necessary.

Interfaces

The only convention that all plugins must follow is the function signature.

function(item, reem, done) {
	// do something with item
	done(error || null, item);
}

However more conventions make all plugins more usable. For instance, most plugins take a configuration object and then return the plugin function.

function(options) {
	// do something with options
	return function(item, reem, done) {
		// do something with item
		done(error || null, item);
	}
}

This is only an optional pattern, but it makes a developer's life so much easier when they can stick to something familiar.

Mutation

Plugins generally take an item, whether it be a post, list, file, or page, and mutate its data representation. This could mean adding or mutating object properties, over-writing the content property (which is what is actually written to the output directory), or doing a combination of such changes.

With great power comes great responsibility. Plugins that clog up object properties, mutate properties that Reem needs to function, those are the plugins that should not be written.

Plugins should take up the Jekyll philosophy of doing what they are told to do; no more, no less.

Type-Flexibility

Plugins should strive to be generally applicable to all Reem middleware stacks (Post, List, File, and Page). Inspecting reem-markdown, it makes no assumption that posts are the only stack it can be applied to even though Post is the prime consumer. List and Page may be exceptions as List is only written as a directory with no content to write and Page has no notion of having a parent list and other tree traversal relationships.

Sequence

Plugins are executed in the order they are added to the stack; first in, first ran. A good plugin specifies in what order it should be placed. A great plugin can be placed in any order and complete its expected behavior.

For more complex plugins it is incredibly important to know how plugins are executed throughout the entire data tree. To summarize, middleware is executed in a bottom-up fashion where it cannot be guaranteed that sibling nodes (posts, lists, or files) have been run through middleware. This is an intentional limitation made to prevent side effects and allow parallelism. It can be known that parent lists have not been processed.

This construction means new nodes may safely added to the parent list and they will be processed just as the node that creates them is. For instance, reem-coffee compiles CoffeeScript to JavaScript files and supports source maps. Source map support is possible because reem-coffee can push a new file node into the parent list. That new source map file is processed by the File stack and written to the output directory just as if it had been read from the source directory.

Naming

There is no reason that a plugin needs to be prefixed with reem-. Plugins use that to avoid potential naming conflicts. A reference to Reem should be in the tags of its package.json. All Reem plugins should have the reem-plugin tag to make them discoverable on NPM if someone were to search for "reem-plugin." Other than the tag, naming is an open field.

Assistance

If plugin writing sounds appealing, go for it. Ask for help from the Reem community of contributors, but most importantly: ask me for help. I thank you for reading.