This is a draft guide on how to write plugins and what properties they have.
Plugins are used to extend the functionalities of the core renderer. They are located under
src/plugins. Each of them is registered in src/plugins.json, grouped by the phase they need to
act upon and in the order they should be loaded and executed.
The behaviour of registering is just like any other node module - they use their containing folder
name while having an index.js inside or directly the name of the file if they don't need to have
a folder. Generally you'd want to have a folder to hold your resource like css, fonts, js, etc.
Plugins have four phases: external, resource, extend, before, after and cleanup.
Each plugin can be registered for multiple phases but only once per phase.
The external phase registers an external showdown plugin.
Read the external plugins section for more information.
The resource phase does nothing other that registering resources.
If your plugin is using some frontend scripts or styles you need to add it to this phase.
The extend phase extends slides directly. You can add whole slides as markdown in this phase.
The slides array is passed to the extend function as first argument and the function is expected
to return an array containing all the slides.
Example usage would be to add wrapping slides like a speaker introduction or a Q&A slide.
The before phase is a replace phase that runs before html generation. It translates to
showdown's lang phase. It requires that pattern regex and replace function are placed
in an object and set as the before property.
They work like String.prototype.replace. The pattern regex matches against the markdown slides and
when a match is found the replace function is executed with the whole match as a first argument
and each capture groups is given in order as the next arguments. You need to return a string that is
the replacement for the match.
The after phase is similar to before but instead of executing before html generation it executes
after it. It works on the same principle and maps to showdown's output phase.
After having created the plugin structure and registered it as described above you can proceed to
writing the actual plugin. Then in your main file you need to provide module.exports with a
function that returns the object representing your plugin.
Here is an example of such a file:
// index.js
module.exports = () => ({
before: {
pattern: /hello/gm,
replace: () => 'hi',
}
});The above plugin would replace any occurrences of hello with hi in the presentation markdown.
There are two types of plugins - native showdown plugins and mrend plugins. For now we're just going to look at the mrend plugins.
Most of the time you'd want to have additional styling or some other resource with your plugin.
Firstly register the plugin for the resource phase. To add a resource you need to put it in the
plugin's dist directory.
Take this structure for example:
my-plugin/
└─ index.js
└─ dist/
└─ my-plugin-style.css
How you would register the resource itself would be:
module.exports = () => ({
resources: ['my-plugin-style.css'],
// ...
});Everything else around linking css or js would be handled by the core functionality.
You can override the dist directory by using
module.exports = () => ({
resources: {
links: ['my-plugin-style.css'],
dist: 'styles',
},
// ...
});Some plugins might want to have some king of configuration provided by the presentation header.
That configuration is passed as the first argument of of the function you export and is called
metadata. You should scope your metadata parameters with your plugin name as a prefix.
Lets say your plugin is called awesomeblock. Then you should expect the metadata to hold
parameters prefixed with awesomeblock-.
module.exports = metadata => {
console.log('Awesome color:', metadata['awesomeblock-color']);
return {
// ...
};
};The metadata is not separated by plugin so you can read global or other plugins' properties but you cannot edit them.
If you need to run initialization steps before actually running the plugin you can put your code inside the exported function. This can be useful if you need to move resource files or prepare for the actual execution.
module.exports = (metadata, utils) => {
console.log('Initializing my-plugin.');
console.log('node_modules path:', utils.MODULES_DIR);
// ...
};The system supports locale files which look like this
{
"<language>": {
"<key>": "<value>",
...
},
...
}Here's an example locale file
{
"en": {
"hello": "Hello"
},
"bg": {
"hello": "Здравей"
}
}You should set the lang metadata property in your presentation
---
lang: en
---And use the locale service
module.exports = (metadata, utils) => {
console.log(utils.i18n('hello'), 'world!');
// ...
};showdownjs provides a plugin system of its own which the mrend plugins utilize. You can use the
existing showdown plugin ecosystem by creating an external mrend plugin. Note that this is the
only plugin type that doesn't have an mrend phase. Its execution order is determined by whether
it's a lang or an output plugin and it's position in the plugin.json file. External plugins
are always executed before regular mrend plugins.
module.exports = () => ({
resources: [ /* ... */ ],
external: require('showdown-highlight'),
});To make that work you need to also copy the resources from node_modules and register them.
For your convenience this is already done in the highlight plugin that comes with the tool.
If you have any questions or find a bug feel free to open an issue at https://github.com/d3lio/mrend/issues. Pull requests are appreciated as well.