Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
quarto-dev
GitHub Repository: quarto-dev/quarto-cli
Path: blob/main/src/render/notebook/notebook-contributor-ipynb.ts
6465 views
1
/*
2
* notebook-contributor-ipynb.ts
3
*
4
* Copyright (C) 2020-2022 Posit Software, PBC
5
*/
6
7
import { renderFile } from "../../command/render/render-files.ts";
8
import {
9
ExecutedFile,
10
RenderedFile,
11
RenderServices,
12
} from "../../command/render/types.ts";
13
import {
14
kClearCellOptions,
15
kClearHiddenClasses,
16
kIpynbProduceSourceNotebook,
17
kIPynbTitleBlockTemplate,
18
kKeepHidden,
19
kOutputFile,
20
kRemoveHidden,
21
kTo,
22
kUnrollMarkdownCells,
23
} from "../../config/constants.ts";
24
import { InternalError } from "../../core/lib/error.ts";
25
import { dirAndStem } from "../../core/path.ts";
26
import { ProjectContext } from "../../project/types.ts";
27
import { NotebookContributor, NotebookMetadata } from "./notebook-types.ts";
28
import { error } from "../../deno_ral/log.ts";
29
import { Format } from "../../config/types.ts";
30
import { ipynbTitleTemplatePath } from "../../format/ipynb/format-ipynb.ts";
31
import { projectOutputDir } from "../../project/project-shared.ts";
32
import { existsSync } from "../../deno_ral/fs.ts";
33
import { dirname, join, relative } from "../../deno_ral/path.ts";
34
import { safeCloneDeep } from "../../core/safe-clone-deep.ts";
35
36
export const outputNotebookContributor: NotebookContributor = {
37
resolve: resolveOutputNotebook,
38
render: renderOutputNotebook,
39
outputFile,
40
cachedPath,
41
};
42
43
export function outputFile(
44
nbAbsPath: string,
45
): string {
46
return ipynbOutputFile(nbAbsPath);
47
}
48
49
function cachedPath(nbAbsPath: string, project?: ProjectContext) {
50
if (project) {
51
const nbRelative = relative(project.dir, dirname(nbAbsPath));
52
const nbOutputDir = join(projectOutputDir(project), nbRelative);
53
54
const outFile = outputFile(nbAbsPath);
55
const outPath = join(nbOutputDir, outFile);
56
if (existsSync(outPath)) {
57
return outPath;
58
}
59
}
60
}
61
62
function resolveOutputNotebook(
63
nbAbsPath: string,
64
_token: string,
65
executedFile: ExecutedFile,
66
_notebookMetadata?: NotebookMetadata,
67
) {
68
const resolved = safeCloneDeep(executedFile);
69
resolved.recipe.format.pandoc[kOutputFile] = outputFile(nbAbsPath);
70
resolved.recipe.output = resolved.recipe.format.pandoc[kOutputFile];
71
72
resolved.recipe.format.pandoc.to = "ipynb";
73
74
// TODO: Move to shared
75
const template = ipynbTitleTemplatePath();
76
77
// Configure echo for this rendering
78
resolved.recipe.format.execute.echo = false;
79
resolved.recipe.format.execute.warning = false;
80
resolved.recipe.format.render[kKeepHidden] = true;
81
resolved.recipe.format.metadata[kClearHiddenClasses] = "all";
82
resolved.recipe.format.metadata[kRemoveHidden] = "none";
83
resolved.recipe.format.metadata[kIPynbTitleBlockTemplate] = template;
84
85
// If this recipe is using a a source notebook, clear the cell options
86
// from the output when rendering
87
if (resolved.recipe.format.render[kIpynbProduceSourceNotebook]) {
88
resolved.recipe.format.render[kClearCellOptions] = true;
89
}
90
91
// Configure markdown behavior for this rendering
92
resolved.recipe.format.metadata[kUnrollMarkdownCells] = false;
93
return resolved;
94
}
95
async function renderOutputNotebook(
96
nbPath: string,
97
_format: Format,
98
_subArticleToken: string,
99
services: RenderServices,
100
_notebookMetadata: NotebookMetadata | undefined,
101
project: ProjectContext,
102
): Promise<RenderedFile> {
103
const rendered = await renderFile(
104
{ path: nbPath, formats: ["ipynb"] },
105
{
106
services,
107
flags: {
108
metadata: {
109
[kTo]: "ipynb",
110
[kOutputFile]: outputFile(nbPath),
111
},
112
quiet: false,
113
},
114
echo: true,
115
warning: true,
116
quietPandoc: true,
117
},
118
services,
119
project,
120
);
121
122
// An error occurred rendering this subarticle
123
if (rendered.error) {
124
error("Rendering of output notebook produced an unexpected result");
125
throw (rendered.error);
126
}
127
128
// There should be only one file
129
if (rendered.files.length !== 1) {
130
throw new InternalError(
131
`Rendering an output notebook should only result in a single file. This attempt resulted in ${rendered.files.length} file(s).`,
132
);
133
}
134
135
return rendered.files[0];
136
}
137
138
function ipynbOutputFile(nbAbsPath: string) {
139
const [_dir, stem] = dirAndStem(nbAbsPath);
140
return `${stem}.out.ipynb`;
141
}
142
143