Skip to content

Commit 026f6bf

Browse files
committed
Merge branch 'main' of github.com:quarto-dev/quarto-cli into main
2 parents 21c00d6 + c4af1a1 commit 026f6bf

File tree

5 files changed

+196
-120
lines changed

5 files changed

+196
-120
lines changed

src/command/render/render-files.ts

Lines changed: 104 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ import {
7676
} from "./freeze.ts";
7777
import { isJupyterNotebook } from "../../core/jupyter/jupyter.ts";
7878
import { MappedString } from "../../core/lib/text-types.ts";
79+
import { createNamedLifetime } from "../../core/lifetimes.ts";
7980

8081
export async function renderExecute(
8182
context: RenderContext,
@@ -282,7 +283,6 @@ export async function renderFiles(
282283
throw e;
283284
}
284285
}
285-
286286
const mergeHandlerResults = (
287287
results: HandlerContextResults | undefined,
288288
executeResult: MappedExecuteResult,
@@ -312,125 +312,131 @@ export async function renderFiles(
312312
];
313313
}
314314
};
315-
316315
for (const format of Object.keys(contexts)) {
317316
const context = ld.cloneDeep(contexts[format]) as RenderContext; // since we're going to mutate it...
318317

319318
// Set the date locale for this render
320319
// Used for date formatting
321320
await setDateLocale(context.format.metadata[kLang] as string);
322321

323-
// one time denoDom init for html compatible formats
324-
if (isHtmlCompatible(context.format)) {
325-
await initDenoDom();
326-
}
327-
328-
// reset figure counter
329-
resetFigureCounter();
322+
const fileLifetime = createNamedLifetime("render-file");
323+
fileLifetime.attach({
324+
cleanup() {
325+
resetFigureCounter();
326+
},
327+
});
328+
try {
329+
// one time denoDom init for html compatible formats
330+
if (isHtmlCompatible(context.format)) {
331+
await initDenoDom();
332+
}
330333

331-
// get output recipe
332-
const recipe = await outputRecipe(context);
334+
// get output recipe
335+
const recipe = await outputRecipe(context);
333336

334-
// determine execute options
335-
const executeOptions = mergeConfigs(
336-
{
337-
alwaysExecute: alwaysExecuteFiles?.includes(file.path),
338-
},
339-
pandocRenderer.onBeforeExecute(recipe.format),
340-
);
337+
// determine execute options
338+
const executeOptions = mergeConfigs(
339+
{
340+
alwaysExecute: alwaysExecuteFiles?.includes(file.path),
341+
},
342+
pandocRenderer.onBeforeExecute(recipe.format),
343+
);
341344

342-
const validate = context.format.metadata?.["validate-yaml"];
343-
if (validate !== false) {
344-
const validationResult = await validateDocument(context);
345-
if (validationResult.length) {
346-
throw new RenderInvalidYAMLError();
345+
const validate = context.format.metadata?.["validate-yaml"];
346+
if (validate !== false) {
347+
const validationResult = await validateDocument(context);
348+
if (validationResult.length) {
349+
throw new RenderInvalidYAMLError();
350+
}
347351
}
348-
}
349352

350-
// FIXME it should be possible to infer this directly now
351-
// based on the information in the mapped strings.
352-
//
353-
// collect line numbers to facilitate runtime error reporting
354-
const { ojsBlockLineNumbers } = annotateOjsLineNumbers(context);
355-
356-
// execute
357-
const baseExecuteResult = await renderExecute(
358-
context,
359-
recipe.output,
360-
executeOptions,
361-
);
353+
// FIXME it should be possible to infer this directly now
354+
// based on the information in the mapped strings.
355+
//
356+
// collect line numbers to facilitate runtime error reporting
357+
const { ojsBlockLineNumbers } = annotateOjsLineNumbers(context);
362358

363-
// recover source map from diff and create a mappedExecuteResult
364-
// for markdown processing pre-pandoc with mapped strings
365-
let mappedMarkdown: MappedString;
366-
367-
if (!isJupyterNotebook(context.target.source)) {
368-
mappedMarkdown = mappedDiff(
369-
context.target.markdown,
370-
baseExecuteResult.markdown,
359+
// execute
360+
const baseExecuteResult = await renderExecute(
361+
context,
362+
recipe.output,
363+
executeOptions,
371364
);
372-
} else {
373-
mappedMarkdown = asMappedString(baseExecuteResult.markdown);
374-
}
375365

376-
const resourceFiles: string[] = [];
377-
if (baseExecuteResult.resourceFiles) {
378-
resourceFiles.push(...baseExecuteResult.resourceFiles);
379-
}
366+
// recover source map from diff and create a mappedExecuteResult
367+
// for markdown processing pre-pandoc with mapped strings
368+
let mappedMarkdown: MappedString;
369+
370+
if (!isJupyterNotebook(context.target.source)) {
371+
mappedMarkdown = mappedDiff(
372+
context.target.markdown,
373+
baseExecuteResult.markdown,
374+
);
375+
} else {
376+
mappedMarkdown = asMappedString(baseExecuteResult.markdown);
377+
}
380378

381-
const languageCellHandlerOptions: LanguageCellHandlerOptions = {
382-
name: "", // will be filled out by handleLanguageCells internally
383-
temp: tempContext,
384-
format: recipe.format,
385-
markdown: mappedMarkdown,
386-
context,
387-
stage: "post-engine",
388-
};
389-
390-
// handle language cells
391-
const { markdown, results } = await handleLanguageCells(
392-
languageCellHandlerOptions,
393-
);
394-
const mappedExecuteResult: MappedExecuteResult = {
395-
...baseExecuteResult,
396-
markdown,
397-
};
398-
399-
mergeHandlerResults(
400-
context.target.preEngineExecuteResults,
401-
mappedExecuteResult,
402-
context,
403-
);
404-
mergeHandlerResults(results, mappedExecuteResult, context);
379+
const resourceFiles: string[] = [];
380+
if (baseExecuteResult.resourceFiles) {
381+
resourceFiles.push(...baseExecuteResult.resourceFiles);
382+
}
405383

406-
// process ojs
407-
const { executeResult, resourceFiles: ojsResourceFiles } =
408-
await ojsExecuteResult(
384+
const languageCellHandlerOptions: LanguageCellHandlerOptions = {
385+
name: "", // will be filled out by handleLanguageCells internally
386+
temp: tempContext,
387+
format: recipe.format,
388+
markdown: mappedMarkdown,
409389
context,
390+
stage: "post-engine",
391+
};
392+
393+
// handle language cells
394+
const { markdown, results } = await handleLanguageCells(
395+
languageCellHandlerOptions,
396+
);
397+
const mappedExecuteResult: MappedExecuteResult = {
398+
...baseExecuteResult,
399+
markdown,
400+
};
401+
402+
mergeHandlerResults(
403+
context.target.preEngineExecuteResults,
410404
mappedExecuteResult,
411-
ojsBlockLineNumbers,
405+
context,
412406
);
413-
resourceFiles.push(...ojsResourceFiles);
407+
mergeHandlerResults(results, mappedExecuteResult, context);
408+
409+
// process ojs
410+
const { executeResult, resourceFiles: ojsResourceFiles } =
411+
await ojsExecuteResult(
412+
context,
413+
mappedExecuteResult,
414+
ojsBlockLineNumbers,
415+
);
416+
resourceFiles.push(...ojsResourceFiles);
417+
418+
// keep md if requested
419+
const keepMd = executionEngineKeepMd(context.target.input);
420+
if (keepMd && context.format.execute[kKeepMd]) {
421+
Deno.writeTextFileSync(keepMd, executeResult.markdown.value);
422+
}
414423

415-
// keep md if requested
416-
const keepMd = executionEngineKeepMd(context.target.input);
417-
if (keepMd && context.format.execute[kKeepMd]) {
418-
Deno.writeTextFileSync(keepMd, executeResult.markdown.value);
419-
}
424+
// now get "unmapped" execute result back to send to pandoc
425+
const unmappedExecuteResult: ExecuteResult = {
426+
...executeResult,
427+
markdown: executeResult.markdown.value,
428+
};
420429

421-
// now get "unmapped" execute result back to send to pandoc
422-
const unmappedExecuteResult: ExecuteResult = {
423-
...executeResult,
424-
markdown: executeResult.markdown.value,
425-
};
426-
427-
// callback
428-
await pandocRenderer.onRender(format, {
429-
context,
430-
recipe,
431-
executeResult: unmappedExecuteResult,
432-
resourceFiles,
433-
}, pandocQuiet);
430+
// callback
431+
await pandocRenderer.onRender(format, {
432+
context,
433+
recipe,
434+
executeResult: unmappedExecuteResult,
435+
resourceFiles,
436+
}, pandocQuiet);
437+
} finally {
438+
fileLifetime.cleanup();
439+
}
434440
}
435441
}
436442

src/command/render/render-shared.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ import { kTextPlain } from "../../core/mime.ts";
4040
import { execProcess } from "../../core/process.ts";
4141
import { createExtensionContext } from "../../extension/extension.ts";
4242
import { createTempContext } from "../../core/temp.ts";
43+
import { Lifetime } from "../../core/lifetimes.ts";
4344

4445
export async function render(
4546
path: string,
@@ -120,11 +121,16 @@ export async function render(
120121
export function renderServices() {
121122
const temp = createTempContext();
122123
const extension = createExtensionContext();
124+
125+
const lifetime = new Lifetime();
126+
lifetime.attach(temp);
127+
123128
return {
124129
temp,
125130
extension,
131+
lifetime,
126132
cleanup: () => {
127-
temp.cleanup();
133+
lifetime.cleanup();
128134
},
129135
};
130136
}

src/core/cri/cri.ts

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import cdp from "./deno-cri/index.js";
1111
import { getBrowserExecutablePath } from "../puppeteer.ts";
1212
import { Semaphore } from "../lib/semaphore.ts";
1313
import { findOpenPort } from "../port.ts";
14+
import { getNamedLifetime, ObjectWithLifetime } from "../lifetimes.ts";
1415

1516
async function waitForServer(port: number, timeout = 3000) {
1617
const interval = 50;
@@ -36,8 +37,10 @@ async function waitForServer(port: number, timeout = 3000) {
3637

3738
const criSemaphore = new Semaphore(1);
3839

40+
export type CriClient = Awaited<ReturnType<typeof criClient>>;
41+
3942
export function withCriClient<T>(
40-
fn: (client: Awaited<ReturnType<typeof criClient>>) => Promise<T>,
43+
fn: (client: CriClient) => Promise<T>,
4144
appPath?: string,
4245
port?: number,
4346
): Promise<T> {
@@ -46,15 +49,26 @@ export function withCriClient<T>(
4649
}
4750

4851
return criSemaphore.runExclusive(async () => {
49-
const client = await criClient(appPath, port);
50-
try {
51-
const result = await fn(client);
52-
await client.close();
53-
return result;
54-
} catch (e) {
55-
await client.close();
56-
throw e;
52+
const lifetime = getNamedLifetime("render-file");
53+
if (lifetime === undefined) {
54+
throw new Error("Internal Error: named lifetime render-file not found");
55+
}
56+
let client: CriClient;
57+
if (lifetime.get("cri-client") === undefined) {
58+
client = await criClient(appPath, port);
59+
lifetime.attach({
60+
client,
61+
async cleanup() {
62+
await client.close();
63+
},
64+
} as ObjectWithLifetime, "cri-client");
65+
} else {
66+
// deno-lint-ignore no-explicit-any
67+
client = (lifetime.get("cri-client") as any).client as CriClient;
5768
}
69+
70+
// this must be awaited since we're in runExclusive.
71+
return await fn(client);
5872
});
5973
}
6074

@@ -76,7 +90,7 @@ export async function criClient(appPath?: string, port?: number) {
7690
];
7791
const browser = Deno.run({ cmd, stdout: "piped", stderr: "piped" });
7892

79-
if (!(await waitForServer(port))) {
93+
if (!(await waitForServer(port as number))) {
8094
throw new Error("Couldn't find open server");
8195
}
8296

src/core/handlers/mermaid.ts

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -135,17 +135,6 @@ const mermaidHandler: LanguageHandler = {
135135
undefined,
136136
new Set(["fig-width", "fig-height"]),
137137
);
138-
} else if (
139-
isMarkdownOutput(handlerContext.options.format.pandoc, ["gfm"])
140-
) {
141-
return this.build(
142-
handlerContext,
143-
cell,
144-
mappedConcat(["\n``` mermaid\n", cellContent, "\n```\n"]),
145-
options,
146-
undefined,
147-
new Set(["fig-width", "fig-height"]),
148-
);
149138
} else {
150139
const {
151140
filenames: [sourceName],

0 commit comments

Comments
 (0)