UNPKG

6.5 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3exports.md2html = exports.md2rst = void 0;
4const commonmark = require("commonmark");
5/**
6 * Convert MarkDown to RST
7 */
8function md2rst(text) {
9 const parser = new commonmark.Parser({ smart: false });
10 const ast = parser.parse(text);
11 const doc = new DocumentBuilder();
12 function directive(name, opening) {
13 if (opening) {
14 doc.appendLine(`.. ${name}::`);
15 doc.paraBreak();
16 doc.pushPrefix(' ');
17 }
18 else {
19 doc.popPrefix();
20 }
21 }
22 function textOf(node) {
23 return node.literal?.replace(/\\/g, '\\\\') ?? '';
24 }
25 pump(ast, {
26 block_quote(_node, entering) {
27 directive('epigraph', entering);
28 },
29 heading(node, _entering) {
30 doc.appendLine(node.literal ?? '');
31 doc.appendLine(headings[node.level - 1].repeat(textOf(node).length));
32 },
33 paragraph(node, entering) {
34 // If we're going to a paragraph that's not in a list, open a block.
35 if (entering && node.parent && node.parent.type !== 'item') {
36 doc.paraBreak();
37 }
38 // If we're coming out of a paragraph that's being followed by
39 // a code block, make sure the current line ends in '::':
40 if (!entering && node.next && node.next?.type === 'code_block') {
41 doc.transformLastLine((lastLine) => {
42 const appended = lastLine.replace(/[\W]$/, '::');
43 if (appended !== lastLine) {
44 return appended;
45 }
46 return `${lastLine} Example::`;
47 });
48 }
49 // End of paragraph at least implies line break.
50 if (!entering) {
51 doc.newline();
52 }
53 },
54 text(node) {
55 doc.append(textOf(node));
56 },
57 softbreak() {
58 doc.newline();
59 },
60 linebreak() {
61 doc.newline();
62 },
63 thematic_break() {
64 doc.appendLine('------');
65 },
66 code(node) {
67 doc.append(`\`\`${textOf(node)}\`\``);
68 },
69 strong() {
70 doc.append('**');
71 },
72 emph() {
73 doc.append('*');
74 },
75 list() {
76 doc.paraBreak();
77 },
78 link(node, entering) {
79 if (entering) {
80 doc.append('`');
81 }
82 else {
83 doc.append(` <${node.destination ?? ''}>\`_`);
84 }
85 },
86 item(node, entering) {
87 // AST hierarchy looks like list -> item -> paragraph -> text
88 if (entering) {
89 if (node.listType === 'bullet') {
90 doc.pushBulletPrefix('- ');
91 }
92 else {
93 doc.pushBulletPrefix(`${node.listStart}. `);
94 }
95 }
96 else {
97 doc.popPrefix();
98 }
99 },
100 code_block(node) {
101 doc.paraBreak();
102 // If there's no paragraph just before me, add the word "Example::".
103 if (!node.prev || node.prev.type !== 'paragraph') {
104 doc.appendLine('Example::');
105 doc.paraBreak();
106 }
107 doc.pushBulletPrefix(' ');
108 for (const l of textOf(node).replace(/\n+$/, '').split('\n')) {
109 doc.appendLine(l);
110 }
111 doc.popPrefix();
112 },
113 });
114 return doc.toString();
115}
116exports.md2rst = md2rst;
117function md2html(text) {
118 const parser = new commonmark.Parser({ smart: false });
119 const renderer = new commonmark.HtmlRenderer({ smart: false, safe: true });
120 return renderer.render(parser.parse(text));
121}
122exports.md2html = md2html;
123/**
124 * Build a document incrementally
125 */
126class DocumentBuilder {
127 constructor() {
128 this.prefix = new Array();
129 this.lines = new Array();
130 this.queuedNewline = false;
131 this.lines.push([]);
132 }
133 pushPrefix(prefix) {
134 this.prefix.push(prefix);
135 }
136 popPrefix() {
137 this.prefix.pop();
138 }
139 paraBreak() {
140 if (this.lines.length > 0 && partsToString(this.lastLine) !== '') {
141 this.newline();
142 }
143 }
144 get length() {
145 return this.lines.length;
146 }
147 get lastLine() {
148 return this.lines[this.length - 1];
149 }
150 append(text) {
151 this.flushQueuedNewline();
152 this.lastLine.push(text);
153 }
154 appendLine(...lines) {
155 for (const line of lines) {
156 this.append(line);
157 this.newline();
158 }
159 }
160 pushBulletPrefix(prefix) {
161 this.append(prefix);
162 this.pushPrefix(' '.repeat(prefix.length));
163 }
164 transformLastLine(block) {
165 if (this.length >= 0) {
166 this.lines[this.length - 1].splice(0, this.lastLine.length, block(partsToString(this.lastLine)));
167 }
168 else {
169 this.lines.push([block('')]);
170 }
171 }
172 newline() {
173 this.flushQueuedNewline();
174 // Don't do the newline here, wait to apply the correct indentation when and if we add more text.
175 this.queuedNewline = true;
176 }
177 toString() {
178 return this.lines.map(partsToString).join('\n').replace(/\n+$/, '');
179 }
180 flushQueuedNewline() {
181 if (this.queuedNewline) {
182 this.lines.push([...this.prefix]);
183 this.queuedNewline = false;
184 }
185 }
186}
187/**
188 * Turn a list of string fragments into a string
189 */
190function partsToString(parts) {
191 return parts.join('').trimRight();
192}
193const headings = ['=', '-', '^', '"'];
194/**
195 * Pump a CommonMark AST tree through a set of handlers
196 */
197function pump(ast, handlers) {
198 const walker = ast.walker();
199 let event = walker.next();
200 while (event) {
201 const h = handlers[event.node.type];
202 if (h) {
203 h(event.node, event.entering);
204 }
205 event = walker.next();
206 }
207}
208/*
209 A typical AST looks like this:
210
211 document
212 ├─┬ paragraph
213 │ └── text
214 └─┬ list
215 ├─┬ item
216 │ └─┬ paragraph
217 │ ├── text
218 │ ├── softbreak
219 │ └── text
220 └─┬ item
221 └─┬ paragraph
222 ├── text
223 ├─┬ emph
224 │ └── text
225 └── text
226
227 */
228//# sourceMappingURL=markdown.js.map
\No newline at end of file