Widget pipelines and ordering

Estimated reading time: 3 minutes.
Date:

One limitation of classic static site generators is that output of a template is generally final and cannot be processed further. They assemble pages by filling the blanks. In some implementations they can set variables, but they cannot create other templates. Soupault's widgets, however, form a pipeline where output of one input can be used as input for another. You can also explicitly order the widgets to make sure they run when their input is available.

First thing you need to know is that soupault first inserts the page content in the template, and then runs the complete page through the widgets. At that stage, ordering is not a concern.

Then things get more interesting. The order of widgets in the config has no effect on their processing order. There are two reasons for it. First, soupault uses TOML, and TOML tables are not order-preserving1. Second, even if it was order-preserving, people would have to move chunks of the configs to reorder widgets. That would be pretty inconvenient.

That is why soupault uses an explicit ordering option. You can make one widget run after another by adding an after option to its config. It accepts either a single widget or a list of widgets, so you can write either after = "my-widget" or after = ["first-widget", "second-widget"].

Suppose you are using the Site URL plugin. You also keep the navigation links and the footer in separate files, and you want that plugin to alter URLs inside the HTML imported from those files.

If you don't specify the order, soupault may run the plugin before those files are included, and you'll end up with a page where links inside the page content have absolute URLs, but those in the navigation menu and the footer are relative.

To avoid that, you need to tell soupault when to run it. Here's a sample config:

[plugins.site-url]
  file = "plugins/site-url.lua"

[widgets.insert-nav-menu]
  widget = "include"
  file = "templates/menu.html"
  selector = "nav"

[widgets.insert-footer]
  widget = "include"
  file = "templates/footer.html"
  selector = "div#footer"

[widgets.set-site-url]
  widget = "site-url"
  site_url = "https://www.example.com"
  after = ["insert-nav-menu", "insert-footer"]

You can see the processing order by running soupault --debug or adding debug = true to the [settings] section of soupault.conf. For this site it looks like this:

$ soupault --debug
[DEBUG] Widget processing order: nav-menu footer footnotes table-of-contents page-title highlight-active-link

Note that a dependency on a widget that doesn't exist is an error. It would be easy to ignore, but it's usually a sign of a mistyped widget name, so I thought it's better to give people a chance to fix their config, rather than leave them wondering why their page looks wrong. Circular dependencies are errors too. In both cases soupault will tell you which widgets are causing problems so you shouldn't fear making complex pipelines.


1They are hash tables and libraries for working with TOML treat them as such. This is also the case with YAML and similar formats.