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

extension-system.mddocs/

0

# Extension System

1

2

Plugin architecture for adding custom parsing behavior and output modifications to Showdown.

3

4

## Capabilities

5

6

### extension

7

8

Registers or retrieves extensions globally.

9

10

```javascript { .api }

11

/**

12

* Gets or registers an extension

13

* @param name - Extension name

14

* @param ext - Extension definition, array of extensions, or factory function

15

* @returns Extension array when getting, void when setting

16

*/

17

showdown.extension(name: string, ext?: Extension | Extension[] | Function): Extension[] | void

18

```

19

20

**Usage Examples:**

21

22

```javascript

23

// Register a simple extension

24

showdown.extension('highlight', {

25

type: 'output',

26

regex: /<pre><code\s+class="([^"]+)">([\s\S]*?)<\/code><\/pre>/g,

27

replace: '<pre class="$1"><code>$2</code></pre>'

28

});

29

30

// Register extension from function

31

showdown.extension('custom', function() {

32

return {

33

type: 'lang',

34

filter: function(text) {

35

return text.replace(/\[TODO\]/g, '<span class="todo">TODO</span>');

36

}

37

};

38

});

39

40

// Get registered extension

41

const highlightExt = showdown.extension('highlight');

42

console.log(highlightExt);

43

```

44

45

### getAllExtensions

46

47

Gets all registered extensions.

48

49

```javascript { .api }

50

/**

51

* Gets all extensions registered

52

* @returns Object with extension names as keys and extension arrays as values

53

*/

54

showdown.getAllExtensions(): { [key: string]: Extension[] }

55

```

56

57

**Usage Examples:**

58

59

```javascript

60

// Get all registered extensions

61

const allExtensions = showdown.getAllExtensions();

62

console.log('Available extensions:', Object.keys(allExtensions));

63

64

// Check if specific extension exists

65

const extensions = showdown.getAllExtensions();

66

if ('highlight' in extensions) {

67

console.log('Highlight extension is available');

68

}

69

```

70

71

### removeExtension

72

73

Removes a registered extension.

74

75

```javascript { .api }

76

/**

77

* Remove an extension

78

* @param name - Extension name to remove

79

*/

80

showdown.removeExtension(name: string): void

81

```

82

83

**Usage Examples:**

84

85

```javascript

86

// Register an extension

87

showdown.extension('temp', {

88

type: 'lang',

89

filter: text => text

90

});

91

92

// Remove it

93

showdown.removeExtension('temp');

94

95

// Verify removal

96

const extensions = showdown.getAllExtensions();

97

console.log('temp' in extensions); // false

98

```

99

100

### resetExtensions

101

102

Removes all registered extensions.

103

104

```javascript { .api }

105

/**

106

* Removes all extensions

107

*/

108

showdown.resetExtensions(): void

109

```

110

111

**Usage Examples:**

112

113

```javascript

114

// Register several extensions

115

showdown.extension('ext1', { type: 'lang', filter: text => text });

116

showdown.extension('ext2', { type: 'output', filter: text => text });

117

118

// Clear all extensions

119

showdown.resetExtensions();

120

121

// Verify cleanup

122

const extensions = showdown.getAllExtensions();

123

console.log(Object.keys(extensions).length); // 0

124

```

125

126

### validateExtension

127

128

Validates extension format and structure.

129

130

```javascript { .api }

131

/**

132

* Validate extension format

133

* @param ext - Extension object to validate

134

* @returns True if valid, false otherwise

135

*/

136

showdown.validateExtension(ext: Extension): boolean

137

```

138

139

**Usage Examples:**

140

141

```javascript

142

// Valid extension

143

const validExt = {

144

type: 'lang',

145

filter: function(text) { return text; }

146

};

147

console.log(showdown.validateExtension(validExt)); // true

148

149

// Invalid extension (missing filter/regex)

150

const invalidExt = {

151

type: 'lang'

152

};

153

console.log(showdown.validateExtension(invalidExt)); // false

154

```

155

156

## Extension Types

157

158

### Language Extensions ('lang')

159

160

Modify the Markdown parsing process before HTML generation.

161

162

```javascript { .api }

163

interface LanguageExtension {

164

type: 'lang';

165

filter?: (text: string, converter: showdown.Converter, options: ConverterOptions) => string;

166

regex?: RegExp | string;

167

replace?: string | Function;

168

}

169

```

170

171

**Usage Examples:**

172

173

```javascript

174

// Filter-based language extension

175

showdown.extension('alerts', {

176

type: 'lang',

177

filter: function(text) {

178

return text.replace(/\[!(INFO|WARNING|ERROR)\]\s*(.+)/g, function(match, type, content) {

179

return `<div class="alert alert-${type.toLowerCase()}">${content}</div>`;

180

});

181

}

182

});

183

184

// Regex-based language extension

185

showdown.extension('mentions', {

186

type: 'lang',

187

regex: /@([a-zA-Z0-9_]+)/g,

188

replace: '<a href="/users/$1">@$1</a>'

189

});

190

```

191

192

### Output Extensions ('output')

193

194

Modify the HTML output after Markdown processing.

195

196

```javascript { .api }

197

interface OutputExtension {

198

type: 'output';

199

filter?: (html: string, converter: showdown.Converter, options: ConverterOptions) => string;

200

regex?: RegExp | string;

201

replace?: string | Function;

202

}

203

```

204

205

**Usage Examples:**

206

207

```javascript

208

// Add syntax highlighting classes

209

showdown.extension('syntax-highlight', {

210

type: 'output',

211

regex: /<pre><code\s+class="language-([^"]+)">/g,

212

replace: '<pre><code class="language-$1 hljs">'

213

});

214

215

// Process all links

216

showdown.extension('external-links', {

217

type: 'output',

218

filter: function(html) {

219

return html.replace(/<a href="([^"]*)">/g, function(match, url) {

220

if (url.startsWith('http')) {

221

return `<a href="${url}" target="_blank" rel="noopener">`;

222

}

223

return match;

224

});

225

}

226

});

227

```

228

229

### Listener Extensions ('listener')

230

231

Event-based extensions that respond to conversion events.

232

233

```javascript { .api }

234

interface ListenerExtension {

235

type: 'listener';

236

listeners: {

237

[eventName: string]: (

238

evtName: string,

239

text: string,

240

converter: showdown.Converter,

241

options: ConverterOptions,

242

globals: any

243

) => string | void;

244

};

245

}

246

```

247

248

**Usage Examples:**

249

250

```javascript

251

showdown.extension('stats', {

252

type: 'listener',

253

listeners: {

254

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

255

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

256

},

257

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

258

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

259

}

260

}

261

});

262

```

263

264

## Extension Loading Patterns

265

266

### File-based Extensions

267

268

```javascript

269

// Load extension from file (Node.js)

270

const myExtension = require('./my-extension');

271

showdown.extension('my-ext', myExtension);

272

273

// Use in converter

274

const converter = new showdown.Converter({

275

extensions: ['my-ext']

276

});

277

```

278

279

### Inline Extensions

280

281

```javascript

282

// Define and register inline

283

showdown.extension('custom', {

284

type: 'lang',

285

regex: /\[\[([^\]]+)\]\]/g,

286

replace: '<span class="wiki-link">$1</span>'

287

});

288

```

289

290

### Factory Function Extensions

291

292

```javascript

293

// Extension factory

294

showdown.extension('configurable', function(options) {

295

options = options || {};

296

const prefix = options.prefix || 'default';

297

298

return {

299

type: 'lang',

300

filter: function(text) {

301

return text.replace(/\[CUSTOM\]/g, `[${prefix}]`);

302

}

303

};

304

});

305

```

306

307

## Using Extensions

308

309

### Global Registration

310

311

```javascript

312

// Register globally

313

showdown.extension('highlight', highlightExtension);

314

315

// All converters can use it

316

const converter1 = new showdown.Converter({ extensions: ['highlight'] });

317

const converter2 = new showdown.Converter({ extensions: ['highlight'] });

318

```

319

320

### Instance-specific Loading

321

322

```javascript

323

// Load extension only for specific converter

324

const converter = new showdown.Converter();

325

converter.addExtension(highlightExtension, 'highlight');

326

```

327

328

### Multiple Extensions

329

330

```javascript

331

// Load multiple extensions

332

const converter = new showdown.Converter({

333

extensions: ['highlight', 'mentions', 'alerts']

334

});

335

336

// Extension order matters - earlier extensions process first

337

```

338

339

## Extension Development

340

341

### Best Practices

342

343

```javascript

344

// Good: Defensive programming

345

showdown.extension('safe-ext', {

346

type: 'lang',

347

filter: function(text, converter, options) {

348

if (!text || typeof text !== 'string') {

349

return text;

350

}

351

352

// Process text safely

353

try {

354

return text.replace(/pattern/g, 'replacement');

355

} catch (error) {

356

console.error('Extension error:', error);

357

return text;

358

}

359

}

360

});

361

362

// Good: Respect options

363

showdown.extension('conditional', {

364

type: 'lang',

365

filter: function(text, converter, options) {

366

if (!options.customFeature) {

367

return text;

368

}

369

return processText(text);

370

}

371

});

372

```

373

374

### Error Handling

375

376

```javascript

377

// Extension with error handling

378

showdown.extension('robust', {

379

type: 'lang',

380

filter: function(text) {

381

try {

382

return complexProcessing(text);

383

} catch (error) {

384

console.warn('Extension failed, returning original text:', error);

385

return text;

386

}

387

}

388

});

389

```

390

391

## SubParser System

392

393

### subParser

394

395

Advanced method for registering or retrieving individual parsing components.

396

397

```javascript { .api }

398

/**

399

* Get or set a subParser

400

* @param name - SubParser name

401

* @param func - SubParser function (optional, for registration)

402

* @returns SubParser function when getting, void when setting

403

*/

404

showdown.subParser(name: string, func?: Function): Function | void

405

```

406

407

**Usage Examples:**

408

409

```javascript

410

// Register a custom subParser

411

showdown.subParser('customBlock', function(text, options, globals) {

412

return text.replace(/\[CUSTOM\]/g, '<div class="custom">Custom Block</div>');

413

});

414

415

// Get existing subParser

416

const headerParser = showdown.subParser('headers');

417

console.log(typeof headerParser); // 'function'

418

419

// Use in extension

420

showdown.extension('enhanced', {

421

type: 'lang',

422

filter: function(text, converter, options) {

423

// Use registered subParser

424

const customParser = showdown.subParser('customBlock');

425

return customParser(text, options, {});

426

}

427

});

428

```

429

430

**Note:** SubParsers are internal parsing components. Modifying them can affect core functionality and should be done with caution.