Skip to content

Commit 84c6be9

Browse files
copy edits
1 parent b42244b commit 84c6be9

2 files changed

Lines changed: 27 additions & 22 deletions

File tree

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,5 @@ _site
44
posts/etc
55

66
/.luarc.json
7+
8+
**/*.quarto_ipynb

posts/2026-03-xx/index.qmd

Lines changed: 25 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
---
22
title: Quarto Engine Extensions
33
author: Gordon Woodhull
4+
toc: true
5+
toc-depth: 3
46
---
57

68
Quarto 1.9 introduces [engine extensions](https://quarto.org/docs/extensions/engine.html), TypeScript plugins that run code blocks and capture their output.
79

8-
Currently there can be only one execution engine; see [Claiming a Language and Class](#claiming-a-language-and-class) to learn how the execution engine is chosen.
10+
Currently, there can be only one execution engine; see [Claiming a Language and Class](#claiming-a-language-and-class) to learn how the execution engine is chosen.
911

1012
Engine extensions are a very low-level mechanism, literally Markdown-in, Markdown-out. It's through the Quarto API that engine extensions get access to all the same tools that the built-in Jupyter and knitr execution engines use.[^1]
1113

@@ -85,7 +87,7 @@ Since only one engine can handle a document, Quarto needs to determine which one
8587
1. **Explicit declaration** — if the YAML frontmatter specifies `engine: marimo`, that engine is used directly.
8688
2. **Language claiming** — otherwise, Quarto extracts the languages from code blocks and asks each engine whether it claims them.
8789

88-
The `claimsLanguage` function returns `false` to pass, `true` to claim (with priority 1), or a number for a custom priority. Highest score wins.
90+
The `claimsLanguage` function returns `false` to pass, `true` to claim (with priority 1), or a number for a custom priority. The highest score wins.
8991

9092
For an engine with its own language, this is straightforward:
9193

@@ -104,7 +106,7 @@ claimsLanguage: (language: string, firstClass?: string): boolean | number => {
104106
},
105107
```
106108

107-
If no engine claims any language, Quarto falls back to Jupyter for unrecognized computational languages, or the markdown engine if there are no code blocks at all.
109+
If no engine claims any language, Quarto falls back to Jupyter for unrecognized computational languages, or to the markdown engine if there are no code blocks at all.
108110

109111
Engines can also claim files by extension via `claimsFile(file, ext)` — this is how the Jupyter engine claims `.ipynb` files. Most engine extensions return `false` here and rely on `claimsLanguage` instead.
110112

@@ -152,7 +154,7 @@ The most important fields of `ExecuteOptions`:
152154

153155
Other fields include `resourceDir`, `cwd`, `params`, `quiet`, `previewServer`, `handledLanguages`, and `project`. See `@quarto/types` for the full interface.
154156

155-
And the most important fields of `ExecuteResult`:
157+
The most important fields of `ExecuteResult`:
156158

157159
- `markdown` — the processed markdown (this is the main output)
158160
- `supporting` — paths to supporting files like figures
@@ -230,11 +232,27 @@ These are part of the `ExecutionEngineInstance` interface but are usually no-ops
230232

231233

232234

235+
## CLI integration
236+
237+
Engine extensions can optionally implement two CLI commands.
238+
239+
### `quarto check <engine-name>`
240+
241+
If your engine implements `checkInstallation(conf)`, users can run `quarto check <engine-name>` to verify that the engine's runtime is installed and working. The `conf` object provides output helpers for formatting check results. The built-in Jupyter and knitr engines check that their runtimes are installed, report capabilities, and perform a test render of a simple document via `quarto.system.checkRender()`.
242+
243+
### `quarto call engine <engine-name>`
244+
245+
If your engine implements `populateCommand(command)`, it can register subcommands under `quarto call engine <engine-name>`. The `command` parameter is a [Cliffy](https://cliffy.io/) `Command` object that you populate with subcommands.
246+
247+
Julia uses this to expose daemon management commands like `quarto call engine julia status` and `quarto call engine julia stop`. Engines that don't run a persistent process are less likely to need custom commands.
248+
233249
## Conclusion
234250

235-
If you've made it this far, you now know the full lifecycle of a Quarto engine extension: discovery, claiming, and execution. That's enough to get building — start with `quarto create extension engine` and look at the [marimo](https://github.com/marimo-team/quarto-marimo) and [Julia](https://github.com/quarto-dev/quarto-cli/tree/main/src/resources/extension-subtrees/julia-engine) engines for real-world examples.
251+
If you've made it this far, you now know the full lifecycle of a Quarto engine extension: discovery, claiming, execution, and CLI integration. That's enough to get building — start with `quarto create extension engine` and look at the [marimo](https://github.com/marimo-team/quarto-marimo) and [Julia](https://github.com/quarto-dev/quarto-cli/tree/main/src/resources/extension-subtrees/julia-engine) engines for real-world examples.
252+
253+
The rest of this post is reference material for the Quarto API types, to consult as needed.
236254

237-
The rest of this post is reference material: the Quarto API types and CLI integration points. Useful when you need them, but not required reading. And since the API is still stabilizing, if you hit rough edges, please [file an issue](https://github.com/quarto-dev/quarto-cli/issues).
255+
We're excited to see what engines people build. Share what you're working xon or ask questions in a [discussion](https://github.com/quarto-dev/quarto-cli/discussions?discussions_q=label%3Aengine-extensions).
238256

239257
## The Quarto API
240258

@@ -343,7 +361,7 @@ The remaining methods are optional.
343361

344362
### `EngineProjectContext`
345363

346-
This is a restricted view of Quarto's project context, passed to `launch()`. We won't cover every field here: `dir`, `isSingleFile`, `config`, `getOutputDirectory()`, and `fileInformationCache` are mostly self-explanatory. But one method deserves attention.
364+
This is a restricted view of Quarto's project context, passed to `launch()`. We won't cover every field here`dir`, `isSingleFile`, `config`, `getOutputDirectory()`, and `fileInformationCache` are mostly self-explanatory. But one method requires explanation.
347365

348366
`resolveFullMarkdownForFile(engine, file, markdown?, force?)`
349367
: `{{< include >}}` shortcodes can appear inside code blocks to import code. The engine needs these expanded before execution, otherwise it will try to execute the raw shortcode text. A Lua filter later in the Pandoc pipeline also handles includes (including ones emitted by code execution), but that runs after the engine. This method expands all `{{< include >}}` shortcodes in the source document before the engine sees it.
@@ -386,19 +404,4 @@ The `QuartoAPI` object received in `init()` provides nine namespaces. See `@quar
386404

387405

388406

389-
## CLI integration
390-
391-
Engine extensions can optionally implement two CLI commands.
392-
393-
### `quarto check <engine-name>`
394-
395-
If your engine implements `checkInstallation(conf)`, users can run `quarto check <engine-name>` to verify that the engine's runtime is installed and working. The `conf` object provides output helpers for formatting check results. The built-in Jupyter and knitr engines check that their runtimes are installed, report capabilities, and perform a test render of a simple document via `quarto.system.checkRender()`.
396-
397-
### `quarto call engine <engine-name>`
398-
399-
If your engine implements `populateCommand(command)`, it can register subcommands under `quarto call engine <engine-name>`. The `command` parameter is a [Cliffy](https://cliffy.io/) `Command` object that you populate with subcommands.
400-
401-
Julia uses this to expose daemon management commands like `quarto call engine julia status` and `quarto call engine julia stop`. Engines that don't run a persistent process are less likely to need custom commands.
402-
403-
404407
[^1]: A long-term goal is to be able to move the execution engines out of the quarto-cli core.

0 commit comments

Comments
 (0)