draftjs_exporter 5.2.0__py3-none-any.whl

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.
@@ -0,0 +1,170 @@
1
+ from typing import List, Optional, Sequence, Union
2
+
3
+ from draftjs_exporter.constants import BLOCK_TYPES
4
+ from draftjs_exporter.dom import DOM
5
+ from draftjs_exporter.options import Options, OptionsMap
6
+ from draftjs_exporter.types import Block, Element, Props, RenderableType
7
+
8
+
9
+ class Wrapper:
10
+ """
11
+ A wrapper is an element that wraps other nodes. It gets created
12
+ when the depth of a block is different than 0, so the DOM elements
13
+ have the appropriate amount of nesting.
14
+ """
15
+
16
+ __slots__ = ("depth", "last_child", "type", "props", "elt")
17
+
18
+ def __init__(self, depth: int, options: Optional[Options] = None) -> None:
19
+ self.depth = depth
20
+ self.last_child = None
21
+
22
+ if options:
23
+ self.type = options.wrapper
24
+ self.props = options.wrapper_props
25
+
26
+ wrapper_props = dict(self.props) if self.props else {}
27
+ wrapper_props["block"] = {"type": options.type, "depth": depth}
28
+
29
+ self.elt = DOM.create_element(self.type, wrapper_props)
30
+ else:
31
+ self.type = None
32
+ self.props = None
33
+ self.elt = DOM.create_element()
34
+
35
+ def is_different(
36
+ self, depth: int, type_: RenderableType, props: Optional[Props]
37
+ ) -> bool:
38
+ return depth > self.depth or type_ != self.type or props != self.props
39
+
40
+
41
+ class WrapperStack:
42
+ """
43
+ Stack data structure for element wrappers.
44
+ The bottom of the stack contains the elements closest to the page body.
45
+ The top of the stack contains the most nested nodes.
46
+ """
47
+
48
+ __slots__ = "stack"
49
+
50
+ def __init__(self) -> None:
51
+ self.stack: List[Wrapper] = []
52
+
53
+ def __str__(self) -> str:
54
+ return str(self.stack)
55
+
56
+ def length(self) -> int:
57
+ return len(self.stack)
58
+
59
+ def append(self, wrapper: Wrapper) -> None:
60
+ return self.stack.append(wrapper)
61
+
62
+ def get(self, index: int) -> Wrapper:
63
+ return self.stack[index]
64
+
65
+ def slice(self, length: int) -> None:
66
+ self.stack = self.stack[:length]
67
+
68
+ def head(self) -> Wrapper:
69
+ if self.length() > 0:
70
+ wrapper = self.stack[-1]
71
+ else:
72
+ wrapper = Wrapper(-1)
73
+
74
+ return wrapper
75
+
76
+ def tail(self) -> Wrapper:
77
+ return self.stack[0]
78
+
79
+
80
+ class WrapperState:
81
+ """
82
+ This class does the initial node building for the tree.
83
+ It sets elements with the right tag, text content, and props.
84
+ It adds a wrapper element around elements, if required.
85
+ """
86
+
87
+ __slots__ = ("block_options", "blocks", "stack")
88
+
89
+ def __init__(self, block_options: OptionsMap, blocks: Sequence[Block]) -> None:
90
+ self.block_options = block_options
91
+ self.blocks = blocks
92
+ self.stack = WrapperStack()
93
+
94
+ def __str__(self) -> str:
95
+ return f"<WrapperState: {self.stack}>"
96
+
97
+ def element_for(
98
+ self, block: Block, block_content: Union[Element, Sequence[Element]]
99
+ ) -> Element:
100
+ type_ = block["type"] if "type" in block else "unstyled"
101
+ depth = block["depth"] if "depth" in block else 0
102
+ options = Options.get(self.block_options, type_, BLOCK_TYPES.FALLBACK)
103
+ props = dict(options.props)
104
+ props["block"] = block
105
+ props["blocks"] = self.blocks
106
+
107
+ # Make an element from the options specified in the block map.
108
+ elt = DOM.create_element(options.element, props, block_content)
109
+
110
+ parent = self.parent_for(options, depth, elt)
111
+
112
+ return parent
113
+
114
+ def parent_for(self, options: Options, depth: int, elt: Element) -> Element:
115
+ if options.wrapper:
116
+ parent = self.get_wrapper_elt(options, depth)
117
+ DOM.append_child(parent, elt)
118
+ self.stack.stack[-1].last_child = elt
119
+ else:
120
+ # Reset the stack if there is no wrapper.
121
+ if self.stack.length() > 0:
122
+ self.stack = WrapperStack()
123
+ parent = elt
124
+
125
+ return parent
126
+
127
+ def get_wrapper_elt(self, options: Options, depth: int) -> Element:
128
+ head = self.stack.head()
129
+ if head.is_different(depth, options.wrapper, options.wrapper_props):
130
+ self.update_stack(options, depth)
131
+
132
+ # If depth is lower than the maximum, we cut the stack.
133
+ if depth < head.depth:
134
+ self.stack.slice(depth + 1)
135
+
136
+ return self.stack.get(depth).elt
137
+
138
+ def update_stack(self, options: Options, depth: int) -> None:
139
+ if depth >= self.stack.length():
140
+ # If the depth is gte the stack length, we need more wrappers.
141
+ depth_levels = range(self.stack.length(), depth + 1)
142
+
143
+ for level in depth_levels:
144
+ new_wrapper = Wrapper(level, options)
145
+
146
+ # Determine where to append the new wrapper.
147
+ if self.stack.head().last_child is None:
148
+ # If there is no content in the current wrapper, we need
149
+ # to add an intermediary node.
150
+ props = dict(options.props)
151
+ props["block"] = {
152
+ "type": options.type,
153
+ "depth": depth,
154
+ "data": {},
155
+ }
156
+ props["blocks"] = self.blocks
157
+
158
+ wrapper_parent = DOM.create_element(options.element, props)
159
+ DOM.append_child(self.stack.head().elt, wrapper_parent)
160
+ else:
161
+ # Otherwise we can append at the end of the last child.
162
+ wrapper_parent = self.stack.head().last_child
163
+
164
+ DOM.append_child(wrapper_parent, new_wrapper.elt)
165
+
166
+ self.stack.append(new_wrapper)
167
+ else:
168
+ # Cut the stack to where it now stops, and add new wrapper.
169
+ self.stack.slice(depth)
170
+ self.stack.append(Wrapper(depth, options))
@@ -0,0 +1,376 @@
1
+ Metadata-Version: 2.3
2
+ Name: draftjs_exporter
3
+ Version: 5.2.0
4
+ Summary: Library to convert rich text from Draft.js raw ContentState to HTML
5
+ Author: Springload
6
+ Author-email: Springload <hello@springload.co.nz>
7
+ License: # The MIT License (MIT)
8
+
9
+ Copyright (c) 2016-current Springload
10
+
11
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
16
+ Classifier: Environment :: Web Environment
17
+ Classifier: Intended Audience :: Developers
18
+ Classifier: License :: OSI Approved :: MIT License
19
+ Classifier: Development Status :: 5 - Production/Stable
20
+ Classifier: Operating System :: OS Independent
21
+ Classifier: Programming Language :: Python
22
+ Classifier: Programming Language :: Python :: 3
23
+ Classifier: Programming Language :: Python :: 3.9
24
+ Classifier: Programming Language :: Python :: 3.10
25
+ Classifier: Programming Language :: Python :: 3.11
26
+ Classifier: Programming Language :: Python :: 3.12
27
+ Classifier: Programming Language :: Python :: 3.13
28
+ Classifier: Programming Language :: Python :: 3.14
29
+ Classifier: Programming Language :: Python :: 3.15
30
+ Classifier: Topic :: Internet :: WWW/HTTP
31
+ Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
32
+ Classifier: Topic :: Internet :: WWW/HTTP :: Site Management
33
+ Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
34
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
35
+ Classifier: Topic :: Text Editors :: Word Processors
36
+ Classifier: Typing :: Typed
37
+ Requires-Dist: beautifulsoup4>=4.4.1,<5 ; extra == 'html5lib'
38
+ Requires-Dist: html5lib>=0.999,<2 ; extra == 'html5lib'
39
+ Requires-Dist: lxml>=4.2.0,<7 ; extra == 'lxml'
40
+ Project-URL: Changelog, https://github.com/springload/draftjs_exporter/blob/main/CHANGELOG.md
41
+ Project-URL: Source, https://github.com/springload/draftjs_exporter
42
+ Project-URL: Tracker, https://github.com/springload/draftjs_exporter/issues
43
+ Provides-Extra: html5lib
44
+ Provides-Extra: lxml
45
+ Description-Content-Type: text/markdown
46
+
47
+ # [Draft.js exporter](https://pypi.org/project/draftjs_exporter/)
48
+
49
+ > Library to convert rich text from Draft.js raw ContentState to HTML.
50
+
51
+ It is developed alongside the [Draftail](https://www.draftail.org/) rich text editor, for [Wagtail](https://github.com/wagtail/wagtail). Check out the [online demo](http://playground.draftail.org/), and our [introductory blog post](https://www.draftail.org/blog/2018/03/13/rethinking-rich-text-pipelines-with-draft-js).
52
+
53
+ ## Why
54
+
55
+ [Draft.js](https://draftjs.org/) is a rich text editor framework for [React](https://reactjs.org/). Its approach is different from most rich text editors because it does not store data as HTML, but rather in its own representation called ContentState. This exporter is useful when the ContentState to HTML conversion has to be done in a Python ecosystem.
56
+
57
+ The initial use case was to gain more control over the content managed by rich text editors in a Wagtail/Django site. If you want to read the full story, have a look at our blog post: [Rethinking rich text pipelines with Draft.js](https://www.draftail.org/blog/2018/03/13/rethinking-rich-text-pipelines-with-draft-js).
58
+
59
+ ## Features
60
+
61
+ This project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html), and [measures performance](https://thib.me/python-memory-profiling-for-the-draft-js-exporter) and [code coverage](https://coveralls.io/github/springload/draftjs_exporter). Code is checked with [mypy](https://mypy.readthedocs.io/en/latest/index.html).
62
+
63
+ - Extensive configuration of the generated HTML.
64
+ - Default, extensible block & inline style maps for common HTML elements.
65
+ - Convert line breaks to `<br>` elements.
66
+ - Define any attribute in the block map – custom class names for elements.
67
+ - React-like API to create custom components.
68
+ - Automatic conversion of entity data to HTML attributes (int & boolean to string, style object to style string).
69
+ - Nested lists (`<li>` elements go inside `<ul>` or `<ol>`, with multiple levels).
70
+ - Output inline styles as inline elements (`<em>`, `<strong>`, pick and choose, with any attribute).
71
+ - Overlapping inline style and entity ranges.
72
+ - Static type annotations.
73
+
74
+ ## Usage
75
+
76
+ Draft.js stores data in a JSON representation based on blocks, representing lines of content in the editor, annotated with entities and styles to represent rich text. For more information, [this article](https://medium.com/@rajaraodv/how-draft-js-represents-rich-text-data-eeabb5f25cf2) covers the concepts further.
77
+
78
+ ### Getting started
79
+
80
+ This exporter takes the Draft.js ContentState data as input, and outputs HTML based on its configuration. To get started, install the package:
81
+
82
+ ```sh
83
+ pip install draftjs_exporter
84
+ ```
85
+
86
+ We support the following Python versions: 3.9, 3.10, 3.11, 3.12, 3.13, 3.14. For legacy Python versions, find compatible releases in the [CHANGELOG](https://github.com/springload/draftjs_exporter/blob/main/CHANGELOG.md).
87
+
88
+ In your code, create an exporter and use the `render` method to create HTML:
89
+
90
+ ```python
91
+ from draftjs_exporter.dom import DOM
92
+ from draftjs_exporter.html import HTML
93
+
94
+ # Configuration options are detailed below.
95
+ config = {}
96
+
97
+ # Initialize the exporter.
98
+ exporter = HTML(config)
99
+
100
+ # Render a Draft.js `contentState`
101
+ html = exporter.render({
102
+ 'entityMap': {},
103
+ 'blocks': [{
104
+ 'key': '6m5fh',
105
+ 'text': 'Hello, world!',
106
+ 'type': 'unstyled',
107
+ 'depth': 0,
108
+ 'inlineStyleRanges': [],
109
+ 'entityRanges': []
110
+ }]
111
+ })
112
+
113
+ print(html)
114
+ ```
115
+
116
+ You can also run an example by downloading this repository and then using `python example.py`, or by using our [online Draft.js demo](http://playground.draftail.org/).
117
+
118
+ ### Configuration
119
+
120
+ The exporter output is extensively configurable to cater for varied rich text requirements.
121
+
122
+ ```python
123
+ # draftjs_exporter provides default configurations and predefined constants for reuse.
124
+ from draftjs_exporter.constants import BLOCK_TYPES, ENTITY_TYPES
125
+ from draftjs_exporter.defaults import BLOCK_MAP, STYLE_MAP
126
+ from draftjs_exporter.dom import DOM
127
+
128
+ config = {
129
+ # `block_map` is a mapping from Draft.js block types to a definition of their HTML representation.
130
+ # Extend BLOCK_MAP to start with sane defaults, or make your own from scratch.
131
+ 'block_map': dict(BLOCK_MAP, **{
132
+ # The most basic mapping format, block type to tag name.
133
+ BLOCK_TYPES.HEADER_TWO: 'h2',
134
+ # Use a dict to define props on the block.
135
+ BLOCK_TYPES.HEADER_THREE: {'element': 'h3', 'props': {'class': 'u-text-center'}},
136
+ # Add a wrapper (and wrapper_props) to wrap adjacent blocks.
137
+ BLOCK_TYPES.UNORDERED_LIST_ITEM: {
138
+ 'element': 'li',
139
+ 'wrapper': 'ul',
140
+ 'wrapper_props': {'class': 'bullet-list'},
141
+ },
142
+ # Use a custom component for more flexibility (reading block data or depth).
143
+ BLOCK_TYPES.BLOCKQUOTE: blockquote,
144
+ BLOCK_TYPES.ORDERED_LIST_ITEM: {
145
+ 'element': list_item,
146
+ 'wrapper': ordered_list,
147
+ },
148
+ # Provide a fallback component (advanced).
149
+ BLOCK_TYPES.FALLBACK: block_fallback
150
+ }),
151
+ # `style_map` defines the HTML representation of inline elements.
152
+ # Extend STYLE_MAP to start with sane defaults, or make your own from scratch.
153
+ 'style_map': dict(STYLE_MAP, **{
154
+ # Use the same mapping format as in the `block_map`.
155
+ 'KBD': 'kbd',
156
+ # The `style` prop can be defined as a dict, that will automatically be converted to a string.
157
+ 'HIGHLIGHT': {'element': 'strong', 'props': {'style': {'textDecoration': 'underline'}}},
158
+ # Provide a fallback component (advanced).
159
+ INLINE_STYLES.FALLBACK: style_fallback,
160
+ }),
161
+ 'entity_decorators': {
162
+ # Map entities to components so they can be rendered with their data.
163
+ ENTITY_TYPES.IMAGE: image,
164
+ ENTITY_TYPES.LINK: link
165
+ # Lambdas work too.
166
+ ENTITY_TYPES.HORIZONTAL_RULE: lambda props: DOM.create_element('hr'),
167
+ # Discard those entities.
168
+ ENTITY_TYPES.EMBED: None,
169
+ # Provide a fallback component (advanced).
170
+ ENTITY_TYPES.FALLBACK: entity_fallback,
171
+ },
172
+ 'composite_decorators': [
173
+ # Use composite decorators to replace text based on a regular expression.
174
+ {
175
+ 'strategy': re.compile(r'\n'),
176
+ 'component': br,
177
+ },
178
+ {
179
+ 'strategy': re.compile(r'#\w+'),
180
+ 'component': hashtag,
181
+ },
182
+ {
183
+ 'strategy': LINKIFY_RE,
184
+ 'component': linkify,
185
+ },
186
+ ],
187
+ }
188
+ ```
189
+
190
+ See [examples.py](example.py) for more details.
191
+
192
+ ## Advanced usage
193
+
194
+ ### Custom components
195
+
196
+ To generate arbitrary markup with dynamic data, the exporter comes with an API to create rendering components. This API mirrors React’s [createElement](https://facebook.github.io/react/docs/top-level-api.html#react.createelement) API (what JSX compiles to).
197
+
198
+ ```python
199
+ # All of the API is available from a single `DOM` namespace
200
+ from draftjs_exporter.dom import DOM
201
+
202
+
203
+ # Components are simple functions that take `props` as parameter and return DOM elements.
204
+ def image(props):
205
+ # This component creates an image element, with the relevant attributes.
206
+ return DOM.create_element('img', {
207
+ 'src': props.get('src'),
208
+ 'width': props.get('width'),
209
+ 'height': props.get('height'),
210
+ 'alt': props.get('alt'),
211
+ })
212
+
213
+
214
+ def blockquote(props):
215
+ # This component uses block data to render a blockquote.
216
+ block_data = props['block']['data']
217
+
218
+ # Here, we want to display the block's content so we pass the `children` prop as the last parameter.
219
+ return DOM.create_element('blockquote', {
220
+ 'cite': block_data.get('cite')
221
+ }, props['children'])
222
+
223
+
224
+ def button(props):
225
+ href = props.get('href', '#')
226
+ icon_name = props.get('icon', None)
227
+ text = props.get('text', '')
228
+
229
+ return DOM.create_element('a', {
230
+ 'class': 'icon-text' if icon_name else None,
231
+ 'href': href,
232
+ },
233
+ # There can be as many `children` as required.
234
+ # It is also possible to reuse other components and render them instead of HTML tags.
235
+ DOM.create_element(icon, {'name': icon_name}) if icon_name else None,
236
+ DOM.create_element('span', {'class': 'icon-text'}, text) if icon_name else text
237
+ )
238
+ ```
239
+
240
+ Apart from `create_element`, a `parse_html` method is also available. Use it to interface with other HTML generators, like template engines – at your own risk: that method does not sanitize the input.
241
+
242
+ See `examples.py` in the repository for more details.
243
+
244
+ ### Fallback components
245
+
246
+ When dealing with changes in the content schema, as part of ongoing development or migrations, some content can go stale.
247
+ To solve this, the exporter allows the definition of fallback components for blocks, styles, and entities.
248
+ This feature is only used for development at the moment, if you have a use case for this in production we would love to hear from you.
249
+ Please get in touch!
250
+
251
+ Add the following to the exporter config,
252
+
253
+ ```python
254
+ config = {
255
+ 'block_map': dict(BLOCK_MAP, **{
256
+ # Provide a fallback for block types.
257
+ BLOCK_TYPES.FALLBACK: block_fallback
258
+ }),
259
+ }
260
+ ```
261
+
262
+ This fallback component can now control the exporter behavior when normal components are not found. Here is an example:
263
+
264
+ ```python
265
+ def block_fallback(props):
266
+ type_ = props['block']['type']
267
+
268
+ if type_ == 'example-discard':
269
+ logging.warning(f'Missing config for "{type_}". Discarding block, keeping content.')
270
+ # Directly return the block's children to keep its content.
271
+ return props['children']
272
+ elif type_ == 'example-delete':
273
+ logging.error(f'Missing config for "{type_}". Deleting block.')
274
+ # Return None to not render anything, removing the whole block.
275
+ return None
276
+ else:
277
+ logging.warning(f'Missing config for "{type_}". Using div instead.')
278
+ # Provide a fallback.
279
+ return DOM.create_element('div', {}, props['children'])
280
+ ```
281
+
282
+ See `examples.py` in the repository for more details.
283
+
284
+ ### Alternative backing engines
285
+
286
+ By default, the exporter uses a dependency-free engine called `string` to build the DOM tree. There are alternatives:
287
+
288
+ - `html5lib` (via BeautifulSoup)
289
+ - `lxml`.
290
+ - `string_compat` (A variant of `string` with no backwards-incompatible changes since its first release).
291
+
292
+ The `string` engine is the fastest, and does not have any dependencies. Its only drawback is that the `parse_html` method does not escape/sanitize HTML like that of other engines.
293
+
294
+ - For `html5lib`, do `pip install draftjs_exporter[html5lib]`.
295
+ - For `lxml`, do `pip install draftjs_exporter[lxml]`. It also requires `libxml2` and `libxslt` to be available on your system.
296
+ - There are no additional dependencies for `string_compat`.
297
+
298
+ Then, use the `engine` attribute of the exporter config:
299
+
300
+ ```python
301
+ config = {
302
+ # Specify which DOM backing engine to use.
303
+ 'engine': DOM.HTML5LIB,
304
+ # Or for lxml:
305
+ 'engine': DOM.LXML,
306
+ # Or to use the "maximum output stability" string_compat engine:
307
+ 'engine': DOM.STRING_COMPAT,
308
+ }
309
+ ```
310
+
311
+ To use the `lxml` or `html5lib` engines with arbitrary versions of those dependencies, simply install `draftjs_exporter` without the extras, and separately install the desired versions of the dependencies.
312
+
313
+ ### Custom backing engines
314
+
315
+ The exporter supports using custom engines to generate its output via the `DOM` API. This can be useful to implement custom export formats, e.g. [to Markdown (experimental)](https://github.com/thibaudcolas/draftjs_exporter_markdown).
316
+
317
+ Here is an example implementation:
318
+
319
+ ```python
320
+ from draftjs_exporter import DOMEngine
321
+
322
+ class DOMListTree(DOMEngine):
323
+ """
324
+ Element tree using nested lists.
325
+ """
326
+
327
+ @staticmethod
328
+ def create_tag(t, attr=None):
329
+ return [t, attr, []]
330
+
331
+ @staticmethod
332
+ def append_child(elt, child):
333
+ elt[2].append(child)
334
+
335
+ @staticmethod
336
+ def render(elt):
337
+ return elt
338
+
339
+
340
+ exporter = HTML({
341
+ # Use the dotted module syntax to point to the DOMEngine implementation.
342
+ 'engine': 'my_project.example.DOMListTree'
343
+ })
344
+ ```
345
+
346
+ ### Type annotations
347
+
348
+ The exporter’s codebase uses static type annotations, checked with mypy. Reusable types are made available:
349
+
350
+ ```python
351
+ from draftjs_exporter.dom import DOM
352
+ from draftjs_exporter.types import Element, Props
353
+
354
+
355
+ # Components are simple functions that take `props` as parameter and return DOM elements.
356
+ def image(props: Props) -> Element:
357
+ # This component creates an image element, with the relevant attributes.
358
+ return DOM.create_element('img', {
359
+ 'src': props.get('src'),
360
+ 'width': props.get('width'),
361
+ 'height': props.get('height'),
362
+ 'alt': props.get('alt'),
363
+ })
364
+ ```
365
+
366
+ ## Contributing
367
+
368
+ See anything you like in here? Anything missing? We welcome all support, whether on bug reports, feature requests, code, design, reviews, tests, documentation, and more. Please have a look at our [contribution guidelines](docs/CONTRIBUTING.md).
369
+
370
+ If you just want to set up the project on your own computer, the contribution guidelines also contain all of the setup commands.
371
+
372
+ ## Credits
373
+
374
+ This project is made possible by the work of [Springload](https://github.com/springload), a New Zealand digital agency. The _beautiful_ demo site is the work of [@thibaudcolas](https://github.com/thibaudcolas).
375
+
376
+ View the full list of [contributors](https://github.com/springload/draftjs_exporter/graphs/contributors). [MIT licensed](LICENSE).
@@ -0,0 +1,25 @@
1
+ draftjs_exporter/__init__.py,sha256=9aiFtigUoqawfhgRL3b4QxYRTFSNblKNSglkDnJ54zY,151
2
+ draftjs_exporter/command.py,sha256=2vGMKJJsCfSLSxQnZdw0D-4YemXQULcvIjh1ug9tZmQ,1870
3
+ draftjs_exporter/composite_decorators.py,sha256=-LBrX11e-cMtoxhpyLoId1tIZo-2y1BvvsgBvt_y_Z4,2316
4
+ draftjs_exporter/constants.py,sha256=ZyyiMSr_sSW0tx2JatPVK9cvXj0zn09bS7-HbK-6suE,1446
5
+ draftjs_exporter/defaults.py,sha256=_qg4jzN16JLU5k0i4ss6GhSN3YsJSI3dPS_yoUS-1no,1811
6
+ draftjs_exporter/dom.py,sha256=ayQKauPMhDRFyxmryji9gzAZFSLuU3v-RXlplevvaZY,4170
7
+ draftjs_exporter/engines/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
+ draftjs_exporter/engines/base.py,sha256=wsaXUlGwfjtx4eRcj0SLKAehqu2K2tbaWUj7XCiNlVQ,1352
9
+ draftjs_exporter/engines/html5lib.py,sha256=8BwxWjhQDQxH5Wg_52fOPGn_-vJk0o-euj2hlKaP7qU,1167
10
+ draftjs_exporter/engines/lxml.py,sha256=guWrLkLSw9sVwvOduLz9_3j9j3FKhS5EUkqxCAgOFcw,1354
11
+ draftjs_exporter/engines/string.py,sha256=rCMShBZ7_XyUuqT7WItzBwV37rU0DrRRXHQP0bB8b68,3372
12
+ draftjs_exporter/engines/string_compat.py,sha256=t7tRE4a87mPg4_vp62gLX-85q65dKusclSui4SeApD8,2129
13
+ draftjs_exporter/entity_state.py,sha256=HHCF1XEqjy35fZGQ79y13-_w9KDlL6nzsQkXtJ_WzxA,3436
14
+ draftjs_exporter/error.py,sha256=AoliTlMGITidJ7uzNyaE4VNU7xcBd0RIeKh8HRwXvDk,98
15
+ draftjs_exporter/html.py,sha256=QFUXoUnqL5HEHXmKa_WpSxymY886b7ymMspM0VzqR_s,6563
16
+ draftjs_exporter/options.py,sha256=djx4ZEiRUVkPyYKKGP6VR5nod7WvPGvh9Dtuo9VpfjU,3195
17
+ draftjs_exporter/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
18
+ draftjs_exporter/style_state.py,sha256=s3eVYQCR8yMuDOH0egjcAZQSGjAtvL6699wjoghyhQc,1709
19
+ draftjs_exporter/types.py,sha256=ycNWphOixDPqcYSoFPlbK3YEfUX_scn2sKbxbgwpm6U,1344
20
+ draftjs_exporter/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
21
+ draftjs_exporter/utils/module_loading.py,sha256=iqZsLMocK84rfxXZ5V5Rp8IbKH0fOGu9yGUGspZ5GDw,828
22
+ draftjs_exporter/wrapper_state.py,sha256=xL54P7KIJfNsdfM239e9B4Z5hnNe1DbN9L5NZxDlohQ,5782
23
+ draftjs_exporter-5.2.0.dist-info/WHEEL,sha256=RRVLqVugUmFOqBedBFAmA4bsgFcROUBiSUKlERi0Hcg,79
24
+ draftjs_exporter-5.2.0.dist-info/METADATA,sha256=8oSHYtj2jgG0f3KxsJ7XLUWa0r9sbyHImXWY8X4jHm4,16410
25
+ draftjs_exporter-5.2.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: uv 0.9.21
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any