Syntax highlighting extension for the marked markdown parser
npx @tessl/cli install tessl/npm-marked-highlight@2.2.00
# marked-highlight
1
2
marked-highlight is a syntax highlighting extension for the marked markdown parser. It provides a flexible API that accepts custom highlight functions (synchronous or asynchronous) and integrates seamlessly with popular syntax highlighting libraries like highlight.js and pygmentize.
3
4
## Package Information
5
6
- **Package Name**: marked-highlight
7
- **Package Type**: npm
8
- **Language**: JavaScript with TypeScript definitions
9
- **Installation**: `npm install marked-highlight`
10
11
## Core Imports
12
13
```javascript
14
import { markedHighlight } from "marked-highlight";
15
```
16
17
For CommonJS:
18
19
```javascript
20
const { markedHighlight } = require("marked-highlight");
21
```
22
23
UMD (browser):
24
25
```html
26
<script src="https://cdn.jsdelivr.net/npm/marked-highlight/lib/index.umd.js"></script>
27
<script>
28
const { markedHighlight } = globalThis.markedHighlight;
29
</script>
30
```
31
32
## Architecture
33
34
marked-highlight integrates with the marked parser using the extension system:
35
36
- **Extension Integration**: The `markedHighlight` function returns a `MarkedExtension` object that plugs into marked's parsing pipeline
37
- **Token Processing**: Uses `walkTokens` to identify and process code block tokens during parsing
38
- **Renderer Override**: Provides a custom `code` renderer that applies CSS classes and processes highlighted content
39
- **Async Support**: Handles both synchronous and asynchronous highlighting functions through Promise-based processing
40
- **Lazy Processing**: Code highlighting occurs during the rendering phase, allowing for efficient token processing
41
42
This architecture allows marked-highlight to integrate seamlessly with any marked instance while maintaining compatibility with other extensions.
43
44
## Basic Usage
45
46
### With highlight.js (synchronous)
47
48
```javascript
49
import { Marked } from "marked";
50
import { markedHighlight } from "marked-highlight";
51
import hljs from 'highlight.js';
52
53
const marked = new Marked(
54
markedHighlight({
55
emptyLangClass: 'hljs',
56
langPrefix: 'hljs language-',
57
highlight(code, lang, info) {
58
const language = hljs.getLanguage(lang) ? lang : 'plaintext';
59
return hljs.highlight(code, { language }).value;
60
}
61
})
62
);
63
64
const html = marked.parse(`
65
\`\`\`javascript
66
const highlight = "code";
67
\`\`\`
68
`);
69
// Output: <pre><code class="hljs language-javascript">
70
// <span class="hljs-keyword">const</span> highlight = <span class="hljs-string">"code"</span>;
71
// </code></pre>
72
```
73
74
### With pygmentize (asynchronous)
75
76
```javascript
77
import { Marked } from "marked";
78
import { markedHighlight } from "marked-highlight";
79
import pygmentize from 'pygmentize-bundled';
80
81
const marked = new Marked(
82
markedHighlight({
83
async: true,
84
highlight(code, lang, info) {
85
return new Promise((resolve, reject) => {
86
pygmentize({ lang, format: 'html' }, code, function (err, result) {
87
if (err) {
88
reject(err);
89
return;
90
}
91
resolve(result.toString());
92
});
93
});
94
}
95
})
96
);
97
98
const html = await marked.parse(`
99
\`\`\`javascript
100
const highlight = "code";
101
\`\`\`
102
`);
103
```
104
105
## Capabilities
106
107
### markedHighlight Function
108
109
Creates a marked extension for syntax highlighting code blocks with three different calling patterns.
110
111
```typescript { .api }
112
/**
113
* Creates a marked extension for syntax highlighting with synchronous highlighting function
114
* @param options - Configuration options with synchronous highlight function
115
* @returns MarkedExtension to be passed to marked.use()
116
*/
117
function markedHighlight(options: SynchronousOptions): MarkedExtension;
118
119
/**
120
* Creates a marked extension for syntax highlighting with asynchronous highlighting function
121
* @param options - Configuration options with asynchronous highlight function
122
* @returns MarkedExtension to be passed to marked.use()
123
*/
124
function markedHighlight(options: AsynchronousOptions): MarkedExtension;
125
126
/**
127
* Creates a marked extension for syntax highlighting with a direct function
128
* @param highlightFunction - A synchronous function to apply syntax highlighting
129
* @returns MarkedExtension to be passed to marked.use()
130
*/
131
function markedHighlight(highlightFunction: SyncHighlightFunction): MarkedExtension;
132
```
133
134
**Usage Examples:**
135
136
```javascript
137
// Direct function approach
138
markedHighlight((code, lang) => {
139
return hljs.highlight(code, { language: lang }).value;
140
});
141
142
// Synchronous options approach
143
markedHighlight({
144
highlight: (code, lang) => hljs.highlight(code, { language: lang }).value,
145
langPrefix: 'hljs language-',
146
emptyLangClass: 'hljs'
147
});
148
149
// Asynchronous options approach
150
markedHighlight({
151
async: true,
152
highlight: async (code, lang) => {
153
return await someAsyncHighlighter(code, lang);
154
}
155
});
156
```
157
158
## Types
159
160
```typescript { .api }
161
/**
162
* A synchronous function to highlight code
163
*/
164
type SyncHighlightFunction = (
165
code: string,
166
language: string,
167
info: string
168
) => string;
169
170
/**
171
* An asynchronous function to highlight code
172
*/
173
type AsyncHighlightFunction = (
174
code: string,
175
language: string,
176
info: string
177
) => Promise<string>;
178
179
/**
180
* Options for configuring the marked-highlight extension using a synchronous
181
* highlighting function.
182
*/
183
interface SynchronousOptions {
184
/** Function to highlight code with */
185
highlight: SyncHighlightFunction;
186
/**
187
* Not necessary when using a synchronous highlighting function, but can be
188
* set without harm (it will make marked.parse() return a promise if true)
189
*/
190
async?: boolean;
191
/**
192
* The language tag found immediately after the code block opening marker is
193
* appended to this to form the class attribute added to the <code> element.
194
* @default 'language-'
195
*/
196
langPrefix?: string;
197
/**
198
* The class attribute added to the <code> element if the language tag is
199
* empty.
200
* @default ''
201
*/
202
emptyLangClass?: string;
203
}
204
205
/**
206
* Options for configuring the marked-highlight extension using an
207
* asynchronous highlighting function.
208
*/
209
interface AsynchronousOptions {
210
/** Function to highlight code with */
211
highlight: AsyncHighlightFunction;
212
/** Must be set to true when using an asynchronous highlight function */
213
async: true;
214
/**
215
* The language tag found immediately after the code block opening marker is
216
* appended to this to form the class attribute added to the <code> element.
217
* @default 'language-'
218
*/
219
langPrefix?: string;
220
/**
221
* The class attribute added to the <code> element if the language tag is
222
* empty.
223
* @default ''
224
*/
225
emptyLangClass?: string;
226
}
227
228
/**
229
* MarkedExtension interface from marked library
230
* This is the return type of markedHighlight() function
231
*/
232
interface MarkedExtension {
233
/** Whether the extension requires async processing */
234
async: boolean;
235
/** Token processing function for code blocks */
236
walkTokens: (token: any) => void | Promise<void>;
237
/** Indicates use of the new renderer system */
238
useNewRenderer: boolean;
239
/** Custom renderer for code blocks */
240
renderer: {
241
code: (code: string, infoString: string, escaped: boolean) => string;
242
};
243
}
244
```
245
246
## Configuration Options
247
248
### highlight (required)
249
250
The function that transforms raw code into highlighted HTML.
251
252
**Parameters:**
253
- `code` (string): The raw code to be highlighted
254
- `language` (string): The language tag found immediately after the code block opening marker (e.g., `javascript` from \`\`\`javascript). Only the first word is used if multiple words are present.
255
- `info` (string): The full string after the code block opening marker (e.g., `ts twoslash` from \`\`\`ts twoslash). Empty string for code blocks without language tags.
256
257
**Returns:** Highlighted code as HTML string (or Promise for async functions). Returning `null` will skip highlighting and render the original code with HTML escaping.
258
259
**Edge Case Behaviors:**
260
- **Multiple words in language**: Only the first word is used (e.g., `ts twoslash` becomes language `ts`)
261
- **Special characters in language**: HTML entities are escaped in CSS class names
262
- **Null return**: When the highlight function returns `null`, the original code is rendered with proper HTML escaping
263
- **Empty language**: When no language is specified, `language` parameter is empty string and `emptyLangClass` is applied
264
265
### async (optional)
266
267
Set to `true` when using an asynchronous highlight function. When `true`, `marked.parse()` will return a Promise.
268
269
**Default:** `false`
270
271
### langPrefix (optional)
272
273
A prefix added to the CSS class on the `<code>` element. The language tag is appended to this prefix.
274
275
**Default:** `'language-'`
276
277
**Example:** With `langPrefix: 'hljs language-'` and language `javascript`, the resulting class will be `hljs language-javascript`
278
279
### emptyLangClass (optional)
280
281
The CSS class added to the `<code>` element when the language tag is empty or not specified.
282
283
**Default:** `''` (empty string)
284
285
**Example:** With `emptyLangClass: 'hljs'`, code blocks without language tags will have `class="hljs"`
286
287
## Error Handling
288
289
The library throws specific errors for common configuration mistakes:
290
291
1. **Missing highlight function:**
292
```
293
"Must provide highlight function"
294
```
295
Thrown when options object is provided without a highlight function.
296
297
2. **Async configuration mismatch:**
298
```
299
"markedHighlight is not set to async but the highlight function is async. Set the async option to true on markedHighlight to await the async highlight function."
300
```
301
Thrown when an async highlight function is used but `async: true` is not set in options.
302
303
## Integration Examples
304
305
### With highlight.js
306
307
```javascript
308
import { markedHighlight } from "marked-highlight";
309
import hljs from 'highlight.js';
310
311
const extension = markedHighlight({
312
langPrefix: 'hljs language-',
313
highlight(code, lang) {
314
const language = hljs.getLanguage(lang) ? lang : 'plaintext';
315
return hljs.highlight(code, { language }).value;
316
}
317
});
318
```
319
320
### With Prism.js
321
322
```javascript
323
import { markedHighlight } from "marked-highlight";
324
import Prism from 'prismjs';
325
326
const extension = markedHighlight({
327
langPrefix: 'language-',
328
highlight(code, lang) {
329
if (Prism.languages[lang]) {
330
return Prism.highlight(code, Prism.languages[lang], lang);
331
}
332
return code;
333
}
334
});
335
```
336
337
### With custom async highlighter
338
339
```javascript
340
import { markedHighlight } from "marked-highlight";
341
342
const extension = markedHighlight({
343
async: true,
344
highlight: async (code, lang) => {
345
const response = await fetch('/api/highlight', {
346
method: 'POST',
347
headers: { 'Content-Type': 'application/json' },
348
body: JSON.stringify({ code, language: lang })
349
});
350
const result = await response.json();
351
return result.highlighted;
352
}
353
});
354
```
355
356
## Requirements
357
358
### Peer Dependencies
359
360
- **marked**: `>=4 <17` (required)
361
362
The extension is compatible with marked versions 4 through 16 and integrates using marked's extension system.
363
364
### Environment Support
365
366
- **Node.js**: CommonJS and ES modules
367
- **Browser**: UMD build available
368
- **TypeScript**: Full type definitions included