or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

built-in-plugins.mdcore-formatting.mdindex.mdplugin-system.md

plugin-system.mddocs/

0

# Plugin System

1

2

Extensible plugin architecture for handling custom data types with both modern and legacy interfaces. Plugins allow pretty-format to serialize application-specific data structures with custom formatting logic.

3

4

## Capabilities

5

6

### Plugin Interface

7

8

Two plugin interfaces are supported: the modern `NewPlugin` interface (recommended) and the legacy `OldPlugin` interface for backwards compatibility.

9

10

```typescript { .api }

11

type Plugin = NewPlugin | OldPlugin;

12

13

interface NewPlugin {

14

/** Test function to determine if this plugin should handle the value */

15

test: (val: any) => boolean;

16

/** Serialize function using the improved interface (version 21+) */

17

serialize: (

18

val: any,

19

config: Config,

20

indentation: string,

21

depth: number,

22

refs: Refs,

23

printer: Printer

24

) => string;

25

}

26

27

interface OldPlugin {

28

/** Test function to determine if this plugin should handle the value */

29

test: (val: any) => boolean;

30

/** Print function using the original interface */

31

print: (

32

val: unknown,

33

print: (val: unknown) => string,

34

indent: (str: string) => string,

35

options: PluginOptions,

36

colors: Colors

37

) => string;

38

}

39

40

interface PluginOptions {

41

edgeSpacing: string;

42

min: boolean;

43

spacing: string;

44

}

45

```

46

47

### Test Function

48

49

The test function determines whether a plugin should handle a specific value.

50

51

**Guidelines for writing test functions:**

52

53

```typescript

54

// Example test functions

55

const reactElementTest = (val: any) =>

56

val && val.$$typeof === Symbol.for('react.element');

57

58

const domElementTest = (val: any) =>

59

val && typeof val.nodeType === 'number' && val.nodeType === 1;

60

61

// Safe property access patterns

62

const safeTest = (val: any) =>

63

val != null && typeof val.customProp === 'string';

64

65

// Efficient type checking

66

const arrayLikeTest = (val: any) =>

67

Array.isArray(val) && val.constructor.name === 'CustomArray';

68

```

69

70

**Usage Examples:**

71

72

```typescript

73

const customPlugin = {

74

test(val) {

75

// Safe null checking to prevent TypeErrors

76

return val != null &&

77

typeof val === 'object' &&

78

val.constructor.name === 'MyCustomClass';

79

},

80

serialize(val, config, indentation, depth, refs, printer) {

81

return `MyCustomClass { ${val.toString()} }`;

82

}

83

};

84

```

85

86

### Modern Plugin Interface (NewPlugin)

87

88

The recommended interface available in version 21 and later with full access to formatting context.

89

90

```typescript { .api }

91

interface NewPlugin {

92

test: (val: any) => boolean;

93

serialize: (

94

val: any,

95

config: Config,

96

indentation: string,

97

depth: number,

98

refs: Refs,

99

printer: Printer

100

) => string;

101

}

102

```

103

104

**Serialize Function Parameters:**

105

106

- `val`: The value that "passed the test"

107

- `config`: Unchanging config object derived from options

108

- `indentation`: Current indentation string (concatenate to config.indent)

109

- `depth`: Current depth number (compare to config.maxDepth)

110

- `refs`: Current refs array for finding circular references

111

- `printer`: Callback function to serialize children

112

113

**Usage Examples:**

114

115

```typescript

116

const advancedPlugin = {

117

test(val) {

118

return val && val.type === 'CustomCollection';

119

},

120

serialize(val, config, indentation, depth, refs, printer) {

121

// Check for circular references

122

if (refs.includes(val)) {

123

return '[Circular]';

124

}

125

126

// Check depth limit

127

if (++depth > config.maxDepth) {

128

return '[CustomCollection]';

129

}

130

131

// Add to refs for circular detection

132

const newRefs = [...refs, val];

133

134

// Format children using printer callback

135

const items = val.items

136

.slice(0, config.maxWidth)

137

.map(item =>

138

indentation + config.indent +

139

printer(item, config, indentation + config.indent, depth, newRefs)

140

)

141

.join(config.spacingInner);

142

143

return `CustomCollection {${config.spacingOuter}${items}${config.spacingOuter}${indentation}}`;

144

}

145

};

146

```

147

148

### Legacy Plugin Interface (OldPlugin)

149

150

The original interface for backwards compatibility with limited access to formatting context.

151

152

```typescript { .api }

153

interface OldPlugin {

154

test: (val: any) => boolean;

155

print: (

156

val: unknown,

157

print: (val: unknown) => string,

158

indent: (str: string) => string,

159

options: PluginOptions,

160

colors: Colors

161

) => string;

162

}

163

```

164

165

**Print Function Parameters:**

166

167

- `val`: The value that "passed the test"

168

- `print`: Current printer callback function to serialize children

169

- `indent`: Current indenter callback function to indent lines at next level

170

- `options`: Config object with `min`, `spacing`, and `edgeSpacing` properties

171

- `colors`: Colors object derived from options

172

173

**Usage Examples:**

174

175

```typescript

176

const legacyPlugin = {

177

test(val) {

178

return typeof val === 'function';

179

},

180

print(val, printer, indenter, options, colors) {

181

const name = val.name || 'anonymous';

182

const paramCount = val.length;

183

184

return `[Function ${name} ${paramCount}]`;

185

}

186

};

187

```

188

189

### Plugin Registration

190

191

Plugins are registered through the `plugins` option in the format function.

192

193

```typescript { .api }

194

interface OptionsReceived {

195

plugins?: Plugin[];

196

}

197

```

198

199

**Usage Examples:**

200

201

```typescript

202

import { format } from "pretty-format";

203

204

// Single plugin

205

const result = format(value, {

206

plugins: [myCustomPlugin]

207

});

208

209

// Multiple plugins (order matters - first matching plugin wins)

210

const formatted = format(value, {

211

plugins: [

212

specificPlugin, // More specific plugins first

213

generalPlugin, // General plugins last

214

fallbackPlugin

215

]

216

});

217

218

// With built-in plugins

219

import { plugins } from "pretty-format";

220

const { ReactElement, DOMElement } = plugins;

221

222

const formatted = format(reactComponent, {

223

plugins: [ReactElement, myCustomPlugin]

224

});

225

```

226

227

### Plugin Execution Flow

228

229

1. **Test Phase**: Each plugin's `test` function is called in order

230

2. **First Match**: The first plugin returning `true` is selected

231

3. **Serialization**: The selected plugin's `serialize` or `print` method is called

232

4. **Fallback**: If no plugin matches, built-in formatting is used

233

234

### Error Handling

235

236

Plugins can throw errors which are wrapped in `PrettyFormatPluginError`.

237

238

```typescript { .api }

239

class PrettyFormatPluginError extends Error {

240

constructor(message: string, stack: string);

241

}

242

```

243

244

**Error Scenarios:**

245

246

- Plugin test function throws an exception

247

- Plugin serialize/print function throws an exception

248

- Plugin returns non-string value from serialize/print

249

250

**Usage Examples:**

251

252

```typescript

253

const robustPlugin = {

254

test(val) {

255

try {

256

return val && val.customType === 'special';

257

} catch (error) {

258

// Test failures are caught and re-thrown as PrettyFormatPluginError

259

return false;

260

}

261

},

262

serialize(val, config, indentation, depth, refs, printer) {

263

try {

264

return `Special: ${val.value}`;

265

} catch (error) {

266

// Serialize failures become PrettyFormatPluginError

267

throw new Error(`Failed to serialize special value: ${error.message}`);

268

}

269

}

270

};

271

```

272

273

### Performance Considerations

274

275

- **Test Efficiency**: Keep test functions fast since they're called frequently

276

- **Early Returns**: Return `false` quickly for non-matching values

277

- **Minimal Computation**: Avoid heavy computation in test functions

278

- **Property Access**: Use safe property access patterns to prevent errors

279

280

**Usage Examples:**

281

282

```typescript

283

// Efficient test function

284

const efficientPlugin = {

285

test(val) {

286

// Quick type check first

287

if (typeof val !== 'object' || val === null) {

288

return false;

289

}

290

291

// Then check specific properties

292

return val.constructor.name === 'MyClass';

293

},

294

serialize(val, config, indentation, depth, refs, printer) {

295

// Heavy computation only happens for matching values

296

return formatMyClass(val, config);

297

}

298

};

299

```