From 2e91d6dde86cd0140b305175f1799a662cd2addf Mon Sep 17 00:00:00 2001 From: Paul Meinhardt Date: Tue, 26 Oct 2021 07:24:25 +0200 Subject: [PATCH] [#5] Support more node types MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit …and handle unknown ones more gracefully. --- index.test.ts | 569 +++++++++++++++++++++++++++++++++++++++++++++- index.ts | 333 ++++++++++++++++++++------- package-lock.json | 12 +- package.json | 6 +- 4 files changed, 812 insertions(+), 108 deletions(-) diff --git a/index.test.ts b/index.test.ts index 09841ac..da07661 100644 --- a/index.test.ts +++ b/index.test.ts @@ -1,4 +1,7 @@ -import type { DocNode as ADFDoc } from "@atlaskit/adf-schema"; +import type { + DocNode as ADFDoc, + PanelType as ADFPanelType, +} from "@atlaskit/adf-schema"; import { u } from "unist-builder"; import convert from "."; @@ -7,29 +10,100 @@ function doc(content: ADFDoc["content"]): ADFDoc { return { version: 1, type: "doc", content }; } -it("converts empty document", () => { +it("converts empty documents", () => { expect(convert(doc([]))).toEqual(u("root", [])); }); -it("converts simple document", () => { +it("converts simple documents", () => { expect( convert( doc([ { type: "paragraph", content: [ + { type: "text", text: "Hello " }, + { type: "text", text: "World", marks: [{ type: "strong" }] }, + ], + }, + ]) + ) + ).toEqual( + u("root", [ + u("paragraph", [u("text", "Hello "), u("strong", [u("text", "World")])]), + ]) + ); +}); + +( + [ + ["strong text", "strong", "strong"], + ["emphasized text", "em", "emphasis"], + ["strikethrough text", "strike", "delete"], + ] as const +).forEach(([description, mark, type]) => { + it(`converts ${description}`, () => { + expect( + convert( + doc([ + { + type: "paragraph", + content: [ + { type: "text", text: "This is " }, + { type: "text", text: description, marks: [{ type: mark }] }, + ], + }, + ]) + ) + ).toEqual( + u("root", [ + u("paragraph", [ + u("text", "This is "), + u(type, [u("text", description)]), + ]), + ]) + ); + }); +}); + +it("converts strong emphasized text", () => { + expect( + convert( + doc([ + { + type: "paragraph", + content: [ + { type: "text", text: "This is " }, { type: "text", - text: "Hello ", + text: "strong & emphasized", + marks: [{ type: "em" }, { type: "strong" }], }, + ], + }, + ]) + ) + ).toEqual( + u("root", [ + u("paragraph", [ + u("text", "This is "), + u("strong", [u("emphasis", [u("text", "strong & emphasized")])]), + ]), + ]) + ); +}); + +it("converts inline code", () => { + expect( + convert( + doc([ + { + type: "paragraph", + content: [ + { type: "text", text: "This is " }, { type: "text", - text: "World", - marks: [ - { - type: "strong", - }, - ], + text: "code", + marks: [{ type: "code" }], }, ], }, @@ -37,7 +111,480 @@ it("converts simple document", () => { ) ).toEqual( u("root", [ - u("paragraph", [u("text", "Hello "), u("strong", [u("text", "World")])]), + u("paragraph", [u("text", "This is "), u("inlineCode", "code")]), + ]) + ); +}); + +( + [ + ["subscript text", "sub"], + ["superscript text", "sup"], + ] as const +).forEach(([description, type]) => { + it(`converts ${description}`, () => { + expect( + convert( + doc([ + { + type: "paragraph", + content: [ + { + type: "text", + text: "x", + }, + { + type: "text", + text: "2", + marks: [{ type: "subsup", attrs: { type } }], + }, + ], + }, + ]) + ) + ).toEqual( + u("root", [ + u("paragraph", [u("text", "x"), u("html", `<${type}>2`)]), + ]) + ); + }); +}); + +it("converts links", () => { + const url = "https://github.com/bitcrowd/mdast-util-from-adf"; + + expect( + convert( + doc([ + { + type: "paragraph", + content: [ + { + type: "text", + text: "GitHub", + marks: [{ type: "link", attrs: { href: url } }], + }, + ], + }, + ]) + ) + ).toEqual( + u("root", [u("paragraph", [u("link", { url }, [u("text", "GitHub")])])]) + ); +}); + +[1, 2, 3, 4, 5, 6].forEach((level) => { + it(`converts headings (${level})`, () => { + expect( + convert( + doc([ + { + type: "heading", + attrs: { level }, + content: [{ type: "text", text: "Heading" }], + }, + ]) + ) + ).toEqual( + u("root", [u("heading", { depth: level }, [u("text", "Heading")])]) + ); + }); +}); + +it("converts code blocks", () => { + expect( + convert( + doc([ + { + type: "codeBlock", + attrs: { + language: "typescript", + }, + content: [ + { + type: "text", + text: 'import { fromADF } from "mdast-util-from-adf";', + }, + ], + }, + ]) + ) + ).toEqual( + u("root", [ + u( + "code", + { lang: "typescript" }, + 'import { fromADF } from "mdast-util-from-adf";' + ), + ]) + ); +}); + +( + [ + ["lists (bullet)", "bulletList", false], + ["lists (ordered)", "orderedList", true], + ] as const +).forEach(([description, type, ordered]) => { + it(`converts ${description}`, () => { + expect( + convert( + doc([ + { + type, + content: [ + { + type: "listItem", + content: [ + { + type: "paragraph", + content: [{ type: "text", text: "one" }], + }, + ], + }, + { + type: "listItem", + content: [ + { + type: "paragraph", + content: [{ type: "text", text: "two" }], + }, + ], + }, + ], + }, + ]) + ) + ).toEqual( + u("root", [ + u("list", { ordered, spread: false }, [ + u("listItem", { spread: false }, [ + u("paragraph", [u("text", "one")]), + ]), + u("listItem", { spread: false }, [ + u("paragraph", [u("text", "two")]), + ]), + ]), + ]) + ); + }); +}); + +it("converts lists (task)", () => { + expect( + convert( + doc([ + { + type: "taskList", + attrs: { + localId: "11244654-2201-4973-b974-522b697d2327", + }, + content: [ + { + type: "taskItem", + attrs: { + localId: "8acdda43-05dd-4eb0-881d-33e7c59c699f", + state: "DONE", + }, + content: [{ type: "text", text: "Completed" }], + }, + { + type: "taskItem", + attrs: { + localId: "89571672-56da-406b-a8b8-6c9156556167", + state: "TODO", + }, + content: [{ type: "text", text: "To be done" }], + }, + ], + }, + ]) + ) + ).toEqual( + u("root", [ + u("list", { ordered: false, spread: false }, [ + u("listItem", { checked: true, spread: false }, [ + u("paragraph", [u("text", "Completed")]), + ]), + u("listItem", { checked: false, spread: false }, [ + u("paragraph", [u("text", "To be done")]), + ]), + ]), + ]) + ); +}); + +it("converts lists (decision)", () => { + expect( + convert( + doc([ + { + type: "decisionList", + attrs: { + localId: "11244654-2201-4973-b974-522b697d2327", + }, + content: [ + { + type: "decisionItem", + attrs: { + localId: "8acdda43-05dd-4eb0-881d-33e7c59c699f", + state: "DECIDED", + }, + content: [{ type: "text", text: "Decided" }], + }, + { + type: "decisionItem", + attrs: { + localId: "89571672-56da-406b-a8b8-6c9156556167", + state: "UNDECIDED", + }, + content: [{ type: "text", text: "Undecided" }], + }, + ], + }, + ]) + ) + ).toEqual( + u("root", [ + u("list", { ordered: false, spread: false }, [ + u("listItem", { checked: true, spread: false }, [ + u("paragraph", [u("text", "Decided")]), + ]), + u("listItem", { checked: false, spread: false }, [ + u("paragraph", [u("text", "Undecided")]), + ]), + ]), ]) ); }); + +it("converts block quotes", () => { + const quote = "I think; therefore I am."; + + expect( + convert( + doc([ + { + type: "blockquote", + content: [ + { type: "paragraph", content: [{ type: "text", text: quote }] }, + ], + }, + ]) + ) + ).toEqual(u("root", [u("blockquote", [u("paragraph", [u("text", quote)])])])); +}); + +it("converts dividers", () => { + expect( + convert( + doc([ + { type: "paragraph", content: [{ type: "text", text: "Before" }] }, + { type: "rule" }, + { type: "paragraph", content: [{ type: "text", text: "After" }] }, + ]) + ) + ).toEqual( + u("root", [ + u("paragraph", [u("text", "Before")]), + u("thematicBreak"), + u("paragraph", [u("text", "After")]), + ]) + ); +}); + +it("converts emoji", () => { + expect( + convert( + doc([ + { + type: "paragraph", + content: [ + { + type: "emoji", + attrs: { + shortName: ":projector:", + id: "1f4fd", + text: "📽", + }, + }, + { + type: "emoji", + attrs: { + shortName: ":boom:", + id: "0000", + }, + }, + ], + }, + ]) + ) + ).toEqual(u("root", [u("paragraph", [u("text", "📽"), u("text", ":boom:")])])); +}); + +it("converts mentions", () => { + expect( + convert( + doc([ + { + type: "paragraph", + content: [ + { + type: "mention", + attrs: { + id: "557058:aafbc62f-3aa7-444a-8c18-42168d05183d", + text: "Alison", + accessLevel: "", + }, + }, + ], + }, + ]) + ) + ).toEqual(u("root", [u("paragraph", [u("text", "@Alison")])])); +}); + +it("converts dates", () => { + const timestamp = "2021-11-03T12:54:08.354Z"; + + expect( + convert( + doc([ + { + type: "paragraph", + content: [{ type: "date", attrs: { timestamp } }], + }, + ]) + ) + ).toEqual(u("root", [u("paragraph", [u("text", timestamp)])])); +}); + +( + [ + ["media (single)", "mediaSingle"], + ["media (group)", "mediaGroup"], + ] as const +).forEach(([description, type]) => { + it(`converts ${description}`, () => { + expect( + convert( + doc([ + { + type, + attrs: { layout: "center" }, + content: [ + { + type: "media", + attrs: { + id: "4b3fc7ea-3a65-4dda-8ae4-0c0576a6b9d3", + type: "file", + collection: "", + width: 1632, + height: 1372, + }, + }, + ], + }, + ]) + ) + ).toEqual( + u("root", [ + u("html", ""), + ]) + ); + }); +}); + +it("converts cards (block)", () => { + const url = "https://github.com/bitcrowd/mdast-util-from-adf"; + + expect( + convert( + doc([ + { + type: "blockCard", + attrs: { + url, + }, + }, + ]) + ) + ).toEqual(u("root", [u("paragraph", [u("text", url)])])); +}); + +it("converts cards (inline)", () => { + const url = "https://github.com/bitcrowd/mdast-util-from-adf"; + + expect( + convert( + doc([ + { + type: "paragraph", + content: [{ type: "inlineCard", attrs: { url } }], + }, + ]) + ) + ).toEqual(u("root", [u("paragraph", [u("text", url)])])); +}); + +it("converts cards (embed)", () => { + const url = "https://github.com/bitcrowd/mdast-util-from-adf"; + + expect( + convert( + doc([ + { + type: "embedCard", + attrs: { layout: "center", url }, + }, + ]) + ) + ).toEqual(u("root", [u("link", { url }, [u("text", url)])])); +}); + +(["info", "note", "success", "warning", "error"] as ADFPanelType[]).forEach( + (type) => { + it(`converts panels (${type})`, () => { + expect( + convert( + doc([ + { + type: "panel", + attrs: { panelType: type }, + content: [ + { + type: "paragraph", + content: [{ type: "text", text: "Some panel text" }], + }, + ], + }, + ]) + ) + ).toEqual(u("root", [u("paragraph", [u("text", "Some panel text")])])); + }); + } +); + +it("converts layout containers", () => { + expect( + convert( + doc([ + { + type: "layoutSection", + content: [ + { + type: "layoutColumn", + attrs: { width: 100 }, + content: [ + { + type: "paragraph", + content: [{ type: "text", text: "Content" }], + }, + ], + }, + ], + }, + ]) + ) + ).toEqual(u("root", [u("paragraph", [u("text", "Content")])])); +}); diff --git a/index.ts b/index.ts index 9568063..797535d 100644 --- a/index.ts +++ b/index.ts @@ -1,118 +1,285 @@ import type { + BlockCardDefinition as ADFBlockCard, BlockContent as ADFBlockContent, + BlockQuoteDefinition as ADFBlockQuote, + BulletListDefinition as ADFBulletList, + CodeBlockBaseDefinition as ADFCodeBlock, + DateDefinition as ADFDate, + DecisionItemDefinition as ADFDecisionItem, + DecisionListDefinition as ADFDecisionList, DocNode as ADFDoc, - HeadingDefinition as ADFHeading, + EmbedCardDefinition as ADFEmbedCard, + EmojiDefinition as ADFEmoji, + HeadingBaseDefinition as ADFHeading, Inline as ADFInlineContent, + InlineCardDefinition as ADFInlineCard, LayoutColumnDefinition as ADFLayoutColumn, + LayoutSectionDefinition as ADFLayoutSection, ListItemDefinition as ADFListItem, MediaDefinition as ADFMedia, + MentionDefinition as ADFMention, + NestedExpandDefinition as ADFNestedExpand, + OrderedListDefinition as ADFOrderedList, + PanelDefinition as ADFPanel, + ParagraphBaseDefinition as ADFParagraph, + RuleDefinition as ADFRule, TableCellDefinition as ADFTableCell, + TableDefinition as ADFTable, TableHeaderDefinition as ADFTableHeader, TableRowDefinition as ADFTableRow, + TaskItemDefinition as ADFTaskItem, + TaskListDefinition as ADFTaskList, TextDefinition as ADFText, } from "@atlaskit/adf-schema"; import type { + Blockquote as MDASTBlockquote, Content as MDASTContent, + Delete as MDASTDelete, + Emphasis as MDASTEmphasis, + Heading as MDASTHeading, Link as MDASTLink, + List as MDASTList, + ListItem as MDASTListItem, + Literal as MDASTLiteral, + Paragraph as MDASTParagraph, Parent as MDASTParent, - PhrasingContent as MDASTPhrasingContent, Root as MDASTRoot, + Strong as MDASTStrong, + Table as MDASTTable, + TableCell as MDASTTableCell, + TableRow as MDASTTableRow, } from "mdast"; +import { u } from "unist-builder"; -type ADFMark = { type: string; attrs?: Attributes }; type ADFNode = | ADFDoc["content"][number] + | ADFBlockCard | ADFBlockContent + | ADFBlockQuote + | ADFBulletList + | ADFCodeBlock + | ADFDate + | ADFDecisionItem + | ADFDecisionList + | ADFEmbedCard + | ADFEmoji + | ADFHeading + | ADFInlineCard | ADFInlineContent | ADFLayoutColumn + | ADFLayoutSection | ADFListItem | ADFMedia + | ADFMention + | ADFNestedExpand + | ADFOrderedList + | ADFPanel + | ADFParagraph + | ADFRule + | ADFTable | ADFTableCell | ADFTableHeader - | ADFTableRow; + | ADFTableRow + | ADFTaskItem + | ADFTaskList + | ADFText; type ADFType = ADFNode["type"]; -type MDASTNode = MDASTRoot | MDASTContent; -type MDASTParents = Extract; +type MDASTNode = MDASTParent | MDASTLiteral; +type MDASTParents = Extract; type MDASTParentNode = MDASTParents & { children: MDASTContent[] }; -type StackEntry = [Node, ADFNode[]]; +type StackEntry = [ADFNode, Node][]; type Stack = [StackEntry, ...StackEntry[]]; -const mappings: Record MDASTContent) | undefined> = { - blockCard: undefined, - blockquote: () => ({ type: "blockquote", children: [] }), +const mappings: Record< + ADFType, + | ((_: any, __: MDASTParentNode) => StackEntry | void) + | undefined +> = { + blockCard: (adf: ADFBlockCard, parent) => { + parent.children.push( + u("paragraph", [ + u( + "text", + "url" in adf.attrs ? adf.attrs.url : JSON.stringify(adf.attrs.data) + ), + ]) + ); + }, + blockquote: (adf: ADFBlockQuote, parent) => { + const node: MDASTBlockquote = u("blockquote", []); + parent.children.push(node); + return adf.content.map((n) => [n, node]); + }, bodiedExtension: undefined, - bulletList: () => ({ type: "list", ordered: false, children: [] }), - codeBlock: () => ({ type: "code", value: "" }), - date: undefined, - decisionList: undefined, - embedCard: undefined, - emoji: undefined, + bulletList: (adf: ADFBulletList, parent) => { + // TODO: Can we share code with ordered list? + const node: MDASTList = u("list", { ordered: false, spread: false }, []); + parent.children.push(node); + return adf.content.map((n) => [n, node]); + }, + codeBlock: (adf: ADFCodeBlock, parent) => { + const value = adf.content?.[0]?.text ?? ""; + const lang = adf.attrs?.language; + parent.children.push(u("code", { lang }, value)); + }, + date: (adf: ADFDate, parent) => { + parent.children.push(u("text", adf.attrs.timestamp)); + }, + decisionItem: (adf: ADFDecisionItem, parent) => { + // TODO: See whether we can refactor this, task item is similar + const paragraph: MDASTParagraph = u("paragraph", []); + const node: MDASTListItem = u( + "listItem", + { spread: false, checked: adf.attrs.state === "DECIDED" }, + [paragraph] + ); + parent.children.push(node); + return adf.content?.map((n) => [n, paragraph]); + }, + decisionList: (adf: ADFDecisionList, parent) => { + // TODO: See whether we can refactor this, task list is similar + const node: MDASTList = u("list", { ordered: false, spread: false }, []); + parent.children.push(node); + return adf.content.map((n) => [n, node]); + }, + embedCard: (adf: ADFEmbedCard, parent) => { + const { url } = adf.attrs; + const link = u("link", { url }, [u("text", url)]); + parent.children.push(link); + }, + emoji: (adf: ADFEmoji, parent) => { + parent.children.push(u("text", adf.attrs.text ?? adf.attrs.shortName)); + }, expand: undefined, extension: undefined, - hardBreak: () => ({ type: "break" }), - heading: (adf: ADFHeading) => ({ - type: "heading", - depth: adf.attrs.level as 1 | 2 | 3 | 4 | 5 | 6, - children: [], - }), - inlineCard: undefined, + hardBreak: (adf, parent) => { + parent.children.push(u("break")); + }, + heading: (adf: ADFHeading, parent) => { + const depth = adf.attrs.level as 1 | 2 | 3 | 4 | 5 | 6; + const node: MDASTHeading = u("heading", { depth }, []); + parent.children.push(node); + return adf.content?.map((n) => [n, node]); + }, + inlineCard: (adf: ADFInlineCard, parent) => { + parent.children.push( + u( + "text", + "url" in adf.attrs ? adf.attrs.url : JSON.stringify(adf.attrs.data) + ) + ); + }, inlineExtension: undefined, - layoutColumn: undefined, - layoutSection: undefined, - listItem: () => ({ type: "listItem", children: [] }), - media: undefined, - mediaGroup: undefined, + layoutColumn: (adf: ADFLayoutColumn, parent) => { + return adf.content.map((n) => [n, parent]); + }, + layoutSection: (adf: ADFLayoutSection, parent) => { + return adf.content.map((n) => [n, parent]); + }, + listItem: (adf: ADFListItem, parent) => { + const node: MDASTListItem = u("listItem", { spread: false }, []); + parent.children.push(node); + return adf.content.map((n) => [n, node]); + }, + media: (adf: ADFMedia, parent) => { + const key = "url" in adf.attrs ? adf.attrs.url : adf.attrs.id; + parent.children.push(u("html", ``)); + }, + mediaGroup: (adf, parent) => { + return adf.content.map((n: ADFMedia) => [n, parent]); + }, mediaInline: undefined, - mediaSingle: undefined, - mention: undefined, - orderedList: () => ({ type: "list", ordered: true, children: [] }), - panel: undefined, - paragraph: () => ({ type: "paragraph", children: [] }), + mediaSingle: (adf, parent) => { + return adf.content.map((n: ADFMedia) => [n, parent]); + }, + mention: (adf: ADFMention, parent) => { + parent.children.push(u("text", `@${adf.attrs.text}`)); + }, + nestedExpand: undefined, + orderedList: (adf: ADFOrderedList, parent) => { + const node: MDASTList = u("list", { ordered: true, spread: false }, []); + parent.children.push(node); + return adf.content.map((n) => [n, node]); + }, + panel: (adf: ADFPanel, parent) => { + return adf.content.map((n) => [n, parent]); + }, + paragraph: (adf: ADFParagraph, parent) => { + const node: MDASTParagraph = u("paragraph", []); + parent.children.push(node); + return adf.content?.map((n) => [n, node]); + }, placeholder: undefined, - rule: () => ({ type: "thematicBreak" }), + rule: (adf: ADFRule, parent) => { + parent.children.push(u("thematicBreak")); + }, status: undefined, - table: () => ({ type: "table", children: [] }), - tableCell: () => ({ type: "tableCell", children: [] }), - tableHeader: () => ({ type: "tableCell", children: [] }), - tableRow: () => ({ type: "tableRow", children: [] }), - taskList: undefined, - text: (adf: ADFText) => - mark({ type: "text", value: adf.text }, adf.marks ?? []), -}; + table: (adf: ADFTable, parent) => { + const node: MDASTTable = u("table", []); + parent.children.push(node); + return adf.content.map((n) => [n, node]); + }, + tableCell: (adf: ADFTableCell, parent) => { + const node: MDASTTableCell = u("tableCell", []); + parent.children.push(node); + return adf.content.map((n) => [n, node]); + }, + tableHeader: (adf: ADFTableHeader, parent) => { + const node: MDASTTableCell = u("tableCell", []); + parent.children.push(node); + return adf.content.map((n) => [n, node]); + }, + tableRow: (adf: ADFTableRow, parent) => { + const node: MDASTTableRow = u("tableRow", []); + parent.children.push(node); + return adf.content.map((n) => [n, node]); + }, + taskItem: (adf: ADFTaskItem, parent) => { + const paragraph: MDASTParagraph = u("paragraph", []); + const node: MDASTListItem = u( + "listItem", + { spread: false, checked: adf.attrs.state === "DONE" }, + [paragraph] + ); + parent.children.push(node); + return adf.content?.map((n) => [n, paragraph]); + }, + taskList: (adf: ADFTaskList, parent) => { + const node: MDASTList = u("list", { ordered: false, spread: false }, []); + parent.children.push(node); + return adf.content.map((n) => [n, node]); + }, + text: (adf: ADFText, parent) => { + const { text, marks = [] } = adf; -/* eslint-disable no-unused-vars */ -type Marker = ( - node: MDASTPhrasingContent, - mark: ADFMark -) => MDASTPhrasingContent; -/* eslint-enable no-unused-vars */ - -const markers: Record = { - // markers which change node type - these should be applied first - code: (node) => ("value" in node ? { ...node, type: "inlineCode" } : node), - - // markers which wrap the node - em: (node) => ({ type: "emphasis", children: [node] }), - link: (node, mark: ADFMark<{ href: string }>) => ({ - type: "link", - url: mark.attrs!.href, - children: [node as MDASTLink["children"][number]], - }), - strike: (node) => ({ type: "delete", children: [node] }), // GFM - strong: (node) => ({ type: "strong", children: [node] }), -}; + const leaf: MDASTLiteral = u("text", adf.text); + let node = leaf as MDASTContent; -const order = Object.keys(markers); + marks.forEach((mark) => { + const { type, attrs } = mark; -function mark(node: MDASTPhrasingContent, marks: ADFMark[] = []) { - return marks - .slice() - .filter((m) => order.includes(m.type)) - .sort((m1, m2) => order.indexOf(m1.type) - order.indexOf(m2.type)) - .reduce((n, m) => markers[m.type](n, m), node); -} + if (type === "code") { + leaf.type = "inlineCode"; + } else { + if (type === "em") { + node = u("emphasis", [node]) as MDASTEmphasis; + } else if (type === "strong") { + node = u("strong", [node]) as MDASTStrong; + } else if (type === "strike") { + node = u("delete", [node]) as MDASTDelete; + } else if (type === "link") { + node = u("link", { url: attrs.href }, [node]) as MDASTLink; + } else if (type === "subsup") { + node = u("html", `<${attrs.type}>${text}`); + } + } + }); + + parent.children.push(node); + }, +}; class AssertionError extends Error {} @@ -121,36 +288,28 @@ function assert(value: unknown, message = ""): asserts value { } export default function convert(doc: ADFDoc): MDASTRoot { - assert(doc.version === 1, "unknown document version"); + assert(doc.version === 1, `unknown document version ${doc.version}`); - const tree: MDASTRoot = { type: "root", children: [] }; - const stack: Stack = [[tree, doc.content]]; + const tree: MDASTRoot = u("root", []); + const stack: Stack = [doc.content.map((adf) => [adf, tree])]; while (stack.length > 0) { const index = stack.length - 1; - const [node, queue] = stack[index]; + const queue = stack[index]; if (queue.length === 0) { - if (index === 0) break; // root node queue is empty, we’re done - const parent = stack[index - 1][0]; - parent.children.push(node as MDASTParentNode); stack.pop(); continue; } - const adf = queue.shift()!; + const [adf, parent] = queue.shift()!; const map = mappings[adf.type]; - assert(map, "unsupported node type"); + assert(map, `unsupported node type "${adf.type}"`); - const mapped = map(adf); + const entry = map(adf, parent); - if ("children" in mapped && "content" in adf) { - const content = adf.content as Array; - stack.push([mapped, content]); - } else { - node.children.push(mapped); - } + if (entry) stack.push(entry); } return tree; diff --git a/package-lock.json b/package-lock.json index 6cb87fb..e25e82b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,16 +1,17 @@ { "name": "mdast-util-from-adf", - "version": "1.0.0", + "version": "1.0.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "mdast-util-from-adf", - "version": "1.0.0", + "version": "1.0.1", "license": "MIT", "dependencies": { "@atlaskit/adf-schema": "^19.2.0", - "@types/mdast": "^3.0.0" + "@types/mdast": "^3.0.0", + "unist-builder": "^3.0.0" }, "devDependencies": { "@babel/preset-env": "^7.15.8", @@ -25,8 +26,7 @@ "eslint-plugin-simple-import-sort": "^7.0.0", "jest": "^27.2.5", "prettier": "^2.4.1", - "typescript": "^4.4.4", - "unist-builder": "^3.0.0" + "typescript": "^4.4.4" } }, "node_modules/@atlaskit/adf-schema": { @@ -7151,7 +7151,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/unist-builder/-/unist-builder-3.0.0.tgz", "integrity": "sha512-GFxmfEAa0vi9i5sd0R2kcrI9ks0r82NasRq5QHh2ysGngrc6GiqD5CDf1FjPenY4vApmFASBIIlk/jj5J5YbmQ==", - "dev": true, "dependencies": { "@types/unist": "^2.0.0" }, @@ -12740,7 +12739,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/unist-builder/-/unist-builder-3.0.0.tgz", "integrity": "sha512-GFxmfEAa0vi9i5sd0R2kcrI9ks0r82NasRq5QHh2ysGngrc6GiqD5CDf1FjPenY4vApmFASBIIlk/jj5J5YbmQ==", - "dev": true, "requires": { "@types/unist": "^2.0.0" } diff --git a/package.json b/package.json index 3bec437..b03561b 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,8 @@ }, "dependencies": { "@atlaskit/adf-schema": "^19.2.0", - "@types/mdast": "^3.0.0" + "@types/mdast": "^3.0.0", + "unist-builder": "^3.0.0" }, "devDependencies": { "@babel/preset-env": "^7.15.8", @@ -34,7 +35,6 @@ "eslint-plugin-simple-import-sort": "^7.0.0", "jest": "^27.2.5", "prettier": "^2.4.1", - "typescript": "^4.4.4", - "unist-builder": "^3.0.0" + "typescript": "^4.4.4" } }