# Processor Types
Every processor in RSConstruct has a type that determines how it discovers inputs, produces outputs, and interacts with the cache. There are four types.
Run `rsconstruct processor types` to list them.
## Checker
A checker validates input files without producing any output. If the check passes, the result is cached — if the inputs haven't changed on the next build, the check is skipped entirely.
### How it works
1. Scans for files matching `src_extensions` in `src_dirs`
2. Creates one product per input file
3. Runs the tool on each file (or batch of files)
4. If the tool exits successfully, records a marker in the cache
5. On the next build, if inputs are unchanged, the check is skipped
### What gets cached
A marker entry — no files, no blobs. The marker's presence means "this check passed with these inputs."
### Examples
**Lint Python files with ruff:**
```toml
[processor.checker.ruff]
```
Scans for `.py` files, runs `ruff check` on each. No output files produced.
```text
src/main.py → (checker)
src/utils.py → (checker)
```
**Lint shell scripts:**
```toml
[processor.checker.shellcheck]
```
Scans for `.sh` and `.bash` files, runs `shellcheck` on each.
**Validate YAML files:**
```toml
[processor.checker.yamllint]
```
Scans for `.yml` and `.yaml` files, runs `yamllint` on each.
**Validate JSON files with jq:**
```toml
[processor.checker.jq]
```
Scans for `.json` files, validates each with `jq`.
**Spell check Markdown files:**
```toml
[processor.checker.zspell]
```
Scans for `.md` files, checks spelling with the built-in zspell engine.
### Built-in checkers
actionlint, ascii, aspell, biome, black, checkpatch, checkstyle, clang_tidy, clippy, cmake, cppcheck, cpplint, doctest, duplicate_files, encoding, eslint, hadolint, htmlhint, htmllint, iactionlint, icpplint, idockerfile, ijq, ijsonlint, iluacheck, isvglint, itaplo, itidy, ixmllint, iyamllint, iyamlschema, iyq, jq, jshint, jslint, json_schema, jsonlint, license_header, luacheck, make, markdownlint, marp_images, mdl, mypy, oxlint, perlcritic, php_lint, prettier, pylint, pyrefly, pytest, ruff, rumdl, script, selene, shellcheck, slidev, standard, stylelint, svglint, svgo, taplo, terms, tidy, xmllint, yamllint, yq, zspell
## Generator
A generator transforms each input file into one or more output files. It creates one product per input file (or one per input x format pair for multi-format generators like pandoc).
### How it works
1. Scans for files matching `src_extensions` in `src_dirs`
2. For each input file, computes the output path from the input path, output directory, and format
3. Creates one product per input x format pair
4. Runs the tool to produce the output file
5. Stores the output as a content-addressed blob in the cache
### What gets cached
One blob per output file. The blob is the raw file content, stored by its SHA-256 hash. On restore, the blob is hardlinked (or copied) to the output path.
### Examples
**Render Tera templates:**
```toml
[processor.generator.tera]
```
Scans `tera.templates/` for `.tera` files, renders each template. The output path is the template path with the `.tera` extension stripped:
```text
tera.templates/config.py.tera → config.py
tera.templates/README.md.tera → README.md
```
**Convert Marp slides to PDF:**
```toml
[processor.generator.marp]
```
Scans `marp/` for `.md` files, converts each to PDF (and optionally other formats):
```text
marp/slides.md → out/processor.generator.marp/slides.pdf
marp/intro.md → out/processor.generator.marp/intro.pdf
```
**Convert documents with pandoc (multi-format):**
```toml
[processor.generator.pandoc]
```
Scans `pandoc/` for `.md` files, converts each to PDF, HTML, and DOCX. Each format is a separate product with its own cache entry:
```text
pandoc/syllabus.md → out/processor.generator.pandoc/syllabus.pdf
pandoc/syllabus.md → out/processor.generator.pandoc/syllabus.html
pandoc/syllabus.md → out/processor.generator.pandoc/syllabus.docx
```
**Compile single-file C programs:**
```toml
[processor.generator.cc_single_file]
```
Scans `src/` for `.c` and `.cc` files, compiles each into an executable:
```text
src/main.c → out/processor.generator.cc_single_file/src/main.elf
src/test.c → out/processor.generator.cc_single_file/src/test.elf
```
**Convert Mermaid diagrams:**
```toml
[processor.generator.mermaid]
```
Scans for `.mmd` files, converts each to PNG (configurable formats):
```text
diagrams/flow.mmd → out/processor.generator.mermaid/diagrams/flow.png
```
**Compile SCSS to CSS:**
```toml
[processor.generator.sass]
```
Scans `sass/` for `.scss` and `.sass` files, compiles each to CSS:
```text
sass/styles.scss → out/processor.generator.sass/styles.css
```
### Built-in generators
a2x, cc_single_file, chromium, drawio, generic, ijinja2, imarkdown2html, ipdfunite, iprotobuf, isass, jinja2, libreoffice, mako, markdown2html, marp, mermaid, objdump, pandoc, pdflatex, pdfunite, protobuf, requirements, rust_single_file, sass, tags, tera, yaml2json
## Explicit
An explicit processor aggregates many inputs into (possibly) many output files and/or directories. Unlike other types which create one product per discovered file, explicit creates a single product with all declared inputs and outputs.
### How it works
1. Inputs are listed explicitly via `inputs` and `input_globs` in the config
2. Creates a single product with all inputs and all outputs
3. Runs the command, passing `--inputs` and `--outputs` on the command line
4. Stores each output file as a content-addressed blob
### What gets cached
One blob per output file (like generator).
### Examples
**Build a static site from generated HTML:**
```toml
[processor.explicit.generic.site]
command = "python3"
args = ["build_site.py"]
input_globs = ["out/processor.generator.pandoc/*.html", "templates/*.html"]
inputs = ["site.yaml"]
outputs = ["out/site/index.html", "out/site/style.css"]
```
Waits for pandoc to produce HTML files, then combines them with templates into a site. All inputs are aggregated into one product:
```text
out/processor.generator.pandoc/page1.html, out/processor.generator.pandoc/page2.html, templates/base.html, site.yaml → out/site/index.html, out/site/style.css
```
**Merge PDFs into a course bundle:**
```toml
[processor.explicit.generic.course]
command = "pdfunite"
input_globs = ["out/processor.generator.pdflatex/*.pdf"]
outputs = ["out/course/full-course.pdf"]
```
Aggregates all PDF outputs from pdflatex into a single merged PDF.
### Built-in explicit processors
generic
## Mass Generator
A mass generator runs a tool that produces many files in one invocation and enumerates them in advance. Every planned file becomes a product of its own with its own inputs and blob cache entry, while the underlying build tool runs at most once per build.
### How it works
1. At graph-build time, computes the output plan (natively or via a planner command)
2. Creates one product per planned file: `inputs` = the entry's `sources`, `outputs` = its `path`
3. Classifies each product individually (skip, restore, or build)
4. The first product that needs building runs `command`; the rest find the tool already run
5. Compares the files the tool wrote against the plan — a missing predicted file or an off-plan write fails the build. Files another product declares, as `output_files` or under its `output_dirs`, are that product's and are not reported even when it writes them during the tool's run
6. Stores each predicted file as a content-addressed blob
### What gets cached
One blob per predicted file (like generator). When every product is clean the tool does not run at all; when some are, the tool runs once and every dirty product caches its own file.
### Examples
**A static site generator with a plan mode:**
```toml
[processor.mass_generator.generic.site]
command = "rssite"
args = ["build"]
predict_command = "rssite"
predict_args = ["plan"]
output_dirs = ["_site"]
```
`rssite plan` prints which `_site/*.html` files `rssite build` will write and which sources each depends on. Changing `docs/about.md` dirties only `_site/about/index.html`; a linter scanning `_site/` depends on each page individually.
```text
docs/index.md, templates/default.html → _site/index.html
docs/about.md, templates/default.html → _site/about/index.html
```
**Build documentation with Sphinx:**
```toml
[processor.mass_generator.sphinx]
dep_inputs = ["src/mypkg/**/*.py", "pyproject.toml", "uv.lock"]
```
Asks Sphinx itself which files `sphinx-build -b html` will write (a planner runs Sphinx's read phase, nothing more), builds `sphinx/` into `docs/` through a staging directory, and caches every page, static file and index as a product of its own.
```text
sphinx/*, src/mypkg/**/*.py → docs/index.html, docs/mypkg.html, docs/_static/basic.css, …
```
### Built-in mass generators
cargo, cc, gem, generic, jekyll, linux_module, mdbook, npm, pip, sphinx, zola
Any tool that honors the manifest contract plugs into the config-driven `generic` mass generator via `[processor.mass_generator.generic.NAME]`; see [Mass Generator](processor/mass_generator/generic.md).
## Comparison
| | Checker | Generator | Explicit | Mass Generator |
|---|---|---|---|---|
| **Purpose** | Validate | Transform | Aggregate | Transform, many at once |
| **Inputs** | Scanned | Scanned | Declared in config | Declared by the tool's plan |
| **Products** | One per input | One per input (x format) | One total | One per predicted file |
| **Outputs** | None | Derived from input path | Declared files | Predicted files |
| **Cache type** | Marker | Blob | Blob | Blob |
| **Runs in** | Project root | Project root | Project root | Project root / anchor dir |
| **Command args** | Input files | Input + output | `--inputs` + `--outputs` | User-defined args |