CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
sagemathinc

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

GitHub Repository: sagemathinc/cocalc
Path: blob/master/src/packages/jupyter/ipynb/parse.ts
Views: 687
1
/*
2
This is a very lightweight ipynb parser in pure javascript.
3
4
It's used, e.g., in our analogue of nbviewer.
5
*/
6
7
import { IPynbImporter } from "@cocalc/jupyter/ipynb/import-from-ipynb";
8
import { JUPYTER_MIMETYPES } from "@cocalc/jupyter/util/misc";
9
import { field_cmp } from "@cocalc/util/misc";
10
11
export interface KernelSpec {
12
language?: string;
13
display_name: string;
14
name: string;
15
}
16
17
interface Parsed {
18
cellList: string[];
19
cells: { [id: string]: object };
20
cmOptions: { [field: string]: any };
21
kernelspec: KernelSpec;
22
}
23
24
export default function parse(content: string): Parsed {
25
const ipynb = JSON.parse(content);
26
const importer = new IPynbImporter();
27
importer.import({
28
ipynb,
29
output_handler: (cell) => {
30
let k: number = 0;
31
return {
32
message: (content) => {
33
process(content);
34
cell.output[`${k}`] = content;
35
k += 1;
36
},
37
};
38
},
39
});
40
41
const cells = importer.cells();
42
const cellList = sortedCellList(cells);
43
const cmOptions = getCMOptions(getMode(ipynb));
44
const kernelspec = ipynb.metadata?.kernelspec ?? {
45
display_name: "Unknown Kernel",
46
};
47
return { cells, cellList, cmOptions, kernelspec };
48
}
49
50
export function getMode(ipynb): string {
51
return (
52
ipynb.metadata?.language_info?.codemirror_mode ??
53
ipynb.metadata?.language_info?.name ??
54
ipynb.metadata?.kernelspec?.language?.toLowerCase() ??
55
"python"
56
);
57
}
58
59
export function getCMOptions(mode: string | { name: string } | undefined | null) {
60
if (mode == null) {
61
mode = { name: "python" };
62
}
63
if (typeof mode === "string") {
64
mode = { name: mode };
65
}
66
if (mode.name.includes("python") || mode.name.includes("sage")) {
67
mode.name = "python";
68
} else if (mode.name === "gp") {
69
mode.name = "pari";
70
} else if (mode.name === "singular") {
71
mode.name = "clike"; // better than nothing
72
} else if (mode.name === "ihaskell") {
73
mode.name = "haskell";
74
}
75
76
return {
77
mode,
78
showTrailingSpace: true,
79
tabSize: 4,
80
lineWrapping: true,
81
readOnly: true,
82
};
83
}
84
85
function process(content): void {
86
if (content?.data == null) {
87
return;
88
}
89
for (const type of JUPYTER_MIMETYPES) {
90
if (
91
content.data[type] != null &&
92
(type.split("/")[0] === "image" || type === "application/pdf")
93
) {
94
content.data[type] = { value: content.data[type] };
95
}
96
}
97
}
98
99
function sortedCellList(cells): string[] {
100
// Given map from id's to cells, returns an list of ids in correct order,
101
// as defined by pos field.
102
const v: { id: string; pos: number }[] = [];
103
for (const id in cells) {
104
v.push({ id, pos: cells[id]?.pos ?? -1 });
105
}
106
v.sort(field_cmp("pos"));
107
const a: string[] = [];
108
for (const { id } of v) {
109
a.push(id);
110
}
111
return a;
112
}
113
114