mkdocs2confluence 0.19.0__tar.gz → 0.20.1__tar.gz
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.
- {mkdocs2confluence-0.19.0/src/mkdocs2confluence.egg-info → mkdocs2confluence-0.20.1}/PKG-INFO +1 -1
- {mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/pyproject.toml +1 -1
- {mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1/src/mkdocs2confluence.egg-info}/PKG-INFO +1 -1
- {mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/src/mkdocs_to_confluence/cli.py +4 -1
- {mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/src/mkdocs_to_confluence/emitter/xhtml.py +51 -2
- {mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/src/mkdocs_to_confluence/loader/config.py +29 -1
- {mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/src/mkdocs_to_confluence/parser/markdown.py +15 -3
- mkdocs2confluence-0.20.1/tests/test_admonition_titles.py +118 -0
- {mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/tests/test_parser.py +49 -0
- {mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/tests/test_publish_config.py +42 -0
- mkdocs2confluence-0.19.0/tests/test_admonition_titles.py +0 -62
- {mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/LICENSE +0 -0
- {mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/README.md +0 -0
- {mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/setup.cfg +0 -0
- {mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/src/mkdocs2confluence.egg-info/SOURCES.txt +0 -0
- {mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/src/mkdocs2confluence.egg-info/dependency_links.txt +0 -0
- {mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/src/mkdocs2confluence.egg-info/entry_points.txt +0 -0
- {mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/src/mkdocs2confluence.egg-info/requires.txt +0 -0
- {mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/src/mkdocs2confluence.egg-info/top_level.txt +0 -0
- {mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/src/mkdocs_to_confluence/__init__.py +0 -0
- {mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/src/mkdocs_to_confluence/compiler/__init__.py +0 -0
- {mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/src/mkdocs_to_confluence/compiler/models.py +0 -0
- {mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/src/mkdocs_to_confluence/compiler/page.py +0 -0
- {mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/src/mkdocs_to_confluence/emitter/__init__.py +0 -0
- {mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/src/mkdocs_to_confluence/ir/__init__.py +0 -0
- {mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/src/mkdocs_to_confluence/ir/nodes.py +0 -0
- {mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/src/mkdocs_to_confluence/ir/treeutil.py +0 -0
- {mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/src/mkdocs_to_confluence/loader/__init__.py +0 -0
- {mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/src/mkdocs_to_confluence/loader/extra_css.py +0 -0
- {mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/src/mkdocs_to_confluence/loader/nav.py +0 -0
- {mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/src/mkdocs_to_confluence/loader/page.py +0 -0
- {mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/src/mkdocs_to_confluence/parser/__init__.py +0 -0
- {mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/src/mkdocs_to_confluence/pdf/__init__.py +0 -0
- {mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/src/mkdocs_to_confluence/pdf/generator.py +0 -0
- {mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/src/mkdocs_to_confluence/pdf/render.py +0 -0
- {mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/src/mkdocs_to_confluence/preprocess/__init__.py +0 -0
- {mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/src/mkdocs_to_confluence/preprocess/abbrevs.py +0 -0
- {mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/src/mkdocs_to_confluence/preprocess/captions.py +0 -0
- {mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/src/mkdocs_to_confluence/preprocess/fence.py +0 -0
- {mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/src/mkdocs_to_confluence/preprocess/frontmatter.py +0 -0
- {mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/src/mkdocs_to_confluence/preprocess/icons.py +0 -0
- {mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/src/mkdocs_to_confluence/preprocess/includes.py +0 -0
- {mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/src/mkdocs_to_confluence/preprocess/linkdefs.py +0 -0
- {mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/src/mkdocs_to_confluence/preview/__init__.py +0 -0
- {mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/src/mkdocs_to_confluence/preview/render.py +0 -0
- {mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/src/mkdocs_to_confluence/preview/server.py +0 -0
- {mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/src/mkdocs_to_confluence/publisher/__init__.py +0 -0
- {mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/src/mkdocs_to_confluence/publisher/changelog.py +0 -0
- {mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/src/mkdocs_to_confluence/publisher/client.py +0 -0
- {mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/src/mkdocs_to_confluence/publisher/executor.py +0 -0
- {mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/src/mkdocs_to_confluence/publisher/http_retry.py +0 -0
- {mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/src/mkdocs_to_confluence/publisher/models.py +0 -0
- {mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/src/mkdocs_to_confluence/publisher/planner.py +0 -0
- {mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/src/mkdocs_to_confluence/skill_installer.py +0 -0
- {mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/src/mkdocs_to_confluence/skills/mkdocs-changelog/SKILL.md +0 -0
- {mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/src/mkdocs_to_confluence/skills/mkdocs-changelog/scripts/changelog_data.py +0 -0
- {mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/src/mkdocs_to_confluence/sync/__init__.py +0 -0
- {mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/src/mkdocs_to_confluence/sync/anchoring.py +0 -0
- {mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/src/mkdocs_to_confluence/sync/command.py +0 -0
- {mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/src/mkdocs_to_confluence/sync/comments.py +0 -0
- {mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/src/mkdocs_to_confluence/sync/github.py +0 -0
- {mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/src/mkdocs_to_confluence/sync/platform.py +0 -0
- {mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/src/mkdocs_to_confluence/sync/state.py +0 -0
- {mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/src/mkdocs_to_confluence/transforms/__init__.py +0 -0
- {mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/src/mkdocs_to_confluence/transforms/_kroki.py +0 -0
- {mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/src/mkdocs_to_confluence/transforms/abbrevs.py +0 -0
- {mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/src/mkdocs_to_confluence/transforms/admonition_titles.py +0 -0
- {mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/src/mkdocs_to_confluence/transforms/assets.py +0 -0
- {mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/src/mkdocs_to_confluence/transforms/attachment_previews.py +0 -0
- {mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/src/mkdocs_to_confluence/transforms/captions.py +0 -0
- {mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/src/mkdocs_to_confluence/transforms/editlink.py +0 -0
- {mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/src/mkdocs_to_confluence/transforms/footer.py +0 -0
- {mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/src/mkdocs_to_confluence/transforms/images.py +0 -0
- {mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/src/mkdocs_to_confluence/transforms/internallinks.py +0 -0
- {mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/src/mkdocs_to_confluence/transforms/mermaid.py +0 -0
- {mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/src/mkdocs_to_confluence/transforms/plantuml.py +0 -0
- {mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/tests/test_abbrevs.py +0 -0
- {mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/tests/test_attachment_previews.py +0 -0
- {mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/tests/test_captions.py +0 -0
- {mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/tests/test_changelog_config.py +0 -0
- {mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/tests/test_changelog_publish.py +0 -0
- {mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/tests/test_children_macro.py +0 -0
- {mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/tests/test_cli.py +0 -0
- {mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/tests/test_editlink.py +0 -0
- {mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/tests/test_emitter.py +0 -0
- {mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/tests/test_exclude_properties_config.py +0 -0
- {mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/tests/test_extra_css.py +0 -0
- {mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/tests/test_footer.py +0 -0
- {mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/tests/test_frontmatter.py +0 -0
- {mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/tests/test_icons.py +0 -0
- {mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/tests/test_images.py +0 -0
- {mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/tests/test_internallinks.py +0 -0
- {mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/tests/test_ir.py +0 -0
- {mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/tests/test_linkdefs.py +0 -0
- {mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/tests/test_loader.py +0 -0
- {mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/tests/test_mermaid.py +0 -0
- {mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/tests/test_page_loader.py +0 -0
- {mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/tests/test_pdf.py +0 -0
- {mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/tests/test_plantuml.py +0 -0
- {mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/tests/test_preprocess.py +0 -0
- {mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/tests/test_preview.py +0 -0
- {mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/tests/test_publish_client.py +0 -0
- {mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/tests/test_publish_pipeline.py +0 -0
- {mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/tests/test_server.py +0 -0
- {mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/tests/test_skill_installer.py +0 -0
- {mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/tests/test_sync_anchoring.py +0 -0
- {mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/tests/test_sync_command.py +0 -0
- {mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/tests/test_sync_comments.py +0 -0
- {mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/tests/test_sync_github.py +0 -0
- {mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/tests/test_sync_state.py +0 -0
- {mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/tests/test_treeutil.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "mkdocs2confluence"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.20.1"
|
|
4
4
|
description = "Publish MkDocs Material pages to Confluence Cloud — admonitions, Mermaid diagrams, tabs, page properties and more"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = { text = "GPL-3.0-or-later" }
|
|
@@ -10,7 +10,7 @@ from pathlib import Path
|
|
|
10
10
|
|
|
11
11
|
from mkdocs_to_confluence import __version__
|
|
12
12
|
from mkdocs_to_confluence.compiler.page import compile_page
|
|
13
|
-
from mkdocs_to_confluence.emitter.xhtml import configure_styles
|
|
13
|
+
from mkdocs_to_confluence.emitter.xhtml import configure_admonitions, configure_styles
|
|
14
14
|
from mkdocs_to_confluence.loader.config import load_config
|
|
15
15
|
from mkdocs_to_confluence.loader.nav import find_section, find_section_by_folder, flat_pages, resolve_nav
|
|
16
16
|
from mkdocs_to_confluence.loader.page import PageLoadError, find_page
|
|
@@ -340,6 +340,7 @@ def _cmd_preview(args: argparse.Namespace) -> None:
|
|
|
340
340
|
config_path = Path(args.config).resolve()
|
|
341
341
|
config = load_config(config_path)
|
|
342
342
|
configure_styles(config.extra_styles)
|
|
343
|
+
configure_admonitions(config.confluence.admonitions if config.confluence else None)
|
|
343
344
|
|
|
344
345
|
all_nodes = resolve_nav(config)
|
|
345
346
|
|
|
@@ -495,6 +496,7 @@ def _cmd_publish(args: argparse.Namespace) -> None:
|
|
|
495
496
|
config_path = Path(args.config).resolve()
|
|
496
497
|
config = load_config(config_path)
|
|
497
498
|
configure_styles(config.extra_styles)
|
|
499
|
+
configure_admonitions(config.confluence.admonitions if config.confluence else None)
|
|
498
500
|
|
|
499
501
|
conf_config = config.confluence
|
|
500
502
|
if conf_config is None:
|
|
@@ -687,6 +689,7 @@ def _cmd_pdf(args: argparse.Namespace) -> None:
|
|
|
687
689
|
config_path = Path(args.config).resolve()
|
|
688
690
|
config = load_config(config_path)
|
|
689
691
|
configure_styles(config.extra_styles)
|
|
692
|
+
configure_admonitions(config.confluence.admonitions if config.confluence else None)
|
|
690
693
|
|
|
691
694
|
nodes = resolve_nav(config)
|
|
692
695
|
|
{mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/src/mkdocs_to_confluence/emitter/xhtml.py
RENAMED
|
@@ -17,6 +17,7 @@ https://developer.atlassian.com/server/confluence/confluence-storage-format/
|
|
|
17
17
|
from __future__ import annotations
|
|
18
18
|
|
|
19
19
|
import html
|
|
20
|
+
import sys
|
|
20
21
|
from pathlib import Path
|
|
21
22
|
from typing import Sequence
|
|
22
23
|
|
|
@@ -77,6 +78,32 @@ def configure_styles(styles: ExtraStyles | None) -> None:
|
|
|
77
78
|
global _styles
|
|
78
79
|
_styles = styles
|
|
79
80
|
|
|
81
|
+
|
|
82
|
+
# ── Custom admonition aliases (set once per run via configure_admonitions) ─────
|
|
83
|
+
|
|
84
|
+
_admonition_aliases: dict[str, str] = {}
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def configure_admonitions(aliases: dict[str, str] | None) -> None:
|
|
88
|
+
"""Map custom admonition kinds onto built-in ones, from ``confluence.admonitions``.
|
|
89
|
+
|
|
90
|
+
Targets naming no built-in kind are kept but warned about — they resolve
|
|
91
|
+
through the same fallback as any unknown kind, so a typo degrades rather
|
|
92
|
+
than fails the build.
|
|
93
|
+
"""
|
|
94
|
+
global _admonition_aliases
|
|
95
|
+
_admonition_aliases = dict(aliases or {})
|
|
96
|
+
unknown = sorted(
|
|
97
|
+
f"{k} -> {v}" for k, v in _admonition_aliases.items() if v not in _KNOWN_ADMONITION_KINDS
|
|
98
|
+
)
|
|
99
|
+
if unknown:
|
|
100
|
+
print(
|
|
101
|
+
"warning: confluence.admonitions targets an unknown admonition kind: "
|
|
102
|
+
+ ", ".join(unknown)
|
|
103
|
+
+ f". Known kinds: {', '.join(sorted(_KNOWN_ADMONITION_KINDS))}.",
|
|
104
|
+
file=sys.stderr,
|
|
105
|
+
)
|
|
106
|
+
|
|
80
107
|
# ── Admonition kind → Confluence macro name ───────────────────────────────────
|
|
81
108
|
|
|
82
109
|
# Types that map to Confluence's four native panel macros (include built-in icons).
|
|
@@ -106,6 +133,11 @@ _ADMONITION_MACRO: dict[str, str] = {
|
|
|
106
133
|
_ADMONITION_DANGER_KINDS: frozenset[str] = frozenset(
|
|
107
134
|
{"danger", "error", "bug", "failure", "fail", "missing"}
|
|
108
135
|
)
|
|
136
|
+
# Every kind the emitter understands as an alias target.
|
|
137
|
+
_KNOWN_ADMONITION_KINDS: frozenset[str] = (
|
|
138
|
+
frozenset(_ADMONITION_MACRO) | _ADMONITION_DANGER_KINDS
|
|
139
|
+
)
|
|
140
|
+
|
|
109
141
|
_DANGER_EMOJI = "🚨"
|
|
110
142
|
_DANGER_COLOURS = {
|
|
111
143
|
"borderColor": "#DE350B",
|
|
@@ -350,8 +382,25 @@ def _emit_code_block(node: CodeBlock) -> str:
|
|
|
350
382
|
return "".join(parts)
|
|
351
383
|
|
|
352
384
|
|
|
385
|
+
def _resolve_admonition_kind(kind: str) -> str:
|
|
386
|
+
"""Resolve *kind* to the built-in kind that decides its Confluence rendering.
|
|
387
|
+
|
|
388
|
+
Order: an explicit ``confluence.admonitions`` alias, then the built-in kinds,
|
|
389
|
+
then ``success`` for anything unrecognised.
|
|
390
|
+
"""
|
|
391
|
+
alias = _admonition_aliases.get(kind)
|
|
392
|
+
if alias is not None:
|
|
393
|
+
return alias
|
|
394
|
+
if kind in _KNOWN_ADMONITION_KINDS:
|
|
395
|
+
return kind
|
|
396
|
+
return "success"
|
|
397
|
+
|
|
398
|
+
|
|
353
399
|
def _emit_admonition(node: Admonition) -> str:
|
|
400
|
+
# The title keeps the author's own kind ("Value-driver"), only the styling
|
|
401
|
+
# follows the resolved kind.
|
|
354
402
|
title = node.title or _DEFAULT_ADMONITION_TITLES.get(node.kind, node.kind.capitalize())
|
|
403
|
+
kind = _resolve_admonition_kind(node.kind)
|
|
355
404
|
body = emit(node.children)
|
|
356
405
|
|
|
357
406
|
# ??? and ???+ → Confluence expand macro (collapsible)
|
|
@@ -367,7 +416,7 @@ def _emit_admonition(node: Admonition) -> str:
|
|
|
367
416
|
|
|
368
417
|
# Fall through for ???+ (expanded=True) — render as a regular admonition below.
|
|
369
418
|
|
|
370
|
-
if
|
|
419
|
+
if kind in _ADMONITION_DANGER_KINDS:
|
|
371
420
|
colours = "".join(
|
|
372
421
|
f' <ac:parameter ac:name="{k}">{v}</ac:parameter>\n'
|
|
373
422
|
for k, v in _DANGER_COLOURS.items()
|
|
@@ -381,7 +430,7 @@ def _emit_admonition(node: Admonition) -> str:
|
|
|
381
430
|
"</ac:structured-macro>\n"
|
|
382
431
|
)
|
|
383
432
|
|
|
384
|
-
macro_name = _ADMONITION_MACRO.get(
|
|
433
|
+
macro_name = _ADMONITION_MACRO.get(kind, "tip")
|
|
385
434
|
return (
|
|
386
435
|
f'<ac:structured-macro ac:name="{macro_name}">\n'
|
|
387
436
|
f' <ac:parameter ac:name="title">{html.escape(title)}</ac:parameter>\n'
|
{mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/src/mkdocs_to_confluence/loader/config.py
RENAMED
|
@@ -4,7 +4,7 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
import os
|
|
6
6
|
import re
|
|
7
|
-
from dataclasses import dataclass
|
|
7
|
+
from dataclasses import dataclass, field
|
|
8
8
|
from pathlib import Path
|
|
9
9
|
from typing import Any
|
|
10
10
|
from urllib.parse import urlparse
|
|
@@ -38,6 +38,10 @@ class ConfluenceConfig:
|
|
|
38
38
|
exclude_properties: tuple[str, ...] = () # front matter keys to omit from Page Properties table
|
|
39
39
|
attachment_preview: bool = False # render PDF/Office attachment links as view-file macros
|
|
40
40
|
children_macro: bool = True # append Children Display macro to section index pages
|
|
41
|
+
# Custom admonition kind → built-in kind it should render as, e.g.
|
|
42
|
+
# {"value-driver": "tip"}. Kinds absent here fall back to the built-in
|
|
43
|
+
# mapping, then to "success". Emitter owns the target vocabulary.
|
|
44
|
+
admonitions: dict[str, str] = field(default_factory=dict)
|
|
41
45
|
|
|
42
46
|
|
|
43
47
|
@dataclass(frozen=True)
|
|
@@ -314,6 +318,29 @@ def load_config(mkdocs_yml: Path) -> MkDocsConfig:
|
|
|
314
318
|
)
|
|
315
319
|
children_macro = raw_children
|
|
316
320
|
|
|
321
|
+
# admonitions (optional) — map custom admonition kinds onto built-in ones.
|
|
322
|
+
# Only the shape is checked here; the emitter owns the target vocabulary
|
|
323
|
+
# and warns about unknown targets, since loader sits below it in the layering.
|
|
324
|
+
raw_admonitions = raw_conf.get("admonitions", {})
|
|
325
|
+
if not isinstance(raw_admonitions, dict):
|
|
326
|
+
raise ConfigError(
|
|
327
|
+
"mkdocs.yml: 'confluence.admonitions' must be a mapping of "
|
|
328
|
+
f"custom kind to built-in kind, got {type(raw_admonitions).__name__}."
|
|
329
|
+
)
|
|
330
|
+
admonitions: dict[str, str] = {}
|
|
331
|
+
for key, value in raw_admonitions.items():
|
|
332
|
+
if not isinstance(key, str) or not key.strip():
|
|
333
|
+
raise ConfigError(
|
|
334
|
+
"mkdocs.yml: 'confluence.admonitions' keys must be non-empty strings."
|
|
335
|
+
)
|
|
336
|
+
if not isinstance(value, str) or not value.strip():
|
|
337
|
+
raise ConfigError(
|
|
338
|
+
f"mkdocs.yml: 'confluence.admonitions.{key}' must be a non-empty "
|
|
339
|
+
"string naming a built-in admonition kind (e.g. 'tip', 'warning')."
|
|
340
|
+
)
|
|
341
|
+
# Admonition kinds are case-insensitive in Markdown; normalise both sides.
|
|
342
|
+
admonitions[key.strip().lower()] = value.strip().lower()
|
|
343
|
+
|
|
317
344
|
confluence = ConfluenceConfig(
|
|
318
345
|
base_url=base_url.rstrip("/"),
|
|
319
346
|
space_key=space_key,
|
|
@@ -332,6 +359,7 @@ def load_config(mkdocs_yml: Path) -> MkDocsConfig:
|
|
|
332
359
|
exclude_properties=exclude_properties,
|
|
333
360
|
attachment_preview=attachment_preview,
|
|
334
361
|
children_macro=children_macro,
|
|
362
|
+
admonitions=admonitions,
|
|
335
363
|
)
|
|
336
364
|
|
|
337
365
|
# --- extra_css (optional) ---
|
{mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/src/mkdocs_to_confluence/parser/markdown.py
RENAMED
|
@@ -51,6 +51,7 @@ from __future__ import annotations
|
|
|
51
51
|
import contextlib
|
|
52
52
|
import html
|
|
53
53
|
import re
|
|
54
|
+
import textwrap
|
|
54
55
|
from dataclasses import dataclass, field
|
|
55
56
|
from typing import Union
|
|
56
57
|
|
|
@@ -256,7 +257,7 @@ _FENCE_OPEN_RE = re.compile(r"^(?P<fence>`{3,}|~{3,})(?P<info>.*)$")
|
|
|
256
257
|
|
|
257
258
|
# Matches an admonition opener: !!! kind / !!! kind "title" / ??? kind / ???+ kind
|
|
258
259
|
_ADMONITION_RE = re.compile(
|
|
259
|
-
r"""^[ ]{0,3}(?P<marker>!{3}|\?{3}\+?)\s+(?P<kind
|
|
260
|
+
r"""^[ ]{0,3}(?P<marker>!{3}|\?{3}\+?)\s+(?P<kind>[\w-]+)(?:\s+(?P<q>["'])(?P<title>.*?)(?P=q))?\s*$"""
|
|
260
261
|
)
|
|
261
262
|
|
|
262
263
|
# Matches a Material for MkDocs content tab opener: === "Label"
|
|
@@ -329,6 +330,17 @@ def _grid_kind(line: str) -> str | None:
|
|
|
329
330
|
_CARD_BULLET_RE = re.compile(r'^(?P<marker>[-*+]\s+)(?P<text>.*)$')
|
|
330
331
|
|
|
331
332
|
|
|
333
|
+
def _dedent_body(lines: list[str]) -> str:
|
|
334
|
+
"""Join admonition/tab body *lines* and drop any indent they all share.
|
|
335
|
+
|
|
336
|
+
The collector strips the 4 columns that delimit the body, but authors often
|
|
337
|
+
indent the content further (8 spaces is a common habit). That residual indent
|
|
338
|
+
is invisible to the author and would stop a list, fence or heading from being
|
|
339
|
+
recognised, since those anchor at column 0.
|
|
340
|
+
"""
|
|
341
|
+
return textwrap.dedent("\n".join(_expand_leading_tabs(ln) for ln in lines))
|
|
342
|
+
|
|
343
|
+
|
|
332
344
|
def _expand_leading_tabs(line: str) -> str:
|
|
333
345
|
"""Expand tabs in *line*'s leading whitespace to 4-space tab stops.
|
|
334
346
|
|
|
@@ -536,7 +548,7 @@ def _tokenize(text: str) -> list[_Token]:
|
|
|
536
548
|
# Drop trailing blank lines so the recursive tokenizer stays clean.
|
|
537
549
|
while body_raw and not body_raw[-1].strip():
|
|
538
550
|
body_raw.pop()
|
|
539
|
-
body_tokens = _tokenize(
|
|
551
|
+
body_tokens = _tokenize(_dedent_body(body_raw))
|
|
540
552
|
tokens.append(
|
|
541
553
|
_AdmonitionToken(
|
|
542
554
|
kind=kind,
|
|
@@ -571,7 +583,7 @@ def _tokenize(text: str) -> list[_Token]:
|
|
|
571
583
|
break
|
|
572
584
|
while body_raw_tab and not body_raw_tab[-1].strip():
|
|
573
585
|
body_raw_tab.pop()
|
|
574
|
-
tabs.append(_TabData(label=label, body_tokens=_tokenize(
|
|
586
|
+
tabs.append(_TabData(label=label, body_tokens=_tokenize(_dedent_body(body_raw_tab))))
|
|
575
587
|
if tabs:
|
|
576
588
|
tokens.append(_ContentTabsToken(tabs=tabs))
|
|
577
589
|
continue
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
"""Tests for the admonition-title link degradation transform."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from mkdocs_to_confluence.ir.nodes import Admonition, Section, TextNode
|
|
6
|
+
from mkdocs_to_confluence.transforms.admonition_titles import (
|
|
7
|
+
strip_links_in_admonition_titles,
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def _adm(title: str | None) -> Admonition:
|
|
12
|
+
return Admonition(kind="warning", title=title, children=())
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def test_link_in_title_stripped_to_text() -> None:
|
|
16
|
+
nodes = (_adm("Conflict - see [Hello](foobar.md#hello)"),)
|
|
17
|
+
out = strip_links_in_admonition_titles(nodes, "index.md")
|
|
18
|
+
assert out[0].title == "Conflict - see Hello"
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def test_warning_emitted_with_page_and_title(capsys) -> None:
|
|
22
|
+
nodes = (_adm("see [Hello](foobar.md#hello)"),)
|
|
23
|
+
strip_links_in_admonition_titles(nodes, "guide/index.md")
|
|
24
|
+
err = capsys.readouterr().err
|
|
25
|
+
assert "warning" in err
|
|
26
|
+
assert "guide/index.md" in err
|
|
27
|
+
assert "see [Hello](foobar.md#hello)" in err
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def test_title_without_link_unchanged_and_no_warning(capsys) -> None:
|
|
31
|
+
nodes = (_adm("Just a plain title"),)
|
|
32
|
+
out = strip_links_in_admonition_titles(nodes, "index.md")
|
|
33
|
+
assert out[0].title == "Just a plain title"
|
|
34
|
+
assert capsys.readouterr().err == ""
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def test_none_title_is_safe(capsys) -> None:
|
|
38
|
+
nodes = (_adm(None),)
|
|
39
|
+
out = strip_links_in_admonition_titles(nodes, "index.md")
|
|
40
|
+
assert out[0].title is None
|
|
41
|
+
assert capsys.readouterr().err == ""
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def test_multiple_links_all_stripped() -> None:
|
|
45
|
+
nodes = (_adm("[A](a.md) and [B](b.md#x)"),)
|
|
46
|
+
out = strip_links_in_admonition_titles(nodes, "index.md")
|
|
47
|
+
assert out[0].title == "A and B"
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def test_nested_admonition_in_section_processed() -> None:
|
|
51
|
+
inner = _adm("see [Hello](foobar.md#hello)")
|
|
52
|
+
section = Section(level=2, anchor="s", title=(TextNode("S"),), children=(inner,))
|
|
53
|
+
out = strip_links_in_admonition_titles((section,), "index.md")
|
|
54
|
+
nested = out[0].children[0]
|
|
55
|
+
assert isinstance(nested, Admonition)
|
|
56
|
+
assert nested.title == "see Hello"
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def test_image_in_title_not_mangled() -> None:
|
|
60
|
+
nodes = (_adm("look "),)
|
|
61
|
+
out = strip_links_in_admonition_titles(nodes, "index.md")
|
|
62
|
+
assert out[0].title == "look "
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class TestCustomAdmonitionKinds:
|
|
66
|
+
"""`confluence.admonitions` config, then built-ins, then a `success` default."""
|
|
67
|
+
|
|
68
|
+
def setup_method(self) -> None:
|
|
69
|
+
from mkdocs_to_confluence.emitter.xhtml import configure_admonitions
|
|
70
|
+
configure_admonitions(None)
|
|
71
|
+
|
|
72
|
+
teardown_method = setup_method
|
|
73
|
+
|
|
74
|
+
def _macro(self, src: str) -> str:
|
|
75
|
+
import re
|
|
76
|
+
|
|
77
|
+
from mkdocs_to_confluence.emitter.xhtml import emit
|
|
78
|
+
from mkdocs_to_confluence.parser import parse
|
|
79
|
+
m = re.search(r'ac:name="(\w+)"', emit(parse(src)))
|
|
80
|
+
return m.group(1) if m else ""
|
|
81
|
+
|
|
82
|
+
def test_hyphenated_kind_parses_as_an_admonition(self) -> None:
|
|
83
|
+
"""`!!! value-driver` must not fall through to a paragraph."""
|
|
84
|
+
assert self._macro('!!! value-driver\n\n Body.\n') != ""
|
|
85
|
+
|
|
86
|
+
def test_unknown_kind_defaults_to_success(self) -> None:
|
|
87
|
+
assert self._macro('!!! value-driver\n\n Body.\n') == "tip" # success -> tip
|
|
88
|
+
|
|
89
|
+
def test_builtin_kind_is_unaffected(self) -> None:
|
|
90
|
+
assert self._macro('!!! note\n\n Body.\n') == "info"
|
|
91
|
+
assert self._macro('!!! warning\n\n Body.\n') == "warning"
|
|
92
|
+
|
|
93
|
+
def test_config_alias_wins_over_the_default(self) -> None:
|
|
94
|
+
from mkdocs_to_confluence.emitter.xhtml import configure_admonitions
|
|
95
|
+
configure_admonitions({"value-driver": "warning"})
|
|
96
|
+
assert self._macro('!!! value-driver\n\n Body.\n') == "warning"
|
|
97
|
+
|
|
98
|
+
def test_config_alias_can_target_a_danger_panel(self) -> None:
|
|
99
|
+
from mkdocs_to_confluence.emitter.xhtml import configure_admonitions
|
|
100
|
+
configure_admonitions({"risk": "danger"})
|
|
101
|
+
assert self._macro('!!! risk\n\n Body.\n') == "panel"
|
|
102
|
+
|
|
103
|
+
def test_config_alias_can_override_a_builtin(self) -> None:
|
|
104
|
+
from mkdocs_to_confluence.emitter.xhtml import configure_admonitions
|
|
105
|
+
configure_admonitions({"note": "warning"})
|
|
106
|
+
assert self._macro('!!! note\n\n Body.\n') == "warning"
|
|
107
|
+
|
|
108
|
+
def test_title_keeps_the_authors_kind_not_the_alias(self) -> None:
|
|
109
|
+
from mkdocs_to_confluence.emitter.xhtml import configure_admonitions, emit
|
|
110
|
+
from mkdocs_to_confluence.parser import parse
|
|
111
|
+
configure_admonitions({"value-driver": "warning"})
|
|
112
|
+
assert "Value-driver" in emit(parse('!!! value-driver\n\n Body.\n'))
|
|
113
|
+
|
|
114
|
+
def test_unknown_alias_target_warns_and_degrades(self, capsys) -> None: # type: ignore[no-untyped-def]
|
|
115
|
+
from mkdocs_to_confluence.emitter.xhtml import configure_admonitions
|
|
116
|
+
configure_admonitions({"oops": "nonsense"})
|
|
117
|
+
assert "unknown admonition kind" in capsys.readouterr().err
|
|
118
|
+
assert self._macro('!!! oops\n\n Body.\n') == "tip"
|
|
@@ -1668,3 +1668,52 @@ class TestGridCardIndentEdgeCases:
|
|
|
1668
1668
|
src = '<div class="grid cards" markdown>\r\n\r\n- !!! tip "T"\r\n\r\n Body.\r\n\r\n</div>\r\n'
|
|
1669
1669
|
from mkdocs_to_confluence.emitter.xhtml import emit
|
|
1670
1670
|
assert "Body." in self._panel_body(emit(parse(src)))
|
|
1671
|
+
|
|
1672
|
+
|
|
1673
|
+
class TestAdmonitionBodyOverIndent:
|
|
1674
|
+
"""Body content indented past the 4 that delimit it must still parse."""
|
|
1675
|
+
|
|
1676
|
+
def _emit(self, src: str) -> str:
|
|
1677
|
+
from mkdocs_to_confluence.emitter.xhtml import emit
|
|
1678
|
+
return emit(parse(src))
|
|
1679
|
+
|
|
1680
|
+
def test_list_indented_eight_spaces(self) -> None:
|
|
1681
|
+
"""8 spaces (4 for the body + 4 more) is a common authoring habit."""
|
|
1682
|
+
xhtml = self._emit('!!! note "T"\n\n - one\n - two\n')
|
|
1683
|
+
assert "<li><p>one</p></li>" in xhtml
|
|
1684
|
+
assert "<p>- one" not in xhtml, "list flattened to a paragraph"
|
|
1685
|
+
|
|
1686
|
+
def test_list_indented_four_spaces_still_works(self) -> None:
|
|
1687
|
+
xhtml = self._emit('!!! note "T"\n\n - one\n - two\n')
|
|
1688
|
+
assert "<li><p>one</p></li>" in xhtml
|
|
1689
|
+
|
|
1690
|
+
def test_nested_list_relative_indent_is_preserved(self) -> None:
|
|
1691
|
+
"""Dedenting must not flatten a sub-list into its parent."""
|
|
1692
|
+
for sub_indent in (" ", " "): # 2 and 4 spaces past the item
|
|
1693
|
+
xhtml = self._emit(f'!!! note "T"\n\n - one\n{sub_indent}- sub\n - two\n')
|
|
1694
|
+
assert xhtml.count("<ul>") == 2, f"sub-list lost at {len(sub_indent)} spaces"
|
|
1695
|
+
|
|
1696
|
+
def test_mixed_indent_body_is_left_alone(self) -> None:
|
|
1697
|
+
"""A deeper line beside a column-0 line keeps its relative indent."""
|
|
1698
|
+
xhtml = self._emit('!!! note "T"\n\n Intro.\n\n - deeper\n')
|
|
1699
|
+
assert "<p>Intro.</p>" in xhtml
|
|
1700
|
+
|
|
1701
|
+
def test_over_indented_code_fence(self) -> None:
|
|
1702
|
+
xhtml = self._emit('!!! note "T"\n\n ```python\n x = 1\n ```\n')
|
|
1703
|
+
assert "<![CDATA[x = 1]]>" in xhtml
|
|
1704
|
+
|
|
1705
|
+
def test_content_tab_body_is_dedented_too(self) -> None:
|
|
1706
|
+
xhtml = self._emit('=== "A"\n\n - one\n - two\n')
|
|
1707
|
+
assert "<li><p>one</p></li>" in xhtml
|
|
1708
|
+
|
|
1709
|
+
def test_grid_card_admonition_with_over_indented_list(self) -> None:
|
|
1710
|
+
"""The reported case: grid card, bulleted admonition, list at 8 spaces."""
|
|
1711
|
+
xhtml = self._emit(
|
|
1712
|
+
'<div class="grid cards">\n\n'
|
|
1713
|
+
'- !!! abstract "Documentation"\n\n'
|
|
1714
|
+
' - **Scope:** test\n'
|
|
1715
|
+
' - **Out:** test\n\n'
|
|
1716
|
+
'</div>\n'
|
|
1717
|
+
)
|
|
1718
|
+
assert "<li><p><strong>Scope:</strong> test</p></li>" in xhtml
|
|
1719
|
+
assert "<p>- " not in xhtml
|
|
@@ -537,3 +537,45 @@ confluence:
|
|
|
537
537
|
config = load_config(config_file)
|
|
538
538
|
assert config.confluence is not None
|
|
539
539
|
assert config.confluence.allow_any_host is False
|
|
540
|
+
|
|
541
|
+
|
|
542
|
+
class TestAdmonitionsConfig:
|
|
543
|
+
"""`confluence.admonitions` parsing and validation."""
|
|
544
|
+
|
|
545
|
+
def _write(self, tmp_path, block: str): # type: ignore[no-untyped-def]
|
|
546
|
+
from mkdocs_to_confluence.loader.config import load_config
|
|
547
|
+
(tmp_path / "docs").mkdir(exist_ok=True)
|
|
548
|
+
(tmp_path / "mkdocs.yml").write_text(
|
|
549
|
+
"site_name: T\n"
|
|
550
|
+
"confluence:\n"
|
|
551
|
+
" base_url: https://x.atlassian.net\n"
|
|
552
|
+
" email: a@b.c\n"
|
|
553
|
+
" token: t\n"
|
|
554
|
+
" space_key: S\n" + block,
|
|
555
|
+
encoding="utf-8",
|
|
556
|
+
)
|
|
557
|
+
return load_config(tmp_path / "mkdocs.yml")
|
|
558
|
+
|
|
559
|
+
def test_absent_defaults_to_empty(self, tmp_path) -> None: # type: ignore[no-untyped-def]
|
|
560
|
+
cfg = self._write(tmp_path, "")
|
|
561
|
+
assert cfg.confluence is not None
|
|
562
|
+
assert cfg.confluence.admonitions == {}
|
|
563
|
+
|
|
564
|
+
def test_mapping_is_parsed_and_lowercased(self, tmp_path) -> None: # type: ignore[no-untyped-def]
|
|
565
|
+
cfg = self._write(tmp_path, " admonitions:\n Value-Driver: TIP\n risk: danger\n")
|
|
566
|
+
assert cfg.confluence is not None
|
|
567
|
+
assert cfg.confluence.admonitions == {"value-driver": "tip", "risk": "danger"}
|
|
568
|
+
|
|
569
|
+
def test_non_mapping_is_rejected(self, tmp_path) -> None: # type: ignore[no-untyped-def]
|
|
570
|
+
import pytest
|
|
571
|
+
|
|
572
|
+
from mkdocs_to_confluence.loader.config import ConfigError
|
|
573
|
+
with pytest.raises(ConfigError, match="must be a mapping"):
|
|
574
|
+
self._write(tmp_path, " admonitions:\n - tip\n")
|
|
575
|
+
|
|
576
|
+
def test_non_string_value_is_rejected(self, tmp_path) -> None: # type: ignore[no-untyped-def]
|
|
577
|
+
import pytest
|
|
578
|
+
|
|
579
|
+
from mkdocs_to_confluence.loader.config import ConfigError
|
|
580
|
+
with pytest.raises(ConfigError, match="non-empty"):
|
|
581
|
+
self._write(tmp_path, " admonitions:\n risk: 5\n")
|
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
"""Tests for the admonition-title link degradation transform."""
|
|
2
|
-
|
|
3
|
-
from __future__ import annotations
|
|
4
|
-
|
|
5
|
-
from mkdocs_to_confluence.ir.nodes import Admonition, Section, TextNode
|
|
6
|
-
from mkdocs_to_confluence.transforms.admonition_titles import (
|
|
7
|
-
strip_links_in_admonition_titles,
|
|
8
|
-
)
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
def _adm(title: str | None) -> Admonition:
|
|
12
|
-
return Admonition(kind="warning", title=title, children=())
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
def test_link_in_title_stripped_to_text() -> None:
|
|
16
|
-
nodes = (_adm("Conflict - see [Hello](foobar.md#hello)"),)
|
|
17
|
-
out = strip_links_in_admonition_titles(nodes, "index.md")
|
|
18
|
-
assert out[0].title == "Conflict - see Hello"
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
def test_warning_emitted_with_page_and_title(capsys) -> None:
|
|
22
|
-
nodes = (_adm("see [Hello](foobar.md#hello)"),)
|
|
23
|
-
strip_links_in_admonition_titles(nodes, "guide/index.md")
|
|
24
|
-
err = capsys.readouterr().err
|
|
25
|
-
assert "warning" in err
|
|
26
|
-
assert "guide/index.md" in err
|
|
27
|
-
assert "see [Hello](foobar.md#hello)" in err
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
def test_title_without_link_unchanged_and_no_warning(capsys) -> None:
|
|
31
|
-
nodes = (_adm("Just a plain title"),)
|
|
32
|
-
out = strip_links_in_admonition_titles(nodes, "index.md")
|
|
33
|
-
assert out[0].title == "Just a plain title"
|
|
34
|
-
assert capsys.readouterr().err == ""
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
def test_none_title_is_safe(capsys) -> None:
|
|
38
|
-
nodes = (_adm(None),)
|
|
39
|
-
out = strip_links_in_admonition_titles(nodes, "index.md")
|
|
40
|
-
assert out[0].title is None
|
|
41
|
-
assert capsys.readouterr().err == ""
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
def test_multiple_links_all_stripped() -> None:
|
|
45
|
-
nodes = (_adm("[A](a.md) and [B](b.md#x)"),)
|
|
46
|
-
out = strip_links_in_admonition_titles(nodes, "index.md")
|
|
47
|
-
assert out[0].title == "A and B"
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
def test_nested_admonition_in_section_processed() -> None:
|
|
51
|
-
inner = _adm("see [Hello](foobar.md#hello)")
|
|
52
|
-
section = Section(level=2, anchor="s", title=(TextNode("S"),), children=(inner,))
|
|
53
|
-
out = strip_links_in_admonition_titles((section,), "index.md")
|
|
54
|
-
nested = out[0].children[0]
|
|
55
|
-
assert isinstance(nested, Admonition)
|
|
56
|
-
assert nested.title == "see Hello"
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
def test_image_in_title_not_mangled() -> None:
|
|
60
|
-
nodes = (_adm("look "),)
|
|
61
|
-
out = strip_links_in_admonition_titles(nodes, "index.md")
|
|
62
|
-
assert out[0].title == "look "
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/src/mkdocs2confluence.egg-info/SOURCES.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/src/mkdocs2confluence.egg-info/requires.txt
RENAMED
|
File without changes
|
{mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/src/mkdocs2confluence.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|
{mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/src/mkdocs_to_confluence/compiler/__init__.py
RENAMED
|
File without changes
|
{mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/src/mkdocs_to_confluence/compiler/models.py
RENAMED
|
File without changes
|
{mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/src/mkdocs_to_confluence/compiler/page.py
RENAMED
|
File without changes
|
{mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/src/mkdocs_to_confluence/emitter/__init__.py
RENAMED
|
File without changes
|
{mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/src/mkdocs_to_confluence/ir/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/src/mkdocs_to_confluence/ir/treeutil.py
RENAMED
|
File without changes
|
{mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/src/mkdocs_to_confluence/loader/__init__.py
RENAMED
|
File without changes
|
{mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/src/mkdocs_to_confluence/loader/extra_css.py
RENAMED
|
File without changes
|
{mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/src/mkdocs_to_confluence/loader/nav.py
RENAMED
|
File without changes
|
{mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/src/mkdocs_to_confluence/loader/page.py
RENAMED
|
File without changes
|
{mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/src/mkdocs_to_confluence/parser/__init__.py
RENAMED
|
File without changes
|
{mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/src/mkdocs_to_confluence/pdf/__init__.py
RENAMED
|
File without changes
|
{mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/src/mkdocs_to_confluence/pdf/generator.py
RENAMED
|
File without changes
|
{mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/src/mkdocs_to_confluence/pdf/render.py
RENAMED
|
File without changes
|
|
File without changes
|
{mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/src/mkdocs_to_confluence/preprocess/abbrevs.py
RENAMED
|
File without changes
|
|
File without changes
|
{mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/src/mkdocs_to_confluence/preprocess/fence.py
RENAMED
|
File without changes
|
|
File without changes
|
{mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/src/mkdocs_to_confluence/preprocess/icons.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/src/mkdocs_to_confluence/preview/__init__.py
RENAMED
|
File without changes
|
{mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/src/mkdocs_to_confluence/preview/render.py
RENAMED
|
File without changes
|
{mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/src/mkdocs_to_confluence/preview/server.py
RENAMED
|
File without changes
|
{mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/src/mkdocs_to_confluence/publisher/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/src/mkdocs_to_confluence/publisher/client.py
RENAMED
|
File without changes
|
{mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/src/mkdocs_to_confluence/publisher/executor.py
RENAMED
|
File without changes
|
|
File without changes
|
{mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/src/mkdocs_to_confluence/publisher/models.py
RENAMED
|
File without changes
|
{mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/src/mkdocs_to_confluence/publisher/planner.py
RENAMED
|
File without changes
|
{mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/src/mkdocs_to_confluence/skill_installer.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/src/mkdocs_to_confluence/sync/__init__.py
RENAMED
|
File without changes
|
{mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/src/mkdocs_to_confluence/sync/anchoring.py
RENAMED
|
File without changes
|
{mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/src/mkdocs_to_confluence/sync/command.py
RENAMED
|
File without changes
|
{mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/src/mkdocs_to_confluence/sync/comments.py
RENAMED
|
File without changes
|
{mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/src/mkdocs_to_confluence/sync/github.py
RENAMED
|
File without changes
|
{mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/src/mkdocs_to_confluence/sync/platform.py
RENAMED
|
File without changes
|
{mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/src/mkdocs_to_confluence/sync/state.py
RENAMED
|
File without changes
|
|
File without changes
|
{mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/src/mkdocs_to_confluence/transforms/_kroki.py
RENAMED
|
File without changes
|
{mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/src/mkdocs_to_confluence/transforms/abbrevs.py
RENAMED
|
File without changes
|
|
File without changes
|
{mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/src/mkdocs_to_confluence/transforms/assets.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/src/mkdocs_to_confluence/transforms/footer.py
RENAMED
|
File without changes
|
{mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/src/mkdocs_to_confluence/transforms/images.py
RENAMED
|
File without changes
|
|
File without changes
|
{mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/src/mkdocs_to_confluence/transforms/mermaid.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mkdocs2confluence-0.19.0 → mkdocs2confluence-0.20.1}/tests/test_exclude_properties_config.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|