Path: blob/master/src/packages/project/jupyter/convert/index.ts
1450 views
/*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 htmlToPDF from "./html-to-pdf";11import { parseSource, parseTo } from "./util";12import { join, parse } from "path";13import { getLogger } from "@cocalc/project/logger";14import { sanitize_nbconvert_path } from "@cocalc/util/sanitize-nbconvert";15import type { NbconvertParams } from "@cocalc/util/jupyter/types";1617const log = getLogger("jupyter-nbconvert");1819export async function nbconvert(opts: NbconvertParams): Promise<void> {20log.debug("start", opts);21try {22if (!opts.timeout) {23opts.timeout = 60;24}2526let { j, to } = parseTo(opts.args);2728let convertToPDF = false;29const originalSource = parseSource(opts.args); // before any mangling for the benefit of nbconvert.30if (to == "lab-pdf") {31for (let i = 0; i < opts.args.length; i++) {32if (opts.args[i] == "lab-pdf") {33opts.args[i] = "html";34break;35}36}37to = "html";38convertToPDF = true;39} else if (to == "classic-pdf") {40for (let i = 0; i < opts.args.length; i++) {41if (opts.args[i] == "classic-pdf") {42opts.args[i] = "html";43break;44}45}46to = "html";47convertToPDF = true;48// Put --template argument at beginning -- path must be at the end.49opts.args = ["--template", "classic"].concat(opts.args);50}5152let command: string;53let args: string[];54if (to === "sagews") {55// support sagews converter, which is its own script, not in nbconvert.56// NOTE that if to is set, then j must be set.57command = "smc-ipynb2sagews";58args = opts.args.slice(0, j).concat(opts.args.slice(j + 3)); // j+3 cuts out --to and --.59} else {60command = "jupyter";61args = ["nbconvert"].concat(opts.args);62// This is the **one and only case** where we sanitize the input filename. Doing so when not calling63// nbconvert would actually break everything.64args[args.length - 1] = sanitize_nbconvert_path(args[args.length - 1]);65}6667log.debug("running ", { command, args });68// Note about bash/ulimit_timeout below. This is critical since nbconvert69// could launch things like pdflatex that might run forever and without70// ulimit they do not get killed properly; this has happened in production!71const output = await executeCode({72command,73args,74path: opts.directory,75err_on_exit: false,76timeout: opts.timeout, // in seconds77ulimit_timeout: true,78bash: true,79});80if (output.exit_code != 0) {81throw Error(output.stderr);82}8384if (convertToPDF) {85// Important to use *unmangled* source here!86await htmlToPDF(htmlPath(join(opts.directory ?? "", originalSource)));87}88} finally {89log.debug("finished");90}91}929394function htmlPath(path: string): string {95const { dir, name } = parse(path);96return join(dir, name + ".html");97}9899100101