draftjs_exporter 7.1.0__tar.gz → 7.2.0__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.
- {draftjs_exporter-7.1.0 → draftjs_exporter-7.2.0}/PKG-INFO +3 -4
- draftjs_exporter-7.2.0/draftjs_exporter/.agents/skills/draftjs-exporter/SKILL.md +340 -0
- {draftjs_exporter-7.1.0 → draftjs_exporter-7.2.0}/draftjs_exporter/__init__.py +17 -1
- {draftjs_exporter-7.1.0 → draftjs_exporter-7.2.0}/draftjs_exporter/command.py +2 -2
- {draftjs_exporter-7.1.0 → draftjs_exporter-7.2.0}/draftjs_exporter/composite_decorators.py +1 -1
- {draftjs_exporter-7.1.0 → draftjs_exporter-7.2.0}/draftjs_exporter/dom.py +31 -6
- {draftjs_exporter-7.1.0 → draftjs_exporter-7.2.0}/draftjs_exporter/engines/html5lib.py +4 -7
- {draftjs_exporter-7.1.0 → draftjs_exporter-7.2.0}/draftjs_exporter/engines/lxml.py +4 -2
- {draftjs_exporter-7.1.0 → draftjs_exporter-7.2.0}/draftjs_exporter/engines/markdown.py +4 -4
- {draftjs_exporter-7.1.0 → draftjs_exporter-7.2.0}/draftjs_exporter/engines/string.py +2 -2
- {draftjs_exporter-7.1.0 → draftjs_exporter-7.2.0}/draftjs_exporter/entity_state.py +13 -8
- {draftjs_exporter-7.1.0 → draftjs_exporter-7.2.0}/draftjs_exporter/html.py +24 -9
- {draftjs_exporter-7.1.0 → draftjs_exporter-7.2.0}/draftjs_exporter/markdown/__init__.py +3 -2
- {draftjs_exporter-7.1.0 → draftjs_exporter-7.2.0}/draftjs_exporter/markdown/blocks.py +47 -5
- {draftjs_exporter-7.1.0 → draftjs_exporter-7.2.0}/draftjs_exporter/markdown/entities.py +17 -3
- {draftjs_exporter-7.1.0 → draftjs_exporter-7.2.0}/draftjs_exporter/markdown/escape.py +9 -7
- draftjs_exporter-7.2.0/draftjs_exporter/markdown/helpers.py +157 -0
- draftjs_exporter-7.2.0/draftjs_exporter/markdown/lists.py +234 -0
- {draftjs_exporter-7.1.0 → draftjs_exporter-7.2.0}/draftjs_exporter/markdown/styles.py +22 -4
- {draftjs_exporter-7.1.0 → draftjs_exporter-7.2.0}/draftjs_exporter/markdown_parser/inline.py +1 -1
- {draftjs_exporter-7.1.0 → draftjs_exporter-7.2.0}/draftjs_exporter/markdown_parser/resolvers.py +1 -1
- {draftjs_exporter-7.1.0 → draftjs_exporter-7.2.0}/draftjs_exporter/types.py +3 -5
- {draftjs_exporter-7.1.0 → draftjs_exporter-7.2.0}/draftjs_exporter/wrapper_state.py +63 -27
- draftjs_exporter-7.2.0/pyproject.toml +236 -0
- draftjs_exporter-7.1.0/pyproject.toml → draftjs_exporter-7.2.0/pyproject.toml.orig +148 -12
- draftjs_exporter-7.1.0/draftjs_exporter/markdown/helpers.py +0 -62
- draftjs_exporter-7.1.0/draftjs_exporter/markdown/lists.py +0 -114
- {draftjs_exporter-7.1.0 → draftjs_exporter-7.2.0}/README.md +0 -0
- {draftjs_exporter-7.1.0 → draftjs_exporter-7.2.0}/draftjs_exporter/constants.py +0 -0
- {draftjs_exporter-7.1.0 → draftjs_exporter-7.2.0}/draftjs_exporter/contentstate_filter/__init__.py +0 -0
- {draftjs_exporter-7.1.0 → draftjs_exporter-7.2.0}/draftjs_exporter/defaults.py +0 -0
- {draftjs_exporter-7.1.0 → draftjs_exporter-7.2.0}/draftjs_exporter/engines/__init__.py +0 -0
- {draftjs_exporter-7.1.0 → draftjs_exporter-7.2.0}/draftjs_exporter/engines/base.py +0 -0
- {draftjs_exporter-7.1.0 → draftjs_exporter-7.2.0}/draftjs_exporter/engines/string_compat.py +0 -0
- {draftjs_exporter-7.1.0 → draftjs_exporter-7.2.0}/draftjs_exporter/error.py +0 -0
- {draftjs_exporter-7.1.0 → draftjs_exporter-7.2.0}/draftjs_exporter/markdown/code.py +0 -0
- {draftjs_exporter-7.1.0 → draftjs_exporter-7.2.0}/draftjs_exporter/markdown/fallbacks.py +0 -0
- {draftjs_exporter-7.1.0 → draftjs_exporter-7.2.0}/draftjs_exporter/markdown_importer/__init__.py +0 -0
- {draftjs_exporter-7.1.0 → draftjs_exporter-7.2.0}/draftjs_exporter/markdown_parser/__init__.py +0 -0
- {draftjs_exporter-7.1.0 → draftjs_exporter-7.2.0}/draftjs_exporter/markdown_parser/blocks.py +0 -0
- {draftjs_exporter-7.1.0 → draftjs_exporter-7.2.0}/draftjs_exporter/markdown_parser/builder.py +0 -0
- {draftjs_exporter-7.1.0 → draftjs_exporter-7.2.0}/draftjs_exporter/options.py +0 -0
- {draftjs_exporter-7.1.0 → draftjs_exporter-7.2.0}/draftjs_exporter/py.typed +0 -0
- {draftjs_exporter-7.1.0 → draftjs_exporter-7.2.0}/draftjs_exporter/style_state.py +0 -0
- {draftjs_exporter-7.1.0 → draftjs_exporter-7.2.0}/draftjs_exporter/utils/__init__.py +0 -0
- {draftjs_exporter-7.1.0 → draftjs_exporter-7.2.0}/draftjs_exporter/utils/module_loading.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: draftjs_exporter
|
|
3
|
-
Version: 7.
|
|
3
|
+
Version: 7.2.0
|
|
4
4
|
Summary: Library to convert rich text from Draft.js raw ContentState to HTML
|
|
5
5
|
License-Expression: MIT
|
|
6
6
|
Classifier: Environment :: Web Environment
|
|
@@ -9,7 +9,6 @@ Classifier: Development Status :: 5 - Production/Stable
|
|
|
9
9
|
Classifier: Operating System :: OS Independent
|
|
10
10
|
Classifier: Programming Language :: Python
|
|
11
11
|
Classifier: Programming Language :: Python :: 3
|
|
12
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
13
12
|
Classifier: Programming Language :: Python :: 3.11
|
|
14
13
|
Classifier: Programming Language :: Python :: 3.12
|
|
15
14
|
Classifier: Programming Language :: Python :: 3.13
|
|
@@ -24,8 +23,8 @@ Classifier: Topic :: Text Editors :: Word Processors
|
|
|
24
23
|
Classifier: Typing :: Typed
|
|
25
24
|
Requires-Dist: beautifulsoup4>=4.4.1,<5 ; extra == 'html5lib'
|
|
26
25
|
Requires-Dist: html5lib>=0.999,<2 ; extra == 'html5lib'
|
|
27
|
-
Requires-Dist: lxml>=4.
|
|
28
|
-
Requires-Python: >=3.
|
|
26
|
+
Requires-Dist: lxml>=4.9.4,<7 ; extra == 'lxml'
|
|
27
|
+
Requires-Python: >=3.11
|
|
29
28
|
Project-URL: Documentation, https://wagtail.github.io/draftjs_exporter/
|
|
30
29
|
Project-URL: Source, https://github.com/wagtail/draftjs_exporter
|
|
31
30
|
Project-URL: Tracker, https://github.com/wagtail/draftjs_exporter/issues
|
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: draftjs-exporter
|
|
3
|
+
description: Use when working with the Draft.js exporter library. Manipulating and rendering Draft.js ContentState to HTML or Markdown, parsing Markdown back into ContentState, writing custom block/entity/style components, configuring block/style/entity maps, picking or building DOM, or extending the exporter with fallbacks and composite decorators. Trigger on imports from `draftjs_exporter`, `DOM.create_element`, `block_map` / `style_map` / `entity_decorators` / `composite_decorators`, `build_markdown_config`, `MarkdownImporter` / `ContentStateFilter` / `scheme_resolver`, or Draft.js `ContentState` / `entityMap` JSON.
|
|
4
|
+
license: MIT
|
|
5
|
+
metadata:
|
|
6
|
+
version: "6.0.0"
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Draft.js exporter
|
|
10
|
+
|
|
11
|
+
Python library converting Draft.js raw [ContentState](https://wagtail.github.io/draftjs_exporter/content-state/) JSON into HTML or Markdown. Maintained by [Wagtail](https://wagtail.org/) contributors, developed alongside the [Draftail](https://www.draftail.org/) rich text editor.
|
|
12
|
+
|
|
13
|
+
The public API is small: an `HTMLExporter` class, a `DOM` namespace with a React-like `create_element`, default config maps (`BLOCK_MAP`, `STYLE_MAP`) and helpers (`code_block`, `render_children`), Markdown helpers (`md_*`), constants (`BLOCK_TYPES`, `ENTITY_TYPES`, `INLINE_STYLES`), and type aliases (`Props`, `Element`, `Component`, `ContentState`). Almost everything else is configuration.
|
|
14
|
+
|
|
15
|
+
## Quick reference
|
|
16
|
+
|
|
17
|
+
You can access a Markdown-native version of every documentation page by adding `index.md` at the end of the URL.
|
|
18
|
+
|
|
19
|
+
| Task | Solution | Docs |
|
|
20
|
+
| --------------------------------------------- | --------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- |
|
|
21
|
+
| Render ContentState to HTML | `HTML({}).render(content_state)` | [getting-started](https://wagtail.github.io/draftjs_exporter/getting-started/) |
|
|
22
|
+
| Override default blocks / styles | `**BLOCK_MAP`, `**STYLE_MAP` then override keys | [block map](https://wagtail.github.io/draftjs_exporter/configuration/#block-map) |
|
|
23
|
+
| Add HTML attributes to a block | `BLOCK_TYPES.X: {"element": "h3", "props": {"class": "…"}}` | [block map](https://wagtail.github.io/draftjs_exporter/configuration/#block-map) |
|
|
24
|
+
| Wrap adjacent blocks (lists) | `"wrapper": "ul", "wrapper_props": {"class": "…"}` | [block map](https://wagtail.github.io/draftjs_exporter/configuration/#block-map) |
|
|
25
|
+
| Render entity data (image, link) | `"entity_decorators": {ENTITY_TYPES.LINK: link}` | [entity components](https://wagtail.github.io/draftjs_exporter/custom-components/#entity-components) |
|
|
26
|
+
| Read block data / depth in a component | `props["block"]["data"]`, `props["block"]["depth"]` | [block components](https://wagtail.github.io/draftjs_exporter/custom-components/#block-components) |
|
|
27
|
+
| Compose components / pass children | `DOM.create_element(type, props, *children)` | [nesting](https://wagtail.github.io/draftjs_exporter/custom-components/#nesting-and-reusing-components) |
|
|
28
|
+
| Replace text by regex (line breaks, mentions) | `"composite_decorators": [{"strategy": rx, "component": fn}]` | [composite decorators](https://wagtail.github.io/draftjs_exporter/configuration/#composite-decorators) |
|
|
29
|
+
| Handle missing block / style / entity types | `BLOCK_TYPES.FALLBACK`, `INLINE_STYLES.FALLBACK`, `ENTITY_TYPES.FALLBACK` | [fallbacks](https://wagtail.github.io/draftjs_exporter/fallback-components/) |
|
|
30
|
+
| Discard an entity type | `ENTITY_TYPES.EMBED: None` | [entity decorators](https://wagtail.github.io/draftjs_exporter/configuration/#entity-decorators) |
|
|
31
|
+
| Switch DOM engine | `"engine": DOM.HTML5LIB` (or `DOM.LXML`, `DOM.STRING_COMPAT`) | [alternative engines](https://wagtail.github.io/draftjs_exporter/alternative-engines/) |
|
|
32
|
+
| Render Markdown instead of HTML | `HTML(MARKDOWN_CONFIG).render(content_state)` | [Markdown](https://wagtail.github.io/draftjs_exporter/markdown/) |
|
|
33
|
+
| Customize Markdown characters | `build_markdown_config({"bold": "__", "italic": "*", ...})` | [Markdown chars](https://wagtail.github.io/draftjs_exporter/markdown/#configuring-output-characters) |
|
|
34
|
+
| Import Markdown as ContentState | `MarkdownImporter({}).import_markdown(md)` | [Markdown importer](https://wagtail.github.io/draftjs_exporter/markdown-importer/) |
|
|
35
|
+
| Resolve internal URLs on import (wagtail://) | `scheme_resolver("wagtail", {"page": "LINK"}, coerce={"id": int})` | [Entity resolution](https://wagtail.github.io/draftjs_exporter/markdown-importer/#entity-resolution) |
|
|
36
|
+
| Filter imported content (demote headings) | `ContentStateFilter([{"type": "block", "match": ..., "action": "demote"}])` | [Filtering](https://wagtail.github.io/draftjs_exporter/markdown-importer/#filtering) |
|
|
37
|
+
| Debug output | `DOM.render_debug(elt)`, `echo '{...}' \| python example.py -` | [API](https://wagtail.github.io/draftjs_exporter/api/) |
|
|
38
|
+
| Migrate between major versions | `DOM.STRING_COMPAT` for old `string` output; per-version notes | [migration guide](https://wagtail.github.io/draftjs_exporter/migration-guide/) |
|
|
39
|
+
| Full public API (constants, types, defaults) | `draftjs_exporter.constants`, `types`, `defaults` | [API reference](https://wagtail.github.io/draftjs_exporter/api/) |
|
|
40
|
+
|
|
41
|
+
## Quick start
|
|
42
|
+
|
|
43
|
+
Install `draftjs_exporter` from PyPI.
|
|
44
|
+
|
|
45
|
+
```python
|
|
46
|
+
from draftjs_exporter import HTML
|
|
47
|
+
|
|
48
|
+
exporter = HTML({}) # empty config = use default block/style/entity maps
|
|
49
|
+
|
|
50
|
+
html = exporter.render(
|
|
51
|
+
{
|
|
52
|
+
"entityMap": {},
|
|
53
|
+
"blocks": [
|
|
54
|
+
{
|
|
55
|
+
"key": "6m5fh",
|
|
56
|
+
"text": "Hello, world!",
|
|
57
|
+
"type": "unstyled",
|
|
58
|
+
"depth": 0,
|
|
59
|
+
"inlineStyleRanges": [],
|
|
60
|
+
"entityRanges": [],
|
|
61
|
+
}
|
|
62
|
+
],
|
|
63
|
+
}
|
|
64
|
+
)
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Debug with real JSON: `echo '{"json": "contents"}' | python example.py -`. See [getting-started](https://wagtail.github.io/draftjs_exporter/getting-started/).
|
|
68
|
+
|
|
69
|
+
## Configuration
|
|
70
|
+
|
|
71
|
+
The config is a single dict passed to `HTML()` with four optional keys plus `engine`. Each map extends the built-in defaults (`BLOCK_MAP`, `STYLE_MAP`) — spread them with `**` and override individual keys.
|
|
72
|
+
|
|
73
|
+
```python
|
|
74
|
+
from draftjs_exporter import BLOCK_MAP, BLOCK_TYPES, DOM, ENTITY_TYPES, HTML, STYLE_MAP
|
|
75
|
+
import re
|
|
76
|
+
|
|
77
|
+
config = {
|
|
78
|
+
"block_map": {
|
|
79
|
+
**BLOCK_MAP,
|
|
80
|
+
BLOCK_TYPES.HEADER_TWO: "h2", # string: tag name
|
|
81
|
+
BLOCK_TYPES.HEADER_THREE: {
|
|
82
|
+
"element": "h3",
|
|
83
|
+
"props": {"class": "u-text-center"},
|
|
84
|
+
},
|
|
85
|
+
BLOCK_TYPES.UNORDERED_LIST_ITEM: { # wrapper for adjacent blocks
|
|
86
|
+
"element": "li",
|
|
87
|
+
"wrapper": "ul",
|
|
88
|
+
"wrapper_props": {"class": "bullet-list"},
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
"style_map": {
|
|
92
|
+
**STYLE_MAP,
|
|
93
|
+
"KBD": "kbd",
|
|
94
|
+
"HIGHLIGHT": {
|
|
95
|
+
"element": "strong",
|
|
96
|
+
"props": {"style": {"textDecoration": "underline"}},
|
|
97
|
+
},
|
|
98
|
+
},
|
|
99
|
+
"entity_decorators": {
|
|
100
|
+
ENTITY_TYPES.LINK: lambda props: DOM.create_element(
|
|
101
|
+
"a", {"href": props["url"]}, props["children"]
|
|
102
|
+
),
|
|
103
|
+
ENTITY_TYPES.EMBED: None, # None discards this entity type
|
|
104
|
+
},
|
|
105
|
+
"composite_decorators": [
|
|
106
|
+
{
|
|
107
|
+
"strategy": re.compile(r"\n"),
|
|
108
|
+
"component": br,
|
|
109
|
+
}, # text transformations by regex
|
|
110
|
+
],
|
|
111
|
+
"engine": DOM.STRING, # default; see Engines below
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
exporter = HTML(config)
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
See [configuration reference](https://wagtail.github.io/draftjs_exporter/configuration/) for the full shape.
|
|
118
|
+
|
|
119
|
+
### Conventions
|
|
120
|
+
|
|
121
|
+
- **Extend `BLOCK_MAP` and `STYLE_MAP` with `**` spread instead of rebuilding from scratch — they cover the common Draft.js types and styles.
|
|
122
|
+
- **Use `BLOCK_TYPES` / `INLINE_STYLES` / `ENTITY_TYPES` constants** instead of raw strings, so renames surface as test failures. They also expose `FALLBACK`.
|
|
123
|
+
- **Pick a component function only when you need block data, depth, or children composition.** A plain string or dict covers most cases.
|
|
124
|
+
- **Stick with the default `string` engine** unless you need HTML sanitization (`html5lib`/`lxml`).
|
|
125
|
+
|
|
126
|
+
## Custom components
|
|
127
|
+
|
|
128
|
+
The component API mirrors React's `createElement`: a function takes a `props` dict and returns an `Element`. The `props` shape differs between entities, blocks, and styles. Reference components from `entity_decorators` (entities) or `block_map` / `style_map` (blocks and styles).
|
|
129
|
+
|
|
130
|
+
```python
|
|
131
|
+
from draftjs_exporter import DOM, Element, Props
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
# Entity component: receives the entity's `data` dict as props.
|
|
135
|
+
def image(props: Props) -> Element:
|
|
136
|
+
"""Render an image element from entity data."""
|
|
137
|
+
return DOM.create_element(
|
|
138
|
+
"img",
|
|
139
|
+
{
|
|
140
|
+
"src": props.get("src"),
|
|
141
|
+
"width": props.get("width"),
|
|
142
|
+
"height": props.get("height"),
|
|
143
|
+
"alt": props.get("alt"),
|
|
144
|
+
},
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
# Block component: receives `block` (Draft.js block object) and `children`.
|
|
149
|
+
def blockquote(props: Props) -> Element:
|
|
150
|
+
"""Render a blockquote with an optional cite attribute."""
|
|
151
|
+
block_data = props["block"]["data"]
|
|
152
|
+
return DOM.create_element(
|
|
153
|
+
"blockquote", {"cite": block_data.get("cite")}, props["children"]
|
|
154
|
+
)
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
Compose by passing extra positional children to `DOM.create_element(type, props, *children)`. Children can be strings, DOM elements, other components, or `None` (renders nothing). Pass `props["children"]` as the last argument so the block's content renders inside the wrapping element. See [custom components](https://wagtail.github.io/draftjs_exporter/custom-components/).
|
|
158
|
+
|
|
159
|
+
### Component props and return values
|
|
160
|
+
|
|
161
|
+
| Component for | `props` contains |
|
|
162
|
+
| ---------------------------- | ---------------------------------------------------------------------------------------------------------------- |
|
|
163
|
+
| Entity (`entity_decorators`) | the entity's `data` keys, plus `children` and `entity` (`type`, `mutability`, `block`, `blocks`, `entity_range`) |
|
|
164
|
+
| Block (`block_map`) | `block`, `blocks`, `children` |
|
|
165
|
+
| Inline style (`style_map`) | `block`, `blocks`, `children`, `inline_style_range` |
|
|
166
|
+
|
|
167
|
+
The style name is `props["inline_style_range"]["style"]`; there is no `props["style"]`. Fallback components receive the same props as a regular component of the same kind.
|
|
168
|
+
|
|
169
|
+
```python
|
|
170
|
+
def style_fallback(props: Props) -> Element:
|
|
171
|
+
name = props["inline_style_range"]["style"]
|
|
172
|
+
return DOM.create_element("span", {"class": name.lower()}, props["children"])
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def trusted_markup(props: Props) -> Element:
|
|
176
|
+
return DOM.parse_html(props["markup"]) # one argument, output unescaped
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
`DOM.create_element` with `None` as the type returns an empty fragment and ignores its children. To group children without a wrapping tag, use `DOM.create_element("fragment", {}, child_a, child_b)`. `ENTITY_TYPES` only has `LINK`, `DOCUMENT`, `IMAGE`, `EMBED`, `HORIZONTAL_RULE` and `FALLBACK`; key any other entity type by its raw string.
|
|
180
|
+
|
|
181
|
+
### Fallbacks
|
|
182
|
+
|
|
183
|
+
Each map accepts a `FALLBACK` key (`BLOCK_TYPES.FALLBACK`, `INLINE_STYLES.FALLBACK`, `ENTITY_TYPES.FALLBACK`) triggered when the exporter hits a type with no explicit mapping. A fallback can return `props["children"]` (keep content, drop wrapper), `None` (remove entirely), or any DOM element (alternative rendering). Useful during development and migrations. See [fallback components](https://wagtail.github.io/draftjs_exporter/fallback-components/).
|
|
184
|
+
|
|
185
|
+
## Engines
|
|
186
|
+
|
|
187
|
+
Engines are pluggable serialization strategies selected at runtime via the `engine` config key. Use the `DOM` class constants:
|
|
188
|
+
|
|
189
|
+
| Constant | Extra install | Notes |
|
|
190
|
+
| ------------------- | ------------------------------------------------------ | ------------------------------------------------- |
|
|
191
|
+
| `DOM.STRING` | none (default) | Fast, dependency-free, `parse_html` unsanitized |
|
|
192
|
+
| `DOM.HTML5LIB` | `pip install draftjs_exporter[html5lib]` | `parse_html` parses and normalizes markup |
|
|
193
|
+
| `DOM.LXML` | `pip install draftjs_exporter[lxml]` + libxml2/libxslt | Same as `html5lib`, alphabetical attrs |
|
|
194
|
+
| `DOM.STRING_COMPAT` | none | Byte-identical to first release of `string` |
|
|
195
|
+
| `DOM.MARKDOWN` | none | Produces Markdown — use `MARKDOWN_CONFIG` instead |
|
|
196
|
+
|
|
197
|
+
**Engines are not guaranteed to produce byte-identical output.** Real differences: attribute ordering (alphabetical for `lxml`/`html5lib`, insertion order for `string`), quote escaping in attributes, attribute-name validation. Expect minor output differences when switching engines — re-check tests that compare rendered HTML exactly. See [troubleshooting: exporter behavior](https://wagtail.github.io/draftjs_exporter/troubleshooting/#exporter-behavior).
|
|
198
|
+
|
|
199
|
+
To build a custom engine, subclass `DOMEngine` and implement `create_tag`, `append_child`, `render`. Reference it by dotted path: `"engine": "my_project.example.DOMListTree"`. Built-in engines also accept short names: `"engine": "string"` etc. See [custom engines](https://wagtail.github.io/draftjs_exporter/custom-engines/).
|
|
200
|
+
|
|
201
|
+
## Markdown
|
|
202
|
+
|
|
203
|
+
Markdown output is **experimental**. Prefer `MARKDOWN_CONFIG` and `build_markdown_config` over hand-rolling a Markdown config.
|
|
204
|
+
|
|
205
|
+
```python
|
|
206
|
+
from draftjs_exporter import HTML, MARKDOWN_CONFIG
|
|
207
|
+
|
|
208
|
+
exporter = HTML(MARKDOWN_CONFIG)
|
|
209
|
+
markdown = exporter.render(content_state)
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
Customize characters and fallbacks with `build_markdown_config`:
|
|
213
|
+
|
|
214
|
+
````python
|
|
215
|
+
from draftjs_exporter import HTML, build_markdown_config
|
|
216
|
+
|
|
217
|
+
config = build_markdown_config(
|
|
218
|
+
{
|
|
219
|
+
"bold": "__",
|
|
220
|
+
"italic": "*",
|
|
221
|
+
"unordered_list_marker": "*",
|
|
222
|
+
"ordered_list_delimiter": ")",
|
|
223
|
+
"horizontal_rule": "---",
|
|
224
|
+
"code_fence": "```",
|
|
225
|
+
"style_fallback": None, # None disables fallback (raises instead)
|
|
226
|
+
}
|
|
227
|
+
)
|
|
228
|
+
exporter = HTML(config)
|
|
229
|
+
````
|
|
230
|
+
|
|
231
|
+
`MARKDOWN_CONFIG` is a regular config dict (`block_map`, `style_map`, `entity_decorators`, `engine`): extend its maps with `**` to change one type and keep the rest. In Markdown components, plain strings are escaped as user text (a returned `"**"` is output as `\*\*`), and a tag name such as `"span"` is output as literal `<span>` HTML. Emit Markdown syntax with these helpers:
|
|
232
|
+
|
|
233
|
+
- `md_inline_style(mark)` is a factory: it returns a style component wrapping children in `mark` on both sides.
|
|
234
|
+
- `md_mark_safe(markup)` returns an element rendered verbatim, unescaped.
|
|
235
|
+
- `md_inline(children)` takes a single list and returns a fragment without a wrapper.
|
|
236
|
+
|
|
237
|
+
```python
|
|
238
|
+
from draftjs_exporter import (
|
|
239
|
+
HTML,
|
|
240
|
+
MARKDOWN_CONFIG,
|
|
241
|
+
md_inline,
|
|
242
|
+
md_inline_style,
|
|
243
|
+
md_mark_safe,
|
|
244
|
+
)
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
def footnote(props):
|
|
248
|
+
# Replaces the covered text: props["children"] is not used.
|
|
249
|
+
return md_inline([md_mark_safe("[^"), props["ref"], md_mark_safe("]")])
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
config = {
|
|
253
|
+
**MARKDOWN_CONFIG,
|
|
254
|
+
"style_map": {**MARKDOWN_CONFIG["style_map"], "SPOILER": md_inline_style("||")},
|
|
255
|
+
"entity_decorators": {**MARKDOWN_CONFIG["entity_decorators"], "FOOTNOTE": footnote},
|
|
256
|
+
}
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
All defaults produce valid [CommonMark](https://commonmark.org/). The exporter [escapes](https://wagtail.github.io/draftjs_exporter/markdown/#escaping) user text so it renders literally rather than as Markdown syntax. Limitations: no underline/subscript/reference-style links/tables; partial bold/italic overlap can produce markers strict parsers reject. See [Markdown support](https://wagtail.github.io/draftjs_exporter/markdown/).
|
|
260
|
+
|
|
261
|
+
### Importer
|
|
262
|
+
|
|
263
|
+
The Markdown importer (`MarkdownImporter`) parses Markdown back into Draft.js `ContentState`, enabling round-trip workflows (`ContentState → Markdown → ContentState`). It is dependency-free and covers the CommonMark core. Parsing runs first, then optional filtering applies content policy.
|
|
264
|
+
|
|
265
|
+
```python
|
|
266
|
+
from draftjs_exporter import BLOCK_TYPES, MarkdownImporter, scheme_resolver
|
|
267
|
+
|
|
268
|
+
importer = MarkdownImporter(
|
|
269
|
+
{
|
|
270
|
+
"parser_config": {
|
|
271
|
+
# Disable constructs, or resolve internal URL schemes to typed entities.
|
|
272
|
+
"image_resolvers": [
|
|
273
|
+
scheme_resolver(
|
|
274
|
+
"wagtail", {"image": "IMAGE"}, coerce={"id": int}, label_key="alt"
|
|
275
|
+
),
|
|
276
|
+
],
|
|
277
|
+
# Whitelist inline HTML tags as styles (no Markdown equivalent).
|
|
278
|
+
"inline_html_styles": {"sup": "SUPERSCRIPT", "sub": "SUBSCRIPT"},
|
|
279
|
+
},
|
|
280
|
+
"filter_rules": [
|
|
281
|
+
# Declarative content policy: remove, keep, demote, or a callable.
|
|
282
|
+
{"type": "block", "match": BLOCK_TYPES.HEADER_ONE, "action": "demote"},
|
|
283
|
+
{"type": "inline_style", "match": "UNDERLINE", "action": "remove"},
|
|
284
|
+
{
|
|
285
|
+
"type": "block",
|
|
286
|
+
"match": BLOCK_TYPES.CODE,
|
|
287
|
+
"action": lambda block: {**block, "type": BLOCK_TYPES.UNSTYLED},
|
|
288
|
+
},
|
|
289
|
+
],
|
|
290
|
+
}
|
|
291
|
+
)
|
|
292
|
+
content_state = importer.import_markdown(markdown)
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
Filter rules:
|
|
296
|
+
|
|
297
|
+
- `type` is `"block"`, `"inline_style"`, or `"entity"`. `match` is the block type, style name, or entity type.
|
|
298
|
+
- `"remove"` deletes the match: the whole block, the style range (text stays), or the entity (text stays).
|
|
299
|
+
- `"demote"` only applies to `header-one` through `header-five` blocks, and raises for anything else. Change other block types with a callable.
|
|
300
|
+
- A callable receives the matched object and returns its replacement, or `None` to remove it: a block or entity dict for `"block"` / `"entity"` rules, the style name for `"inline_style"` rules.
|
|
301
|
+
- `ContentStateFilter(rules).apply(content_state)` applies the same rules to any ContentState, without Markdown.
|
|
302
|
+
|
|
303
|
+
See [Markdown importer](https://wagtail.github.io/draftjs_exporter/markdown-importer/). The importer inverts the exporter's text escaping and sized code-span delimiters on round-trip; see [Known round-trip limitations](https://wagtail.github.io/draftjs_exporter/markdown-importer/#known-round-trip-limitations) for the remaining gaps.
|
|
304
|
+
|
|
305
|
+
## Common gotchas
|
|
306
|
+
|
|
307
|
+
1. **`entity` and `children` are reserved `props` keys.** The exporter overrides them — `entity` becomes a dict with `type`/`mutability`, and `children` becomes the already-rendered content. Pick entity `data` keys that avoid them; there is no workaround. See [entity props override](https://wagtail.github.io/draftjs_exporter/troubleshooting/#entity-props-override).
|
|
308
|
+
2. **Every engine escapes text.** A string returned by a component, or passed as a child to `DOM.create_element`, renders as text with `<`, `>` and `&` escaped, including with the default `string` engine. To output trusted markup unescaped, return `DOM.parse_html(markup)`. It takes exactly one argument. With the `string` engine it does no sanitization; use `html5lib`/`lxml` if you need that.
|
|
309
|
+
3. **Engine output is not byte-identical across engines.** Switching engines produces real differences (attribute order, quote escaping, self-closing tags, attribute-name validation). Update snapshot tests when changing `engine`.
|
|
310
|
+
4. **Overlapping inline styles render with minimum tags** (e.g. `<strong>Bold <em>Italic</em></strong>` rather than reopening `<strong>`). Semantically equivalent but breaks tests asserting exact strings.
|
|
311
|
+
5. **`style` props accept a dict** (camelCase keys) converted to a CSS string. Properties keep insertion order — not sorted alphabetically. Pass `style` as a string for byte-stable output.
|
|
312
|
+
6. **`className` is not auto-converted to `class`.** Use `class` directly.
|
|
313
|
+
7. **Engine constants are dotted-path strings, not classes.** The exporter imports the class lazily at runtime via `import_string`.
|
|
314
|
+
8. **`unstyled` blocks without text render as empty elements** (`<p></p>`), not nothing.
|
|
315
|
+
|
|
316
|
+
## Public API
|
|
317
|
+
|
|
318
|
+
All imported from `draftjs_exporter` directly:
|
|
319
|
+
|
|
320
|
+
- **`HTMLExporter`** — `HTMLExporter(config).render(content_state)`.
|
|
321
|
+
- **`DOM`** — facade over the active engine. `create_element`, `render`, `render_debug`, `parse_html`, `append_child`, `camel_to_dash`. Engine constants: `DOM.STRING`, `DOM.HTML5LIB`, `DOM.LXML`, `DOM.STRING_COMPAT`, `DOM.MARKDOWN`.
|
|
322
|
+
- **Default maps & configs**: `BLOCK_MAP`, `STYLE_MAP`, `HTML_CONFIG`, `MARKDOWN_CONFIG`.
|
|
323
|
+
- **Default components**: `code_block` (`pre` > `code`), `render_children` (passthrough; used for atomic blocks).
|
|
324
|
+
- **Constants**: `BLOCK_TYPES`, `INLINE_STYLES`, `ENTITY_TYPES` (each with a `FALLBACK` member).
|
|
325
|
+
- **Markdown helper**: `build_markdown_config(options)`, plus the option type alias `MarkdownOptions`.
|
|
326
|
+
- **Markdown components**: `md_block`, `md_inline`, `md_mark_safe`, `md_link_destination`, `md_link`, `md_image`, `md_prefixed_block`, `md_make_ul` / `md_ul`, `md_make_ol` / `md_ol`, `md_list_wrapper`, `md_code_element` / `md_code_wrapper`, `md_inline_style`, `md_code_span`, `md_horizontal_rule` / `md_make_horizontal_rule`, `md_*_fallback`.
|
|
327
|
+
- **Importer**: `MarkdownImporter(config).import_markdown(markdown)` — converts Markdown to ContentState. Config keys: `parser` (dotted path), `parser_config` (feature toggles, `link_resolvers`/`image_resolvers`, `inline_html_styles`), `filter_rules`.
|
|
328
|
+
- **Parser**: `MarkdownParser(config).parse(markdown)`, `ParserConfig`, `scheme_resolver(scheme, type_map, coerce, label_key, mutability)`, `EntityResolver`, `EntityResolution`.
|
|
329
|
+
- **Filter**: `ContentStateFilter(rules).apply(content_state)`, `FilterRule` (`type`: `block`/`inline_style`/`entity`; `match`; `action`: `remove`/`keep`/`demote`/callable).
|
|
330
|
+
- **Errors**: `MarkdownParseError` (with `.line` and `.message`).
|
|
331
|
+
- **Type aliases**: `Props`, `Element`, `Component`, `ContentState`, `Block`, `Entity`, `EntityMap`, `EntityRange`, `InlineStyleRange`, `RenderableConfig`, `ExporterConfig`, plus internals (`CompositeDecorators`, `ConfigMap`, `Decorator`, `EntityKey`, `Mutability`, `RenderableType`, `Tag`).
|
|
332
|
+
- **`DOMEngine`** — abstract base for custom engines, re-exported at the top level.
|
|
333
|
+
|
|
334
|
+
For every `BLOCK_TYPES.*`, `INLINE_STYLES.*`, `ENTITY_TYPES.*` value, see [the API reference](https://wagtail.github.io/draftjs_exporter/api/) or [`constants.py`](https://github.com/wagtail/draftjs_exporter/blob/main/draftjs_exporter/constants.py).
|
|
335
|
+
|
|
336
|
+
## Resources
|
|
337
|
+
|
|
338
|
+
- [Full docs site](https://wagtail.github.io/draftjs_exporter/)
|
|
339
|
+
- [llms-full.txt](https://wagtail.github.io/draftjs_exporter/llms-full.txt)
|
|
340
|
+
- [GitHub](https://github.com/wagtail/draftjs_exporter)
|
|
@@ -6,7 +6,7 @@ type aliases, and helpers so callers can import everything from
|
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
8
|
__title__ = "draftjs_exporter"
|
|
9
|
-
__version__ = "7.
|
|
9
|
+
__version__ = "7.2.0"
|
|
10
10
|
__uri__ = "https://wagtail.github.io/draftjs_exporter/"
|
|
11
11
|
__author__ = "Springload and Contributors"
|
|
12
12
|
__license__ = "MIT"
|
|
@@ -24,12 +24,17 @@ from draftjs_exporter.defaults import STYLE_MAP as STYLE_MAP
|
|
|
24
24
|
from draftjs_exporter.defaults import code_block as code_block
|
|
25
25
|
from draftjs_exporter.defaults import render_children as render_children
|
|
26
26
|
from draftjs_exporter.dom import DOM as DOM
|
|
27
|
+
from draftjs_exporter.engines.base import DOMEngine as DOMEngine
|
|
28
|
+
from draftjs_exporter.entity_state import (
|
|
29
|
+
ExporterEntityException as ExporterEntityException,
|
|
30
|
+
)
|
|
27
31
|
from draftjs_exporter.error import MarkdownParseError as MarkdownParseError
|
|
28
32
|
from draftjs_exporter.html import HTML as HTML
|
|
29
33
|
from draftjs_exporter.html import ExporterConfig as ExporterConfig
|
|
30
34
|
from draftjs_exporter.markdown import CONFIG as MARKDOWN_CONFIG
|
|
31
35
|
from draftjs_exporter.markdown import MarkdownOptions as MarkdownOptions
|
|
32
36
|
from draftjs_exporter.markdown import build_markdown_config as build_markdown_config
|
|
37
|
+
from draftjs_exporter.markdown.blocks import md_blockquote as md_blockquote
|
|
33
38
|
from draftjs_exporter.markdown.blocks import md_list_wrapper as md_list_wrapper
|
|
34
39
|
from draftjs_exporter.markdown.blocks import md_make_ol as md_make_ol
|
|
35
40
|
from draftjs_exporter.markdown.blocks import md_make_ul as md_make_ul
|
|
@@ -46,6 +51,10 @@ from draftjs_exporter.markdown.entities import md_link as md_link
|
|
|
46
51
|
from draftjs_exporter.markdown.entities import (
|
|
47
52
|
md_make_horizontal_rule as md_make_horizontal_rule,
|
|
48
53
|
)
|
|
54
|
+
from draftjs_exporter.markdown.escape import (
|
|
55
|
+
md_escape_link_destination as md_escape_link_destination,
|
|
56
|
+
)
|
|
57
|
+
from draftjs_exporter.markdown.escape import md_escape_text as md_escape_text
|
|
49
58
|
from draftjs_exporter.markdown.fallbacks import md_block_fallback as md_block_fallback
|
|
50
59
|
from draftjs_exporter.markdown.fallbacks import md_entity_fallback as md_entity_fallback
|
|
51
60
|
from draftjs_exporter.markdown.fallbacks import md_style_fallback as md_style_fallback
|
|
@@ -53,6 +62,7 @@ from draftjs_exporter.markdown.helpers import md_block as md_block
|
|
|
53
62
|
from draftjs_exporter.markdown.helpers import md_inline as md_inline
|
|
54
63
|
from draftjs_exporter.markdown.helpers import md_link_destination as md_link_destination
|
|
55
64
|
from draftjs_exporter.markdown.helpers import md_mark_safe as md_mark_safe
|
|
65
|
+
from draftjs_exporter.markdown.lists import md_make_list_item as md_make_list_item
|
|
56
66
|
from draftjs_exporter.markdown.styles import md_code_span as md_code_span
|
|
57
67
|
from draftjs_exporter.markdown.styles import md_inline_style as md_inline_style
|
|
58
68
|
from draftjs_exporter.markdown_importer import ImporterConfig as ImporterConfig
|
|
@@ -95,6 +105,8 @@ __all__ = [
|
|
|
95
105
|
"ExporterConfig",
|
|
96
106
|
"ContentState",
|
|
97
107
|
"DOM",
|
|
108
|
+
"DOMEngine",
|
|
109
|
+
"ExporterEntityException",
|
|
98
110
|
# Configs
|
|
99
111
|
"HTML_CONFIG",
|
|
100
112
|
"MARKDOWN_CONFIG",
|
|
@@ -103,10 +115,13 @@ __all__ = [
|
|
|
103
115
|
# Markdown helpers
|
|
104
116
|
"md_block",
|
|
105
117
|
"md_block_fallback",
|
|
118
|
+
"md_blockquote",
|
|
106
119
|
"md_code_element",
|
|
107
120
|
"md_code_span",
|
|
108
121
|
"md_code_wrapper",
|
|
109
122
|
"md_entity_fallback",
|
|
123
|
+
"md_escape_link_destination",
|
|
124
|
+
"md_escape_text",
|
|
110
125
|
"md_horizontal_rule",
|
|
111
126
|
"md_image",
|
|
112
127
|
"md_inline",
|
|
@@ -117,6 +132,7 @@ __all__ = [
|
|
|
117
132
|
"md_make_code_element",
|
|
118
133
|
"md_make_code_wrapper",
|
|
119
134
|
"md_make_horizontal_rule",
|
|
135
|
+
"md_make_list_item",
|
|
120
136
|
"md_make_ol",
|
|
121
137
|
"md_make_ul",
|
|
122
138
|
"md_mark_safe",
|
|
@@ -38,7 +38,7 @@ class Command:
|
|
|
38
38
|
Returns:
|
|
39
39
|
A list of start_entity and stop_entity commands.
|
|
40
40
|
"""
|
|
41
|
-
commands: list[
|
|
41
|
+
commands: list[Command] = []
|
|
42
42
|
for r in block.get("entityRanges", []):
|
|
43
43
|
# Entity key is an integer in entity ranges, while a string in the entity map.
|
|
44
44
|
data = str(r["key"])
|
|
@@ -59,7 +59,7 @@ class Command:
|
|
|
59
59
|
Returns:
|
|
60
60
|
A list of start_inline_style and stop_inline_style commands.
|
|
61
61
|
"""
|
|
62
|
-
commands: list[
|
|
62
|
+
commands: list[Command] = []
|
|
63
63
|
for r in block.get("inlineStyleRanges", []):
|
|
64
64
|
data = r["style"]
|
|
65
65
|
start = r["offset"]
|
|
@@ -36,6 +36,35 @@ class DOM:
|
|
|
36
36
|
STRING_COMPAT = "draftjs_exporter.engines.string_compat.DOMStringCompat"
|
|
37
37
|
"""Identifier for the string compatibility DOM engine."""
|
|
38
38
|
|
|
39
|
+
_BUILTIN_ENGINES = {
|
|
40
|
+
"html5lib": HTML5LIB,
|
|
41
|
+
"lxml": LXML,
|
|
42
|
+
"markdown": MARKDOWN,
|
|
43
|
+
"string": STRING,
|
|
44
|
+
"string_compat": STRING_COMPAT,
|
|
45
|
+
}
|
|
46
|
+
"""Short names accepted as engine identifiers, mapped to their dotted paths."""
|
|
47
|
+
|
|
48
|
+
@classmethod
|
|
49
|
+
def _resolve_engine(cls, engine: str) -> type[DOMEngine]:
|
|
50
|
+
"""Resolve an engine identifier to the engine class.
|
|
51
|
+
|
|
52
|
+
Accepts the short names of the built-in engines (``"string"``,
|
|
53
|
+
``"lxml"``, ...), or a dotted import path for any other engine.
|
|
54
|
+
|
|
55
|
+
Parameters:
|
|
56
|
+
engine: A short name or dotted import path of a DOM engine.
|
|
57
|
+
|
|
58
|
+
Returns:
|
|
59
|
+
The resolved engine class.
|
|
60
|
+
|
|
61
|
+
Raises:
|
|
62
|
+
ImportError: If the name is not a built-in engine and not a
|
|
63
|
+
valid dotted import path.
|
|
64
|
+
"""
|
|
65
|
+
dotted_path = cls._BUILTIN_ENGINES.get(engine, engine)
|
|
66
|
+
return cast(type[DOMEngine], import_string(dotted_path))
|
|
67
|
+
|
|
39
68
|
@staticmethod
|
|
40
69
|
def camel_to_dash(camel_cased_str: str) -> str:
|
|
41
70
|
"""Convert a camelCase string to a dashed-case attribute name."""
|
|
@@ -58,9 +87,7 @@ class DOM:
|
|
|
58
87
|
try:
|
|
59
88
|
resolved = _engine_cache[engine]
|
|
60
89
|
except KeyError:
|
|
61
|
-
resolved = _engine_cache[engine] =
|
|
62
|
-
type[DOMEngine], import_string(engine)
|
|
63
|
-
)
|
|
90
|
+
resolved = _engine_cache[engine] = cls._resolve_engine(engine)
|
|
64
91
|
_engine_var.set(resolved)
|
|
65
92
|
|
|
66
93
|
@staticmethod
|
|
@@ -70,9 +97,7 @@ class DOM:
|
|
|
70
97
|
try:
|
|
71
98
|
resolved = _engine_cache[engine]
|
|
72
99
|
except KeyError:
|
|
73
|
-
resolved = _engine_cache[engine] =
|
|
74
|
-
type[DOMEngine], import_string(engine)
|
|
75
|
-
)
|
|
100
|
+
resolved = _engine_cache[engine] = DOM._resolve_engine(engine)
|
|
76
101
|
token = _engine_var.set(resolved)
|
|
77
102
|
try:
|
|
78
103
|
yield
|
|
@@ -2,16 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
import re
|
|
4
4
|
|
|
5
|
+
from bs4 import BeautifulSoup
|
|
6
|
+
|
|
5
7
|
from draftjs_exporter.engines.base import Attr, DOMEngine
|
|
6
8
|
from draftjs_exporter.types import HTML, Element, Tag
|
|
7
9
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
# Cache empty soup so we can create tags in isolation without the performance overhead.
|
|
12
|
-
soup = BeautifulSoup("", "html5lib")
|
|
13
|
-
except ImportError:
|
|
14
|
-
pass
|
|
10
|
+
# Cache empty soup so we can create tags in isolation without the performance overhead.
|
|
11
|
+
soup = BeautifulSoup("", "html5lib")
|
|
15
12
|
|
|
16
13
|
RENDER_RE = re.compile(r"</?(fragment|body|html|head)>")
|
|
17
14
|
RENDER_DEBUG_RE = re.compile(r"</?(body|html|head)>")
|
|
@@ -69,8 +69,10 @@ class DOM_LXML(DOMEngine):
|
|
|
69
69
|
elt: The parent element.
|
|
70
70
|
child: The child element or text to append.
|
|
71
71
|
"""
|
|
72
|
-
#
|
|
73
|
-
#
|
|
72
|
+
# Compat: lxml below 6.0.0 has no `etree.Element` class, only a
|
|
73
|
+
# Cython function, so the `etree.Element | str` annotation above
|
|
74
|
+
# would raise a TypeError when evaluated eagerly, on Python 3.11
|
|
75
|
+
# through 3.13. Revert once the lxml extra requires 6.0.0.
|
|
74
76
|
if hasattr(child, "tag"):
|
|
75
77
|
elt.append(child)
|
|
76
78
|
else:
|
|
@@ -6,7 +6,7 @@ from draftjs_exporter.engines.base import Attr, DOMEngine
|
|
|
6
6
|
from draftjs_exporter.markdown.escape import (
|
|
7
7
|
code_block_fence,
|
|
8
8
|
code_span_delimiters,
|
|
9
|
-
|
|
9
|
+
md_escape_text,
|
|
10
10
|
)
|
|
11
11
|
from draftjs_exporter.types import HTML, Tag
|
|
12
12
|
|
|
@@ -40,7 +40,7 @@ class Elt:
|
|
|
40
40
|
|
|
41
41
|
__slots__ = ("type", "attr", "children", "markup")
|
|
42
42
|
|
|
43
|
-
def __init__(self, type_: Tag, attr: Attr | None, markup: HTML = ""):
|
|
43
|
+
def __init__(self, type_: Tag, attr: Attr | None, markup: HTML = "") -> None:
|
|
44
44
|
"""Initialize a new element.
|
|
45
45
|
|
|
46
46
|
Parameters:
|
|
@@ -50,7 +50,7 @@ class Elt:
|
|
|
50
50
|
"""
|
|
51
51
|
self.type = type_
|
|
52
52
|
self.attr = attr
|
|
53
|
-
self.children: list[
|
|
53
|
+
self.children: list[str | Elt] = []
|
|
54
54
|
self.markup = markup
|
|
55
55
|
|
|
56
56
|
@staticmethod
|
|
@@ -160,7 +160,7 @@ class DOMMarkdown(DOMEngine):
|
|
|
160
160
|
elif rendered:
|
|
161
161
|
at_line_start = rendered.endswith(("\n", "\r"))
|
|
162
162
|
else:
|
|
163
|
-
out.append(
|
|
163
|
+
out.append(md_escape_text(c, at_line_start))
|
|
164
164
|
if c:
|
|
165
165
|
at_line_start = c.endswith(("\n", "\r"))
|
|
166
166
|
return "".join(out)
|
|
@@ -34,7 +34,7 @@ class Elt:
|
|
|
34
34
|
|
|
35
35
|
__slots__ = ("type", "attr", "children", "markup")
|
|
36
36
|
|
|
37
|
-
def __init__(self, type_: Tag, attr: Attr | None, markup: HTML = ""):
|
|
37
|
+
def __init__(self, type_: Tag, attr: Attr | None, markup: HTML = "") -> None:
|
|
38
38
|
"""Initialize a new element.
|
|
39
39
|
|
|
40
40
|
Parameters:
|
|
@@ -44,7 +44,7 @@ class Elt:
|
|
|
44
44
|
"""
|
|
45
45
|
self.type = type_
|
|
46
46
|
self.attr = attr
|
|
47
|
-
self.children: list[
|
|
47
|
+
self.children: list[str | Elt] = []
|
|
48
48
|
self.markup = markup
|
|
49
49
|
|
|
50
50
|
@staticmethod
|