@deepnote/convert 1.1.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 +94 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +87 -0
- package/package.json +55 -0
package/README.md
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# @deepnote/convert
|
|
2
|
+
|
|
3
|
+
Convert Jupyter Notebook files (`.ipynb`) to Deepnote project files (`.deepnote`).
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @deepnote/convert
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## CLI Usage
|
|
12
|
+
|
|
13
|
+
The package provides a `deepnote-convert` command-line tool for converting Jupyter notebooks to Deepnote format.
|
|
14
|
+
|
|
15
|
+
### Convert a Single Notebook
|
|
16
|
+
|
|
17
|
+
Convert a single `.ipynb` file to a `.deepnote` file:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
deepnote-convert path/to/notebook.ipynb
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
This will create a `notebook.deepnote` file in the current directory.
|
|
24
|
+
|
|
25
|
+
### Convert a Directory of Notebooks
|
|
26
|
+
|
|
27
|
+
Convert all `.ipynb` files in a directory to a single `.deepnote` project:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
deepnote-convert path/to/notebooks/
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
This will create a `notebooks.deepnote` file in the current directory containing all notebooks from the directory.
|
|
34
|
+
|
|
35
|
+
### Options
|
|
36
|
+
|
|
37
|
+
#### `--projectName <name>`
|
|
38
|
+
|
|
39
|
+
Set a custom name for the Deepnote project:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
deepnote-convert notebook.ipynb --projectName "My Analysis"
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
If not specified, the project name will default to the filename (without extension) or directory name.
|
|
46
|
+
|
|
47
|
+
#### `-o, --outputPath <path>`
|
|
48
|
+
|
|
49
|
+
Specify where to save the output `.deepnote` file:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
# Save to a specific file
|
|
53
|
+
deepnote-convert notebook.ipynb -o output/project.deepnote
|
|
54
|
+
|
|
55
|
+
# Save to a directory (filename will be auto-generated)
|
|
56
|
+
deepnote-convert notebook.ipynb -o output/
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
If not specified, the output file will be saved in the current directory.
|
|
60
|
+
|
|
61
|
+
### Examples
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
# Convert a single notebook with custom name
|
|
65
|
+
deepnote-convert titanic.ipynb --projectName "Titanic Analysis"
|
|
66
|
+
|
|
67
|
+
# Convert all notebooks in a directory
|
|
68
|
+
deepnote-convert ./analysis --projectName "Data Science Project" -o ./output
|
|
69
|
+
|
|
70
|
+
# Convert multiple notebooks from a folder
|
|
71
|
+
deepnote-convert ~/notebooks/ml-experiments -o ~/projects/
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Programmatic Usage
|
|
75
|
+
|
|
76
|
+
You can also use the conversion function programmatically in your Node.js or TypeScript applications.
|
|
77
|
+
|
|
78
|
+
### Basic Usage
|
|
79
|
+
|
|
80
|
+
```typescript
|
|
81
|
+
import { convertIpynbFilesToDeepnoteFile } from '@deepnote/convert'
|
|
82
|
+
|
|
83
|
+
await convertIpynbFilesToDeepnoteFile(
|
|
84
|
+
['path/to/notebook.ipynb'],
|
|
85
|
+
{
|
|
86
|
+
outputPath: 'output.deepnote',
|
|
87
|
+
projectName: 'My Project'
|
|
88
|
+
}
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
## License
|
|
92
|
+
|
|
93
|
+
Apache-2.0
|
|
94
|
+
```
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
//#region src/jupyter-to-deepnote.d.ts
|
|
2
|
+
interface ConvertIpynbFilesToDeepnoteFileOptions {
|
|
3
|
+
outputPath: string;
|
|
4
|
+
projectName: string;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Converts multiple Jupyter Notebook (.ipynb) files into a single Deepnote project file.
|
|
8
|
+
*/
|
|
9
|
+
declare function convertIpynbFilesToDeepnoteFile(inputFilePaths: string[], options: ConvertIpynbFilesToDeepnoteFileOptions): Promise<void>;
|
|
10
|
+
//#endregion
|
|
11
|
+
export { type ConvertIpynbFilesToDeepnoteFileOptions, convertIpynbFilesToDeepnoteFile };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import { basename, dirname, extname } from "node:path";
|
|
3
|
+
import { v4 } from "uuid";
|
|
4
|
+
import { stringify } from "yaml";
|
|
5
|
+
|
|
6
|
+
//#region src/jupyter-to-deepnote.ts
|
|
7
|
+
/**
|
|
8
|
+
* Converts multiple Jupyter Notebook (.ipynb) files into a single Deepnote project file.
|
|
9
|
+
*/
|
|
10
|
+
async function convertIpynbFilesToDeepnoteFile(inputFilePaths, options) {
|
|
11
|
+
const deepnoteFile = {
|
|
12
|
+
metadata: { createdAt: (/* @__PURE__ */ new Date()).toISOString() },
|
|
13
|
+
project: {
|
|
14
|
+
id: v4(),
|
|
15
|
+
initNotebookId: void 0,
|
|
16
|
+
integrations: [],
|
|
17
|
+
name: options.projectName,
|
|
18
|
+
notebooks: [],
|
|
19
|
+
settings: {}
|
|
20
|
+
},
|
|
21
|
+
version: "1.0.0"
|
|
22
|
+
};
|
|
23
|
+
for (const filePath of inputFilePaths) {
|
|
24
|
+
const name = basename(filePath, extname(filePath)) || "Untitled notebook";
|
|
25
|
+
const blocks = (await parseIpynbFile(filePath)).cells.map((cell, index) => {
|
|
26
|
+
const source = Array.isArray(cell.source) ? cell.source.join("") : cell.source;
|
|
27
|
+
return {
|
|
28
|
+
blockGroup: v4(),
|
|
29
|
+
content: source,
|
|
30
|
+
executionCount: cell.execution_count ?? void 0,
|
|
31
|
+
id: v4(),
|
|
32
|
+
metadata: {},
|
|
33
|
+
outputs: cell.cell_type === "code" ? cell.outputs : void 0,
|
|
34
|
+
sortingKey: createSortingKey(index),
|
|
35
|
+
type: cell.cell_type === "code" ? "code" : "markdown",
|
|
36
|
+
version: 1
|
|
37
|
+
};
|
|
38
|
+
});
|
|
39
|
+
deepnoteFile.project.notebooks.push({
|
|
40
|
+
blocks,
|
|
41
|
+
executionMode: "block",
|
|
42
|
+
id: v4(),
|
|
43
|
+
isModule: false,
|
|
44
|
+
name,
|
|
45
|
+
workingDirectory: void 0
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
const yamlContent = stringify(deepnoteFile);
|
|
49
|
+
const parentDir = dirname(options.outputPath);
|
|
50
|
+
await fs.mkdir(parentDir, { recursive: true });
|
|
51
|
+
await fs.writeFile(options.outputPath, yamlContent, "utf-8");
|
|
52
|
+
}
|
|
53
|
+
async function parseIpynbFile(filePath) {
|
|
54
|
+
let ipynbJson;
|
|
55
|
+
try {
|
|
56
|
+
ipynbJson = await fs.readFile(filePath, "utf-8");
|
|
57
|
+
} catch (error) {
|
|
58
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
59
|
+
throw new Error(`Failed to read ${filePath}: ${message}`);
|
|
60
|
+
}
|
|
61
|
+
try {
|
|
62
|
+
return JSON.parse(ipynbJson);
|
|
63
|
+
} catch (error) {
|
|
64
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
65
|
+
throw new Error(`Failed to parse ${filePath}: invalid JSON - ${message}`);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
function createSortingKey(index) {
|
|
69
|
+
const maxLength = 6;
|
|
70
|
+
const chars = "0123456789abcdefghijklmnopqrstuvwxyz";
|
|
71
|
+
const base = 36;
|
|
72
|
+
if (index < 0) throw new Error("Index must be non-negative");
|
|
73
|
+
let result = "";
|
|
74
|
+
let num = index + 1;
|
|
75
|
+
let iterations = 0;
|
|
76
|
+
while (num > 0 && iterations < maxLength) {
|
|
77
|
+
num--;
|
|
78
|
+
result = chars[num % base] + result;
|
|
79
|
+
num = Math.floor(num / base);
|
|
80
|
+
iterations++;
|
|
81
|
+
}
|
|
82
|
+
if (num > 0) throw new Error(`Index ${index} exceeds maximum key length of ${maxLength}`);
|
|
83
|
+
return result;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
//#endregion
|
|
87
|
+
export { convertIpynbFilesToDeepnoteFile };
|
package/package.json
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@deepnote/convert",
|
|
3
|
+
"version": "1.1.0",
|
|
4
|
+
"description": "",
|
|
5
|
+
"keywords": [],
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "https://github.com/deepnote/deepnote.git",
|
|
9
|
+
"directory": "packages/convert"
|
|
10
|
+
},
|
|
11
|
+
"license": "Apache-2.0",
|
|
12
|
+
"type": "module",
|
|
13
|
+
"exports": {
|
|
14
|
+
".": {
|
|
15
|
+
"import": "./dist/index.js",
|
|
16
|
+
"types": "./dist/index.d.ts"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"main": "./dist/index.js",
|
|
20
|
+
"types": "./dist/index.d.ts",
|
|
21
|
+
"bin": {
|
|
22
|
+
"deepnote-convert": "./dist/bin.js"
|
|
23
|
+
},
|
|
24
|
+
"files": [
|
|
25
|
+
"dist",
|
|
26
|
+
"README.md",
|
|
27
|
+
"LICENSE",
|
|
28
|
+
"package.json"
|
|
29
|
+
],
|
|
30
|
+
"scripts": {
|
|
31
|
+
"build": "tsdown --format esm --dts",
|
|
32
|
+
"test": "vitest",
|
|
33
|
+
"watch": "tsdown --watch --format esm --dts"
|
|
34
|
+
},
|
|
35
|
+
"dependencies": {
|
|
36
|
+
"@deepnote/blocks": "workspace:*",
|
|
37
|
+
"chalk": "^5.6.2",
|
|
38
|
+
"cleye": "^1.3.4",
|
|
39
|
+
"ora": "^9.0.0",
|
|
40
|
+
"uuid": "^13.0.0",
|
|
41
|
+
"yaml": "^2.8.1"
|
|
42
|
+
},
|
|
43
|
+
"devDependencies": {
|
|
44
|
+
"@types/node": "^22.0.0",
|
|
45
|
+
"tsx": "^4.20.6",
|
|
46
|
+
"typescript": "^5.0.0"
|
|
47
|
+
},
|
|
48
|
+
"engines": {
|
|
49
|
+
"node": ">=18"
|
|
50
|
+
},
|
|
51
|
+
"publishConfig": {
|
|
52
|
+
"access": "public",
|
|
53
|
+
"registry": "https://registry.npmjs.org"
|
|
54
|
+
}
|
|
55
|
+
}
|