@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/README.md +154 -71
- package/dist/bin.js +154 -45
- package/dist/index.d.ts +1097 -3
- package/dist/index.js +2 -2
- package/dist/src-CUESP0m8.js +1441 -0
- package/package.json +2 -2
- package/dist/src-j4HyYJfD.js +0 -279
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deepnote/convert",
|
|
3
|
-
"version": "
|
|
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": "
|
|
36
|
+
"@deepnote/blocks": "2.0.0"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
39
39
|
"@types/node": "^22.0.0",
|
package/dist/src-j4HyYJfD.js
DELETED
|
@@ -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 };
|