@knpkv/confluence-to-markdown 0.5.0 → 0.6.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/CHANGELOG.md +22 -0
- package/README.md +45 -10
- package/dist/ConfluenceAuth.d.ts.map +1 -1
- package/dist/ConfluenceAuth.js +12 -22
- package/dist/ConfluenceAuth.js.map +1 -1
- package/dist/ConfluenceClient.d.ts +13 -3
- package/dist/ConfluenceClient.d.ts.map +1 -1
- package/dist/ConfluenceClient.js +34 -70
- package/dist/ConfluenceClient.js.map +1 -1
- package/dist/ConfluenceError.d.ts +12 -12
- package/dist/GitError.d.ts +5 -5
- package/dist/GitService.d.ts.map +1 -1
- package/dist/GitService.js +0 -3
- package/dist/GitService.js.map +1 -1
- package/dist/SchemaConverterError.d.ts +3 -3
- package/dist/ast/BlockNode.d.ts +48 -33
- package/dist/ast/BlockNode.d.ts.map +1 -1
- package/dist/ast/BlockNode.js +11 -2
- package/dist/ast/BlockNode.js.map +1 -1
- package/dist/ast/Document.d.ts +30 -2
- package/dist/ast/Document.d.ts.map +1 -1
- package/dist/parsers/ConfluenceParser.d.ts.map +1 -1
- package/dist/parsers/ConfluenceParser.js +7 -12
- package/dist/parsers/ConfluenceParser.js.map +1 -1
- package/dist/parsers/MarkdownParser.js +8 -117
- package/dist/parsers/MarkdownParser.js.map +1 -1
- package/dist/parsers/preprocessing/ConfluencePreprocessing.d.ts +23 -0
- package/dist/parsers/preprocessing/ConfluencePreprocessing.d.ts.map +1 -0
- package/dist/parsers/preprocessing/ConfluencePreprocessing.js +323 -0
- package/dist/parsers/preprocessing/ConfluencePreprocessing.js.map +1 -0
- package/dist/parsers/preprocessing/index.d.ts +7 -0
- package/dist/parsers/preprocessing/index.d.ts.map +1 -0
- package/dist/parsers/preprocessing/index.js +7 -0
- package/dist/parsers/preprocessing/index.js.map +1 -0
- package/dist/schemas/preprocessing/ConfluencePreprocessor.d.ts +29 -0
- package/dist/schemas/preprocessing/ConfluencePreprocessor.d.ts.map +1 -1
- package/dist/schemas/preprocessing/ConfluencePreprocessor.js +5 -15
- package/dist/schemas/preprocessing/ConfluencePreprocessor.js.map +1 -1
- package/dist/serializers/ConfluenceSerializer.js +0 -9
- package/dist/serializers/ConfluenceSerializer.js.map +1 -1
- package/dist/serializers/MarkdownSerializer.js +9 -49
- package/dist/serializers/MarkdownSerializer.js.map +1 -1
- package/package.json +35 -26
- package/src/AdfPlaceholders.ts +266 -0
- package/src/AdfSchemaValidator.ts +67 -0
- package/src/AdfWalker.ts +511 -0
- package/src/AtlaskitTransformers.ts +72 -0
- package/src/ConfluenceClient.ts +4 -4
- package/src/ConfluenceError.ts +65 -3
- package/src/MarkdownConverter.ts +106 -139
- package/src/Schemas.ts +4 -4
- package/src/SyncEngine.ts +130 -83
- package/src/atlaskit-adf-schema.d.ts +3 -0
- package/src/commands/clone.ts +8 -1
- package/src/commands/layers.ts +11 -4
- package/src/index.ts +3 -18
- package/test/AdfPlaceholders.test.ts +295 -0
- package/test/AdfSchemaValidator.test.ts +34 -0
- package/test/AdfWalker.test.ts +530 -0
- package/test/AtlaskitTransformers.test.ts +25 -0
- package/test/MarkdownConverter.test.ts +120 -105
- package/test/RoundTrip.test.ts +266 -0
- package/LICENSE +0 -21
- package/src/SchemaConverterError.ts +0 -108
- package/src/ast/BlockNode.ts +0 -469
- package/src/ast/Document.ts +0 -90
- package/src/ast/InlineNode.ts +0 -323
- package/src/ast/MacroNode.ts +0 -245
- package/src/ast/index.ts +0 -83
- package/src/parsers/ConfluenceParser.ts +0 -956
- package/src/parsers/MarkdownParser.ts +0 -1338
- package/src/parsers/index.ts +0 -8
- package/src/schemas/ConfluenceSchema.ts +0 -56
- package/src/schemas/ConversionSchema.ts +0 -318
- package/src/schemas/MarkdownSchema.ts +0 -56
- package/src/schemas/hast/HastFromHtml.ts +0 -153
- package/src/schemas/hast/HastSchema.ts +0 -274
- package/src/schemas/hast/index.ts +0 -35
- package/src/schemas/index.ts +0 -20
- package/src/schemas/mdast/MdastFromMarkdown.ts +0 -118
- package/src/schemas/mdast/MdastSchema.ts +0 -566
- package/src/schemas/mdast/index.ts +0 -59
- package/src/schemas/mdast/mdastToString.ts +0 -102
- package/src/schemas/nodes/block/BlockSchema.ts +0 -773
- package/src/schemas/nodes/block/index.ts +0 -13
- package/src/schemas/nodes/index.ts +0 -20
- package/src/schemas/nodes/inline/InlineSchema.ts +0 -523
- package/src/schemas/nodes/inline/index.ts +0 -14
- package/src/schemas/nodes/macro/MacroSchema.ts +0 -226
- package/src/schemas/nodes/macro/index.ts +0 -6
- package/src/schemas/preprocessing/ConfluencePreprocessor.ts +0 -455
- package/src/schemas/preprocessing/index.ts +0 -8
- package/src/serializers/ConfluenceSerializer.ts +0 -737
- package/src/serializers/MarkdownSerializer.ts +0 -543
- package/src/serializers/index.ts +0 -8
- package/test/ast/BlockNode.test.ts +0 -265
- package/test/ast/Document.test.ts +0 -126
- package/test/ast/InlineNode.test.ts +0 -161
- package/test/fixtures/integration-test.html.fixture +0 -103
- package/test/fixtures/integration-test.md.expected +0 -257
- package/test/parsers/ConfluenceParser.test.ts +0 -452
- package/test/schemas/ConfluencePreprocessor.test.ts +0 -180
- package/test/schemas/ConversionSchema.test.ts +0 -159
- package/test/schemas/HastSchema.test.ts +0 -138
- package/test/schemas/MdastSchema.test.ts +0 -145
- package/test/schemas/nodes/block/BlockSchema.test.ts +0 -173
- package/test/schemas/nodes/inline/InlineSchema.test.ts +0 -198
- package/test/schemas/nodes/macro/MacroSchema.test.ts +0 -142
|
@@ -1,159 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from "@effect/vitest"
|
|
2
|
-
import { Effect, Schema } from "effect"
|
|
3
|
-
import { ConfluenceToMarkdown, DocumentFromHast, DocumentFromMdast } from "../../src/schemas/ConversionSchema.js"
|
|
4
|
-
import { HastFromHtml } from "../../src/schemas/hast/index.js"
|
|
5
|
-
import { MdastFromMarkdown } from "../../src/schemas/mdast/index.js"
|
|
6
|
-
|
|
7
|
-
describe("ConversionSchema", () => {
|
|
8
|
-
describe("DocumentFromHast", () => {
|
|
9
|
-
it.effect("converts simple HAST root to Document", () =>
|
|
10
|
-
Effect.gen(function*() {
|
|
11
|
-
const html = "<h1>Title</h1><p>Hello world</p>"
|
|
12
|
-
const hast = yield* Schema.decode(HastFromHtml)(html)
|
|
13
|
-
const doc = yield* Schema.decode(DocumentFromHast)(hast)
|
|
14
|
-
|
|
15
|
-
expect(doc.version).toBe(1)
|
|
16
|
-
expect(doc.children.length).toBe(2)
|
|
17
|
-
expect(doc.children[0]?._tag).toBe("Heading")
|
|
18
|
-
expect(doc.children[1]?._tag).toBe("Paragraph")
|
|
19
|
-
}))
|
|
20
|
-
|
|
21
|
-
it.effect("handles code blocks", () =>
|
|
22
|
-
Effect.gen(function*() {
|
|
23
|
-
const html = "<pre><code>const x = 1</code></pre>"
|
|
24
|
-
const hast = yield* Schema.decode(HastFromHtml)(html)
|
|
25
|
-
const doc = yield* Schema.decode(DocumentFromHast)(hast)
|
|
26
|
-
|
|
27
|
-
expect(doc.children[0]?._tag).toBe("CodeBlock")
|
|
28
|
-
}))
|
|
29
|
-
|
|
30
|
-
it.effect("handles empty input", () =>
|
|
31
|
-
Effect.gen(function*() {
|
|
32
|
-
const html = ""
|
|
33
|
-
const hast = yield* Schema.decode(HastFromHtml)(html)
|
|
34
|
-
const doc = yield* Schema.decode(DocumentFromHast)(hast)
|
|
35
|
-
|
|
36
|
-
expect(doc.children.length).toBe(0)
|
|
37
|
-
}))
|
|
38
|
-
})
|
|
39
|
-
|
|
40
|
-
describe("DocumentFromMdast", () => {
|
|
41
|
-
it.effect("converts simple MDAST root to Document", () =>
|
|
42
|
-
Effect.gen(function*() {
|
|
43
|
-
const md = "# Title\n\nHello world"
|
|
44
|
-
const mdast = yield* Schema.decode(MdastFromMarkdown)(md)
|
|
45
|
-
const doc = yield* Schema.decode(DocumentFromMdast)(mdast)
|
|
46
|
-
|
|
47
|
-
expect(doc.version).toBe(1)
|
|
48
|
-
expect(doc.children.length).toBe(2)
|
|
49
|
-
expect(doc.children[0]?._tag).toBe("Heading")
|
|
50
|
-
expect(doc.children[1]?._tag).toBe("Paragraph")
|
|
51
|
-
}))
|
|
52
|
-
|
|
53
|
-
it.effect("handles code blocks", () =>
|
|
54
|
-
Effect.gen(function*() {
|
|
55
|
-
const md = "```ts\nconst x = 1\n```"
|
|
56
|
-
const mdast = yield* Schema.decode(MdastFromMarkdown)(md)
|
|
57
|
-
const doc = yield* Schema.decode(DocumentFromMdast)(mdast)
|
|
58
|
-
|
|
59
|
-
expect(doc.children[0]?._tag).toBe("CodeBlock")
|
|
60
|
-
}))
|
|
61
|
-
|
|
62
|
-
it.effect("handles lists", () =>
|
|
63
|
-
Effect.gen(function*() {
|
|
64
|
-
const md = "- Item 1\n- Item 2"
|
|
65
|
-
const mdast = yield* Schema.decode(MdastFromMarkdown)(md)
|
|
66
|
-
const doc = yield* Schema.decode(DocumentFromMdast)(mdast)
|
|
67
|
-
|
|
68
|
-
expect(doc.children[0]?._tag).toBe("List")
|
|
69
|
-
}))
|
|
70
|
-
})
|
|
71
|
-
|
|
72
|
-
describe("ConfluenceToMarkdown", () => {
|
|
73
|
-
it.effect("decodes simple Confluence HTML to Markdown", () =>
|
|
74
|
-
Effect.gen(function*() {
|
|
75
|
-
const html = "<h1>Title</h1><p>Hello world</p>"
|
|
76
|
-
const md = yield* Schema.decode(ConfluenceToMarkdown)(html)
|
|
77
|
-
|
|
78
|
-
expect(md).toContain("# Title")
|
|
79
|
-
expect(md).toContain("Hello world")
|
|
80
|
-
}))
|
|
81
|
-
|
|
82
|
-
it.effect("decodes code blocks", () =>
|
|
83
|
-
Effect.gen(function*() {
|
|
84
|
-
const html = "<pre><code class=\"language-ts\">const x = 1</code></pre>"
|
|
85
|
-
const md = yield* Schema.decode(ConfluenceToMarkdown)(html)
|
|
86
|
-
|
|
87
|
-
expect(md).toContain("```")
|
|
88
|
-
expect(md).toContain("const x = 1")
|
|
89
|
-
}))
|
|
90
|
-
|
|
91
|
-
it.effect("decodes inline formatting", () =>
|
|
92
|
-
Effect.gen(function*() {
|
|
93
|
-
const html = "<p><strong>bold</strong> and <em>italic</em></p>"
|
|
94
|
-
const md = yield* Schema.decode(ConfluenceToMarkdown)(html)
|
|
95
|
-
|
|
96
|
-
expect(md).toContain("**bold**")
|
|
97
|
-
expect(md).toContain("_italic_")
|
|
98
|
-
}))
|
|
99
|
-
|
|
100
|
-
it.effect("encodes Markdown to basic HTML (structure only)", () =>
|
|
101
|
-
Effect.gen(function*() {
|
|
102
|
-
const md = "# Title\n\nHello world"
|
|
103
|
-
const html = yield* Schema.encode(ConfluenceToMarkdown)(md)
|
|
104
|
-
|
|
105
|
-
// Note: basic HTML encoding only preserves structure, not content
|
|
106
|
-
expect(html).toContain("<h1>")
|
|
107
|
-
expect(html).toContain("<p>")
|
|
108
|
-
}))
|
|
109
|
-
|
|
110
|
-
it.effect("handles links", () =>
|
|
111
|
-
Effect.gen(function*() {
|
|
112
|
-
const html = "<p><a href=\"https://example.com\">Click here</a></p>"
|
|
113
|
-
const md = yield* Schema.decode(ConfluenceToMarkdown)(html)
|
|
114
|
-
|
|
115
|
-
expect(md).toContain("[Click here](https://example.com)")
|
|
116
|
-
}))
|
|
117
|
-
|
|
118
|
-
it.effect("handles horizontal rules", () =>
|
|
119
|
-
Effect.gen(function*() {
|
|
120
|
-
const html = "<hr />"
|
|
121
|
-
const md = yield* Schema.decode(ConfluenceToMarkdown)(html)
|
|
122
|
-
|
|
123
|
-
expect(md).toContain("---")
|
|
124
|
-
}))
|
|
125
|
-
|
|
126
|
-
it.effect("handles lists", () =>
|
|
127
|
-
Effect.gen(function*() {
|
|
128
|
-
const html = "<ul><li><p>Item 1</p></li><li><p>Item 2</p></li></ul>"
|
|
129
|
-
const md = yield* Schema.decode(ConfluenceToMarkdown)(html)
|
|
130
|
-
|
|
131
|
-
expect(md).toContain("- ")
|
|
132
|
-
expect(md).toContain("Item 1")
|
|
133
|
-
expect(md).toContain("Item 2")
|
|
134
|
-
}))
|
|
135
|
-
|
|
136
|
-
it.effect("handles tables", () =>
|
|
137
|
-
Effect.gen(function*() {
|
|
138
|
-
const html = "<table><tr><th>A</th></tr><tr><td>1</td></tr></table>"
|
|
139
|
-
const md = yield* Schema.decode(ConfluenceToMarkdown)(html)
|
|
140
|
-
|
|
141
|
-
expect(md).toContain("|")
|
|
142
|
-
expect(md).toContain("---")
|
|
143
|
-
}))
|
|
144
|
-
})
|
|
145
|
-
|
|
146
|
-
describe("roundtrip", () => {
|
|
147
|
-
it.effect("HTML -> Markdown -> HTML preserves structure", () =>
|
|
148
|
-
Effect.gen(function*() {
|
|
149
|
-
// Start from HTML since that direction is fully supported
|
|
150
|
-
const originalHtml = "<h1>Hello</h1><p>World</p>"
|
|
151
|
-
const md = yield* Schema.decode(ConfluenceToMarkdown)(originalHtml)
|
|
152
|
-
const html = yield* Schema.encode(ConfluenceToMarkdown)(md)
|
|
153
|
-
|
|
154
|
-
// Structure is preserved
|
|
155
|
-
expect(html).toContain("<h1>")
|
|
156
|
-
expect(html).toContain("<p>")
|
|
157
|
-
}))
|
|
158
|
-
})
|
|
159
|
-
})
|
|
@@ -1,138 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from "@effect/vitest"
|
|
2
|
-
import * as Effect from "effect/Effect"
|
|
3
|
-
import * as Either from "effect/Either"
|
|
4
|
-
import * as Schema from "effect/Schema"
|
|
5
|
-
import {
|
|
6
|
-
getTextContent,
|
|
7
|
-
HastFromHtml,
|
|
8
|
-
HastNodeSchema,
|
|
9
|
-
HastRootSchema,
|
|
10
|
-
isHastElement,
|
|
11
|
-
isHastText,
|
|
12
|
-
makeHastElement,
|
|
13
|
-
makeHastRoot,
|
|
14
|
-
makeHastText
|
|
15
|
-
} from "../../src/schemas/hast/index.js"
|
|
16
|
-
|
|
17
|
-
describe("HastSchema", () => {
|
|
18
|
-
describe("constructors", () => {
|
|
19
|
-
it("creates text node", () => {
|
|
20
|
-
const node = makeHastText("hello")
|
|
21
|
-
expect(node._tag).toBe("text")
|
|
22
|
-
expect(node.value).toBe("hello")
|
|
23
|
-
})
|
|
24
|
-
|
|
25
|
-
it("creates element node", () => {
|
|
26
|
-
const node = makeHastElement("div", { class: "test" }, [makeHastText("child")])
|
|
27
|
-
expect(node._tag).toBe("element")
|
|
28
|
-
expect(node.tagName).toBe("div")
|
|
29
|
-
expect(node.properties.class).toBe("test")
|
|
30
|
-
expect(node.children).toHaveLength(1)
|
|
31
|
-
})
|
|
32
|
-
|
|
33
|
-
it("creates root node", () => {
|
|
34
|
-
const root = makeHastRoot([makeHastElement("p", {}, [makeHastText("text")])])
|
|
35
|
-
expect(root._tag).toBe("root")
|
|
36
|
-
expect(root.children).toHaveLength(1)
|
|
37
|
-
})
|
|
38
|
-
})
|
|
39
|
-
|
|
40
|
-
describe("type guards", () => {
|
|
41
|
-
it("isHastText returns true for text node", () => {
|
|
42
|
-
expect(isHastText(makeHastText("hello"))).toBe(true)
|
|
43
|
-
expect(isHastText(makeHastElement("div"))).toBe(false)
|
|
44
|
-
})
|
|
45
|
-
|
|
46
|
-
it("isHastElement returns true for element node", () => {
|
|
47
|
-
expect(isHastElement(makeHastElement("div"))).toBe(true)
|
|
48
|
-
expect(isHastElement(makeHastText("hello"))).toBe(false)
|
|
49
|
-
})
|
|
50
|
-
})
|
|
51
|
-
|
|
52
|
-
describe("getTextContent", () => {
|
|
53
|
-
it("extracts text from text node", () => {
|
|
54
|
-
expect(getTextContent(makeHastText("hello"))).toBe("hello")
|
|
55
|
-
})
|
|
56
|
-
|
|
57
|
-
it("extracts text from nested elements", () => {
|
|
58
|
-
const el = makeHastElement("div", {}, [
|
|
59
|
-
makeHastText("hello "),
|
|
60
|
-
makeHastElement("strong", {}, [makeHastText("world")])
|
|
61
|
-
])
|
|
62
|
-
expect(getTextContent(el)).toBe("hello world")
|
|
63
|
-
})
|
|
64
|
-
})
|
|
65
|
-
|
|
66
|
-
describe("HastNodeSchema", () => {
|
|
67
|
-
it("validates text node", () => {
|
|
68
|
-
const result = Schema.decodeUnknownEither(HastNodeSchema)(makeHastText("test"))
|
|
69
|
-
expect(Either.isRight(result)).toBe(true)
|
|
70
|
-
})
|
|
71
|
-
|
|
72
|
-
it("validates element node", () => {
|
|
73
|
-
const result = Schema.decodeUnknownEither(HastNodeSchema)(makeHastElement("p"))
|
|
74
|
-
expect(Either.isRight(result)).toBe(true)
|
|
75
|
-
})
|
|
76
|
-
})
|
|
77
|
-
|
|
78
|
-
describe("HastRootSchema", () => {
|
|
79
|
-
it("validates root node", () => {
|
|
80
|
-
const root = makeHastRoot([makeHastElement("p", {}, [makeHastText("text")])])
|
|
81
|
-
const result = Schema.decodeUnknownEither(HastRootSchema)(root)
|
|
82
|
-
expect(Either.isRight(result)).toBe(true)
|
|
83
|
-
})
|
|
84
|
-
})
|
|
85
|
-
})
|
|
86
|
-
|
|
87
|
-
describe("HastFromHtml", () => {
|
|
88
|
-
describe("decode (HTML -> HAST)", () => {
|
|
89
|
-
it.effect("parses simple HTML", () =>
|
|
90
|
-
Effect.gen(function*() {
|
|
91
|
-
const hast = yield* Schema.decode(HastFromHtml)("<p>Hello</p>")
|
|
92
|
-
expect(hast._tag).toBe("root")
|
|
93
|
-
expect(hast.children).toHaveLength(1)
|
|
94
|
-
const p = hast.children[0]
|
|
95
|
-
expect(isHastElement(p) && p.tagName).toBe("p")
|
|
96
|
-
}))
|
|
97
|
-
|
|
98
|
-
it.effect("parses nested HTML", () =>
|
|
99
|
-
Effect.gen(function*() {
|
|
100
|
-
const hast = yield* Schema.decode(HastFromHtml)("<div><p>Text</p></div>")
|
|
101
|
-
expect(hast.children).toHaveLength(1)
|
|
102
|
-
const div = hast.children[0]
|
|
103
|
-
if (isHastElement(div)) {
|
|
104
|
-
expect(div.tagName).toBe("div")
|
|
105
|
-
expect(div.children).toHaveLength(1)
|
|
106
|
-
}
|
|
107
|
-
}))
|
|
108
|
-
|
|
109
|
-
it.effect("parses HTML with attributes", () =>
|
|
110
|
-
Effect.gen(function*() {
|
|
111
|
-
const hast = yield* Schema.decode(HastFromHtml)("<a href=\"https://example.com\">Link</a>")
|
|
112
|
-
const a = hast.children[0]
|
|
113
|
-
if (isHastElement(a)) {
|
|
114
|
-
expect(a.properties.href).toBe("https://example.com")
|
|
115
|
-
}
|
|
116
|
-
}))
|
|
117
|
-
})
|
|
118
|
-
|
|
119
|
-
describe("encode (HAST -> HTML)", () => {
|
|
120
|
-
it.effect("stringifies HAST to HTML", () =>
|
|
121
|
-
Effect.gen(function*() {
|
|
122
|
-
const hast = makeHastRoot([
|
|
123
|
-
makeHastElement("p", {}, [makeHastText("Hello")])
|
|
124
|
-
])
|
|
125
|
-
const html = yield* Schema.encode(HastFromHtml)(hast)
|
|
126
|
-
expect(html).toContain("<p>")
|
|
127
|
-
expect(html).toContain("Hello")
|
|
128
|
-
}))
|
|
129
|
-
|
|
130
|
-
it.effect("roundtrips HTML", () =>
|
|
131
|
-
Effect.gen(function*() {
|
|
132
|
-
const original = "<p>Test paragraph</p>"
|
|
133
|
-
const hast = yield* Schema.decode(HastFromHtml)(original)
|
|
134
|
-
const html = yield* Schema.encode(HastFromHtml)(hast)
|
|
135
|
-
expect(html.replace(/\s/g, "")).toBe(original.replace(/\s/g, ""))
|
|
136
|
-
}))
|
|
137
|
-
})
|
|
138
|
-
})
|
|
@@ -1,145 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from "@effect/vitest"
|
|
2
|
-
import * as Effect from "effect/Effect"
|
|
3
|
-
import * as Either from "effect/Either"
|
|
4
|
-
import * as Schema from "effect/Schema"
|
|
5
|
-
import {
|
|
6
|
-
isMdastHeading,
|
|
7
|
-
isMdastParagraph,
|
|
8
|
-
isMdastText,
|
|
9
|
-
makeMdastHeading,
|
|
10
|
-
makeMdastParagraph,
|
|
11
|
-
makeMdastRoot,
|
|
12
|
-
makeMdastText,
|
|
13
|
-
MdastFromMarkdown,
|
|
14
|
-
MdastRootSchema,
|
|
15
|
-
MdastTextSchema
|
|
16
|
-
} from "../../src/schemas/mdast/index.js"
|
|
17
|
-
|
|
18
|
-
describe("MdastSchema", () => {
|
|
19
|
-
describe("constructors", () => {
|
|
20
|
-
it("creates text node", () => {
|
|
21
|
-
const node = makeMdastText("hello")
|
|
22
|
-
expect(node.type).toBe("text")
|
|
23
|
-
expect(node.value).toBe("hello")
|
|
24
|
-
})
|
|
25
|
-
|
|
26
|
-
it("creates heading node", () => {
|
|
27
|
-
const node = makeMdastHeading(2, [makeMdastText("Title")])
|
|
28
|
-
expect(node.type).toBe("heading")
|
|
29
|
-
expect(node.depth).toBe(2)
|
|
30
|
-
expect(node.children).toHaveLength(1)
|
|
31
|
-
})
|
|
32
|
-
|
|
33
|
-
it("creates paragraph node", () => {
|
|
34
|
-
const node = makeMdastParagraph([makeMdastText("content")])
|
|
35
|
-
expect(node.type).toBe("paragraph")
|
|
36
|
-
expect(node.children).toHaveLength(1)
|
|
37
|
-
})
|
|
38
|
-
|
|
39
|
-
it("creates root node", () => {
|
|
40
|
-
const root = makeMdastRoot([makeMdastParagraph([makeMdastText("text")])])
|
|
41
|
-
expect(root.type).toBe("root")
|
|
42
|
-
expect(root.children).toHaveLength(1)
|
|
43
|
-
})
|
|
44
|
-
})
|
|
45
|
-
|
|
46
|
-
describe("type guards", () => {
|
|
47
|
-
it("isMdastText returns true for text node", () => {
|
|
48
|
-
expect(isMdastText(makeMdastText("hello"))).toBe(true)
|
|
49
|
-
expect(isMdastText(makeMdastHeading(1, []))).toBe(false)
|
|
50
|
-
})
|
|
51
|
-
|
|
52
|
-
it("isMdastHeading returns true for heading node", () => {
|
|
53
|
-
expect(isMdastHeading(makeMdastHeading(1, []))).toBe(true)
|
|
54
|
-
expect(isMdastHeading(makeMdastText("hello"))).toBe(false)
|
|
55
|
-
})
|
|
56
|
-
|
|
57
|
-
it("isMdastParagraph returns true for paragraph node", () => {
|
|
58
|
-
expect(isMdastParagraph(makeMdastParagraph([]))).toBe(true)
|
|
59
|
-
expect(isMdastParagraph(makeMdastText("hello"))).toBe(false)
|
|
60
|
-
})
|
|
61
|
-
})
|
|
62
|
-
|
|
63
|
-
describe("MdastTextSchema", () => {
|
|
64
|
-
it("validates text node", () => {
|
|
65
|
-
const result = Schema.decodeUnknownEither(MdastTextSchema)(makeMdastText("test"))
|
|
66
|
-
expect(Either.isRight(result)).toBe(true)
|
|
67
|
-
})
|
|
68
|
-
|
|
69
|
-
it("rejects invalid type", () => {
|
|
70
|
-
const result = Schema.decodeUnknownEither(MdastTextSchema)({ type: "other", value: "x" })
|
|
71
|
-
expect(Either.isLeft(result)).toBe(true)
|
|
72
|
-
})
|
|
73
|
-
})
|
|
74
|
-
|
|
75
|
-
describe("MdastRootSchema", () => {
|
|
76
|
-
it("validates root node", () => {
|
|
77
|
-
const root = makeMdastRoot([makeMdastParagraph([makeMdastText("text")])])
|
|
78
|
-
const result = Schema.decodeUnknownEither(MdastRootSchema)(root)
|
|
79
|
-
expect(Either.isRight(result)).toBe(true)
|
|
80
|
-
})
|
|
81
|
-
})
|
|
82
|
-
})
|
|
83
|
-
|
|
84
|
-
describe("MdastFromMarkdown", () => {
|
|
85
|
-
describe("decode (Markdown -> MDAST)", () => {
|
|
86
|
-
it.effect("parses heading", () =>
|
|
87
|
-
Effect.gen(function*() {
|
|
88
|
-
const mdast = yield* Schema.decode(MdastFromMarkdown)("# Hello")
|
|
89
|
-
expect(mdast.type).toBe("root")
|
|
90
|
-
expect(mdast.children).toHaveLength(1)
|
|
91
|
-
const heading = mdast.children[0]
|
|
92
|
-
expect(heading.type).toBe("heading")
|
|
93
|
-
}))
|
|
94
|
-
|
|
95
|
-
it.effect("parses paragraph", () =>
|
|
96
|
-
Effect.gen(function*() {
|
|
97
|
-
const mdast = yield* Schema.decode(MdastFromMarkdown)("Hello world")
|
|
98
|
-
expect(mdast.children).toHaveLength(1)
|
|
99
|
-
expect(mdast.children[0].type).toBe("paragraph")
|
|
100
|
-
}))
|
|
101
|
-
|
|
102
|
-
it.effect("parses code block", () =>
|
|
103
|
-
Effect.gen(function*() {
|
|
104
|
-
const mdast = yield* Schema.decode(MdastFromMarkdown)("```ts\nconst x = 1\n```")
|
|
105
|
-
expect(mdast.children).toHaveLength(1)
|
|
106
|
-
const code = mdast.children[0]
|
|
107
|
-
expect(code.type).toBe("code")
|
|
108
|
-
}))
|
|
109
|
-
|
|
110
|
-
it.effect("parses list", () =>
|
|
111
|
-
Effect.gen(function*() {
|
|
112
|
-
const mdast = yield* Schema.decode(MdastFromMarkdown)("- item 1\n- item 2")
|
|
113
|
-
expect(mdast.children).toHaveLength(1)
|
|
114
|
-
expect(mdast.children[0].type).toBe("list")
|
|
115
|
-
}))
|
|
116
|
-
})
|
|
117
|
-
|
|
118
|
-
describe("encode (MDAST -> Markdown)", () => {
|
|
119
|
-
it.effect("stringifies heading", () =>
|
|
120
|
-
Effect.gen(function*() {
|
|
121
|
-
const mdast = makeMdastRoot([
|
|
122
|
-
makeMdastHeading(1, [makeMdastText("Title")])
|
|
123
|
-
])
|
|
124
|
-
const md = yield* Schema.encode(MdastFromMarkdown)(mdast)
|
|
125
|
-
expect(md.trim()).toBe("# Title")
|
|
126
|
-
}))
|
|
127
|
-
|
|
128
|
-
it.effect("stringifies paragraph", () =>
|
|
129
|
-
Effect.gen(function*() {
|
|
130
|
-
const mdast = makeMdastRoot([
|
|
131
|
-
makeMdastParagraph([makeMdastText("Hello world")])
|
|
132
|
-
])
|
|
133
|
-
const md = yield* Schema.encode(MdastFromMarkdown)(mdast)
|
|
134
|
-
expect(md.trim()).toBe("Hello world")
|
|
135
|
-
}))
|
|
136
|
-
|
|
137
|
-
it.effect("roundtrips markdown", () =>
|
|
138
|
-
Effect.gen(function*() {
|
|
139
|
-
const original = "# Title\n\nParagraph text."
|
|
140
|
-
const mdast = yield* Schema.decode(MdastFromMarkdown)(original)
|
|
141
|
-
const md = yield* Schema.encode(MdastFromMarkdown)(mdast)
|
|
142
|
-
expect(md.trim()).toBe(original)
|
|
143
|
-
}))
|
|
144
|
-
})
|
|
145
|
-
})
|
|
@@ -1,173 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from "@effect/vitest"
|
|
2
|
-
import type { UnsupportedBlock } from "../../../../src/ast/BlockNode.js"
|
|
3
|
-
import {
|
|
4
|
-
CodeBlock,
|
|
5
|
-
Heading,
|
|
6
|
-
Paragraph,
|
|
7
|
-
Table,
|
|
8
|
-
TableCell,
|
|
9
|
-
TableRow,
|
|
10
|
-
ThematicBreak
|
|
11
|
-
} from "../../../../src/ast/BlockNode.js"
|
|
12
|
-
import { Text } from "../../../../src/ast/InlineNode.js"
|
|
13
|
-
import { makeMdastHeading, makeMdastParagraph, makeMdastText } from "../../../../src/schemas/mdast/index.js"
|
|
14
|
-
import { blockNodeFromMdast, blockNodeToHast, blockNodeToMdast } from "../../../../src/schemas/nodes/block/index.js"
|
|
15
|
-
|
|
16
|
-
describe("BlockSchema", () => {
|
|
17
|
-
describe("blockNodeToHast", () => {
|
|
18
|
-
it("converts Heading to hast h element", () => {
|
|
19
|
-
const heading = new Heading({ level: 2, children: [new Text({ value: "Title" })] })
|
|
20
|
-
const result = blockNodeToHast(heading)
|
|
21
|
-
expect(result._tag).toBe("element")
|
|
22
|
-
expect(result.tagName).toBe("h2")
|
|
23
|
-
})
|
|
24
|
-
|
|
25
|
-
it("converts Paragraph to hast p element", () => {
|
|
26
|
-
const para = new Paragraph({ children: [new Text({ value: "Hello" })] })
|
|
27
|
-
const result = blockNodeToHast(para)
|
|
28
|
-
expect(result._tag).toBe("element")
|
|
29
|
-
expect(result.tagName).toBe("p")
|
|
30
|
-
})
|
|
31
|
-
|
|
32
|
-
it("converts CodeBlock to hast pre/code elements", () => {
|
|
33
|
-
const code = new CodeBlock({ code: "const x = 1", language: "ts" })
|
|
34
|
-
const result = blockNodeToHast(code)
|
|
35
|
-
expect(result._tag).toBe("element")
|
|
36
|
-
expect(result.tagName).toBe("pre")
|
|
37
|
-
expect(result.properties?.dataLanguage).toBe("ts")
|
|
38
|
-
})
|
|
39
|
-
|
|
40
|
-
it("converts ThematicBreak to hast hr element", () => {
|
|
41
|
-
const hr = new ThematicBreak({})
|
|
42
|
-
const result = blockNodeToHast(hr)
|
|
43
|
-
expect(result._tag).toBe("element")
|
|
44
|
-
expect(result.tagName).toBe("hr")
|
|
45
|
-
})
|
|
46
|
-
|
|
47
|
-
it("converts Table to hast table element", () => {
|
|
48
|
-
const table = new Table({
|
|
49
|
-
header: new TableRow({
|
|
50
|
-
cells: [new TableCell({ isHeader: true, children: [new Text({ value: "Col" })] })]
|
|
51
|
-
}),
|
|
52
|
-
rows: [
|
|
53
|
-
new TableRow({
|
|
54
|
-
cells: [new TableCell({ children: [new Text({ value: "Val" })] })]
|
|
55
|
-
})
|
|
56
|
-
]
|
|
57
|
-
})
|
|
58
|
-
const result = blockNodeToHast(table)
|
|
59
|
-
expect(result._tag).toBe("element")
|
|
60
|
-
expect(result.tagName).toBe("table")
|
|
61
|
-
})
|
|
62
|
-
})
|
|
63
|
-
|
|
64
|
-
describe("blockNodeToMdast", () => {
|
|
65
|
-
it("converts Heading to mdast heading", () => {
|
|
66
|
-
const heading = new Heading({ level: 1, children: [new Text({ value: "Title" })] })
|
|
67
|
-
const result = blockNodeToMdast(heading)
|
|
68
|
-
expect(result.type).toBe("heading")
|
|
69
|
-
})
|
|
70
|
-
|
|
71
|
-
it("converts Paragraph to mdast paragraph", () => {
|
|
72
|
-
const para = new Paragraph({ children: [new Text({ value: "Hello" })] })
|
|
73
|
-
const result = blockNodeToMdast(para)
|
|
74
|
-
expect(result.type).toBe("paragraph")
|
|
75
|
-
})
|
|
76
|
-
|
|
77
|
-
it("converts CodeBlock to mdast code", () => {
|
|
78
|
-
const code = new CodeBlock({ code: "const x = 1", language: "ts" })
|
|
79
|
-
const result = blockNodeToMdast(code)
|
|
80
|
-
expect(result.type).toBe("code")
|
|
81
|
-
expect((result as { value: string }).value).toBe("const x = 1")
|
|
82
|
-
})
|
|
83
|
-
|
|
84
|
-
it("converts ThematicBreak to mdast thematicBreak", () => {
|
|
85
|
-
const hr = new ThematicBreak({})
|
|
86
|
-
const result = blockNodeToMdast(hr)
|
|
87
|
-
expect(result.type).toBe("thematicBreak")
|
|
88
|
-
})
|
|
89
|
-
|
|
90
|
-
it("converts Table to mdast table", () => {
|
|
91
|
-
const table = new Table({ rows: [] })
|
|
92
|
-
const result = blockNodeToMdast(table)
|
|
93
|
-
expect(result.type).toBe("table")
|
|
94
|
-
})
|
|
95
|
-
})
|
|
96
|
-
|
|
97
|
-
describe("blockNodeFromMdast", () => {
|
|
98
|
-
it("converts mdast heading to Heading", () => {
|
|
99
|
-
const mdast = makeMdastHeading(2, [makeMdastText("Title")])
|
|
100
|
-
const result = blockNodeFromMdast(mdast)
|
|
101
|
-
expect(result._tag).toBe("Heading")
|
|
102
|
-
expect((result as Heading).level).toBe(2)
|
|
103
|
-
})
|
|
104
|
-
|
|
105
|
-
it("converts mdast paragraph to Paragraph", () => {
|
|
106
|
-
const mdast = makeMdastParagraph([makeMdastText("Hello")])
|
|
107
|
-
const result = blockNodeFromMdast(mdast)
|
|
108
|
-
expect(result._tag).toBe("Paragraph")
|
|
109
|
-
})
|
|
110
|
-
|
|
111
|
-
it("converts mdast code to CodeBlock", () => {
|
|
112
|
-
const mdast = { type: "code" as const, value: "const x = 1", lang: "ts" }
|
|
113
|
-
const result = blockNodeFromMdast(mdast)
|
|
114
|
-
expect(result._tag).toBe("CodeBlock")
|
|
115
|
-
expect((result as CodeBlock).code).toBe("const x = 1")
|
|
116
|
-
expect((result as CodeBlock).language).toBe("ts")
|
|
117
|
-
})
|
|
118
|
-
|
|
119
|
-
it("converts mdast thematicBreak to ThematicBreak", () => {
|
|
120
|
-
const mdast = { type: "thematicBreak" as const }
|
|
121
|
-
const result = blockNodeFromMdast(mdast)
|
|
122
|
-
expect(result._tag).toBe("ThematicBreak")
|
|
123
|
-
})
|
|
124
|
-
|
|
125
|
-
it("converts mdast blockquote to BlockQuote", () => {
|
|
126
|
-
const mdast = {
|
|
127
|
-
type: "blockquote" as const,
|
|
128
|
-
children: [makeMdastParagraph([makeMdastText("Quote")])]
|
|
129
|
-
}
|
|
130
|
-
const result = blockNodeFromMdast(mdast)
|
|
131
|
-
expect(result._tag).toBe("BlockQuote")
|
|
132
|
-
})
|
|
133
|
-
|
|
134
|
-
it("converts mdast list to List", () => {
|
|
135
|
-
const mdast = {
|
|
136
|
-
type: "list" as const,
|
|
137
|
-
ordered: true,
|
|
138
|
-
children: [{
|
|
139
|
-
type: "listItem" as const,
|
|
140
|
-
children: [makeMdastParagraph([makeMdastText("Item")])]
|
|
141
|
-
}]
|
|
142
|
-
}
|
|
143
|
-
const result = blockNodeFromMdast(mdast)
|
|
144
|
-
expect(result._tag).toBe("List")
|
|
145
|
-
})
|
|
146
|
-
|
|
147
|
-
it("converts mdast html to UnsupportedBlock", () => {
|
|
148
|
-
const mdast = { type: "html" as const, value: "<custom>x</custom>" }
|
|
149
|
-
const result = blockNodeFromMdast(mdast)
|
|
150
|
-
expect(result._tag).toBe("UnsupportedBlock")
|
|
151
|
-
expect((result as UnsupportedBlock).rawMarkdown).toBe("<custom>x</custom>")
|
|
152
|
-
})
|
|
153
|
-
})
|
|
154
|
-
|
|
155
|
-
describe("roundtrip", () => {
|
|
156
|
-
it("Heading roundtrips through MDAST", () => {
|
|
157
|
-
const original = new Heading({ level: 3, children: [new Text({ value: "Title" })] })
|
|
158
|
-
const mdast = blockNodeToMdast(original)
|
|
159
|
-
const result = blockNodeFromMdast(mdast)
|
|
160
|
-
expect(result._tag).toBe("Heading")
|
|
161
|
-
expect((result as Heading).level).toBe(3)
|
|
162
|
-
})
|
|
163
|
-
|
|
164
|
-
it("CodeBlock roundtrips through MDAST", () => {
|
|
165
|
-
const original = new CodeBlock({ code: "x = 1", language: "py" })
|
|
166
|
-
const mdast = blockNodeToMdast(original)
|
|
167
|
-
const result = blockNodeFromMdast(mdast)
|
|
168
|
-
expect(result._tag).toBe("CodeBlock")
|
|
169
|
-
expect((result as CodeBlock).code).toBe("x = 1")
|
|
170
|
-
expect((result as CodeBlock).language).toBe("py")
|
|
171
|
-
})
|
|
172
|
-
})
|
|
173
|
-
})
|