@knpkv/confluence-to-markdown 0.2.0 → 0.4.1
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 +60 -0
- package/LICENSE +21 -0
- package/README.md +282 -14
- package/dist/ConfluenceAuth.d.ts +76 -0
- package/dist/ConfluenceAuth.d.ts.map +1 -0
- package/dist/ConfluenceAuth.js +356 -0
- package/dist/ConfluenceAuth.js.map +1 -0
- package/dist/ConfluenceClient.d.ts +26 -2
- package/dist/ConfluenceClient.d.ts.map +1 -1
- package/dist/ConfluenceClient.js +98 -92
- package/dist/ConfluenceClient.js.map +1 -1
- package/dist/ConfluenceConfig.d.ts +4 -24
- package/dist/ConfluenceConfig.d.ts.map +1 -1
- package/dist/ConfluenceConfig.js +45 -7
- package/dist/ConfluenceConfig.js.map +1 -1
- package/dist/ConfluenceError.d.ts +89 -6
- package/dist/ConfluenceError.d.ts.map +1 -1
- package/dist/ConfluenceError.js +88 -5
- package/dist/ConfluenceError.js.map +1 -1
- package/dist/GitError.d.ts +103 -0
- package/dist/GitError.d.ts.map +1 -0
- package/dist/GitError.js +85 -0
- package/dist/GitError.js.map +1 -0
- package/dist/GitService.d.ts +175 -0
- package/dist/GitService.d.ts.map +1 -0
- package/dist/GitService.js +431 -0
- package/dist/GitService.js.map +1 -0
- package/dist/LocalFileSystem.d.ts +29 -4
- package/dist/LocalFileSystem.d.ts.map +1 -1
- package/dist/LocalFileSystem.js +80 -6
- package/dist/LocalFileSystem.js.map +1 -1
- package/dist/MarkdownConverter.d.ts +49 -2
- package/dist/MarkdownConverter.d.ts.map +1 -1
- package/dist/MarkdownConverter.js +73 -111
- package/dist/MarkdownConverter.js.map +1 -1
- package/dist/SchemaConverterError.d.ts +108 -0
- package/dist/SchemaConverterError.d.ts.map +1 -0
- package/dist/SchemaConverterError.js +84 -0
- package/dist/SchemaConverterError.js.map +1 -0
- package/dist/Schemas.d.ts +225 -1
- package/dist/Schemas.d.ts.map +1 -1
- package/dist/Schemas.js +155 -6
- package/dist/Schemas.js.map +1 -1
- package/dist/SyncEngine.d.ts +30 -20
- package/dist/SyncEngine.d.ts.map +1 -1
- package/dist/SyncEngine.js +566 -117
- package/dist/SyncEngine.js.map +1 -1
- package/dist/ast/BlockNode.d.ts +468 -0
- package/dist/ast/BlockNode.d.ts.map +1 -0
- package/dist/ast/BlockNode.js +319 -0
- package/dist/ast/BlockNode.js.map +1 -0
- package/dist/ast/Document.d.ts +244 -0
- package/dist/ast/Document.d.ts.map +1 -0
- package/dist/ast/Document.js +69 -0
- package/dist/ast/Document.js.map +1 -0
- package/dist/ast/InlineNode.d.ts +477 -0
- package/dist/ast/InlineNode.d.ts.map +1 -0
- package/dist/ast/InlineNode.js +263 -0
- package/dist/ast/InlineNode.js.map +1 -0
- package/dist/ast/MacroNode.d.ts +267 -0
- package/dist/ast/MacroNode.d.ts.map +1 -0
- package/dist/ast/MacroNode.js +164 -0
- package/dist/ast/MacroNode.js.map +1 -0
- package/dist/ast/index.d.ts +10 -0
- package/dist/ast/index.d.ts.map +1 -0
- package/dist/ast/index.js +14 -0
- package/dist/ast/index.js.map +1 -0
- package/dist/bin.js +33 -149
- package/dist/bin.js.map +1 -1
- package/dist/commands/auth.d.ts +15 -0
- package/dist/commands/auth.d.ts.map +1 -0
- package/dist/commands/auth.js +86 -0
- package/dist/commands/auth.js.map +1 -0
- package/dist/commands/clone.d.ts +12 -0
- package/dist/commands/clone.d.ts.map +1 -0
- package/dist/commands/clone.js +93 -0
- package/dist/commands/clone.js.map +1 -0
- package/dist/commands/delete.d.ts +13 -0
- package/dist/commands/delete.d.ts.map +1 -0
- package/dist/commands/delete.js +48 -0
- package/dist/commands/delete.js.map +1 -0
- package/dist/commands/errorHandler.d.ts +14 -0
- package/dist/commands/errorHandler.d.ts.map +1 -0
- package/dist/commands/errorHandler.js +33 -0
- package/dist/commands/errorHandler.js.map +1 -0
- package/dist/commands/git.d.ts +22 -0
- package/dist/commands/git.d.ts.map +1 -0
- package/dist/commands/git.js +72 -0
- package/dist/commands/git.js.map +1 -0
- package/dist/commands/index.d.ts +11 -0
- package/dist/commands/index.d.ts.map +1 -0
- package/dist/commands/index.js +11 -0
- package/dist/commands/index.js.map +1 -0
- package/dist/commands/layers.d.ts +31 -0
- package/dist/commands/layers.d.ts.map +1 -0
- package/dist/commands/layers.js +137 -0
- package/dist/commands/layers.js.map +1 -0
- package/dist/commands/new.d.ts +9 -0
- package/dist/commands/new.d.ts.map +1 -0
- package/dist/commands/new.js +80 -0
- package/dist/commands/new.js.map +1 -0
- package/dist/commands/pageTree.d.ts +18 -0
- package/dist/commands/pageTree.d.ts.map +1 -0
- package/dist/commands/pageTree.js +20 -0
- package/dist/commands/pageTree.js.map +1 -0
- package/dist/commands/shared.d.ts +15 -0
- package/dist/commands/shared.d.ts.map +1 -0
- package/dist/commands/shared.js +27 -0
- package/dist/commands/shared.js.map +1 -0
- package/dist/commands/sync.d.ts +15 -0
- package/dist/commands/sync.d.ts.map +1 -0
- package/dist/commands/sync.js +101 -0
- package/dist/commands/sync.js.map +1 -0
- package/dist/index.d.ts +10 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -1
- package/dist/internal/NodeLayers.d.ts +7 -0
- package/dist/internal/NodeLayers.d.ts.map +1 -0
- package/dist/internal/NodeLayers.js +19 -0
- package/dist/internal/NodeLayers.js.map +1 -0
- package/dist/internal/frontmatter.d.ts +10 -0
- package/dist/internal/frontmatter.d.ts.map +1 -1
- package/dist/internal/frontmatter.js +16 -0
- package/dist/internal/frontmatter.js.map +1 -1
- package/dist/internal/gitCommands.d.ts +78 -0
- package/dist/internal/gitCommands.d.ts.map +1 -0
- package/dist/internal/gitCommands.js +156 -0
- package/dist/internal/gitCommands.js.map +1 -0
- package/dist/internal/hashUtils.d.ts +42 -1
- package/dist/internal/hashUtils.d.ts.map +1 -1
- package/dist/internal/hashUtils.js +38 -2
- package/dist/internal/hashUtils.js.map +1 -1
- package/dist/internal/oauthServer.d.ts +55 -0
- package/dist/internal/oauthServer.d.ts.map +1 -0
- package/dist/internal/oauthServer.js +110 -0
- package/dist/internal/oauthServer.js.map +1 -0
- package/dist/internal/pathUtils.d.ts +21 -4
- package/dist/internal/pathUtils.d.ts.map +1 -1
- package/dist/internal/pathUtils.js +24 -13
- package/dist/internal/pathUtils.js.map +1 -1
- package/dist/internal/tokenStorage.d.ts +75 -0
- package/dist/internal/tokenStorage.d.ts.map +1 -0
- package/dist/internal/tokenStorage.js +149 -0
- package/dist/internal/tokenStorage.js.map +1 -0
- package/dist/internal/userCache.d.ts +42 -0
- package/dist/internal/userCache.d.ts.map +1 -0
- package/dist/internal/userCache.js +51 -0
- package/dist/internal/userCache.js.map +1 -0
- package/dist/parsers/ConfluenceParser.d.ts +26 -0
- package/dist/parsers/ConfluenceParser.d.ts.map +1 -0
- package/dist/parsers/ConfluenceParser.js +792 -0
- package/dist/parsers/ConfluenceParser.js.map +1 -0
- package/dist/parsers/MarkdownParser.d.ts +26 -0
- package/dist/parsers/MarkdownParser.d.ts.map +1 -0
- package/dist/parsers/MarkdownParser.js +873 -0
- package/dist/parsers/MarkdownParser.js.map +1 -0
- package/dist/parsers/index.d.ts +8 -0
- package/dist/parsers/index.d.ts.map +1 -0
- package/dist/parsers/index.js +8 -0
- package/dist/parsers/index.js.map +1 -0
- package/dist/schemas/ConfluenceSchema.d.ts +21 -0
- package/dist/schemas/ConfluenceSchema.d.ts.map +1 -0
- package/dist/schemas/ConfluenceSchema.js +38 -0
- package/dist/schemas/ConfluenceSchema.js.map +1 -0
- package/dist/schemas/ConversionSchema.d.ts +35 -0
- package/dist/schemas/ConversionSchema.d.ts.map +1 -0
- package/dist/schemas/ConversionSchema.js +208 -0
- package/dist/schemas/ConversionSchema.js.map +1 -0
- package/dist/schemas/MarkdownSchema.d.ts +21 -0
- package/dist/schemas/MarkdownSchema.d.ts.map +1 -0
- package/dist/schemas/MarkdownSchema.js +38 -0
- package/dist/schemas/MarkdownSchema.js.map +1 -0
- package/dist/schemas/hast/HastFromHtml.d.ts +27 -0
- package/dist/schemas/hast/HastFromHtml.d.ts.map +1 -0
- package/dist/schemas/hast/HastFromHtml.js +107 -0
- package/dist/schemas/hast/HastFromHtml.js.map +1 -0
- package/dist/schemas/hast/HastSchema.d.ts +195 -0
- package/dist/schemas/hast/HastSchema.d.ts.map +1 -0
- package/dist/schemas/hast/HastSchema.js +183 -0
- package/dist/schemas/hast/HastSchema.js.map +1 -0
- package/dist/schemas/hast/index.d.ts +9 -0
- package/dist/schemas/hast/index.d.ts.map +1 -0
- package/dist/schemas/hast/index.js +3 -0
- package/dist/schemas/hast/index.js.map +1 -0
- package/dist/schemas/index.d.ts +14 -0
- package/dist/schemas/index.d.ts.map +1 -0
- package/dist/schemas/index.js +16 -0
- package/dist/schemas/index.js.map +1 -0
- package/dist/schemas/mdast/MdastFromMarkdown.d.ts +30 -0
- package/dist/schemas/mdast/MdastFromMarkdown.d.ts.map +1 -0
- package/dist/schemas/mdast/MdastFromMarkdown.js +79 -0
- package/dist/schemas/mdast/MdastFromMarkdown.js.map +1 -0
- package/dist/schemas/mdast/MdastSchema.d.ts +385 -0
- package/dist/schemas/mdast/MdastSchema.d.ts.map +1 -0
- package/dist/schemas/mdast/MdastSchema.js +266 -0
- package/dist/schemas/mdast/MdastSchema.js.map +1 -0
- package/dist/schemas/mdast/index.d.ts +10 -0
- package/dist/schemas/mdast/index.d.ts.map +1 -0
- package/dist/schemas/mdast/index.js +4 -0
- package/dist/schemas/mdast/index.js.map +1 -0
- package/dist/schemas/mdast/mdastToString.d.ts +13 -0
- package/dist/schemas/mdast/mdastToString.d.ts.map +1 -0
- package/dist/schemas/mdast/mdastToString.js +85 -0
- package/dist/schemas/mdast/mdastToString.js.map +1 -0
- package/dist/schemas/nodes/block/BlockSchema.d.ts +43 -0
- package/dist/schemas/nodes/block/BlockSchema.d.ts.map +1 -0
- package/dist/schemas/nodes/block/BlockSchema.js +634 -0
- package/dist/schemas/nodes/block/BlockSchema.js.map +1 -0
- package/dist/schemas/nodes/block/index.d.ts +7 -0
- package/dist/schemas/nodes/block/index.d.ts.map +1 -0
- package/dist/schemas/nodes/block/index.js +7 -0
- package/dist/schemas/nodes/block/index.js.map +1 -0
- package/dist/schemas/nodes/index.d.ts +9 -0
- package/dist/schemas/nodes/index.d.ts.map +1 -0
- package/dist/schemas/nodes/index.js +12 -0
- package/dist/schemas/nodes/index.js.map +1 -0
- package/dist/schemas/nodes/inline/InlineSchema.d.ts +48 -0
- package/dist/schemas/nodes/inline/InlineSchema.d.ts.map +1 -0
- package/dist/schemas/nodes/inline/InlineSchema.js +436 -0
- package/dist/schemas/nodes/inline/InlineSchema.js.map +1 -0
- package/dist/schemas/nodes/inline/index.d.ts +7 -0
- package/dist/schemas/nodes/inline/index.d.ts.map +1 -0
- package/dist/schemas/nodes/inline/index.js +7 -0
- package/dist/schemas/nodes/inline/index.js.map +1 -0
- package/dist/schemas/nodes/macro/MacroSchema.d.ts +27 -0
- package/dist/schemas/nodes/macro/MacroSchema.d.ts.map +1 -0
- package/dist/schemas/nodes/macro/MacroSchema.js +162 -0
- package/dist/schemas/nodes/macro/MacroSchema.js.map +1 -0
- package/dist/schemas/nodes/macro/index.d.ts +7 -0
- package/dist/schemas/nodes/macro/index.d.ts.map +1 -0
- package/dist/schemas/nodes/macro/index.js +7 -0
- package/dist/schemas/nodes/macro/index.js.map +1 -0
- package/dist/schemas/preprocessing/ConfluencePreprocessor.d.ts +24 -0
- package/dist/schemas/preprocessing/ConfluencePreprocessor.d.ts.map +1 -0
- package/dist/schemas/preprocessing/ConfluencePreprocessor.js +351 -0
- package/dist/schemas/preprocessing/ConfluencePreprocessor.js.map +1 -0
- package/dist/schemas/preprocessing/index.d.ts +8 -0
- package/dist/schemas/preprocessing/index.d.ts.map +1 -0
- package/dist/schemas/preprocessing/index.js +2 -0
- package/dist/schemas/preprocessing/index.js.map +1 -0
- package/dist/serializers/ConfluenceSerializer.d.ts +30 -0
- package/dist/serializers/ConfluenceSerializer.d.ts.map +1 -0
- package/dist/serializers/ConfluenceSerializer.js +551 -0
- package/dist/serializers/ConfluenceSerializer.js.map +1 -0
- package/dist/serializers/MarkdownSerializer.d.ts +34 -0
- package/dist/serializers/MarkdownSerializer.d.ts.map +1 -0
- package/dist/serializers/MarkdownSerializer.js +355 -0
- package/dist/serializers/MarkdownSerializer.js.map +1 -0
- package/dist/serializers/index.d.ts +8 -0
- package/dist/serializers/index.d.ts.map +1 -0
- package/dist/serializers/index.js +8 -0
- package/dist/serializers/index.js.map +1 -0
- package/package.json +27 -16
- package/src/ConfluenceAuth.ts +571 -0
- package/src/ConfluenceClient.ts +188 -156
- package/src/ConfluenceConfig.ts +63 -7
- package/src/ConfluenceError.ts +110 -14
- package/src/GitError.ts +92 -0
- package/src/GitService.ts +859 -0
- package/src/LocalFileSystem.ts +179 -9
- package/src/MarkdownConverter.ts +126 -122
- package/src/SchemaConverterError.ts +108 -0
- package/src/Schemas.ts +223 -6
- package/src/SyncEngine.ts +745 -162
- package/src/ast/BlockNode.ts +425 -0
- package/src/ast/Document.ts +90 -0
- package/src/ast/InlineNode.ts +323 -0
- package/src/ast/MacroNode.ts +245 -0
- package/src/ast/index.ts +83 -0
- package/src/bin.ts +50 -249
- package/src/commands/auth.ts +117 -0
- package/src/commands/clone.ts +145 -0
- package/src/commands/delete.ts +57 -0
- package/src/commands/errorHandler.ts +32 -0
- package/src/commands/git.ts +114 -0
- package/src/commands/index.ts +10 -0
- package/src/commands/layers.ts +211 -0
- package/src/commands/new.ts +99 -0
- package/src/commands/pageTree.ts +40 -0
- package/src/commands/shared.ts +35 -0
- package/src/commands/sync.ts +129 -0
- package/src/index.ts +21 -1
- package/src/internal/NodeLayers.ts +21 -0
- package/src/internal/frontmatter.ts +21 -0
- package/src/internal/gitCommands.ts +229 -0
- package/src/internal/hashUtils.ts +65 -3
- package/src/internal/oauthServer.ts +199 -0
- package/src/internal/pathUtils.ts +34 -17
- package/src/internal/tokenStorage.ts +240 -0
- package/src/internal/userCache.ts +90 -0
- package/src/parsers/ConfluenceParser.ts +950 -0
- package/src/parsers/MarkdownParser.ts +1198 -0
- package/src/parsers/index.ts +8 -0
- package/src/schemas/ConfluenceSchema.ts +56 -0
- package/src/schemas/ConversionSchema.ts +318 -0
- package/src/schemas/MarkdownSchema.ts +56 -0
- package/src/schemas/hast/HastFromHtml.ts +153 -0
- package/src/schemas/hast/HastSchema.ts +274 -0
- package/src/schemas/hast/index.ts +35 -0
- package/src/schemas/index.ts +20 -0
- package/src/schemas/mdast/MdastFromMarkdown.ts +118 -0
- package/src/schemas/mdast/MdastSchema.ts +566 -0
- package/src/schemas/mdast/index.ts +59 -0
- package/src/schemas/mdast/mdastToString.ts +102 -0
- package/src/schemas/nodes/block/BlockSchema.ts +773 -0
- package/src/schemas/nodes/block/index.ts +13 -0
- package/src/schemas/nodes/index.ts +20 -0
- package/src/schemas/nodes/inline/InlineSchema.ts +523 -0
- package/src/schemas/nodes/inline/index.ts +14 -0
- package/src/schemas/nodes/macro/MacroSchema.ts +226 -0
- package/src/schemas/nodes/macro/index.ts +6 -0
- package/src/schemas/preprocessing/ConfluencePreprocessor.ts +446 -0
- package/src/schemas/preprocessing/index.ts +8 -0
- package/src/serializers/ConfluenceSerializer.ts +717 -0
- package/src/serializers/MarkdownSerializer.ts +493 -0
- package/src/serializers/index.ts +8 -0
- package/test/GitService.test.ts +209 -0
- package/test/MarkdownConverter.test.ts +37 -3
- package/test/Schemas.test.ts +97 -2
- package/test/ast/BlockNode.test.ts +265 -0
- package/test/ast/Document.test.ts +126 -0
- package/test/ast/InlineNode.test.ts +161 -0
- package/test/fixtures/integration-test.html.fixture +103 -0
- package/test/fixtures/integration-test.md.expected +257 -0
- package/test/integration.test.ts +269 -0
- package/test/oauthServer.test.ts +50 -0
- package/test/parsers/ConfluenceParser.test.ts +283 -0
- package/test/schemas/ConfluencePreprocessor.test.ts +180 -0
- package/test/schemas/ConversionSchema.test.ts +159 -0
- package/test/schemas/HastSchema.test.ts +138 -0
- package/test/schemas/MdastSchema.test.ts +145 -0
- package/test/schemas/nodes/block/BlockSchema.test.ts +173 -0
- package/test/schemas/nodes/inline/InlineSchema.test.ts +198 -0
- package/test/schemas/nodes/macro/MacroSchema.test.ts +142 -0
- package/test/tokenStorage.test.ts +99 -0
|
@@ -0,0 +1,773 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Transform schemas for block nodes (Hast <-> AST <-> Mdast).
|
|
3
|
+
*
|
|
4
|
+
* Provides bidirectional transforms between HAST elements, AST block nodes,
|
|
5
|
+
* and MDAST block content for structural document elements.
|
|
6
|
+
*
|
|
7
|
+
* @module
|
|
8
|
+
*/
|
|
9
|
+
import * as Effect from "effect/Effect"
|
|
10
|
+
import * as ParseResult from "effect/ParseResult"
|
|
11
|
+
import * as Schema from "effect/Schema"
|
|
12
|
+
import {
|
|
13
|
+
type BlockNode,
|
|
14
|
+
type BlockQuote,
|
|
15
|
+
CodeBlock,
|
|
16
|
+
Heading,
|
|
17
|
+
Image,
|
|
18
|
+
type List,
|
|
19
|
+
type ListItem,
|
|
20
|
+
Paragraph,
|
|
21
|
+
Table,
|
|
22
|
+
TableCell,
|
|
23
|
+
TableRow,
|
|
24
|
+
type TaskItem,
|
|
25
|
+
type TaskList,
|
|
26
|
+
ThematicBreak,
|
|
27
|
+
UnsupportedBlock
|
|
28
|
+
} from "../../../ast/BlockNode.js"
|
|
29
|
+
import type { InlineNode } from "../../../ast/InlineNode.js"
|
|
30
|
+
import type { HastElement, HastNode, HastText } from "../../hast/index.js"
|
|
31
|
+
import { getTextContent, isHastElement, isHastText, makeHastElement, makeHastText } from "../../hast/index.js"
|
|
32
|
+
import type { MdastBlockContent } from "../../mdast/index.js"
|
|
33
|
+
import { makeMdastCode, makeMdastHeading, makeMdastParagraph } from "../../mdast/index.js"
|
|
34
|
+
import { inlineNodeFromHastElement, inlineNodeToHast, inlineNodeToMdast, textFromHastText } from "../inline/index.js"
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Parse HAST children to inline nodes.
|
|
38
|
+
*/
|
|
39
|
+
const parseHastChildrenToInline = (
|
|
40
|
+
children: ReadonlyArray<HastNode>
|
|
41
|
+
): Effect.Effect<ReadonlyArray<InlineNode>, ParseResult.ParseError> =>
|
|
42
|
+
Effect.gen(function*() {
|
|
43
|
+
const nodes: Array<InlineNode> = []
|
|
44
|
+
const parseChildren = (
|
|
45
|
+
c: ReadonlyArray<HastNode>
|
|
46
|
+
): Effect.Effect<ReadonlyArray<InlineNode>, ParseResult.ParseError> => parseHastChildrenToInline(c)
|
|
47
|
+
|
|
48
|
+
for (const child of children) {
|
|
49
|
+
if (isHastText(child)) {
|
|
50
|
+
if ((child as HastText).value.trim() || nodes.length > 0) {
|
|
51
|
+
nodes.push(textFromHastText(child))
|
|
52
|
+
}
|
|
53
|
+
} else if (isHastElement(child)) {
|
|
54
|
+
const node = yield* inlineNodeFromHastElement(child, parseChildren)
|
|
55
|
+
if (node) nodes.push(node)
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return nodes
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Convert HAST element to AST block node.
|
|
63
|
+
*/
|
|
64
|
+
export const blockNodeFromHastElement = (
|
|
65
|
+
element: HastElement
|
|
66
|
+
): Effect.Effect<BlockNode | null, ParseResult.ParseError> =>
|
|
67
|
+
Effect.gen(function*() {
|
|
68
|
+
const tagName = element.tagName.toLowerCase()
|
|
69
|
+
|
|
70
|
+
// Heading
|
|
71
|
+
if (/^h[1-6]$/.test(tagName)) {
|
|
72
|
+
const levelStr = tagName[1]
|
|
73
|
+
if (!levelStr) return null
|
|
74
|
+
const level = parseInt(levelStr) as 1 | 2 | 3 | 4 | 5 | 6
|
|
75
|
+
const children = yield* parseHastChildrenToInline(element.children)
|
|
76
|
+
return new Heading({ level, children })
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Paragraph
|
|
80
|
+
if (tagName === "p") {
|
|
81
|
+
const children = yield* parseHastChildrenToInline(element.children)
|
|
82
|
+
const style = element.properties?.style as string | undefined
|
|
83
|
+
let alignment: "left" | "center" | "right" | undefined
|
|
84
|
+
let indent: number | undefined
|
|
85
|
+
|
|
86
|
+
if (style) {
|
|
87
|
+
const alignMatch = style.match(/text-align:\s*(left|center|right)/)
|
|
88
|
+
if (alignMatch?.[1]) {
|
|
89
|
+
alignment = alignMatch[1] as "left" | "center" | "right"
|
|
90
|
+
}
|
|
91
|
+
const marginMatch = style.match(/margin-left:\s*(\d+(?:\.\d+)?)\s*px/)
|
|
92
|
+
if (marginMatch?.[1]) {
|
|
93
|
+
indent = parseFloat(marginMatch[1])
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (alignment !== undefined || indent !== undefined) {
|
|
98
|
+
return new Paragraph({
|
|
99
|
+
children,
|
|
100
|
+
...(alignment !== undefined ? { alignment } : {}),
|
|
101
|
+
...(indent !== undefined ? { indent } : {})
|
|
102
|
+
})
|
|
103
|
+
}
|
|
104
|
+
return new Paragraph({ children })
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Code block
|
|
108
|
+
if (tagName === "pre") {
|
|
109
|
+
const codeEl = element.children.find(
|
|
110
|
+
(c): c is HastElement => isHastElement(c) && c.tagName === "code"
|
|
111
|
+
)
|
|
112
|
+
const code = codeEl ? getTextContent(codeEl) : getTextContent(element)
|
|
113
|
+
const language = (element.properties?.["dataLanguage"] as string) || undefined
|
|
114
|
+
return new CodeBlock({ code, language })
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Thematic break
|
|
118
|
+
if (tagName === "hr") {
|
|
119
|
+
return new ThematicBreak({})
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Image
|
|
123
|
+
if (tagName === "img") {
|
|
124
|
+
const src = element.properties?.src as string | undefined
|
|
125
|
+
const dataAttachment = element.properties?.["dataAttachment"] as string | undefined
|
|
126
|
+
const dataAlign = element.properties?.["dataAlign"] as string | undefined
|
|
127
|
+
const dataWidth = element.properties?.["dataWidth"] as string | undefined
|
|
128
|
+
const alt = (element.properties?.alt as string) || undefined
|
|
129
|
+
|
|
130
|
+
if (dataAttachment) {
|
|
131
|
+
return new Image({
|
|
132
|
+
attachment: { filename: dataAttachment },
|
|
133
|
+
alt,
|
|
134
|
+
...(dataAlign ? { align: dataAlign } : {}),
|
|
135
|
+
...(dataWidth ? { width: parseInt(dataWidth) } : {})
|
|
136
|
+
})
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (!src) return null
|
|
140
|
+
return new Image({
|
|
141
|
+
src,
|
|
142
|
+
alt,
|
|
143
|
+
title: (element.properties?.title as string) || undefined
|
|
144
|
+
})
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Table
|
|
148
|
+
if (tagName === "table") {
|
|
149
|
+
return yield* parseTable(element)
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Task list
|
|
153
|
+
if (tagName === "ul" && element.properties?.["dataMacro"] === "task-list") {
|
|
154
|
+
return yield* parseTaskList(element)
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Lists
|
|
158
|
+
if (tagName === "ul" || tagName === "ol") {
|
|
159
|
+
return yield* parseList(element, tagName === "ol")
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Block quote
|
|
163
|
+
if (tagName === "blockquote") {
|
|
164
|
+
const children = yield* parseHastChildrenToSimpleBlocks(element.children)
|
|
165
|
+
return {
|
|
166
|
+
_tag: "BlockQuote" as const,
|
|
167
|
+
version: 1,
|
|
168
|
+
children
|
|
169
|
+
} satisfies BlockQuote
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Unknown block element
|
|
173
|
+
return new UnsupportedBlock({
|
|
174
|
+
rawHtml: hastElementToHtml(element),
|
|
175
|
+
source: "confluence"
|
|
176
|
+
})
|
|
177
|
+
})
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Convert AST block node to HAST element.
|
|
181
|
+
*/
|
|
182
|
+
export const blockNodeToHast = (node: BlockNode): HastElement => {
|
|
183
|
+
switch (node._tag) {
|
|
184
|
+
case "Heading":
|
|
185
|
+
return makeHastElement(
|
|
186
|
+
`h${node.level}`,
|
|
187
|
+
{},
|
|
188
|
+
node.children.map(inlineNodeToHast)
|
|
189
|
+
)
|
|
190
|
+
case "Paragraph": {
|
|
191
|
+
const style = [
|
|
192
|
+
node.alignment ? `text-align: ${node.alignment}` : "",
|
|
193
|
+
node.indent ? `margin-left: ${node.indent}px` : ""
|
|
194
|
+
].filter(Boolean).join("; ")
|
|
195
|
+
return makeHastElement(
|
|
196
|
+
"p",
|
|
197
|
+
style ? { style } : {},
|
|
198
|
+
node.children.map(inlineNodeToHast)
|
|
199
|
+
)
|
|
200
|
+
}
|
|
201
|
+
case "CodeBlock":
|
|
202
|
+
return makeHastElement(
|
|
203
|
+
"pre",
|
|
204
|
+
node.language ? { dataLanguage: node.language } : {},
|
|
205
|
+
[makeHastElement("code", {}, [makeHastText(node.code)])]
|
|
206
|
+
)
|
|
207
|
+
case "ThematicBreak":
|
|
208
|
+
return makeHastElement("hr")
|
|
209
|
+
case "Image":
|
|
210
|
+
if (node.attachment) {
|
|
211
|
+
return makeHastElement("img", {
|
|
212
|
+
dataAttachment: node.attachment.filename,
|
|
213
|
+
...(node.alt ? { alt: node.alt } : {}),
|
|
214
|
+
...(node.align ? { dataAlign: node.align } : {}),
|
|
215
|
+
...(node.width ? { dataWidth: String(node.width) } : {})
|
|
216
|
+
})
|
|
217
|
+
}
|
|
218
|
+
return makeHastElement("img", {
|
|
219
|
+
src: node.src ?? "",
|
|
220
|
+
...(node.alt ? { alt: node.alt } : {}),
|
|
221
|
+
...(node.title ? { title: node.title } : {})
|
|
222
|
+
})
|
|
223
|
+
case "Table":
|
|
224
|
+
return tableToHast(node)
|
|
225
|
+
case "BlockQuote":
|
|
226
|
+
return makeHastElement("blockquote", {}, node.children.map(blockNodeToHast))
|
|
227
|
+
case "List":
|
|
228
|
+
return listToHast(node)
|
|
229
|
+
case "TaskList":
|
|
230
|
+
return taskListToHast(node)
|
|
231
|
+
case "UnsupportedBlock":
|
|
232
|
+
return makeHastElement("div", { dangerouslySetInnerHTML: node.rawHtml ?? node.rawMarkdown ?? "" })
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Convert AST block node to MDAST block content.
|
|
238
|
+
*/
|
|
239
|
+
export const blockNodeToMdast = (node: BlockNode): MdastBlockContent => {
|
|
240
|
+
switch (node._tag) {
|
|
241
|
+
case "Heading":
|
|
242
|
+
return makeMdastHeading(node.level, node.children.map(inlineNodeToMdast))
|
|
243
|
+
case "Paragraph":
|
|
244
|
+
return makeMdastParagraph(node.children.map(inlineNodeToMdast))
|
|
245
|
+
case "CodeBlock":
|
|
246
|
+
return makeMdastCode(node.code, node.language)
|
|
247
|
+
case "ThematicBreak":
|
|
248
|
+
return { type: "thematicBreak" }
|
|
249
|
+
case "Image":
|
|
250
|
+
// MDAST doesn't have block images at root - wrap in paragraph
|
|
251
|
+
return makeMdastParagraph([{
|
|
252
|
+
type: "image",
|
|
253
|
+
url: node.src ?? node.attachment?.filename ?? "",
|
|
254
|
+
alt: node.alt ?? null,
|
|
255
|
+
title: node.title ?? null
|
|
256
|
+
}])
|
|
257
|
+
case "Table":
|
|
258
|
+
return tableToMdast(node)
|
|
259
|
+
case "BlockQuote":
|
|
260
|
+
return {
|
|
261
|
+
type: "blockquote",
|
|
262
|
+
children: node.children.map(blockNodeToMdast)
|
|
263
|
+
}
|
|
264
|
+
case "List":
|
|
265
|
+
return listToMdast(node)
|
|
266
|
+
case "TaskList":
|
|
267
|
+
// Convert to list with checkboxes
|
|
268
|
+
return {
|
|
269
|
+
type: "list",
|
|
270
|
+
ordered: false,
|
|
271
|
+
children: node.children.map((item) => ({
|
|
272
|
+
type: "listItem",
|
|
273
|
+
checked: item.status === "complete",
|
|
274
|
+
children: [{
|
|
275
|
+
type: "paragraph",
|
|
276
|
+
children: item.body.map(inlineNodeToMdast)
|
|
277
|
+
}]
|
|
278
|
+
}))
|
|
279
|
+
}
|
|
280
|
+
case "UnsupportedBlock":
|
|
281
|
+
return {
|
|
282
|
+
type: "html",
|
|
283
|
+
value: node.rawHtml ?? node.rawMarkdown ?? ""
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* Convert MDAST block content to AST block node.
|
|
290
|
+
*/
|
|
291
|
+
export const blockNodeFromMdast = (node: MdastBlockContent): BlockNode => {
|
|
292
|
+
switch (node.type) {
|
|
293
|
+
case "heading":
|
|
294
|
+
return new Heading({
|
|
295
|
+
level: node.depth,
|
|
296
|
+
children: [] // Would need full inline conversion
|
|
297
|
+
})
|
|
298
|
+
case "paragraph":
|
|
299
|
+
return new Paragraph({ children: [] })
|
|
300
|
+
case "code":
|
|
301
|
+
return new CodeBlock({ code: node.value, language: node.lang })
|
|
302
|
+
case "thematicBreak":
|
|
303
|
+
return new ThematicBreak({})
|
|
304
|
+
case "blockquote":
|
|
305
|
+
return {
|
|
306
|
+
_tag: "BlockQuote",
|
|
307
|
+
version: 1,
|
|
308
|
+
children: node.children.map(blockNodeFromMdast)
|
|
309
|
+
} as BlockQuote
|
|
310
|
+
case "list":
|
|
311
|
+
return {
|
|
312
|
+
_tag: "List",
|
|
313
|
+
version: 1,
|
|
314
|
+
ordered: node.ordered ?? false,
|
|
315
|
+
start: node.start,
|
|
316
|
+
children: node.children.map((item) => ({
|
|
317
|
+
_tag: "ListItem" as const,
|
|
318
|
+
checked: item.checked ?? undefined,
|
|
319
|
+
children: [] // Would need full block conversion
|
|
320
|
+
}))
|
|
321
|
+
} as List
|
|
322
|
+
case "table":
|
|
323
|
+
return new Table({ rows: [] })
|
|
324
|
+
case "html":
|
|
325
|
+
return new UnsupportedBlock({ rawMarkdown: node.value, source: "markdown" })
|
|
326
|
+
default:
|
|
327
|
+
return new UnsupportedBlock({ rawMarkdown: JSON.stringify(node), source: "markdown" })
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// Helper functions
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* Parse table element.
|
|
335
|
+
*/
|
|
336
|
+
const parseTable = (element: HastElement): Effect.Effect<Table, ParseResult.ParseError> =>
|
|
337
|
+
Effect.gen(function*() {
|
|
338
|
+
let header: TableRow | undefined
|
|
339
|
+
const rows: Array<TableRow> = []
|
|
340
|
+
|
|
341
|
+
for (const child of element.children) {
|
|
342
|
+
if (!isHastElement(child)) continue
|
|
343
|
+
|
|
344
|
+
if (child.tagName === "thead") {
|
|
345
|
+
const tr = child.children.find(
|
|
346
|
+
(c): c is HastElement => isHastElement(c) && c.tagName === "tr"
|
|
347
|
+
)
|
|
348
|
+
if (tr) {
|
|
349
|
+
header = yield* parseTableRow(tr, true)
|
|
350
|
+
}
|
|
351
|
+
} else if (child.tagName === "tbody") {
|
|
352
|
+
for (const row of child.children) {
|
|
353
|
+
if (isHastElement(row) && row.tagName === "tr") {
|
|
354
|
+
const allTh = row.children
|
|
355
|
+
.filter(isHastElement)
|
|
356
|
+
.every((c) => c.tagName === "th")
|
|
357
|
+
if (allTh && !header && rows.length === 0) {
|
|
358
|
+
header = yield* parseTableRow(row, true)
|
|
359
|
+
} else {
|
|
360
|
+
rows.push(yield* parseTableRow(row, false))
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
} else if (child.tagName === "tr") {
|
|
365
|
+
rows.push(yield* parseTableRow(child, false))
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
return new Table({ header, rows })
|
|
370
|
+
})
|
|
371
|
+
|
|
372
|
+
/**
|
|
373
|
+
* Parse table row.
|
|
374
|
+
*/
|
|
375
|
+
const parseTableRow = (
|
|
376
|
+
element: HastElement,
|
|
377
|
+
isHeader: boolean
|
|
378
|
+
): Effect.Effect<TableRow, ParseResult.ParseError> =>
|
|
379
|
+
Effect.gen(function*() {
|
|
380
|
+
const cells: Array<TableCell> = []
|
|
381
|
+
for (const child of element.children) {
|
|
382
|
+
if (isHastElement(child) && (child.tagName === "td" || child.tagName === "th")) {
|
|
383
|
+
const cellIsHeader = isHeader || child.tagName === "th"
|
|
384
|
+
const children = yield* parseCellContent(child.children)
|
|
385
|
+
cells.push(new TableCell({ isHeader: cellIsHeader, children }))
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
return new TableRow({ cells })
|
|
389
|
+
})
|
|
390
|
+
|
|
391
|
+
/**
|
|
392
|
+
* Parse cell content, unwrapping single <p> elements.
|
|
393
|
+
*/
|
|
394
|
+
const parseCellContent = (
|
|
395
|
+
children: ReadonlyArray<HastNode>
|
|
396
|
+
): Effect.Effect<ReadonlyArray<InlineNode>, ParseResult.ParseError> =>
|
|
397
|
+
Effect.gen(function*() {
|
|
398
|
+
const elementChildren = children.filter((c) => {
|
|
399
|
+
if (isHastElement(c)) return true
|
|
400
|
+
if (isHastText(c) && c.value.trim()) return true
|
|
401
|
+
return false
|
|
402
|
+
})
|
|
403
|
+
|
|
404
|
+
if (elementChildren.length === 1) {
|
|
405
|
+
const first = elementChildren[0]
|
|
406
|
+
if (first && isHastElement(first) && first.tagName === "p") {
|
|
407
|
+
return yield* parseHastChildrenToInline(first.children)
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
return yield* parseHastChildrenToInline(children)
|
|
412
|
+
})
|
|
413
|
+
|
|
414
|
+
/**
|
|
415
|
+
* Parse task list element.
|
|
416
|
+
*/
|
|
417
|
+
const parseTaskList = (element: HastElement): Effect.Effect<TaskList, ParseResult.ParseError> =>
|
|
418
|
+
Effect.gen(function*() {
|
|
419
|
+
const items: Array<TaskItem> = []
|
|
420
|
+
|
|
421
|
+
for (const child of element.children) {
|
|
422
|
+
if (isHastElement(child) && child.tagName === "li") {
|
|
423
|
+
const id = (child.properties?.["dataTaskId"] as string) || ""
|
|
424
|
+
const uuid = (child.properties?.["dataTaskUuid"] as string) || ""
|
|
425
|
+
const status = (child.properties?.["dataTaskStatus"] as string) === "complete"
|
|
426
|
+
? "complete" as const
|
|
427
|
+
: "incomplete" as const
|
|
428
|
+
const body = yield* parseHastChildrenToInline(child.children)
|
|
429
|
+
|
|
430
|
+
items.push({
|
|
431
|
+
_tag: "TaskItem",
|
|
432
|
+
id,
|
|
433
|
+
uuid,
|
|
434
|
+
status,
|
|
435
|
+
body
|
|
436
|
+
})
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
return {
|
|
441
|
+
_tag: "TaskList" as const,
|
|
442
|
+
version: 1,
|
|
443
|
+
children: items
|
|
444
|
+
}
|
|
445
|
+
})
|
|
446
|
+
|
|
447
|
+
/**
|
|
448
|
+
* Parse list element.
|
|
449
|
+
*/
|
|
450
|
+
const parseList = (
|
|
451
|
+
element: HastElement,
|
|
452
|
+
ordered: boolean
|
|
453
|
+
): Effect.Effect<List, ParseResult.ParseError> =>
|
|
454
|
+
Effect.gen(function*() {
|
|
455
|
+
const items: Array<ListItem> = []
|
|
456
|
+
const startProp = element.properties?.start
|
|
457
|
+
const start = ordered && startProp ? parseInt(String(startProp)) : undefined
|
|
458
|
+
|
|
459
|
+
for (const child of element.children) {
|
|
460
|
+
if (isHastElement(child) && child.tagName === "li") {
|
|
461
|
+
const children = yield* parseListItemContent(child.children)
|
|
462
|
+
const checkbox = child.children.find(
|
|
463
|
+
(c): c is HastElement =>
|
|
464
|
+
isHastElement(c) &&
|
|
465
|
+
c.tagName === "input" &&
|
|
466
|
+
c.properties?.type === "checkbox"
|
|
467
|
+
)
|
|
468
|
+
const checked = checkbox ? (checkbox.properties?.checked === true) : undefined
|
|
469
|
+
if (checked !== undefined) {
|
|
470
|
+
items.push({ _tag: "ListItem", checked, children })
|
|
471
|
+
} else {
|
|
472
|
+
items.push({ _tag: "ListItem", children })
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
if (start !== undefined) {
|
|
478
|
+
return { _tag: "List" as const, version: 1, ordered, start, children: items }
|
|
479
|
+
}
|
|
480
|
+
return { _tag: "List" as const, version: 1, ordered, children: items }
|
|
481
|
+
})
|
|
482
|
+
|
|
483
|
+
type SimpleBlock = Heading | Paragraph | CodeBlock | ThematicBreak | Image | Table | UnsupportedBlock
|
|
484
|
+
|
|
485
|
+
/**
|
|
486
|
+
* Parse HAST children to simple block nodes.
|
|
487
|
+
*/
|
|
488
|
+
const parseHastChildrenToSimpleBlocks = (
|
|
489
|
+
children: ReadonlyArray<HastNode>
|
|
490
|
+
): Effect.Effect<Array<SimpleBlock>, ParseResult.ParseError> =>
|
|
491
|
+
Effect.gen(function*() {
|
|
492
|
+
const blocks: Array<SimpleBlock> = []
|
|
493
|
+
for (const child of children) {
|
|
494
|
+
if (isHastElement(child)) {
|
|
495
|
+
const tagName = child.tagName.toLowerCase()
|
|
496
|
+
|
|
497
|
+
if (/^h[1-6]$/.test(tagName)) {
|
|
498
|
+
const levelStr = tagName[1]
|
|
499
|
+
if (levelStr) {
|
|
500
|
+
const level = parseInt(levelStr) as 1 | 2 | 3 | 4 | 5 | 6
|
|
501
|
+
const inlineChildren = yield* parseHastChildrenToInline(child.children)
|
|
502
|
+
blocks.push(new Heading({ level, children: inlineChildren }))
|
|
503
|
+
}
|
|
504
|
+
} else if (tagName === "p") {
|
|
505
|
+
const inlineChildren = yield* parseHastChildrenToInline(child.children)
|
|
506
|
+
blocks.push(new Paragraph({ children: inlineChildren }))
|
|
507
|
+
} else if (tagName === "pre") {
|
|
508
|
+
const codeEl = child.children.find(
|
|
509
|
+
(c): c is HastElement => isHastElement(c) && c.tagName === "code"
|
|
510
|
+
)
|
|
511
|
+
const code = codeEl ? getTextContent(codeEl) : getTextContent(child)
|
|
512
|
+
blocks.push(new CodeBlock({ code }))
|
|
513
|
+
} else if (tagName === "hr") {
|
|
514
|
+
blocks.push(new ThematicBreak({}))
|
|
515
|
+
} else if (tagName === "img") {
|
|
516
|
+
const src = child.properties?.src as string | undefined
|
|
517
|
+
if (src) blocks.push(new Image({ src }))
|
|
518
|
+
} else if (tagName === "table") {
|
|
519
|
+
blocks.push(yield* parseTable(child))
|
|
520
|
+
} else {
|
|
521
|
+
blocks.push(new UnsupportedBlock({ rawHtml: hastElementToHtml(child), source: "confluence" }))
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
return blocks
|
|
526
|
+
})
|
|
527
|
+
|
|
528
|
+
/**
|
|
529
|
+
* Parse list item content.
|
|
530
|
+
*/
|
|
531
|
+
const parseListItemContent = (
|
|
532
|
+
children: ReadonlyArray<HastNode>
|
|
533
|
+
): Effect.Effect<Array<SimpleBlock>, ParseResult.ParseError> =>
|
|
534
|
+
Effect.gen(function*() {
|
|
535
|
+
const blocks: Array<SimpleBlock> = []
|
|
536
|
+
|
|
537
|
+
const hasDirectInlineContent = children.some((child) => {
|
|
538
|
+
if (isHastText(child)) {
|
|
539
|
+
return child.value.trim() !== ""
|
|
540
|
+
}
|
|
541
|
+
if (isHastElement(child)) {
|
|
542
|
+
const tagName = child.tagName.toLowerCase()
|
|
543
|
+
return ["a", "strong", "em", "b", "i", "u", "code", "span", "del", "sub", "sup"].includes(tagName)
|
|
544
|
+
}
|
|
545
|
+
return false
|
|
546
|
+
})
|
|
547
|
+
|
|
548
|
+
if (hasDirectInlineContent) {
|
|
549
|
+
const inlineChildren = yield* parseHastChildrenToInline(children)
|
|
550
|
+
if (inlineChildren.length > 0) {
|
|
551
|
+
blocks.push(new Paragraph({ children: inlineChildren }))
|
|
552
|
+
}
|
|
553
|
+
return blocks
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
for (const child of children) {
|
|
557
|
+
if (!isHastElement(child)) continue
|
|
558
|
+
const tagName = child.tagName.toLowerCase()
|
|
559
|
+
|
|
560
|
+
if (tagName === "p") {
|
|
561
|
+
const inlineChildren = yield* parseHastChildrenToInline(child.children)
|
|
562
|
+
blocks.push(new Paragraph({ children: inlineChildren }))
|
|
563
|
+
} else if (tagName === "ul" || tagName === "ol") {
|
|
564
|
+
blocks.push(new UnsupportedBlock({ rawHtml: hastElementToHtml(child), source: "confluence" }))
|
|
565
|
+
} else if (tagName === "pre") {
|
|
566
|
+
const codeEl = child.children.find(
|
|
567
|
+
(c): c is HastElement => isHastElement(c) && c.tagName === "code"
|
|
568
|
+
)
|
|
569
|
+
const code = codeEl ? getTextContent(codeEl) : getTextContent(child)
|
|
570
|
+
blocks.push(new CodeBlock({ code }))
|
|
571
|
+
} else if (tagName === "hr") {
|
|
572
|
+
blocks.push(new ThematicBreak({}))
|
|
573
|
+
} else if (tagName === "img") {
|
|
574
|
+
const src = child.properties?.src as string | undefined
|
|
575
|
+
if (src) blocks.push(new Image({ src }))
|
|
576
|
+
} else if (tagName === "table") {
|
|
577
|
+
blocks.push(yield* parseTable(child))
|
|
578
|
+
} else if (/^h[1-6]$/.test(tagName)) {
|
|
579
|
+
const levelStr = tagName[1]
|
|
580
|
+
if (levelStr) {
|
|
581
|
+
const level = parseInt(levelStr) as 1 | 2 | 3 | 4 | 5 | 6
|
|
582
|
+
const inlineChildren = yield* parseHastChildrenToInline(child.children)
|
|
583
|
+
blocks.push(new Heading({ level, children: inlineChildren }))
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
return blocks
|
|
589
|
+
})
|
|
590
|
+
|
|
591
|
+
/**
|
|
592
|
+
* Convert table to HAST.
|
|
593
|
+
*/
|
|
594
|
+
const tableToHast = (table: Table): HastElement => {
|
|
595
|
+
const rows = table.rows.map((row) =>
|
|
596
|
+
makeHastElement(
|
|
597
|
+
"tr",
|
|
598
|
+
{},
|
|
599
|
+
row.cells.map((cell) =>
|
|
600
|
+
makeHastElement(
|
|
601
|
+
cell.isHeader ? "th" : "td",
|
|
602
|
+
{},
|
|
603
|
+
cell.children.map(inlineNodeToHast)
|
|
604
|
+
)
|
|
605
|
+
)
|
|
606
|
+
)
|
|
607
|
+
)
|
|
608
|
+
|
|
609
|
+
if (table.header) {
|
|
610
|
+
const headerRow = makeHastElement(
|
|
611
|
+
"tr",
|
|
612
|
+
{},
|
|
613
|
+
table.header.cells.map((cell) => makeHastElement("th", {}, cell.children.map(inlineNodeToHast)))
|
|
614
|
+
)
|
|
615
|
+
return makeHastElement("table", {}, [
|
|
616
|
+
makeHastElement("thead", {}, [headerRow]),
|
|
617
|
+
makeHastElement("tbody", {}, rows)
|
|
618
|
+
])
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
return makeHastElement("table", {}, [makeHastElement("tbody", {}, rows)])
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
/**
|
|
625
|
+
* Convert table to MDAST.
|
|
626
|
+
*/
|
|
627
|
+
const tableToMdast = (table: Table): MdastBlockContent => {
|
|
628
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
629
|
+
const rows: Array<any> = []
|
|
630
|
+
|
|
631
|
+
if (table.header) {
|
|
632
|
+
rows.push({
|
|
633
|
+
type: "tableRow",
|
|
634
|
+
children: table.header.cells.map((cell) => ({
|
|
635
|
+
type: "tableCell",
|
|
636
|
+
children: cell.children.map(inlineNodeToMdast)
|
|
637
|
+
}))
|
|
638
|
+
})
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
for (const row of table.rows) {
|
|
642
|
+
rows.push({
|
|
643
|
+
type: "tableRow",
|
|
644
|
+
children: row.cells.map((cell) => ({
|
|
645
|
+
type: "tableCell",
|
|
646
|
+
children: cell.children.map(inlineNodeToMdast)
|
|
647
|
+
}))
|
|
648
|
+
})
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
652
|
+
return { type: "table", children: rows } as any
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
/**
|
|
656
|
+
* Convert list to HAST.
|
|
657
|
+
*/
|
|
658
|
+
const listToHast = (list: List): HastElement => {
|
|
659
|
+
const items = list.children.map((item) => makeHastElement("li", {}, item.children.map(blockNodeToHast)))
|
|
660
|
+
return makeHastElement(
|
|
661
|
+
list.ordered ? "ol" : "ul",
|
|
662
|
+
list.start !== undefined ? { start: String(list.start) } : {},
|
|
663
|
+
items
|
|
664
|
+
)
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
/**
|
|
668
|
+
* Convert list to MDAST.
|
|
669
|
+
*/
|
|
670
|
+
const listToMdast = (list: List): MdastBlockContent => {
|
|
671
|
+
const result = {
|
|
672
|
+
type: "list" as const,
|
|
673
|
+
ordered: list.ordered,
|
|
674
|
+
children: list.children.map((item) => ({
|
|
675
|
+
type: "listItem" as const,
|
|
676
|
+
checked: item.checked ?? null,
|
|
677
|
+
children: item.children.map(blockNodeToMdast)
|
|
678
|
+
}))
|
|
679
|
+
}
|
|
680
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
681
|
+
if (list.start !== undefined) (result as any).start = list.start
|
|
682
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
683
|
+
return result as any
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
/**
|
|
687
|
+
* Convert task list to HAST.
|
|
688
|
+
*/
|
|
689
|
+
const taskListToHast = (taskList: TaskList): HastElement => {
|
|
690
|
+
const items = taskList.children.map((item) =>
|
|
691
|
+
makeHastElement(
|
|
692
|
+
"li",
|
|
693
|
+
{
|
|
694
|
+
dataTaskId: item.id,
|
|
695
|
+
dataTaskUuid: item.uuid,
|
|
696
|
+
dataTaskStatus: item.status
|
|
697
|
+
},
|
|
698
|
+
item.body.map(inlineNodeToHast)
|
|
699
|
+
)
|
|
700
|
+
)
|
|
701
|
+
return makeHastElement("ul", { dataMacro: "task-list" }, items)
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
/**
|
|
705
|
+
* Convert HAST element to HTML string.
|
|
706
|
+
*/
|
|
707
|
+
const hastElementToHtml = (element: HastElement): string => {
|
|
708
|
+
const props = Object.entries(element.properties || {})
|
|
709
|
+
.map(([k, v]) => {
|
|
710
|
+
const attrName = k.replace(/([A-Z])/g, "-$1").toLowerCase()
|
|
711
|
+
return `${attrName}="${String(v)}"`
|
|
712
|
+
})
|
|
713
|
+
.join(" ")
|
|
714
|
+
const openTag = props ? `<${element.tagName} ${props}>` : `<${element.tagName}>`
|
|
715
|
+
const closeTag = `</${element.tagName}>`
|
|
716
|
+
const content = element.children
|
|
717
|
+
.map((c) => {
|
|
718
|
+
if (isHastText(c)) return c.value
|
|
719
|
+
if (isHastElement(c)) return hastElementToHtml(c)
|
|
720
|
+
return ""
|
|
721
|
+
})
|
|
722
|
+
.join("")
|
|
723
|
+
return `${openTag}${content}${closeTag}`
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
/**
|
|
727
|
+
* Schema-based HAST to BlockNode array transform.
|
|
728
|
+
*
|
|
729
|
+
* @category Schemas
|
|
730
|
+
*/
|
|
731
|
+
export const BlockNodesFromHast = Schema.transformOrFail(
|
|
732
|
+
Schema.Array(Schema.Unknown),
|
|
733
|
+
Schema.Array(Schema.Any),
|
|
734
|
+
{
|
|
735
|
+
strict: false,
|
|
736
|
+
decode: (hastNodes, _options, ast) =>
|
|
737
|
+
Effect.gen(function*() {
|
|
738
|
+
const results: Array<BlockNode> = []
|
|
739
|
+
|
|
740
|
+
for (const hastNode of hastNodes) {
|
|
741
|
+
if (isHastElement(hastNode as HastNode)) {
|
|
742
|
+
const node = yield* blockNodeFromHastElement(hastNode as HastElement)
|
|
743
|
+
if (node) results.push(node)
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
return results
|
|
748
|
+
}).pipe(
|
|
749
|
+
Effect.mapError((e) =>
|
|
750
|
+
e instanceof ParseResult.ParseError
|
|
751
|
+
? e.issue
|
|
752
|
+
: new ParseResult.Type(ast, hastNodes, String(e))
|
|
753
|
+
)
|
|
754
|
+
),
|
|
755
|
+
encode: (nodes, _options, _ast) => Effect.succeed(nodes.map(blockNodeToHast) as ReadonlyArray<unknown>)
|
|
756
|
+
}
|
|
757
|
+
)
|
|
758
|
+
|
|
759
|
+
/**
|
|
760
|
+
* Schema-based MDAST to BlockNode array transform.
|
|
761
|
+
*
|
|
762
|
+
* @category Schemas
|
|
763
|
+
*/
|
|
764
|
+
export const BlockNodesFromMdast = Schema.transformOrFail(
|
|
765
|
+
Schema.Array(Schema.Unknown),
|
|
766
|
+
Schema.Array(Schema.Any),
|
|
767
|
+
{
|
|
768
|
+
strict: false,
|
|
769
|
+
decode: (mdastNodes, _options, _ast) =>
|
|
770
|
+
Effect.succeed(mdastNodes.map((n) => blockNodeFromMdast(n as MdastBlockContent))),
|
|
771
|
+
encode: (nodes, _options, _ast) => Effect.succeed(nodes.map(blockNodeToMdast) as ReadonlyArray<unknown>)
|
|
772
|
+
}
|
|
773
|
+
)
|