Processors

A guide on extending Lume with custom processors

Processors are functions to transform the content of pages during the build. They are executed after the page is rendered so you can use the page.text or page.bytes properties to read and modify the content of the pages. Let's see an example of a processor to minify HTML pages:

function minifyHTML(pages) {
  for (const page of pages) {
    page.text = minify(page.text);
  }
}

To use this processor, use the site.process function in _config.js file. The first argument is an array with the page extensions to be processed. In this example we want to process HTML pages so the array has only the .html extension.

site.process([".html"], minifyHTML);

Now, all HTML pages will be minified.

Instead of the output extension (.html) it's also possible to use the input extension. For example, to minify all HTML pages generated from .md files:

site.process([".md"], minifyHTML);

The page object

As you can see, the processor function receives an array of pages. Each page is an object with the following properties:

function process(pages) {
  for (const page of pages) {
    page.text; // The content of the page (as string)
    page.bytes; // The content of the page (as Uint8Array)
    page.content; // The content of the page (as string or Uint8Array)
    page.document; // The parsed HTML code, to use the DOM API
    page.src; // The info about the source file of this page
    page.data; // All data available for this page (front matter merged with _data)
  }
}

For example, let's say you only want to minify the pages where the value minify is true:

site.process([".html"], (pages) => {
  for (const page of pages) {
    if (page.data.minify) {
      page.text = minify(page.text);
    }
  }
});

Using the DOM API

You can use the DOM API (powered by deno-dom) with methods like querySelector, setAttribute, etc to modify HTML code. For example, let's create a processor to add automatically the alt attribute to all images:

site.process([".html"], (pages) => {
  for (const page of pages) {
    for (const img of page.document.querySelectorAll("img")) {
      if (!img.hasAttribute("alt")) {
        img.setAttribute("alt", "This is a random alt");
      }
    }
  }
});

Process assets

For non-HTML pages (like CSS or JavaScript files), you can use processors to compile CSS, minify JavaScript code or minify images.

site.process([".js"], function (pages) {
  for (const page of pages) {
    page.text = myBundler(page.text);

    // Append .min to the filename
    // so it will be saved as example.min.js
    page.data.url = page.data.url.replace(/\.js$/, ".min.js");
  }
});

Preprocess

If you need to execute a function before rendering (for example, to configure a custom template engine or add extra data to some pages), you can use a preprocessor. Preprocessors work like processors, but they are executed before rendering.

Let's create a preprocessor to include a variable with the source filename:

site.preprocess([".html"], (pages) => {
  for (const page of pages) {
    page.data.filename = page.sourcePath;
  }
});

Create pages dynamically

Processors can generate additional pages (or remove them). The function site.getOrCreatePage returns a page or create it if it doesn't exist. For example:

site.process([".css"], async (filteredPages, allPages) => {
  for (const page of filteredPages) {
    // Minify the css content
    const { code, map } = myCssMinifier(page.text);

    // Update the page content
    page.text = code;

    // Create a new page with the source map
    const pageMap = await site.getOrCreatePage(page.data.url + ".map");
    pageMap.text = map;
  }
});

Remove pages dynamically

During the build process, you can remove any page dynamically using the site.removePage function.

// Remove all html pages with the language = "en"
site.process([".html"], (pages) => {
  for (const page of pages) {
    if (page.data.lang === "en") {
      site.removePage(page);
    }
  }
});

Both processors and preprocessors are executed in the same order as they are defined. This allows chaining different processors to the same file extension. For example: two processors for the .css extension, one to compile the code and other to minify.

Global (pre)processors

If you want to run a processor or preprocessor for all pages, use * in the first argument:

site.process("*", processAllPages);

This is also equivalent to omit the first argument:

site.process(processAllPages);