markdown-maker 1.10.2 → 1.10.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/node.js.yml +21 -26
- package/.vscode/extensions.json +3 -0
- package/.vscode/settings.json +8 -3
- package/.vscode/snippets.code-snippets +6 -8
- package/jest.config.js +7 -0
- package/package.json +41 -40
- package/src/cltool.ts +113 -71
- package/src/commands.ts +237 -243
- package/src/errors.ts +26 -0
- package/src/main.ts +110 -109
- package/src/parser.ts +378 -350
- package/src/templates.ts +5 -1
- package/src/types.ts +33 -0
- package/tests/_test-util.ts +44 -0
- package/tests/advanced.spec.ts +92 -0
- package/tests/basic.spec.ts +68 -0
- package/tests/clargs.spec.ts +50 -0
- package/tests/errors.spec.ts +64 -0
- package/tests/html.spec.ts +23 -0
- package/tests/line.spec.ts +21 -0
- package/tests/marked.spec.ts +40 -0
- package/tests/target.spec.ts +41 -0
- package/tests/vars.spec.ts +45 -0
- package/tsconfig.json +66 -64
- package/prettierrc.yaml +0 -4
- package/test/advanced.test.js +0 -34
- package/test/basic.test.js +0 -67
- package/test/clargs.test.js +0 -51
- package/test/errors.test.js +0 -47
- package/test/hooks.js +0 -9
- package/test/hooks.test.js +0 -114
- package/test/html.test.js +0 -37
- package/test/line.test.js +0 -21
- package/test/marked.test.js +0 -43
- package/test/target.test.js +0 -43
- package/test/tester.test.js +0 -41
- package/test/vars.test.js +0 -49
package/src/parser.ts
CHANGED
@@ -1,370 +1,398 @@
|
|
1
1
|
import fs from "fs"; /* for handling reading of files */
|
2
2
|
import path from "path"; /* for handling file paths */
|
3
3
|
|
4
|
-
import Colors = require("colors.ts"); /* for adding colours to strings */
|
5
|
-
Colors.enable();
|
6
4
|
import marked from "marked";
|
7
5
|
|
8
|
-
import { Command, commands, load_extensions
|
9
|
-
import {
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
}
|
6
|
+
import { Command, commands, load_extensions } from "./commands";
|
7
|
+
import {
|
8
|
+
argParser,
|
9
|
+
IncompleteCommandLineArgs,
|
10
|
+
IncompleteParserOptions,
|
11
|
+
ParserOptions,
|
12
|
+
} from "./cltool";
|
13
|
+
import { MDMError, MDMNonParserError, MDMWarning } from "./errors";
|
14
|
+
import { CommandGroupType, TaggedElement, TargetType } from "./types";
|
15
15
|
|
16
16
|
/* parse some md
|
17
17
|
* recursively with extra options */
|
18
18
|
class Parser {
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
19
|
+
file: string;
|
20
|
+
parent?: Parser;
|
21
|
+
line_num: number;
|
22
|
+
wd: string;
|
23
|
+
wd_full: string;
|
24
|
+
blobs: {
|
25
|
+
[key: number]: string | undefined;
|
26
|
+
};
|
27
|
+
opts: ParserOptions;
|
28
|
+
raw: string;
|
29
|
+
|
30
|
+
static TOKEN = "#md";
|
31
|
+
|
32
|
+
constructor(
|
33
|
+
filename: string,
|
34
|
+
clargs?: IncompleteCommandLineArgs,
|
35
|
+
opts?: IncompleteParserOptions
|
36
|
+
) {
|
37
|
+
/* this.working_directory */
|
38
|
+
this.file = filename;
|
39
|
+
|
40
|
+
this.line_num = 0;
|
41
|
+
this.wd = path.dirname(filename);
|
42
|
+
this.wd_full = path.resolve(this.wd);
|
43
|
+
|
44
|
+
/* finished blob */
|
45
|
+
this.blobs = {};
|
46
|
+
|
47
|
+
if (!clargs) {
|
48
|
+
clargs = argParser.parse_args([filename]);
|
49
|
+
}
|
50
|
+
|
51
|
+
/* get default options, and overwrite with the ones present
|
52
|
+
in the arguments */
|
53
|
+
this.opts = defaultParserOptions();
|
54
|
+
Object.assign(this.opts, clargs);
|
55
|
+
Object.assign(this.opts, opts);
|
56
|
+
|
57
|
+
this.raw = this.opts.isFileCallback(filename) || filename;
|
58
|
+
}
|
59
|
+
|
60
|
+
private parse() {
|
61
|
+
load_extensions(this);
|
62
|
+
if (this.opts.verbose || this.opts.debug) {
|
63
|
+
console.log(
|
64
|
+
`parsing ${this.file}: depth=${this.opts.depth}`.magenta
|
65
|
+
);
|
66
|
+
}
|
67
|
+
|
68
|
+
if (this.opts.debug) {
|
69
|
+
console.log("Parsing options:");
|
70
|
+
console.log(this.opts);
|
71
|
+
}
|
72
|
+
|
73
|
+
/* reset sections for beginning parse */
|
74
|
+
if (this.opts.depth === 0) this.opts.secs = [];
|
75
|
+
let __blob = this.raw;
|
76
|
+
|
77
|
+
/* apply preproccessing to raw file */
|
78
|
+
__blob = this.preprocess(__blob);
|
79
|
+
|
80
|
+
/* main parser instance call */
|
81
|
+
__blob = this.mainparse(__blob);
|
82
|
+
|
83
|
+
/**
|
84
|
+
* apply postprocessing after */
|
85
|
+
__blob = this.postprocess(__blob);
|
86
|
+
|
87
|
+
return __blob;
|
88
|
+
}
|
89
|
+
|
90
|
+
private mainparse(blob: string) {
|
91
|
+
if (this.opts.verbose || this.opts.debug) {
|
92
|
+
console.debug(`beginning mainparse of '${this.file}'`.blue);
|
93
|
+
}
|
94
|
+
|
95
|
+
/* main parser instance loop */
|
96
|
+
blob.split("\n").forEach((line, lnum) => {
|
97
|
+
this.line_num = lnum;
|
98
|
+
|
99
|
+
/* if line looks like a title */
|
100
|
+
const titleMatch = line.trim().match(/^(#+) (.+)$/);
|
101
|
+
|
102
|
+
if (titleMatch) {
|
103
|
+
if (this.opts.verbose || this.opts.debug)
|
104
|
+
console.log("found toc element: " + line);
|
105
|
+
|
106
|
+
/* implement toc level */
|
107
|
+
let level = titleMatch[1].length;
|
108
|
+
let title = titleMatch[2];
|
109
|
+
|
110
|
+
this.opts.secs.push({ level, title });
|
111
|
+
|
112
|
+
if (this.opts.debug) {
|
113
|
+
console.log("updated sections:", { level, title });
|
114
|
+
}
|
115
|
+
}
|
116
|
+
});
|
117
|
+
|
118
|
+
return this.parse_commands(blob, commands.parse);
|
119
|
+
}
|
120
|
+
|
121
|
+
private preprocess(blob: string) {
|
122
|
+
if (this.opts.verbose || this.opts.debug) {
|
123
|
+
console.debug(`beginning preprocess of '${this.file}'`.blue);
|
124
|
+
}
|
125
|
+
|
126
|
+
return this.parse_commands(blob, commands.preparse);
|
127
|
+
}
|
128
|
+
|
129
|
+
private postprocess(blob: string) {
|
130
|
+
if (this.opts.verbose || this.opts.debug) {
|
131
|
+
console.debug(`beginning postprocess of '${this.file}'`.blue);
|
132
|
+
}
|
133
|
+
|
134
|
+
blob = this.parse_commands(blob, commands.postparse);
|
135
|
+
|
136
|
+
/* remove double empty lines */
|
137
|
+
blob = this.remove_double_blank_lines(blob);
|
138
|
+
blob = blob.trimEnd() + "\n\n";
|
139
|
+
return blob;
|
140
|
+
}
|
141
|
+
|
142
|
+
private parse_commands(blob: string, commands: Command[]) {
|
143
|
+
commands.forEach((command) => {
|
144
|
+
/* Add global flag to RegExp */
|
145
|
+
const re = new RegExp(
|
146
|
+
command.validator.source,
|
147
|
+
(command.validator.flags || "") + "g"
|
148
|
+
);
|
149
|
+
|
150
|
+
const replacer = (args: RegExpExecArray) => {
|
151
|
+
try {
|
152
|
+
return command.act(args, this) || "";
|
153
|
+
} catch (error) {
|
154
|
+
switch (true) {
|
155
|
+
case error instanceof MDMError:
|
156
|
+
throw error;
|
157
|
+
case error instanceof MDMWarning:
|
158
|
+
console.warn(error.message);
|
159
|
+
return `**Warning: ${error.message}**`;
|
160
|
+
default:
|
161
|
+
console.error(error);
|
162
|
+
throw error;
|
163
|
+
}
|
164
|
+
}
|
165
|
+
};
|
166
|
+
|
167
|
+
/* */
|
168
|
+
|
169
|
+
let match: RegExpExecArray | null;
|
170
|
+
while ((match = re.exec(blob)) !== null) {
|
171
|
+
blob =
|
172
|
+
blob.slice(0, match.index) +
|
173
|
+
replacer(match) +
|
174
|
+
blob.slice(match.index + match[0].length);
|
175
|
+
}
|
176
|
+
});
|
177
|
+
return blob;
|
178
|
+
}
|
179
|
+
|
180
|
+
/* Parse all commands sequentially on a sub-blob */
|
181
|
+
private parse_all_commands(blob: string, commands: CommandGroupType) {
|
182
|
+
blob = this.parse_commands(blob, commands.preparse);
|
183
|
+
blob = this.parse_commands(blob, commands.parse);
|
184
|
+
blob = this.parse_commands(blob, commands.postparse);
|
185
|
+
return blob;
|
186
|
+
}
|
187
|
+
|
188
|
+
titleId(title: string) {
|
189
|
+
const sep = this.opts.use_underscore ? "_" : "-";
|
190
|
+
|
191
|
+
title = title
|
192
|
+
.toLowerCase()
|
193
|
+
.replace(/[^\w\s]+/g, "")
|
194
|
+
.replace(/[\s_]+/g, sep);
|
195
|
+
return title;
|
196
|
+
}
|
197
|
+
|
198
|
+
get_toc() {
|
199
|
+
let __blob = [];
|
200
|
+
let tabSize = 2;
|
201
|
+
const beg = "* ";
|
202
|
+
const hor = " ".repeat(tabSize);
|
203
|
+
|
204
|
+
this.opts.secs.forEach((sec) => {
|
205
|
+
if (sec.level > this.opts.toc_level) return;
|
206
|
+
let title = sec.title.replace(/_/g, " ");
|
207
|
+
title = this.parse_all_commands(title, commands);
|
208
|
+
const link = this.titleId(title);
|
209
|
+
|
210
|
+
let __line =
|
211
|
+
hor.repeat(Math.max(sec.level - 1, 0)) +
|
212
|
+
beg +
|
213
|
+
`[${title}](#${link})`;
|
214
|
+
|
215
|
+
__blob.push(__line);
|
216
|
+
});
|
217
|
+
return __blob.join("\n");
|
218
|
+
}
|
219
|
+
|
220
|
+
add_hook(
|
221
|
+
name: string,
|
222
|
+
hook: (map: { [key: string]: TaggedElement }) => void
|
223
|
+
) {
|
224
|
+
if (this.opts.hooks[name] != undefined)
|
225
|
+
throw new MDMNonParserError(`Hook "${name}" already exists!`);
|
226
|
+
this.opts.hooks[name] = hook;
|
227
|
+
}
|
228
|
+
|
229
|
+
private line_num_from_index(index: number) {
|
230
|
+
return this.raw.substring(0, index).split("\n").length;
|
231
|
+
}
|
232
|
+
|
233
|
+
private remove_double_blank_lines(blob) {
|
234
|
+
/* replace all triple newlines, and EOF by double newline */
|
235
|
+
blob = blob.replace(/(\r\n|\n){3,}/g, "\n\n");
|
236
|
+
|
237
|
+
return blob;
|
238
|
+
}
|
239
|
+
|
240
|
+
/* output the parsed document to bundle */
|
241
|
+
to(bundleName: string, callback: (fileName: string) => void) {
|
242
|
+
const dir = path.dirname(bundleName);
|
243
|
+
if (callback === undefined) callback = () => {};
|
244
|
+
|
245
|
+
if (!fs.existsSync(dir)) {
|
246
|
+
fs.mkdirSync(dir, { recursive: true });
|
247
|
+
}
|
248
|
+
|
249
|
+
if (!this.opts.html) {
|
250
|
+
this.get(TargetType.MARKDOWN, (blob) => {
|
251
|
+
fs.writeFile(bundleName, blob, () => callback(bundleName));
|
252
|
+
});
|
253
|
+
} else {
|
254
|
+
const htmlFileName = bundleName.replace(".md", ".html");
|
255
|
+
fs.writeFile(htmlFileName, this.html(), () =>
|
256
|
+
callback(htmlFileName)
|
257
|
+
);
|
258
|
+
}
|
259
|
+
}
|
260
|
+
|
261
|
+
html() {
|
262
|
+
const htmlFormatted = marked
|
263
|
+
.parse(this.get(TargetType.HTML))
|
264
|
+
.toString();
|
265
|
+
if (this.opts.watch) {
|
266
|
+
return (
|
267
|
+
`<script>` +
|
268
|
+
`w=new WebSocket("ws:localhost:7788");` +
|
269
|
+
`w.addEventListener("message",(e)=>` +
|
270
|
+
` {if(e.data=="refresh")location.reload();}` +
|
271
|
+
`);` +
|
272
|
+
`</script>\n` +
|
273
|
+
htmlFormatted
|
274
|
+
);
|
275
|
+
}
|
276
|
+
return htmlFormatted;
|
277
|
+
}
|
278
|
+
|
279
|
+
createChild(file: string) {
|
280
|
+
return new Parser(file, undefined, {
|
281
|
+
parent: this,
|
282
|
+
depth: this.opts.depth + 1,
|
283
|
+
...this.opts,
|
284
|
+
});
|
285
|
+
}
|
286
|
+
|
287
|
+
get(targetType?: TargetType, callback?: (blob: string) => void): string {
|
288
|
+
/* If target type is undefined, markdown is the default */
|
289
|
+
if (targetType === undefined) targetType = TargetType.MARKDOWN;
|
290
|
+
if (this.blobs[targetType]) {
|
291
|
+
if (callback) {
|
292
|
+
callback(this.blobs[targetType]);
|
293
|
+
}
|
294
|
+
return this.blobs[targetType];
|
295
|
+
} else {
|
296
|
+
try {
|
297
|
+
this.opts.targetType = targetType;
|
298
|
+
let blob = this.parse();
|
299
|
+
this.opts.targetType = undefined;
|
300
|
+
if (callback) callback(blob);
|
301
|
+
return blob;
|
302
|
+
} catch (error) {
|
303
|
+
/* Compile a traceback of error */
|
304
|
+
let traceback = "";
|
305
|
+
let p: Parser = this;
|
306
|
+
|
307
|
+
do {
|
308
|
+
if (error instanceof MDMError)
|
309
|
+
traceback += `\n...on line ${p.line_num_from_index(
|
310
|
+
error.match.index
|
311
|
+
)} in ${p.file}`.grey(15);
|
312
|
+
else
|
313
|
+
traceback +=
|
314
|
+
`\n...on line ${p.line_num} in ${p.file}`.grey(15);
|
315
|
+
if (p.parent) p = p.parent;
|
316
|
+
} while (p.parent);
|
317
|
+
|
318
|
+
error.message += traceback;
|
319
|
+
|
320
|
+
/* only interested in node stacktrace when debugging */
|
321
|
+
if (!this.opts.debug) error.stack = "";
|
322
|
+
|
323
|
+
if (this.opts.only_warn) console.error(error);
|
324
|
+
else throw error;
|
325
|
+
}
|
326
|
+
}
|
327
|
+
}
|
328
|
+
}
|
329
|
+
|
330
|
+
function defaultParserOptions(): ParserOptions {
|
331
|
+
return {
|
332
|
+
defs: {},
|
333
|
+
secs: [],
|
334
|
+
args: [],
|
335
|
+
depth: 0,
|
336
|
+
verbose: false,
|
337
|
+
debug: false,
|
338
|
+
max_depth: 5,
|
339
|
+
use_underscore: false,
|
340
|
+
toc_level: 3,
|
341
|
+
allow_undefined: false,
|
342
|
+
html: false,
|
343
|
+
watch: false,
|
344
|
+
targetType: undefined,
|
345
|
+
only_warn: false,
|
346
|
+
parent: undefined,
|
347
|
+
hooks: {},
|
348
|
+
isFileCallback: (f) => {
|
349
|
+
if (!fs.existsSync(f)) return false;
|
350
|
+
return fs.readFileSync(f, "utf-8") + "\n";
|
351
|
+
},
|
352
|
+
};
|
329
353
|
}
|
330
354
|
|
331
355
|
export function splice(
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
356
|
+
str: string,
|
357
|
+
startIndex: number,
|
358
|
+
width: number,
|
359
|
+
newSubStr: string
|
336
360
|
) {
|
337
|
-
|
338
|
-
|
339
|
-
|
361
|
+
const start = str.slice(0, startIndex);
|
362
|
+
const end = str.slice(startIndex + width);
|
363
|
+
return start + newSubStr + end;
|
340
364
|
}
|
341
365
|
|
342
366
|
/* add extention to marked for classed blockquotes*/
|
343
367
|
marked.use({
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
368
|
+
renderer: {
|
369
|
+
blockquote(quote: string) {
|
370
|
+
/* find the ending, and if not, return the default */
|
371
|
+
const ending = quote.match(/\{(.+)\}\s*<\/p>/);
|
372
|
+
if (!ending) return `<blockquote>${quote}</blockquote>`;
|
373
|
+
|
374
|
+
const args = ending[1].split(" ");
|
375
|
+
|
376
|
+
const classes = args.filter((arg) => arg.startsWith("."));
|
377
|
+
const id = args.filter((arg) => arg.startsWith("#"));
|
378
|
+
|
379
|
+
const classNames = classes.map((c) => c.slice(1));
|
380
|
+
const classText =
|
381
|
+
classes.length > 0 ? `class="${classNames.join(" ")}"` : "";
|
382
|
+
const idText = id.length > 0 ? `id="${id[0].slice(1)}"` : "";
|
383
|
+
|
384
|
+
/* remove the ending from the quote */
|
385
|
+
quote = quote.replace(/\{(.+)\}\s*<\/p>/, "</p>");
|
386
|
+
|
387
|
+
return `<blockquote ${classText} ${idText}>\n${quote.trim()}</blockquote>`;
|
388
|
+
},
|
389
|
+
heading(text: string, level: number) {
|
390
|
+
/* add an id to each heading */
|
391
|
+
return `<h${level} id="${text
|
392
|
+
.replace(/ /g, "-")
|
393
|
+
.toLowerCase()}">${text}</h${level}>`;
|
394
|
+
},
|
395
|
+
},
|
366
396
|
});
|
367
397
|
|
368
|
-
module.exports = Parser;
|
369
|
-
|
370
398
|
export default Parser;
|