0
# Serializer System
1
2
The serializer system provides state machine functionality for converting ProseMirror document structures into markdown AST. It offers a fluent API for building complex document serialization with extensible node and mark processing.
3
4
## Capabilities
5
6
### SerializerState Class
7
8
The main state machine class for serializing ProseMirror nodes to markdown AST.
9
10
```typescript { .api }
11
/**
12
* State for serializer. Transform prosemirror state into remark AST.
13
*/
14
class SerializerState extends Stack<MarkdownNode, SerializerStackElement> {
15
readonly schema: Schema;
16
17
/**
18
* Create a serializer from schema and remark instance.
19
* @param schema - ProseMirror schema defining available nodes and marks
20
* @param remark - Remark parser instance for processing markdown
21
* @returns Serializer function that converts ProseMirror nodes to markdown text
22
*/
23
static create(schema: Schema, remark: RemarkParser): Serializer;
24
25
/**
26
* Open a new node, the next operations will add nodes into that new node until closeNode is called.
27
* @param type - Markdown AST node type to open
28
* @param value - Optional text value for the node
29
* @param props - Optional additional properties for the node
30
* @returns SerializerState instance for chaining
31
*/
32
openNode(type: string, value?: string, props?: JSONRecord): SerializerState;
33
34
/**
35
* Close the current node and push it into the parent node.
36
* @returns SerializerState instance for chaining
37
*/
38
closeNode(): SerializerState;
39
40
/**
41
* Add a node into current node.
42
* @param type - Markdown AST node type to add
43
* @param children - Optional child nodes
44
* @param value - Optional text value for the node
45
* @param props - Optional additional properties for the node
46
* @returns SerializerState instance for chaining
47
*/
48
addNode(type: string, children?: MarkdownNode[], value?: string, props?: JSONRecord): SerializerState;
49
50
/**
51
* Open a new mark, the next nodes added will have that mark.
52
* The mark will be closed automatically.
53
* @param mark - ProseMirror mark instance
54
* @param type - Markdown AST node type for the mark
55
* @param value - Optional text value for the mark
56
* @param props - Optional additional properties for the mark
57
* @returns SerializerState instance for chaining
58
*/
59
withMark(mark: Mark, type: string, value?: string, props?: JSONRecord): SerializerState;
60
61
/**
62
* Close a opened mark.
63
* In most cases you don't need this because marks will be closed automatically.
64
* @param mark - ProseMirror mark instance to close
65
* @returns SerializerState instance for chaining
66
*/
67
closeMark(mark: Mark): SerializerState;
68
69
/**
70
* Give the node or node list back to the state and the state will find a proper runner to handle it.
71
* @param nodes - ProseMirror nodes to process (single node or fragment)
72
* @returns SerializerState instance for chaining
73
*/
74
next(nodes: Node | Fragment): SerializerState;
75
76
/**
77
* Use a remark parser to serialize current AST stored.
78
* @param remark - Remark parser instance
79
* @returns Serialized markdown text
80
*/
81
toString(remark: RemarkParser): string;
82
83
/**
84
* Transform a prosemirror node tree into remark AST.
85
* @param tree - ProseMirror node tree to transform
86
* @returns SerializerState instance for chaining
87
*/
88
run(tree: Node): SerializerState;
89
}
90
```
91
92
**Usage Examples:**
93
94
```typescript
95
import { SerializerState } from "@milkdown/transformer";
96
import { remark } from "remark";
97
import { schema } from "@milkdown/prose/model";
98
99
// Create a serializer
100
const serializer = SerializerState.create(schema, remark());
101
102
// Serialize ProseMirror to markdown
103
const markdown = serializer(prosemirrorDoc);
104
105
// Manual state manipulation
106
const state = new SerializerState(schema);
107
state.openNode('root')
108
.openNode('heading', undefined, { depth: 1 })
109
.addNode('text', undefined, 'Hello World')
110
.closeNode()
111
.openNode('paragraph')
112
.openNode('strong')
113
.addNode('text', undefined, 'bold')
114
.closeNode()
115
.addNode('text', undefined, ' text')
116
.closeNode()
117
.closeNode();
118
119
const markdown = state.toString(remark());
120
```
121
122
### SerializerStackElement Class
123
124
Stack element implementation for serializer state management.
125
126
```typescript { .api }
127
/**
128
* Stack element for serializer state, holds markdown AST nodes during transformation.
129
*/
130
class SerializerStackElement extends StackElement<MarkdownNode> {
131
type: string;
132
children?: MarkdownNode[];
133
value?: string;
134
props: JSONRecord;
135
136
/**
137
* Create a new serializer stack element.
138
* @param type - Markdown AST node type
139
* @param children - Optional child nodes
140
* @param value - Optional text value
141
* @param props - Optional additional properties
142
* @returns New SerializerStackElement instance
143
*/
144
static create(type: string, children?: MarkdownNode[], value?: string, props?: JSONRecord): SerializerStackElement;
145
146
/**
147
* Add nodes to the element's children.
148
* @param node - Node to add
149
* @param rest - Additional nodes to add
150
*/
151
push(node: MarkdownNode, ...rest: MarkdownNode[]): void;
152
153
/**
154
* Remove and return the last node from children.
155
* @returns Last node or undefined if empty
156
*/
157
pop(): MarkdownNode | undefined;
158
}
159
```
160
161
### Serializer Specification Types
162
163
Types for defining custom serialization behavior.
164
165
```typescript { .api }
166
/**
167
* The serializer type which is used to transform prosemirror node into markdown text.
168
*/
169
type Serializer = (content: Node) => string;
170
171
/**
172
* The spec for node serializer in schema.
173
*/
174
interface NodeSerializerSpec {
175
/**
176
* The match function to check if the node is the target node.
177
* @param node - ProseMirror node to check
178
* @returns true if this spec should handle the node
179
*/
180
match: (node: Node) => boolean;
181
182
/**
183
* The runner function to transform the node into markdown text.
184
* Generally, you should call methods in state to add node to state.
185
* @param state - Serializer state for adding nodes
186
* @param node - ProseMirror node to transform
187
*/
188
runner: (state: SerializerState, node: Node) => void;
189
}
190
191
/**
192
* The spec for mark serializer in schema.
193
*/
194
interface MarkSerializerSpec {
195
/**
196
* The match function to check if the mark is the target mark.
197
* @param mark - ProseMirror mark to check
198
* @returns true if this spec should handle the mark
199
*/
200
match: (mark: Mark) => boolean;
201
202
/**
203
* The runner function to transform the mark into markdown text.
204
* Generally, you should call methods in state to add mark to state.
205
* @param state - Serializer state for adding marks
206
* @param mark - ProseMirror mark to transform
207
* @param node - Associated ProseMirror node
208
* @returns Optional boolean to prevent further processing
209
*/
210
runner: (state: SerializerState, mark: Mark, node: Node) => void | boolean;
211
}
212
```
213
214
**Specification Examples:**
215
216
```typescript
217
// Example node serializer spec for paragraphs
218
const paragraphSerializerSpec: NodeSerializerSpec = {
219
match: (node) => node.type.name === 'paragraph',
220
runner: (state, node) => {
221
state
222
.openNode('paragraph')
223
.next(node.content)
224
.closeNode();
225
}
226
};
227
228
// Example mark serializer spec for emphasis
229
const emphasisSerializerSpec: MarkSerializerSpec = {
230
match: (mark) => mark.type.name === 'emphasis',
231
runner: (state, mark, node) => {
232
state.withMark(mark, 'emphasis');
233
}
234
};
235
236
// Example node serializer spec for headings
237
const headingSerializerSpec: NodeSerializerSpec = {
238
match: (node) => node.type.name === 'heading',
239
runner: (state, node) => {
240
state
241
.openNode('heading', undefined, { depth: node.attrs.level })
242
.next(node.content)
243
.closeNode();
244
}
245
};
246
```