Path: blob/main/src/render/notebook/notebook-contributor-html.ts
6458 views
/*1* notebook-contributor-html.ts2*3* Copyright (C) 2020-2022 Posit Software, PBC4*/56import { renderFile } from "../../command/render/render-files.ts";7import {8ExecutedFile,9RenderedFile,10RenderServices,11} from "../../command/render/types.ts";12import {13kClearCellOptions,14kClearHiddenClasses,15kDisableArticleLayout,16kFormatLinks,17kIpynbProduceSourceNotebook,18kKeepHidden,19kNotebookPreserveCells,20kNotebookPreviewBack,21kNotebookPreviewDownload,22kNotebookPreviewDownloadSrc,23kNotebookViewStyle,24kOutputFile,25kRemoveHidden,26kTemplate,27kTheme,28kTo,29kToc,30kTocLocation,31kUnrollMarkdownCells,32} from "../../config/constants.ts";33import { InternalError } from "../../core/lib/error.ts";34import { ProjectContext } from "../../project/types.ts";35import {36NotebookContributor,37NotebookMetadata,38NotebookTemplateMetadata,39} from "./notebook-types.ts";4041import { error } from "../../deno_ral/log.ts";42import { formatResourcePath } from "../../core/resources.ts";43import { kNotebookViewStyleNotebook } from "../../format/html/format-html-constants.ts";44import { kAppendixStyle } from "../../format/html/format-html-shared.ts";45import { basename, dirname, join, relative } from "../../deno_ral/path.ts";46import { Format } from "../../config/types.ts";47import { dirAndStem, isQmdFile } from "../../core/path.ts";48import { projectOutputDir } from "../../project/project-shared.ts";49import { existsSync } from "../../deno_ral/fs.ts";50import { safeCloneDeep } from "../../core/safe-clone-deep.ts";5152export const htmlNotebookContributor: NotebookContributor = {53resolve: resolveHtmlNotebook,54render: renderHtmlNotebook,55outputFile,56cachedPath,57};5859export function outputFile(60nbAbsPath: string,61): string {62const [_dir, stem] = dirAndStem(basename(nbAbsPath));63return `${stem}-preview.html`;64}6566function cachedPath(nbAbsPath: string, project?: ProjectContext) {67if (project) {68const nbRelative = relative(project.dir, dirname(nbAbsPath));69const nbOutputDir = join(projectOutputDir(project), nbRelative);7071const outFile = outputFile(nbAbsPath);72const outPath = join(nbOutputDir, outFile);73if (existsSync(outPath)) {74return outPath;75}76}77}7879function resolveHtmlNotebook(80nbAbsPath: string,81_token: string,82executedFile: ExecutedFile,83notebookMetadata?: NotebookMetadata,84) {85const resolved = safeCloneDeep(executedFile);8687// Set the output file88resolved.recipe.format.pandoc[kOutputFile] = `${outputFile(nbAbsPath)}`;89resolved.recipe.output = resolved.recipe.format.pandoc[kOutputFile];9091// Configure echo for this rendering to ensure there is output92// that we can manually control93resolved.recipe.format.execute.echo = false;94resolved.recipe.format.execute.warning = false;95resolved.recipe.format.render[kKeepHidden] = true;96resolved.recipe.format.metadata[kClearHiddenClasses] = "all";97resolved.recipe.format.metadata[kRemoveHidden] = "none";9899// If this recipe is using a a source notebook, clear the cell options100// from the output when rendering101if (resolved.recipe.format.render[kIpynbProduceSourceNotebook]) {102resolved.recipe.format.render[kClearCellOptions] = true;103}104105// Use the special `embed/notebook` template for this render106const template = formatResourcePath(107"html",108join("embed", "template.html"),109);110resolved.recipe.format.pandoc[kTemplate] = template;111112// Metadata used by template when rendering113resolved.recipe.format.metadata["nbMeta"] = {114...notebookMetadata,115downloadLabel: downloadLabel(116notebookMetadata?.filename || nbAbsPath,117resolved.recipe.format,118),119backLabel: resolved.recipe.format.language[kNotebookPreviewBack],120} as NotebookTemplateMetadata;121122// Configure the notebook style123resolved.recipe.format.render[kNotebookViewStyle] =124kNotebookViewStyleNotebook;125resolved.recipe.format.render[kNotebookPreserveCells] = true;126resolved.recipe.format.metadata[kUnrollMarkdownCells] = false;127128// Configure the appearance129resolved.recipe.format.pandoc[kToc] = true;130resolved.recipe.format.metadata[kTocLocation] = "left";131resolved.recipe.format.metadata[kAppendixStyle] = "none";132resolved.recipe.format.render[kFormatLinks] = false;133134resolved.recipe.format.metadata[kDisableArticleLayout] = true;135136return resolved;137}138139async function renderHtmlNotebook(140nbPath: string,141format: Format,142_subArticleToken: string,143services: RenderServices,144notebookMetadata: NotebookMetadata | undefined,145project: ProjectContext,146): Promise<RenderedFile> {147// Use the special `embed` template for this render148const template = formatResourcePath(149"html",150join("embed", "template.html"),151);152153// Render the notebook and update the path154const rendered = await renderFile(155{ path: nbPath, formats: ["html"] },156{157services,158flags: {159metadata: {160[kTo]: "html",161[kTheme]: format.metadata[kTheme],162[kOutputFile]: `${outputFile(nbPath)}`,163[kTemplate]: template,164[kNotebookViewStyle]: kNotebookViewStyleNotebook,165[kAppendixStyle]: "none",166[kNotebookPreserveCells]: true,167["nbMeta"]: {168...notebookMetadata,169downloadLabel: downloadLabel(170notebookMetadata?.filename || nbPath,171format,172),173backLabel: format.language[kNotebookPreviewBack],174} as NotebookTemplateMetadata,175[kToc]: true,176[kTocLocation]: "left",177[kDisableArticleLayout]: true,178},179quiet: false,180},181echo: true,182warning: true,183quietPandoc: true,184},185services,186project,187);188189// An error occurred rendering this subarticle190if (rendered.error) {191error("Rendering of output notebook produced an unexpected result");192throw (rendered.error);193}194195// There should be only one file196if (rendered.files.length !== 1) {197throw new InternalError(198`Rendering an output notebook should only result in a single file. This attempt resulted in ${rendered.files.length} file(s).`,199);200}201202return rendered.files[0];203}204205function downloadLabel(file: string, format: Format) {206return isQmdFile(file)207? format.language[kNotebookPreviewDownloadSrc]208: format.language[kNotebookPreviewDownload];209}210211212