UNPKG

9.02 kBJavaScriptView Raw
1const {
2 noopTest,
3 edit,
4 merge
5} = require('./helpers.js');
6
7/**
8 * Block-Level Grammar
9 */
10const block = {
11 newline: /^\n+/,
12 code: /^( {4}[^\n]+\n*)+/,
13 fences: /^ {0,3}(`{3,}|~{3,})([^`~\n]*)\n(?:|([\s\S]*?)\n)(?: {0,3}\1[~`]* *(?:\n+|$)|$)/,
14 hr: /^ {0,3}((?:- *){3,}|(?:_ *){3,}|(?:\* *){3,})(?:\n+|$)/,
15 heading: /^ {0,3}(#{1,6}) +([^\n]*?)(?: +#+)? *(?:\n+|$)/,
16 blockquote: /^( {0,3}> ?(paragraph|[^\n]*)(?:\n|$))+/,
17 list: /^( {0,3})(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
18 html: '^ {0,3}(?:' // optional indentation
19 + '<(script|pre|style)[\\s>][\\s\\S]*?(?:</\\1>[^\\n]*\\n+|$)' // (1)
20 + '|comment[^\\n]*(\\n+|$)' // (2)
21 + '|<\\?[\\s\\S]*?\\?>\\n*' // (3)
22 + '|<![A-Z][\\s\\S]*?>\\n*' // (4)
23 + '|<!\\[CDATA\\[[\\s\\S]*?\\]\\]>\\n*' // (5)
24 + '|</?(tag)(?: +|\\n|/?>)[\\s\\S]*?(?:\\n{2,}|$)' // (6)
25 + '|<(?!script|pre|style)([a-z][\\w-]*)(?:attribute)*? */?>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:\\n{2,}|$)' // (7) open tag
26 + '|</(?!script|pre|style)[a-z][\\w-]*\\s*>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:\\n{2,}|$)' // (7) closing tag
27 + ')',
28 def: /^ {0,3}\[(label)\]: *\n? *<?([^\s>]+)>?(?:(?: +\n? *| *\n *)(title))? *(?:\n+|$)/,
29 nptable: noopTest,
30 table: noopTest,
31 lheading: /^([^\n]+)\n {0,3}(=+|-+) *(?:\n+|$)/,
32 // regex template, placeholders will be replaced according to different paragraph
33 // interruption rules of commonmark and the original markdown spec:
34 _paragraph: /^([^\n]+(?:\n(?!hr|heading|lheading|blockquote|fences|list|html)[^\n]+)*)/,
35 text: /^[^\n]+/
36};
37
38block._label = /(?!\s*\])(?:\\[\[\]]|[^\[\]])+/;
39block._title = /(?:"(?:\\"?|[^"\\])*"|'[^'\n]*(?:\n[^'\n]+)*\n?'|\([^()]*\))/;
40block.def = edit(block.def)
41 .replace('label', block._label)
42 .replace('title', block._title)
43 .getRegex();
44
45block.bullet = /(?:[*+-]|\d{1,9}\.)/;
46block.item = /^( *)(bull) ?[^\n]*(?:\n(?!\1bull ?)[^\n]*)*/;
47block.item = edit(block.item, 'gm')
48 .replace(/bull/g, block.bullet)
49 .getRegex();
50
51block.list = edit(block.list)
52 .replace(/bull/g, block.bullet)
53 .replace('hr', '\\n+(?=\\1?(?:(?:- *){3,}|(?:_ *){3,}|(?:\\* *){3,})(?:\\n+|$))')
54 .replace('def', '\\n+(?=' + block.def.source + ')')
55 .getRegex();
56
57block._tag = 'address|article|aside|base|basefont|blockquote|body|caption'
58 + '|center|col|colgroup|dd|details|dialog|dir|div|dl|dt|fieldset|figcaption'
59 + '|figure|footer|form|frame|frameset|h[1-6]|head|header|hr|html|iframe'
60 + '|legend|li|link|main|menu|menuitem|meta|nav|noframes|ol|optgroup|option'
61 + '|p|param|section|source|summary|table|tbody|td|tfoot|th|thead|title|tr'
62 + '|track|ul';
63block._comment = /<!--(?!-?>)[\s\S]*?-->/;
64block.html = edit(block.html, 'i')
65 .replace('comment', block._comment)
66 .replace('tag', block._tag)
67 .replace('attribute', / +[a-zA-Z:_][\w.:-]*(?: *= *"[^"\n]*"| *= *'[^'\n]*'| *= *[^\s"'=<>`]+)?/)
68 .getRegex();
69
70block.paragraph = edit(block._paragraph)
71 .replace('hr', block.hr)
72 .replace('heading', ' {0,3}#{1,6} +')
73 .replace('|lheading', '') // setex headings don't interrupt commonmark paragraphs
74 .replace('blockquote', ' {0,3}>')
75 .replace('fences', ' {0,3}(?:`{3,}|~{3,})[^`\\n]*\\n')
76 .replace('list', ' {0,3}(?:[*+-]|1[.)]) ') // only lists starting from 1 can interrupt
77 .replace('html', '</?(?:tag)(?: +|\\n|/?>)|<(?:script|pre|style|!--)')
78 .replace('tag', block._tag) // pars can be interrupted by type (6) html blocks
79 .getRegex();
80
81block.blockquote = edit(block.blockquote)
82 .replace('paragraph', block.paragraph)
83 .getRegex();
84
85/**
86 * Normal Block Grammar
87 */
88
89block.normal = merge({}, block);
90
91/**
92 * GFM Block Grammar
93 */
94
95block.gfm = merge({}, block.normal, {
96 nptable: /^ *([^|\n ].*\|.*)\n *([-:]+ *\|[-| :]*)(?:\n((?:.*[^>\n ].*(?:\n|$))*)\n*|$)/,
97 table: /^ *\|(.+)\n *\|?( *[-:]+[-| :]*)(?:\n((?: *[^>\n ].*(?:\n|$))*)\n*|$)/
98});
99
100/**
101 * Pedantic grammar (original John Gruber's loose markdown specification)
102 */
103
104block.pedantic = merge({}, block.normal, {
105 html: edit(
106 '^ *(?:comment *(?:\\n|\\s*$)'
107 + '|<(tag)[\\s\\S]+?</\\1> *(?:\\n{2,}|\\s*$)' // closed tag
108 + '|<tag(?:"[^"]*"|\'[^\']*\'|\\s[^\'"/>\\s]*)*?/?> *(?:\\n{2,}|\\s*$))')
109 .replace('comment', block._comment)
110 .replace(/tag/g, '(?!(?:'
111 + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code|var|samp|kbd|sub'
112 + '|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo|span|br|wbr|ins|del|img)'
113 + '\\b)\\w+(?!:|[^\\w\\s@]*@)\\b')
114 .getRegex(),
115 def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +(["(][^\n]+[")]))? *(?:\n+|$)/,
116 heading: /^ *(#{1,6}) *([^\n]+?) *(?:#+ *)?(?:\n+|$)/,
117 fences: noopTest, // fences not supported
118 paragraph: edit(block.normal._paragraph)
119 .replace('hr', block.hr)
120 .replace('heading', ' *#{1,6} *[^\n]')
121 .replace('lheading', block.lheading)
122 .replace('blockquote', ' {0,3}>')
123 .replace('|fences', '')
124 .replace('|list', '')
125 .replace('|html', '')
126 .getRegex()
127});
128
129/**
130 * Inline-Level Grammar
131 */
132const inline = {
133 escape: /^\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/,
134 autolink: /^<(scheme:[^\s\x00-\x1f<>]*|email)>/,
135 url: noopTest,
136 tag: '^comment'
137 + '|^</[a-zA-Z][\\w:-]*\\s*>' // self-closing tag
138 + '|^<[a-zA-Z][\\w-]*(?:attribute)*?\\s*/?>' // open tag
139 + '|^<\\?[\\s\\S]*?\\?>' // processing instruction, e.g. <?php ?>
140 + '|^<![a-zA-Z]+\\s[\\s\\S]*?>' // declaration, e.g. <!DOCTYPE html>
141 + '|^<!\\[CDATA\\[[\\s\\S]*?\\]\\]>', // CDATA section
142 link: /^!?\[(label)\]\(\s*(href)(?:\s+(title))?\s*\)/,
143 reflink: /^!?\[(label)\]\[(?!\s*\])((?:\\[\[\]]?|[^\[\]\\])+)\]/,
144 nolink: /^!?\[(?!\s*\])((?:\[[^\[\]]*\]|\\[\[\]]|[^\[\]])*)\](?:\[\])?/,
145 strong: /^__([^\s_])__(?!_)|^\*\*([^\s*])\*\*(?!\*)|^__([^\s][\s\S]*?[^\s])__(?!_)|^\*\*([^\s][\s\S]*?[^\s])\*\*(?!\*)/,
146 em: /^_([^\s_])_(?!_)|^\*([^\s*<\[])\*(?!\*)|^_([^\s<][\s\S]*?[^\s_])_(?!_|[^\spunctuation])|^_([^\s_<][\s\S]*?[^\s])_(?!_|[^\spunctuation])|^\*([^\s<"][\s\S]*?[^\s\*])\*(?!\*|[^\spunctuation])|^\*([^\s*"<\[][\s\S]*?[^\s])\*(?!\*)/,
147 code: /^(`+)([^`]|[^`][\s\S]*?[^`])\1(?!`)/,
148 br: /^( {2,}|\\)\n(?!\s*$)/,
149 del: noopTest,
150 text: /^(`+|[^`])(?:[\s\S]*?(?:(?=[\\<!\[`*]|\b_|$)|[^ ](?= {2,}\n))|(?= {2,}\n))/
151};
152
153// list of punctuation marks from common mark spec
154// without ` and ] to workaround Rule 17 (inline code blocks/links)
155inline._punctuation = '!"#$%&\'()*+,\\-./:;<=>?@\\[^_{|}~';
156inline.em = edit(inline.em).replace(/punctuation/g, inline._punctuation).getRegex();
157
158inline._escapes = /\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/g;
159
160inline._scheme = /[a-zA-Z][a-zA-Z0-9+.-]{1,31}/;
161inline._email = /[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+(@)[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+(?![-_])/;
162inline.autolink = edit(inline.autolink)
163 .replace('scheme', inline._scheme)
164 .replace('email', inline._email)
165 .getRegex();
166
167inline._attribute = /\s+[a-zA-Z:_][\w.:-]*(?:\s*=\s*"[^"]*"|\s*=\s*'[^']*'|\s*=\s*[^\s"'=<>`]+)?/;
168
169inline.tag = edit(inline.tag)
170 .replace('comment', block._comment)
171 .replace('attribute', inline._attribute)
172 .getRegex();
173
174inline._label = /(?:\[[^\[\]]*\]|\\.|`[^`]*`|[^\[\]\\`])*?/;
175inline._href = /<(?:\\[<>]?|[^\s<>\\])*>|[^\s\x00-\x1f]*/;
176inline._title = /"(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)/;
177
178inline.link = edit(inline.link)
179 .replace('label', inline._label)
180 .replace('href', inline._href)
181 .replace('title', inline._title)
182 .getRegex();
183
184inline.reflink = edit(inline.reflink)
185 .replace('label', inline._label)
186 .getRegex();
187
188/**
189 * Normal Inline Grammar
190 */
191
192inline.normal = merge({}, inline);
193
194/**
195 * Pedantic Inline Grammar
196 */
197
198inline.pedantic = merge({}, inline.normal, {
199 strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
200 em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/,
201 link: edit(/^!?\[(label)\]\((.*?)\)/)
202 .replace('label', inline._label)
203 .getRegex(),
204 reflink: edit(/^!?\[(label)\]\s*\[([^\]]*)\]/)
205 .replace('label', inline._label)
206 .getRegex()
207});
208
209/**
210 * GFM Inline Grammar
211 */
212
213inline.gfm = merge({}, inline.normal, {
214 escape: edit(inline.escape).replace('])', '~|])').getRegex(),
215 _extended_email: /[A-Za-z0-9._+-]+(@)[a-zA-Z0-9-_]+(?:\.[a-zA-Z0-9-_]*[a-zA-Z0-9])+(?![-_])/,
216 url: /^((?:ftp|https?):\/\/|www\.)(?:[a-zA-Z0-9\-]+\.?)+[^\s<]*|^email/,
217 _backpedal: /(?:[^?!.,:;*_~()&]+|\([^)]*\)|&(?![a-zA-Z0-9]+;$)|[?!.,:;*_~)]+(?!$))+/,
218 del: /^~+(?=\S)([\s\S]*?\S)~+/,
219 text: /^(`+|[^`])(?:[\s\S]*?(?:(?=[\\<!\[`*~]|\b_|https?:\/\/|ftp:\/\/|www\.|$)|[^ ](?= {2,}\n)|[^a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-](?=[a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-]+@))|(?= {2,}\n|[a-zA-Z0-9.!#$%&'*+\/=?_`{\|}~-]+@))/
220});
221
222inline.gfm.url = edit(inline.gfm.url, 'i')
223 .replace('email', inline.gfm._extended_email)
224 .getRegex();
225/**
226 * GFM + Line Breaks Inline Grammar
227 */
228
229inline.breaks = merge({}, inline.gfm, {
230 br: edit(inline.br).replace('{2,}', '*').getRegex(),
231 text: edit(inline.gfm.text)
232 .replace('\\b_', '\\b_| {2,}\\n')
233 .replace(/\{2,\}/g, '*')
234 .getRegex()
235});
236
237module.exports = {
238 block,
239 inline
240};