Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
07843f8
Initial architecture refactoring and Quarto API foundation
gordonwoodhull Sep 22, 2025
0ff735e
Port Julia engine to Discovery pattern and expand Quarto API
gordonwoodhull Oct 28, 2025
9920ad2
Port Jupyter and Knitr engines and complete Quarto API
gordonwoodhull Oct 30, 2025
56a15dd
Move Julia to extension, remove legacy patterns, make check extensible
gordonwoodhull Nov 3, 2025
cb92ff6
Add git subtree infrastructure and move Julia to bundled extension
gordonwoodhull Nov 5, 2025
a66c205
Polish architecture and improve Julia tests
gordonwoodhull Nov 10, 2025
3a813d5
Merge extension metadata and engines for single-file project context
gordonwoodhull Aug 8, 2025
6f6290b
Add engine template, organize subtree extensions, and final fixes
gordonwoodhull Nov 13, 2025
877ebbc
simplify imports to quarto api
gordonwoodhull Nov 18, 2025
0ab819f
simplify incorrect import
gordonwoodhull Nov 18, 2025
20ace90
enable experimental regexp engine
gordonwoodhull Nov 18, 2025
9cbf097
move target index cache metrics to own module
gordonwoodhull Nov 18, 2025
73697e1
Accept cell language as command-line argument for engine extensions
gordonwoodhull Nov 18, 2025
09f2f95
claude: Add build-ts-extension command for TypeScript engine extensions
gordonwoodhull Nov 19, 2025
0eafbb9
claude: Fix extension-build import-map to work in both dev and dist m…
gordonwoodhull Nov 19, 2025
f91e87c
claude: Add --init-config flag to build-ts-extension command
gordonwoodhull Nov 19, 2025
5773910
claude: Align engine template with build-ts-extension system
gordonwoodhull Nov 19, 2025
4068ca1
claude: Use glob pattern to find _extension.yml at any depth
gordonwoodhull Nov 19, 2025
a7bcffb
claude: Simplify import map and add external dependencies support
gordonwoodhull Nov 19, 2025
83c9ee7
claude: Replace esbuild with deno bundle for extension building
gordonwoodhull Nov 19, 2025
e5f0e64
claude: Add optional entry-point argument to build-ts-extension
gordonwoodhull Nov 19, 2025
f775d29
claude: Rename quartoExtension to bundle in deno.json config
gordonwoodhull Nov 19, 2025
a46755f
claude: Allow bundle.outputFile to be just a filename
gordonwoodhull Nov 19, 2025
f45c34b
update test deno.json
gordonwoodhull Nov 19, 2025
bb87ea7
claude: idiomatic errors and warnings
gordonwoodhull Nov 19, 2025
47fdf15
artifacts
gordonwoodhull Nov 20, 2025
5129ab3
Squashed 'src/resources/extension-subtrees/julia-engine/' content fro…
gordonwoodhull Nov 20, 2025
3ae0c28
Merge commit '5129ab3e48391201b67ec7ea7b2509b3663205e5' as 'src/resou…
gordonwoodhull Nov 20, 2025
ecb4468
Trigger rerun of tests
gordonwoodhull Nov 20, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions dev-docs/subtree-extensions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Subtree extensions

Subtree extensions live in `src/resources/subtree-extensions`.

Each is the complete git repository of a Quarto extension, e.g. the directory tree for the Julia engine looks like

```
src/resources/extension-subtrees/
julia-engine/
_extensions/
julia-engine/
_extension.yml
julia-engine.ts
...
```

The command to add or update a subtree is

```
quarto dev-call pull-git-subtree subtree-name
```

Omit _subtree-name_ to add/update all.

The code in `src/command/dev-call/pull-git-subtree/cmd.ts` contains a table of subtree

- `name`
- `prefix` (subdirectory)
- `remoteUrl`
- `remoteBranch`

If the command is successful, it will add two commits, one the squashed changes from the remote repo and one a merge commit.

The commits have subtree status information in the message and metadata, so don't change them.

The commits can't be rebased -- you'll get weird errors indicating it tried to merge changes at the root of the repo.

So you must either

- run the command when ready to merge to main, or
- remove the commits when rebasing, and run the `dev-call` command again
62 changes: 62 additions & 0 deletions package/src/common/prepare-dist.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,11 @@ export async function prepareDist(
supportingFiles(config);
info("");

// Update extension-build import map for distribution
info("Updating extension-build import map");
updateImportMap(config);
info("");

// Create the deno bundle
// const input = join(config.directoryInfo.src, "quarto.ts");
const output = join(config.directoryInfo.pkgWorking.bin, "quarto.js");
Expand Down Expand Up @@ -163,6 +168,20 @@ export async function prepareDist(
);
}

// Copy quarto-types to extension-build directory
// Note: deno.json and import-map.json are copied by supportingFiles() and
// import-map.json is then updated by updateImportMap() for distribution
info("Copying quarto-types.d.ts to extension-build directory");
const extensionBuildDir = join(
config.directoryInfo.pkgWorking.share,
"extension-build",
);
copySync(
join(config.directoryInfo.root, "packages/quarto-types/dist/index.d.ts"),
join(extensionBuildDir, "quarto-types.d.ts"),
{ overwrite: true },
);

// Remove the config directory, if present
info(`Cleaning config`);
const configDir = join(config.directoryInfo.dist, "config");
Expand Down Expand Up @@ -209,6 +228,49 @@ function supportingFiles(config: Configuration) {
pathsToClean.forEach((path) => Deno.removeSync(path, { recursive: true }));
}

function updateImportMap(config: Configuration) {
// Read the import map that was copied from src/resources/extension-build/
const importMapPath = join(
config.directoryInfo.pkgWorking.share,
"extension-build",
"import-map.json",
);
const importMapContent = JSON.parse(Deno.readTextFileSync(importMapPath));

// Read the source import map to get current Deno std versions
const sourceImportMapPath = join(config.directoryInfo.src, "import_map.json");
const sourceImportMap = JSON.parse(Deno.readTextFileSync(sourceImportMapPath));
const sourceImports = sourceImportMap.imports as Record<string, string>;

// Update the import map for distribution:
// 1. Change @quarto/types path from dev (../../../packages/...) to dist (./quarto-types.d.ts)
// 2. Update all other imports (Deno std versions) from source import map
const updatedImports: Record<string, string> = {
"@quarto/types": "./quarto-types.d.ts",
};

// Copy all other imports from source, updating versions
for (const key in importMapContent.imports) {
if (key !== "@quarto/types") {
const sourceValue = sourceImports[key];
if (!sourceValue) {
throw new Error(
`Import map key "${key}" not found in source import_map.json`,
);
}
updatedImports[key] = sourceValue;
}
}

importMapContent.imports = updatedImports;

// Write back the updated import map
Deno.writeTextFileSync(
importMapPath,
JSON.stringify(importMapContent, null, 2) + "\n",
);
}

interface Filter {
// The name of the filter (the LUA file and perhaps the directory)
name: string;
Expand Down
3 changes: 2 additions & 1 deletion package/src/quarto-bld
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,5 @@ export DENO_NO_UPDATE_CHECK=1

# TODO: Consider generating a source map or something to get a good stack
# Create the Deno bundle
"$QUARTO_DENO" run --no-check --unstable-kv --unstable-ffi --allow-all --v8-flags=--stack-trace-limit=100 --importmap="${SCRIPT_PATH}/../../src/import_map.json" "$SCRIPT_PATH/bld.ts" $@
# --enable-experimental-regexp-engine is required for /regex/l, https://github.com/quarto-dev/quarto-cli/issues/9737
"$QUARTO_DENO" run --no-check --unstable-kv --unstable-ffi --allow-all --v8-flags=--enable-experimental-regexp-engine,--stack-trace-limit=100 --importmap="${SCRIPT_PATH}/../../src/import_map.json" "$SCRIPT_PATH/bld.ts" $@
3 changes: 2 additions & 1 deletion package/src/quarto-bld.cmd
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@ if NOT DEFINED QUARTO_DENO (
SET "RUST_BACKTRACE=full"
SET "DENO_NO_UPDATE_CHECK=1"

"%QUARTO_DENO%" run --unstable-kv --unstable-ffi --allow-all --importmap=%~dp0\..\..\src\import_map.json %~dp0\bld.ts %*
REM --enable-experimental-regexp-engine is required for /regex/l, https://github.com/quarto-dev/quarto-cli/issues/9737
"%QUARTO_DENO%" run --no-check --unstable-kv --unstable-ffi --allow-all --v8-flags=--enable-experimental-regexp-engine,--stack-trace-limit=100 --importmap=%~dp0\..\..\src\import_map.json %~dp0\bld.ts %*
2 changes: 2 additions & 0 deletions package/src/util/deno.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ export async function compile(
denoBundleCmd.push("compile");
denoBundleCmd.push("--unstable-kv");
denoBundleCmd.push("--unstable-ffi");
// --enable-experimental-regexp-engine is required for /regex/l, https://github.com/quarto-dev/quarto-cli/issues/9737
denoBundleCmd.push("--v8-flags=--enable-experimental-regexp-engine,--stack-trace-limit=100");
denoBundleCmd.push(
"--importmap=" + configuration.importmap,
);
Expand Down
20 changes: 20 additions & 0 deletions packages/quarto-types/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Dependency directories
node_modules/
npm-debug.log
yarn-error.log
yarn-debug.log

# Output directories

# TypeScript cache
*.tsbuildinfo

# IDE files
.idea/
.vscode/
*.swp
*.swo

# OS specific files
.DS_Store
Thumbs.db
21 changes: 21 additions & 0 deletions packages/quarto-types/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2023 Quarto

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
100 changes: 100 additions & 0 deletions packages/quarto-types/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
# @quarto/types

TypeScript type definitions for developing Quarto execution engines.

## Installation

The goal in a couple of releases is for you to be able to do

```bash
npm install @quarto/types
```

But this package is not published yet, because the interfaces are still in flux.

Instead, build the package and copy dist/index.d.ts to types/quarto-types.d.ts in your engine repo.

```bash
npm run build
```

## Usage

This package provides TypeScript type definitions for implementing custom execution engines for Quarto.

```typescript
import { ExecutionEngine, EngineProjectContext } from "@quarto/types";

export const customEngine: ExecutionEngine = {
name: "custom",
defaultExt: ".qmd",

// Implement required methods...

target: (
file: string,
quiet: boolean | undefined,
markdown: MappedString | undefined,
project: EngineProjectContext // Using the restricted context interface
) => {
// Implementation...
},
};
```

## Core Interfaces

### ExecutionEngineDiscovery

Interface for implementing a Quarto execution engine discovery, responsible for determining which engine should handle a file and launching engine instances.

### ExecutionEngineInstance

Interface for a launched execution engine that can execute documents within a project context.

### EngineProjectContext

Restricted version of Quarto's ProjectContext that only exposes functionality needed by execution engines.

## Type Simplifications

This package is designed to be a standalone, lightweight type library for external engine development. To maintain this independence, some internal Quarto types are intentionally simplified:

### Simplified types with omitted fields

- **`RenderOptions`**: The internal Quarto version includes a `services: RenderServices` field, which provides access to temp file context, extension context, and notebook context. This field has been omitted as it requires pulling in large portions of Quarto's internal type system. All other fields are included. See [src/command/render/types.ts](https://github.com/quarto-dev/quarto-cli/blob/main/src/command/render/types.ts#L29-L42) for the full type.

### Simplified to `Record<string, unknown>` or index signatures

- **`Format.render`**: The full internal type is `FormatRender` with 100+ lines of specific rendering options including brand configuration and CSS handling. See [src/config/types.ts](https://github.com/quarto-dev/quarto-cli/blob/main/src/config/types.ts#L463-L525).

- **`Format.execute`**: The full internal type is `FormatExecute` with specific execution options. See [src/config/types.ts](https://github.com/quarto-dev/quarto-cli/blob/main/src/config/types.ts#L527-L561).

- **`Format.pandoc`**: The full internal type is `FormatPandoc` with 80+ lines of pandoc-specific options. Simplified to `{ to?: string; [key: string]: unknown }` preserving only the most commonly accessed `to` property. See [src/config/types.ts](https://github.com/quarto-dev/quarto-cli/blob/main/src/config/types.ts#L563-L646).

When accessing properties from these records, use type assertions as needed:

```typescript
const figFormat = options.format.execute["fig-format"] as string | undefined;
const keepHidden = options.format.render?.["keep-hidden"] as
| boolean
| undefined;
const writer = options.format.pandoc.to; // Type-safe access to 'to'
const standalone = options.format.pandoc["standalone"] as boolean | undefined;
```

### Omitted optional functions

The full internal `Format` interface includes several optional functions that are used internally by Quarto but are not needed by external engines:

- **`mergeAdditionalFormats`**: Internal format merging logic
- **`resolveFormat`**: Format resolution and normalization
- **`formatExtras`**: Compute format-specific extras (requires `RenderServices` and `ProjectContext`)
- **`formatPreviewFile`**: Preview file name transformation
- **`extensions`**: Format-specific extension configuration

These functions require deep integration with Quarto's internal systems and are not exposed to external engines. See [src/config/types.ts](https://github.com/quarto-dev/quarto-cli/blob/main/src/config/types.ts#L420-L456) for the full `Format` interface.

## License

MIT
Loading