or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

index.mdparser.mdserializer.mdspecifications.mdutilities.md

utilities.mddocs/

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

```