Skip to content

Commit 27d381e

Browse files
claude: Add Quarto API and error message documentation
- llm-docs/quarto-api.md: Step-by-step guide for adding functionality to the Quarto API (type definitions in @quarto/types and implementation in quarto-api.ts), including usage patterns for internal and external engines - llm-docs/error-messages.md: Style guide for writing idiomatic error messages with proper newline placement (end messages with \n, never start with \n or use empty calls)
1 parent 3115a37 commit 27d381e

File tree

3 files changed

+297
-6
lines changed

3 files changed

+297
-6
lines changed

llm-docs/error-messages.md

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
# Writing Error Messages in Quarto
2+
3+
## The Rule
4+
5+
Each `error()`, `warning()`, or `info()` call should be **exactly one line**.
6+
7+
- ✅ End messages with `\n` to add blank lines after
8+
- ❌ Never start messages with `\n`
9+
- ❌ Never use empty `error("")` calls
10+
11+
## Why This Matters
12+
13+
Quarto's logging prefixes each call with `ERROR:` / `WARNING:` / `INFO:`. Starting a message with `\n` or using empty calls creates confusing output:
14+
15+
```
16+
ERROR: Multiple files found
17+
ERROR:
18+
Or specify entry point: ← Empty "ERROR:" line from \n at start
19+
```
20+
21+
## Adding Blank Lines
22+
23+
To add a blank line between sections, end the **previous** message with `\n`:
24+
25+
### ✅ Good
26+
27+
```typescript
28+
error("Multiple .ts files found in src/\n"); // \n at END
29+
error("Specify entry point as argument:");
30+
error(" quarto call build-ts-extension src/my-engine.ts");
31+
```
32+
33+
Output:
34+
```
35+
ERROR: Multiple .ts files found in src/
36+
ERROR:
37+
ERROR: Specify entry point as argument:
38+
ERROR: quarto call build-ts-extension src/my-engine.ts
39+
```
40+
41+
### ❌ Bad
42+
43+
```typescript
44+
error("Multiple .ts files found in src/");
45+
error("\nSpecify entry point as argument:"); // \n at START
46+
error(" quarto call build-ts-extension src/my-engine.ts");
47+
```
48+
49+
Output:
50+
```
51+
ERROR: Multiple .ts files found in src/
52+
ERROR:
53+
ERROR: Specify entry point as argument: ← Blank "ERROR:" line before
54+
ERROR: quarto call build-ts-extension src/my-engine.ts
55+
```
56+
57+
### ❌ Also Bad
58+
59+
```typescript
60+
error("Multiple .ts files found in src/");
61+
error(""); // Empty call to add spacing
62+
error("Specify entry point as argument:");
63+
```
64+
65+
Output:
66+
```
67+
ERROR: Multiple .ts files found in src/
68+
ERROR: ← Empty "ERROR:" line
69+
ERROR: Specify entry point as argument:
70+
```
71+
72+
## Complete Example
73+
74+
Here's a real example from `build-ts-extension` showing proper formatting:
75+
76+
### ✅ Good
77+
78+
```typescript
79+
error("No src/ directory found.\n");
80+
error("Create a TypeScript file in src/:");
81+
error(" mkdir -p src");
82+
error(" touch src/my-engine.ts\n");
83+
error("Or specify entry point as argument:");
84+
error(" quarto call build-ts-extension src/my-engine.ts");
85+
```
86+
87+
Output:
88+
```
89+
ERROR: No src/ directory found.
90+
ERROR:
91+
ERROR: Create a TypeScript file in src/:
92+
ERROR: mkdir -p src
93+
ERROR: touch src/my-engine.ts
94+
ERROR:
95+
ERROR: Or specify entry point as argument:
96+
ERROR: quarto call build-ts-extension src/my-engine.ts
97+
```
98+
99+
Notice:
100+
- Each `error()` call is one complete line
101+
- Blank lines are created by ending the previous message with `\n`
102+
- Indentation (with spaces) is preserved within each message
103+
- Message flow is clear and readable

llm-docs/quarto-api.md

Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
# Quarto API and @quarto/types
2+
3+
## Building @quarto/types
4+
5+
To build the @quarto/types package:
6+
7+
```
8+
cd packages/quarto-types
9+
npm run build
10+
```
11+
12+
This runs typecheck and then bundles all type definitions into `dist/index.d.ts`.
13+
14+
---
15+
16+
## Updating the Quarto API
17+
18+
The Quarto API is how external execution engines access Quarto's core functionality. The API exists in two places:
19+
20+
1. **Type definitions** in `packages/quarto-types/` - consumed by external engines (TypeScript)
21+
2. **Implementation** in `src/core/quarto-api.ts` - used within quarto-cli
22+
23+
### Step-by-step: Adding to the Quarto API
24+
25+
Follow these steps in order when adding new functionality to the API:
26+
27+
#### 1. Update quarto-types type definitions
28+
29+
**Add auxiliary types** (if needed):
30+
31+
- Types belong in `packages/quarto-types/src/`
32+
- Follow the existing file organization:
33+
- `system.ts` - System/process types (ProcessResult, TempContext, etc.)
34+
- `console.ts` - Console/UI types (SpinnerOptions, etc.)
35+
- `jupyter.ts` - Jupyter-specific types
36+
- `check.ts` - Check command types
37+
- `execution.ts` - Execution engine types
38+
- etc.
39+
- Create new files if needed for logical grouping
40+
41+
**Export types from index.ts:**
42+
43+
```typescript
44+
// In packages/quarto-types/src/index.ts
45+
export type * from "./your-new-file.ts";
46+
```
47+
48+
**Add to QuartoAPI interface:**
49+
50+
```typescript
51+
// In packages/quarto-types/src/quarto-api.ts
52+
53+
// 1. Import any new types at the top
54+
import type { YourNewType } from "./your-file.ts";
55+
56+
// 2. Add to the QuartoAPI interface
57+
export interface QuartoAPI {
58+
// ... existing namespaces
59+
60+
yourNamespace: {
61+
yourMethod: (param: YourNewType) => ReturnType;
62+
};
63+
}
64+
```
65+
66+
#### 2. Test the type definitions
67+
68+
```bash
69+
cd packages/quarto-types
70+
npm run build
71+
```
72+
73+
This will:
74+
75+
- Run `tsc --noEmit` to typecheck
76+
- Bundle types into `dist/index.d.ts`
77+
- Show any type errors
78+
79+
Fix any errors before proceeding.
80+
81+
#### 3. Update the internal QuartoAPI interface
82+
83+
The file `src/core/quarto-api.ts` contains a **duplicate** QuartoAPI interface definition used for the internal implementation. Update it to match:
84+
85+
```typescript
86+
// In src/core/quarto-api.ts (near top of file)
87+
export interface QuartoAPI {
88+
// ... existing namespaces
89+
90+
yourNamespace: {
91+
yourMethod: (param: YourNewType) => ReturnType;
92+
};
93+
}
94+
```
95+
96+
**Note:** This interface must match the one in quarto-types, but uses internal types.
97+
98+
#### 4. Wire up the implementation
99+
100+
Still in `src/core/quarto-api.ts`:
101+
102+
**Add imports** (near top):
103+
104+
```typescript
105+
import { yourMethod } from "./your-module.ts";
106+
```
107+
108+
**Add to quartoAPI object** (at bottom):
109+
110+
```typescript
111+
export const quartoAPI: QuartoAPI = {
112+
// ... existing namespaces
113+
114+
yourNamespace: {
115+
yourMethod,
116+
},
117+
};
118+
```
119+
120+
#### 5. Verify with typecheck
121+
122+
Run the quarto typecheck:
123+
124+
```bash
125+
package/dist/bin/quarto
126+
```
127+
128+
No output means success! Fix any type errors.
129+
130+
#### 6. Commit with built artifact
131+
132+
**Always commit the built `dist/index.d.ts` file** along with source changes:
133+
134+
```bash
135+
git add packages/quarto-types/src/your-file.ts \
136+
packages/quarto-types/src/index.ts \
137+
packages/quarto-types/src/quarto-api.ts \
138+
packages/quarto-types/dist/index.d.ts \
139+
src/core/quarto-api.ts
140+
141+
git commit -m "Add yourNamespace to Quarto API"
142+
```
143+
144+
### Using the Quarto API in source files
145+
146+
#### Inside quarto-cli (internal modules)
147+
148+
```typescript
149+
// Import the quartoAPI instance
150+
import { quartoAPI as quarto } from "../../core/quarto-api.ts";
151+
152+
// Use it
153+
const caps = await quarto.jupyter.capabilities();
154+
await quarto.console.withSpinner({ message: "Working..." }, async () => {
155+
// do work
156+
});
157+
```
158+
159+
#### External engines
160+
161+
External engines receive the API via their `init()` method:
162+
163+
```typescript
164+
let quarto: QuartoAPI;
165+
166+
export const myEngineDiscovery: ExecutionEngineDiscovery = {
167+
init: (quartoAPI: QuartoAPI) => {
168+
quarto = quartoAPI; // Store for later use
169+
},
170+
171+
// ... other methods can now use quarto
172+
};
173+
```
174+
175+
#### Removing old imports
176+
177+
When moving functionality to the API, **remove direct imports** from internal modules:
178+
179+
```typescript
180+
// ❌ OLD - direct import
181+
import { withSpinner } from "../../core/console.ts";
182+
183+
// ✅ NEW - use API
184+
import { quartoAPI as quarto } from "../../core/quarto-api.ts";
185+
const result = await quarto.console.withSpinner(...);
186+
```
187+
188+
This ensures external engines and internal code use the same interface.

src/command/call/build-ts-extension/cmd.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -88,11 +88,11 @@ async function autoDetectEntryPoint(
8888
error(" mkdir -p src");
8989
error(" touch src/my-engine.ts\n");
9090
error("Or specify entry point as argument:");
91-
error(" quarto call build-ts-extension src/my-engine.ts");
91+
error(" quarto call build-ts-extension src/my-engine.ts\n");
9292

9393
// Only show deno.json config if it already exists
9494
if (existsSync("deno.json")) {
95-
error("\nOr configure in deno.json:");
95+
error("Or configure in deno.json:");
9696
error(" {");
9797
error(' "bundle": {');
9898
error(' "entryPoint": "path/to/file.ts"');
@@ -140,13 +140,13 @@ async function autoDetectEntryPoint(
140140

141141
error(`Multiple .ts files found in src/: ${tsFiles.join(", ")}\n`);
142142
error("Specify entry point as argument:");
143-
error(" quarto call build-ts-extension src/my-engine.ts");
144-
error("\nOr rename one file to mod.ts:");
145-
error(` mv src/${tsFiles[0]} src/mod.ts`);
143+
error(" quarto call build-ts-extension src/my-engine.ts\n");
144+
error("Or rename one file to mod.ts:");
145+
error(` mv src/${tsFiles[0]} src/mod.ts\n`);
146146

147147
// Only show deno.json config if it already exists
148148
if (existsSync("deno.json")) {
149-
error("\nOr configure in deno.json:");
149+
error("Or configure in deno.json:");
150150
error(" {");
151151
error(' "bundle": {');
152152
error(' "entryPoint": "src/my-engine.ts"');

0 commit comments

Comments
 (0)