plainb 0.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Trung Le Duc
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,140 @@
1
+ # plainb
2
+
3
+ Convert plain-text notebook files to [Jupyter nbformat 4](https://nbformat.readthedocs.io/) JSON — in the browser or in Node.
4
+
5
+ ## Supported formats
6
+
7
+ | Format | Description |
8
+ | ------------------ | -------------------------------------------------------------------------------------------------------- |
9
+ | **`.py` percent** | VS Code / Spyder / Jupytext percent format — cells delimited by `# %%` |
10
+ | **`.md` classic** | Markdown file where fenced code blocks with a language tag become code cells |
11
+ | **`.md` MyST** | [MyST Notebook](https://myst-nb.readthedocs.io/) format — `{code-cell}` directives and `+++` cell breaks |
12
+ | **Sphinx Gallery** | [Sphinx-Gallery](https://sphinx-gallery.github.io/) `.py` scripts — RST docstring + `# %%` sections |
13
+
14
+ ## Install
15
+
16
+ ```sh
17
+ npm install plainb
18
+ ```
19
+
20
+ ## Usage
21
+
22
+ ```ts
23
+ import { parse, parsePy, parseMd, parseSphinxGallery } from "plainb";
24
+
25
+ // Auto-dispatch by format
26
+ const notebook = parse(source, "py"); // or 'md' | 'sphinx-gallery'
27
+
28
+ // Or call parsers directly
29
+ const nb1 = parsePy(source); // .py percent format
30
+ const nb2 = parseMd(source); // .md classic or MyST (auto-detected)
31
+ const nb3 = parseSphinxGallery(source); // Sphinx Gallery
32
+ ```
33
+
34
+ The returned object is a valid `nbformat.INotebookContent` (nbformat 4.5) — ready to save as `.ipynb` or pass to any Jupyter rendering library.
35
+
36
+ ## `.py` percent format
37
+
38
+ ```python
39
+ # %% [markdown]
40
+ # # Introduction
41
+ # Some **markdown** text.
42
+
43
+ # %%
44
+ import numpy as np
45
+ x = np.linspace(0, 1, 100)
46
+
47
+ # %% My cell name tags='["hide-input"]'
48
+ print(x)
49
+ ```
50
+
51
+ - Cell type: `[markdown]`, `[md]`, `[raw]`, or omitted (defaults to code)
52
+ - Cell name: free text after the type tag → stored in `metadata.name`
53
+ - Tags: `tags='["tag1","tag2"]'` → stored in `metadata.tags`
54
+ - Markdown content: line-comment prefix (`# `) or triple-quoted string (`"""`)
55
+
56
+ ## `.md` classic
57
+
58
+ ````markdown
59
+ # My Notebook
60
+
61
+ Some prose text becomes a **markdown cell**.
62
+
63
+ ```python
64
+ x = 1 + 1
65
+ ```
66
+
67
+ Only fenced blocks with a language identifier become code cells.
68
+ Untagged fences stay as markdown.
69
+ ````
70
+
71
+ ## `.md` MyST
72
+
73
+ ````markdown
74
+ ---
75
+ kernelspec: python3
76
+ ---
77
+
78
+ # Title
79
+
80
+ +++ {"tags": ["hide-input"]}
81
+
82
+ Markdown cell content.
83
+
84
+ ```{code-cell} ipython3
85
+ :tags: ["parameters"]
86
+ x = 42
87
+ ```
88
+ ````
89
+
90
+ Supports `{code-cell}`, `{raw-cell}`, `{markdown-cell}` directives and `+++` cell breaks.
91
+
92
+ ## Sphinx Gallery
93
+
94
+ ```python
95
+ """
96
+ ================
97
+ My Gallery Script
98
+ ================
99
+
100
+ Module docstring becomes the first markdown cell.
101
+ RST headings are converted to Markdown.
102
+ """
103
+
104
+ # Authors: ... ← stripped
105
+ # SPDX-... ← stripped
106
+
107
+ # %%
108
+ # Section heading
109
+ # ---------------
110
+ # Comment block becomes a markdown cell.
111
+
112
+ import numpy as np # code follows
113
+ ```
114
+
115
+ ## API
116
+
117
+ ```ts
118
+ parse(text: string, format: 'py' | 'md' | 'sphinx-gallery'): Notebook
119
+
120
+ parsePy(text: string): Notebook
121
+ parseMd(text: string): Notebook // auto-detects classic vs MyST
122
+ parseSphinxGallery(text: string): Notebook
123
+
124
+ // Types (re-exported from the package)
125
+ interface Notebook { nbformat: 4; nbformat_minor: 5; metadata: …; cells: Cell[] }
126
+ type Cell = CodeCell | MarkdownCell | RawCell
127
+ ```
128
+
129
+ ## Demo
130
+
131
+ ```sh
132
+ git clone https://github.com/trungleduc/plainb
133
+ cd plainb
134
+ npm install
135
+ npm run demo
136
+ ```
137
+
138
+ ## License
139
+
140
+ MIT
@@ -0,0 +1,7 @@
1
+ export { parsePy } from "./parsePy.js";
2
+ export { parseMd } from "./parseMd.js";
3
+ export { parseSphinxGallery } from "./parseSphinxGallery.js";
4
+ export type { Notebook, Cell, CodeCell, MarkdownCell, RawCell } from "./notebook.js";
5
+ import type { Notebook } from "./notebook.js";
6
+ export declare function parse(text: string, format: "py" | "md" | "sphinx-gallery"): Notebook;
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAC7D,YAAY,EAAE,QAAQ,EAAE,IAAI,EAAE,QAAQ,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAKrF,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAE9C,wBAAgB,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,GAAG,IAAI,GAAG,gBAAgB,GAAG,QAAQ,CAKpF"}
package/dist/index.js ADDED
@@ -0,0 +1,16 @@
1
+ export { parsePy } from "./parsePy.js";
2
+ export { parseMd } from "./parseMd.js";
3
+ export { parseSphinxGallery } from "./parseSphinxGallery.js";
4
+ import { parsePy } from "./parsePy.js";
5
+ import { parseMd } from "./parseMd.js";
6
+ import { parseSphinxGallery } from "./parseSphinxGallery.js";
7
+ export function parse(text, format) {
8
+ if (format === "py")
9
+ return parsePy(text);
10
+ if (format === "md")
11
+ return parseMd(text);
12
+ if (format === "sphinx-gallery")
13
+ return parseSphinxGallery(text);
14
+ throw new Error(`Unknown format: "${format}". Expected "py", "md", or "sphinx-gallery".`);
15
+ }
16
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAG7D,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAG7D,MAAM,UAAU,KAAK,CAAC,IAAY,EAAE,MAAsC;IACxE,IAAI,MAAM,KAAK,IAAI;QAAE,OAAO,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1C,IAAI,MAAM,KAAK,IAAI;QAAE,OAAO,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1C,IAAI,MAAM,KAAK,gBAAgB;QAAE,OAAO,kBAAkB,CAAC,IAAI,CAAC,CAAC;IACjE,MAAM,IAAI,KAAK,CAAC,oBAAoB,MAAM,8CAA8C,CAAC,CAAC;AAC5F,CAAC"}
@@ -0,0 +1,34 @@
1
+ export interface CodeCell {
2
+ cell_type: "code";
3
+ id: string;
4
+ metadata: Record<string, unknown>;
5
+ source: string[];
6
+ outputs: [];
7
+ execution_count: null;
8
+ }
9
+ export interface MarkdownCell {
10
+ cell_type: "markdown";
11
+ id: string;
12
+ metadata: Record<string, unknown>;
13
+ source: string[];
14
+ }
15
+ export interface RawCell {
16
+ cell_type: "raw";
17
+ id: string;
18
+ metadata: Record<string, unknown>;
19
+ source: string[];
20
+ }
21
+ export type Cell = CodeCell | MarkdownCell | RawCell;
22
+ export interface Notebook {
23
+ nbformat: 4;
24
+ nbformat_minor: 5;
25
+ metadata: Record<string, unknown>;
26
+ cells: Cell[];
27
+ }
28
+ /** Convert a raw string into nbformat source array (lines with \n except last). */
29
+ export declare function toSource(text: string): string[];
30
+ export declare function codeCell(source: string, metadata?: Record<string, unknown>, used?: Set<string>): CodeCell;
31
+ export declare function markdownCell(source: string, metadata?: Record<string, unknown>, used?: Set<string>): MarkdownCell;
32
+ export declare function rawCell(source: string, metadata?: Record<string, unknown>, used?: Set<string>): RawCell;
33
+ export declare function makeNotebook(cells: Cell[], metadata?: Record<string, unknown>): Notebook;
34
+ //# sourceMappingURL=notebook.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"notebook.d.ts","sourceRoot":"","sources":["../src/notebook.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,QAAQ;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,OAAO,EAAE,EAAE,CAAC;IACZ,eAAe,EAAE,IAAI,CAAC;CACvB;AAED,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,UAAU,CAAC;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED,MAAM,WAAW,OAAO;IACtB,SAAS,EAAE,KAAK,CAAC;IACjB,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED,MAAM,MAAM,IAAI,GAAG,QAAQ,GAAG,YAAY,GAAG,OAAO,CAAC;AAErD,MAAM,WAAW,QAAQ;IACvB,QAAQ,EAAE,CAAC,CAAC;IACZ,cAAc,EAAE,CAAC,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,KAAK,EAAE,IAAI,EAAE,CAAC;CACf;AAmBD,mFAAmF;AACnF,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAO/C;AAMD,wBAAgB,QAAQ,CACtB,MAAM,EAAE,MAAM,EACd,QAAQ,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,EACtC,IAAI,GAAE,GAAG,CAAC,MAAM,CAAa,GAC5B,QAAQ,CASV;AAED,wBAAgB,YAAY,CAC1B,MAAM,EAAE,MAAM,EACd,QAAQ,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,EACtC,IAAI,GAAE,GAAG,CAAC,MAAM,CAAa,GAC5B,YAAY,CAOd;AAED,wBAAgB,OAAO,CACrB,MAAM,EAAE,MAAM,EACd,QAAQ,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,EACtC,IAAI,GAAE,GAAG,CAAC,MAAM,CAAa,GAC5B,OAAO,CAOT;AAMD,wBAAgB,YAAY,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,QAAQ,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GAAG,QAAQ,CAE5F"}
@@ -0,0 +1,62 @@
1
+ // nbformat 4 types and factory helpers
2
+ // ---------------------------------------------------------------------------
3
+ // ID generation — no crypto, browser-safe
4
+ // ---------------------------------------------------------------------------
5
+ function makeId(used) {
6
+ let id;
7
+ do {
8
+ id = Math.random().toString(36).slice(2, 10).padEnd(8, "0");
9
+ } while (used.has(id));
10
+ used.add(id);
11
+ return id;
12
+ }
13
+ // ---------------------------------------------------------------------------
14
+ // Source normalization
15
+ // ---------------------------------------------------------------------------
16
+ /** Convert a raw string into nbformat source array (lines with \n except last). */
17
+ export function toSource(text) {
18
+ if (!text)
19
+ return [];
20
+ // Strip single trailing newline that separates cells (not part of content)
21
+ const stripped = text.endsWith("\n") ? text.slice(0, -1) : text;
22
+ if (!stripped)
23
+ return [];
24
+ const lines = stripped.split("\n");
25
+ return lines.map((line, i) => (i < lines.length - 1 ? line + "\n" : line));
26
+ }
27
+ // ---------------------------------------------------------------------------
28
+ // Cell factories
29
+ // ---------------------------------------------------------------------------
30
+ export function codeCell(source, metadata = {}, used = new Set()) {
31
+ return {
32
+ cell_type: "code",
33
+ id: makeId(used),
34
+ metadata,
35
+ source: toSource(source),
36
+ outputs: [],
37
+ execution_count: null,
38
+ };
39
+ }
40
+ export function markdownCell(source, metadata = {}, used = new Set()) {
41
+ return {
42
+ cell_type: "markdown",
43
+ id: makeId(used),
44
+ metadata,
45
+ source: toSource(source),
46
+ };
47
+ }
48
+ export function rawCell(source, metadata = {}, used = new Set()) {
49
+ return {
50
+ cell_type: "raw",
51
+ id: makeId(used),
52
+ metadata,
53
+ source: toSource(source),
54
+ };
55
+ }
56
+ // ---------------------------------------------------------------------------
57
+ // Notebook factory
58
+ // ---------------------------------------------------------------------------
59
+ export function makeNotebook(cells, metadata = {}) {
60
+ return { nbformat: 4, nbformat_minor: 5, metadata, cells };
61
+ }
62
+ //# sourceMappingURL=notebook.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"notebook.js","sourceRoot":"","sources":["../src/notebook.ts"],"names":[],"mappings":"AAAA,uCAAuC;AAkCvC,8EAA8E;AAC9E,0CAA0C;AAC1C,8EAA8E;AAE9E,SAAS,MAAM,CAAC,IAAiB;IAC/B,IAAI,EAAU,CAAC;IACf,GAAG,CAAC;QACF,EAAE,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IAC9D,CAAC,QAAQ,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE;IACvB,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IACb,OAAO,EAAE,CAAC;AACZ,CAAC;AAED,8EAA8E;AAC9E,uBAAuB;AACvB,8EAA8E;AAE9E,mFAAmF;AACnF,MAAM,UAAU,QAAQ,CAAC,IAAY;IACnC,IAAI,CAAC,IAAI;QAAE,OAAO,EAAE,CAAC;IACrB,2EAA2E;IAC3E,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAChE,IAAI,CAAC,QAAQ;QAAE,OAAO,EAAE,CAAC;IACzB,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACnC,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;AAC7E,CAAC;AAED,8EAA8E;AAC9E,iBAAiB;AACjB,8EAA8E;AAE9E,MAAM,UAAU,QAAQ,CACtB,MAAc,EACd,WAAoC,EAAE,EACtC,OAAoB,IAAI,GAAG,EAAE;IAE7B,OAAO;QACL,SAAS,EAAE,MAAM;QACjB,EAAE,EAAE,MAAM,CAAC,IAAI,CAAC;QAChB,QAAQ;QACR,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC;QACxB,OAAO,EAAE,EAAE;QACX,eAAe,EAAE,IAAI;KACtB,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,YAAY,CAC1B,MAAc,EACd,WAAoC,EAAE,EACtC,OAAoB,IAAI,GAAG,EAAE;IAE7B,OAAO;QACL,SAAS,EAAE,UAAU;QACrB,EAAE,EAAE,MAAM,CAAC,IAAI,CAAC;QAChB,QAAQ;QACR,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC;KACzB,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,OAAO,CACrB,MAAc,EACd,WAAoC,EAAE,EACtC,OAAoB,IAAI,GAAG,EAAE;IAE7B,OAAO;QACL,SAAS,EAAE,KAAK;QAChB,EAAE,EAAE,MAAM,CAAC,IAAI,CAAC;QAChB,QAAQ;QACR,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC;KACzB,CAAC;AACJ,CAAC;AAED,8EAA8E;AAC9E,mBAAmB;AACnB,8EAA8E;AAE9E,MAAM,UAAU,YAAY,CAAC,KAAa,EAAE,WAAoC,EAAE;IAChF,OAAO,EAAE,QAAQ,EAAE,CAAC,EAAE,cAAc,EAAE,CAAC,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;AAC7D,CAAC"}
@@ -0,0 +1,3 @@
1
+ import { type Notebook } from "./notebook.js";
2
+ export declare function parseClassicMd(text: string): Notebook;
3
+ //# sourceMappingURL=parseClassicMd.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parseClassicMd.d.ts","sourceRoot":"","sources":["../src/parseClassicMd.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmD,KAAK,QAAQ,EAAE,MAAM,eAAe,CAAC;AAgB/F,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,CA4CrD"}
@@ -0,0 +1,55 @@
1
+ import { codeCell, markdownCell, makeNotebook } from "./notebook.js";
2
+ // ---------------------------------------------------------------------------
3
+ // Classic markdown parser
4
+ // Two states: MARKDOWN | IN_FENCE
5
+ // Only language-tagged fences (```python, ```js, etc.) become code cells.
6
+ // Untagged fences stay as markdown text.
7
+ // ---------------------------------------------------------------------------
8
+ // Opening fence with a required language identifier
9
+ const OPENING_FENCE_RE = /^(`{3,})(\w[\w.-]*)\s*$/;
10
+ // Generic fence close (same or longer fence)
11
+ function isClosingFence(line, fence) {
12
+ return line.startsWith(fence) && line.slice(fence.length).trim() === "";
13
+ }
14
+ export function parseClassicMd(text) {
15
+ const lines = text.replace(/\r\n/g, "\n").split("\n");
16
+ const cells = [];
17
+ const used = new Set();
18
+ const mdLines = [];
19
+ function flushMarkdown() {
20
+ const trimmed = mdLines.join("\n").trim();
21
+ if (trimmed)
22
+ cells.push(markdownCell(trimmed, {}, used));
23
+ mdLines.length = 0;
24
+ }
25
+ let i = 0;
26
+ while (i < lines.length) {
27
+ const line = lines[i];
28
+ const fenceMatch = line.match(OPENING_FENCE_RE);
29
+ if (fenceMatch) {
30
+ const fence = fenceMatch[1]; // e.g. "```"
31
+ flushMarkdown();
32
+ // Collect code lines until closing fence
33
+ i++;
34
+ const codeLines = [];
35
+ while (i < lines.length && !isClosingFence(lines[i], fence)) {
36
+ codeLines.push(lines[i]);
37
+ i++;
38
+ }
39
+ i++; // skip closing fence (or EOF)
40
+ // Strip trailing blank lines from code
41
+ let end = codeLines.length;
42
+ while (end > 0 && codeLines[end - 1].trim() === "")
43
+ end--;
44
+ const source = codeLines.slice(0, end).join("\n");
45
+ cells.push(codeCell(source, {}, used));
46
+ }
47
+ else {
48
+ mdLines.push(line);
49
+ i++;
50
+ }
51
+ }
52
+ flushMarkdown();
53
+ return makeNotebook(cells);
54
+ }
55
+ //# sourceMappingURL=parseClassicMd.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parseClassicMd.js","sourceRoot":"","sources":["../src/parseClassicMd.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,YAAY,EAA4B,MAAM,eAAe,CAAC;AAE/F,8EAA8E;AAC9E,0BAA0B;AAC1B,kCAAkC;AAClC,0EAA0E;AAC1E,yCAAyC;AACzC,8EAA8E;AAE9E,oDAAoD;AACpD,MAAM,gBAAgB,GAAG,yBAAyB,CAAC;AACnD,6CAA6C;AAC7C,SAAS,cAAc,CAAC,IAAY,EAAE,KAAa;IACjD,OAAO,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC;AAC1E,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,IAAY;IACzC,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACtD,MAAM,KAAK,GAAW,EAAE,CAAC;IACzB,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAE/B,MAAM,OAAO,GAAa,EAAE,CAAC;IAE7B,SAAS,aAAa;QACpB,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC;QAC1C,IAAI,OAAO;YAAE,KAAK,CAAC,IAAI,CAAC,YAAY,CAAC,OAAO,EAAE,EAAE,EAAE,IAAI,CAAC,CAAC,CAAC;QACzD,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC;IACrB,CAAC;IAED,IAAI,CAAC,GAAG,CAAC,CAAC;IACV,OAAO,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC;QACxB,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QACtB,MAAM,UAAU,GAAG,IAAI,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC;QAEhD,IAAI,UAAU,EAAE,CAAC;YACf,MAAM,KAAK,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,aAAa;YAC1C,aAAa,EAAE,CAAC;YAEhB,yCAAyC;YACzC,CAAC,EAAE,CAAC;YACJ,MAAM,SAAS,GAAa,EAAE,CAAC;YAC/B,OAAO,CAAC,GAAG,KAAK,CAAC,MAAM,IAAI,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,EAAE,CAAC;gBAC5D,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;gBACzB,CAAC,EAAE,CAAC;YACN,CAAC;YACD,CAAC,EAAE,CAAC,CAAC,8BAA8B;YAEnC,uCAAuC;YACvC,IAAI,GAAG,GAAG,SAAS,CAAC,MAAM,CAAC;YAC3B,OAAO,GAAG,GAAG,CAAC,IAAI,SAAS,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE;gBAAE,GAAG,EAAE,CAAC;YAC1D,MAAM,MAAM,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAClD,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,EAAE,EAAE,IAAI,CAAC,CAAC,CAAC;QACzC,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACnB,CAAC,EAAE,CAAC;QACN,CAAC;IACH,CAAC;IAED,aAAa,EAAE,CAAC;IAChB,OAAO,YAAY,CAAC,KAAK,CAAC,CAAC;AAC7B,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { Notebook } from "./notebook.js";
2
+ export declare function parseMd(text: string): Notebook;
3
+ //# sourceMappingURL=parseMd.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parseMd.d.ts","sourceRoot":"","sources":["../src/parseMd.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAc9C,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,CAE9C"}
@@ -0,0 +1,19 @@
1
+ import { parseClassicMd } from "./parseClassicMd.js";
2
+ import { parseMystMd } from "./parseMystMd.js";
3
+ /** Detect MyST notebook format by scanning for {code-cell}/{raw-cell} directives or +++ breaks. */
4
+ function isMyST(text) {
5
+ const lines = text.split("\n");
6
+ const limit = Math.min(lines.length, 100);
7
+ for (let i = 0; i < limit; i++) {
8
+ const line = lines[i];
9
+ if (/^```\{(?:code-cell|raw-cell|markdown-cell)\}/.test(line))
10
+ return true;
11
+ if (/^\+\+\+/.test(line))
12
+ return true;
13
+ }
14
+ return false;
15
+ }
16
+ export function parseMd(text) {
17
+ return isMyST(text) ? parseMystMd(text) : parseClassicMd(text);
18
+ }
19
+ //# sourceMappingURL=parseMd.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parseMd.js","sourceRoot":"","sources":["../src/parseMd.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAG/C,mGAAmG;AACnG,SAAS,MAAM,CAAC,IAAY;IAC1B,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC/B,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC1C,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC;QAC/B,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QACtB,IAAI,8CAA8C,CAAC,IAAI,CAAC,IAAI,CAAC;YAAE,OAAO,IAAI,CAAC;QAC3E,IAAI,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC;YAAE,OAAO,IAAI,CAAC;IACxC,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,UAAU,OAAO,CAAC,IAAY;IAClC,OAAO,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC;AACjE,CAAC"}
@@ -0,0 +1,3 @@
1
+ import { type Notebook } from "./notebook.js";
2
+ export declare function parseMystMd(text: string): Notebook;
3
+ //# sourceMappingURL=parseMystMd.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parseMystMd.d.ts","sourceRoot":"","sources":["../src/parseMystMd.ts"],"names":[],"mappings":"AAAA,OAAO,EAML,KAAK,QAAQ,EACd,MAAM,eAAe,CAAC;AAuFvB,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,CAmIlD"}
@@ -0,0 +1,205 @@
1
+ import { codeCell, markdownCell, rawCell, makeNotebook, } from "./notebook.js";
2
+ // ---------------------------------------------------------------------------
3
+ // MyST notebook format parser
4
+ // Handles {code-cell}, {raw-cell}, {markdown-cell} directives
5
+ // and +++ cell-break syntax
6
+ // ---------------------------------------------------------------------------
7
+ // ```{code-cell} ipython3 or ```{raw-cell}
8
+ const DIRECTIVE_OPEN_RE = /^(`{3,})\{([\w-]+)\}(?:\s+(\S+))?\s*$/;
9
+ // +++ or +++ {"tags": [...]}
10
+ const CELL_BREAK_RE = /^\+\+\+(?:\s+(\{.*\}))?\s*$/;
11
+ // YAML shorthand option :key: value
12
+ const SHORTHAND_RE = /^:([\w-]+):\s*(.*)/;
13
+ const CELL_DIRECTIVES = new Set(["code-cell", "raw-cell", "markdown-cell"]);
14
+ /** Extract the human-readable label from a MyST role content string.
15
+ * ~some.dotted.path → last segment
16
+ * label <target> → label
17
+ * plain text → plain text
18
+ */
19
+ function roleLabel(content) {
20
+ const angleMatch = content.match(/^(.+?)\s*<[^>]+>$/);
21
+ if (angleMatch)
22
+ return angleMatch[1].trim();
23
+ if (content.startsWith("~")) {
24
+ const parts = content.slice(1).split(".");
25
+ return parts[parts.length - 1];
26
+ }
27
+ return content;
28
+ }
29
+ /** Substitute MyST inline syntax into readable plain Markdown. */
30
+ function substituteInlineRoles(line) {
31
+ // {math}`expr` → $expr$ (must run before the catch-all below)
32
+ line = line.replace(/\{math\}`([^`]*)`/g, (_, expr) => `$${expr}$`);
33
+ // {cite}`label` → remove (bibliography references add no value in a notebook)
34
+ line = line.replace(/\{cite\}`[^`]*`/g, "");
35
+ // {{ var }} → var (substitution references — values not available)
36
+ line = line.replace(/\{\{\s*([\w.-]+)\s*\}\}/g, (_, name) => name);
37
+ // All remaining inline roles → plain display text
38
+ line = line.replace(/\{[\w-]+\}`([^`]*)`/g, (_, content) => roleLabel(content));
39
+ return line;
40
+ }
41
+ /** Minimal YAML/JSON option parser for cell metadata.
42
+ * Handles flat key:value and key: [flow, list] only. */
43
+ function parseOptions(lines) {
44
+ const result = {};
45
+ for (const line of lines) {
46
+ const m = line.match(/^([\w-]+):\s*(.*)/);
47
+ if (!m)
48
+ continue;
49
+ const key = m[1];
50
+ const val = m[2].trim();
51
+ if (val.startsWith("[") || val.startsWith("{")) {
52
+ try {
53
+ result[key] = JSON.parse(val);
54
+ }
55
+ catch {
56
+ result[key] = val;
57
+ }
58
+ }
59
+ else {
60
+ result[key] = val;
61
+ }
62
+ }
63
+ return result;
64
+ }
65
+ /** Parse notebook-level YAML front matter (---...---) into a plain object.
66
+ * Only handles flat key: value pairs — sufficient for kernelspec. */
67
+ function parseFrontMatter(yamlLines) {
68
+ const result = {};
69
+ for (const line of yamlLines) {
70
+ const m = line.match(/^([\w-]+):\s*(.*)/);
71
+ if (m)
72
+ result[m[1]] = m[2].trim();
73
+ }
74
+ return result;
75
+ }
76
+ function stripTrailingBlank(lines) {
77
+ let end = lines.length;
78
+ while (end > 0 && lines[end - 1].trim() === "")
79
+ end--;
80
+ return lines.slice(0, end);
81
+ }
82
+ export function parseMystMd(text) {
83
+ const lines = text.replace(/\r\n/g, "\n").split("\n");
84
+ const cells = [];
85
+ const used = new Set();
86
+ let notebookMeta = {};
87
+ const mdLines = [];
88
+ let pendingMeta = {};
89
+ function flushMarkdown() {
90
+ const trimmed = mdLines.join("\n").trim();
91
+ if (trimmed)
92
+ cells.push(markdownCell(trimmed, pendingMeta, used));
93
+ mdLines.length = 0;
94
+ pendingMeta = {};
95
+ }
96
+ let i = 0;
97
+ // Strip notebook-level front matter (---...---)
98
+ if (lines[0] === "---") {
99
+ i = 1;
100
+ const fmLines = [];
101
+ while (i < lines.length && lines[i] !== "---") {
102
+ fmLines.push(lines[i]);
103
+ i++;
104
+ }
105
+ i++; // skip closing ---
106
+ notebookMeta = parseFrontMatter(fmLines);
107
+ }
108
+ while (i < lines.length) {
109
+ const line = lines[i];
110
+ // +++ cell break
111
+ const breakMatch = line.match(CELL_BREAK_RE);
112
+ if (breakMatch) {
113
+ // Flush current markdown with its (empty) metadata, then set pending
114
+ // metadata for the NEXT cell that accumulates after this break
115
+ flushMarkdown();
116
+ if (breakMatch[1]) {
117
+ try {
118
+ pendingMeta = JSON.parse(breakMatch[1]);
119
+ }
120
+ catch {
121
+ /* ignore */
122
+ }
123
+ }
124
+ i++;
125
+ continue;
126
+ }
127
+ // ```{directive} ...
128
+ const dirMatch = line.match(DIRECTIVE_OPEN_RE);
129
+ if (dirMatch) {
130
+ const fence = dirMatch[1];
131
+ const directive = dirMatch[2];
132
+ // {math} block → convert to $$ display math
133
+ if (directive === "math") {
134
+ i++;
135
+ const mathLines = [];
136
+ while (i < lines.length && !isClosingFence(lines[i], fence)) {
137
+ mathLines.push(lines[i]);
138
+ i++;
139
+ }
140
+ i++; // skip closing fence
141
+ const inner = stripTrailingBlank(mathLines).join("\n");
142
+ mdLines.push("$$");
143
+ if (inner)
144
+ mdLines.push(inner);
145
+ mdLines.push("$$");
146
+ continue;
147
+ }
148
+ // Other non-cell directives ({note}, {try_on_binder}, etc.) stay as markdown text
149
+ if (!CELL_DIRECTIVES.has(directive)) {
150
+ mdLines.push(line);
151
+ i++;
152
+ continue;
153
+ }
154
+ flushMarkdown();
155
+ i++;
156
+ // Collect shorthand options (:key: value) or YAML block (---...---)
157
+ const optionLines = [];
158
+ if (lines[i] === "---") {
159
+ i++;
160
+ while (i < lines.length && lines[i] !== "---") {
161
+ optionLines.push(lines[i]);
162
+ i++;
163
+ }
164
+ i++; // skip closing ---
165
+ }
166
+ else {
167
+ while (i < lines.length && SHORTHAND_RE.test(lines[i])) {
168
+ const m = lines[i].match(SHORTHAND_RE);
169
+ optionLines.push(`${m[1]}: ${m[2]}`);
170
+ i++;
171
+ }
172
+ }
173
+ const cellMeta = parseOptions(optionLines);
174
+ // Skip blank line after options
175
+ if (i < lines.length && lines[i].trim() === "")
176
+ i++;
177
+ // Collect content until closing fence
178
+ const contentLines = [];
179
+ while (i < lines.length && !isClosingFence(lines[i], fence)) {
180
+ contentLines.push(lines[i]);
181
+ i++;
182
+ }
183
+ i++; // skip closing fence
184
+ const source = stripTrailingBlank(contentLines).join("\n");
185
+ if (directive === "code-cell") {
186
+ cells.push(codeCell(source, cellMeta, used));
187
+ }
188
+ else if (directive === "raw-cell") {
189
+ cells.push(rawCell(source, cellMeta, used));
190
+ }
191
+ else {
192
+ cells.push(markdownCell(source, cellMeta, used));
193
+ }
194
+ continue;
195
+ }
196
+ mdLines.push(substituteInlineRoles(line));
197
+ i++;
198
+ }
199
+ flushMarkdown();
200
+ return makeNotebook(cells, notebookMeta);
201
+ }
202
+ function isClosingFence(line, fence) {
203
+ return line.startsWith(fence) && line.slice(fence.length).trim() === "";
204
+ }
205
+ //# sourceMappingURL=parseMystMd.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parseMystMd.js","sourceRoot":"","sources":["../src/parseMystMd.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import { type Notebook } from "./notebook.js";
2
+ export declare function parsePy(text: string): Notebook;
3
+ //# sourceMappingURL=parsePy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parsePy.d.ts","sourceRoot":"","sources":["../src/parsePy.ts"],"names":[],"mappings":"AAAA,OAAO,EAML,KAAK,QAAQ,EACd,MAAM,eAAe,CAAC;AA4EvB,wBAAgB,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,CA2C9C"}
@@ -0,0 +1,109 @@
1
+ import { codeCell, markdownCell, rawCell, makeNotebook, } from "./notebook.js";
2
+ // ---------------------------------------------------------------------------
3
+ // Percent format parser
4
+ // ---------------------------------------------------------------------------
5
+ // Matches: # %% optional-rest
6
+ const DELIMITER_RE = /^# %%(.*)$/;
7
+ // Matches cell type tag: [markdown], [md], [raw]
8
+ const TYPE_TAG_RE = /\[(\w+)\]/;
9
+ function parseCellHeader(rest) {
10
+ let s = rest.trim();
11
+ let cellType = "code";
12
+ const typeMatch = s.match(TYPE_TAG_RE);
13
+ if (typeMatch) {
14
+ const t = typeMatch[1].toLowerCase();
15
+ if (t === "markdown" || t === "md")
16
+ cellType = "markdown";
17
+ else if (t === "raw")
18
+ cellType = "raw";
19
+ s = s.replace(TYPE_TAG_RE, "").trim();
20
+ }
21
+ const metadata = {};
22
+ // Parse tags="[...]" or tags=['...']
23
+ const tagsMatch = s.match(/\btags\s*=\s*(['"])\[(.+?)\]\1/);
24
+ if (tagsMatch) {
25
+ try {
26
+ metadata.tags = JSON.parse("[" + tagsMatch[2] + "]");
27
+ }
28
+ catch {
29
+ // ignore malformed tags
30
+ }
31
+ s = s.replace(tagsMatch[0], "").trim();
32
+ }
33
+ // Remaining text is the cell title/name
34
+ if (s)
35
+ metadata.name = s;
36
+ return { cellType, metadata };
37
+ }
38
+ /** Strip leading `# ` (or bare `#`) comment prefix from markdown/raw cell lines. */
39
+ function uncomment(lines) {
40
+ return lines
41
+ .map((line) => {
42
+ if (line === "#")
43
+ return "";
44
+ if (line.startsWith("# "))
45
+ return line.slice(2);
46
+ return line;
47
+ })
48
+ .join("\n");
49
+ }
50
+ /** Strip triple-quote wrappers from markdown cell content. */
51
+ function stripTripleQuotes(lines) {
52
+ let start = 0;
53
+ let end = lines.length;
54
+ if (lines[0]?.trim() === '"""')
55
+ start = 1;
56
+ if (lines[end - 1]?.trim() === '"""')
57
+ end -= 1;
58
+ return lines.slice(start, end).join("\n");
59
+ }
60
+ function stripTrailingBlank(lines) {
61
+ let end = lines.length;
62
+ while (end > 0 && lines[end - 1].trim() === "")
63
+ end--;
64
+ return lines.slice(0, end);
65
+ }
66
+ export function parsePy(text) {
67
+ // Normalize line endings
68
+ const lines = text.replace(/\r\n/g, "\n").split("\n");
69
+ const cells = [];
70
+ const used = new Set();
71
+ // Find all delimiter positions
72
+ const delimiters = [];
73
+ for (let i = 0; i < lines.length; i++) {
74
+ const m = lines[i].match(DELIMITER_RE);
75
+ if (m)
76
+ delimiters.push({ idx: i, rest: m[1] });
77
+ }
78
+ if (delimiters.length === 0) {
79
+ // No delimiters — entire file is one code cell
80
+ const source = lines.join("\n");
81
+ if (source.trim())
82
+ cells.push(codeCell(source, {}, used));
83
+ return makeNotebook(cells);
84
+ }
85
+ for (let d = 0; d < delimiters.length; d++) {
86
+ const start = delimiters[d].idx + 1;
87
+ const end = d + 1 < delimiters.length ? delimiters[d + 1].idx : lines.length;
88
+ const { cellType, metadata } = parseCellHeader(delimiters[d].rest);
89
+ const rawLines = stripTrailingBlank(lines.slice(start, end));
90
+ if (cellType === "code") {
91
+ const source = rawLines.join("\n");
92
+ if (source.trim())
93
+ cells.push(codeCell(source, metadata, used));
94
+ }
95
+ else {
96
+ // markdown or raw
97
+ const isTripleQuote = rawLines[0]?.trim() === '"""';
98
+ const source = isTripleQuote ? stripTripleQuotes(rawLines) : uncomment(rawLines);
99
+ if (cellType === "markdown") {
100
+ cells.push(markdownCell(source, metadata, used));
101
+ }
102
+ else {
103
+ cells.push(rawCell(source, metadata, used));
104
+ }
105
+ }
106
+ }
107
+ return makeNotebook(cells);
108
+ }
109
+ //# sourceMappingURL=parsePy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parsePy.js","sourceRoot":"","sources":["../src/parsePy.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import { type Notebook } from "./notebook.js";
2
+ export declare function parseSphinxGallery(text: string): Notebook;
3
+ //# sourceMappingURL=parseSphinxGallery.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parseSphinxGallery.d.ts","sourceRoot":"","sources":["../src/parseSphinxGallery.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmD,KAAK,QAAQ,EAAE,MAAM,eAAe,CAAC;AAkH/F,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,CAwDzD"}
@@ -0,0 +1,153 @@
1
+ import { codeCell, markdownCell, makeNotebook } from "./notebook.js";
2
+ // ---------------------------------------------------------------------------
3
+ // RST helpers
4
+ // ---------------------------------------------------------------------------
5
+ // RST underline character → markdown heading prefix
6
+ const RST_HEADING = {
7
+ "=": "#",
8
+ "-": "##",
9
+ "^": "###",
10
+ "~": "####",
11
+ '"': "#####",
12
+ };
13
+ function isRstUnderline(s) {
14
+ return s.length >= 2 && /^([-=^~"*+#`'!])\1+$/.test(s);
15
+ }
16
+ /** Convert RST overline/underline headings to Markdown headings (in-place). */
17
+ function rstHeadingsToMd(lines) {
18
+ const out = [];
19
+ let i = 0;
20
+ while (i < lines.length) {
21
+ const cur = lines[i];
22
+ const nxt = lines[i + 1] ?? "";
23
+ const aft = lines[i + 2] ?? "";
24
+ // Overline + title + underline (===\nTitle\n===)
25
+ if (isRstUnderline(cur) && nxt.trim() && isRstUnderline(aft) && cur[0] === aft[0]) {
26
+ out.push(`${RST_HEADING[cur[0]] ?? "#"} ${nxt.trim()}`);
27
+ i += 3;
28
+ continue;
29
+ }
30
+ // Title + underline (Title\n---)
31
+ if (cur.trim() && isRstUnderline(nxt)) {
32
+ out.push(`${RST_HEADING[nxt[0]] ?? "##"} ${cur.trim()}`);
33
+ i += 2;
34
+ continue;
35
+ }
36
+ out.push(cur);
37
+ i++;
38
+ }
39
+ return out;
40
+ }
41
+ /** Extract display label from RST role content string. */
42
+ function rstRoleLabel(content) {
43
+ // `label <target>`_ or label <target>
44
+ const angleMatch = content.match(/^(.+?)\s*<[^>]+>`?_?$/);
45
+ if (angleMatch)
46
+ return angleMatch[1].trim().replace(/^`/, "");
47
+ // ~some.dotted.path → last segment
48
+ if (content.startsWith("~")) {
49
+ const parts = content.slice(1).split(".");
50
+ return parts[parts.length - 1];
51
+ }
52
+ return content;
53
+ }
54
+ /** Strip RST inline roles (:role:`content`) to plain readable text. */
55
+ function substituteRstRoles(line) {
56
+ return line.replace(/:[\w-]+:`([^`]*)`/g, (_, content) => rstRoleLabel(content));
57
+ }
58
+ // ---------------------------------------------------------------------------
59
+ // Comment/docstring stripping
60
+ // ---------------------------------------------------------------------------
61
+ function uncomment(lines) {
62
+ return lines.map((l) => (l === "#" ? "" : l.startsWith("# ") ? l.slice(2) : l));
63
+ }
64
+ function stripTrailingBlank(lines) {
65
+ let end = lines.length;
66
+ while (end > 0 && lines[end - 1].trim() === "")
67
+ end--;
68
+ return lines.slice(0, end);
69
+ }
70
+ function stripLeadingBlank(lines) {
71
+ let start = 0;
72
+ while (start < lines.length && lines[start].trim() === "")
73
+ start++;
74
+ return lines.slice(start);
75
+ }
76
+ /** Extract the module-level triple-quoted docstring. */
77
+ function extractDocstring(lines) {
78
+ if (!lines[0]?.startsWith('"""'))
79
+ return null;
80
+ const afterOpen = lines[0].slice(3);
81
+ const sameLineClose = afterOpen.indexOf('"""');
82
+ if (sameLineClose >= 0) {
83
+ return { content: afterOpen.slice(0, sameLineClose), end: 1 };
84
+ }
85
+ const contentLines = afterOpen ? [afterOpen] : [];
86
+ for (let i = 1; i < lines.length; i++) {
87
+ const ci = lines[i].indexOf('"""');
88
+ if (ci >= 0) {
89
+ if (ci > 0)
90
+ contentLines.push(lines[i].slice(0, ci));
91
+ return { content: contentLines.join("\n"), end: i + 1 };
92
+ }
93
+ contentLines.push(lines[i]);
94
+ }
95
+ return null;
96
+ }
97
+ // ---------------------------------------------------------------------------
98
+ // Main parser
99
+ // ---------------------------------------------------------------------------
100
+ const DELIMITER_RE = /^# %%(.*)$/;
101
+ export function parseSphinxGallery(text) {
102
+ const lines = text.replace(/\r\n/g, "\n").split("\n");
103
+ const cells = [];
104
+ const used = new Set();
105
+ let i = 0;
106
+ // 1. Module docstring → first markdown cell
107
+ const ds = extractDocstring(lines);
108
+ if (ds) {
109
+ const raw = ds.content.split("\n");
110
+ const converted = rstHeadingsToMd(raw).map(substituteRstRoles);
111
+ const trimmed = stripTrailingBlank(stripLeadingBlank(converted)).join("\n");
112
+ if (trimmed)
113
+ cells.push(markdownCell(trimmed, {}, used));
114
+ i = ds.end;
115
+ }
116
+ // 2. Skip lines before first # %% (authors, SPDX, blanks)
117
+ while (i < lines.length && !DELIMITER_RE.test(lines[i]))
118
+ i++;
119
+ // 3. Collect delimiter positions
120
+ const delimiters = [];
121
+ for (let j = i; j < lines.length; j++) {
122
+ if (DELIMITER_RE.test(lines[j]))
123
+ delimiters.push(j);
124
+ }
125
+ // 4. Process each section
126
+ for (let d = 0; d < delimiters.length; d++) {
127
+ const start = delimiters[d] + 1;
128
+ const end = d + 1 < delimiters.length ? delimiters[d + 1] : lines.length;
129
+ const section = lines.slice(start, end);
130
+ // Split: leading comment lines → markdown, rest → code
131
+ let split = 0;
132
+ while (split < section.length && section[split].startsWith("#"))
133
+ split++;
134
+ const commentLines = section.slice(0, split);
135
+ // Skip blank separator between comment block and code
136
+ let codeStart = split;
137
+ while (codeStart < section.length && section[codeStart].trim() === "")
138
+ codeStart++;
139
+ const codeLines = stripTrailingBlank(section.slice(codeStart));
140
+ if (commentLines.length > 0) {
141
+ const raw = uncomment(commentLines);
142
+ const converted = rstHeadingsToMd(raw).map(substituteRstRoles);
143
+ const mdText = stripTrailingBlank(converted).join("\n").trim();
144
+ if (mdText)
145
+ cells.push(markdownCell(mdText, {}, used));
146
+ }
147
+ if (codeLines.length > 0) {
148
+ cells.push(codeCell(codeLines.join("\n"), {}, used));
149
+ }
150
+ }
151
+ return makeNotebook(cells);
152
+ }
153
+ //# sourceMappingURL=parseSphinxGallery.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parseSphinxGallery.js","sourceRoot":"","sources":["../src/parseSphinxGallery.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "plainb",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "description": "Convert plain text (.py percent format, .md classic/MyST) to Jupyter nbformat 4 JSON",
6
+ "exports": {
7
+ ".": {
8
+ "types": "./dist/index.d.ts",
9
+ "default": "./dist/index.js"
10
+ }
11
+ },
12
+ "main": "./dist/index.js",
13
+ "types": "./dist/index.d.ts",
14
+ "files": [
15
+ "dist"
16
+ ],
17
+ "repository": {
18
+ "type": "git",
19
+ "url": "https://github.com/trungleduc/plainb"
20
+ },
21
+ "homepage": "https://github.com/trungleduc/plainb",
22
+ "license": "MIT",
23
+ "scripts": {
24
+ "prepublishOnly": "npm run build && npm test",
25
+ "build": "tsc",
26
+ "test": "node --test --import tsx test/*.test.ts",
27
+ "lint": "eslint src",
28
+ "format": "prettier --write src",
29
+ "demo": "npm run --prefix demo dev",
30
+ "demo:build": "npm run --prefix demo build"
31
+ },
32
+ "devDependencies": {
33
+ "@eslint/js": "^10.0.1",
34
+ "eslint": "^10.0.3",
35
+ "eslint-config-prettier": "^10.1.8",
36
+ "prettier": "^3.8.1",
37
+ "tsx": "^4.19.0",
38
+ "typescript": "^5.8.0",
39
+ "typescript-eslint": "^8.57.0"
40
+ }
41
+ }