@knpkv/confluence-to-markdown 0.5.0 → 0.7.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 +61 -0
- package/README.md +58 -14
- package/dist/AdfPlaceholders.d.ts +42 -0
- package/dist/AdfPlaceholders.d.ts.map +1 -0
- package/dist/AdfPlaceholders.js +547 -0
- package/dist/AdfPlaceholders.js.map +1 -0
- package/dist/AdfSchemaValidator.d.ts +37 -0
- package/dist/AdfSchemaValidator.d.ts.map +1 -0
- package/dist/AdfSchemaValidator.js +37 -0
- package/dist/AdfSchemaValidator.js.map +1 -0
- package/dist/AdfWalker.d.ts +39 -0
- package/dist/AdfWalker.d.ts.map +1 -0
- package/dist/AdfWalker.js +527 -0
- package/dist/AdfWalker.js.map +1 -0
- package/dist/AtlaskitTransformers.d.ts +35 -0
- package/dist/AtlaskitTransformers.d.ts.map +1 -0
- package/dist/AtlaskitTransformers.js +48 -0
- package/dist/AtlaskitTransformers.js.map +1 -0
- package/dist/Brand.d.ts +6 -6
- package/dist/Brand.d.ts.map +1 -1
- package/dist/Brand.js +8 -6
- package/dist/Brand.js.map +1 -1
- package/dist/ConfluenceAuth.d.ts +4 -4
- package/dist/ConfluenceAuth.d.ts.map +1 -1
- package/dist/ConfluenceAuth.js +15 -27
- package/dist/ConfluenceAuth.js.map +1 -1
- package/dist/ConfluenceClient.d.ts +4 -4
- package/dist/ConfluenceClient.d.ts.map +1 -1
- package/dist/ConfluenceClient.js +21 -14
- package/dist/ConfluenceClient.js.map +1 -1
- package/dist/ConfluenceConfig.d.ts +3 -3
- package/dist/ConfluenceConfig.d.ts.map +1 -1
- package/dist/ConfluenceConfig.js +13 -11
- package/dist/ConfluenceConfig.js.map +1 -1
- package/dist/ConfluenceError.d.ts +56 -4
- package/dist/ConfluenceError.d.ts.map +1 -1
- package/dist/ConfluenceError.js +30 -1
- package/dist/ConfluenceError.js.map +1 -1
- package/dist/GitService.d.ts +11 -3
- package/dist/GitService.d.ts.map +1 -1
- package/dist/GitService.js +19 -27
- package/dist/GitService.js.map +1 -1
- package/dist/LocalFileSystem.d.ts +3 -3
- package/dist/LocalFileSystem.d.ts.map +1 -1
- package/dist/LocalFileSystem.js +6 -6
- package/dist/LocalFileSystem.js.map +1 -1
- package/dist/MarkdownConverter.d.ts +16 -65
- package/dist/MarkdownConverter.d.ts.map +1 -1
- package/dist/MarkdownConverter.js +64 -85
- package/dist/MarkdownConverter.js.map +1 -1
- package/dist/Schemas.d.ts +128 -141
- package/dist/Schemas.d.ts.map +1 -1
- package/dist/Schemas.js +21 -23
- package/dist/Schemas.js.map +1 -1
- package/dist/SyncEngine.d.ts +8 -5
- package/dist/SyncEngine.d.ts.map +1 -1
- package/dist/SyncEngine.js +189 -113
- package/dist/SyncEngine.js.map +1 -1
- package/dist/bin.js +23 -35
- package/dist/bin.js.map +1 -1
- package/dist/commands/auth.d.ts +2 -14
- package/dist/commands/auth.d.ts.map +1 -1
- package/dist/commands/auth.js +11 -16
- package/dist/commands/auth.js.map +1 -1
- package/dist/commands/clone.d.ts +4 -6
- package/dist/commands/clone.d.ts.map +1 -1
- package/dist/commands/clone.js +34 -32
- package/dist/commands/clone.js.map +1 -1
- package/dist/commands/delete.d.ts +2 -10
- package/dist/commands/delete.d.ts.map +1 -1
- package/dist/commands/delete.js +5 -4
- package/dist/commands/delete.js.map +1 -1
- package/dist/commands/errorHandler.d.ts +2 -1
- package/dist/commands/errorHandler.d.ts.map +1 -1
- package/dist/commands/errorHandler.js +22 -15
- package/dist/commands/errorHandler.js.map +1 -1
- package/dist/commands/fetch.d.ts +27 -0
- package/dist/commands/fetch.d.ts.map +1 -0
- package/dist/commands/fetch.js +48 -0
- package/dist/commands/fetch.js.map +1 -0
- package/dist/commands/git.d.ts +7 -10
- package/dist/commands/git.d.ts.map +1 -1
- package/dist/commands/git.js +6 -6
- package/dist/commands/git.js.map +1 -1
- package/dist/commands/index.d.ts +1 -0
- package/dist/commands/index.d.ts.map +1 -1
- package/dist/commands/index.js +1 -0
- package/dist/commands/index.js.map +1 -1
- package/dist/commands/layers.d.ts +10 -9
- package/dist/commands/layers.d.ts.map +1 -1
- package/dist/commands/layers.js +41 -30
- package/dist/commands/layers.js.map +1 -1
- package/dist/commands/new.d.ts +2 -6
- package/dist/commands/new.d.ts.map +1 -1
- package/dist/commands/new.js +5 -4
- package/dist/commands/new.js.map +1 -1
- package/dist/commands/pageInput.d.ts +19 -0
- package/dist/commands/pageInput.d.ts.map +1 -0
- package/dist/commands/pageInput.js +68 -0
- package/dist/commands/pageInput.js.map +1 -0
- package/dist/commands/root.d.ts +8 -0
- package/dist/commands/root.d.ts.map +1 -0
- package/dist/commands/root.js +29 -0
- package/dist/commands/root.js.map +1 -0
- package/dist/commands/sync.d.ts +6 -9
- package/dist/commands/sync.d.ts.map +1 -1
- package/dist/commands/sync.js +5 -6
- package/dist/commands/sync.js.map +1 -1
- package/dist/index.d.ts +3 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -13
- package/dist/index.js.map +1 -1
- package/dist/internal/NodeLayers.d.ts.map +1 -1
- package/dist/internal/NodeLayers.js +1 -2
- package/dist/internal/NodeLayers.js.map +1 -1
- package/dist/internal/adfMetadata.d.ts +30 -0
- package/dist/internal/adfMetadata.d.ts.map +1 -0
- package/dist/internal/adfMetadata.js +126 -0
- package/dist/internal/adfMetadata.js.map +1 -0
- package/dist/internal/cleanMarkdown.d.ts +5 -0
- package/dist/internal/cleanMarkdown.d.ts.map +1 -0
- package/dist/internal/cleanMarkdown.js +13 -0
- package/dist/internal/cleanMarkdown.js.map +1 -0
- package/dist/internal/frontmatter.d.ts.map +1 -1
- package/dist/internal/frontmatter.js +41 -8
- package/dist/internal/frontmatter.js.map +1 -1
- package/dist/internal/gitCommands.d.ts +9 -3
- package/dist/internal/gitCommands.d.ts.map +1 -1
- package/dist/internal/gitCommands.js +18 -9
- package/dist/internal/gitCommands.js.map +1 -1
- package/dist/internal/hashUtils.d.ts +1 -1
- package/dist/internal/hashUtils.d.ts.map +1 -1
- package/dist/internal/hashUtils.js +1 -1
- package/dist/internal/hashUtils.js.map +1 -1
- package/dist/internal/oauthServer.d.ts +10 -5
- package/dist/internal/oauthServer.d.ts.map +1 -1
- package/dist/internal/oauthServer.js +19 -40
- package/dist/internal/oauthServer.js.map +1 -1
- package/dist/internal/pathUtils.d.ts +1 -1
- package/dist/internal/pathUtils.d.ts.map +1 -1
- package/dist/internal/pathUtils.js +1 -1
- package/dist/internal/pathUtils.js.map +1 -1
- package/dist/internal/process.d.ts +15 -0
- package/dist/internal/process.d.ts.map +1 -0
- package/dist/internal/process.js +10 -0
- package/dist/internal/process.js.map +1 -0
- package/dist/internal/stdio.d.ts +6 -0
- package/dist/internal/stdio.d.ts.map +1 -0
- package/dist/internal/stdio.js +15 -0
- package/dist/internal/stdio.js.map +1 -0
- package/dist/internal/tokenStorage.d.ts +3 -13
- package/dist/internal/tokenStorage.d.ts.map +1 -1
- package/dist/internal/tokenStorage.js +26 -24
- package/dist/internal/tokenStorage.js.map +1 -1
- package/dist/internal/userCache.d.ts +1 -1
- package/dist/internal/userCache.d.ts.map +1 -1
- package/dist/internal/userCache.js +1 -1
- package/dist/internal/userCache.js.map +1 -1
- package/package.json +29 -20
- package/skills/confluence/SKILL.md +143 -0
- package/skills/confluence/agents/openai.yaml +4 -0
- package/src/AdfPlaceholders.ts +563 -0
- package/src/AdfSchemaValidator.ts +65 -0
- package/src/AdfWalker.ts +591 -0
- package/src/AtlaskitTransformers.ts +70 -0
- package/src/Brand.ts +11 -16
- package/src/ConfluenceAuth.ts +22 -30
- package/src/ConfluenceClient.ts +28 -24
- package/src/ConfluenceConfig.ts +14 -14
- package/src/ConfluenceError.ts +65 -3
- package/src/GitService.ts +39 -49
- package/src/LocalFileSystem.ts +7 -9
- package/src/MarkdownConverter.ts +108 -143
- package/src/Schemas.ts +17 -16
- package/src/SyncEngine.ts +272 -127
- package/src/atlaskit-adf-schema.d.ts +3 -0
- package/src/bin.ts +30 -56
- package/src/commands/auth.ts +21 -18
- package/src/commands/clone.ts +46 -38
- package/src/commands/delete.ts +5 -4
- package/src/commands/errorHandler.ts +25 -18
- package/src/commands/fetch.ts +90 -0
- package/src/commands/git.ts +6 -6
- package/src/commands/index.ts +1 -0
- package/src/commands/layers.ts +64 -37
- package/src/commands/new.ts +5 -4
- package/src/commands/pageInput.ts +103 -0
- package/src/commands/root.ts +59 -0
- package/src/commands/sync.ts +7 -6
- package/src/index.ts +3 -18
- package/src/internal/NodeLayers.ts +1 -2
- package/src/internal/adfMetadata.ts +145 -0
- package/src/internal/cleanMarkdown.ts +15 -0
- package/src/internal/frontmatter.ts +45 -8
- package/src/internal/gitCommands.ts +23 -17
- package/src/internal/hashUtils.ts +2 -2
- package/src/internal/oauthServer.ts +84 -105
- package/src/internal/pathUtils.ts +1 -1
- package/src/internal/process.ts +15 -0
- package/src/internal/stdio.ts +22 -0
- package/src/internal/tokenStorage.ts +39 -29
- package/src/internal/userCache.ts +2 -2
- package/test/AdfPlaceholders.test.ts +508 -0
- package/test/AdfSchemaValidator.test.ts +34 -0
- package/test/AdfWalker.test.ts +676 -0
- package/test/AtlaskitTransformers.test.ts +25 -0
- package/test/Brand.test.ts +11 -11
- package/test/GitService.test.ts +6 -2
- package/test/MarkdownConverter.test.ts +121 -105
- package/test/RoundTrip.test.ts +521 -0
- package/test/Schemas.test.ts +40 -40
- package/test/adfMetadata.test.ts +110 -0
- package/test/cleanMarkdown.test.ts +36 -0
- package/test/commandHarness.test.ts +79 -0
- package/test/commandHarness.ts +147 -0
- package/test/fetch.test.ts +61 -0
- package/test/frontmatter.test.ts +41 -0
- package/test/integration.test.ts +569 -156
- package/test/layers.test.ts +12 -0
- package/test/oauthServer.test.ts +4 -5
- package/test/pageInput.test.ts +83 -0
- package/test/tokenStorage.test.ts +17 -17
- package/dist/SchemaConverterError.d.ts +0 -108
- package/dist/SchemaConverterError.d.ts.map +0 -1
- package/dist/SchemaConverterError.js +0 -84
- package/dist/SchemaConverterError.js.map +0 -1
- package/dist/ast/BlockNode.d.ts +0 -453
- package/dist/ast/BlockNode.d.ts.map +0 -1
- package/dist/ast/BlockNode.js +0 -310
- package/dist/ast/BlockNode.js.map +0 -1
- package/dist/ast/Document.d.ts +0 -216
- package/dist/ast/Document.d.ts.map +0 -1
- package/dist/ast/Document.js +0 -69
- package/dist/ast/Document.js.map +0 -1
- package/dist/ast/InlineNode.d.ts +0 -477
- package/dist/ast/InlineNode.d.ts.map +0 -1
- package/dist/ast/InlineNode.js +0 -263
- package/dist/ast/InlineNode.js.map +0 -1
- package/dist/ast/MacroNode.d.ts +0 -267
- package/dist/ast/MacroNode.d.ts.map +0 -1
- package/dist/ast/MacroNode.js +0 -164
- package/dist/ast/MacroNode.js.map +0 -1
- package/dist/ast/index.d.ts +0 -10
- package/dist/ast/index.d.ts.map +0 -1
- package/dist/ast/index.js +0 -14
- package/dist/ast/index.js.map +0 -1
- package/dist/parsers/ConfluenceParser.d.ts +0 -26
- package/dist/parsers/ConfluenceParser.d.ts.map +0 -1
- package/dist/parsers/ConfluenceParser.js +0 -797
- package/dist/parsers/ConfluenceParser.js.map +0 -1
- package/dist/parsers/MarkdownParser.d.ts +0 -26
- package/dist/parsers/MarkdownParser.d.ts.map +0 -1
- package/dist/parsers/MarkdownParser.js +0 -982
- package/dist/parsers/MarkdownParser.js.map +0 -1
- package/dist/parsers/index.d.ts +0 -8
- package/dist/parsers/index.d.ts.map +0 -1
- package/dist/parsers/index.js +0 -8
- package/dist/parsers/index.js.map +0 -1
- package/dist/schemas/ConfluenceSchema.d.ts +0 -21
- package/dist/schemas/ConfluenceSchema.d.ts.map +0 -1
- package/dist/schemas/ConfluenceSchema.js +0 -38
- package/dist/schemas/ConfluenceSchema.js.map +0 -1
- package/dist/schemas/ConversionSchema.d.ts +0 -35
- package/dist/schemas/ConversionSchema.d.ts.map +0 -1
- package/dist/schemas/ConversionSchema.js +0 -208
- package/dist/schemas/ConversionSchema.js.map +0 -1
- package/dist/schemas/MarkdownSchema.d.ts +0 -21
- package/dist/schemas/MarkdownSchema.d.ts.map +0 -1
- package/dist/schemas/MarkdownSchema.js +0 -38
- package/dist/schemas/MarkdownSchema.js.map +0 -1
- package/dist/schemas/hast/HastFromHtml.d.ts +0 -27
- package/dist/schemas/hast/HastFromHtml.d.ts.map +0 -1
- package/dist/schemas/hast/HastFromHtml.js +0 -107
- package/dist/schemas/hast/HastFromHtml.js.map +0 -1
- package/dist/schemas/hast/HastSchema.d.ts +0 -195
- package/dist/schemas/hast/HastSchema.d.ts.map +0 -1
- package/dist/schemas/hast/HastSchema.js +0 -183
- package/dist/schemas/hast/HastSchema.js.map +0 -1
- package/dist/schemas/hast/index.d.ts +0 -9
- package/dist/schemas/hast/index.d.ts.map +0 -1
- package/dist/schemas/hast/index.js +0 -3
- package/dist/schemas/hast/index.js.map +0 -1
- package/dist/schemas/index.d.ts +0 -14
- package/dist/schemas/index.d.ts.map +0 -1
- package/dist/schemas/index.js +0 -16
- package/dist/schemas/index.js.map +0 -1
- package/dist/schemas/mdast/MdastFromMarkdown.d.ts +0 -30
- package/dist/schemas/mdast/MdastFromMarkdown.d.ts.map +0 -1
- package/dist/schemas/mdast/MdastFromMarkdown.js +0 -79
- package/dist/schemas/mdast/MdastFromMarkdown.js.map +0 -1
- package/dist/schemas/mdast/MdastSchema.d.ts +0 -385
- package/dist/schemas/mdast/MdastSchema.d.ts.map +0 -1
- package/dist/schemas/mdast/MdastSchema.js +0 -266
- package/dist/schemas/mdast/MdastSchema.js.map +0 -1
- package/dist/schemas/mdast/index.d.ts +0 -10
- package/dist/schemas/mdast/index.d.ts.map +0 -1
- package/dist/schemas/mdast/index.js +0 -4
- package/dist/schemas/mdast/index.js.map +0 -1
- package/dist/schemas/mdast/mdastToString.d.ts +0 -13
- package/dist/schemas/mdast/mdastToString.d.ts.map +0 -1
- package/dist/schemas/mdast/mdastToString.js +0 -85
- package/dist/schemas/mdast/mdastToString.js.map +0 -1
- package/dist/schemas/nodes/block/BlockSchema.d.ts +0 -43
- package/dist/schemas/nodes/block/BlockSchema.d.ts.map +0 -1
- package/dist/schemas/nodes/block/BlockSchema.js +0 -634
- package/dist/schemas/nodes/block/BlockSchema.js.map +0 -1
- package/dist/schemas/nodes/block/index.d.ts +0 -7
- package/dist/schemas/nodes/block/index.d.ts.map +0 -1
- package/dist/schemas/nodes/block/index.js +0 -7
- package/dist/schemas/nodes/block/index.js.map +0 -1
- package/dist/schemas/nodes/index.d.ts +0 -9
- package/dist/schemas/nodes/index.d.ts.map +0 -1
- package/dist/schemas/nodes/index.js +0 -12
- package/dist/schemas/nodes/index.js.map +0 -1
- package/dist/schemas/nodes/inline/InlineSchema.d.ts +0 -48
- package/dist/schemas/nodes/inline/InlineSchema.d.ts.map +0 -1
- package/dist/schemas/nodes/inline/InlineSchema.js +0 -436
- package/dist/schemas/nodes/inline/InlineSchema.js.map +0 -1
- package/dist/schemas/nodes/inline/index.d.ts +0 -7
- package/dist/schemas/nodes/inline/index.d.ts.map +0 -1
- package/dist/schemas/nodes/inline/index.js +0 -7
- package/dist/schemas/nodes/inline/index.js.map +0 -1
- package/dist/schemas/nodes/macro/MacroSchema.d.ts +0 -27
- package/dist/schemas/nodes/macro/MacroSchema.d.ts.map +0 -1
- package/dist/schemas/nodes/macro/MacroSchema.js +0 -162
- package/dist/schemas/nodes/macro/MacroSchema.js.map +0 -1
- package/dist/schemas/nodes/macro/index.d.ts +0 -7
- package/dist/schemas/nodes/macro/index.d.ts.map +0 -1
- package/dist/schemas/nodes/macro/index.js +0 -7
- package/dist/schemas/nodes/macro/index.js.map +0 -1
- package/dist/schemas/preprocessing/ConfluencePreprocessor.d.ts +0 -24
- package/dist/schemas/preprocessing/ConfluencePreprocessor.d.ts.map +0 -1
- package/dist/schemas/preprocessing/ConfluencePreprocessor.js +0 -359
- package/dist/schemas/preprocessing/ConfluencePreprocessor.js.map +0 -1
- package/dist/schemas/preprocessing/index.d.ts +0 -8
- package/dist/schemas/preprocessing/index.d.ts.map +0 -1
- package/dist/schemas/preprocessing/index.js +0 -2
- package/dist/schemas/preprocessing/index.js.map +0 -1
- package/dist/serializers/ConfluenceSerializer.d.ts +0 -30
- package/dist/serializers/ConfluenceSerializer.d.ts.map +0 -1
- package/dist/serializers/ConfluenceSerializer.js +0 -560
- package/dist/serializers/ConfluenceSerializer.js.map +0 -1
- package/dist/serializers/MarkdownSerializer.d.ts +0 -34
- package/dist/serializers/MarkdownSerializer.d.ts.map +0 -1
- package/dist/serializers/MarkdownSerializer.js +0 -395
- package/dist/serializers/MarkdownSerializer.js.map +0 -1
- package/dist/serializers/index.d.ts +0 -8
- package/dist/serializers/index.d.ts.map +0 -1
- package/dist/serializers/index.js +0 -8
- package/dist/serializers/index.js.map +0 -1
- 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
|
@@ -0,0 +1,521 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Markdown → ADF → Markdown round-trip fidelity tests for the new pipeline.
|
|
3
|
+
*
|
|
4
|
+
* Replaces the structural fidelity coverage previously held by the deleted
|
|
5
|
+
* `ConfluenceParser`/`MarkdownParser`/`ConversionSchema`/fixture tests. We
|
|
6
|
+
* pin the *substantive* output of the round-trip (heading structure, list
|
|
7
|
+
* markers, code-block fences, table layout, link/title fidelity) rather than
|
|
8
|
+
* exact whitespace, since the @atlaskit transformer normalizes whitespace
|
|
9
|
+
* and we don't want to over-couple to its emission.
|
|
10
|
+
*/
|
|
11
|
+
import { describe, expect, it } from "@effect/vitest"
|
|
12
|
+
import * as Effect from "effect/Effect"
|
|
13
|
+
import * as Layer from "effect/Layer"
|
|
14
|
+
import { layer as AdfSchemaValidatorLayer } from "../src/AdfSchemaValidator.js"
|
|
15
|
+
import { layer as AtlaskitTransformersLayer } from "../src/AtlaskitTransformers.js"
|
|
16
|
+
import { layer as MarkdownConverterLayer, MarkdownConverter } from "../src/MarkdownConverter.js"
|
|
17
|
+
|
|
18
|
+
const TestLayer = MarkdownConverterLayer.pipe(
|
|
19
|
+
Layer.provide(AtlaskitTransformersLayer),
|
|
20
|
+
Layer.provide(AdfSchemaValidatorLayer)
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
const roundTrip = (source: string) =>
|
|
24
|
+
Effect.gen(function*() {
|
|
25
|
+
const converter = yield* MarkdownConverter
|
|
26
|
+
const adf = yield* converter.markdownToAdf(source)
|
|
27
|
+
return yield* converter.adfToMarkdown(adf)
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
describe("MarkdownConverter round-trip", () => {
|
|
31
|
+
it.effect("preserves nested headings", () =>
|
|
32
|
+
Effect.gen(function*() {
|
|
33
|
+
const md = yield* roundTrip("# H1\n\n## H2\n\n### H3\n")
|
|
34
|
+
expect(md).toContain("# H1")
|
|
35
|
+
expect(md).toContain("## H2")
|
|
36
|
+
expect(md).toContain("### H3")
|
|
37
|
+
}).pipe(Effect.provide(TestLayer)))
|
|
38
|
+
|
|
39
|
+
it.effect("preserves a fenced code block with language", () =>
|
|
40
|
+
Effect.gen(function*() {
|
|
41
|
+
const md = yield* roundTrip("```ts\nconst x: number = 1\n```\n")
|
|
42
|
+
expect(md).toContain("```ts")
|
|
43
|
+
expect(md).toContain("const x: number = 1")
|
|
44
|
+
}).pipe(Effect.provide(TestLayer)))
|
|
45
|
+
|
|
46
|
+
it.effect("preserves a blockquote", () =>
|
|
47
|
+
Effect.gen(function*() {
|
|
48
|
+
const md = yield* roundTrip("> a quote\n")
|
|
49
|
+
expect(md).toMatch(/^> a quote/m)
|
|
50
|
+
}).pipe(Effect.provide(TestLayer)))
|
|
51
|
+
|
|
52
|
+
it.effect("preserves a bullet list", () =>
|
|
53
|
+
Effect.gen(function*() {
|
|
54
|
+
const md = yield* roundTrip("- one\n- two\n- three\n")
|
|
55
|
+
expect(md).toContain("- one")
|
|
56
|
+
expect(md).toContain("- two")
|
|
57
|
+
expect(md).toContain("- three")
|
|
58
|
+
}).pipe(Effect.provide(TestLayer)))
|
|
59
|
+
|
|
60
|
+
it.effect("preserves an ordered list", () =>
|
|
61
|
+
Effect.gen(function*() {
|
|
62
|
+
const md = yield* roundTrip("1. one\n2. two\n3. three\n")
|
|
63
|
+
expect(md).toMatch(/1\. one/)
|
|
64
|
+
expect(md).toMatch(/2\. two/)
|
|
65
|
+
expect(md).toMatch(/3\. three/)
|
|
66
|
+
}).pipe(Effect.provide(TestLayer)))
|
|
67
|
+
|
|
68
|
+
it.effect("preserves a GFM table with header", () =>
|
|
69
|
+
Effect.gen(function*() {
|
|
70
|
+
const md = yield* roundTrip("| A | B |\n| --- | --- |\n| 1 | 2 |\n")
|
|
71
|
+
expect(md).toContain("| A | B |")
|
|
72
|
+
expect(md).toContain("| --- | --- |")
|
|
73
|
+
expect(md).toContain("| 1 | 2 |")
|
|
74
|
+
}).pipe(Effect.provide(TestLayer)))
|
|
75
|
+
|
|
76
|
+
// The @atlaskit markdown transformer drops link titles when parsing
|
|
77
|
+
// markdown (its ProseMirror schema for `link` does not capture `title`).
|
|
78
|
+
// So we only assert that text + href survive the round-trip; the title
|
|
79
|
+
// attribute is documented as round-trip-lossy via this test's name.
|
|
80
|
+
it.effect("preserves link text and href (title is lossy via @atlaskit)", () =>
|
|
81
|
+
Effect.gen(function*() {
|
|
82
|
+
const md = yield* roundTrip(`[home](https://example.com "Home")\n`)
|
|
83
|
+
expect(md).toContain("[home]")
|
|
84
|
+
expect(md).toContain("https://example.com")
|
|
85
|
+
}).pipe(Effect.provide(TestLayer)))
|
|
86
|
+
|
|
87
|
+
it.effect("preserves bold, italic, and inline code combinations", () =>
|
|
88
|
+
Effect.gen(function*() {
|
|
89
|
+
const md = yield* roundTrip("a **b** _c_ `d` text\n")
|
|
90
|
+
expect(md).toContain("**b**")
|
|
91
|
+
expect(md).toContain("_c_")
|
|
92
|
+
expect(md).toContain("`d`")
|
|
93
|
+
}).pipe(Effect.provide(TestLayer)))
|
|
94
|
+
|
|
95
|
+
it.effect("does not over-escape ordinary version strings", () =>
|
|
96
|
+
Effect.gen(function*() {
|
|
97
|
+
const md = yield* roundTrip("Released v1.0.0 on 2026-05-03\n")
|
|
98
|
+
expect(md).toContain("v1.0.0")
|
|
99
|
+
expect(md).toContain("2026-05-03")
|
|
100
|
+
expect(md).not.toContain("v1\\.0\\.0")
|
|
101
|
+
expect(md).not.toContain("2026\\-05\\-03")
|
|
102
|
+
}).pipe(Effect.provide(TestLayer)))
|
|
103
|
+
|
|
104
|
+
it.effect("does not escape parentheses or other line-start-only characters", () =>
|
|
105
|
+
Effect.gen(function*() {
|
|
106
|
+
const md = yield* roundTrip("a (b) c+ d!\n")
|
|
107
|
+
expect(md).toContain("a (b) c+ d!")
|
|
108
|
+
expect(md).not.toContain("\\(")
|
|
109
|
+
expect(md).not.toContain("\\+")
|
|
110
|
+
}).pipe(Effect.provide(TestLayer)))
|
|
111
|
+
|
|
112
|
+
// Regression: escaping inside code spans put literal backslashes into the
|
|
113
|
+
// ADF text, which the next pull re-escaped — doubling them on every
|
|
114
|
+
// round-trip (`a_b` → `a\_b` → `a\\\_b` → …).
|
|
115
|
+
it.effect("code spans with markdown-special characters are a round-trip fixed point", () =>
|
|
116
|
+
Effect.gen(function*() {
|
|
117
|
+
const source = "x `a_b` y `c()` (z)\n"
|
|
118
|
+
const once = yield* roundTrip(source)
|
|
119
|
+
const twice = yield* roundTrip(once)
|
|
120
|
+
expect(once).toContain("`a_b`")
|
|
121
|
+
expect(once).toContain("`c()`")
|
|
122
|
+
expect(once).not.toContain("\\")
|
|
123
|
+
expect(twice).toBe(once)
|
|
124
|
+
}).pipe(Effect.provide(TestLayer)))
|
|
125
|
+
|
|
126
|
+
it.effect("preserves an inline status placeholder through round-trip", () =>
|
|
127
|
+
Effect.gen(function*() {
|
|
128
|
+
const md = yield* roundTrip(
|
|
129
|
+
`Status: <span class="adf-status" data-color="blue">TESTING</span>\n`
|
|
130
|
+
)
|
|
131
|
+
expect(md).toContain(`<span class="adf-status" data-color="blue">TESTING</span>`)
|
|
132
|
+
}).pipe(Effect.provide(TestLayer)))
|
|
133
|
+
|
|
134
|
+
it.effect("upgrades a legacy generic block extension placeholder to the attrs form, then stays fixed", () =>
|
|
135
|
+
Effect.gen(function*() {
|
|
136
|
+
const md = yield* roundTrip(
|
|
137
|
+
`<!-- adf:extension key=anchor type=com.atlassian.confluence.macro.core -->\n`
|
|
138
|
+
)
|
|
139
|
+
expect(md).toContain("<!-- adf:extension key=anchor type=com.atlassian.confluence.macro.core attrs=")
|
|
140
|
+
const again = yield* roundTrip(md)
|
|
141
|
+
expect(again).toBe(md)
|
|
142
|
+
}).pipe(Effect.provide(TestLayer)))
|
|
143
|
+
|
|
144
|
+
it.effect("round-trips native TOC syntax as a fixed point", () =>
|
|
145
|
+
Effect.gen(function*() {
|
|
146
|
+
const md = yield* roundTrip("[[toc:min=2,max=4]]\n")
|
|
147
|
+
expect(md).toContain("[[toc:min=2,max=4]]")
|
|
148
|
+
const again = yield* roundTrip(md)
|
|
149
|
+
expect(again).toBe(md)
|
|
150
|
+
}).pipe(Effect.provide(TestLayer)))
|
|
151
|
+
|
|
152
|
+
it.effect("round-trips macro parameters through the placeholder attrs blob", () =>
|
|
153
|
+
Effect.gen(function*() {
|
|
154
|
+
const converter = yield* MarkdownConverter
|
|
155
|
+
const attrs = {
|
|
156
|
+
extensionKey: "toc",
|
|
157
|
+
extensionType: "com.atlassian.confluence.macro.core",
|
|
158
|
+
layout: "default",
|
|
159
|
+
localId: "abc-123",
|
|
160
|
+
parameters: { macroParams: { maxLevel: { value: "3" } } }
|
|
161
|
+
}
|
|
162
|
+
const md = yield* converter.adfToMarkdown(JSON.stringify({
|
|
163
|
+
version: 1,
|
|
164
|
+
type: "doc",
|
|
165
|
+
content: [{ type: "extension", attrs }]
|
|
166
|
+
}))
|
|
167
|
+
const adfOut = JSON.parse(yield* converter.markdownToAdf(md)) as {
|
|
168
|
+
content: Array<{ type: string; attrs: Record<string, unknown> }>
|
|
169
|
+
}
|
|
170
|
+
expect(adfOut.content[0]).toEqual({ type: "extension", attrs })
|
|
171
|
+
}).pipe(Effect.provide(TestLayer)))
|
|
172
|
+
|
|
173
|
+
it.effect("round-trips Confluence TOC macroMetadata through the placeholder attrs blob", () =>
|
|
174
|
+
Effect.gen(function*() {
|
|
175
|
+
const converter = yield* MarkdownConverter
|
|
176
|
+
const attrs = {
|
|
177
|
+
extensionKey: "toc",
|
|
178
|
+
extensionType: "com.atlassian.confluence.macro.core",
|
|
179
|
+
layout: "default",
|
|
180
|
+
parameters: {
|
|
181
|
+
macroMetadata: {
|
|
182
|
+
schemaVersion: { value: "1" },
|
|
183
|
+
title: "Table of Contents"
|
|
184
|
+
},
|
|
185
|
+
macroParams: {}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
const md = yield* converter.adfToMarkdown(JSON.stringify({
|
|
189
|
+
version: 1,
|
|
190
|
+
type: "doc",
|
|
191
|
+
content: [{ type: "extension", attrs }]
|
|
192
|
+
}))
|
|
193
|
+
expect(md).toContain("<!-- adf:extension key=toc type=com.atlassian.confluence.macro.core attrs=")
|
|
194
|
+
expect(md).not.toContain("[[toc")
|
|
195
|
+
const adfOut = JSON.parse(yield* converter.markdownToAdf(md)) as {
|
|
196
|
+
content: Array<{ type: string; attrs: Record<string, unknown> }>
|
|
197
|
+
}
|
|
198
|
+
expect(adfOut.content[0]).toEqual({ type: "extension", attrs })
|
|
199
|
+
}).pipe(Effect.provide(TestLayer)))
|
|
200
|
+
|
|
201
|
+
it.effect("round-trips a bodiedExtension with its body re-attached", () =>
|
|
202
|
+
Effect.gen(function*() {
|
|
203
|
+
const converter = yield* MarkdownConverter
|
|
204
|
+
const attrs = { extensionKey: "details", extensionType: "com.atlassian.confluence.macro.core" }
|
|
205
|
+
const md = yield* converter.adfToMarkdown(JSON.stringify({
|
|
206
|
+
version: 1,
|
|
207
|
+
type: "doc",
|
|
208
|
+
content: [{
|
|
209
|
+
type: "bodiedExtension",
|
|
210
|
+
attrs,
|
|
211
|
+
content: [
|
|
212
|
+
{ type: "paragraph", content: [{ type: "text", text: "first body paragraph" }] },
|
|
213
|
+
{ type: "paragraph", content: [{ type: "text", text: "second body paragraph" }] }
|
|
214
|
+
]
|
|
215
|
+
}]
|
|
216
|
+
}))
|
|
217
|
+
const adfOut = JSON.parse(yield* converter.markdownToAdf(md)) as {
|
|
218
|
+
content: Array<{ type: string; attrs: Record<string, unknown>; content: Array<unknown> }>
|
|
219
|
+
}
|
|
220
|
+
expect(adfOut.content).toHaveLength(1)
|
|
221
|
+
expect(adfOut.content[0]).toMatchObject({ type: "bodiedExtension", attrs })
|
|
222
|
+
expect(adfOut.content[0]!.content).toEqual([
|
|
223
|
+
{ type: "paragraph", content: [{ type: "text", text: "first body paragraph" }] },
|
|
224
|
+
{ type: "paragraph", content: [{ type: "text", text: "second body paragraph" }] }
|
|
225
|
+
])
|
|
226
|
+
}).pipe(Effect.provide(TestLayer)))
|
|
227
|
+
|
|
228
|
+
it.effect("round-trips a Confluence panel as a panel node", () =>
|
|
229
|
+
Effect.gen(function*() {
|
|
230
|
+
const converter = yield* MarkdownConverter
|
|
231
|
+
const attrs = { panelType: "warning" }
|
|
232
|
+
const md = yield* converter.adfToMarkdown(JSON.stringify({
|
|
233
|
+
version: 1,
|
|
234
|
+
type: "doc",
|
|
235
|
+
content: [{
|
|
236
|
+
type: "panel",
|
|
237
|
+
attrs,
|
|
238
|
+
content: [{ type: "paragraph", content: [{ type: "text", text: "watch this" }] }]
|
|
239
|
+
}]
|
|
240
|
+
}))
|
|
241
|
+
const adfOut = JSON.parse(yield* converter.markdownToAdf(md)) as {
|
|
242
|
+
content: Array<{ type: string; attrs: Record<string, unknown>; content: Array<unknown> }>
|
|
243
|
+
}
|
|
244
|
+
expect(adfOut.content[0]).toEqual({
|
|
245
|
+
type: "panel",
|
|
246
|
+
attrs,
|
|
247
|
+
content: [{ type: "paragraph", content: [{ type: "text", text: "watch this" }] }]
|
|
248
|
+
})
|
|
249
|
+
}).pipe(Effect.provide(TestLayer)))
|
|
250
|
+
|
|
251
|
+
it.effect("round-trips Confluence-only inline marks as native marks", () =>
|
|
252
|
+
Effect.gen(function*() {
|
|
253
|
+
const converter = yield* MarkdownConverter
|
|
254
|
+
const source = {
|
|
255
|
+
version: 1,
|
|
256
|
+
type: "doc",
|
|
257
|
+
content: [{
|
|
258
|
+
type: "paragraph",
|
|
259
|
+
content: [
|
|
260
|
+
{ type: "text", text: "underline", marks: [{ type: "underline" }] },
|
|
261
|
+
{ type: "text", text: "2", marks: [{ type: "subsup", attrs: { type: "sub" } }] },
|
|
262
|
+
{ type: "text", text: "2", marks: [{ type: "subsup", attrs: { type: "sup" } }] },
|
|
263
|
+
{ type: "text", text: "Colored", marks: [{ type: "textColor", attrs: { color: "#ff5630" } }] },
|
|
264
|
+
{ type: "text", text: "highlighted", marks: [{ type: "backgroundColor", attrs: { color: "#f8e6a0" } }] }
|
|
265
|
+
]
|
|
266
|
+
}]
|
|
267
|
+
}
|
|
268
|
+
const md = yield* converter.adfToMarkdown(JSON.stringify(source))
|
|
269
|
+
const adfOut = JSON.parse(yield* converter.markdownToAdf(md)) as typeof source
|
|
270
|
+
expect(adfOut.content[0]).toEqual(source.content[0])
|
|
271
|
+
}).pipe(Effect.provide(TestLayer)))
|
|
272
|
+
|
|
273
|
+
it.effect("round-trips paragraph alignment and indentation marks", () =>
|
|
274
|
+
Effect.gen(function*() {
|
|
275
|
+
const converter = yield* MarkdownConverter
|
|
276
|
+
const source = {
|
|
277
|
+
version: 1,
|
|
278
|
+
type: "doc",
|
|
279
|
+
content: [
|
|
280
|
+
{
|
|
281
|
+
type: "paragraph",
|
|
282
|
+
marks: [{ type: "alignment", attrs: { align: "center" } }],
|
|
283
|
+
content: [{ type: "text", text: "centered" }]
|
|
284
|
+
},
|
|
285
|
+
{
|
|
286
|
+
type: "paragraph",
|
|
287
|
+
marks: [{ type: "alignment", attrs: { align: "end" } }],
|
|
288
|
+
content: [{ type: "text", text: "right" }]
|
|
289
|
+
},
|
|
290
|
+
{
|
|
291
|
+
type: "paragraph",
|
|
292
|
+
marks: [{ type: "indentation", attrs: { level: 2 } }],
|
|
293
|
+
content: [{ type: "text", text: "indented" }]
|
|
294
|
+
}
|
|
295
|
+
]
|
|
296
|
+
}
|
|
297
|
+
const md = yield* converter.adfToMarkdown(JSON.stringify(source))
|
|
298
|
+
const adfOut = JSON.parse(yield* converter.markdownToAdf(md)) as typeof source
|
|
299
|
+
expect(adfOut.content).toEqual(source.content)
|
|
300
|
+
}).pipe(Effect.provide(TestLayer)))
|
|
301
|
+
|
|
302
|
+
it.effect("round-trips inline smart links as native inlineCard nodes", () =>
|
|
303
|
+
Effect.gen(function*() {
|
|
304
|
+
const converter = yield* MarkdownConverter
|
|
305
|
+
const source = {
|
|
306
|
+
version: 1,
|
|
307
|
+
type: "doc",
|
|
308
|
+
content: [{
|
|
309
|
+
type: "paragraph",
|
|
310
|
+
content: [
|
|
311
|
+
{ type: "text", text: "Inline smart link: " },
|
|
312
|
+
{ type: "inlineCard", attrs: { url: "https://www.atlassian.com" } },
|
|
313
|
+
{ type: "text", text: "." }
|
|
314
|
+
]
|
|
315
|
+
}]
|
|
316
|
+
}
|
|
317
|
+
const md = yield* converter.adfToMarkdown(JSON.stringify(source))
|
|
318
|
+
const adfOut = JSON.parse(yield* converter.markdownToAdf(md)) as typeof source
|
|
319
|
+
expect(adfOut.content[0]).toEqual(source.content[0])
|
|
320
|
+
}).pipe(Effect.provide(TestLayer)))
|
|
321
|
+
|
|
322
|
+
it.effect("round-trips task lists as native task nodes", () =>
|
|
323
|
+
Effect.gen(function*() {
|
|
324
|
+
const converter = yield* MarkdownConverter
|
|
325
|
+
const source = {
|
|
326
|
+
version: 1,
|
|
327
|
+
type: "doc",
|
|
328
|
+
content: [{
|
|
329
|
+
type: "taskList",
|
|
330
|
+
attrs: { localId: "tasks-1" },
|
|
331
|
+
content: [
|
|
332
|
+
{
|
|
333
|
+
type: "taskItem",
|
|
334
|
+
attrs: { localId: "task-1", state: "DONE" },
|
|
335
|
+
content: [{ type: "text", text: "Existing primitive coverage reviewed" }]
|
|
336
|
+
},
|
|
337
|
+
{
|
|
338
|
+
type: "taskItem",
|
|
339
|
+
attrs: { localId: "task-2", state: "TODO" },
|
|
340
|
+
content: [{ type: "text", text: "Insert real @mention in editor" }]
|
|
341
|
+
}
|
|
342
|
+
]
|
|
343
|
+
}]
|
|
344
|
+
}
|
|
345
|
+
const md = yield* converter.adfToMarkdown(JSON.stringify(source))
|
|
346
|
+
const adfOut = JSON.parse(yield* converter.markdownToAdf(md)) as typeof source
|
|
347
|
+
expect(adfOut.content[0]).toEqual(source.content[0])
|
|
348
|
+
}).pipe(Effect.provide(TestLayer)))
|
|
349
|
+
|
|
350
|
+
it.effect("round-trips decision lists as native decision nodes", () =>
|
|
351
|
+
Effect.gen(function*() {
|
|
352
|
+
const converter = yield* MarkdownConverter
|
|
353
|
+
const source = {
|
|
354
|
+
version: 1,
|
|
355
|
+
type: "doc",
|
|
356
|
+
content: [{
|
|
357
|
+
type: "decisionList",
|
|
358
|
+
attrs: { localId: "decisions-1" },
|
|
359
|
+
content: [{
|
|
360
|
+
type: "decisionItem",
|
|
361
|
+
attrs: { localId: "decision-1", state: "DECIDED" },
|
|
362
|
+
content: [{ type: "text", text: "Decide whether to maintain a separate asset for advanced macros." }]
|
|
363
|
+
}]
|
|
364
|
+
}]
|
|
365
|
+
}
|
|
366
|
+
const md = yield* converter.adfToMarkdown(JSON.stringify(source))
|
|
367
|
+
const adfOut = JSON.parse(yield* converter.markdownToAdf(md)) as typeof source
|
|
368
|
+
expect(adfOut.content[0]).toEqual(source.content[0])
|
|
369
|
+
}).pipe(Effect.provide(TestLayer)))
|
|
370
|
+
|
|
371
|
+
it.effect("round-trips expand, table, layout, cards, date, and emoji as native nodes", () =>
|
|
372
|
+
Effect.gen(function*() {
|
|
373
|
+
const converter = yield* MarkdownConverter
|
|
374
|
+
const source = {
|
|
375
|
+
version: 1,
|
|
376
|
+
type: "doc",
|
|
377
|
+
content: [
|
|
378
|
+
{
|
|
379
|
+
type: "expand",
|
|
380
|
+
attrs: { title: "Expandable supplementary content" },
|
|
381
|
+
content: [{ type: "paragraph", content: [{ type: "text", text: "This section can be expanded." }] }]
|
|
382
|
+
},
|
|
383
|
+
{
|
|
384
|
+
type: "table",
|
|
385
|
+
content: [{
|
|
386
|
+
type: "tableRow",
|
|
387
|
+
content: [
|
|
388
|
+
{
|
|
389
|
+
type: "tableHeader",
|
|
390
|
+
content: [{ type: "paragraph", content: [{ type: "text", text: "Primitive" }] }]
|
|
391
|
+
},
|
|
392
|
+
{
|
|
393
|
+
type: "tableHeader",
|
|
394
|
+
content: [{ type: "paragraph", content: [{ type: "text", text: "Example" }] }]
|
|
395
|
+
}
|
|
396
|
+
]
|
|
397
|
+
}, {
|
|
398
|
+
type: "tableRow",
|
|
399
|
+
content: [
|
|
400
|
+
{ type: "tableCell", content: [{ type: "paragraph", content: [{ type: "text", text: "Date" }] }] },
|
|
401
|
+
{
|
|
402
|
+
type: "tableCell",
|
|
403
|
+
content: [{
|
|
404
|
+
type: "paragraph",
|
|
405
|
+
content: [{ type: "date", attrs: { timestamp: "1782259200000" } }]
|
|
406
|
+
}]
|
|
407
|
+
}
|
|
408
|
+
]
|
|
409
|
+
}, {
|
|
410
|
+
type: "tableRow",
|
|
411
|
+
content: [
|
|
412
|
+
{ type: "tableCell", content: [{ type: "paragraph", content: [{ type: "text", text: "Emoji" }] }] },
|
|
413
|
+
{
|
|
414
|
+
type: "tableCell",
|
|
415
|
+
content: [{
|
|
416
|
+
type: "paragraph",
|
|
417
|
+
content: [{ type: "emoji", attrs: { shortName: ":white_check_mark:", text: "✅" } }]
|
|
418
|
+
}]
|
|
419
|
+
}
|
|
420
|
+
]
|
|
421
|
+
}]
|
|
422
|
+
},
|
|
423
|
+
{
|
|
424
|
+
type: "layoutSection",
|
|
425
|
+
content: [
|
|
426
|
+
{
|
|
427
|
+
type: "layoutColumn",
|
|
428
|
+
attrs: { width: 50 },
|
|
429
|
+
content: [{ type: "paragraph", content: [{ type: "text", text: "Left column" }] }]
|
|
430
|
+
},
|
|
431
|
+
{
|
|
432
|
+
type: "layoutColumn",
|
|
433
|
+
attrs: { width: 50 },
|
|
434
|
+
content: [{ type: "paragraph", content: [{ type: "text", text: "Right column" }] }]
|
|
435
|
+
}
|
|
436
|
+
]
|
|
437
|
+
},
|
|
438
|
+
{ type: "blockCard", attrs: { url: "https://www.atlassian.com/software/confluence" } },
|
|
439
|
+
{ type: "embedCard", attrs: { url: "https://www.atlassian.com/software/confluence", layout: "center" } }
|
|
440
|
+
]
|
|
441
|
+
}
|
|
442
|
+
const md = yield* converter.adfToMarkdown(JSON.stringify(source))
|
|
443
|
+
const adfOut = JSON.parse(yield* converter.markdownToAdf(md)) as typeof source
|
|
444
|
+
expect(adfOut.content).toEqual(source.content)
|
|
445
|
+
}).pipe(Effect.provide(TestLayer)))
|
|
446
|
+
|
|
447
|
+
// Regression: `|` in a cell was escaped twice (escapeText + the table-cell
|
|
448
|
+
// pass), emitting `\\|` — GFM reads that as literal backslash + bare pipe,
|
|
449
|
+
// which opens a phantom column and breaks the row.
|
|
450
|
+
it.effect("escapes a pipe inside a table cell exactly once", () =>
|
|
451
|
+
Effect.gen(function*() {
|
|
452
|
+
const md = yield* roundTrip("| A |\n| --- |\n| a\\|b |\n")
|
|
453
|
+
expect(md).toContain("a\\|b")
|
|
454
|
+
expect(md).not.toContain("a\\\\|b")
|
|
455
|
+
}).pipe(Effect.provide(TestLayer)))
|
|
456
|
+
|
|
457
|
+
// Regression: a code fence *quoting* the placeholder syntax got structured
|
|
458
|
+
// nodes injected into the codeBlock, failing outgoing schema validation.
|
|
459
|
+
it.effect("does not expand placeholder-looking text inside a code fence", () =>
|
|
460
|
+
Effect.gen(function*() {
|
|
461
|
+
const fence = "```html\n" +
|
|
462
|
+
`<span class="adf-status" data-color="blue">X</span>\n` +
|
|
463
|
+
`<!-- adf:inlineExtension key=k type=t -->\n` +
|
|
464
|
+
"```\n"
|
|
465
|
+
const md = yield* roundTrip(fence)
|
|
466
|
+
expect(md).toContain(`<span class="adf-status" data-color="blue">X</span>`)
|
|
467
|
+
expect(md).toContain(`<!-- adf:inlineExtension key=k type=t -->`)
|
|
468
|
+
expect(md).toContain("```html")
|
|
469
|
+
}).pipe(Effect.provide(TestLayer)))
|
|
470
|
+
|
|
471
|
+
// Regression: the inline twin of the fence case — a code *span* quoting a
|
|
472
|
+
// placeholder was replaced by a real status node, silently dropping the
|
|
473
|
+
// quoted sample.
|
|
474
|
+
it.effect("does not expand placeholder-looking text inside a code span", () =>
|
|
475
|
+
Effect.gen(function*() {
|
|
476
|
+
const converter = yield* MarkdownConverter
|
|
477
|
+
const source = `Use \`<span class="adf-status" data-color="green">DONE</span>\` in docs\n`
|
|
478
|
+
const adf = JSON.parse(yield* converter.markdownToAdf(source)) as {
|
|
479
|
+
content: Array<{ content: Array<{ type: string }> }>
|
|
480
|
+
}
|
|
481
|
+
expect(adf.content[0]!.content.some((n) => n.type === "status")).toBe(false)
|
|
482
|
+
}).pipe(Effect.provide(TestLayer)))
|
|
483
|
+
|
|
484
|
+
// Regression: '# x' / '> x' / '+ x' paragraph text became real headings,
|
|
485
|
+
// quotes, and lists after the ESCAPE_RE narrowing dropped those characters.
|
|
486
|
+
it.effect("keeps line-start block markers in paragraph text as text", () =>
|
|
487
|
+
Effect.gen(function*() {
|
|
488
|
+
const converter = yield* MarkdownConverter
|
|
489
|
+
const paragraph = (text: string) => ({ type: "paragraph", content: [{ type: "text", text }] })
|
|
490
|
+
const md = yield* converter.adfToMarkdown(JSON.stringify({
|
|
491
|
+
version: 1,
|
|
492
|
+
type: "doc",
|
|
493
|
+
content: [paragraph("# not a heading"), paragraph("> not a quote"), paragraph("+ not a list")]
|
|
494
|
+
}))
|
|
495
|
+
const adfOut = JSON.parse(yield* converter.markdownToAdf(md)) as {
|
|
496
|
+
content: Array<{ type: string }>
|
|
497
|
+
}
|
|
498
|
+
expect(adfOut.content.map((n) => n.type)).toEqual(["paragraph", "paragraph", "paragraph"])
|
|
499
|
+
}).pipe(Effect.provide(TestLayer)))
|
|
500
|
+
|
|
501
|
+
it.effect("keeps the bodied kind for an empty-body bodied extension", () =>
|
|
502
|
+
Effect.gen(function*() {
|
|
503
|
+
const converter = yield* MarkdownConverter
|
|
504
|
+
const attrs = { extensionKey: "excerpt", extensionType: "com.atlassian.confluence.macro.core" }
|
|
505
|
+
const md = yield* converter.adfToMarkdown(JSON.stringify({
|
|
506
|
+
version: 1,
|
|
507
|
+
type: "doc",
|
|
508
|
+
content: [{ type: "bodiedExtension", attrs, content: [{ type: "paragraph", content: [] }] }]
|
|
509
|
+
}))
|
|
510
|
+
const adfOut = JSON.parse(yield* converter.markdownToAdf(md)) as {
|
|
511
|
+
content: Array<{ type: string }>
|
|
512
|
+
}
|
|
513
|
+
expect(adfOut.content[0]!.type).toBe("bodiedExtension")
|
|
514
|
+
}).pipe(Effect.provide(TestLayer)))
|
|
515
|
+
|
|
516
|
+
it.effect("preserves a mention's accountId through round-trip", () =>
|
|
517
|
+
Effect.gen(function*() {
|
|
518
|
+
const md = yield* roundTrip(`[@Andrey Konopkov](confluence-mention://557057%3Aabc-123)\n`)
|
|
519
|
+
expect(md).toContain("[@Andrey Konopkov](confluence-mention://557057%3Aabc-123)")
|
|
520
|
+
}).pipe(Effect.provide(TestLayer)))
|
|
521
|
+
})
|