From eadaefa62fce5038d76fb23a7a91c2cbae5eef52 Mon Sep 17 00:00:00 2001 From: Pooya Parsa Date: Mon, 19 Feb 2024 21:04:07 +0100 Subject: [PATCH] add tests for heading + fixes --- README.md | 22 ++-- src/parser/_utils.ts | 19 ++++ src/parser/parsers/md4w.ts | 6 +- src/parser/parsers/mdast.ts | 16 ++- test/fixtures/simple.md | 37 ++++-- test/snapshots/simple.gh.html | 32 ++++-- test/snapshots/simple.markdownit.json | 155 +++++++++++++++++++------ test/snapshots/simple.md4w.json | 157 ++++++++++++++++++++------ test/snapshots/simple.mdast.json | 155 +++++++++++++++++++------ 9 files changed, 457 insertions(+), 142 deletions(-) create mode 100644 src/parser/_utils.ts diff --git a/README.md b/README.md index 1758bff..1afc277 100644 --- a/README.md +++ b/README.md @@ -90,10 +90,10 @@ Format a string as a code block. **Example:** -```js +````js md.codeBlock('console.log("Hello, World!");', "js"); // => "```js\nconsole.log("Hello, World!");\n```" -``` +```` ### `heading(text, level)` @@ -149,6 +149,7 @@ Render a markdown link. md.link("https://www.google.com", "Google"); // => "[Google](https://www.google.com)" ``` + ```js md.link("https://www.google.com", "Google", { external: true }); // => "Google" @@ -164,6 +165,7 @@ Render a markdown ordered or unordered list. md.list(["Item 1", "Item 2", "Item 3"]); // => "- Item 1\n- Item 2\n- Item 3" ``` + ```js md.list(["Item 1", "Item 2", "Item 3"], { ordered: true }); // => "1. Item 1\n2. Item 2\n3. Item 3" @@ -188,17 +190,16 @@ Render a markdown table. ```js md.table({ - columns: ["Breed", "Origin", "Size", "Temperament"], - rows: [ - ["Abyssinian", "Egypt", "Medium", "Active"], - ["Aegean", "Greece", "Medium", "Active"], - ["American Bobtail", "United States", "Medium", "Active"], - ["Applehead Siamese", "Thailand", "Medium", "Active"], + columns: ["Breed", "Origin", "Size", "Temperament"], + rows: [ + ["Abyssinian", "Egypt", "Medium", "Active"], + ["Aegean", "Greece", "Medium", "Active"], + ["American Bobtail", "United States", "Medium", "Active"], + ["Applehead Siamese", "Thailand", "Medium", "Active"], ], }); ``` - @@ -247,8 +248,7 @@ const parser = await initMdAstParser(); const { tree } = parser.parse("# Hello, *world*!"); ``` - - + ## Development diff --git a/src/parser/_utils.ts b/src/parser/_utils.ts new file mode 100644 index 0000000..5b8366f --- /dev/null +++ b/src/parser/_utils.ts @@ -0,0 +1,19 @@ +export function mergeStrings>(arr: T): T { + const res = [] as unknown as T; + let strBuff: string[] = []; + for (const el of arr) { + if (typeof el === "string") { + strBuff.push(el); + } else { + if (strBuff.length > 0) { + res.push(strBuff.join("")); + strBuff = []; + } + res.push(el); + } + } + if (strBuff.length > 0) { + res.push(strBuff.join("")); + } + return res; +} diff --git a/src/parser/parsers/md4w.ts b/src/parser/parsers/md4w.ts index aecc8f9..bc05a2b 100644 --- a/src/parser/parsers/md4w.ts +++ b/src/parser/parsers/md4w.ts @@ -1,5 +1,6 @@ import type { MDTree, MDNode, Options } from "md4w"; import type { Node, Type, Parser, ParsedTree } from "../types"; +import { mergeStrings } from "../_utils"; /** * @@ -54,10 +55,7 @@ function _normalizeTree(tree: MDTree | MDNode): ParsedTree { node.children = node.type === "code" ? [child.children.join("")] - : _normalizeTree(child); - if (!node.children.some((n) => typeof n !== "string")) { - node.children = [node.children.join("")]; - } + : mergeStrings(_normalizeTree(child)); } if (child.props) { node.props = child.props as any; diff --git a/src/parser/parsers/mdast.ts b/src/parser/parsers/mdast.ts index 604cb70..646f6af 100644 --- a/src/parser/parsers/mdast.ts +++ b/src/parser/parsers/mdast.ts @@ -1,6 +1,7 @@ import type { RootContent, Root } from "mdast"; import type { Options } from "mdast-util-from-markdown"; import type { ParsedTree, Parser, Node, Type } from "../types"; +import { mergeStrings } from "../_utils"; /** * @@ -71,14 +72,9 @@ function _normalizeTree(_node: Root | RootContent): ParsedTree { } if ("children" in _node) { - node.children = _node.children.flatMap((c) => _normalizeTree(c)); - - if ( - node.type === "p" && - !node.children.some((c) => typeof c !== "string") - ) { - node.children = [node.children.join("")]; - } + node.children = mergeStrings( + _node.children.flatMap((c) => _normalizeTree(c)), + ); if (_node.type === "listItem") { node.children = node.children?.flatMap((c) => { @@ -108,7 +104,6 @@ const typeMap: Partial> = { inlineCode: "code", delete: "s", emphasis: "em", - heading: "h1", link: "a", listItem: "li", paragraph: "p", @@ -122,5 +117,8 @@ function getType(node: Root | RootContent): Type { if (node.type === "list") { return node.ordered ? "ol" : "ul"; } + if (node.type === "heading") { + return `h${node.depth}` as Type; + } return typeMap[node.type] || (node.type as Type); } diff --git a/test/fixtures/simple.md b/test/fixtures/simple.md index bfec821..a16064c 100644 --- a/test/fixtures/simple.md +++ b/test/fixtures/simple.md @@ -1,8 +1,17 @@ initial paragraph -# Title +# Markdown Testing Page -> blockquote +## Formatting + +**bold** +_emphasis_ + +## Links + +[link](https://example.com) + +## Code ```js import { foo } from "bar"; @@ -12,17 +21,29 @@ console.log(foo()); `inline code` -**bold** -_emphasis_ -[link](https://example.com) +## Lists 1. first `item` 2. second item - nested item ---- - -This is hardly a "corner case," for some reason. +## Image ![image alt](https://example.com/image.png "this is an image") ![](https://example.com/image.png) + +## Headings + +### h3 with `code` + +#### 🐈 h4 with emoji + +#### h5 with **bold** text + +###### h6 with [link](./link) + +## Other + +> blockquote + +This is hardly a "corner case," for some reason. diff --git a/test/snapshots/simple.gh.html b/test/snapshots/simple.gh.html index 2decfa3..f9e9f91 100644 --- a/test/snapshots/simple.gh.html +++ b/test/snapshots/simple.gh.html @@ -1,15 +1,16 @@ -

initial paragraph

-

Title

-
-

blockquote

-
+

initial paragraph

+

Markdown Testing Page

+

Formatting

+

bold +emphasis

+

Links

+

link

+

Code

import { foo } from "bar";
 
 console.log(foo());

inline code

-

bold -emphasis -link

+

Lists

  1. first item
  2. @@ -19,7 +20,18 @@

Image

image alt

+

Headings

+

h3 with code +

+

🐈 h4 with emoji

+

h5 with bold text

+
h6 with link +
+

Other

+
+

blockquote

+
+

This is hardly a "corner case," for some reason.

diff --git a/test/snapshots/simple.markdownit.json b/test/snapshots/simple.markdownit.json index 05822b7..6070be5 100644 --- a/test/snapshots/simple.markdownit.json +++ b/test/snapshots/simple.markdownit.json @@ -9,20 +9,59 @@ { "type": "h1", "children": [ - "Title" + "Markdown Testing Page" ] }, { - "type": "blockquote", + "type": "h2", + "children": [ + "Formatting" + ] + }, + { + "type": "p", "children": [ { - "type": "p", + "type": "strong", "children": [ - "blockquote" + "bold" + ] + }, + "\n", + { + "type": "em", + "children": [ + "emphasis" ] } ] }, + { + "type": "h2", + "children": [ + "Links" + ] + }, + { + "type": "p", + "children": [ + { + "type": "a", + "children": [ + "link" + ], + "props": { + "href": "https://example.com" + } + } + ] + }, + { + "type": "h2", + "children": [ + "Code" + ] + }, { "type": "code", "children": [ @@ -44,31 +83,9 @@ ] }, { - "type": "p", + "type": "h2", "children": [ - { - "type": "strong", - "children": [ - "bold" - ] - }, - "\n", - { - "type": "em", - "children": [ - "emphasis" - ] - }, - "\n", - { - "type": "a", - "children": [ - "link" - ], - "props": { - "href": "https://example.com" - } - } + "Lists" ] }, { @@ -106,12 +123,9 @@ ] }, { - "type": "hr" - }, - { - "type": "p", + "type": "h2", "children": [ - "This is hardly a \"corner case,\" for some reason." + "Image" ] }, { @@ -134,6 +148,81 @@ } } ] + }, + { + "type": "h2", + "children": [ + "Headings" + ] + }, + { + "type": "h3", + "children": [ + "h3 with ", + { + "type": "code", + "children": [ + "code" + ] + } + ] + }, + { + "type": "h4", + "children": [ + "🐈 h4 with emoji" + ] + }, + { + "type": "h4", + "children": [ + "h5 with ", + { + "type": "strong", + "children": [ + "bold" + ] + }, + " text" + ] + }, + { + "type": "h6", + "children": [ + "h6 with ", + { + "type": "a", + "children": [ + "link" + ], + "props": { + "href": "./link" + } + } + ] + }, + { + "type": "h2", + "children": [ + "Other" + ] + }, + { + "type": "blockquote", + "children": [ + { + "type": "p", + "children": [ + "blockquote" + ] + } + ] + }, + { + "type": "p", + "children": [ + "This is hardly a \"corner case,\" for some reason." + ] } ] } \ No newline at end of file diff --git a/test/snapshots/simple.md4w.json b/test/snapshots/simple.md4w.json index 05822b7..996c375 100644 --- a/test/snapshots/simple.md4w.json +++ b/test/snapshots/simple.md4w.json @@ -9,20 +9,59 @@ { "type": "h1", "children": [ - "Title" + "Markdown Testing Page" ] }, { - "type": "blockquote", + "type": "h2", + "children": [ + "Formatting" + ] + }, + { + "type": "p", "children": [ { - "type": "p", + "type": "strong", "children": [ - "blockquote" + "bold" + ] + }, + "\n", + { + "type": "em", + "children": [ + "emphasis" ] } ] }, + { + "type": "h2", + "children": [ + "Links" + ] + }, + { + "type": "p", + "children": [ + { + "type": "a", + "children": [ + "link" + ], + "props": { + "href": "https://example.com" + } + } + ] + }, + { + "type": "h2", + "children": [ + "Code" + ] + }, { "type": "code", "children": [ @@ -44,31 +83,9 @@ ] }, { - "type": "p", + "type": "h2", "children": [ - { - "type": "strong", - "children": [ - "bold" - ] - }, - "\n", - { - "type": "em", - "children": [ - "emphasis" - ] - }, - "\n", - { - "type": "a", - "children": [ - "link" - ], - "props": { - "href": "https://example.com" - } - } + "Lists" ] }, { @@ -106,12 +123,9 @@ ] }, { - "type": "hr" - }, - { - "type": "p", + "type": "h2", "children": [ - "This is hardly a \"corner case,\" for some reason." + "Image" ] }, { @@ -134,6 +148,81 @@ } } ] + }, + { + "type": "h2", + "children": [ + "Headings" + ] + }, + { + "type": "h3", + "children": [ + "h3 with ", + { + "type": "code", + "children": [ + "code" + ] + } + ] + }, + { + "type": "h4", + "children": [ + "🐈 h4 with emoji" + ] + }, + { + "type": "h4", + "children": [ + "h5 with ", + { + "type": "strong", + "children": [ + "bold" + ] + }, + " text" + ] + }, + { + "type": "h6", + "children": [ + "h6 with ", + { + "type": "a", + "children": [ + "link" + ], + "props": { + "href": "./link" + } + } + ] + }, + { + "type": "h2", + "children": [ + "Other" + ] + }, + { + "type": "blockquote", + "children": [ + { + "type": "p", + "children": [ + "blockquote" + ] + } + ] + }, + { + "type": "p", + "children": [ + "This is hardly a \"corner case,\" for some reason." + ] } ] -} \ No newline at end of file +} diff --git a/test/snapshots/simple.mdast.json b/test/snapshots/simple.mdast.json index 05822b7..6070be5 100644 --- a/test/snapshots/simple.mdast.json +++ b/test/snapshots/simple.mdast.json @@ -9,20 +9,59 @@ { "type": "h1", "children": [ - "Title" + "Markdown Testing Page" ] }, { - "type": "blockquote", + "type": "h2", + "children": [ + "Formatting" + ] + }, + { + "type": "p", "children": [ { - "type": "p", + "type": "strong", "children": [ - "blockquote" + "bold" + ] + }, + "\n", + { + "type": "em", + "children": [ + "emphasis" ] } ] }, + { + "type": "h2", + "children": [ + "Links" + ] + }, + { + "type": "p", + "children": [ + { + "type": "a", + "children": [ + "link" + ], + "props": { + "href": "https://example.com" + } + } + ] + }, + { + "type": "h2", + "children": [ + "Code" + ] + }, { "type": "code", "children": [ @@ -44,31 +83,9 @@ ] }, { - "type": "p", + "type": "h2", "children": [ - { - "type": "strong", - "children": [ - "bold" - ] - }, - "\n", - { - "type": "em", - "children": [ - "emphasis" - ] - }, - "\n", - { - "type": "a", - "children": [ - "link" - ], - "props": { - "href": "https://example.com" - } - } + "Lists" ] }, { @@ -106,12 +123,9 @@ ] }, { - "type": "hr" - }, - { - "type": "p", + "type": "h2", "children": [ - "This is hardly a \"corner case,\" for some reason." + "Image" ] }, { @@ -134,6 +148,81 @@ } } ] + }, + { + "type": "h2", + "children": [ + "Headings" + ] + }, + { + "type": "h3", + "children": [ + "h3 with ", + { + "type": "code", + "children": [ + "code" + ] + } + ] + }, + { + "type": "h4", + "children": [ + "🐈 h4 with emoji" + ] + }, + { + "type": "h4", + "children": [ + "h5 with ", + { + "type": "strong", + "children": [ + "bold" + ] + }, + " text" + ] + }, + { + "type": "h6", + "children": [ + "h6 with ", + { + "type": "a", + "children": [ + "link" + ], + "props": { + "href": "./link" + } + } + ] + }, + { + "type": "h2", + "children": [ + "Other" + ] + }, + { + "type": "blockquote", + "children": [ + { + "type": "p", + "children": [ + "blockquote" + ] + } + ] + }, + { + "type": "p", + "children": [ + "This is hardly a \"corner case,\" for some reason." + ] } ] } \ No newline at end of file