v2.0.0 — the dev loop, modernized

| 5 min read

Poops 2.0 has no new features. It is the release where the core loop — watch, build, reload — stops carrying old dependencies and old spellings, and where the compat shims that survived the whole 1.x line finally go.

Poops 2.0 has no new features. It is the release where the core loop — watch, build, reload — stops carrying old dependencies and old spellings, and where the compat shims that survived the whole 1.x line finally go.

Most projects need three edits to move, and two of them are optional until 3.0. Here is the whole list.

Migrating

1. Node 22

Node 20 reached end of life in April 2026. Poops now requires Node.js 22 or newer and is tested on 22 and 24, on Ubuntu and Windows.

2. Delete the livereload snippet from your templates

Live reload no longer runs a second server on its own port. It rides the serve port: Poops answers /__poops_reload as a server-sent events stream and appends a small client script to each HTML page it serves — to the response, so nothing lands in your build output.

Delete this from your layouts:

<script>
  document.write(
    '<script src="http://' +
      (location.host || "localhost").split(":")[0] +
      ':35729/livereload.js?snipver=1"></' +
      "script>",
  );
</script>

A snippet you forget is harmless — it 404s quietly — but the livereload_port template global it reads is gone, so it renders nothing anyway.

Config shrinks to a switch, and now needs serve alongside it:

{
  "serve": { "base": "dist" },
  "livereload": true
}

livereload.port, livereload.exclude, livereload.extraExts and livereload.exts are removed, along with the --livereload-port / -l flag. The last three had already stopped doing anything back in 1.5.1, when the reload server stopped watching files.

Behaviour is unchanged: one reload per save once the build settles, and a CSS-only build swaps stylesheets in place instead of reloading the page. That hot-swap now actually fires when you build with minify — see Fixed below.

3. Rename ssg to reactor

The last compatibility shim in the codebase. If your config still says ssg, rename it; Poops tells you so on startup:

[info] Config key "ssg" is renamed to "reactor" in 2.0 — ignored.

4. Move markup settings into options (deprecated, not yet required)

markup now has the shape every other entry has — in, out, and everything else in options:

{
  "markup": {
    "in": "src/markup",
    "out": "dist",
    "options": {
      "engine": "nunjucks",
      "site": { "title": "My Site" },
      "dateFormat": "MMM D, YYYY",
      "searchIndex": "search-index.json",
      "nav": { "out": "nav.json" }
    }
  }
}

Three renames come with it:

1.x 2.0
markup.<key> markup.options.<key>
timeDateFormat dateFormat
llms.output, nav.output, feed.output, … …out

All three are still honoured in 2.x and warn once, naming the replacement. They stop working in 3.0. Nothing breaks silently: if you keep the old spelling, your search index and feeds keep being written.

Two conveniences ride along. serve.base now defaults to the markup out directory, so most configs can drop it. And if you set target on your scripts or reactor entries, nothing changes — but the default moved from es2019 to es2020, because ?. and ?? are ES2020 and lowering them for browsers that have supported them since 2020 only made bundles bigger.

5. Custom engines: one parameter renamed

If you maintain a markup engine, registerFilters({ timeDateFormat, markupOut }) is now registerFilters({ dateFormat, markupOut }). That is the only signature change, and it is a hard rename rather than a deprecation — it lands before the engine interface becomes public API in this same release.

The engine interface is public API now

markup.options.engine has always accepted any importable module, and poops-shopify ships a production engine against it. Nothing declared that interface stable, so a method rename in a patch release could have broken it silently.

It is now documented in the engine API reference, semver applies to it from 2.0 on, and a contract test asserts both builtin engines still expose the shape — a failing test is a breaking change caught before it ships.

Under the hood

Fixed


Changelog Docs
💩💩💩💩
This static site was generated using Poops and serves as an example.