Part of making NodeBB extensible is programming a plugin system, similar to CMS platforms like Wordpress and Drupal. The plugins themselves enhance the base feature set of NodeBB and allow the community to accelerate development of features that they deem necessary, not the core developers.
Prior to today, the plugin system was fairly unremarkable. It loaded plugins from the
/plugins folder1 or via node modules installed via
npm, and exposed system functionality via a set of hooks. This meant that plugins themselves were essentially standalone, and that all logic was contained within the module itself.
In the case of NodeBB's "mentions" plugin, we were using the following hooks:
We needed the following functionality in addition to what was provided:
- If a mention was detected (e.g. "@julian"), only make that mention an anchor if the user exists in the NodeBB
- If a user mentions another user, send them a notification via the built-in Notifications class.
The problem with these two features was that we required the
Notifications modules, respectively2.
My first attempt was simply
require-ing the modules like any other module:
var Topics = require('../../src/topics');
However, this is undesirable behaviour because it may be bad form to refer to Objects outside of the module root. In addition, we ran into an issue while using
To resolve this, we needed an elegant (or if not elegant, then a "node-like") way to utilise core NodeBB functionality while preserving
npm link functionality.
Objectmodule.exports from the resolved module
module.requiremethod provides a way to load a module as if
require()was called from the original module.
Note that in order to do this, you must get a reference to the
module.exports, and the
moduleis typically only available within a specific module's code, it must be explicitly exported in order to be used.
The description is a bit confusing... how is it different from calling
require() on my own?
The second paragraph only made sense after you realize that "you must get a reference to the
module object" means that
module itself needs to be a reference to another instance of
module.exports. For example, if my app had a
foo module, and I wanted to call the
bar module required by
foo, you could write...
var foo = require('foo'), bar = foo.require('bar');
Luckily for us, this works in reverse too, if we combine it with
module.parent! From inside our module:
var Topics = module.parent.require('./topics'), User = module.parent.require('./user'), Notifications = module.parent.require('./notifications');
In our case, because every NodeBB plugin is always called by
plugin.js, we can safely assume that
module.parent points to
module.exports provided by
1 Loading local modules via the
/plugins folder is deprecated as of v0.0.7
nodebb-plugin-mentions actually uses three NodeBB classes,
3 The gist of it is - when
npm link-ed, the relative paths used (
../../) point to
/usr/lib/, not the current working directory of the node application!