49 | ```
50 |
51 | ### Atlassian Wiki Syntax
52 |
53 | We'll refer to this as the `jira` variable in the examples below.
54 |
55 | ```
56 | *Some bold things**
57 | _Some italic stuff_
58 | h2. H2
59 | [http://google.com]
60 | ```
61 |
62 | ### Examples
63 |
64 | ```javascript
65 | // Include the module
66 | const j2m = require('jira2md');
67 |
68 | // If converting from Mardown to Jira Wiki Syntax:
69 | const jira = j2m.to_jira(md);
70 |
71 | // If converting from Jira Wiki Syntax to Markdown:
72 | const md = j2m.to_markdown(jira);
73 |
74 | // If converting from Markdown to HTML:
75 | const html = j2m.md_to_html(md);
76 |
77 | // If converting from JIRA Wiki Syntax to HTML:
78 | const html = j2m.jira_to_html(jira);
79 | ```
80 |
--------------------------------------------------------------------------------
/test/test.html:
--------------------------------------------------------------------------------
1 | Biggest heading
2 | Bigger heading
3 | Biggest heading
4 | Bigger heading
5 | Big heading
6 | Normal heading
7 | Small heading
8 | Smallest heading
9 | strong
emphasis
monospaced
deleted
inserted
superscript
subscript
10 | var hello = 'world';
11 |
12 | 

13 | http://google.com
Google
14 | GitHub Flavor
deleted
15 | preformatted piece of text
16 | so *no_ further _formatting* is done here
17 |
18 | Should be bold AND italic
19 |
20 | - First li
21 | - Second li
22 | - Indented li
23 | - Three columns in li
24 |
25 |
26 |
27 |
28 | - Back to first level li
29 |
30 |
31 | - First li
32 | - Second li
33 | - Indented li
34 | - Three columns in li
35 |
36 |
37 |
38 |
39 | - Back to first level li
40 |
41 |
42 | - Here's italic inside li
43 | - here's bold inside li
44 | - Here's bold + italic inside li
45 | - Here they are in one line indented: italic bold
46 |
47 |
48 |
49 |
50 | Here's a long single-paragraph block quote. It should look pretty and stuff.
51 |
52 |
53 |
54 |
55 | | A title |
56 |
57 |
58 |
59 | | Panel text |
60 |
61 |
62 |
63 |
64 |
65 | | Heading 1 |
66 | Heading 2 |
67 |
68 |
69 |
70 | | Col A1 |
71 | Col A2 |
72 |
73 |
74 | | Col B1 |
75 | Col B2 |
76 |
77 |
78 |
--------------------------------------------------------------------------------
/test/md2jira.js:
--------------------------------------------------------------------------------
1 | const should = require('chai').should();
2 | const fs = require('fs');
3 | const path = require('path');
4 |
5 | const j2m = require('../index');
6 |
7 | describe('to_jira', () => {
8 | it('should exist', () => {
9 | should.exist(j2m.to_jira);
10 | });
11 | it('should be a function', () => {
12 | j2m.to_jira.should.be.a('function');
13 | });
14 | it('should convert bolds properly', () => {
15 | const jira = j2m.to_jira('**bold**');
16 | jira.should.eql('*bold*');
17 | });
18 | it('should convert italics properly', () => {
19 | const jira = j2m.to_jira('*italic*');
20 | jira.should.eql('_italic_');
21 | });
22 | it('should convert monospaced content properly', () => {
23 | const jira = j2m.to_jira('`monospaced`');
24 | jira.should.eql('{{monospaced}}');
25 | });
26 | // it('should convert citations properly', () => {
27 | // const jira = j2m.to_jira('citation');
28 | // jira.should.eql('??citation??');
29 | // });
30 | it('should convert strikethroughs properly', () => {
31 | const jira = j2m.to_jira(' ~~deleted~~ ');
32 | jira.should.eql(' -deleted- ');
33 | });
34 | it('should convert inserts properly', () => {
35 | const jira = j2m.to_jira('inserted');
36 | jira.should.eql('+inserted+');
37 | });
38 | it('should convert superscript properly', () => {
39 | const jira = j2m.to_jira('superscript');
40 | jira.should.eql('^superscript^');
41 | });
42 | it('should convert subscript properly', () => {
43 | const jira = j2m.to_jira('subscript');
44 | jira.should.eql('~subscript~');
45 | });
46 | it('should convert preformatted blocks properly', () => {
47 | const jira = j2m.to_jira('```\nso *no* further **formatting** is done here\n```');
48 | jira.should.eql('{code}\nso _no_ further *formatting* is done here\n{code}');
49 | });
50 | it('should convert language-specific code blocks properly', () => {
51 | const jira = j2m.to_jira("```javascript\nconst hello = 'world';\n```");
52 | jira.should.eql("{code:javascript}\nconst hello = 'world';\n{code}");
53 | });
54 | it('should convert unnamed images properly', () => {
55 | const jira = j2m.to_jira('');
56 | jira.should.eql('!http://google.com/image!');
57 | });
58 | it('should convert named images properly', () => {
59 | const jira = j2m.to_jira('');
60 | jira.should.eql('!http://google.com/image!');
61 | });
62 | it('should convert linked images properly', () => {
63 | const jira = j2m.to_jira('[](http://google.com/link)');
64 | jira.should.eql('[!http://google.com/image!|http://google.com/link]');
65 | });
66 | it('should convert unnamed links properly', () => {
67 | const jira = j2m.to_jira('');
68 | jira.should.eql('[http://google.com]');
69 | });
70 | it('should convert named links properly', () => {
71 | const jira = j2m.to_jira('[Google](http://google.com)');
72 | jira.should.eql('[Google|http://google.com]');
73 | });
74 | it('should convert headers properly', () => {
75 | const h1 = j2m.to_jira('# Biggest heading');
76 | const h2 = j2m.to_jira('## Bigger heading');
77 | const h3 = j2m.to_jira('### Big heading');
78 | const h4 = j2m.to_jira('#### Normal heading');
79 | const h5 = j2m.to_jira('##### Small heading');
80 | const h6 = j2m.to_jira('###### Smallest heading');
81 | h1.should.eql('h1. Biggest heading');
82 | h2.should.eql('h2. Bigger heading');
83 | h3.should.eql('h3. Big heading');
84 | h4.should.eql('h4. Normal heading');
85 | h5.should.eql('h5. Small heading');
86 | h6.should.eql('h6. Smallest heading');
87 | });
88 | it('should convert underline-style headers properly', () => {
89 | const h1 = j2m.to_jira('Biggest heading\n=======');
90 | const h2 = j2m.to_jira('Bigger heading\n------');
91 | h1.should.eql('h1. Biggest heading');
92 | h2.should.eql('h2. Bigger heading');
93 | });
94 | it('should convert blockquotes properly', () => {
95 | const jira = j2m.to_jira('> This is a long blockquote type thingy that needs to be converted.');
96 | jira.should.eql('bq. This is a long blockquote type thingy that needs to be converted.');
97 | });
98 | it('should convert un-ordered lists properly', () => {
99 | const jira = j2m.to_jira('* Foo\n* Bar\n* Baz\n * FooBar\n * BarBaz\n * FooBarBaz\n* Starting Over');
100 | jira.should.eql('* Foo\n* Bar\n* Baz\n** FooBar\n** BarBaz\n*** FooBarBaz\n* Starting Over');
101 | });
102 | it('should convert ordered lists properly', () => {
103 | const jira = j2m.to_jira(
104 | '1. Foo\n1. Bar\n1. Baz\n 1. FooBar\n 1. BarBaz\n 1. FooBarBaz\n1. Starting Over'
105 | );
106 | jira.should.eql('# Foo\n# Bar\n# Baz\n## FooBar\n## BarBaz\n### FooBarBaz\n# Starting Over');
107 | });
108 | it('should handle bold AND italic (combined) correctly', () => {
109 | const jira = j2m.to_jira('This is ***emphatically bold***!');
110 | jira.should.eql('This is _*emphatically bold*_!');
111 | });
112 | it('should handle bold within a un-ordered list item', () => {
113 | const jira = j2m.to_jira('* This is not bold!\n * This is **bold**.');
114 | jira.should.eql('* This is not bold!\n** This is *bold*.');
115 | });
116 | it('should be able to handle a complicated multi-line markdown string and convert it to markdown', () => {
117 | const jiraStr = fs.readFileSync(path.resolve(__dirname, 'test.jira'), 'utf8');
118 | const mdStr = fs.readFileSync(path.resolve(__dirname, 'test.md'), 'utf8');
119 | const jira = j2m.to_jira(mdStr);
120 | jira.should.eql(jiraStr);
121 | });
122 | });
123 |
--------------------------------------------------------------------------------
/test/jira2md.js:
--------------------------------------------------------------------------------
1 | const should = require('chai').should();
2 | const fs = require('fs');
3 | const path = require('path');
4 |
5 | const j2m = require('../index');
6 |
7 | describe('to_markdown', () => {
8 | it('should exist', () => {
9 | should.exist(j2m.to_markdown);
10 | });
11 | it('should be a function', () => {
12 | j2m.to_markdown.should.be.a('function');
13 | });
14 | it('should convert bolds properly', () => {
15 | const markdown = j2m.to_markdown('*bold*');
16 | markdown.should.eql('**bold**');
17 | });
18 | it('should convert italics properly', () => {
19 | const markdown = j2m.to_markdown('_italic_');
20 | markdown.should.eql('*italic*');
21 | });
22 | it('should convert monospaced content properly', () => {
23 | const markdown = j2m.to_markdown('{{monospaced}}');
24 | markdown.should.eql('`monospaced`');
25 | });
26 | // it('should convert citations properly', () => {
27 | // const markdown = j2m.to_markdown('??citation??');
28 | // markdown.should.eql('citation');
29 | // });
30 | it('should convert strikethroughs properly', () => {
31 | const markdown = j2m.to_markdown(' -deleted- ');
32 | markdown.should.eql(' ~~deleted~~ ');
33 | });
34 | it('should convert inserts properly', () => {
35 | const markdown = j2m.to_markdown('+inserted+');
36 | markdown.should.eql('inserted');
37 | });
38 | it('should convert superscript properly', () => {
39 | const markdown = j2m.to_markdown('^superscript^');
40 | markdown.should.eql('superscript');
41 | });
42 | it('should convert subscript properly', () => {
43 | const markdown = j2m.to_markdown('~subscript~');
44 | markdown.should.eql('subscript');
45 | });
46 | it('should convert preformatted blocks properly', () => {
47 | const markdown = j2m.to_markdown('{noformat}\nso *no* further _formatting_ is done here\n{noformat}');
48 | markdown.should.eql('```\nso **no** further *formatting* is done here\n```');
49 | });
50 | it('should convert language-specific code blocks properly', () => {
51 | const markdown = j2m.to_markdown("{code:javascript}\nconst hello = 'world';\n{code}");
52 | markdown.should.eql("```javascript\nconst hello = 'world';\n```");
53 | });
54 | it('should convert code without language-specific and with title into code block', () => {
55 | const markdown = j2m.to_markdown(
56 | '{code:title=Foo.java}\nclass Foo {\n public static void main() {\n }\n}\n{code}'
57 | );
58 | markdown.should.eql('```\nclass Foo {\n public static void main() {\n }\n}\n```');
59 | });
60 | it('should convert code without line feed before the end code block', () => {
61 | const markdown = j2m.to_markdown('{code:java}\njava code{code}');
62 | markdown.should.eql('```java\njava code\n```');
63 | });
64 | it('should convert fully configured code block', () => {
65 | const markdown = j2m.to_markdown(
66 | '{code:xml|title=My Title|borderStyle=dashed|borderColor=#ccc|titleBGColor=#F7D6C1|bgColor=#FFFFCE}' +
67 | '\n ' +
68 | '\n ' +
69 | '\n ' +
70 | '\n{code}'
71 | );
72 | markdown.should.eql('```xml\n \n \n \n```');
73 | });
74 | it('should convert images properly', () => {
75 | const markdown = j2m.to_markdown('!http://google.com/image!');
76 | markdown.should.eql('');
77 | });
78 | it('should convert linked images properly', () => {
79 | const markdown = j2m.to_markdown('[!http://google.com/image!|http://google.com/link]');
80 | markdown.should.eql('[](http://google.com/link)');
81 | });
82 | it('should convert unnamed links properly', () => {
83 | const markdown = j2m.to_markdown('[http://google.com]');
84 | markdown.should.eql('');
85 | });
86 | it('should convert named links properly', () => {
87 | const markdown = j2m.to_markdown('[Google|http://google.com]');
88 | markdown.should.eql('[Google](http://google.com)');
89 | });
90 | it('should convert headers properly', () => {
91 | const h1 = j2m.to_markdown('h1. Biggest heading');
92 | const h2 = j2m.to_markdown('h2. Bigger heading');
93 | const h3 = j2m.to_markdown('h3. Big heading');
94 | const h4 = j2m.to_markdown('h4. Normal heading');
95 | const h5 = j2m.to_markdown('h5. Small heading');
96 | const h6 = j2m.to_markdown('h6. Smallest heading');
97 | h1.should.eql('# Biggest heading');
98 | h2.should.eql('## Bigger heading');
99 | h3.should.eql('### Big heading');
100 | h4.should.eql('#### Normal heading');
101 | h5.should.eql('##### Small heading');
102 | h6.should.eql('###### Smallest heading');
103 | });
104 | it('should convert blockquotes properly', () => {
105 | const markdown = j2m.to_markdown('bq. This is a long blockquote type thingy that needs to be converted.');
106 | markdown.should.eql('> This is a long blockquote type thingy that needs to be converted.');
107 | });
108 | it('should convert un-ordered lists properly', () => {
109 | const markdown = j2m.to_markdown('* Foo\n* Bar\n* Baz\n** FooBar\n** BarBaz\n*** FooBarBaz\n* Starting Over');
110 | markdown.should.eql('* Foo\n* Bar\n* Baz\n * FooBar\n * BarBaz\n * FooBarBaz\n* Starting Over');
111 | });
112 | it('should convert ordered lists properly', () => {
113 | const markdown = j2m.to_markdown('# Foo\n# Bar\n# Baz\n## FooBar\n## BarBaz\n### FooBarBaz\n# Starting Over');
114 | markdown.should.eql('1. Foo\n1. Bar\n1. Baz\n 1. FooBar\n 1. BarBaz\n 1. FooBarBaz\n1. Starting Over');
115 | });
116 | it('should handle bold AND italic (combined) correctly', () => {
117 | const markdown = j2m.to_markdown('This is _*emphatically bold*_!');
118 | markdown.should.eql('This is ***emphatically bold***!');
119 | });
120 | it('should handle bold within a un-ordered list item', () => {
121 | const markdown = j2m.to_markdown('* This is not bold!\n** This is *bold*.');
122 | markdown.should.eql('* This is not bold!\n * This is **bold**.');
123 | });
124 | it('should be able to handle a complicated multi-line jira-wiki string and convert it to markdown', () => {
125 | const jiraStr = fs.readFileSync(path.resolve(__dirname, 'test.jira'), 'utf8');
126 | const mdStr = fs.readFileSync(path.resolve(__dirname, 'test.md'), 'utf8');
127 | const markdown = j2m.to_markdown(jiraStr);
128 | markdown.should.eql(mdStr);
129 | });
130 | it('should not recognize strikethroughs over multiple lines', () => {
131 | const markdown = j2m.to_markdown(
132 | "* Here's an un-ordered list line\n* Multi-line strikethroughs shouldn't work."
133 | );
134 | markdown.should.eql("* Here's an un-ordered list line\n* Multi-line strikethroughs shouldn't work.");
135 | });
136 | it('should remove color attributes', () => {
137 | const markdown = j2m.to_markdown('A text with{color:blue} blue \n lines {color} is not necessary.');
138 | markdown.should.eql('A text with blue \n lines is not necessary.');
139 | });
140 | it('should remove multiple color attributes', () => {
141 | const markdown = j2m.to_markdown(
142 | 'A text with{color:blue} blue \n lines {color} is not necessary. {color:red} red {color}'
143 | );
144 | markdown.should.eq('A text with blue \n lines is not necessary. red ');
145 | });
146 | // it('should not recognize inserts across multiple table cells', () => {
147 | // const markdown = j2m.to_markdown('||Heading 1||Heading 2||\n|Col+A1|Col+A2|');
148 | // markdown.should.eql('\n|Heading 1|Heading 2|\n| --- | --- |\n|Col+A1|Col+A2|');
149 | // });
150 | });
151 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | const { marked } = require('marked');
2 |
3 | marked.setOptions({ breaks: true, smartyPants: true });
4 |
5 | class J2M {
6 | /**
7 | * Converts a Markdown string into HTML (just a wrapper to Marked's parse method).
8 | *
9 | * @static
10 | * @param {string} str - String to convert from Markdown to HTML
11 | * @returns {string} The HTML result
12 | */
13 | static md_to_html(str) {
14 | return marked.parse(str);
15 | }
16 |
17 | /**
18 | * Converts a Jira Wiki string into HTML.
19 | *
20 | * @static
21 | * @param {string} str - String to convert from Jira Wiki syntax to HTML
22 | * @returns {string} The HTML result
23 | */
24 | static jira_to_html(str) {
25 | return marked.parse(J2M.to_markdown(str));
26 | }
27 |
28 | /**
29 | * Converts a Jira Wiki string into Markdown.
30 | *
31 | * @static
32 | * @param {string} str - Jira Wiki string to convert to Markdown
33 | * @returns {string} The Markdown result
34 | */
35 | static to_markdown(str) {
36 | return (
37 | str
38 | // Un-Ordered Lists
39 | .replace(/^[ \t]*(\*+)\s+/gm, (match, stars) => {
40 | return `${Array(stars.length).join(' ')}* `;
41 | })
42 | // Ordered lists
43 | .replace(/^[ \t]*(#+)\s+/gm, (match, nums) => {
44 | return `${Array(nums.length).join(' ')}1. `;
45 | })
46 | // Headers 1-6
47 | .replace(/^h([0-6])\.(.*)$/gm, (match, level, content) => {
48 | return Array(parseInt(level, 10) + 1).join('#') + content;
49 | })
50 | // Bold
51 | .replace(/\*(\S.*)\*/g, '**$1**')
52 | // Italic
53 | .replace(/_(\S.*)_/g, '*$1*')
54 | // Monospaced text
55 | .replace(/\{\{([^}]+)\}\}/g, '`$1`')
56 | // Citations (buggy)
57 | // .replace(/\?\?((?:.[^?]|[^?].)+)\?\?/g, '$1')
58 | // Inserts
59 | .replace(/\+([^+]*)\+/g, '$1')
60 | // Superscript
61 | .replace(/\^([^^]*)\^/g, '$1')
62 | // Subscript
63 | .replace(/~([^~]*)~/g, '$1')
64 | // Strikethrough
65 | .replace(/(\s+)-(\S+.*?\S)-(\s+)/g, '$1~~$2~~$3')
66 | // Code Block
67 | .replace(
68 | /\{code(:([a-z]+))?([:|]?(title|borderStyle|borderColor|borderWidth|bgColor|titleBGColor)=.+?)*\}([^]*?)\n?\{code\}/gm,
69 | '```$2$5\n```'
70 | )
71 | // Pre-formatted text
72 | .replace(/{noformat}/g, '```')
73 | // Un-named Links
74 | .replace(/\[([^|]+?)\]/g, '<$1>')
75 | // Images
76 | .replace(/!(.+)!/g, '')
77 | // Named Links
78 | .replace(/\[(.+?)\|(.+?)\]/g, '[$1]($2)')
79 | // Single Paragraph Blockquote
80 | .replace(/^bq\.\s+/gm, '> ')
81 | // Remove color: unsupported in md
82 | .replace(/\{color:[^}]+\}([^]*?)\{color\}/gm, '$1')
83 | // panel into table
84 | .replace(/\{panel:title=([^}]*)\}\n?([^]*?)\n?\{panel\}/gm, '\n| $1 |\n| --- |\n| $2 |')
85 | // table header
86 | .replace(/^[ \t]*((?:\|\|.*?)+\|\|)[ \t]*$/gm, (match, headers) => {
87 | const singleBarred = headers.replace(/\|\|/g, '|');
88 | return `\n${singleBarred}\n${singleBarred.replace(/\|[^|]+/g, '| --- ')}`;
89 | })
90 | // remove leading-space of table headers and rows
91 | .replace(/^[ \t]*\|/gm, '|')
92 | );
93 | // // remove unterminated inserts across table cells
94 | // .replace(/\|([^<]*)(?![^|]*<\/ins>)([^|]*)\|/g, (_, preceding, following) => {
95 | // return `|${preceding}+${following}|`;
96 | // })
97 | // // remove unopened inserts across table cells
98 | // .replace(/\|(?)([^<]*)<\/ins>([^|]*)\|/g, (_, preceding, following) => {
99 | // return `|${preceding}+${following}|`;
100 | // });
101 | }
102 |
103 | /**
104 | * Converts a Markdown string into Jira Wiki syntax.
105 | *
106 | * @static
107 | * @param {string} str - Markdown string to convert to Jira Wiki syntax
108 | * @returns {string} The Jira Wiki syntax result
109 | */
110 | static to_jira(str) {
111 | const map = {
112 | // cite: '??',
113 | del: '-',
114 | ins: '+',
115 | sup: '^',
116 | sub: '~',
117 | };
118 |
119 | return (
120 | str
121 | // Tables
122 | .replace(
123 | /^\n((?:\|.*?)+\|)[ \t]*\n((?:\|\s*?-{3,}\s*?)+\|)[ \t]*\n((?:(?:\|.*?)+\|[ \t]*\n)*)$/gm,
124 | (match, headerLine, separatorLine, rowstr) => {
125 | const headers = headerLine.match(/[^|]+(?=\|)/g);
126 | const separators = separatorLine.match(/[^|]+(?=\|)/g);
127 | if (headers.length !== separators.length) return match;
128 |
129 | const rows = rowstr.split('\n');
130 | if (rows.length === 2 && headers.length === 1)
131 | // Panel
132 | return `{panel:title=${headers[0].trim()}}\n${rowstr
133 | .replace(/^\|(.*)[ \t]*\|/, '$1')
134 | .trim()}\n{panel}\n`;
135 |
136 | return `||${headers.join('||')}||\n${rowstr}`;
137 | }
138 | )
139 | // Bold, Italic, and Combined (bold+italic)
140 | .replace(/([*_]+)(\S.*?)\1/g, (match, wrapper, content) => {
141 | switch (wrapper.length) {
142 | case 1:
143 | return `_${content}_`;
144 | case 2:
145 | return `*${content}*`;
146 | case 3:
147 | return `_*${content}*_`;
148 | default:
149 | return wrapper + content + wrapper;
150 | }
151 | })
152 | // All Headers (# format)
153 | .replace(/^([#]+)(.*?)$/gm, (match, level, content) => {
154 | return `h${level.length}.${content}`;
155 | })
156 | // Headers (H1 and H2 underlines)
157 | .replace(/^(.*?)\n([=-]+)$/gm, (match, content, level) => {
158 | return `h${level[0] === '=' ? 1 : 2}. ${content}`;
159 | })
160 | // Ordered lists
161 | .replace(/^([ \t]*)\d+\.\s+/gm, (match, spaces) => {
162 | return `${Array(Math.floor(spaces.length / 3) + 1)
163 | .fill('#')
164 | .join('')} `;
165 | })
166 | // Un-Ordered Lists
167 | .replace(/^([ \t]*)\*\s+/gm, (match, spaces) => {
168 | return `${Array(Math.floor(spaces.length / 2 + 1))
169 | .fill('*')
170 | .join('')} `;
171 | })
172 | // Headers (h1 or h2) (lines "underlined" by ---- or =====)
173 | // Citations, Inserts, Subscripts, Superscripts, and Strikethroughs
174 | .replace(new RegExp(`<(${Object.keys(map).join('|')})>(.*?)\\1>`, 'g'), (match, from, content) => {
175 | const to = map[from];
176 | return to + content + to;
177 | })
178 | // Other kind of strikethrough
179 | .replace(/(\s+)~~(.*?)~~(\s+)/g, '$1-$2-$3')
180 | // Named/Un-Named Code Block
181 | .replace(/```(.+\n)?((?:.|\n)*?)```/g, (match, synt, content) => {
182 | let code = '{code}';
183 | if (synt) {
184 | code = `{code:${synt.replace(/\n/g, '')}}\n`;
185 | }
186 | return `${code}${content}{code}`;
187 | })
188 | // Inline-Preformatted Text
189 | .replace(/`([^`]+)`/g, '{{$1}}')
190 | // Images
191 | .replace(/!\[[^\]]*\]\(([^)]+)\)/g, '!$1!')
192 | // Named Link
193 | .replace(/\[([^\]]+)\]\(([^)]+)\)/g, '[$1|$2]')
194 | // Un-Named Link
195 | .replace(/<([^>]+)>/g, '[$1]')
196 | // Single Paragraph Blockquote
197 | .replace(/^>/gm, 'bq.')
198 | );
199 | }
200 | }
201 |
202 | module.exports = J2M;
203 |
--------------------------------------------------------------------------------