diff --git a/packages/myst-cli/src/build/build.spec.ts b/packages/myst-cli/src/build/build.spec.ts index d3151a081..7ce7bdd3c 100644 --- a/packages/myst-cli/src/build/build.spec.ts +++ b/packages/myst-cli/src/build/build.spec.ts @@ -36,6 +36,7 @@ describe('get export formats', () => { ExportFormats.tex, ExportFormats.xml, ExportFormats.md, + ExportFormats.ipynb, ExportFormats.meca, ExportFormats.cff, ]); diff --git a/packages/myst-to-ipynb/src/index.ts b/packages/myst-to-ipynb/src/index.ts index 4f490c7c6..4593d951c 100644 --- a/packages/myst-to-ipynb/src/index.ts +++ b/packages/myst-to-ipynb/src/index.ts @@ -24,13 +24,14 @@ export function writeIpynb(file: VFile, node: Root, frontmatter?: PageFrontmatte source: sourceToStringList(code.value), }; } - const md = writeMd(file, { type: 'root', children: block.children as any }).result as string; + const md = writeMd(file, { type: 'root', children: [block] }).result as string; return { cell_type: 'markdown', metadata: {}, source: sourceToStringList(md), }; }); + const ipynb = { cells, metadata: { @@ -41,6 +42,7 @@ export function writeIpynb(file: VFile, node: Root, frontmatter?: PageFrontmatte nbformat: 4, nbformat_minor: 2, }; + file.result = JSON.stringify(ipynb, null, 2); return file; } diff --git a/packages/myst-to-ipynb/tests/basic.yml b/packages/myst-to-ipynb/tests/basic.yml index 48c124b3c..a0080e27c 100644 --- a/packages/myst-to-ipynb/tests/basic.yml +++ b/packages/myst-to-ipynb/tests/basic.yml @@ -1,4 +1,4 @@ -title: myst-to-md basic features +title: myst-to-ipynb basic features cases: - title: styles in paragraph mdast: @@ -22,8 +22,18 @@ cases: value: ' ' - type: inlineCode value: style`s - markdown: |- - Some % *markdown* with **different** ``style`s`` + ipynb: + cells: + - cell_type: "markdown" + metadata: {} + source: + - "Some % *markdown* with **different** ``style`s``" + metadata: + language_info: + name: "python" + nbformat: 4 + nbformat_minor: 2 + - title: headings mdast: type: root @@ -46,235 +56,505 @@ cases: children: - type: text value: fourth - markdown: |- - # first - - Some % *markdown* - - #### fourth - - title: thematic break - mdast: - type: root - children: - - type: paragraph - children: - - type: text - value: Some markdown - - type: thematicBreak - - type: paragraph - children: - - type: text - value: Some more markdown - markdown: |- - Some markdown - - --- - - Some more markdown - - title: block quote - mdast: - type: root - children: - - type: blockquote - children: - - type: paragraph - children: - - type: text - value: 'Some % ' - - type: emphasis - children: - - type: text - value: markdown - markdown: |- - > Some % *markdown* - - title: unordered list - mdast: - type: root - children: - - type: list - ordered: false - children: - - type: listItem - children: - - type: paragraph - children: - - type: text - value: Some markdown - - type: listItem - children: - - type: paragraph - children: - - type: text - value: Some more markdown - markdown: |- - * Some markdown + ipynb: + cells: + - cell_type: "markdown" + metadata: {} + source: + - "# first" + - cell_type: "markdown" + metadata: {} + source: + - "Some % *markdown*" + - cell_type: "markdown" + metadata: {} + source: + - "#### fourth" + metadata: + language_info: + name: "python" + nbformat: 4 + nbformat_minor: 2 - * Some more markdown - - title: ordered list - mdast: - type: root - children: - - type: list - ordered: true - start: 5 - children: - - type: listItem - children: - - type: paragraph - children: - - type: text - value: Some markdown - - type: listItem - children: - - type: paragraph - children: - - type: text - value: Some more markdown - markdown: |- - 5. Some markdown - - 6. Some more markdown - - title: html - mdast: - type: root - children: - - type: html - value:
*Not markdown*<\div> - markdown: |- -
*Not markdown*<\div> - - title: code - plain - mdast: - type: root - children: - - type: code - value: |- - 5+5 - print("hello world") - markdown: |- - ``` - 5+5 - print("hello world") - ``` - - title: code - nested backticks - mdast: - type: root - children: - - type: code - value: |- - 5+5 - ````{abc} - ```` - print("hello world") - markdown: |- - ````` - 5+5 - ````{abc} - ```` - print("hello world") - ````` - - title: code - with language - mdast: - type: root - children: - - type: code - lang: python - value: |- - 5+5 - print("hello world") - markdown: |- - ```python - 5+5 - print("hello world") - ``` - - title: code - with metadata - mdast: - type: root - children: - - type: code - lang: python - meta: highlight-line="2" - value: |- - 5+5 - print("hello world") - markdown: |- - ```python highlight-line="2" - 5+5 - print("hello world") - ``` - - title: definition - mdast: - type: root - children: - - type: definition - identifier: my-def - label: My-Def - url: https://example.com - title: Example - markdown: |- - [My-Def]: https://example.com "Example" - - title: break - mdast: - type: root - children: - - type: paragraph - children: - - type: text - value: Some markdown - - type: break - - type: text - value: Some more markdown - markdown: |- - Some markdown\ - Some more markdown - - title: link - mdast: - type: root - children: - - type: link - url: https://example.com - title: my link - children: - - type: text - value: 'Some % ' - - type: emphasis - children: - - type: text - value: markdown - markdown: |- - [Some % *markdown*](https://example.com "my link") - - title: image - mdast: - type: root - children: - - type: image - url: https://example.com - title: my image - alt: Some text - markdown: |- - ![Some text](https://example.com "my image") - - title: link reference - mdast: - type: root - children: - - type: linkReference - identifier: my-link - label: My-Link - children: - - type: text - value: 'Some % ' - - type: emphasis - children: - - type: text - value: markdown - markdown: |- - [Some % *markdown*][My-Link] - - title: image reference - mdast: - type: root - children: - - type: imageReference - identifier: my-image - label: My-Image - alt: Some text - markdown: |- - ![Some text][My-Image] + # - title: thematic break + # mdast: + # type: root + # children: + # - type: paragraph + # children: + # - type: text + # value: Some markdown + # - type: thematicBreak + # - type: paragraph + # children: + # - type: text + # value: Some more markdown + # ipynb: |- + # { + # "cells": [ + # { + # "cell_type": "markdown", + # "metadata": {}, + # "source": [ + # "Some markdown\n", + # "\n", + # "---\n", + # "\n", + # "Some more markdown" + # ] + # } + # ], + # "metadata": { + # "language_info": { + # "name": "python" + # } + # }, + # "nbformat": 4, + # "nbformat_minor": 2 + # } + # - title: block quote + # mdast: + # type: root + # children: + # - type: blockquote + # children: + # - type: paragraph + # children: + # - type: text + # value: 'Some % ' + # - type: emphasis + # children: + # - type: text + # value: markdown + # ipynb: |- + # { + # "cells": [ + # { + # "cell_type": "markdown", + # "metadata": {}, + # "source": [ + # "> Some % *markdown*" + # ] + # } + # ], + # "metadata": { + # "language_info": { + # "name": "python" + # } + # }, + # "nbformat": 4, + # "nbformat_minor": 2 + # } + # - title: unordered list + # mdast: + # type: root + # children: + # - type: list + # ordered: false + # children: + # - type: listItem + # children: + # - type: paragraph + # children: + # - type: text + # value: Some markdown + # - type: listItem + # children: + # - type: paragraph + # children: + # - type: text + # value: Some more markdown + # ipynb: |- + # { + # "cells": [ + # { + # "cell_type": "markdown", + # "metadata": {}, + # "source": [ + # "* Some markdown\n", + # "\n", + # "* Some more markdown" + # ] + # } + # ], + # "metadata": { + # "language_info": { + # "name": "python" + # } + # }, + # "nbformat": 4, + # "nbformat_minor": 2 + # } + # - title: ordered list + # mdast: + # type: root + # children: + # - type: list + # ordered: true + # start: 5 + # children: + # - type: listItem + # children: + # - type: paragraph + # children: + # - type: text + # value: Some markdown + # - type: listItem + # children: + # - type: paragraph + # children: + # - type: text + # value: Some more markdown + # ipynb: |- + # { + # "cells": [ + # { + # "cell_type": "markdown", + # "metadata": {}, + # "source": [ + # "5. Some markdown\n", + # "\n", + # "6. Some more markdown" + # ] + # } + # ], + # "metadata": { + # "language_info": { + # "name": "python" + # } + # }, + # "nbformat": 4, + # "nbformat_minor": 2 + # } + # - title: html + # mdast: + # type: root + # children: + # - type: html + # value:
*Not markdown*
+ # ipynb: |- + # { + # "cells": [ + # { + # "cell_type": "markdown", + # "metadata": {}, + # "source": [ + # "
*Not markdown*
" + # ] + # } + # ], + # "metadata": { + # "language_info": { + # "name": "python" + # } + # }, + # "nbformat": 4, + # "nbformat_minor": 2 + # } + # - title: code - plain + # mdast: + # type: root + # children: + # - type: code + # value: |- + # 5+5 + # print("hello world\n") + # ipynb: |- + # { + # "cells": [ + # { + # "cell_type": "markdown", + # "metadata": {}, + # "source": [ + # "```\n", + # "5+5\n", + # "print(\"hello world\\n\")\n", + # "```" + # ] + # } + # ], + # "metadata": { + # "language_info": { + # "name": "python" + # } + # }, + # "nbformat": 4, + # "nbformat_minor": 2 + # } + # - title: code - nested backticks + # mdast: + # type: root + # children: + # - type: code + # value: |- + # 5+5 + # ````{abc} + # ```` + # print("hello world") + # ipynb: |- + # { + # "cells": [ + # { + # "cell_type": "markdown", + # "metadata": {}, + # "source": [ + # "`````\n", + # "5+5\n", + # "````{abc}\n", + # "````\n", + # "print(\"hello world\")\n", + # "`````" + # ] + # } + # ], + # "metadata": { + # "language_info": { + # "name": "python" + # } + # }, + # "nbformat": 4, + # "nbformat_minor": 2 + # } + # - title: code - with language + # mdast: + # type: root + # children: + # - type: block + # kind: notebook-code + # data: + # id: nb-cell-0 + # identifier: nb-cell-0 + # label: nb-cell-0 + # html_id: nb-cell-0 + # children: + # - type: code + # lang: python + # executable: true + # value: print('abc\n') + # identifier: nb-cell-0-code + # enumerator: 1 + # html_id: nb-cell-0-code + # - type: output + # id: T7FMDqDm8dM2bOT1tKeeM + # identifier: nb-cell-0-output + # html_id: nb-cell-0-output + # - type: code + # lang: python + # value: |- + # 5+5 + # print("hello world") + # ipynb: |- + # { + # "cells": [ + # { + # "cell_type": "code", + # "execution_count": null, + # "metadata": {}, + # "outputs": [], + # "source": [ + # "print('abc\\n')" + # ] + # }, + # { + # "cell_type": "markdown", + # "metadata": {}, + # "source": [ + # "```python\n", + # "5+5\n", + # "print(\"hello world\")\n", + # "```" + # ] + # } + # ], + # "metadata": { + # "language_info": { + # "name": "python" + # } + # }, + # "nbformat": 4, + # "nbformat_minor": 2 + # } + # - title: code - with metadata + # mdast: + # type: root + # children: + # - type: code + # lang: python + # meta: highlight-line="2" + # value: |- + # 5+5 + # print("hello world") + # ipynb: |- + # { + # "cells": [ + # { + # "cell_type": "markdown", + # "metadata": {}, + # "source": [ + # "```python highlight-line=\"2\"\n", + # "5+5\n", + # "print(\"hello world\")\n", + # "```" + # ] + # } + # ], + # "metadata": { + # "language_info": { + # "name": "python" + # } + # }, + # "nbformat": 4, + # "nbformat_minor": 2 + # } + # - title: definition + # mdast: + # type: root + # children: + # - type: definition + # identifier: my-def + # label: My-Def + # url: https://example.com + # title: Example + # ipynb: |- + # { + # "cells": [ + # { + # "cell_type": "markdown", + # "metadata": {}, + # "source": [ + # "[My-Def]: https://example.com \"Example\"" + # ] + # } + # ], + # "metadata": { + # "language_info": { + # "name": "python" + # } + # }, + # "nbformat": 4, + # "nbformat_minor": 2 + # } + # - title: break + # mdast: + # type: root + # children: + # - type: paragraph + # children: + # - type: text + # value: Some markdown + # - type: break + # - type: text + # value: Some more markdown + # ipynb: |- + # { + # "cells": [ + # { + # "cell_type": "markdown", + # "metadata": {}, + # "source": [ + # "Some markdown\\\n", + # "Some more markdown" + # ] + # } + # ], + # "metadata": { + # "language_info": { + # "name": "python" + # } + # }, + # "nbformat": 4, + # "nbformat_minor": 2 + # } + # - title: link + # mdast: + # type: root + # children: + # - type: link + # url: https://example.com + # title: my link + # children: + # - type: text + # value: 'Some % ' + # - type: emphasis + # children: + # - type: text + # value: markdown + # ipynb: |- + # { + # "cells": [ + # { + # "cell_type": "markdown", + # "metadata": {}, + # "source": [ + # "[Some % *markdown*](https://example.com \"my link\")" + # ] + # } + # ], + # "metadata": { + # "language_info": { + # "name": "python" + # } + # }, + # "nbformat": 4, + # "nbformat_minor": 2 + # } + # - title: link reference + # mdast: + # type: root + # children: + # - type: linkReference + # identifier: my-link + # label: My-Link + # children: + # - type: text + # value: 'Some % ' + # - type: emphasis + # children: + # - type: text + # value: markdown + # ipynb: |- + # { + # "cells": [ + # { + # "cell_type": "markdown", + # "metadata": {}, + # "source": [ + # "[Some % *markdown*][My-Link]" + # ] + # } + # ], + # "metadata": { + # "language_info": { + # "name": "python" + # } + # }, + # "nbformat": 4, + # "nbformat_minor": 2 + # } + # - title: image reference + # mdast: + # type: root + # children: + # - type: imageReference + # identifier: my-image + # label: My-Image + # alt: Some text + # ipynb: |- + # { + # "cells": [ + # { + # "cell_type": "markdown", + # "metadata": {}, + # "source": [ + # "![Some text][My-Image]" + # ] + # } + # ], + # "metadata": { + # "language_info": { + # "name": "python" + # } + # }, + # "nbformat": 4, + # "nbformat_minor": 2 + # } diff --git a/packages/myst-to-ipynb/tests/run.spec.ts b/packages/myst-to-ipynb/tests/run.spec.ts index dc707e2b1..c128772da 100644 --- a/packages/myst-to-ipynb/tests/run.spec.ts +++ b/packages/myst-to-ipynb/tests/run.spec.ts @@ -3,11 +3,11 @@ import fs from 'node:fs'; import path from 'node:path'; import yaml from 'js-yaml'; import { unified } from 'unified'; -import mystToMd from '../src'; +import writeIpynb from '../src'; type TestCase = { title: string; - markdown: string; + ipynb: Record; mdast: Record; }; @@ -28,64 +28,43 @@ casesList.forEach(({ title, cases }) => { describe(title, () => { test.each(cases.map((c): [string, TestCase] => [c.title, c]))( '%s', - (_, { markdown, mdast }) => { - const pipe = unified().use(mystToMd); + (_, { ipynb, mdast }) => { + const pipe = unified().use(writeIpynb); pipe.runSync(mdast as any); const file = pipe.stringify(mdast as any); - expect(file.result).toEqual(markdown); + expect(JSON.parse(file.result)).toEqual(ipynb); }, ); }); }); -describe('myst-to-md frontmatter', () => { +describe('myst-to-ipynb frontmatter', () => { test('empty frontmatter passes', () => { - const pipe = unified().use(mystToMd, {}); + const pipe = unified().use(writeIpynb, {}); const mdast = { type: 'root', children: [{ type: 'paragraph', children: [{ type: 'text', value: 'Hello world!' }] }], }; pipe.runSync(mdast as any); const file = pipe.stringify(mdast as any); - expect(file.result).toEqual('Hello world!'); - }); - test('simple frontmatter passes', () => { - const pipe = unified().use(mystToMd, { title: 'My Title' }); - const mdast = { - type: 'root', - children: [{ type: 'paragraph', children: [{ type: 'text', value: 'Hello world!' }] }], - }; - pipe.runSync(mdast as any); - const file = pipe.stringify(mdast as any); - expect(file.result).toEqual('---\ntitle: My Title\n---\nHello world!'); - }); - test('frontmatter with licenses passes', () => { - const pipe = unified().use(mystToMd, { - title: 'My Title', - license: { - content: { - id: 'Apache-2.0', - name: 'Apache License 2.0', - url: 'https://opensource.org/licenses/Apache-2.0', - free: true, - osi: true, - }, - code: { - name: 'Creative Commons Attribution 3.0 Unported', - id: 'CC-BY-3.0', - CC: true, - url: 'https://creativecommons.org/licenses/by/3.0/', - }, - }, - }); - const mdast = { - type: 'root', - children: [{ type: 'paragraph', children: [{ type: 'text', value: 'Hello world!' }] }], - }; - pipe.runSync(mdast as any); - const file = pipe.stringify(mdast as any); - expect(file.result).toEqual( - '---\ntitle: My Title\nlicense:\n content: Apache-2.0\n code: CC-BY-3.0\n---\nHello world!', + expect(file.result).toEqual(`{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Hello world!" + ] + } + ], + "metadata": { + "language_info": { + "name": "python" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +}` ); }); });