Skip to content

Commit 9b452bc

Browse files
authored
feat: add preprocess and postprocess hooks (#2730)
1 parent 042dcc5 commit 9b452bc

File tree

8 files changed

+418
-167
lines changed

8 files changed

+418
-167
lines changed

docs/USING_PRO.md

Lines changed: 78 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,7 @@ smartypants('"this ... string"')
262262

263263
<h2 id="walk-tokens">Walk Tokens : <code>walkTokens</code></h2>
264264

265-
The walkTokens function gets called with every token. Child tokens are called before moving on to sibling tokens. Each token is passed by reference so updates are persisted when passed to the parser. When [`async`](#async) mode is enabled, the return value is awaited. Otherwise the return value is ignored.
265+
The walkTokens function gets called with every token. Child tokens are called before moving on to sibling tokens. Each token is passed by reference so updates are persisted when passed to the parser. When [`async`](#async) mode is enabled, the return value is awaited. Otherwise the return value is ignored.
266266

267267
`marked.use()` can be called multiple times with different `walkTokens` functions. Each function will be called in order, starting with the function that was assigned *last*.
268268

@@ -293,6 +293,83 @@ console.log(marked.parse('# heading 2\n\n## heading 3'));
293293

294294
***
295295

296+
<h2 id="hooks">Hooks : <code>hooks</code></h2>
297+
298+
Hooks are methods that hook into some part of marked. The following hooks are available:
299+
300+
| signature | description |
301+
|-----------|-------------|
302+
| `preprocess(markdown: string): string` | Process markdown before sending it to marked. |
303+
| `postprocess(html: string): string` | Process html after marked has finished parsing. |
304+
305+
`marked.use()` can be called multiple times with different `hooks` functions. Each function will be called in order, starting with the function that was assigned *last*.
306+
307+
**Example:** Set options based on [front-matter](https://www.npmjs.com/package/front-matter)
308+
309+
```js
310+
import { marked } from 'marked';
311+
import fm from 'front-matter';
312+
313+
// Override function
314+
const hooks = {
315+
preprocess(markdown) {
316+
const { attributes, body } = fm(markdown);
317+
for (const prop in attributes) {
318+
if (prop in this.options) {
319+
this.options[prop] = attributes[prop];
320+
}
321+
}
322+
return body;
323+
}
324+
};
325+
326+
marked.use({ hooks });
327+
328+
// Run marked
329+
console.log(marked.parse(`
330+
---
331+
headerIds: false
332+
---
333+
334+
## test
335+
`.trim()));
336+
```
337+
338+
**Output:**
339+
340+
```html
341+
<h2>test</h2>
342+
```
343+
344+
**Example:** Sanitize HTML with [isomorphic-dompurify](https://www.npmjs.com/package/isomorphic-dompurify)
345+
346+
```js
347+
import { marked } from 'marked';
348+
import DOMPurify from 'isomorphic-dompurify';
349+
350+
// Override function
351+
const hooks = {
352+
postprocess(html) {
353+
return DOMPurify.sanitize(html);
354+
}
355+
};
356+
357+
marked.use({ hooks });
358+
359+
// Run marked
360+
console.log(marked.parse(`
361+
<img src=x onerror=alert(1)//>
362+
`));
363+
```
364+
365+
**Output:**
366+
367+
```html
368+
<img src="x">
369+
```
370+
371+
***
372+
296373
<h2 id="extensions">Custom Extensions : <code>extensions</code></h2>
297374

298375
You may supply an `extensions` array to the `options` object. This array can contain any number of `extension` objects, using the following properties:

docs/_document.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ <h1>Marked Documentation</h1>
5353
<li><a href="/using_pro#renderer">Renderer</a></li>
5454
<li><a href="/using_pro#tokenizer">Tokenizer</a></li>
5555
<li><a href="/using_pro#walk-tokens">Walk Tokens</a></li>
56+
<li><a href="/using_pro#hooks">Hooks</a></li>
5657
<li><a href="/using_pro#extensions">Custom Extensions</a></li>
5758
<li><a href="/using_pro#async">Async Marked</a></li>
5859
<li><a href="/using_pro#lexer">Lexer</a></li>

src/Hooks.js

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { defaults } from './defaults.js';
2+
3+
export class Hooks {
4+
constructor(options) {
5+
this.options = options || defaults;
6+
}
7+
8+
static passThroughHooks = new Set([
9+
'preprocess',
10+
'postprocess'
11+
]);
12+
13+
/**
14+
* Process markdown before marked
15+
*/
16+
preprocess(markdown) {
17+
return markdown;
18+
}
19+
20+
/**
21+
* Process HTML after marked is finished
22+
*/
23+
postprocess(html) {
24+
return html;
25+
}
26+
}

src/defaults.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ export function getDefaults() {
88
headerIds: true,
99
headerPrefix: '',
1010
highlight: null,
11+
hooks: null,
1112
langPrefix: 'language-',
1213
mangle: true,
1314
pedantic: false,

src/helpers.js

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -142,23 +142,6 @@ export function resolveUrl(base, href) {
142142

143143
export const noopTest = { exec: function noopTest() {} };
144144

145-
export function merge(obj) {
146-
let i = 1,
147-
target,
148-
key;
149-
150-
for (; i < arguments.length; i++) {
151-
target = arguments[i];
152-
for (key in target) {
153-
if (Object.prototype.hasOwnProperty.call(target, key)) {
154-
obj[key] = target[key];
155-
}
156-
}
157-
}
158-
159-
return obj;
160-
}
161-
162145
export function splitCells(tableRow, count) {
163146
// ensure that every cell-delimiting pipe has a space
164147
// before it to distinguish it from an escaped pipe

0 commit comments

Comments
 (0)