diff --git a/news/changelog-1.8.md b/news/changelog-1.8.md index 6ec0bf0ad5..e2fab88327 100644 --- a/news/changelog-1.8.md +++ b/news/changelog-1.8.md @@ -16,6 +16,7 @@ All changes included in 1.8: - ([#12551](https://github.com/quarto-dev/quarto-cli/pull/12551)): Improve warning issued when `aliases` would overwrite an existing document. - ([#12616](https://github.com/quarto-dev/quarto-cli/issues/12616)): find SVG images in image discovery for listings. +- ([#12693](https://github.com/quarto-dev/quarto-cli/issues/12693)): Prevent resource exhaustion on large websites by serializing `NotebookContext` information to file instead of the environment. ## Crossrefs diff --git a/src/command/render/filters.ts b/src/command/render/filters.ts index 1081b5fc6e..0c87d1ece7 100644 --- a/src/command/render/filters.ts +++ b/src/command/render/filters.ts @@ -549,7 +549,7 @@ function jatsFilterParams(options: PandocOptions) { function notebookContextFilterParams(options: PandocOptions) { const nbContext = options.services.notebook; - const notebooks = nbContext.all(); + const notebooks = nbContext.all(options.project); if (notebooks.length > 0) { return { "notebook-context": notebooks, diff --git a/src/render/notebook/notebook-context.ts b/src/render/notebook/notebook-context.ts index 6b2f30ec39..a7565ef061 100644 --- a/src/render/notebook/notebook-context.ts +++ b/src/render/notebook/notebook-context.ts @@ -75,6 +75,7 @@ export function notebookContext(): NotebookContext { const nb: Notebook = notebooks[nbAbsPath] || emptyNotebook(nbAbsPath); nb[renderType] = output; notebooks[nbAbsPath] = nb; + needRewrite = true; if (context) { const contrib = contributor(renderType); @@ -191,9 +192,28 @@ export function notebookContext(): NotebookContext { } } + let allNotebooksTempFilename: string | undefined; + let needRewrite = true; + return { - all: () => { - return Object.values(notebooks); + all: (context: ProjectContext) => { + if (!allNotebooksTempFilename) { + allNotebooksTempFilename = context.temp.createFile({ + suffix: ".json", + }); + } + if (needRewrite) { + debug( + `[NotebookContext]: Writing all notebooks to ${allNotebooksTempFilename}`, + ); + const objs = Object.values(notebooks); + Deno.writeTextFileSync( + allNotebooksTempFilename, + JSON.stringify(objs), + ); + needRewrite = false; + } + return allNotebooksTempFilename; }, get: (nbAbsPath: string, context?: ProjectContext) => { debug(`[NotebookContext]: Get Notebook:${nbAbsPath}`); diff --git a/src/render/notebook/notebook-types.ts b/src/render/notebook/notebook-types.ts index 2899bf82e5..c6b4089282 100644 --- a/src/render/notebook/notebook-types.ts +++ b/src/render/notebook/notebook-types.ts @@ -81,7 +81,10 @@ export interface NotebookTemplateMetadata extends NotebookMetadata { export interface NotebookContext { // Retrieves the notebook from the context. get: (nbPath: string, context: ProjectContext) => Notebook | undefined; - all: () => Notebook[]; + + // returns a file name with the JSON serialization of all notebooks, Notebook[] + all: (context: ProjectContext) => string; + // Resolves the data on an executedFile into data that will // create a `renderType` output when rendered. addMetadata: (nbPath: string, notebookMetadata: NotebookMetadata) => void; diff --git a/src/resources/filters/layout/manuscript.lua b/src/resources/filters/layout/manuscript.lua index 061e2bdc4c..a0a3f18cc4 100644 --- a/src/resources/filters/layout/manuscript.lua +++ b/src/resources/filters/layout/manuscript.lua @@ -82,7 +82,8 @@ function manuscript() -- Use the notebook cotnext to try to determine the name -- of the output file - local notebooks = param("notebook-context", {}) + local notebooks_filename = param("notebook-context", {}) + local notebooks = quarto.json.decode(io.open(notebooks_filename, "r"):read("*a")) local nbFileName = pandoc.path.filename(nbRelPath) local previewFile = nbFileName .. ".html" for _i, notebook in ipairs(notebooks) do