Skip to content

Plugins

Zulko edited this page Mar 19, 2020 · 6 revisions

Plugins extend ReLaXed with new features, providing Pug commands or Javascript or styling frameworks to your documents, or post-processing filters. This page describes how to use and how to write plugins.

Using plugins

This section explains how to activate a plugin. It does not cover the plugins which are activated by default in ReLaXed, since these can be considered "features" of relaxed.

Plugin activation and options

To activate plugins, create a config.yml in the same folder as your master document, with the following content:

plugins:
  - some-plugin-name
  - some-other-plugin

Some plugin may accept optional parameters, at which case you must add : after the plugin name, and then provide parameter values:

plugins:
  - some-plugin-name:
      parameter-one: 90
      parameter-two: Jean-Paul

For instance, here is how you activate the svg plugin, and specify that when optimizing pictures it should use a compression quality of 90:

plugins:
  - svg:
      jpeg-quality: 90

Note that when running in an interactive session, the plugins are reloaded automatically when the config.yml is updated.

Built-in plugins

ReLaXed has a few plugins "out of the box", such as mathjax for mathematical equations (see this section on how to use it). Note that some of these plugins may not be built-in in the future.

Installed plugins

External ReLaXed plugins are released on NPM under names such as relaxed-pluginName, for instance relaxed-svg. They can be installed via

npm install -g relaxed-pluginName

This provides the pluginName plugin which you can use in your config.yml file:

plugins:
  - pluginName

Note that plugins do not have to be installed via an online NPM repository, you can also have a plugin on your computer, installed globally using npm link (this isn't tested on every platform though, please open an issue if this doesn't work for you).

Local plugins

It is also possible to define a custom plugin as a standalone file for use in just one or two projects. Just have a file with a .plugin.js extension in the same directory as your master document. e.g. say-my-name.plugin.js and activate it in config.yml:

plugins:
  - say-my-name:
      name: Heisenberg

For illustration sake, assume that the say-my-name.plugin.js contains the following miminal plugin definition, which adds the variable name to the Pug environment.

exports.constructor = async function (pluginDefinition) {
  return {
    pugHeaders: [`- var name = "${pluginDefinition.name}"`]
  }
}

Then your master Pug document can use this variable

p Say my name ! #{name} // will print "Heisenberg"

the next section explains every possible actions offered by plugins.

Writing Plugins

Generalities

A ReLaXed Plugin is a Javascript module exporting a (possibly async) function constructor(parameters) => hooks, where the "hooks" are lists of routines that will be applied at various times of the document creation.

So the master file of your plugin will look something like this:

exports.constructor = async function (parameters) {
  var hooks = {
    watchers: [...],
    pugHeaders: [...],
    pugFilters: [...],
    headElements: [...],
    htmlModifiers: [...],
    pageModifiers: [...],
    postPDF: [...]
  }
  return hooks
}

See section Hooks below for a detailed description of the different kinds of hooks.

Packaging

As explained in the Using plugins section, a plugin can be either used by , or published online with NPM. To release the plugin on NPM, the package name must be prefixed by relaxed-, e.g. relaxed-pluginName.

Examples of plugins

If you need inspiration, have a look at:

Hooks

This section describes in detail all the different categories of hooks. For a summary of all hooks, see the schema at the end of this section.

A plugin can omit any category of hooks, for instance a plugin providing only some pug headers will look like this:

exports.constructor = async function (pluginDefinition) {
  return { pugHeaders: ["header 1", "header 2"] }
}

For each category, a plugin can provide either a single hook or a list of hooks, so you can write for instance

pugHeaders: ["header 1", "header 2"]
\\ or
pugHeaders: "header"

watchers

Watchers are not involved in the creation of the final PDF, but are instead meant to trigger actions when files with special extensions are modified, in the watched directory.

A common usage of watchers is the interactive creation of assets (images, tables, etc.). For instance the chartjs watches files with the .chart.js extension. Every time a file with such extension is read, the plugin reads the javascript content (which defines a chart) and creates a PNG image from it.

A plugin's list of watchers is defined as follows:

watchers: [
  {
    extensions: ['.ext', '.other.ext', etc.],
    handler: async function (filepath, page)
  },
  {
    extensions: ['.ext', '.other.ext', etc.],
    handler: async function (filepath, page)
  },
  ...
]

Each watcher reacts to the provided extensions by calling a handler function, with parameters filepath (the file that was just modified) and page (a controllable Puppeteer page, which can be used to render an asset with Chromium).


pugHeaders

Pug headers are strings of Pug code that will be added at the top of your master document's Pug before rendering to HTML. These are useful to define variables or Pug mixins (which can be seen as "macros". "commands" in LaTeX).

A typical plugin providing headers could look like this (we assume there is a my_mixins.pug document in the plugin's directory):

const fs = require('fs')
const path = require('path')

exports.constructor = async function (params) {
  return {
    pugHeaders: [
      "- var myParameter = 834",
      fs.readFileSync(path.join(__dirname, 'my_mixins.pug'), 'utf8')
    ]
  }
}

pugFilters

A list of text-transforming functions that will be available in Pug as filters (see here for more infos on Pug filters).

For instance if the plugin provide these filters

pugFilters: {
      uppercase: function (text, options) { return text.toUpperCase }
      "add-suffix": function (text, options) { return text + options.suffix }
}

Then your Pug code can look like this:

p I am a normal paragraph

p:uppercase
    I will be an upper-case paragraph

p
  :add-suffix(suffix=" I will be added at the end")
    I am some text and a suffix will be added to me.

Two important remarks:

  • These functions cannot be asynchronous.
  • The options (like suffix above) must be constants, you cannot write :add-suffix(suffix=someVariable). If you need non-constant options, use a Pug mixin instead of a filter.

headElements

HTML elements to be added to the head section of the HTML page that will be rendered. This can be useful to add metadata, styles, javascript scripts to the page. for instance

headElements: [
  '<link rel="stylesheet" href="https://cdn.jsdelivr.net/katex.min.css"/>',
  '<script src="./myscript.js"/>',
]

htmlModifiers

Functions (synchronous or asynchronous) which will modify the final HTML, just before it is sent to Chromium: they will be executed in the order given.

htmlModifiers: [
    function (html) { ... return modifiedHtml},
    async function (html) = {... return modifiedHtml},
    ...
]

pageModifiers

to be written


postPDF

to be written


Summary of all hooks during PDF creation: