From 99a642b714e612f096efbee2292220b28168a01b Mon Sep 17 00:00:00 2001 From: Christophe Dervieux Date: Tue, 20 Oct 2026 11:28:20 +0200 Subject: [PATCH 1/3] docs(extensions): document installed layout, source layout and qualified names The install namespace rule was only written down in an unpublished draft, and "Archive Distribution" said every archive shares a single namespace, which is not true for GitHub archive URLs. Extension authors kept shipping an owner folder in their source `_extensions/`, which Quarto drops at install time, and templates referencing a qualified format name then broke when used from a local copy (quarto-dev/quarto-cli#15111). Document what quarto-cli does: - installs land in `_extensions///`, where the owner is the GitHub owner the extension is installed from; other URLs, local archives and local directories install as `_extensions//` - source repos put extensions directly under `_extensions/`, and extensions from another owner are bundled with `quarto add --embed` - a qualified `/` selects by installed owner, so a shipped `template.qmd` should use the bare name - the install-time note from quarto-dev/quarto-cli#15117 (1.11) Also replace the stale `quarto install` wording in starter-templates. --- docs/extensions/distributing.qmd | 59 ++++++++++++++++++++++++++- docs/extensions/managing.qmd | 2 +- docs/extensions/starter-templates.qmd | 2 +- 3 files changed, 60 insertions(+), 3 deletions(-) diff --git a/docs/extensions/distributing.qmd b/docs/extensions/distributing.qmd index 2e9f0b35e0..841a198a1a 100644 --- a/docs/extensions/distributing.qmd +++ b/docs/extensions/distributing.qmd @@ -34,6 +34,12 @@ _extensions/ Note that the only thing strictly required is the `_extensions` directory (anything above that is for your own purposes and is ignored during installation). Even so, it's good practice to include a `README.md` and `LICENSE` file, and the `example.qmd` will be useful for developing your extension. +Place each extension directly under `_extensions`, named after the extension (`_extensions/my-filter/`). Don't add a folder for your GitHub organization (`_extensions/cooltools/my-filter/`): Quarto adds the owner when the extension is installed (see [Installed Layout](#installed-layout)), and ignores any owner folder in the source. To include an extension from another owner, embed it in your extension as described in [Embedding Extensions](#embedding-extensions). + +{{< prerelease-callout 1.11 >}} + +When an extension's source has an owner folder that differs from the owner it is installed under, `quarto add` prints a note with the name to use for the installed extension. + ### \_extension.yml Each extension is defined by its `_extension.yml` file which contains the metadata about the extension as well as the what items it contributes when used. For example, here is the `_extension.yml` for a filter extension: @@ -122,6 +128,20 @@ Invalid `version` values would be: - 1.2.3-beta.5+build (missing build metadata identifier) ::: +## Installed Layout + +When an extension is installed, Quarto copies each extension found in the source `_extensions` directory into the `_extensions` directory of the project or document. Extensions installed from GitHub land in a folder named after the GitHub owner they were installed from. Extensions installed from any other source have no owner and land directly in `_extensions`: + +| Command | Installed location | +|-----------------------------------------------------------------------------|----------------------------------| +| `quarto add quarto-ext/lightbox` | `_extensions/quarto-ext/lightbox/` | +| `quarto add https://github.com/quarto-ext/lightbox/archive/refs/heads/main.zip` | `_extensions/quarto-ext/lightbox/` | +| `quarto add https://cooltools.org/quarto/shorty.zip` | `_extensions/shorty/` | +| `quarto add ~/Downloads/shorty.zip` | `_extensions/shorty/` | +| `quarto add /share/quarto/extensions/shorty` | `_extensions/shorty/` | + +The owner comes from the source you install from, not from the extension's source files. The extension folder keeps the name it has under the source `_extensions` directory, which can differ from the repository name. For example, `quarto add cooltools/icons/fontawesome` (see [subdirectories](#github-distribution) below) installs `_extensions/cooltools/fontawesome/`. + ## GitHub Distribution Distributing extensions on GitHub has a number of benefits, including compact syntax (e.g. `quarto add org-name/extension`), the use of organizations as a "namespace" for managing name conflicts, and the ability to target specific releases or tags. @@ -149,6 +169,10 @@ filters: --- ``` +A qualified name `/` refers to the extension installed in `_extensions///`. You can use it wherever you refer to an extension by name, for example in `filters` or in a format name such as `format: quarto-journals/acm-pdf`. A name without an owner (`lightbox`, `acm-pdf`) matches the extension whether or not it was installed with an owner, and Quarto warns if more than one installed extension matches. + +Because the owner depends on where an extension is installed from, use the name without an owner in a `template.qmd` that ships with your extension (for example `format: acm-pdf`). The template then works whether it is used from GitHub, from an archive, or from a local copy. + You can also add an extension from a subdirectory of a GitHub repository. For example, here we install two different extensions from the `cooltools/icons` repository: ``` {.bash filename="Terminal"} @@ -160,7 +184,7 @@ quarto add cooltools/icons/iconify Distributing extensions as a `.zip` or `.tar.gz` archive has the benefit of not requiring public distribution. These extensions can also be added directly from non-GitHub version control services using the archive URLs normally provided for repositories. -Note that unlike GitHub hosted extensions, extensions installed from archives do not have an organizational namespace (they all share a single namespace). +Note that unlike GitHub hosted extensions, extensions installed from archives or local directories do not have an organizational namespace: they install as `_extensions//`, all share a single namespace, and are referred to by name only. The one exception is a GitHub archive URL (`https://github.com///archive/refs/...`), which installs under its GitHub owner like any other GitHub extension. ### Git Repositories @@ -192,3 +216,36 @@ Or alternatively from a local archive file or even ordinary uncompressed directo quarto add ~/Downloads/shorty.zip quarto add /share/quarto/extensions/shorty ``` + +## Embedding Extensions + +A format extension can use filters and shortcodes from another extension. If that extension comes from another owner, embed it in your format extension rather than adding it next to your extension in `_extensions`: anything at the top level of your `_extensions` directory is installed under the owner your extension is installed from (see [Installed Layout](#installed-layout)), not the owner of the extension you copied. + +To embed an extension, run `quarto add` with the `--embed` option from the directory that contains your `_extensions` directory, giving the name of your format extension: + +``` {.bash filename="Terminal"} +quarto add quarto-ext/fancy-text --embed my-format +``` + +This installs the extension inside your format extension, with its own owner: + +``` default +_extensions/ + my-format/ + _extension.yml + _extensions/ + quarto-ext/ + fancy-text/ +``` + +Embedded extensions keep this layout when your extension is installed. Once the extension is embedded, refer to it in your `_extension.yml` by its name or qualified name: + +``` {.yaml filename="_extensions/my-format/_extension.yml"} +contributes: + formats: + html: + shortcodes: + - quarto-ext/fancy-text +``` + +Only extensions that contribute a format can embed other extensions. Run `quarto add --embed` before you reference the embedded extension in `_extension.yml`, as Quarto reads your extension (and fails on the missing reference) when embedding. diff --git a/docs/extensions/managing.qmd b/docs/extensions/managing.qmd index 751e26c25d..58214252ad 100644 --- a/docs/extensions/managing.qmd +++ b/docs/extensions/managing.qmd @@ -14,7 +14,7 @@ quarto add quarto-ext/fontawesome quarto add quarto-ext/video ``` -This will result in an `_extensions` folder being created at the root of your project, and the `fontawesome` and `video` extensions being placed within it. +This will result in an `_extensions` folder being created at the root of your project, and the `fontawesome` and `video` extensions being placed within it, in `_extensions/quarto-ext/fontawesome/` and `_extensions/quarto-ext/video/`. Note that a project isn't strictly required for using extensions---if you add extensions in a directory that isn't a project then any document located directly alongside the `_extensions` folder can use the extensions. diff --git a/docs/extensions/starter-templates.qmd b/docs/extensions/starter-templates.qmd index 02a964e10e..04ef79c5c7 100644 --- a/docs/extensions/starter-templates.qmd +++ b/docs/extensions/starter-templates.qmd @@ -55,7 +55,7 @@ If the command is run in an empty directory, the user will be prompted whether t When creating [Journal Articles](/docs/journals/index.qmd), [Custom Formats](formats.qmd), or [Project Type](project-types.qmd) extensions, we recommend that you additionally provide a starter template to make it easy for users to get started. -This is generally as easy as adding a `template.qmd` file to your extension that demonstrates its use. With this configuration, users can either begin using your extension via the template or by a conventional `quarto install` of the extension. +This is generally as easy as adding a `template.qmd` file to your extension that demonstrates its use. With this configuration, users can either begin using your extension via the template or by a conventional `quarto add` of the extension. For example, consider the [ACM](https://github.com/quarto-journals/acm) Journal Article extension. The extension repository supports _either_ getting started with a template: From 1b698d6d63aae8bdb9dd7a8af6688ec6fa58b0aa Mon Sep 17 00:00:00 2001 From: Christophe Dervieux Date: Tue, 20 Oct 2026 11:53:12 +0200 Subject: [PATCH 2/3] docs(extensions): clarify installation and embedding wording Make the GitHub owner condition explicit and simplify the embedding instructions so readers can follow the required order. Shorten the installation summary while preserving the documented paths. --- docs/extensions/distributing.qmd | 6 +++--- docs/extensions/managing.qmd | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/extensions/distributing.qmd b/docs/extensions/distributing.qmd index 841a198a1a..b99c2fc494 100644 --- a/docs/extensions/distributing.qmd +++ b/docs/extensions/distributing.qmd @@ -34,7 +34,7 @@ _extensions/ Note that the only thing strictly required is the `_extensions` directory (anything above that is for your own purposes and is ignored during installation). Even so, it's good practice to include a `README.md` and `LICENSE` file, and the `example.qmd` will be useful for developing your extension. -Place each extension directly under `_extensions`, named after the extension (`_extensions/my-filter/`). Don't add a folder for your GitHub organization (`_extensions/cooltools/my-filter/`): Quarto adds the owner when the extension is installed (see [Installed Layout](#installed-layout)), and ignores any owner folder in the source. To include an extension from another owner, embed it in your extension as described in [Embedding Extensions](#embedding-extensions). +Place each extension directly under `_extensions`, named after the extension (`_extensions/my-filter/`). Don't add a folder for your GitHub organization (`_extensions/cooltools/my-filter/`). For installations from GitHub, Quarto adds the owner folder (see [Installed Layout](#installed-layout)). Any owner folder in the source is ignored. To include an extension from another owner, embed it in your extension as described in [Embedding Extensions](#embedding-extensions). {{< prerelease-callout 1.11 >}} @@ -219,7 +219,7 @@ quarto add /share/quarto/extensions/shorty ## Embedding Extensions -A format extension can use filters and shortcodes from another extension. If that extension comes from another owner, embed it in your format extension rather than adding it next to your extension in `_extensions`: anything at the top level of your `_extensions` directory is installed under the owner your extension is installed from (see [Installed Layout](#installed-layout)), not the owner of the extension you copied. +A format extension can use filters and shortcodes from another extension. If that extension comes from another owner, embed it in your format extension. Extensions placed alongside your format extension in `_extensions` are installed under the same owner as your format extension (see [Installed Layout](#installed-layout)). To embed an extension, run `quarto add` with the `--embed` option from the directory that contains your `_extensions` directory, giving the name of your format extension: @@ -248,4 +248,4 @@ contributes: - quarto-ext/fancy-text ``` -Only extensions that contribute a format can embed other extensions. Run `quarto add --embed` before you reference the embedded extension in `_extension.yml`, as Quarto reads your extension (and fails on the missing reference) when embedding. +Only extensions that contribute a format can embed other extensions. Run `quarto add --embed` before adding the embedded extension's name to `_extension.yml`. Quarto reads this file during embedding and fails if it references an extension that is not yet installed. diff --git a/docs/extensions/managing.qmd b/docs/extensions/managing.qmd index 58214252ad..e5291b9e24 100644 --- a/docs/extensions/managing.qmd +++ b/docs/extensions/managing.qmd @@ -14,7 +14,7 @@ quarto add quarto-ext/fontawesome quarto add quarto-ext/video ``` -This will result in an `_extensions` folder being created at the root of your project, and the `fontawesome` and `video` extensions being placed within it, in `_extensions/quarto-ext/fontawesome/` and `_extensions/quarto-ext/video/`. +These commands install the extensions in `_extensions/quarto-ext/fontawesome/` and `_extensions/quarto-ext/video/` at the root of your project. Note that a project isn't strictly required for using extensions---if you add extensions in a directory that isn't a project then any document located directly alongside the `_extensions` folder can use the extensions. From 317ff1c4f2beaf4074de04455a39ac68427aff1b Mon Sep 17 00:00:00 2001 From: Christophe Dervieux Date: Tue, 20 Oct 2026 11:57:24 +0200 Subject: [PATCH 3/3] docs(extensions): say embedded, not installed, for the --embed ordering note The host extension's _extension.yml only resolves filters and shortcodes from its own embedded _extensions/, so an extension installed at the project level still fails the check until it is embedded. --- docs/extensions/distributing.qmd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/extensions/distributing.qmd b/docs/extensions/distributing.qmd index b99c2fc494..0caa7e67f4 100644 --- a/docs/extensions/distributing.qmd +++ b/docs/extensions/distributing.qmd @@ -248,4 +248,4 @@ contributes: - quarto-ext/fancy-text ``` -Only extensions that contribute a format can embed other extensions. Run `quarto add --embed` before adding the embedded extension's name to `_extension.yml`. Quarto reads this file during embedding and fails if it references an extension that is not yet installed. +Only extensions that contribute a format can embed other extensions. Run `quarto add --embed` before adding the embedded extension's name to `_extension.yml`. Quarto reads this file during embedding and fails if it references an extension that is not yet embedded.