or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

cli.mdcore-conversion.mdevent-system.mdextension-system.mdflavor-management.mdglobal-configuration.mdindex.mdinstance-configuration.md

event-system.mddocs/

0

# Event System

1

2

Event listener system for hooking into the conversion process at various stages, enabling custom processing and monitoring.

3

4

## Capabilities

5

6

### listen

7

8

Adds an event listener to a Converter instance.

9

10

```javascript { .api }

11

/**

12

* Listen to an event

13

* @param name - Event name

14

* @param callback - Event callback function

15

* @returns Converter instance for chaining

16

*/

17

converter.listen(name: string, callback: EventCallback): showdown.Converter

18

```

19

20

Where `EventCallback` has the signature:

21

22

```javascript { .api }

23

type EventCallback = (

24

evtName: string,

25

text: string,

26

converter: showdown.Converter,

27

options: ConverterOptions,

28

globals: any

29

) => string | void

30

```

31

32

**Usage Examples:**

33

34

```javascript

35

const converter = new showdown.Converter();

36

37

// Listen to conversion events

38

converter.listen('conversion.start', function(evtName, text, converter, options, globals) {

39

console.log('Starting conversion of', text.length, 'characters');

40

});

41

42

converter.listen('conversion.end', function(evtName, html, converter, options, globals) {

43

console.log('Generated', html.length, 'characters of HTML');

44

});

45

46

// Listen and modify content

47

converter.listen('headers.before', function(evtName, text, converter, options, globals) {

48

// Add custom processing before header parsing

49

return text.replace(/^# (.+)/gm, '# πŸ“ $1');

50

});

51

52

// Chaining listeners

53

converter

54

.listen('event1', callback1)

55

.listen('event2', callback2);

56

```

57

58

## Event Types

59

60

Events are dispatched at various stages of the conversion process:

61

62

### Conversion Lifecycle Events

63

64

```javascript

65

// Conversion start/end

66

converter.listen('conversion.start', function(evtName, text) {

67

console.log('Conversion started');

68

});

69

70

converter.listen('conversion.end', function(evtName, html) {

71

console.log('Conversion completed');

72

});

73

```

74

75

### Parsing Stage Events

76

77

Events fired during different parsing stages:

78

79

```javascript

80

// Before/after specific parsing stages

81

converter.listen('headers.before', function(evtName, text) {

82

console.log('About to process headers');

83

return text; // Return modified text or undefined to keep original

84

});

85

86

converter.listen('headers.after', function(evtName, text) {

87

console.log('Headers processed');

88

});

89

90

converter.listen('links.before', function(evtName, text) {

91

console.log('About to process links');

92

});

93

94

converter.listen('codeBlocks.before', function(evtName, text) {

95

console.log('About to process code blocks');

96

});

97

```

98

99

## Event Callback Details

100

101

### Parameters

102

103

- **evtName**: The name of the event being fired

104

- **text**: The current text being processed (may be markdown or HTML)

105

- **converter**: The Converter instance firing the event

106

- **options**: Current converter options

107

- **globals**: Internal conversion state and utilities

108

109

### Return Values

110

111

Event callbacks can:

112

- Return `undefined` or nothing: Keep original text unchanged

113

- Return a string: Replace the text with the returned string

114

- Modify globals object: Affect internal conversion state

115

116

```javascript

117

converter.listen('custom.event', function(evtName, text, converter, options, globals) {

118

// Log event details

119

console.log('Event:', evtName);

120

console.log('Text length:', text.length);

121

console.log('Options:', options);

122

123

// Modify text

124

const modifiedText = text.replace(/\[CUSTOM\]/g, '<span class="custom">CUSTOM</span>');

125

126

// Return modified text

127

return modifiedText;

128

});

129

```

130

131

## Custom Event Dispatching

132

133

While primarily used internally, events can be manually dispatched:

134

135

```javascript

136

// Access internal dispatch method (not recommended for external use)

137

const result = converter._dispatch('custom.event', 'some text', options, globals);

138

```

139

140

## Event-Based Extensions

141

142

Events are commonly used in listener-type extensions:

143

144

```javascript

145

showdown.extension('analytics', {

146

type: 'listener',

147

listeners: {

148

'conversion.start': function(evtName, text, converter) {

149

analytics.track('markdown.conversion.start', {

150

textLength: text.length,

151

options: converter.getOptions()

152

});

153

},

154

155

'conversion.end': function(evtName, html, converter) {

156

analytics.track('markdown.conversion.end', {

157

htmlLength: html.length

158

});

159

},

160

161

'headers.before': function(evtName, text) {

162

// Count headers before processing

163

const headerCount = (text.match(/^#+\s/gm) || []).length;

164

analytics.track('markdown.headers.count', { count: headerCount });

165

}

166

}

167

});

168

169

// Use extension

170

const converter = new showdown.Converter({

171

extensions: ['analytics']

172

});

173

```

174

175

## Practical Use Cases

176

177

### Content Monitoring

178

179

```javascript

180

const converter = new showdown.Converter();

181

182

// Monitor conversion performance

183

const stats = { conversions: 0, totalTime: 0 };

184

185

converter.listen('conversion.start', function() {

186

this.startTime = Date.now();

187

});

188

189

converter.listen('conversion.end', function() {

190

const duration = Date.now() - this.startTime;

191

stats.conversions++;

192

stats.totalTime += duration;

193

console.log(`Conversion ${stats.conversions} took ${duration}ms`);

194

});

195

```

196

197

### Content Modification

198

199

```javascript

200

const converter = new showdown.Converter();

201

202

// Add custom processing

203

converter.listen('headers.before', function(evtName, text) {

204

// Add emoji to headers

205

return text.replace(/^(#+)\s*(.+)/gm, '$1 🎯 $2');

206

});

207

208

converter.listen('links.after', function(evtName, text) {

209

// Add target="_blank" to external links

210

return text.replace(

211

/<a href="(https?:\/\/[^"]+)"/g,

212

'<a href="$1" target="_blank" rel="noopener"'

213

);

214

});

215

```

216

217

### Debugging and Development

218

219

```javascript

220

const converter = new showdown.Converter();

221

222

// Debug event system

223

const debugEvents = ['conversion.start', 'conversion.end', 'headers.before', 'links.before'];

224

225

debugEvents.forEach(eventName => {

226

converter.listen(eventName, function(evtName, text) {

227

console.log(`[DEBUG] Event: ${evtName}, Text length: ${text.length}`);

228

if (text.length < 100) {

229

console.log(`[DEBUG] Text: ${text}`);

230

}

231

});

232

});

233

```

234

235

### Content Validation

236

237

```javascript

238

const converter = new showdown.Converter();

239

240

// Validate content during conversion

241

converter.listen('conversion.start', function(evtName, text) {

242

if (text.length > 100000) {

243

console.warn('Large document detected:', text.length, 'characters');

244

}

245

246

const linkCount = (text.match(/\[([^\]]+)\]\([^)]+\)/g) || []).length;

247

if (linkCount > 50) {

248

console.warn('Document contains many links:', linkCount);

249

}

250

});

251

```

252

253

## Error Handling in Event Callbacks

254

255

```javascript

256

converter.listen('custom.event', function(evtName, text) {

257

try {

258

// Potentially risky operation

259

return processText(text);

260

} catch (error) {

261

console.error('Event callback error:', error);

262

// Return original text on error

263

return text;

264

}

265

});

266

```

267

268

## Multiple Listeners

269

270

Multiple listeners can be registered for the same event:

271

272

```javascript

273

const converter = new showdown.Converter();

274

275

// First listener

276

converter.listen('conversion.start', function() {

277

console.log('Listener 1: Conversion starting');

278

});

279

280

// Second listener

281

converter.listen('conversion.start', function() {

282

console.log('Listener 2: Conversion starting');

283

});

284

285

// Both will be called in registration order

286

```