0
# Utility System
1
2
The utility system provides base classes, type definitions, and utility functions for stack management, type safety, and integration with the remark and ProseMirror ecosystems.
3
4
## Capabilities
5
6
### Stack Management
7
8
Generic stack implementation for managing nested transformation contexts during parsing and serialization.
9
10
```typescript { .api }
11
/**
12
* The stack that is used to store the elements.
13
* Generally, you don't need to use this class directly.
14
*
15
* When using the stack, users can call stack.open to push a new element into the stack.
16
* And use stack.push to push a node into the top element.
17
* Then use stack.close to close the top element and pop it.
18
*
19
* For example: stack.open(A).push(B).push(C).close() will generate a structure like A(B, C).
20
*/
21
class Stack<Node, Element extends StackElement<Node>> {
22
protected elements: Element[];
23
24
/**
25
* Get the size of the stack.
26
* @returns Number of elements in the stack
27
*/
28
size(): number;
29
30
/**
31
* Get the top element of the stack.
32
* @returns Top element or undefined if stack is empty
33
*/
34
top(): Element | undefined;
35
36
/**
37
* Push a node into the top element.
38
* @param node - Node to push into top element
39
*/
40
push(node: Node): void;
41
42
/**
43
* Push a new element.
44
* @param node - Element to push onto stack
45
*/
46
open(node: Element): void;
47
48
/**
49
* Close the top element and pop it.
50
* @returns The popped element
51
* @throws Error if stack is empty
52
*/
53
close(): Element;
54
}
55
56
/**
57
* The element of the stack, which holds an array of nodes.
58
*/
59
abstract class StackElement<Node> {
60
/**
61
* A method that can push a node into the element.
62
* @param node - Node to push
63
* @param rest - Additional nodes to push
64
*/
65
abstract push(node: Node, ...rest: Node[]): void;
66
}
67
```
68
69
**Usage Examples:**
70
71
```typescript
72
import { Stack, StackElement } from "@milkdown/transformer";
73
74
// Custom stack element implementation
75
class MyStackElement extends StackElement<string> {
76
private items: string[] = [];
77
78
push(node: string, ...rest: string[]): void {
79
this.items.push(node, ...rest);
80
}
81
82
getItems(): string[] {
83
return this.items;
84
}
85
}
86
87
// Using the stack
88
const stack = new Stack<string, MyStackElement>();
89
const element = new MyStackElement();
90
91
stack.open(element);
92
stack.push("item1");
93
stack.push("item2");
94
const closedElement = stack.close();
95
96
console.log(closedElement.getItems()); // ["item1", "item2"]
97
```
98
99
### Type Definitions
100
101
Core type definitions for markdown AST, JSON data, and remark integration.
102
103
```typescript { .api }
104
/**
105
* The universal type of a node in mdast.
106
*/
107
type MarkdownNode = Node & {
108
children?: MarkdownNode[];
109
[x: string]: unknown;
110
};
111
112
/**
113
* JSON value type for representing arbitrary JSON data.
114
*/
115
type JSONValue =
116
| string
117
| number
118
| boolean
119
| null
120
| JSONValue[]
121
| { [key: string]: JSONValue };
122
123
/**
124
* JSON record type for object properties.
125
*/
126
type JSONRecord = Record<string, JSONValue>;
127
128
/**
129
* The type of remark instance.
130
*/
131
type RemarkParser = ReturnType<typeof remark>;
132
133
/**
134
* Node type for remark transformer parameters.
135
*/
136
type Node = Parameters<Transformer>[0];
137
138
/**
139
* Root type for remark stringify parameters.
140
*/
141
type Root = Parameters<(typeof remark)['stringify']>[0];
142
```
143
144
### Plugin System Types
145
146
Types for extending functionality through remark plugins.
147
148
```typescript { .api }
149
/**
150
* Raw plugin type for remark integration.
151
*/
152
type RemarkPluginRaw<T> = Plugin<[T], Root>;
153
154
/**
155
* The universal type of a remark plugin.
156
*/
157
interface RemarkPlugin<T = Record<string, unknown>> {
158
plugin: Plugin<[T], Root>;
159
options: T;
160
}
161
```
162
163
**Plugin Usage Examples:**
164
165
```typescript
166
import { RemarkPlugin, RemarkParser } from "@milkdown/transformer";
167
import { remark } from "remark";
168
169
// Define a custom plugin
170
const myPlugin: RemarkPlugin<{ prefix: string }> = {
171
plugin: (options) => (tree, file) => {
172
// Transform tree based on options
173
if (options.prefix) {
174
// Add prefix to all text nodes
175
}
176
},
177
options: { prefix: ">> " }
178
};
179
180
// Use plugin with remark
181
const remarkInstance: RemarkParser = remark()
182
.use(myPlugin.plugin, myPlugin.options);
183
184
// Plugin with type safety
185
const typedPlugin: RemarkPlugin<{
186
enableFeature: boolean;
187
maxDepth: number;
188
}> = {
189
plugin: ({ enableFeature, maxDepth }) => (tree) => {
190
if (enableFeature) {
191
// Process tree with maxDepth limit
192
}
193
},
194
options: {
195
enableFeature: true,
196
maxDepth: 5
197
}
198
};
199
```
200
201
### Utility Functions and Constants
202
203
Helper utilities for working with markdown and ProseMirror data structures.
204
205
```typescript { .api }
206
/**
207
* Type guard to check if a node has text content.
208
*/
209
function hasText(node: Node): node is Node & { text: string };
210
211
/**
212
* Type guard to check if an object is a ProseMirror Fragment.
213
*/
214
function isFragment(x: Node | Fragment): x is Fragment;
215
```
216
217
**Advanced Type Usage Examples:**
218
219
```typescript
220
import { MarkdownNode, JSONRecord } from "@milkdown/transformer";
221
222
// Working with markdown nodes
223
function processMarkdownNode(node: MarkdownNode): void {
224
console.log(`Processing ${node.type}`);
225
226
if (node.children) {
227
node.children.forEach(child => {
228
processMarkdownNode(child);
229
});
230
}
231
232
// Access arbitrary properties safely
233
if (node.depth && typeof node.depth === 'number') {
234
console.log(`Heading depth: ${node.depth}`);
235
}
236
}
237
238
// Working with JSON records
239
function createNodeProps(data: any): JSONRecord {
240
const props: JSONRecord = {};
241
242
if (typeof data.id === 'string') props.id = data.id;
243
if (typeof data.level === 'number') props.level = data.level;
244
if (Array.isArray(data.classes)) props.classes = data.classes;
245
246
return props;
247
}
248
249
// Type-safe node creation
250
interface CustomMarkdownNode extends MarkdownNode {
251
type: 'custom';
252
customProp: string;
253
}
254
255
function isCustomNode(node: MarkdownNode): node is CustomMarkdownNode {
256
return node.type === 'custom' && typeof node.customProp === 'string';
257
}
258
```
259
260
### Error Handling Utilities
261
262
While not directly exported, the utility system integrates with @milkdown/exception for consistent error handling.
263
264
```typescript { .api }
265
// Error types that may be thrown during transformation
266
interface TransformationError extends Error {
267
name: 'TransformationError';
268
node?: MarkdownNode | Node;
269
context?: string;
270
}
271
272
interface ParserMatchError extends Error {
273
name: 'ParserMatchError';
274
node: MarkdownNode;
275
}
276
277
interface SerializerMatchError extends Error {
278
name: 'SerializerMatchError';
279
nodeType: NodeType | MarkType;
280
}
281
```
282
283
**Error Handling Examples:**
284
285
```typescript
286
import { ParserState } from "@milkdown/transformer";
287
288
try {
289
const parser = ParserState.create(schema, remark());
290
const doc = parser(markdownText);
291
} catch (error) {
292
if (error.name === 'ParserMatchError') {
293
console.error(`No parser found for node type: ${error.node.type}`);
294
} else if (error.name === 'SerializerMatchError') {
295
console.error(`No serializer found for node type: ${error.nodeType.name}`);
296
} else {
297
console.error('Unexpected transformation error:', error);
298
}
299
}
300
```