From cf1abaa4b4149d7eeab9930e1d82ad6c620b4d69 Mon Sep 17 00:00:00 2001
From: Tony Brix
Date: Sun, 19 Apr 2020 00:03:50 -0500
Subject: [PATCH 1/6] add marked.use
---
src/marked.js | 23 +++++++++++++++++++++++
1 file changed, 23 insertions(+)
diff --git a/src/marked.js b/src/marked.js
index 8a77ddcef6..1592aefdbd 100644
--- a/src/marked.js
+++ b/src/marked.js
@@ -127,6 +127,29 @@ marked.getDefaults = getDefaults;
marked.defaults = defaults;
+/**
+ * Use Extension
+ */
+
+marked.use = function(extension) {
+ const opts = merge({}, extension);
+ if (extension.renderer) {
+ const renderer = marked.defaults.renderer || new Renderer();
+ for (const prop in extension.renderer) {
+ renderer[prop] = extension.renderer[prop];
+ }
+ opts.renderer = renderer;
+ }
+ if (extension.tokenizer) {
+ const tokenizer = marked.defaults.tokenizer || new Tokenizer();
+ for (const prop in extension.tokenizer) {
+ tokenizer[prop] = extension.tokenizer[prop];
+ }
+ opts.tokenizer = tokenizer;
+ }
+ marked.setOptions(opts);
+};
+
/**
* Expose
*/
From dd86b714a300b36df77312ec65cd3fee7ffd881f Mon Sep 17 00:00:00 2001
From: Tony Brix
Date: Sun, 19 Apr 2020 00:04:06 -0500
Subject: [PATCH 2/6] add marked.use tests
---
test/unit/marked-spec.js | 100 +++++++++++++++++++++++++++++++++++++++
1 file changed, 100 insertions(+)
diff --git a/test/unit/marked-spec.js b/test/unit/marked-spec.js
index cc84d16191..bf6e6ed0bb 100644
--- a/test/unit/marked-spec.js
+++ b/test/unit/marked-spec.js
@@ -96,3 +96,103 @@ describe('inlineLexer', () => {
expect(renderer.html).toHaveBeenCalledWith('
');
});
});
+
+describe('use extension', () => {
+ it('should use renderer', () => {
+ const extension = {
+ renderer: {
+ paragraph(text) {
+ return 'extension';
+ }
+ }
+ };
+ spyOn(extension.renderer, 'paragraph').and.callThrough();
+ marked.use(extension);
+ const html = marked('text');
+ expect(extension.renderer.paragraph).toHaveBeenCalledWith('text');
+ expect(html).toBe('extension');
+ });
+
+ it('should use tokenizer', () => {
+ const extension = {
+ tokenizer: {
+ paragraph(text) {
+ return {
+ type: 'paragraph',
+ raw: text,
+ text: 'extension'
+ };
+ }
+ }
+ };
+ spyOn(extension.tokenizer, 'paragraph').and.callThrough();
+ marked.use(extension);
+ const html = marked('text');
+ expect(extension.tokenizer.paragraph).toHaveBeenCalledWith('text');
+ expect(html).toBe('extension
\n');
+ });
+
+ it('should use options from extension', () => {
+ const extension = {
+ headerIds: false
+ };
+ marked.use(extension);
+ const html = marked('# heading');
+ expect(html).toBe('heading
\n');
+ });
+
+ it('should use last extension function and not override others', () => {
+ const extension1 = {
+ renderer: {
+ paragraph(text) {
+ return 'extension1 paragraph\n';
+ },
+ html(html) {
+ return 'extension1 html\n';
+ }
+ }
+ };
+ const extension2 = {
+ renderer: {
+ paragraph(text) {
+ return 'extension2 paragraph\n';
+ }
+ }
+ };
+ marked.use(extension1);
+ marked.use(extension2);
+ const html = marked(`
+paragraph
+
+
+
+# heading
+`);
+ expect(html).toBe('extension2 paragraph\nextension1 html\nheading
\n');
+ });
+
+ it('should get options with this.options', () => {
+ const extension = {
+ renderer: {
+ heading: () => {
+ return this.options ? 'arrow options\n' : 'arrow no options\n';
+ },
+ html: function() {
+ return this.options ? 'function options\n' : 'function no options\n';
+ },
+ paragraph() {
+ return this.options ? 'shorthand options\n' : 'shorthand no options\n';
+ }
+ }
+ };
+ marked.use(extension);
+ const html = marked(`
+# heading
+
+
+
+paragraph
+`);
+ expect(html).toBe('arrow no options\nfunction options\nshorthand options\n');
+ });
+});
From 07510c9301251893ea0718ee813501c4d3e24bbe Mon Sep 17 00:00:00 2001
From: Tony Brix
Date: Sun, 19 Apr 2020 00:38:19 -0500
Subject: [PATCH 3/6] update docs with marked.use
---
docs/USING_PRO.md | 60 +++++++++++++++++++++++++----------------------
1 file changed, 32 insertions(+), 28 deletions(-)
diff --git a/docs/USING_PRO.md b/docs/USING_PRO.md
index 0b2aea001a..2d73cf97e1 100644
--- a/docs/USING_PRO.md
+++ b/docs/USING_PRO.md
@@ -12,24 +12,25 @@ The renderer defines the output of the parser.
// Create reference instance
const marked = require('marked');
-// Get reference
-const renderer = new marked.Renderer();
-
// Override function
-renderer.heading = function(text, level) {
- const escapedText = text.toLowerCase().replace(/[^\w]+/g, '-');
-
- return `
-
-
-
-
- ${text}
- `;
+const renderer = {
+ heading(text, level) {
+ const escapedText = text.toLowerCase().replace(/[^\w]+/g, '-');
+
+ return `
+
+
+
+
+ ${text}
+ `;
+ }
};
+marked.use({ renderer });
+
// Run marked
-console.log(marked('# heading+', { renderer }));
+console.log(marked('# heading+'));
```
**Output:**
@@ -99,30 +100,33 @@ The tokenizer defines how to turn markdown text into tokens.
// Create reference instance
const marked = require('marked');
-// Get reference
-const tokenizer = new marked.Tokenizer();
-const originalCodespan = tokenizer.codespan;
// Override function
-tokenizer.codespan = function(src) {
- const match = src.match(/\$+([^\$\n]+?)\$+/);
- if (match) {
- return {
- type: 'codespan',
- raw: match[0],
- text: match[1].trim()
- };
+const tokenizer = {
+ codespan(src) {
+ const match = src.match(/\$+([^\$\n]+?)\$+/);
+ if (match) {
+ return {
+ type: 'codespan',
+ raw: match[0],
+ text: match[1].trim()
+ };
+ }
+ const originalTokenizer = new marked.Tokenizer(this.options);
+ return originalTokenizer.codespan.apply(this, arguments);
}
- return originalCodespan.apply(this, arguments);
};
+marked.use({ tokenizer });
+
// Run marked
-console.log(marked('$ latex code $', { tokenizer }));
+console.log(marked('$ latex code $\n\n` other code `'));
```
**Output:**
```html
-latext code
+latex code
+other code
```
### Block level tokenizer methods
From 07f2b334f058b2db1bd4b0d4881228bf2ca3378f Mon Sep 17 00:00:00 2001
From: Tony Brix
Date: Sun, 19 Apr 2020 01:08:54 -0500
Subject: [PATCH 4/6] return false to use last renderer/tokenizer
---
docs/USING_PRO.md | 5 +++--
src/marked.js | 18 ++++++++++++++++--
test/unit/marked-spec.js | 33 +++++++++++++++++++++++++++++++++
3 files changed, 52 insertions(+), 4 deletions(-)
diff --git a/docs/USING_PRO.md b/docs/USING_PRO.md
index 2d73cf97e1..3766ee60ba 100644
--- a/docs/USING_PRO.md
+++ b/docs/USING_PRO.md
@@ -111,8 +111,9 @@ const tokenizer = {
text: match[1].trim()
};
}
- const originalTokenizer = new marked.Tokenizer(this.options);
- return originalTokenizer.codespan.apply(this, arguments);
+
+ // return false to use original codespan renderer
+ return false;
}
};
diff --git a/src/marked.js b/src/marked.js
index 1592aefdbd..8488e79f03 100644
--- a/src/marked.js
+++ b/src/marked.js
@@ -136,14 +136,28 @@ marked.use = function(extension) {
if (extension.renderer) {
const renderer = marked.defaults.renderer || new Renderer();
for (const prop in extension.renderer) {
- renderer[prop] = extension.renderer[prop];
+ const prevRenderer = renderer[prop];
+ renderer[prop] = (...args) => {
+ let ret = extension.renderer[prop].apply(renderer, args);
+ if (ret === false) {
+ ret = prevRenderer.apply(renderer, args);
+ }
+ return ret;
+ };
}
opts.renderer = renderer;
}
if (extension.tokenizer) {
const tokenizer = marked.defaults.tokenizer || new Tokenizer();
for (const prop in extension.tokenizer) {
- tokenizer[prop] = extension.tokenizer[prop];
+ const prevTokenizer = tokenizer[prop];
+ tokenizer[prop] = (...args) => {
+ let ret = extension.tokenizer[prop].apply(tokenizer, args);
+ if (ret === false) {
+ ret = prevTokenizer.apply(tokenizer, args);
+ }
+ return ret;
+ };
}
opts.tokenizer = tokenizer;
}
diff --git a/test/unit/marked-spec.js b/test/unit/marked-spec.js
index bf6e6ed0bb..1b6beafdd6 100644
--- a/test/unit/marked-spec.js
+++ b/test/unit/marked-spec.js
@@ -171,6 +171,39 @@ paragraph
expect(html).toBe('extension2 paragraph\nextension1 html\nheading
\n');
});
+ it('should use previous extension when returning false', () => {
+ const extension1 = {
+ renderer: {
+ paragraph(text) {
+ if (text !== 'original') {
+ return 'extension1 paragraph\n';
+ }
+ return false;
+ }
+ }
+ };
+ const extension2 = {
+ renderer: {
+ paragraph(text) {
+ if (text !== 'extension1' && text !== 'original') {
+ return 'extension2 paragraph\n';
+ }
+ return false;
+ }
+ }
+ };
+ marked.use(extension1);
+ marked.use(extension2);
+ const html = marked(`
+paragraph
+
+extension1
+
+original
+`);
+ expect(html).toBe('extension2 paragraph\nextension1 paragraph\noriginal
\n');
+ });
+
it('should get options with this.options', () => {
const extension = {
renderer: {
From 10504607370b84fc985df10eae1486b201f3c7c5 Mon Sep 17 00:00:00 2001
From: Tony Brix
Date: Sun, 19 Apr 2020 01:38:41 -0500
Subject: [PATCH 5/6] Fix docs
---
docs/USING_PRO.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/USING_PRO.md b/docs/USING_PRO.md
index 3766ee60ba..193c61ce9a 100644
--- a/docs/USING_PRO.md
+++ b/docs/USING_PRO.md
@@ -112,7 +112,7 @@ const tokenizer = {
};
}
- // return false to use original codespan renderer
+ // return false to use original codespan tokenizer
return false;
}
};
From d5de6af1f664e51672bf385017aaf612374f3293 Mon Sep 17 00:00:00 2001
From: Tony Brix
Date: Mon, 20 Apr 2020 10:50:09 -0500
Subject: [PATCH 6/6] add marked.use section to docs
---
docs/USING_PRO.md | 10 ++++++++++
docs/index.html | 1 +
2 files changed, 11 insertions(+)
diff --git a/docs/USING_PRO.md b/docs/USING_PRO.md
index 193c61ce9a..40aadea661 100644
--- a/docs/USING_PRO.md
+++ b/docs/USING_PRO.md
@@ -2,6 +2,16 @@
To champion the single-responsibility and open/closed principles, we have tried to make it relatively painless to extend marked. If you are looking to add custom functionality, this is the place to start.
+marked.use()
+
+`marked.use(options)` is the recommended way to extend marked. The options object can contain any [option](#/USING_ADVANCED.md#options) available in marked.
+
+The `renderer` and `tokenizer` options can be an object with functions that will be merged into the `renderer` and `tokenizer` respectively.
+
+The `renderer` and `tokenizer` functions can return false to fallback to the previous function.
+
+All other options will overwrite previously set options.
+
The renderer
The renderer defines the output of the parser.
diff --git a/docs/index.html b/docs/index.html
index 9d68e7bb41..a7de1818c0 100644
--- a/docs/index.html
+++ b/docs/index.html
@@ -154,6 +154,7 @@ Marked.js Documentation
Extensibility