mkdocs-carve
A MkDocs plugin that renders
Carve (.crv) source files as
documentation pages, converting them to HTML with the
carve-py engine. Carve pages
are wrapped by your active MkDocs theme exactly like Markdown pages, and they
coexist with .md pages in the same docs/ tree.
Installation
pip install mkdocs-carve
The Carve engine (carve-lang, the PyO3 binding over carve-rs) is installed
from PyPI as a normal dependency. It ships prebuilt abi3 wheels for Linux, macOS
and Windows, so no Rust toolchain is needed.
Usage
Enable the plugin in mkdocs.yml:
site_name: My Site
plugins:
- carve
Then write pages with a .crv extension under docs/ and
reference them in nav by their source path:
nav:
- Home: index.crv
- About: about.crv
- Guide:
- Introduction: guide/intro.crv
- Changelog: changelog.md # plain Markdown still works
A Carve page, docs/index.crv:
# Carve in MkDocs
This homepage is written in *Carve* (note: `*bold*` is strong,
`/italic/` is emphasis in Carve).
- lists
- `inline code`
- [links](https://example.com)
| feature | works |
|---------|-------|
| tables | yes |
Configuration
| Option | Type | Default | Description |
|---|---|---|---|
extensions |
list of str | ["heading_permalinks"] |
Carve extension names enabled for every Carve page. Passed straight to carve.to_html. Set to [] to use the core renderer only. |
emoji |
none | unicode | twemoji |
none |
Resolve :smile: and friends through the emoji database your Markdown pages already use. See Symbols and emoji. |
symbols |
mapping, or a path | (none) | Your own :name: symbols, written inline or kept in a JSON file whose path is given here. Values are emitted raw - see the warning below. |
includes |
bool | false |
Expand {{ path }} includes for file-backed pages. Off leaves a directive literal. See Includes. |
include_root |
absolute path | docs_dir |
The containment root includes resolve within. Must be absolute; a relative value is refused, not resolved. |
Example enabling additional Carve extensions:
plugins:
- carve:
extensions:
- heading_permalinks
- math_block
- list_table
Available extension names come from the installed Carve engine, so ask it rather than trusting a list in a README that cannot know which engine build you have:
python -c "import carve; print(carve.extensions())"
Symbols and emoji
Carve parses :name: as a symbol in its core - no extension needed - but what a
name renders as is a render option. Without a map, :smile: renders as the text
:smile:.
emoji turns on a name table. The names are not bundled with this plugin: they
come from pymdownx.emoji, which is the same database your site's Markdown
pages resolve :smile: through (Material for MkDocs enables it by default). One
source means a :smile: in page.md and a :smile: in page.crv cannot drift
apart. If your site configures its own emoji_index under markdown_extensions
- Material for MkDocs does - that index is used instead, together with the
optionsit is configured with, so a custom icon set resolves on both page types.
plugins:
- carve:
emoji: unicode # the character itself
# emoji: twemoji # the <img class="twemoji"> a Markdown page gets
unicode needs no network and inherits the page font, which on some platforms
has no color emoji face. twemoji emits the same element and the same class a
Markdown page gets, so the theme's sizing applies and the two page types look
identical. Install pymdown-extensions if your theme does not already pull it
in; a mode other than none without it is a configuration error, not a silent
fallback.
symbols adds your own entries, layered on top of the emoji table so you can
override one or add something that is not an emoji at all:
plugins:
- carve:
symbols:
crv: '<abbr title="Carve">CRV</abbr>'
rarr: "→"
A long map buries the rest of mkdocs.yml, so it can live in a JSON file
instead. The path is resolved relative to mkdocs.yml:
plugins:
- carve:
symbols: symbols.json
{
"crv": "<abbr title=\"Carve\">CRV</abbr>",
"rarr": "→"
}
The :name: match carries a word-boundary guard, so ordinary punctuation is
left alone. With smile mapped:
A :smile: here -> substituted
(:smile:) -> substituted
a:smile:b -> left literal
3:smile:4 -> left literal
`:smile:` -> left literal (a code span)
ratio 3:4, 12:30 -> left literal
The map is trusted input, and it is emitted raw
A mapped value is inserted into the output without escaping - the same trust
class as a build-time renderer callback. symbols: {logo: "<img src='/l.svg'>"}
emits a real <img> element. That is deliberate: processor configuration is
trusted, and it is what makes the twemoji mode possible at all.
It is safe only because of where the map can come from. This plugin resolves it
once, in on_config, from exactly two places: mkdocs.yml itself, and a JSON
file whose path is written in mkdocs.yml. No page has been read at that point,
so no page can contribute a value.
Never build a symbols map out of untrusted or user-supplied input. Do not generate the JSON file from page content, from front matter, from a form submission, or from anything a contributor can influence without review. If a value can reach the map from outside your own repository, it can put arbitrary HTML on every page of your site.
Includes
A Carve page can pull another file in with {{ path }}. The plugin leaves that
literal until a site asks for it:
plugins:
- carve:
includes: true
Paths resolve relative to the file that wrote them, and nothing resolves outside
the containment root. The root is docs_dir unless include_root names another
one, and an include that would leave it is not expanded.
include_root must be an absolute path. A relative one is refused rather
than resolved, because every way of resolving it ends at the directory the build
happened to run from, which is not a root anyone chose. Absolutize it yourself if
you need to - in mkdocs.yml that means writing the path out, or setting the
value from your own build script.
plugins:
- carve:
includes: true
include_root: /srv/shared/fragments
A target that cannot be read is reported as a build warning naming the page, and
the directive is left as written. The warning does not say whether the file was
missing or refused by containment: both report include-unresolved, so a page
cannot be used to probe the filesystem. Which one it was is in the build log at
INFO.
Under mkdocs serve, a change anywhere under the containment root triggers a
rebuild, including a fragment that did not exist yet.
Fragments living under docs_dir are still pages in their own right, since this
plugin claims every .crv file. Keep them out of the built site with MkDocs'
own exclude_docs,
or put them under an include_root outside docs_dir.
Include expansion needs
carve-lang0.1.4 or newer, above the floor this plugin declares.includes: trueon an engine without it is a configuration error rather than a silent no-op.
How it works
on_filespromotes each.crvfile to a documentation page and recomputes its destination path and URL using the same rules MkDocs applies to Markdown (driven by the file stem anduse_directory_urls). This handlesindexpages,READMEfiles, nested folders, and bothuse_directory_urls: trueandfalsemodes without hand-rolled path logic.- The Carve file keeps its original
.crvsrc_uri, sonaventries that reference.crvpaths resolve without translation. on_page_markdownconverts the Carve source to an HTML fragment. MkDocs' Markdown step passes raw HTML through untouched, so the theme template wraps the converted output like any normal page.
Development
Contributor setup, testing, and maintenance notes are in the development guide.
Metadata
Release files for mkdocs-carve 0.1.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| mkdocs_carve-0.1.2.tar.gz | 30.8 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| mkdocs_carve-0.1.2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 47.6 kB
Release files / mkdocs_carve-0.1.2.tar.gz
| Download URL | mkdocs_carve-0.1.2.tar.gz |
|---|---|
| Size | 30.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
abe90397328114f8c3f86816bf41e84c136be442f8bbeaefcbcca2515a5efcb8
|
|
BLAKE2b-256 checksum How to use checksums |
29c0718ac70585490fa3e101157a0496138e9ad590018b396d87d8c66ae11af1
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Release files / mkdocs_carve-0.1.2-py3-none-any.whl
| Download URL | mkdocs_carve-0.1.2-py3-none-any.whl |
|---|---|
| Size | 16.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
316f2759d394b551437cddb8efb396bad05876f54738a8685b9029e09de978db
|
|
BLAKE2b-256 checksum How to use checksums |
d097a6355fa8678d64d0e8ced49be742d089b147acf15469e03646d59e231336
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|