Skip to content

Commit

Permalink
feat: add preprocess and postprocess hooks (#2730)
Browse files Browse the repository at this point in the history
  • Loading branch information
UziTech committed Mar 22, 2023
1 parent 042dcc5 commit 9b452bc
Show file tree
Hide file tree
Showing 8 changed files with 418 additions and 167 deletions.
79 changes: 78 additions & 1 deletion docs/USING_PRO.md
Expand Up @@ -262,7 +262,7 @@ smartypants('"this ... string"')

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

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.
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.

`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*.

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

***

<h2 id="hooks">Hooks : <code>hooks</code></h2>

Hooks are methods that hook into some part of marked. The following hooks are available:

| signature | description |
|-----------|-------------|
| `preprocess(markdown: string): string` | Process markdown before sending it to marked. |
| `postprocess(html: string): string` | Process html after marked has finished parsing. |

`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*.

**Example:** Set options based on [front-matter](https://www.npmjs.com/package/front-matter)

```js
import { marked } from 'marked';
import fm from 'front-matter';

// Override function
const hooks = {
preprocess(markdown) {
const { attributes, body } = fm(markdown);
for (const prop in attributes) {
if (prop in this.options) {
this.options[prop] = attributes[prop];
}
}
return body;
}
};

marked.use({ hooks });

// Run marked
console.log(marked.parse(`
---
headerIds: false
---
## test
`.trim()));
```

**Output:**

```html
<h2>test</h2>
```

**Example:** Sanitize HTML with [isomorphic-dompurify](https://www.npmjs.com/package/isomorphic-dompurify)

```js
import { marked } from 'marked';
import DOMPurify from 'isomorphic-dompurify';

// Override function
const hooks = {
postprocess(html) {
return DOMPurify.sanitize(html);
}
};

marked.use({ hooks });

// Run marked
console.log(marked.parse(`
<img src=x onerror=alert(1)//>
`));
```

**Output:**

```html
<img src="x">
```

***

<h2 id="extensions">Custom Extensions : <code>extensions</code></h2>

You may supply an `extensions` array to the `options` object. This array can contain any number of `extension` objects, using the following properties:
Expand Down
1 change: 1 addition & 0 deletions docs/_document.html
Expand Up @@ -53,6 +53,7 @@ <h1>Marked Documentation</h1>
<li><a href="/using_pro#renderer">Renderer</a></li>
<li><a href="/using_pro#tokenizer">Tokenizer</a></li>
<li><a href="/using_pro#walk-tokens">Walk Tokens</a></li>
<li><a href="/using_pro#hooks">Hooks</a></li>
<li><a href="/using_pro#extensions">Custom Extensions</a></li>
<li><a href="/using_pro#async">Async Marked</a></li>
<li><a href="/using_pro#lexer">Lexer</a></li>
Expand Down
26 changes: 26 additions & 0 deletions src/Hooks.js
@@ -0,0 +1,26 @@
import { defaults } from './defaults.js';

export class Hooks {
constructor(options) {
this.options = options || defaults;
}

static passThroughHooks = new Set([
'preprocess',
'postprocess'
]);

/**
* Process markdown before marked
*/
preprocess(markdown) {
return markdown;
}

/**
* Process HTML after marked is finished
*/
postprocess(html) {
return html;
}
}
1 change: 1 addition & 0 deletions src/defaults.js
Expand Up @@ -8,6 +8,7 @@ export function getDefaults() {
headerIds: true,
headerPrefix: '',
highlight: null,
hooks: null,
langPrefix: 'language-',
mangle: true,
pedantic: false,
Expand Down
17 changes: 0 additions & 17 deletions src/helpers.js
Expand Up @@ -142,23 +142,6 @@ export function resolveUrl(base, href) {

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

export function merge(obj) {
let i = 1,
target,
key;

for (; i < arguments.length; i++) {
target = arguments[i];
for (key in target) {
if (Object.prototype.hasOwnProperty.call(target, key)) {
obj[key] = target[key];
}
}
}

return obj;
}

export function splitCells(tableRow, count) {
// ensure that every cell-delimiting pipe has a space
// before it to distinguish it from an escaped pipe
Expand Down

1 comment on commit 9b452bc

@vercel
Copy link

@vercel vercel bot commented on 9b452bc Mar 22, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.