Writing a plugin

About 2 min read

The folio Plugin contract lets you ship your own engine (a new command), transform (HTML post-processing), provider (new LLM/image API), or publish target.

EnginePlugin in 5 minutes

Create a new npm package folio-engine-poll (or @youorg/engine-poll):

// index.ts
import type { EnginePlugin } from "@foliolang/cli/plugin/contract";
import { contentHash } from "@foliolang/cli/build/canonical";

const PollPlugin: EnginePlugin = {
  id: "engine-poll",
  kind: "engine",
  cmd: "/插入投票",
  effects: [],
  cacheKey: (block) => contentHash({ body: block.body }),
  async run(block, ctx) {
    const lines = block.body.split("\n").filter(Boolean);
    const question = lines[0];
    const options = lines.slice(1).map((o) => `<li>${o}</li>`).join("");
    return { html: `<div class="poll"><h4>${question}</h4><ul>${options}</ul></div>` };
  },
};

export default PollPlugin;

package.json:

{
  "name": "folio-engine-poll",
  "main": "index.ts",
  "type": "module"
}

A user installs it:

npm install folio-engine-poll

On startup, folio scans node_modules for any package whose name starts with folio-engine- or @org/folio-engine- and registers it automatically.

In markdown:

::: /插入投票
Which widget do you like best?
filterable-table
tabs
slider-calc
:::

Other plugin kinds

Full contract

src/plugin/contract.ts is the authoritative definition. Every field (effects, requires, cacheKey, estimateCost) has its semantics documented inline.

Full tutorial