@deepnote/convert 1.4.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 +476 -13
- package/dist/index.js +2 -2
- package/dist/src-CUESP0m8.js +1441 -0
- package/package.json +2 -2
- package/dist/src-DgOsAHcf.js +0 -309
package/README.md
CHANGED
|
@@ -1,13 +1,18 @@
|
|
|
1
1
|
# @deepnote/convert
|
|
2
2
|
|
|
3
|
-
Bidirectional converter between
|
|
3
|
+
Bidirectional converter between Deepnote project files (`.deepnote`) and multiple notebook formats: Jupyter (`.ipynb`), Quarto (`.qmd`), Percent (`.py`), and Marimo (`.py`).
|
|
4
4
|
|
|
5
5
|
```bash
|
|
6
|
-
# Convert
|
|
6
|
+
# Convert any supported format to Deepnote
|
|
7
7
|
npx @deepnote/convert notebook.ipynb
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
npx @deepnote/convert document.qmd
|
|
9
|
+
npx @deepnote/convert notebook.py # percent or marimo format
|
|
10
|
+
|
|
11
|
+
# Convert Deepnote to any supported format
|
|
12
|
+
npx @deepnote/convert project.deepnote # defaults to Jupyter
|
|
13
|
+
npx @deepnote/convert project.deepnote --outputFormat quarto
|
|
14
|
+
npx @deepnote/convert project.deepnote --outputFormat percent
|
|
15
|
+
npx @deepnote/convert project.deepnote --outputFormat marimo
|
|
11
16
|
```
|
|
12
17
|
|
|
13
18
|
## Installation
|
|
@@ -16,153 +21,231 @@ npx @deepnote/convert project.deepnote
|
|
|
16
21
|
npm install -g @deepnote/convert
|
|
17
22
|
```
|
|
18
23
|
|
|
19
|
-
##
|
|
20
|
-
|
|
21
|
-
The package provides a `deepnote-convert` command-line tool for bidirectional conversion between Jupyter and Deepnote formats.
|
|
24
|
+
## Supported Formats
|
|
22
25
|
|
|
23
|
-
|
|
26
|
+
| Format | Extension | Description |
|
|
27
|
+
| ----------- | --------- | ------------------------------------------------------- |
|
|
28
|
+
| **Jupyter** | `.ipynb` | Standard Jupyter Notebook JSON format |
|
|
29
|
+
| **Quarto** | `.qmd` | Quarto markdown documents with code chunks |
|
|
30
|
+
| **Percent** | `.py` | Python files with `# %%` cell markers (VS Code, Spyder) |
|
|
31
|
+
| **Marimo** | `.py` | Marimo reactive notebooks with `@app.cell` decorators |
|
|
24
32
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
Convert a single `.ipynb` file to a `.deepnote` file:
|
|
28
|
-
|
|
29
|
-
```bash
|
|
30
|
-
deepnote-convert path/to/notebook.ipynb
|
|
31
|
-
```
|
|
33
|
+
## CLI Usage
|
|
32
34
|
|
|
33
|
-
|
|
35
|
+
The package provides a `deepnote-convert` command-line tool for bidirectional conversion.
|
|
34
36
|
|
|
35
|
-
|
|
37
|
+
### Convert to Deepnote
|
|
36
38
|
|
|
37
|
-
|
|
39
|
+
Any supported format converts to `.deepnote` automatically:
|
|
38
40
|
|
|
39
41
|
```bash
|
|
42
|
+
# Single file
|
|
43
|
+
deepnote-convert notebook.ipynb
|
|
44
|
+
deepnote-convert document.qmd
|
|
45
|
+
deepnote-convert notebook.py # auto-detects percent vs marimo
|
|
46
|
+
|
|
47
|
+
# Directory of files
|
|
40
48
|
deepnote-convert path/to/notebooks/
|
|
41
49
|
```
|
|
42
50
|
|
|
43
|
-
|
|
51
|
+
### Convert from Deepnote
|
|
44
52
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
Convert a `.deepnote` file to Jupyter notebooks:
|
|
53
|
+
Use `--outputFormat` to choose the target format (defaults to `jupyter`):
|
|
48
54
|
|
|
49
55
|
```bash
|
|
50
|
-
deepnote-convert
|
|
56
|
+
deepnote-convert project.deepnote # → Jupyter notebooks
|
|
57
|
+
deepnote-convert project.deepnote --outputFormat jupyter # → Jupyter notebooks
|
|
58
|
+
deepnote-convert project.deepnote --outputFormat quarto # → Quarto documents
|
|
59
|
+
deepnote-convert project.deepnote --outputFormat percent # → Percent format files
|
|
60
|
+
deepnote-convert project.deepnote --outputFormat marimo # → Marimo notebooks
|
|
51
61
|
```
|
|
52
62
|
|
|
53
|
-
This will create a `project/` directory containing separate `.ipynb` files for each notebook in the Deepnote project.
|
|
54
|
-
|
|
55
63
|
### Options
|
|
56
64
|
|
|
57
65
|
#### `--projectName <name>`
|
|
58
66
|
|
|
59
|
-
Set a custom name for the Deepnote project:
|
|
67
|
+
Set a custom name for the Deepnote project (when converting to `.deepnote`):
|
|
60
68
|
|
|
61
69
|
```bash
|
|
62
70
|
deepnote-convert notebook.ipynb --projectName "My Analysis"
|
|
63
71
|
```
|
|
64
72
|
|
|
65
|
-
If not specified, the project name will default to the filename (without extension) or directory name.
|
|
66
|
-
|
|
67
73
|
#### `-o, --outputPath <path>`
|
|
68
74
|
|
|
69
|
-
Specify where to save the output
|
|
75
|
+
Specify where to save the output:
|
|
70
76
|
|
|
71
77
|
```bash
|
|
72
|
-
#
|
|
78
|
+
# To Deepnote: Save to a specific file or directory
|
|
73
79
|
deepnote-convert notebook.ipynb -o output/project.deepnote
|
|
74
|
-
|
|
75
|
-
# For Jupyter → Deepnote: Save to a directory (filename will be auto-generated)
|
|
76
80
|
deepnote-convert notebook.ipynb -o output/
|
|
77
81
|
|
|
78
|
-
#
|
|
79
|
-
deepnote-convert project.deepnote -o output/
|
|
82
|
+
# From Deepnote: Specify output directory
|
|
83
|
+
deepnote-convert project.deepnote -o output/notebooks/
|
|
80
84
|
```
|
|
81
85
|
|
|
82
|
-
|
|
86
|
+
#### `--outputFormat <format>`
|
|
87
|
+
|
|
88
|
+
Choose output format when converting from `.deepnote`:
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
deepnote-convert project.deepnote --outputFormat quarto
|
|
92
|
+
```
|
|
83
93
|
|
|
84
|
-
|
|
85
|
-
- For Deepnote → Jupyter: A directory will be created using the `.deepnote` filename (e.g., `project.deepnote` → `project/`)
|
|
94
|
+
Options: `jupyter` (default), `quarto`, `percent`, `marimo`
|
|
86
95
|
|
|
87
96
|
### Examples
|
|
88
97
|
|
|
89
98
|
```bash
|
|
90
|
-
# Jupyter → Deepnote
|
|
99
|
+
# Jupyter → Deepnote
|
|
91
100
|
deepnote-convert titanic.ipynb --projectName "Titanic Analysis"
|
|
101
|
+
deepnote-convert ./notebooks -o ./output
|
|
92
102
|
|
|
93
|
-
#
|
|
94
|
-
deepnote-convert
|
|
103
|
+
# Quarto → Deepnote
|
|
104
|
+
deepnote-convert analysis.qmd --projectName "Data Report"
|
|
95
105
|
|
|
96
|
-
#
|
|
97
|
-
deepnote-convert
|
|
106
|
+
# Percent → Deepnote (auto-detected from # %% markers)
|
|
107
|
+
deepnote-convert script.py
|
|
98
108
|
|
|
99
|
-
#
|
|
100
|
-
deepnote-convert
|
|
109
|
+
# Marimo → Deepnote (auto-detected from @app.cell decorators)
|
|
110
|
+
deepnote-convert reactive.py
|
|
101
111
|
|
|
102
|
-
# Deepnote →
|
|
103
|
-
deepnote-convert
|
|
112
|
+
# Deepnote → various formats
|
|
113
|
+
deepnote-convert project.deepnote # Jupyter (default)
|
|
114
|
+
deepnote-convert project.deepnote --outputFormat quarto # Quarto
|
|
115
|
+
deepnote-convert project.deepnote --outputFormat percent # Percent
|
|
116
|
+
deepnote-convert project.deepnote --outputFormat marimo # Marimo
|
|
104
117
|
```
|
|
105
118
|
|
|
119
|
+
### Python File Detection
|
|
120
|
+
|
|
121
|
+
When converting `.py` files, the converter auto-detects the format:
|
|
122
|
+
|
|
123
|
+
- **Marimo**: Contains `import marimo` and `@app.cell` decorators
|
|
124
|
+
- **Percent**: Contains `# %%` cell markers
|
|
125
|
+
|
|
126
|
+
For directory scanning, use explicit naming:
|
|
127
|
+
|
|
128
|
+
- `*.marimo.py` for Marimo files
|
|
129
|
+
- `*.percent.py` for percent format files
|
|
130
|
+
|
|
106
131
|
### Lossless Roundtrip Conversion
|
|
107
132
|
|
|
108
|
-
The converter supports lossless roundtrip conversions:
|
|
133
|
+
The converter supports lossless roundtrip conversions for Jupyter:
|
|
109
134
|
|
|
110
|
-
- **Deepnote → Jupyter → Deepnote**: Preserves all Deepnote-specific metadata in Jupyter cell metadata
|
|
135
|
+
- **Deepnote → Jupyter → Deepnote**: Preserves all Deepnote-specific metadata in Jupyter cell metadata
|
|
111
136
|
- **Jupyter → Deepnote → Jupyter**: Preserves original Jupyter content while adding Deepnote metadata
|
|
112
137
|
|
|
113
|
-
|
|
138
|
+
Other formats (Quarto, Percent, Marimo) preserve content and structure but may not retain all Deepnote-specific metadata.
|
|
114
139
|
|
|
115
|
-
|
|
140
|
+
### Platform Compatibility
|
|
116
141
|
|
|
117
|
-
|
|
142
|
+
Since Jupyter (`.ipynb`) is the standard format, notebooks from cloud platforms that use Jupyter are fully supported with metadata preservation:
|
|
118
143
|
|
|
119
|
-
|
|
144
|
+
| Platform | Status | Notes |
|
|
145
|
+
| ---------------------- | ------------------- | -------------------------------------------------- |
|
|
146
|
+
| **Google Colab** | ✅ Fully compatible | Preserves Colab cell IDs, form cells, GPU settings |
|
|
147
|
+
| **Amazon SageMaker** | ✅ Fully compatible | Preserves tags, training/inference markers |
|
|
148
|
+
| **Kaggle** | ✅ Fully compatible | Preserves UUIDs, cell GUIDs, hide input/output |
|
|
149
|
+
| **Azure ML Notebooks** | ✅ Fully compatible | Standard Jupyter with Azure metadata |
|
|
150
|
+
| **JupyterLab/Hub** | ✅ Fully compatible | Standard Jupyter format |
|
|
151
|
+
|
|
152
|
+
Platform-specific cell metadata is preserved during roundtrip conversion, allowing notebooks to be edited in Deepnote and exported back to the original platform without losing settings.
|
|
153
|
+
|
|
154
|
+
## Programmatic Usage
|
|
155
|
+
|
|
156
|
+
### Convert to Deepnote
|
|
120
157
|
|
|
121
158
|
```typescript
|
|
122
|
-
import {
|
|
159
|
+
import {
|
|
160
|
+
convertIpynbFilesToDeepnoteFile,
|
|
161
|
+
convertQuartoFilesToDeepnoteFile,
|
|
162
|
+
convertPercentFilesToDeepnoteFile,
|
|
163
|
+
convertMarimoFilesToDeepnoteFile,
|
|
164
|
+
} from "@deepnote/convert";
|
|
165
|
+
|
|
166
|
+
// From Jupyter
|
|
167
|
+
await convertIpynbFilesToDeepnoteFile(["notebook.ipynb"], {
|
|
168
|
+
outputPath: "output.deepnote",
|
|
169
|
+
projectName: "My Project",
|
|
170
|
+
});
|
|
123
171
|
|
|
124
|
-
|
|
172
|
+
// From Quarto
|
|
173
|
+
await convertQuartoFilesToDeepnoteFile(["document.qmd"], {
|
|
125
174
|
outputPath: "output.deepnote",
|
|
126
175
|
projectName: "My Project",
|
|
127
176
|
});
|
|
128
|
-
```
|
|
129
177
|
|
|
130
|
-
|
|
178
|
+
// From Percent
|
|
179
|
+
await convertPercentFilesToDeepnoteFile(["script.py"], {
|
|
180
|
+
outputPath: "output.deepnote",
|
|
181
|
+
projectName: "My Project",
|
|
182
|
+
});
|
|
131
183
|
|
|
132
|
-
|
|
184
|
+
// From Marimo
|
|
185
|
+
await convertMarimoFilesToDeepnoteFile(["notebook.py"], {
|
|
186
|
+
outputPath: "output.deepnote",
|
|
187
|
+
projectName: "My Project",
|
|
188
|
+
});
|
|
189
|
+
```
|
|
133
190
|
|
|
134
|
-
|
|
191
|
+
### Convert from Deepnote
|
|
135
192
|
|
|
136
193
|
```typescript
|
|
137
|
-
import {
|
|
138
|
-
|
|
139
|
-
|
|
194
|
+
import {
|
|
195
|
+
convertDeepnoteFileToJupyter,
|
|
196
|
+
convertDeepnoteFileToQuartoFiles,
|
|
197
|
+
convertDeepnoteFileToPercentFiles,
|
|
198
|
+
convertDeepnoteFileToMarimoFiles,
|
|
199
|
+
} from "@deepnote/convert";
|
|
200
|
+
|
|
201
|
+
// To Jupyter
|
|
202
|
+
await convertDeepnoteFileToJupyter("project.deepnote", {
|
|
140
203
|
outputDir: "./jupyter-notebooks",
|
|
141
204
|
});
|
|
205
|
+
|
|
206
|
+
// To Quarto
|
|
207
|
+
await convertDeepnoteFileToQuartoFiles("project.deepnote", {
|
|
208
|
+
outputDir: "./quarto-docs",
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
// To Percent
|
|
212
|
+
await convertDeepnoteFileToPercentFiles("project.deepnote", {
|
|
213
|
+
outputDir: "./percent-scripts",
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
// To Marimo
|
|
217
|
+
await convertDeepnoteFileToMarimoFiles("project.deepnote", {
|
|
218
|
+
outputDir: "./marimo-notebooks",
|
|
219
|
+
});
|
|
142
220
|
```
|
|
143
221
|
|
|
144
|
-
|
|
222
|
+
### Pure Conversion (No File I/O)
|
|
145
223
|
|
|
146
224
|
For programmatic use with in-memory data:
|
|
147
225
|
|
|
148
226
|
```typescript
|
|
149
227
|
import fs from "node:fs/promises";
|
|
150
228
|
import { deserializeDeepnoteFile } from "@deepnote/blocks";
|
|
151
|
-
import {
|
|
229
|
+
import {
|
|
230
|
+
convertDeepnoteToJupyterNotebooks,
|
|
231
|
+
convertDeepnoteToQuartoDocuments,
|
|
232
|
+
convertDeepnoteToPercentNotebooks,
|
|
233
|
+
convertDeepnoteToMarimoApps,
|
|
234
|
+
} from "@deepnote/convert";
|
|
152
235
|
|
|
153
236
|
// Read and deserialize the Deepnote file
|
|
154
237
|
const yamlContent = await fs.readFile("project.deepnote", "utf-8");
|
|
155
238
|
const deepnoteFile = deserializeDeepnoteFile(yamlContent);
|
|
156
239
|
|
|
157
|
-
// Convert to
|
|
158
|
-
const
|
|
240
|
+
// Convert to any format (pure functions, no I/O)
|
|
241
|
+
const jupyterNotebooks = convertDeepnoteToJupyterNotebooks(deepnoteFile);
|
|
242
|
+
const quartoDocuments = convertDeepnoteToQuartoDocuments(deepnoteFile);
|
|
243
|
+
const percentNotebooks = convertDeepnoteToPercentNotebooks(deepnoteFile);
|
|
244
|
+
const marimoApps = convertDeepnoteToMarimoApps(deepnoteFile);
|
|
159
245
|
|
|
160
|
-
//
|
|
161
|
-
for (const { filename, notebook } of
|
|
246
|
+
// Work with the results in memory
|
|
247
|
+
for (const { filename, notebook } of jupyterNotebooks) {
|
|
162
248
|
console.log(`${filename}: ${notebook.cells.length} cells`);
|
|
163
|
-
|
|
164
|
-
// Or save them yourself
|
|
165
|
-
await fs.writeFile(filename, JSON.stringify(notebook, null, 2));
|
|
166
249
|
}
|
|
167
250
|
```
|
|
168
251
|
|
package/dist/bin.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { a as convertDeepnoteFileToJupyterFiles,
|
|
2
|
+
import { T as convertDeepnoteFileToMarimoFiles, _ as convertDeepnoteFileToQuartoFiles, a as convertPercentFilesToDeepnoteFile, d as convertMarimoFilesToDeepnoteFile, k as convertDeepnoteFileToJupyterFiles, p as convertIpynbFilesToDeepnoteFile, r as convertQuartoFilesToDeepnoteFile, x as convertDeepnoteFileToPercentFiles } from "./src-CUESP0m8.js";
|
|
3
3
|
import { cli } from "cleye";
|
|
4
4
|
import fs from "node:fs/promises";
|
|
5
5
|
import { basename, extname, resolve } from "node:path";
|
|
@@ -8,7 +8,7 @@ import ora from "ora";
|
|
|
8
8
|
|
|
9
9
|
//#region src/cli.ts
|
|
10
10
|
async function convert(options) {
|
|
11
|
-
const { inputPath, projectName: customProjectName, outputPath: customOutputPath, cwd = process.cwd() } = options;
|
|
11
|
+
const { inputPath, projectName: customProjectName, outputPath: customOutputPath, cwd = process.cwd(), outputFormat = "jupyter" } = options;
|
|
12
12
|
const resolveProjectName = (possibleName) => {
|
|
13
13
|
if (customProjectName) return customProjectName;
|
|
14
14
|
if (possibleName) return possibleName;
|
|
@@ -24,56 +24,165 @@ async function convert(options) {
|
|
|
24
24
|
};
|
|
25
25
|
const absolutePath = resolve(cwd, inputPath);
|
|
26
26
|
if ((await fs.stat(absolutePath)).isDirectory()) {
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
27
|
+
const entries = await fs.readdir(absolutePath, { withFileTypes: true });
|
|
28
|
+
const ipynbFiles = entries.filter((entry) => entry.isFile() && entry.name.toLowerCase().endsWith(".ipynb")).map((entry) => entry.name).sort((a, b) => a.localeCompare(b));
|
|
29
|
+
const quartoFiles = entries.filter((entry) => entry.isFile() && entry.name.toLowerCase().endsWith(".qmd")).map((entry) => entry.name).sort((a, b) => a.localeCompare(b));
|
|
30
|
+
const pyFiles = entries.filter((entry) => entry.isFile() && entry.name.toLowerCase().endsWith(".py")).map((entry) => entry.name).sort((a, b) => a.localeCompare(b));
|
|
31
|
+
const classifyPyFile = async (pyFile) => {
|
|
32
|
+
const content = await fs.readFile(resolve(absolutePath, pyFile), "utf-8");
|
|
33
|
+
if (isMarimoContent(content)) return {
|
|
34
|
+
file: pyFile,
|
|
35
|
+
type: "marimo"
|
|
36
|
+
};
|
|
37
|
+
if (isPercentContent(content)) return {
|
|
38
|
+
file: pyFile,
|
|
39
|
+
type: "percent"
|
|
40
|
+
};
|
|
41
|
+
return {
|
|
42
|
+
file: pyFile,
|
|
43
|
+
type: "unknown"
|
|
44
|
+
};
|
|
45
|
+
};
|
|
46
|
+
const CONCURRENCY_LIMIT = 10;
|
|
47
|
+
const results = [];
|
|
48
|
+
for (let i = 0; i < pyFiles.length; i += CONCURRENCY_LIMIT) {
|
|
49
|
+
const batch = pyFiles.slice(i, i + CONCURRENCY_LIMIT);
|
|
50
|
+
const batchResults = await Promise.all(batch.map(classifyPyFile));
|
|
51
|
+
results.push(...batchResults);
|
|
43
52
|
}
|
|
53
|
+
const marimoFiles = results.filter((r) => r.type === "marimo").map((r) => r.file);
|
|
54
|
+
const percentFiles = results.filter((r) => r.type === "percent").map((r) => r.file);
|
|
55
|
+
if (ipynbFiles.length > 0) return convertDirectory(absolutePath, ipynbFiles, "jupyter", resolveProjectName, resolveOutputPath);
|
|
56
|
+
if (quartoFiles.length > 0) return convertDirectory(absolutePath, quartoFiles, "quarto", resolveProjectName, resolveOutputPath);
|
|
57
|
+
if (marimoFiles.length > 0) return convertDirectory(absolutePath, marimoFiles, "marimo", resolveProjectName, resolveOutputPath);
|
|
58
|
+
if (percentFiles.length > 0) return convertDirectory(absolutePath, percentFiles, "percent", resolveProjectName, resolveOutputPath);
|
|
59
|
+
throw new Error("No supported notebook files found in the specified directory (.ipynb, .qmd, .py)");
|
|
44
60
|
}
|
|
45
61
|
const ext = extname(absolutePath).toLowerCase();
|
|
46
|
-
if (ext === ".ipynb")
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
62
|
+
if (ext === ".ipynb") return convertJupyterToDeepnote(absolutePath, resolveProjectName, resolveOutputPath);
|
|
63
|
+
if (ext === ".qmd") return convertQuartoToDeepnote(absolutePath, resolveProjectName, resolveOutputPath);
|
|
64
|
+
if (ext === ".py") {
|
|
65
|
+
const content = await fs.readFile(absolutePath, "utf-8");
|
|
66
|
+
if (isMarimoContent(content)) return convertMarimoToDeepnote(absolutePath, resolveProjectName, resolveOutputPath);
|
|
67
|
+
if (isPercentContent(content)) return convertPercentToDeepnote(absolutePath, resolveProjectName, resolveOutputPath);
|
|
68
|
+
throw new Error("Unsupported Python file format. File must be a percent format (# %% markers) or Marimo notebook (@app.cell decorators).");
|
|
69
|
+
}
|
|
70
|
+
if (ext === ".deepnote") return convertDeepnoteToFormat(absolutePath, outputFormat, customOutputPath, cwd);
|
|
71
|
+
throw new Error("Unsupported file type. Please provide a .ipynb, .qmd, .py (percent/marimo), or .deepnote file.");
|
|
72
|
+
}
|
|
73
|
+
async function convertDirectory(dirPath, files, format, resolveProjectName, resolveOutputPath) {
|
|
74
|
+
const spinner = ora(`Converting ${{
|
|
75
|
+
jupyter: "Jupyter Notebooks",
|
|
76
|
+
quarto: "Quarto documents",
|
|
77
|
+
percent: "percent format notebooks",
|
|
78
|
+
marimo: "Marimo notebooks"
|
|
79
|
+
}[format]} to a Deepnote project...`).start();
|
|
80
|
+
try {
|
|
81
|
+
const filenameWithoutExtension = basename(dirPath);
|
|
82
|
+
const projectName = resolveProjectName(filenameWithoutExtension);
|
|
83
|
+
const outputPath = await resolveOutputPath(`${filenameWithoutExtension}.deepnote`);
|
|
84
|
+
const inputFilePaths = files.map((file) => resolve(dirPath, file));
|
|
85
|
+
switch (format) {
|
|
86
|
+
case "jupyter":
|
|
87
|
+
await convertIpynbFilesToDeepnoteFile(inputFilePaths, {
|
|
88
|
+
projectName,
|
|
89
|
+
outputPath
|
|
90
|
+
});
|
|
91
|
+
break;
|
|
92
|
+
case "quarto":
|
|
93
|
+
await convertQuartoFilesToDeepnoteFile(inputFilePaths, {
|
|
94
|
+
projectName,
|
|
95
|
+
outputPath
|
|
96
|
+
});
|
|
97
|
+
break;
|
|
98
|
+
case "percent":
|
|
99
|
+
await convertPercentFilesToDeepnoteFile(inputFilePaths, {
|
|
100
|
+
projectName,
|
|
101
|
+
outputPath
|
|
102
|
+
});
|
|
103
|
+
break;
|
|
104
|
+
case "marimo":
|
|
105
|
+
await convertMarimoFilesToDeepnoteFile(inputFilePaths, {
|
|
106
|
+
projectName,
|
|
107
|
+
outputPath
|
|
108
|
+
});
|
|
109
|
+
break;
|
|
61
110
|
}
|
|
111
|
+
spinner.succeed(`The Deepnote project has been saved to ${chalk.bold(outputPath)}`);
|
|
112
|
+
return outputPath;
|
|
113
|
+
} catch (error) {
|
|
114
|
+
spinner.fail("Conversion failed");
|
|
115
|
+
throw error;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
async function convertSingleFileToDeepnote(absolutePath, formatName, converter, resolveProjectName, resolveOutputPath) {
|
|
119
|
+
const spinner = ora(`Converting the ${formatName} to a Deepnote project...`).start();
|
|
120
|
+
try {
|
|
121
|
+
const filenameWithoutExtension = basename(absolutePath, extname(absolutePath));
|
|
122
|
+
const projectName = resolveProjectName(filenameWithoutExtension);
|
|
123
|
+
const outputPath = await resolveOutputPath(`${filenameWithoutExtension}.deepnote`);
|
|
124
|
+
await converter([absolutePath], {
|
|
125
|
+
projectName,
|
|
126
|
+
outputPath
|
|
127
|
+
});
|
|
128
|
+
spinner.succeed(`The Deepnote project has been saved to ${chalk.bold(outputPath)}`);
|
|
129
|
+
return outputPath;
|
|
130
|
+
} catch (error) {
|
|
131
|
+
spinner.fail("Conversion failed");
|
|
132
|
+
throw error;
|
|
62
133
|
}
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
134
|
+
}
|
|
135
|
+
function convertJupyterToDeepnote(absolutePath, resolveProjectName, resolveOutputPath) {
|
|
136
|
+
return convertSingleFileToDeepnote(absolutePath, "Jupyter Notebook", convertIpynbFilesToDeepnoteFile, resolveProjectName, resolveOutputPath);
|
|
137
|
+
}
|
|
138
|
+
function convertQuartoToDeepnote(absolutePath, resolveProjectName, resolveOutputPath) {
|
|
139
|
+
return convertSingleFileToDeepnote(absolutePath, "Quarto document", convertQuartoFilesToDeepnoteFile, resolveProjectName, resolveOutputPath);
|
|
140
|
+
}
|
|
141
|
+
function convertPercentToDeepnote(absolutePath, resolveProjectName, resolveOutputPath) {
|
|
142
|
+
return convertSingleFileToDeepnote(absolutePath, "percent format notebook", convertPercentFilesToDeepnoteFile, resolveProjectName, resolveOutputPath);
|
|
143
|
+
}
|
|
144
|
+
function convertMarimoToDeepnote(absolutePath, resolveProjectName, resolveOutputPath) {
|
|
145
|
+
return convertSingleFileToDeepnote(absolutePath, "Marimo notebook", convertMarimoFilesToDeepnoteFile, resolveProjectName, resolveOutputPath);
|
|
146
|
+
}
|
|
147
|
+
async function convertDeepnoteToFormat(absolutePath, outputFormat, customOutputPath, cwd) {
|
|
148
|
+
const formatNames = {
|
|
149
|
+
jupyter: "Jupyter Notebooks",
|
|
150
|
+
percent: "percent format files",
|
|
151
|
+
quarto: "Quarto documents",
|
|
152
|
+
marimo: "Marimo notebooks"
|
|
153
|
+
};
|
|
154
|
+
const spinner = ora(`Converting Deepnote project to ${formatNames[outputFormat]}...`).start();
|
|
155
|
+
try {
|
|
156
|
+
const outputDirName = basename(absolutePath, extname(absolutePath));
|
|
157
|
+
const outputDir = customOutputPath ? resolve(cwd, customOutputPath) : resolve(cwd, outputDirName);
|
|
158
|
+
switch (outputFormat) {
|
|
159
|
+
case "jupyter":
|
|
160
|
+
await convertDeepnoteFileToJupyterFiles(absolutePath, { outputDir });
|
|
161
|
+
break;
|
|
162
|
+
case "percent":
|
|
163
|
+
await convertDeepnoteFileToPercentFiles(absolutePath, { outputDir });
|
|
164
|
+
break;
|
|
165
|
+
case "quarto":
|
|
166
|
+
await convertDeepnoteFileToQuartoFiles(absolutePath, { outputDir });
|
|
167
|
+
break;
|
|
168
|
+
case "marimo":
|
|
169
|
+
await convertDeepnoteFileToMarimoFiles(absolutePath, { outputDir });
|
|
170
|
+
break;
|
|
74
171
|
}
|
|
172
|
+
spinner.succeed(`${formatNames[outputFormat]} have been saved to ${chalk.bold(outputDir)}`);
|
|
173
|
+
return outputDir;
|
|
174
|
+
} catch (error) {
|
|
175
|
+
spinner.fail("Conversion failed");
|
|
176
|
+
throw error;
|
|
75
177
|
}
|
|
76
|
-
|
|
178
|
+
}
|
|
179
|
+
/** Check if file content is Marimo format */
|
|
180
|
+
function isMarimoContent(content) {
|
|
181
|
+
return /^import marimo\b/m.test(content) && /@app\.cell\b/.test(content) && !/^\s*['"]{3}[\s\S]*?import marimo/m.test(content);
|
|
182
|
+
}
|
|
183
|
+
/** Check if file content is percent format */
|
|
184
|
+
function isPercentContent(content) {
|
|
185
|
+
return /^# %%/m.test(content) && !/^\s*['"]{3}[\s\S]*?# %%/m.test(content);
|
|
77
186
|
}
|
|
78
187
|
|
|
79
188
|
//#endregion
|