@deepnote/convert 1.2.0 → 1.2.1

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 CHANGED
@@ -5,7 +5,7 @@ Convert Jupyter Notebook files (`.ipynb`) to Deepnote project files (`.deepnote`
5
5
  ## Installation
6
6
 
7
7
  ```bash
8
- npm install @deepnote/convert
8
+ npm install -g @deepnote/convert
9
9
  ```
10
10
 
11
11
  ## CLI Usage
@@ -78,17 +78,14 @@ You can also use the conversion function programmatically in your Node.js or Typ
78
78
  ### Basic Usage
79
79
 
80
80
  ```typescript
81
- import { convertIpynbFilesToDeepnoteFile } from '@deepnote/convert'
81
+ import { convertIpynbFilesToDeepnoteFile } from "@deepnote/convert";
82
82
 
83
- await convertIpynbFilesToDeepnoteFile(
84
- ['path/to/notebook.ipynb'],
85
- {
86
- outputPath: 'output.deepnote',
87
- projectName: 'My Project'
88
- }
89
- )
83
+ await convertIpynbFilesToDeepnoteFile(["path/to/notebook.ipynb"], {
84
+ outputPath: "output.deepnote",
85
+ projectName: "My Project",
86
+ });
87
+ ```
90
88
 
91
89
  ## License
92
90
 
93
91
  Apache-2.0
94
- ```
package/dist/bin.d.ts ADDED
@@ -0,0 +1 @@
1
+ export { };
package/dist/bin.js ADDED
@@ -0,0 +1,102 @@
1
+ #!/usr/bin/env node
2
+ import { t as convertIpynbFilesToDeepnoteFile } from "./src-n1bYyJ74.js";
3
+ import { cli } from "cleye";
4
+ import fs from "node:fs/promises";
5
+ import { basename, extname, resolve } from "node:path";
6
+ import chalk from "chalk";
7
+ import ora from "ora";
8
+
9
+ //#region src/cli.ts
10
+ async function convert(options) {
11
+ const { inputPath, projectName: customProjectName, outputPath: customOutputPath, cwd = process.cwd() } = options;
12
+ const resolveProjectName = (possibleName) => {
13
+ if (customProjectName) return customProjectName;
14
+ if (possibleName) return possibleName;
15
+ return "Untitled project";
16
+ };
17
+ const resolveOutputPath = async (outputFilename) => {
18
+ if (customOutputPath) {
19
+ const absoluteOutputPath = resolve(cwd, customOutputPath);
20
+ if ((await fs.stat(absoluteOutputPath).catch(() => null))?.isDirectory()) return resolve(absoluteOutputPath, outputFilename);
21
+ return absoluteOutputPath;
22
+ }
23
+ return resolve(cwd, outputFilename);
24
+ };
25
+ const absolutePath = resolve(cwd, inputPath);
26
+ if ((await fs.stat(absolutePath)).isDirectory()) {
27
+ const ipynbFiles = (await fs.readdir(absolutePath, { withFileTypes: true })).filter((entry) => entry.isFile() && entry.name.toLowerCase().endsWith(".ipynb")).map((entry) => entry.name).sort((a, b) => a.localeCompare(b));
28
+ if (ipynbFiles.length === 0) throw new Error("No .ipynb files found in the specified directory.");
29
+ const spinner = ora("Converting Jupyter Notebooks to a Deepnote project...").start();
30
+ try {
31
+ const filenameWithoutExtension = basename(absolutePath);
32
+ const projectName = resolveProjectName(filenameWithoutExtension);
33
+ const outputPath = await resolveOutputPath(`${filenameWithoutExtension}.deepnote`);
34
+ await convertIpynbFilesToDeepnoteFile(ipynbFiles.map((file) => resolve(absolutePath, file)), {
35
+ projectName,
36
+ outputPath
37
+ });
38
+ spinner.succeed(`The Deepnote project has been saved to ${chalk.bold(outputPath)}`);
39
+ return outputPath;
40
+ } catch (error) {
41
+ spinner.fail("Conversion failed");
42
+ throw error;
43
+ }
44
+ }
45
+ const ext = extname(absolutePath).toLowerCase();
46
+ if (ext === ".ipynb") {
47
+ const spinner = ora("Converting the Jupyter Notebook to a Deepnote project...").start();
48
+ try {
49
+ const filenameWithoutExtension = basename(absolutePath, ext);
50
+ const projectName = resolveProjectName(filenameWithoutExtension);
51
+ const outputPath = await resolveOutputPath(`${filenameWithoutExtension}.deepnote`);
52
+ await convertIpynbFilesToDeepnoteFile([absolutePath], {
53
+ projectName,
54
+ outputPath
55
+ });
56
+ spinner.succeed(`The Deepnote project has been saved to ${chalk.bold(outputPath)}`);
57
+ return outputPath;
58
+ } catch (error) {
59
+ spinner.fail("Conversion failed");
60
+ throw error;
61
+ }
62
+ }
63
+ if (ext === ".deepnote") throw new Error("The .deepnote format is not supported for conversion yet.");
64
+ throw new Error("Unsupported file type. Please provide a .ipynb or .deepnote file.");
65
+ }
66
+
67
+ //#endregion
68
+ //#region src/bin.ts
69
+ async function main() {
70
+ const argv = cli({
71
+ name: "deepnote-convert",
72
+ parameters: ["<path>"],
73
+ flags: {
74
+ projectName: {
75
+ description: "The name of the Deepnote project.",
76
+ type: String
77
+ },
78
+ outputPath: {
79
+ alias: "o",
80
+ description: "The path where the .deepnote file will be saved.",
81
+ type: String
82
+ },
83
+ cwd: {
84
+ description: "The working directory to resolve paths relative to.",
85
+ type: String
86
+ }
87
+ }
88
+ });
89
+ await convert({
90
+ inputPath: argv._.path,
91
+ projectName: argv.flags.projectName,
92
+ outputPath: argv.flags.outputPath,
93
+ cwd: argv.flags.cwd ?? process.cwd()
94
+ });
95
+ }
96
+ if (process.env.NODE_ENV !== "test" && process.env.VITEST !== "true") main().catch((error) => {
97
+ console.error(error);
98
+ process.exit(1);
99
+ });
100
+
101
+ //#endregion
102
+ export { };
package/dist/index.js CHANGED
@@ -1,87 +1,3 @@
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";
1
+ import { t as convertIpynbFilesToDeepnoteFile } from "./src-n1bYyJ74.js";
5
2
 
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
3
  export { convertIpynbFilesToDeepnoteFile };
@@ -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 as t };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deepnote/convert",
3
- "version": "1.2.0",
3
+ "version": "1.2.1",
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.5"
36
+ "@deepnote/blocks": "1.3.6"
37
37
  },
38
38
  "devDependencies": {
39
39
  "@types/node": "^22.0.0",
@@ -48,8 +48,8 @@
48
48
  "registry": "https://registry.npmjs.org"
49
49
  },
50
50
  "scripts": {
51
- "build": "tsdown --format esm --dts",
51
+ "build": "tsdown --format esm --dts src/bin.ts src/index.ts",
52
52
  "test": "vitest",
53
- "watch": "tsdown --watch --format esm --dts"
53
+ "watch": "tsdown --watch --format esm --dts src/bin.ts src/index.ts"
54
54
  }
55
55
  }