@deepnote/convert 1.3.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deepnote/convert",
3
- "version": "1.3.0",
3
+ "version": "2.0.0",
4
4
  "description": "",
5
5
  "keywords": [],
6
6
  "repository": {
@@ -33,7 +33,7 @@
33
33
  "ora": "^9.0.0",
34
34
  "uuid": "^13.0.0",
35
35
  "yaml": "^2.8.1",
36
- "@deepnote/blocks": "1.3.6"
36
+ "@deepnote/blocks": "2.0.0"
37
37
  },
38
38
  "devDependencies": {
39
39
  "@types/node": "^22.0.0",
@@ -1,279 +0,0 @@
1
- import fs from "node:fs/promises";
2
- import { basename, dirname, extname, join } from "node:path";
3
- import { createMarkdown, createPythonCode, deserializeDeepnoteFile } from "@deepnote/blocks";
4
- import { v4 } from "uuid";
5
- import { stringify } from "yaml";
6
-
7
- //#region src/deepnote-to-jupyter.ts
8
- /**
9
- * Converts an array of Deepnote blocks into a single Jupyter Notebook.
10
- * This is the lowest-level conversion function, suitable for use in Deepnote Cloud.
11
- *
12
- * @param blocks - Array of DeepnoteBlock objects to convert
13
- * @param options - Notebook metadata options
14
- * @returns A JupyterNotebook object
15
- *
16
- * @example
17
- * ```typescript
18
- * import { convertBlocksToJupyterNotebook } from '@deepnote/convert'
19
- *
20
- * const notebook = convertBlocksToJupyterNotebook(blocks, {
21
- * notebookId: 'abc123',
22
- * notebookName: 'My Notebook',
23
- * executionMode: 'block'
24
- * })
25
- * ```
26
- */
27
- function convertBlocksToJupyterNotebook(blocks, options) {
28
- return {
29
- cells: blocks.map((block) => convertBlockToCell(block)),
30
- metadata: {
31
- deepnote_notebook_id: options.notebookId,
32
- deepnote_notebook_name: options.notebookName,
33
- deepnote_execution_mode: options.executionMode,
34
- deepnote_is_module: options.isModule,
35
- deepnote_working_directory: options.workingDirectory
36
- },
37
- nbformat: 4,
38
- nbformat_minor: 0
39
- };
40
- }
41
- /**
42
- * Converts a Deepnote project into Jupyter Notebook objects.
43
- * This is a pure conversion function that doesn't perform any file I/O.
44
- * Each notebook in the Deepnote project is converted to a separate Jupyter notebook.
45
- *
46
- * @param deepnoteFile - The deserialized Deepnote project file
47
- * @returns Array of objects containing filename and corresponding Jupyter notebook
48
- *
49
- * @example
50
- * ```typescript
51
- * import { deserializeDeepnoteFile } from '@deepnote/blocks'
52
- * import { convertDeepnoteToJupyterNotebooks } from '@deepnote/convert'
53
- *
54
- * const yamlContent = await fs.readFile('project.deepnote', 'utf-8')
55
- * const deepnoteFile = deserializeDeepnoteFile(yamlContent)
56
- * const notebooks = convertDeepnoteToJupyterNotebooks(deepnoteFile)
57
- *
58
- * for (const { filename, notebook } of notebooks) {
59
- * console.log(`${filename}: ${notebook.cells.length} cells`)
60
- * }
61
- * ```
62
- */
63
- function convertDeepnoteToJupyterNotebooks(deepnoteFile) {
64
- return deepnoteFile.project.notebooks.map((notebook) => {
65
- const jupyterNotebook = convertNotebookToJupyter(deepnoteFile, notebook);
66
- return {
67
- filename: `${sanitizeFileName(notebook.name)}.ipynb`,
68
- notebook: jupyterNotebook
69
- };
70
- });
71
- }
72
- /**
73
- * Converts a Deepnote project file into separate Jupyter Notebook (.ipynb) files.
74
- * Each notebook in the Deepnote project becomes a separate .ipynb file.
75
- */
76
- async function convertDeepnoteFileToJupyterFiles(deepnoteFilePath, options) {
77
- const notebooks = convertDeepnoteToJupyterNotebooks(deserializeDeepnoteFile(await fs.readFile(deepnoteFilePath, "utf-8")));
78
- await fs.mkdir(options.outputDir, { recursive: true });
79
- for (const { filename, notebook } of notebooks) {
80
- const filePath = join(options.outputDir, filename);
81
- await fs.writeFile(filePath, JSON.stringify(notebook, null, 2), "utf-8");
82
- }
83
- }
84
- function convertBlockToCell(block) {
85
- const content = block.content || "";
86
- const jupyterCellType = convertBlockTypeToJupyter(block.type);
87
- const metadata = {
88
- cell_id: block.id,
89
- deepnote_block_group: block.blockGroup,
90
- deepnote_cell_type: block.type,
91
- deepnote_sorting_key: block.sortingKey,
92
- ...block.metadata || {}
93
- };
94
- metadata.deepnote_source = content;
95
- return {
96
- block_group: block.blockGroup,
97
- cell_type: jupyterCellType,
98
- execution_count: block.executionCount ?? null,
99
- metadata,
100
- outputs: block.outputs,
101
- source: getSourceForBlock(block, jupyterCellType, content)
102
- };
103
- }
104
- function getSourceForBlock(block, jupyterCellType, content) {
105
- if (jupyterCellType === "markdown") return createMarkdown(block);
106
- if (block.type === "code") return content;
107
- return createPythonCode(block);
108
- }
109
- function convertBlockTypeToJupyter(blockType) {
110
- const codeTypes = [
111
- "big-number",
112
- "button",
113
- "code",
114
- "notebook-function",
115
- "sql",
116
- "visualization"
117
- ];
118
- if (blockType.startsWith("input-")) return "code";
119
- return codeTypes.includes(blockType) ? "code" : "markdown";
120
- }
121
- function convertNotebookToJupyter(_deepnoteFile, notebook) {
122
- return convertBlocksToJupyterNotebook(notebook.blocks, {
123
- notebookId: notebook.id,
124
- notebookName: notebook.name,
125
- executionMode: notebook.executionMode,
126
- isModule: notebook.isModule,
127
- workingDirectory: notebook.workingDirectory
128
- });
129
- }
130
- function sanitizeFileName(name) {
131
- return name.replace(/[<>:"/\\|?*]/g, "_").replace(/\s+/g, "-");
132
- }
133
-
134
- //#endregion
135
- //#region src/jupyter-to-deepnote.ts
136
- /**
137
- * Converts a single Jupyter Notebook into an array of Deepnote blocks.
138
- * This is the lowest-level conversion function, suitable for use in Deepnote Cloud.
139
- *
140
- * @param notebook - The Jupyter notebook object to convert
141
- * @param options - Optional conversion options including custom ID generator
142
- * @returns Array of DeepnoteBlock objects
143
- *
144
- * @example
145
- * ```typescript
146
- * import { convertJupyterNotebookToBlocks } from '@deepnote/convert'
147
- *
148
- * const notebook = JSON.parse(ipynbContent)
149
- * const blocks = convertJupyterNotebookToBlocks(notebook, {
150
- * idGenerator: () => myCustomIdGenerator()
151
- * })
152
- * ```
153
- */
154
- function convertJupyterNotebookToBlocks(notebook, options) {
155
- const idGenerator = options?.idGenerator ?? v4;
156
- return notebook.cells.map((cell, index) => convertCellToBlock(cell, index, idGenerator));
157
- }
158
- /**
159
- * Converts Jupyter Notebook objects into a Deepnote project file.
160
- * This is a pure conversion function that doesn't perform any file I/O.
161
- *
162
- * @param notebooks - Array of Jupyter notebooks with filenames
163
- * @param options - Conversion options including project name
164
- * @returns A DeepnoteFile object
165
- */
166
- function convertJupyterNotebooksToDeepnote(notebooks, options) {
167
- const deepnoteFile = {
168
- metadata: { createdAt: (/* @__PURE__ */ new Date()).toISOString() },
169
- project: {
170
- id: v4(),
171
- initNotebookId: void 0,
172
- integrations: [],
173
- name: options.projectName,
174
- notebooks: [],
175
- settings: {}
176
- },
177
- version: "1.0.0"
178
- };
179
- for (const { filename, notebook } of notebooks) {
180
- const filenameWithoutExt = basename(filename, extname(filename)) || "Untitled notebook";
181
- const blocks = convertJupyterNotebookToBlocks(notebook);
182
- const notebookId = notebook.metadata?.deepnote_notebook_id;
183
- const notebookName = notebook.metadata?.deepnote_notebook_name;
184
- const executionMode = notebook.metadata?.deepnote_execution_mode;
185
- const isModule = notebook.metadata?.deepnote_is_module;
186
- const workingDirectory = notebook.metadata?.deepnote_working_directory;
187
- deepnoteFile.project.notebooks.push({
188
- blocks,
189
- executionMode: executionMode ?? "block",
190
- id: notebookId ?? v4(),
191
- isModule: isModule ?? false,
192
- name: notebookName ?? filenameWithoutExt,
193
- workingDirectory
194
- });
195
- }
196
- return deepnoteFile;
197
- }
198
- /**
199
- * Converts multiple Jupyter Notebook (.ipynb) files into a single Deepnote project file.
200
- */
201
- async function convertIpynbFilesToDeepnoteFile(inputFilePaths, options) {
202
- const notebooks = [];
203
- for (const filePath of inputFilePaths) {
204
- const notebook = await parseIpynbFile(filePath);
205
- notebooks.push({
206
- filename: basename(filePath),
207
- notebook
208
- });
209
- }
210
- const yamlContent = stringify(convertJupyterNotebooksToDeepnote(notebooks, { projectName: options.projectName }));
211
- const parentDir = dirname(options.outputPath);
212
- await fs.mkdir(parentDir, { recursive: true });
213
- await fs.writeFile(options.outputPath, yamlContent, "utf-8");
214
- }
215
- async function parseIpynbFile(filePath) {
216
- let ipynbJson;
217
- try {
218
- ipynbJson = await fs.readFile(filePath, "utf-8");
219
- } catch (error) {
220
- const message = error instanceof Error ? error.message : String(error);
221
- throw new Error(`Failed to read ${filePath}: ${message}`);
222
- }
223
- try {
224
- return JSON.parse(ipynbJson);
225
- } catch (error) {
226
- const message = error instanceof Error ? error.message : String(error);
227
- throw new Error(`Failed to parse ${filePath}: invalid JSON - ${message}`);
228
- }
229
- }
230
- function convertCellToBlock(cell, index, idGenerator) {
231
- let source = Array.isArray(cell.source) ? cell.source.join("") : cell.source;
232
- const cellId = cell.metadata?.cell_id;
233
- const deepnoteCellType = cell.metadata?.deepnote_cell_type;
234
- const sortingKey = cell.metadata?.deepnote_sorting_key;
235
- const blockGroup = cell.metadata?.deepnote_block_group ?? cell.block_group ?? idGenerator();
236
- const deepnoteSource = cell.metadata?.deepnote_source;
237
- if (deepnoteSource !== void 0) source = deepnoteSource;
238
- const blockType = deepnoteCellType ?? (cell.cell_type === "code" ? "code" : "markdown");
239
- const originalMetadata = { ...cell.metadata };
240
- delete originalMetadata.cell_id;
241
- delete originalMetadata.deepnote_cell_type;
242
- delete originalMetadata.deepnote_block_group;
243
- delete originalMetadata.deepnote_sorting_key;
244
- delete originalMetadata.deepnote_source;
245
- delete cell.block_group;
246
- const executionCount = cell.execution_count ?? void 0;
247
- const hasExecutionCount = executionCount !== void 0;
248
- const hasOutputs = cell.cell_type === "code" && cell.outputs !== void 0;
249
- return {
250
- blockGroup,
251
- content: source,
252
- ...hasExecutionCount ? { executionCount } : {},
253
- id: cellId ?? idGenerator(),
254
- metadata: originalMetadata,
255
- ...hasOutputs ? { outputs: cell.outputs } : {},
256
- sortingKey: sortingKey ?? createSortingKey(index),
257
- type: blockType
258
- };
259
- }
260
- function createSortingKey(index) {
261
- const maxLength = 6;
262
- const chars = "0123456789abcdefghijklmnopqrstuvwxyz";
263
- const base = 36;
264
- if (index < 0) throw new Error("Index must be non-negative");
265
- let result = "";
266
- let num = index + 1;
267
- let iterations = 0;
268
- while (num > 0 && iterations < maxLength) {
269
- num--;
270
- result = chars[num % base] + result;
271
- num = Math.floor(num / base);
272
- iterations++;
273
- }
274
- if (num > 0) throw new Error(`Index ${index} exceeds maximum key length of ${maxLength}`);
275
- return result;
276
- }
277
-
278
- //#endregion
279
- export { convertDeepnoteFileToJupyterFiles as a, convertBlocksToJupyterNotebook as i, convertJupyterNotebookToBlocks as n, convertDeepnoteToJupyterNotebooks as o, convertJupyterNotebooksToDeepnote as r, convertIpynbFilesToDeepnoteFile as t };