You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: posts/2026-03-xx/index.qmd
+25-22Lines changed: 25 additions & 22 deletions
Original file line number
Diff line number
Diff line change
@@ -1,11 +1,13 @@
1
1
---
2
2
title: Quarto Engine Extensions
3
3
author: Gordon Woodhull
4
+
toc: true
5
+
toc-depth: 3
4
6
---
5
7
6
8
Quarto 1.9 introduces [engine extensions](https://quarto.org/docs/extensions/engine.html), TypeScript plugins that run code blocks and capture their output.
7
9
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.
9
11
10
12
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]
11
13
@@ -85,7 +87,7 @@ Since only one engine can handle a document, Quarto needs to determine which one
85
87
1. **Explicit declaration** — if the YAML frontmatter specifies `engine: marimo`, that engine is used directly.
86
88
2. **Language claiming** — otherwise, Quarto extracts the languages from code blocks and asks each engine whether it claims them.
87
89
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.
89
91
90
92
For an engine with its own language, this is straightforward:
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.
108
110
109
111
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.
110
112
@@ -152,7 +154,7 @@ The most important fields of `ExecuteOptions`:
152
154
153
155
Other fields include `resourceDir`, `cwd`, `params`, `quiet`, `previewServer`, `handledLanguages`, and `project`. See `@quarto/types` for the full interface.
154
156
155
-
And the most important fields of `ExecuteResult`:
157
+
The most important fields of `ExecuteResult`:
156
158
157
159
- `markdown`— the processed markdown (this is the main output)
158
160
- `supporting`— paths to supporting files like figures
@@ -230,11 +232,27 @@ These are part of the `ExecutionEngineInstance` interface but are usually no-ops
230
232
231
233
232
234
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
+
233
249
## Conclusion
234
250
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.
236
254
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).
238
256
239
257
## The Quarto API
240
258
@@ -343,7 +361,7 @@ The remaining methods are optional.
343
361
344
362
### `EngineProjectContext`
345
363
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.
: `{{< 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
386
404
387
405
388
406
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
-
404
407
[^1]: A long-term goal is to be able to move the execution engines out of the quarto-cli core.
0 commit comments