Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/src/packages/project/jupyter/convert/index.ts
Views: 687
/*1* This file is part of CoCalc: Copyright © 2020 Sagemath, Inc.2* License: MS-RSL – see LICENSE.md for details3*/45/*6Node.js interface to nbconvert.7*/89import { executeCode } from "@cocalc/backend/execute-code";10import ipynbToHtml, { htmlPath } from "./ipynb-to-html";11import htmlToPDF from "./html-to-pdf";12import { parseSource, parseTo } from "./util";13import { join } from "path";14import { getLogger } from "@cocalc/project/logger";15import { sanitize_nbconvert_path } from "@cocalc/util/sanitize-nbconvert";16import type { NbconvertParams } from "@cocalc/jupyter/types/nbconvert";1718const log = getLogger("jupyter-nbconvert");1920export async function nbconvert(opts: NbconvertParams): Promise<void> {21log.debug("start", opts);22try {23if (!opts.timeout) {24opts.timeout = 60;25}2627let { j, to } = parseTo(opts.args);2829if (to == "cocalc-html" || to == "cocalc-pdf") {30// We use our own internal cocalc conversion, since I'm tired of weird subtle issues31// with upstream nbconvert, and we can also be much faster.32const ipynb = join(opts.directory ?? "", parseSource(opts.args)); // make relative to home directory33const html = await ipynbToHtml(ipynb);34if (to == "cocalc-html") {35return;36}37if (to == "cocalc-pdf") {38await htmlToPDF(html, opts.timeout);39return;40}41throw Error("impossible");42}4344let convertToPDF = false;45const originalSource = parseSource(opts.args); // before any mangling for the benefit of nbconvert.46if (to == "lab-pdf") {47for (let i = 0; i < opts.args.length; i++) {48if (opts.args[i] == "lab-pdf") {49opts.args[i] = "html";50break;51}52}53to = "html";54convertToPDF = true;55} else if (to == "classic-pdf") {56for (let i = 0; i < opts.args.length; i++) {57if (opts.args[i] == "classic-pdf") {58opts.args[i] = "html";59break;60}61}62to = "html";63convertToPDF = true;64// Put --template argument at beginning -- path must be at the end.65opts.args = ["--template", "classic"].concat(opts.args);66}6768let command: string;69let args: string[];70if (to === "sagews") {71// support sagews converter, which is its own script, not in nbconvert.72// NOTE that if to is set, then j must be set.73command = "smc-ipynb2sagews";74args = opts.args.slice(0, j).concat(opts.args.slice(j + 3)); // j+3 cuts out --to and --.75} else {76command = "jupyter";77args = ["nbconvert"].concat(opts.args);78// This is the **one and only case** where we sanitize the input filename. Doing so when not calling79// nbconvert would actually break everything.80args[args.length - 1] = sanitize_nbconvert_path(args[args.length - 1]);81}8283log.debug("running ", { command, args });84// Note about bash/ulimit_timeout below. This is critical since nbconvert85// could launch things like pdflatex that might run forever and without86// ulimit they do not get killed properly; this has happened in production!87const output = await executeCode({88command,89args,90path: opts.directory,91err_on_exit: false,92timeout: opts.timeout, // in seconds93ulimit_timeout: true,94bash: true,95});96if (output.exit_code != 0) {97throw Error(output.stderr);98}99100if (convertToPDF) {101// Important to use *unmangled* source here!102await htmlToPDF(htmlPath(join(opts.directory ?? "", originalSource)));103}104} finally {105log.debug("finished");106}107}108109110