Generate Sources

Use @pathmx/generate to turn JSON or CSV records into ordinary Markdown Sources with Knap templates.

Generated Sources work with PathMX's existing queries, links, layouts, and editing.

bun run pmx plugins add generate

Generate from arguments

Put your records in data/artists.json:

[
  {
    "slug": "ada-clay",
    "title": "Ada Clay",
    "category": "Ceramics",
    "booth": "A12"
  },
  { "slug": "rowan-paper", "title": "Rowan Paper", "category": "Printmaking" }
]

Create templates/artist.knap.md beside your Paths directory:

---
{{ category | yaml_property:"category" }}
---

# {{ title | escape_md }}

{{ category | escape_md }}

{% if booth %}Visit booth {{ booth | escape_md }}.{% endif %}

Preview the resulting files:

bun run pmx generate \
  --template templates/artist.knap.md \
  --data data/artists.json \
  --output 'paths/artists/{{ slug }}.artist.md' \
  --dry-run

Each record supplies the variables for both the template and the output path. This example creates paths/artists/ada-clay.artist.md and paths/artists/rowan-paper.artist.md. Ada's page includes the booth line; Rowan's omits it. Remove --dry-run to create the files.

Quote output patterns so your shell passes them unchanged. Outputs must be visible .md files inside the selected Paths directory. Templates and input data may live elsewhere. Keep templates outside Paths when they are authoring inputs rather than pages you want PathMX to index.

Save a recipe in a Source

Save the same request in paths/artists.recipe.md:

---
generate:
  template: ../templates/artist.knap.md
  data: ../data/artists.json
  output: "./artists/{{ slug }}.artist.md"
---

# Generate artist pages

Create one artist Source per record in the gallery export.
Review replacements before overwriting hand-edited pages.

Run it with:

bun run pmx generate paths/artists.recipe.md --dry-run

The recipe is a Source in the selected Paths directory. Its type is your choice: .recipe.md, .import.md, or another filename works. The plugin validates the generate declaration's shape; pmx lint reports malformed declarations. template, data, and output are required nonempty strings. Optional dataFormat is json or csv. Other declaration fields are rejected.

Arguments override the corresponding saved fields. Paths in the declaration resolve beside the recipe Source; paths supplied as arguments resolve from the working directory. The recipe filename itself is also working-directory-relative.

bun run pmx generate paths/artists.recipe.md --data data/updated-artists.csv --dry-run

Data and templates

JSON input accepts one object or an array of objects. CSV uses the first row for column names and retains all values as strings. Quoted commas, embedded newlines, and UTF-8 BOMs are supported. Empty batches, inconsistent CSV rows, and empty or duplicate column names are errors.

The data filename selects CSV for .csv; other filenames default to JSON. Use --data-format csv or --data-format json to override that choice. Piped input defaults to JSON:

cat data/artists.json | bun run pmx generate \
  --template templates/artist.knap.md --data - \
  --output 'paths/artists/{{ slug }}.artist.md' --dry-run

Use --data - --data-format csv for piped CSV. A recipe can also declare data: '-' and dataFormat: csv.

Templates use Knap syntax. Variables come directly from each record; PathMX's live $ bindings and implicit source scope are not supplied. Knap evaluates its tokens throughout the template, including code fences. Use escape_md for literal text and yaml_property for metadata. Raw values can deliberately emit Markdown and HTML. Source generation does not automatically escape every value or sanitize authored markup.

Preview and replace

--dry-run renders the entire batch, validates proposed Source changes, and prints file diffs without committing them. Templates report errors with their filename, line, column, and input record. --json returns a structured report with file paths, statuses, warnings, and preview diffs.

Existing files with identical content are unchanged. Replacing different content requires --overwrite, including during a preview:

bun run pmx generate paths/artists.recipe.md --overwrite --dry-run
bun run pmx generate paths/artists.recipe.md --overwrite

Replacement writes the entire generated Source. Keep manual additions in the template or input data if they should survive later replacements. The command does not merge content, synchronize remote data, or delete stale outputs.

Duplicate destinations, paths outside Paths, symbolic-link destinations, and outputs that replace the recipe, template, or data input are rejected. Rendering and validation finish before any generated Source is committed. Writes use PathMX's existing Source transaction lifecycle, so watching servers see ordinary Source changes.