Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemathinc
GitHub Repository: sagemathinc/cocalc
Path: blob/master/src/packages/project/jupyter/convert/index.ts
1450 views
1
/*
2
* This file is part of CoCalc: Copyright © 2020 Sagemath, Inc.
3
* License: MS-RSL – see LICENSE.md for details
4
*/
5
6
/*
7
Node.js interface to nbconvert.
8
*/
9
10
import { executeCode } from "@cocalc/backend/execute-code";
11
import htmlToPDF from "./html-to-pdf";
12
import { parseSource, parseTo } from "./util";
13
import { join, parse } from "path";
14
import { getLogger } from "@cocalc/project/logger";
15
import { sanitize_nbconvert_path } from "@cocalc/util/sanitize-nbconvert";
16
import type { NbconvertParams } from "@cocalc/util/jupyter/types";
17
18
const log = getLogger("jupyter-nbconvert");
19
20
export async function nbconvert(opts: NbconvertParams): Promise<void> {
21
log.debug("start", opts);
22
try {
23
if (!opts.timeout) {
24
opts.timeout = 60;
25
}
26
27
let { j, to } = parseTo(opts.args);
28
29
let convertToPDF = false;
30
const originalSource = parseSource(opts.args); // before any mangling for the benefit of nbconvert.
31
if (to == "lab-pdf") {
32
for (let i = 0; i < opts.args.length; i++) {
33
if (opts.args[i] == "lab-pdf") {
34
opts.args[i] = "html";
35
break;
36
}
37
}
38
to = "html";
39
convertToPDF = true;
40
} else if (to == "classic-pdf") {
41
for (let i = 0; i < opts.args.length; i++) {
42
if (opts.args[i] == "classic-pdf") {
43
opts.args[i] = "html";
44
break;
45
}
46
}
47
to = "html";
48
convertToPDF = true;
49
// Put --template argument at beginning -- path must be at the end.
50
opts.args = ["--template", "classic"].concat(opts.args);
51
}
52
53
let command: string;
54
let args: string[];
55
if (to === "sagews") {
56
// support sagews converter, which is its own script, not in nbconvert.
57
// NOTE that if to is set, then j must be set.
58
command = "smc-ipynb2sagews";
59
args = opts.args.slice(0, j).concat(opts.args.slice(j + 3)); // j+3 cuts out --to and --.
60
} else {
61
command = "jupyter";
62
args = ["nbconvert"].concat(opts.args);
63
// This is the **one and only case** where we sanitize the input filename. Doing so when not calling
64
// nbconvert would actually break everything.
65
args[args.length - 1] = sanitize_nbconvert_path(args[args.length - 1]);
66
}
67
68
log.debug("running ", { command, args });
69
// Note about bash/ulimit_timeout below. This is critical since nbconvert
70
// could launch things like pdflatex that might run forever and without
71
// ulimit they do not get killed properly; this has happened in production!
72
const output = await executeCode({
73
command,
74
args,
75
path: opts.directory,
76
err_on_exit: false,
77
timeout: opts.timeout, // in seconds
78
ulimit_timeout: true,
79
bash: true,
80
});
81
if (output.exit_code != 0) {
82
throw Error(output.stderr);
83
}
84
85
if (convertToPDF) {
86
// Important to use *unmangled* source here!
87
await htmlToPDF(htmlPath(join(opts.directory ?? "", originalSource)));
88
}
89
} finally {
90
log.debug("finished");
91
}
92
}
93
94
95
function htmlPath(path: string): string {
96
const { dir, name } = parse(path);
97
return join(dir, name + ".html");
98
}
99
100
101