@knpkv/confluence-to-markdown 0.2.0 → 0.4.2
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 +73 -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 +366 -0
- package/dist/ConfluenceAuth.js.map +1 -0
- package/dist/ConfluenceClient.d.ts +26 -12
- package/dist/ConfluenceClient.d.ts.map +1 -1
- package/dist/ConfluenceClient.js +139 -97
- 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 +99 -16
- 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 +32 -21
- package/src/ConfluenceAuth.ts +581 -0
- package/src/ConfluenceClient.ts +230 -165
- 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,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bi-directional schema for Confluence HTML <-> AST transformation.
|
|
3
|
+
*
|
|
4
|
+
* @module
|
|
5
|
+
*/
|
|
6
|
+
import * as Effect from "effect/Effect"
|
|
7
|
+
import * as ParseResult from "effect/ParseResult"
|
|
8
|
+
import * as Schema from "effect/Schema"
|
|
9
|
+
import type { Document } from "../ast/Document.js"
|
|
10
|
+
import { parseConfluenceHtml } from "../parsers/ConfluenceParser.js"
|
|
11
|
+
import { serializeToConfluence } from "../serializers/ConfluenceSerializer.js"
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Schema.Type placeholder for Document to use with transformOrFail.
|
|
15
|
+
* We use Schema.Any since Document is already a union type.
|
|
16
|
+
*/
|
|
17
|
+
const DocumentSchema = Schema.Any as Schema.Schema<Document, Document>
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Bi-directional schema: Confluence HTML string <-> Document AST.
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```typescript
|
|
24
|
+
* import { DocumentFromConfluence } from "@knpkv/confluence-to-markdown/schemas/ConfluenceSchema"
|
|
25
|
+
* import { Schema, Effect } from "effect"
|
|
26
|
+
*
|
|
27
|
+
* // Decode: HTML -> AST
|
|
28
|
+
* const decodeResult = Schema.decodeUnknown(DocumentFromConfluence)(htmlString)
|
|
29
|
+
*
|
|
30
|
+
* // Encode: AST -> HTML
|
|
31
|
+
* const encodeResult = Schema.encode(DocumentFromConfluence)(document)
|
|
32
|
+
* ```
|
|
33
|
+
*
|
|
34
|
+
* @category Schemas
|
|
35
|
+
*/
|
|
36
|
+
export const DocumentFromConfluence: Schema.Schema<
|
|
37
|
+
Document,
|
|
38
|
+
string,
|
|
39
|
+
never
|
|
40
|
+
> = Schema.transformOrFail(
|
|
41
|
+
Schema.String,
|
|
42
|
+
DocumentSchema,
|
|
43
|
+
{
|
|
44
|
+
strict: true,
|
|
45
|
+
decode: (html, _opts, ast) =>
|
|
46
|
+
Effect.mapError(
|
|
47
|
+
parseConfluenceHtml(html),
|
|
48
|
+
(err) => new ParseResult.Type(ast, html, err.message)
|
|
49
|
+
),
|
|
50
|
+
encode: (doc, _opts, ast) =>
|
|
51
|
+
Effect.mapError(
|
|
52
|
+
serializeToConfluence(doc),
|
|
53
|
+
(err) => new ParseResult.Type(ast, doc, err.message)
|
|
54
|
+
)
|
|
55
|
+
}
|
|
56
|
+
)
|
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Direct conversion schema: Confluence HTML <-> Markdown.
|
|
3
|
+
*
|
|
4
|
+
* Composes preprocessing, parsing, and serialization into a single transform.
|
|
5
|
+
*
|
|
6
|
+
* @module
|
|
7
|
+
*/
|
|
8
|
+
import * as Effect from "effect/Effect"
|
|
9
|
+
import * as ParseResult from "effect/ParseResult"
|
|
10
|
+
import * as Schema from "effect/Schema"
|
|
11
|
+
import type { BlockNode } from "../ast/BlockNode.js"
|
|
12
|
+
import { type Document, makeDocument } from "../ast/Document.js"
|
|
13
|
+
import type { MacroNode } from "../ast/MacroNode.js"
|
|
14
|
+
import type { HastElement, HastNode, HastRoot } from "./hast/index.js"
|
|
15
|
+
import { HastFromHtml, isHastElement } from "./hast/index.js"
|
|
16
|
+
import type { MdastBlockContent, MdastRoot } from "./mdast/index.js"
|
|
17
|
+
import { MdastFromMarkdown, mdastToString } from "./mdast/index.js"
|
|
18
|
+
import { blockNodeFromHastElement, blockNodeFromMdast, blockNodeToMdast } from "./nodes/index.js"
|
|
19
|
+
import { macroNodeFromHastElement, macroNodeToMdast } from "./nodes/macro/index.js"
|
|
20
|
+
import { PreprocessedHtmlFromConfluence } from "./preprocessing/index.js"
|
|
21
|
+
|
|
22
|
+
type DocumentNode = BlockNode | MacroNode
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Parse HAST children to simple block nodes.
|
|
26
|
+
* Note: Returns BlockNode which is broader than SimpleBlock used by macros,
|
|
27
|
+
* but at runtime only simple blocks are returned from single-level parsing.
|
|
28
|
+
*/
|
|
29
|
+
const parseBlockChildren = (
|
|
30
|
+
children: ReadonlyArray<HastNode>
|
|
31
|
+
): Effect.Effect<ReadonlyArray<BlockNode>, ParseResult.ParseError> =>
|
|
32
|
+
Effect.forEach(
|
|
33
|
+
children.filter(isHastElement),
|
|
34
|
+
(el) =>
|
|
35
|
+
blockNodeFromHastElement(el).pipe(
|
|
36
|
+
Effect.map((node): ReadonlyArray<BlockNode> => (node !== null ? [node] : []))
|
|
37
|
+
)
|
|
38
|
+
).pipe(Effect.map((arrays) => arrays.flat()))
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Parse single HAST element to document node (block or macro).
|
|
42
|
+
*/
|
|
43
|
+
const documentNodeFromHastElement = (
|
|
44
|
+
element: HastElement
|
|
45
|
+
): Effect.Effect<DocumentNode | null, ParseResult.ParseError> =>
|
|
46
|
+
Effect.gen(function*() {
|
|
47
|
+
// Try macro first
|
|
48
|
+
const macro = yield* macroNodeFromHastElement(element, parseBlockChildren)
|
|
49
|
+
if (macro !== null) {
|
|
50
|
+
return macro
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Fall back to block
|
|
54
|
+
const block = yield* blockNodeFromHastElement(element)
|
|
55
|
+
return block
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Parse HAST root to Document AST.
|
|
60
|
+
*/
|
|
61
|
+
const documentFromHastRoot = (
|
|
62
|
+
root: HastRoot,
|
|
63
|
+
rawConfluence?: string
|
|
64
|
+
): Effect.Effect<Document, ParseResult.ParseError> =>
|
|
65
|
+
Effect.gen(function*() {
|
|
66
|
+
const elements = root.children.filter(isHastElement)
|
|
67
|
+
const nodeArrays = yield* Effect.forEach(elements, (el) =>
|
|
68
|
+
documentNodeFromHastElement(el).pipe(
|
|
69
|
+
Effect.map((node) => (node !== null ? [node] : []))
|
|
70
|
+
))
|
|
71
|
+
const children = nodeArrays.flat()
|
|
72
|
+
return makeDocument(children, rawConfluence)
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Parse MDAST root to Document AST.
|
|
77
|
+
*/
|
|
78
|
+
const documentFromMdastRoot = (root: MdastRoot): Document => {
|
|
79
|
+
const children: Array<DocumentNode> = []
|
|
80
|
+
for (const node of root.children) {
|
|
81
|
+
const blockContent = node as MdastBlockContent
|
|
82
|
+
const block = blockNodeFromMdast(blockContent)
|
|
83
|
+
children.push(block)
|
|
84
|
+
}
|
|
85
|
+
return makeDocument(children)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Check if node is a macro node.
|
|
90
|
+
*/
|
|
91
|
+
const isMacroNode = (node: DocumentNode): node is MacroNode => {
|
|
92
|
+
return node._tag === "InfoPanel" ||
|
|
93
|
+
node._tag === "ExpandMacro" ||
|
|
94
|
+
node._tag === "TocMacro" ||
|
|
95
|
+
node._tag === "CodeMacro" ||
|
|
96
|
+
node._tag === "StatusMacro"
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Serialize Document AST to Markdown.
|
|
101
|
+
*/
|
|
102
|
+
const documentToMarkdown = (doc: Document): string => {
|
|
103
|
+
const parts: Array<string> = []
|
|
104
|
+
for (const node of doc.children) {
|
|
105
|
+
const mdast = isMacroNode(node)
|
|
106
|
+
? macroNodeToMdast(node)
|
|
107
|
+
: blockNodeToMdast(node as BlockNode)
|
|
108
|
+
parts.push(mdastToString(mdast))
|
|
109
|
+
}
|
|
110
|
+
return parts.join("\n\n")
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Schema for transforming preprocessed Confluence HTML to Document AST.
|
|
115
|
+
*/
|
|
116
|
+
export const DocumentFromHast: Schema.Schema<
|
|
117
|
+
Document,
|
|
118
|
+
HastRoot,
|
|
119
|
+
never
|
|
120
|
+
> = Schema.transformOrFail(
|
|
121
|
+
Schema.Any as Schema.Schema<HastRoot, HastRoot>,
|
|
122
|
+
Schema.Any as Schema.Schema<Document, Document>,
|
|
123
|
+
{
|
|
124
|
+
strict: true,
|
|
125
|
+
decode: (root, _options, ast) =>
|
|
126
|
+
documentFromHastRoot(root).pipe(
|
|
127
|
+
Effect.mapError((e) => new ParseResult.Type(ast, root, e.message))
|
|
128
|
+
),
|
|
129
|
+
encode: (_doc, _options, ast) =>
|
|
130
|
+
Effect.fail(new ParseResult.Type(ast, _doc, "Document to HAST encoding not implemented"))
|
|
131
|
+
}
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Schema for transforming MDAST root to Document AST.
|
|
136
|
+
*/
|
|
137
|
+
export const DocumentFromMdast: Schema.Schema<
|
|
138
|
+
Document,
|
|
139
|
+
MdastRoot,
|
|
140
|
+
never
|
|
141
|
+
> = Schema.transformOrFail(
|
|
142
|
+
Schema.Any as Schema.Schema<MdastRoot, MdastRoot>,
|
|
143
|
+
Schema.Any as Schema.Schema<Document, Document>,
|
|
144
|
+
{
|
|
145
|
+
strict: true,
|
|
146
|
+
decode: (root, _options, _ast) => Effect.succeed(documentFromMdastRoot(root)),
|
|
147
|
+
encode: (_doc, _options, ast) =>
|
|
148
|
+
Effect.fail(new ParseResult.Type(ast, _doc, "Document to MDAST encoding not implemented"))
|
|
149
|
+
}
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Direct conversion schema: Confluence HTML string <-> Markdown string.
|
|
154
|
+
*
|
|
155
|
+
* @example
|
|
156
|
+
* ```typescript
|
|
157
|
+
* import { ConfluenceToMarkdown } from "@knpkv/confluence-to-markdown/schemas/ConversionSchema"
|
|
158
|
+
* import { Schema, Effect } from "effect"
|
|
159
|
+
*
|
|
160
|
+
* // Decode: Confluence HTML -> Markdown
|
|
161
|
+
* const markdown = Effect.runSync(
|
|
162
|
+
* Schema.decode(ConfluenceToMarkdown)(confluenceHtml)
|
|
163
|
+
* )
|
|
164
|
+
*
|
|
165
|
+
* // Encode: Markdown -> Confluence HTML (limited support)
|
|
166
|
+
* const html = Effect.runSync(
|
|
167
|
+
* Schema.encode(ConfluenceToMarkdown)(markdown)
|
|
168
|
+
* )
|
|
169
|
+
* ```
|
|
170
|
+
*
|
|
171
|
+
* @category Schemas
|
|
172
|
+
*/
|
|
173
|
+
export const ConfluenceToMarkdown: Schema.Schema<
|
|
174
|
+
string,
|
|
175
|
+
string,
|
|
176
|
+
never
|
|
177
|
+
> = Schema.transformOrFail(
|
|
178
|
+
Schema.String,
|
|
179
|
+
Schema.String,
|
|
180
|
+
{
|
|
181
|
+
strict: true,
|
|
182
|
+
decode: (html, _options, ast) =>
|
|
183
|
+
Effect.gen(function*() {
|
|
184
|
+
// Preprocess
|
|
185
|
+
const preprocessed = yield* Schema.decode(PreprocessedHtmlFromConfluence)(html).pipe(
|
|
186
|
+
Effect.mapError((e) => new ParseResult.Type(ast, html, `Preprocessing failed: ${e.message}`))
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
// Parse to HAST
|
|
190
|
+
const hast = yield* Schema.decode(HastFromHtml)(preprocessed).pipe(
|
|
191
|
+
Effect.mapError((e) => new ParseResult.Type(ast, html, `HAST parsing failed: ${e.message}`))
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
// Convert to Document
|
|
195
|
+
const doc = yield* documentFromHastRoot(hast, html).pipe(
|
|
196
|
+
Effect.mapError((e) => new ParseResult.Type(ast, html, `Document conversion failed: ${e.message}`))
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
// Serialize to Markdown
|
|
200
|
+
return documentToMarkdown(doc)
|
|
201
|
+
}),
|
|
202
|
+
encode: (md, _options, ast) =>
|
|
203
|
+
Schema.decode(MdastFromMarkdown)(md).pipe(
|
|
204
|
+
Effect.mapError((e) => new ParseResult.Type(ast, md, `MDAST parsing failed: ${e.message}`)),
|
|
205
|
+
Effect.map((mdast) => {
|
|
206
|
+
const doc = documentFromMdastRoot(mdast)
|
|
207
|
+
|
|
208
|
+
// Note: Full HTML serialization is complex; return basic HTML
|
|
209
|
+
// For full fidelity, use the existing ConfluenceSerializer
|
|
210
|
+
const parts: Array<string> = []
|
|
211
|
+
for (const node of doc.children) {
|
|
212
|
+
parts.push(documentNodeToHtml(node))
|
|
213
|
+
}
|
|
214
|
+
return parts.join("\n")
|
|
215
|
+
})
|
|
216
|
+
)
|
|
217
|
+
}
|
|
218
|
+
)
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Basic document node to HTML conversion.
|
|
222
|
+
*/
|
|
223
|
+
const documentNodeToHtml = (node: DocumentNode): string => {
|
|
224
|
+
switch (node._tag) {
|
|
225
|
+
case "Heading":
|
|
226
|
+
return `<h${node.level}>${node.children.map(inlineToHtml).join("")}</h${node.level}>`
|
|
227
|
+
case "Paragraph":
|
|
228
|
+
return `<p>${node.children.map(inlineToHtml).join("")}</p>`
|
|
229
|
+
case "CodeBlock":
|
|
230
|
+
return `<pre><code${node.language ? ` class="language-${node.language}"` : ""}>${
|
|
231
|
+
escapeHtml(node.code)
|
|
232
|
+
}</code></pre>`
|
|
233
|
+
case "ThematicBreak":
|
|
234
|
+
return "<hr />"
|
|
235
|
+
case "Image":
|
|
236
|
+
return `<img src="${escapeHtml(node.src ?? "")}" alt="${escapeHtml(node.alt ?? "")}" />`
|
|
237
|
+
case "BlockQuote":
|
|
238
|
+
return `<blockquote>${node.children.map((c) => documentNodeToHtml(c as DocumentNode)).join("")}</blockquote>`
|
|
239
|
+
case "List": {
|
|
240
|
+
const tag = node.ordered ? "ol" : "ul"
|
|
241
|
+
return `<${tag}>${
|
|
242
|
+
node.children.map((item) =>
|
|
243
|
+
`<li>${item.children.map((c) => documentNodeToHtml(c as DocumentNode)).join("")}</li>`
|
|
244
|
+
).join("")
|
|
245
|
+
}</${tag}>`
|
|
246
|
+
}
|
|
247
|
+
case "Table":
|
|
248
|
+
return `<table>${
|
|
249
|
+
node.rows.map((row) =>
|
|
250
|
+
`<tr>${
|
|
251
|
+
row.cells.map((cell) =>
|
|
252
|
+
cell.isHeader
|
|
253
|
+
? `<th>${cell.children.map(inlineToHtml).join("")}</th>`
|
|
254
|
+
: `<td>${cell.children.map(inlineToHtml).join("")}</td>`
|
|
255
|
+
).join("")
|
|
256
|
+
}</tr>`
|
|
257
|
+
).join("")
|
|
258
|
+
}</table>`
|
|
259
|
+
case "InfoPanel":
|
|
260
|
+
return `<div data-macro="${node.panelType}">${
|
|
261
|
+
node.children.map((c) => documentNodeToHtml(c as DocumentNode)).join("")
|
|
262
|
+
}</div>`
|
|
263
|
+
case "ExpandMacro":
|
|
264
|
+
return `<details><summary>${escapeHtml(node.title ?? "")}</summary>${
|
|
265
|
+
node.children.map((c) => documentNodeToHtml(c as DocumentNode)).join("")
|
|
266
|
+
}</details>`
|
|
267
|
+
case "TocMacro":
|
|
268
|
+
return `<nav data-macro="toc"></nav>`
|
|
269
|
+
case "CodeMacro":
|
|
270
|
+
return `<pre data-macro="code"${node.language ? ` data-language="${node.language}"` : ""}><code>${
|
|
271
|
+
escapeHtml(node.code)
|
|
272
|
+
}</code></pre>`
|
|
273
|
+
case "StatusMacro":
|
|
274
|
+
return `<span data-macro="status" data-color="${node.color}">${escapeHtml(node.text)}</span>`
|
|
275
|
+
default:
|
|
276
|
+
return ""
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* Basic inline node to HTML.
|
|
282
|
+
*/
|
|
283
|
+
const inlineToHtml = (
|
|
284
|
+
node: { _tag: string; value?: string; children?: ReadonlyArray<unknown>; url?: string }
|
|
285
|
+
): string => {
|
|
286
|
+
switch (node._tag) {
|
|
287
|
+
case "Text":
|
|
288
|
+
return escapeHtml(node.value ?? "")
|
|
289
|
+
case "Strong":
|
|
290
|
+
return `<strong>${
|
|
291
|
+
(node.children as ReadonlyArray<{ _tag: string; value?: string }>)?.map(inlineToHtml).join("") ?? ""
|
|
292
|
+
}</strong>`
|
|
293
|
+
case "Emphasis":
|
|
294
|
+
return `<em>${
|
|
295
|
+
(node.children as ReadonlyArray<{ _tag: string; value?: string }>)?.map(inlineToHtml).join("") ?? ""
|
|
296
|
+
}</em>`
|
|
297
|
+
case "InlineCode":
|
|
298
|
+
return `<code>${escapeHtml(node.value ?? "")}</code>`
|
|
299
|
+
case "Link":
|
|
300
|
+
return `<a href="${escapeHtml(node.url ?? "")}">${
|
|
301
|
+
(node.children as ReadonlyArray<{ _tag: string; value?: string }>)?.map(inlineToHtml).join("") ?? ""
|
|
302
|
+
}</a>`
|
|
303
|
+
case "LineBreak":
|
|
304
|
+
return "<br />"
|
|
305
|
+
default:
|
|
306
|
+
return ""
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* Escape HTML special characters.
|
|
312
|
+
*/
|
|
313
|
+
const escapeHtml = (str: string): string =>
|
|
314
|
+
str
|
|
315
|
+
.replace(/&/g, "&")
|
|
316
|
+
.replace(/</g, "<")
|
|
317
|
+
.replace(/>/g, ">")
|
|
318
|
+
.replace(/"/g, """)
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bi-directional schema for Markdown <-> AST transformation.
|
|
3
|
+
*
|
|
4
|
+
* @module
|
|
5
|
+
*/
|
|
6
|
+
import * as Effect from "effect/Effect"
|
|
7
|
+
import * as ParseResult from "effect/ParseResult"
|
|
8
|
+
import * as Schema from "effect/Schema"
|
|
9
|
+
import type { Document } from "../ast/Document.js"
|
|
10
|
+
import { parseMarkdown } from "../parsers/MarkdownParser.js"
|
|
11
|
+
import { serializeToMarkdown } from "../serializers/MarkdownSerializer.js"
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Schema.Type placeholder for Document to use with transformOrFail.
|
|
15
|
+
* We use Schema.Any since Document is already a union type.
|
|
16
|
+
*/
|
|
17
|
+
const DocumentSchema = Schema.Any as Schema.Schema<Document, Document>
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Bi-directional schema: Markdown string <-> Document AST.
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```typescript
|
|
24
|
+
* import { DocumentFromMarkdown } from "@knpkv/confluence-to-markdown/schemas/MarkdownSchema"
|
|
25
|
+
* import { Schema, Effect } from "effect"
|
|
26
|
+
*
|
|
27
|
+
* // Decode: Markdown -> AST
|
|
28
|
+
* const decodeResult = Schema.decodeUnknown(DocumentFromMarkdown)(markdownString)
|
|
29
|
+
*
|
|
30
|
+
* // Encode: AST -> Markdown
|
|
31
|
+
* const encodeResult = Schema.encode(DocumentFromMarkdown)(document)
|
|
32
|
+
* ```
|
|
33
|
+
*
|
|
34
|
+
* @category Schemas
|
|
35
|
+
*/
|
|
36
|
+
export const DocumentFromMarkdown: Schema.Schema<
|
|
37
|
+
Document,
|
|
38
|
+
string,
|
|
39
|
+
never
|
|
40
|
+
> = Schema.transformOrFail(
|
|
41
|
+
Schema.String,
|
|
42
|
+
DocumentSchema,
|
|
43
|
+
{
|
|
44
|
+
strict: true,
|
|
45
|
+
decode: (md, _opts, ast) =>
|
|
46
|
+
Effect.mapError(
|
|
47
|
+
parseMarkdown(md),
|
|
48
|
+
(err) => new ParseResult.Type(ast, md, err.message)
|
|
49
|
+
),
|
|
50
|
+
encode: (doc, _opts, ast) =>
|
|
51
|
+
Effect.mapError(
|
|
52
|
+
serializeToMarkdown(doc),
|
|
53
|
+
(err) => new ParseResult.Type(ast, doc, err.message)
|
|
54
|
+
)
|
|
55
|
+
}
|
|
56
|
+
)
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Schema transformation from HTML string to HAST (Hypertext Abstract Syntax Tree).
|
|
3
|
+
*
|
|
4
|
+
* Uses rehype-parse for HTML parsing and wraps the result in Effect Schema types.
|
|
5
|
+
*
|
|
6
|
+
* @module
|
|
7
|
+
*/
|
|
8
|
+
import * as Effect from "effect/Effect"
|
|
9
|
+
import * as ParseResult from "effect/ParseResult"
|
|
10
|
+
import * as Schema from "effect/Schema"
|
|
11
|
+
import rehypeParse from "rehype-parse"
|
|
12
|
+
import rehypeStringify from "rehype-stringify"
|
|
13
|
+
import { unified } from "unified"
|
|
14
|
+
import type { HastNode, HastRoot } from "./HastSchema.js"
|
|
15
|
+
import {
|
|
16
|
+
HastRootSchema,
|
|
17
|
+
makeHastComment,
|
|
18
|
+
makeHastDoctype,
|
|
19
|
+
makeHastElement,
|
|
20
|
+
makeHastRoot,
|
|
21
|
+
makeHastText
|
|
22
|
+
} from "./HastSchema.js"
|
|
23
|
+
|
|
24
|
+
// Unist/Hast types from rehype-parse (simplified)
|
|
25
|
+
interface UnistNode {
|
|
26
|
+
type: string
|
|
27
|
+
value?: string
|
|
28
|
+
tagName?: string
|
|
29
|
+
properties?: Record<string, unknown>
|
|
30
|
+
children?: Array<UnistNode>
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
interface UnistRoot {
|
|
34
|
+
type: "root"
|
|
35
|
+
children: Array<UnistNode>
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Convert unist node from rehype-parse to our HastNode type.
|
|
40
|
+
*/
|
|
41
|
+
const convertToHastNode = (node: UnistNode): HastNode => {
|
|
42
|
+
switch (node.type) {
|
|
43
|
+
case "text":
|
|
44
|
+
return makeHastText(node.value ?? "")
|
|
45
|
+
case "comment":
|
|
46
|
+
return makeHastComment(node.value ?? "")
|
|
47
|
+
case "doctype":
|
|
48
|
+
return makeHastDoctype()
|
|
49
|
+
case "element":
|
|
50
|
+
return makeHastElement(
|
|
51
|
+
node.tagName ?? "div",
|
|
52
|
+
node.properties ?? {},
|
|
53
|
+
(node.children ?? []).map(convertToHastNode)
|
|
54
|
+
)
|
|
55
|
+
default:
|
|
56
|
+
// Unknown node type - treat as comment to preserve
|
|
57
|
+
return makeHastComment(`unknown:${node.type}`)
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Convert our HastNode type back to unist format for serialization.
|
|
63
|
+
*/
|
|
64
|
+
const convertFromHastNode = (node: HastNode): UnistNode => {
|
|
65
|
+
switch (node._tag) {
|
|
66
|
+
case "text":
|
|
67
|
+
return { type: "text", value: node.value }
|
|
68
|
+
case "comment":
|
|
69
|
+
return { type: "comment", value: node.value }
|
|
70
|
+
case "doctype":
|
|
71
|
+
return { type: "doctype" }
|
|
72
|
+
case "element":
|
|
73
|
+
return {
|
|
74
|
+
type: "element",
|
|
75
|
+
tagName: node.tagName,
|
|
76
|
+
properties: node.properties,
|
|
77
|
+
children: node.children.map(convertFromHastNode)
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Convert unist root to HastRoot.
|
|
84
|
+
*/
|
|
85
|
+
const convertToHastRoot = (root: UnistRoot): HastRoot => makeHastRoot(root.children.map(convertToHastNode))
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Convert HastRoot back to unist root for serialization.
|
|
89
|
+
*/
|
|
90
|
+
const convertFromHastRoot = (root: HastRoot): UnistRoot => ({
|
|
91
|
+
type: "root",
|
|
92
|
+
children: root.children.map(convertFromHastNode)
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Transform HTML string to HAST root.
|
|
97
|
+
*
|
|
98
|
+
* Uses rehype-parse to parse HTML and converts the result to our schema types.
|
|
99
|
+
* Supports bidirectional transformation (decode HTML -> HAST, encode HAST -> HTML).
|
|
100
|
+
*
|
|
101
|
+
* @example
|
|
102
|
+
* ```typescript
|
|
103
|
+
* import { HastFromHtml } from "@knpkv/confluence-to-markdown/schemas/hast"
|
|
104
|
+
* import * as Schema from "effect/Schema"
|
|
105
|
+
* import { Effect } from "effect"
|
|
106
|
+
*
|
|
107
|
+
* const program = Effect.gen(function* () {
|
|
108
|
+
* const hast = yield* Schema.decode(HastFromHtml)("<h1>Hello</h1><p>World</p>")
|
|
109
|
+
* console.log(hast.children.length) // 2
|
|
110
|
+
* console.log(hast.children[0]._tag) // "element"
|
|
111
|
+
* })
|
|
112
|
+
*
|
|
113
|
+
* Effect.runPromise(program)
|
|
114
|
+
* ```
|
|
115
|
+
*
|
|
116
|
+
* @category Hast
|
|
117
|
+
*/
|
|
118
|
+
export const HastFromHtml: Schema.Schema<HastRoot, string> = Schema.transformOrFail(
|
|
119
|
+
Schema.String,
|
|
120
|
+
HastRootSchema,
|
|
121
|
+
{
|
|
122
|
+
strict: true,
|
|
123
|
+
decode: (html, _options, ast) =>
|
|
124
|
+
Effect.try({
|
|
125
|
+
try: () => {
|
|
126
|
+
const result = unified()
|
|
127
|
+
.use(rehypeParse, { fragment: true })
|
|
128
|
+
.parse(html) as UnistRoot
|
|
129
|
+
return convertToHastRoot(result)
|
|
130
|
+
},
|
|
131
|
+
catch: (error) =>
|
|
132
|
+
new ParseResult.Type(
|
|
133
|
+
ast,
|
|
134
|
+
html,
|
|
135
|
+
`HTML parse error: ${error instanceof Error ? error.message : String(error)}`
|
|
136
|
+
)
|
|
137
|
+
}),
|
|
138
|
+
encode: (hast, _options, ast) =>
|
|
139
|
+
Effect.try({
|
|
140
|
+
try: () => {
|
|
141
|
+
const unist = convertFromHastRoot(hast)
|
|
142
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
143
|
+
return unified().use(rehypeStringify).stringify(unist as any)
|
|
144
|
+
},
|
|
145
|
+
catch: (error) =>
|
|
146
|
+
new ParseResult.Type(
|
|
147
|
+
ast,
|
|
148
|
+
hast,
|
|
149
|
+
`HTML stringify error: ${error instanceof Error ? error.message : String(error)}`
|
|
150
|
+
)
|
|
151
|
+
})
|
|
152
|
+
}
|
|
153
|
+
)
|