Skip to content

Commit faba735

Browse files
committed
convert to new html
1 parent ea03ce9 commit faba735

File tree

6 files changed

+95
-206
lines changed

6 files changed

+95
-206
lines changed

src/format/html/format-html-notebook-preview.ts

Lines changed: 47 additions & 199 deletions
Original file line numberDiff line numberDiff line change
@@ -3,43 +3,24 @@
33
*
44
* Copyright (C) 2020-2022 Posit Software, PBC
55
*/
6-
import { formatResourcePath } from "../../core/resources.ts";
7-
import { renderEjs } from "../../core/ejs.ts";
86
import { asArray } from "../../core/array.ts";
97
import * as ld from "../../core/lodash.ts";
108

119
import {
1210
kDownloadUrl,
13-
kNotebookPreserveCells,
14-
kNotebookPreviewBack,
15-
kNotebookPreviewDownload,
1611
kNotebookPreviewOptions,
17-
kNotebookViewStyle,
18-
kOutputFile,
19-
kTemplate,
20-
kTheme,
21-
kTo,
2212
} from "../../config/constants.ts";
2313
import { Format, NotebookPreviewDescriptor } from "../../config/types.ts";
2414

2515
import { RenderServices } from "../../command/render/types.ts";
2616

27-
import {
28-
basename,
29-
dirname,
30-
extname,
31-
isAbsolute,
32-
join,
33-
relative,
34-
} from "path/mod.ts";
35-
import { renderFiles } from "../../command/render/render-files.ts";
36-
import { kNotebookViewStyleNotebook } from "./format-html-constants.ts";
17+
import { basename, dirname, isAbsolute, join, relative } from "path/mod.ts";
3718
import { pathWithForwardSlashes } from "../../core/path.ts";
38-
import { kAppendixStyle } from "./format-html-shared.ts";
3919
import { ProjectContext } from "../../project/types.ts";
4020
import { projectIsBook } from "../../project/project-shared.ts";
41-
import { logProgress } from "../../core/log.ts";
42-
import { readBaseInputIndex } from "../../project/project-index.ts";
21+
import { kHtmlPreview } from "../../render/notebook/notebook-types.ts";
22+
import { kRenderedIPynb } from "../../render/notebook/notebook-types.ts";
23+
import { InternalError } from "../../core/lib/error.ts";
4324

4425
export interface NotebookPreview {
4526
title: string;
@@ -61,15 +42,6 @@ interface NotebookPreviewOptions {
6142
back?: boolean;
6243
}
6344

64-
interface NotebookPreviewConfig {
65-
title: string;
66-
url?: string;
67-
previewFileName: string;
68-
downloadUrl?: string;
69-
downloadFileName?: string;
70-
backHref?: string;
71-
}
72-
7345
export const notebookPreviewer = (
7446
nbView: boolean | NotebookPreviewDescriptor | NotebookPreviewDescriptor[],
7547
format: Format,
@@ -123,7 +95,6 @@ export const notebookPreviewer = (
12395
},
12496
) as NotebookPreviewTask[];
12597

126-
let shownProgressHeader = false;
12798
const total = uniqueWork.length;
12899
for (let i = 0; i < total; i++) {
129100
const work = uniqueWork[i];
@@ -138,86 +109,65 @@ export const notebookPreviewer = (
138109
const descriptor: NotebookPreviewDescriptor | undefined =
139110
nbDescriptors[nbPath];
140111
const nbAbsPath = isAbsolute(nbPath) ? nbPath : join(inputDir, nbPath);
141-
112+
const nbContext = services.notebook;
113+
const notebook = nbContext.get(nbAbsPath);
142114
const supporting: string[] = [];
143115
const resources: string[] = [];
144-
const nbRelPath = relative(inputDir, nbAbsPath);
145116

146-
// Render an output version of the notebook
147-
let downloadUrl = undefined;
148-
let downloadFileName = undefined;
149-
if (!descriptor?.[kDownloadUrl] && !isBook) {
150-
let notebook = services.notebook.get(nbAbsPath);
151-
if (!notebook && output) {
152-
if (!shownProgressHeader) {
153-
logProgress(`Rendering notebooks`);
154-
shownProgressHeader = true;
155-
}
156-
logProgress(`[${i + 1}/${total}] ${nbRelPath}`);
157-
158-
notebook = await services.notebook.render(
117+
// Ensure this has an rendered ipynb and an html preview
118+
if (notebook && notebook[kHtmlPreview] && notebook[kRenderedIPynb]) {
119+
} else {
120+
// Render an ipynb
121+
if (
122+
(!notebook || !notebook[kRenderedIPynb]) &&
123+
!descriptor?.[kDownloadUrl]
124+
) {
125+
const outIpynb = await nbContext.render(
159126
nbAbsPath,
160-
output,
127+
input,
161128
format,
162-
"rendered-ipynb",
129+
kRenderedIPynb,
163130
services,
164131
project,
165132
);
166-
}
167-
if (notebook && notebook["rendered-ipynb"]) {
168-
const outputNb = notebook["rendered-ipynb"];
169-
downloadUrl = outputNb.path;
170-
171-
// Ensure that the output file name for this notebook preview is an `.ipynb`
172-
if (extname(nbAbsPath) !== ".ipynb") {
173-
downloadFileName = `${basename(nbAbsPath)}.ipynb`;
133+
if (outIpynb) {
134+
supporting.push(...outIpynb.supporting);
135+
resources.push(...outIpynb.resourceFiles.files);
174136
}
175-
176-
supporting.push(...outputNb.supporting);
177137
}
178-
}
179138

180-
// Make sure that we have a resolved title
181-
const resolveTitle = async () => {
182-
let resolvedTitle = descriptor?.title || title;
183-
if (!resolvedTitle && project) {
184-
const inputIndex = await readBaseInputIndex(nbPath, project);
185-
if (inputIndex) {
186-
resolvedTitle = inputIndex.title;
139+
if (!notebook || !notebook[kHtmlPreview]) {
140+
const backHref = nbOptions && nbOptions.back && output
141+
? relative(dirname(nbAbsPath), output)
142+
: undefined;
143+
144+
const htmlOut = await nbContext.render(
145+
nbAbsPath,
146+
input,
147+
format,
148+
kHtmlPreview,
149+
services,
150+
project,
151+
);
152+
if (htmlOut.supporting) {
153+
supporting.push(...htmlOut.supporting);
154+
}
155+
if (htmlOut.resourceFiles.files) {
156+
resources.push(...htmlOut.resourceFiles.files);
187157
}
188158
}
189-
return resolvedTitle || basename(nbPath);
190-
};
191-
192-
const backHref = nbOptions && nbOptions.back && output
193-
? relative(dirname(nbAbsPath), output)
194-
: undefined;
195-
const htmlPreview = await renderHtmlView(
196-
inputDir,
197-
nbAbsPath,
198-
{
199-
title: await resolveTitle(),
200-
previewFileName: nbPreviewFile || `${basename(nbPath)}.html`,
201-
url: descriptor?.url,
202-
downloadUrl: descriptor?.[kDownloadUrl] || downloadUrl,
203-
downloadFileName,
204-
backHref,
205-
},
206-
format,
207-
services,
208-
project,
209-
quiet,
210-
);
211-
if (htmlPreview.supporting) {
212-
supporting.push(...htmlPreview.supporting);
213-
}
214-
if (htmlPreview.resources) {
215-
resources.push(...htmlPreview.resources);
216159
}
217160

161+
const renderedNotebook = nbContext.get(nbAbsPath);
162+
if (!renderedNotebook || !renderedNotebook[kHtmlPreview]) {
163+
throw new InternalError(
164+
"We just ensured that notebooks had rendered previews, but they preview then didn't exist.",
165+
);
166+
}
218167
const nbPreview = {
219-
title: htmlPreview.title,
220-
href: htmlPreview.href,
168+
title: renderedNotebook.title ||
169+
"UNTITLED FIX ME",
170+
href: relative(inputDir, renderedNotebook[kHtmlPreview].path),
221171
supporting,
222172
resources,
223173
};
@@ -246,105 +196,3 @@ export const notebookPreviewer = (
246196
descriptor,
247197
};
248198
};
249-
250-
// Renders an HTML preview of a notebook
251-
async function renderHtmlView(
252-
inputDir: string,
253-
nbAbsPath: string,
254-
previewConfig: NotebookPreviewConfig,
255-
format: Format,
256-
services: RenderServices,
257-
project?: ProjectContext,
258-
quiet?: boolean,
259-
): Promise<NotebookPreview> {
260-
// Compute the preview title
261-
if (previewConfig.url === undefined) {
262-
// Create a link back to the input
263-
const href = previewConfig.backHref;
264-
const label = format.language[kNotebookPreviewBack];
265-
266-
// Use the special `embed` template for this render
267-
const embedHtmlEjs = formatResourcePath(
268-
"html",
269-
join("embed", "template.ejs.html"),
270-
);
271-
const embedTemplate = renderEjs(embedHtmlEjs, {
272-
title: previewConfig.title,
273-
path: previewConfig.downloadUrl || basename(nbAbsPath),
274-
filename: previewConfig.downloadFileName || basename(nbAbsPath),
275-
backOptions: {
276-
href,
277-
label,
278-
},
279-
downloadOptions: {
280-
label: format.language[kNotebookPreviewDownload],
281-
},
282-
});
283-
const templatePath = services.temp.createFile({ suffix: ".html" });
284-
Deno.writeTextFileSync(templatePath, embedTemplate);
285-
286-
// Render the notebook and update the path
287-
const rendered = await renderFiles(
288-
[{ path: nbAbsPath, formats: ["html"] }],
289-
{
290-
services,
291-
flags: {
292-
metadata: {
293-
[kTo]: "html",
294-
[kTheme]: format.metadata[kTheme],
295-
[kOutputFile]: previewConfig.previewFileName,
296-
[kTemplate]: templatePath,
297-
[kNotebookViewStyle]: kNotebookViewStyleNotebook,
298-
[kAppendixStyle]: "none",
299-
[kNotebookPreserveCells]: true,
300-
},
301-
quiet,
302-
},
303-
echo: true,
304-
warning: true,
305-
quietPandoc: true,
306-
},
307-
[],
308-
undefined,
309-
project,
310-
);
311-
if (rendered.error) {
312-
throw new Error(`Failed to render preview for notebook ${nbAbsPath}`, {
313-
cause: rendered.error,
314-
});
315-
}
316-
317-
const nbDir = dirname(nbAbsPath);
318-
const supporting = [];
319-
const resources = [];
320-
for (const renderedFile of rendered.files) {
321-
supporting.push(join(inputDir, renderedFile.file));
322-
if (renderedFile.supporting) {
323-
supporting.push(...renderedFile.supporting.map((file) => {
324-
return isAbsolute(file) ? file : join(nbDir, file);
325-
}));
326-
}
327-
328-
if (renderedFile.resourceFiles) {
329-
resources.push(...renderedFile.resourceFiles.files.map((file) => {
330-
return isAbsolute(file) ? file : join(nbDir, file);
331-
}));
332-
}
333-
}
334-
335-
const nbRelPath = relative(inputDir, nbAbsPath);
336-
return {
337-
title: previewConfig.title,
338-
href: pathWithForwardSlashes(
339-
join(dirname(nbRelPath), previewConfig.previewFileName),
340-
),
341-
supporting,
342-
resources,
343-
};
344-
} else {
345-
return {
346-
title: previewConfig.title,
347-
href: previewConfig.url,
348-
};
349-
}
350-
}

src/render/notebook/notebook-context.ts

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,18 @@ export function notebookContext(): NotebookContext {
4141
return `nb-${++nbCount}`;
4242
};
4343

44+
const setTitle = (nbAbsPath: string, title: string) => {
45+
const nb = notebooks[nbAbsPath];
46+
if (nb) {
47+
nb.title = title;
48+
} else {
49+
notebooks[nbAbsPath] = {
50+
source: nbAbsPath,
51+
title,
52+
};
53+
}
54+
};
55+
4456
const contribute = (
4557
nbAbsPath: string,
4658
renderType: RenderType,
@@ -59,7 +71,6 @@ export function notebookContext(): NotebookContext {
5971
} else {
6072
notebooks[nbAbsPath] = {
6173
source: nbAbsPath,
62-
title: "",
6374
[renderType]: output,
6475
};
6576
}
@@ -99,10 +110,14 @@ export function notebookContext(): NotebookContext {
99110
parentFilePath,
100111
token(),
101112
executedFile,
113+
(title: string) => {
114+
setTitle(nbAbsPath, title);
115+
},
102116
renderedNotebook(nbAbsPath),
103117
);
104118
},
105119
contribute,
120+
setTitle,
106121
render: async (
107122
nbAbsPath: string,
108123
parentFilePath: string,
@@ -117,11 +132,20 @@ export function notebookContext(): NotebookContext {
117132
format,
118133
token(),
119134
services,
135+
(title: string) => {
136+
setTitle(nbAbsPath, title);
137+
},
120138
renderedNotebook(nbAbsPath),
121139
project,
122140
);
141+
123142
contribute(nbAbsPath, kJatsSubarticle, renderedFile);
124-
return notebooks[nbAbsPath];
143+
if (!notebooks[nbAbsPath][renderType]) {
144+
throw new InternalError(
145+
"We just rendered and contributed a notebook, but it isn't present in the notebook context.",
146+
);
147+
}
148+
return notebooks[nbAbsPath][renderType]!;
125149
},
126150
cleanup: () => {
127151
const hasNotebooks = Object.keys(notebooks).length > 0;

0 commit comments

Comments
 (0)