33 *
44 * Copyright (C) 2020-2022 Posit Software, PBC
55 */
6- import { formatResourcePath } from "../../core/resources.ts" ;
7- import { renderEjs } from "../../core/ejs.ts" ;
86import { asArray } from "../../core/array.ts" ;
97import * as ld from "../../core/lodash.ts" ;
108
119import {
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" ;
2313import { Format , NotebookPreviewDescriptor } from "../../config/types.ts" ;
2414
2515import { 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" ;
3718import { pathWithForwardSlashes } from "../../core/path.ts" ;
38- import { kAppendixStyle } from "./format-html-shared.ts" ;
3919import { ProjectContext } from "../../project/types.ts" ;
4020import { 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
4425export 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-
7345export 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- }
0 commit comments