{"id":51073382,"url":"https://github.com/karlb/minipandoc","last_synced_at":"2026-06-23T12:06:09.105Z","repository":{"id":354212631,"uuid":"1209441067","full_name":"karlb/minipandoc","owner":"karlb","description":"Convert formats via pandoc Lua readers/writers without requiring pandoc.","archived":false,"fork":false,"pushed_at":"2026-04-27T15:32:43.000Z","size":943,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2026-04-27T17:26:01.699Z","etag":null,"topics":["converter","djot","lua","pandoc"],"latest_commit_sha":null,"homepage":"","language":"Lua","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/karlb.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":"ROADMAP.md","authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-04-13T12:36:58.000Z","updated_at":"2026-04-27T16:05:07.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/karlb/minipandoc","commit_stats":null,"previous_names":["karlb/minipandoc"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/karlb/minipandoc","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/karlb%2Fminipandoc","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/karlb%2Fminipandoc/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/karlb%2Fminipandoc/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/karlb%2Fminipandoc/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/karlb","download_url":"https://codeload.github.com/karlb/minipandoc/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/karlb%2Fminipandoc/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34686786,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-23T02:00:07.161Z","response_time":65,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["converter","djot","lua","pandoc"],"created_at":"2026-06-23T12:06:08.348Z","updated_at":"2026-06-23T12:06:09.096Z","avatar_url":"https://github.com/karlb.png","language":"Lua","funding_links":[],"categories":[],"sub_categories":[],"readme":"# minipandoc\n\nA small, pandoc-compatible document converter where every format\nreader and writer is a Lua script. The Rust core (~2.3 MB release\nbinary, ~400 KB gzipped wasm) provides the pipeline, the `pandoc.*`\nLua API, and format resolution. No format knowledge lives in Rust.\n\n## Status\n\nReaders: `native`, `djot`, `html`, `markdown`.\n\nWriters: `native`, `djot`, `html`, `plain`, `markdown`, `latex`, `epub`.\n\nStandalone output (`-s`) is template-driven and ships defaults for\n`html`, `plain`, `markdown`, and `latex`. `--embed-resources` inlines\nlocal images and CSS in HTML output.\n\nPandoc Lua filters using the canonical 3.x idioms (`el.content[i]`,\n`#el.content`, `ipairs`, in-place mutation, multi-handler tables, etc.)\nrun unmodified — see `tests/filter_parity.rs`. Libraries that branch\non `type(x) == \"userdata\"` (e.g. `tarleb/panluna`) won't work; our AST\nelements are plain Lua tables.\n\n### Caveats\n\n- Markdown reader is a fork of `jgm/lunamark` with grammar fixes;\n  ~50% of the CommonMark spec suite passes today\n  (see `tests/commonmark_spec.rs`). Block-level work is ongoing for\n  specific downstream pitches; full CommonMark/GFM parity is not the\n  goal. Grid tables, delimiter-run emphasis, and full HTML-block\n  precedence are out of scope.\n- Plain writer's complex-table column-width algorithm doesn't\n  byte-match pandoc's, and `Math` elements emit raw TeX rather than\n  Unicode.\n- Native writer output is compact, not pretty-printed.\n- `pandoc.template` covers `$var$`, `$if$`, `$for$`, `$$`, dotted\n  paths, and pandoc's whitespace rule. Partials (`${name}`) and\n  `$var/pat/repl$` filters are not implemented.\n- No docx/odt — needs an XML primitive that hasn't landed yet.\n  EPUB works because it only needs ZIP.\n\n## Build\n\n```sh\ncargo build --release\n./target/release/minipandoc -f markdown -t html input.md\n```\n\n`mlua` is vendored with the `lua54` feature, so no system Lua is\nrequired. `build.rs` compiles LPeg from `scripts/vendor/lpeg/` against\nthe same Lua headers and regenerates the amalgamated reader/writer\nbundles when vendored sources change.\n\n```sh\ncargo test                                  # full suite\n./target/debug/minipandoc --list-input-formats\n./target/debug/minipandoc --list-output-formats\n```\n\nIntegration tests that compare against real pandoc skip gracefully\nwhen `pandoc` is not on `PATH`.\n\n### Slim build (no bundled formats)\n\nThe `bundled-formats` Cargo feature (on by default) embeds every\nformat reader/writer and template into the binary. Disable it for a\nsmaller core that loads format scripts only from `\u003cdata_dir\u003e/custom/`\nor explicit `.lua` paths:\n\n```sh\ncargo build --release --no-default-features\n```\n\nThis trims roughly 600 KB. The Lua runtime (`pandoc.*` API, layout,\ntemplate, LPeg) stays bundled — only format-specific scripts and the\ndefault templates drop out. With the feature off,\n`--list-input-formats` returns an empty list and bare names like\n`-f djot` fail with `unknown format`; pass `-f path/to/djot.lua` or\ninstall the script under `\u003cdata_dir\u003e/custom/djot.lua` instead.\n\n## Usage\n\nThe CLI mirrors pandoc's flag surface where it overlaps:\n\n```\nminipandoc -f FROM -t TO [-o OUT] [-s] [--template FILE]\n           [-V key=val] [-M key=val] [-L filter.lua]\n           [--embed-resources] [--data-dir DIR] [INPUT...]\n```\n\nExamples:\n\n```sh\nminipandoc -f djot   -t html  notes.dj\nminipandoc -f markdown -t latex -s paper.md -o paper.tex\nminipandoc -f markdown -t epub -s book.md -o book.epub\nminipandoc -f html -t markdown -L cleanup.lua page.html\n```\n\n## Browser / WASM\n\nLive demo: \u003chttps://karlb.github.io/minipandoc/\u003e.\n\n`scripts/build-wasm.sh` produces a WASI artifact that runs unchanged\nin the browser via the vendored `@bjorn3/browser_wasi_shim`. The\nscript auto-downloads a pinned wasi-sdk into `~/.cache/` on first\nrun (LPeg is C, so a wasm-targeted clang + sysroot is required);\nif you already have wasi-sdk wired up via `CC_wasm32_wasip1` /\n`AR_wasm32_wasip1` / `CFLAGS_wasm32_wasip1` / `RUSTFLAGS`, plain\n`cargo build --target wasm32-wasip1 --release` works too.\n`web/minipandoc.mjs` is the ES-module loader; `web/index.html` is\na demo. Pandoc Lua filters work unmodified there too — the browser\npath is the same Lua-5.4 binary.\n\n## Architecture\n\n```\nsrc/ast.rs        pandoc-types in Rust (reference; not on the hot path)\nsrc/cli.rs        clap-derive parser, pandoc-compatible flags\nsrc/format.rs     format resolution (data dir + bundled fallbacks)\nsrc/options.rs    ReaderOptions / WriterOptions passed to Lua\nsrc/pipeline.rs   read → filters → write orchestration\nsrc/lua/mod.rs    Lua state setup, pandoc.read / pandoc.write recursion\n\nscripts/pandoc_module.lua    pandoc.* Lua API\nscripts/layout.lua           pandoc.layout pretty-printer\nscripts/template.lua         pandoc.template (doctemplates subset)\nscripts/readers/*.lua        bundled readers\nscripts/writers/*.lua        bundled writers\nscripts/templates/*          bundled default templates\nscripts/vendor/djot/         upstream jgm/djot.lua, unmodified\nscripts/vendor/lpeg/         LPeg 1.1.0 C sources, built by build.rs\nscripts/lunamark/            forked jgm/lunamark (markdown reader)\n```\n\nThe AST lives in Lua as plain tables with metatables; Rust never\nconverts to `src/ast.rs` types in the pipeline. A fresh Lua state is\ncreated per conversion. `pandoc.read` / `pandoc.write` recurse via\nsub-states.\n\nFormats can also be supplied on the CLI without registering them,\nmatching pandoc's custom-reader/writer convention: `-f ./gemtext.lua`\n(literal path) or `-f gemtext.lua` (resolved against\n`\u003cdata_dir\u003e/custom/`, including `~/.local/share/pandoc/custom/`).\nA bare name like `-f gemtext` only resolves built-ins.\n\nAdding a *built-in* format means writing Lua under `scripts/readers/`\nor `scripts/writers/` (or vendoring an upstream pandoc-API script\nunder `scripts/vendor/`) and registering it in `src/format.rs`. See\n`CLAUDE.md` for the full procedure and conventions.\n\n## License\n\nMIT OR Apache-2.0. Vendored third-party code retains its original\nlicense — see `scripts/vendor/\u003cname\u003e/LICENSE` and\n`scripts/lunamark/FORKED_FROM`.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkarlb%2Fminipandoc","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkarlb%2Fminipandoc","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkarlb%2Fminipandoc/lists"}