markdown-maker 1.7.10 → 1.9.1
Sign up to get free protection for your applications and to get access to all the features.
- package/.github/workflows/node.js.yml +13 -8
- package/README.md +1 -0
- package/doc/.mdmconfig.json +7 -0
- package/package.json +9 -7
- package/prettierrc.yaml +1 -1
- package/re-test.js +10 -0
- package/src/cltool.ts +42 -13
- package/src/commands.ts +152 -80
- package/src/parse.ts +114 -116
- package/src/templates/mathjax.js +3 -0
- package/src/templates/presentation.js +3 -0
- package/src/templates.ts +18 -0
- package/test/advanced.test.js +35 -0
- package/test/basic.test.js +29 -22
- package/test/clargs.test.js +2 -2
- package/test/errors.test.js +14 -53
- package/test/html.test.js +9 -3
- package/test/marked.test.js +43 -0
- package/test/target.test.js +2 -4
- package/test/tester.test.js +16 -14
- package/tsconfig.json +1 -1
- package/build/cltool.d.ts +0 -1
- package/build/cltool.js +0 -124
- package/build/commands.d.ts +0 -1
- package/build/commands.js +0 -137
- package/build/parse.d.ts +0 -57
- package/build/parse.js +0 -295
package/src/parse.ts
CHANGED
@@ -4,7 +4,7 @@ const path = require("path"); /* for handling file paths */
|
|
4
4
|
import Colors = require("colors.ts"); /* for adding colours to strings */
|
5
5
|
Colors.enable();
|
6
6
|
const marked = require("marked");
|
7
|
-
|
7
|
+
import { Command, commands, load_extensions, MDMError } from "./commands";
|
8
8
|
|
9
9
|
enum TargetType {
|
10
10
|
HTML,
|
@@ -18,6 +18,7 @@ class Parser {
|
|
18
18
|
parent?: Parser;
|
19
19
|
line_num: number;
|
20
20
|
wd: string;
|
21
|
+
wd_full: string;
|
21
22
|
blobs: {
|
22
23
|
[key: number]: string | undefined;
|
23
24
|
};
|
@@ -36,8 +37,9 @@ class Parser {
|
|
36
37
|
max_depth: number;
|
37
38
|
use_underscore: boolean;
|
38
39
|
toc_level: number;
|
39
|
-
|
40
|
+
allow_undefined: boolean;
|
40
41
|
html: boolean;
|
42
|
+
watch: boolean;
|
41
43
|
targetType: TargetType | undefined;
|
42
44
|
only_warn: boolean;
|
43
45
|
parent?: Parser;
|
@@ -53,7 +55,7 @@ class Parser {
|
|
53
55
|
opts?: {
|
54
56
|
parent?: Parser;
|
55
57
|
isFileCallback?: (s: string) => false | string;
|
56
|
-
}
|
58
|
+
},
|
57
59
|
) {
|
58
60
|
/* this.working_directory */
|
59
61
|
this.file = filename;
|
@@ -65,6 +67,7 @@ class Parser {
|
|
65
67
|
|
66
68
|
this.line_num = 0;
|
67
69
|
this.wd = path.dirname(filename);
|
70
|
+
this.wd_full = path.resolve(this.wd);
|
68
71
|
|
69
72
|
/* finished blob */
|
70
73
|
this.blobs = {};
|
@@ -80,8 +83,9 @@ class Parser {
|
|
80
83
|
max_depth: 5,
|
81
84
|
use_underscore: false,
|
82
85
|
toc_level: 3,
|
83
|
-
|
86
|
+
allow_undefined: false,
|
84
87
|
html: false,
|
88
|
+
watch: false,
|
85
89
|
targetType: undefined,
|
86
90
|
only_warn: false,
|
87
91
|
parent: undefined,
|
@@ -94,6 +98,7 @@ class Parser {
|
|
94
98
|
if (!clargs) {
|
95
99
|
clargs = {};
|
96
100
|
}
|
101
|
+
|
97
102
|
/* append all commandline arguments to this */
|
98
103
|
Object.assign(this.opts, clargs);
|
99
104
|
Object.assign(this.opts, opts);
|
@@ -106,12 +111,13 @@ class Parser {
|
|
106
111
|
* preprocessing, parsing and postprocess
|
107
112
|
**/
|
108
113
|
parse() {
|
114
|
+
load_extensions(this);
|
109
115
|
if (this.opts.verbose || this.opts.debug) {
|
110
116
|
console.log(
|
111
117
|
Colors.colors(
|
112
118
|
"magenta",
|
113
|
-
"parsing " + this.file + ": depth=" + this.opts.depth
|
114
|
-
)
|
119
|
+
"parsing " + this.file + ": depth=" + this.opts.depth,
|
120
|
+
),
|
115
121
|
);
|
116
122
|
}
|
117
123
|
|
@@ -137,11 +143,10 @@ class Parser {
|
|
137
143
|
return __blob;
|
138
144
|
}
|
139
145
|
|
140
|
-
mainparse(blob) {
|
146
|
+
mainparse(blob: string) {
|
141
147
|
if (this.opts.verbose || this.opts.debug) {
|
142
148
|
console.debug(`beginning mainparse of '${this.file}'`.blue);
|
143
149
|
}
|
144
|
-
let __blob = "";
|
145
150
|
|
146
151
|
/* main parser instance loop */
|
147
152
|
blob.split("\n").forEach((line, lnum) => {
|
@@ -156,121 +161,57 @@ class Parser {
|
|
156
161
|
|
157
162
|
/* implement toc level */
|
158
163
|
let level = titleMatch[1].length;
|
164
|
+
let title = titleMatch[2];
|
159
165
|
|
160
|
-
|
161
|
-
* parse elements of title
|
162
|
-
* such as variables */
|
163
|
-
if (level <= this.opts.toc_level) {
|
164
|
-
let title = titleMatch[2]
|
165
|
-
.trim()
|
166
|
-
.split(" ")
|
167
|
-
.map((s) =>
|
168
|
-
s.startsWith(Parser.TOKEN) ? this.parseToken(s) : s
|
169
|
-
)
|
170
|
-
.join("_");
|
171
|
-
|
172
|
-
this.opts.secs.push({ level, title });
|
173
|
-
|
174
|
-
if (this.opts.debug) {
|
175
|
-
console.log("updated sections:", { level, title });
|
176
|
-
}
|
177
|
-
}
|
178
|
-
}
|
166
|
+
this.opts.secs.push({ level, title });
|
179
167
|
|
180
|
-
|
181
|
-
|
182
|
-
line.split(" ").forEach((token) => {
|
183
|
-
/* if token is not #md token,
|
184
|
-
* just add it and continue */
|
185
|
-
if (token.startsWith(Parser.TOKEN)) {
|
186
|
-
token = this.parseToken(token);
|
168
|
+
if (this.opts.debug) {
|
169
|
+
console.log("updated sections:", { level, title });
|
187
170
|
}
|
188
|
-
|
189
|
-
__line_tokens.push(token);
|
190
|
-
});
|
191
|
-
/* put line back properly */
|
192
|
-
__blob += __line_tokens.join(" ") + "\n";
|
193
|
-
});
|
194
|
-
|
195
|
-
return __blob;
|
196
|
-
}
|
197
|
-
|
198
|
-
parseToken(token) {
|
199
|
-
/* iterate over all commands,
|
200
|
-
* and if command is valid, execute it */
|
201
|
-
|
202
|
-
if (this.opts.verbose || this.opts.debug)
|
203
|
-
console.log("found mdtoken: " + token);
|
204
|
-
|
205
|
-
for (let i = 0; i < commands.parse.length; i++) {
|
206
|
-
const command = commands.parse[i];
|
207
|
-
|
208
|
-
if (command.valid(token, this)) {
|
209
|
-
return command.act(token, this);
|
210
171
|
}
|
211
|
-
}
|
212
|
-
|
213
|
-
/* check if the command is for later */
|
214
|
-
for (let i = 0; i < commands.postparse.length; i++) {
|
215
|
-
const command = commands.postparse[i];
|
216
|
-
|
217
|
-
if (command.valid(token, this)) {
|
218
|
-
return token;
|
219
|
-
}
|
220
|
-
}
|
172
|
+
});
|
221
173
|
|
222
|
-
|
174
|
+
return this.parse_commands(blob, commands.parse);
|
223
175
|
}
|
224
176
|
|
225
|
-
preprocess(blob) {
|
177
|
+
preprocess(blob: string) {
|
226
178
|
if (this.opts.verbose || this.opts.debug) {
|
227
179
|
console.debug(`beginning preprocess of '${this.file}'`.blue);
|
228
180
|
}
|
229
|
-
let __blob = "";
|
230
|
-
const lines = blob.split("\n");
|
231
|
-
|
232
|
-
lines.forEach((line) => {
|
233
|
-
let __line_tokens = [];
|
234
|
-
line.split(" ").forEach((token) => {
|
235
|
-
for (const command of commands.preparse) {
|
236
|
-
if (command.valid(token, this)) {
|
237
|
-
token = command.act(token, this);
|
238
|
-
}
|
239
|
-
}
|
240
181
|
|
241
|
-
|
242
|
-
});
|
243
|
-
__blob += __line_tokens.join(" ") + "\n";
|
244
|
-
});
|
245
|
-
return __blob;
|
182
|
+
return this.parse_commands(blob, commands.preparse);
|
246
183
|
}
|
247
184
|
|
248
|
-
postprocess(blob) {
|
185
|
+
postprocess(blob: string) {
|
249
186
|
if (this.opts.verbose || this.opts.debug) {
|
250
187
|
console.debug(`beginning postprocess of '${this.file}'`.blue);
|
251
188
|
}
|
252
|
-
let __blob = "";
|
253
|
-
const lines = blob.split("\n");
|
254
|
-
|
255
|
-
lines.forEach((line) => {
|
256
|
-
let __line_tokens = [];
|
257
|
-
line.split(" ").forEach((token) => {
|
258
|
-
// only look
|
259
|
-
|
260
|
-
for (const command of commands.postparse) {
|
261
|
-
if (command.valid(token, this)) {
|
262
|
-
token = command.act(token, this);
|
263
|
-
}
|
264
|
-
}
|
265
189
|
|
266
|
-
|
267
|
-
});
|
268
|
-
__blob += __line_tokens.join(" ") + "\n";
|
269
|
-
});
|
190
|
+
blob = this.parse_commands(blob, commands.postparse);
|
270
191
|
|
271
192
|
/* remove double empty lines */
|
272
|
-
|
273
|
-
|
193
|
+
blob = this.remove_double_blank_lines(blob);
|
194
|
+
blob = blob.trimEnd() + "\n\n";
|
195
|
+
return blob;
|
196
|
+
}
|
197
|
+
|
198
|
+
parse_commands(blob: string, commands: Command[]) {
|
199
|
+
commands.forEach((command) => {
|
200
|
+
/* Add global flag to RegExp */
|
201
|
+
const re = new RegExp(
|
202
|
+
command.validator.source,
|
203
|
+
(command.validator.flags || "") + "g",
|
204
|
+
);
|
205
|
+
blob = blob.replace(re, (...args) => command.act(args, this) || "");
|
206
|
+
});
|
207
|
+
return blob;
|
208
|
+
}
|
209
|
+
|
210
|
+
parse_all_commands(blob: string, commands: { [key: string]: Command[] }) {
|
211
|
+
Object.keys(commands).forEach((key) => {
|
212
|
+
blob = this.parse_commands(blob, commands[key]);
|
213
|
+
});
|
214
|
+
return blob;
|
274
215
|
}
|
275
216
|
|
276
217
|
titleId(title: string) {
|
@@ -290,18 +231,25 @@ class Parser {
|
|
290
231
|
const hor = " ".repeat(tabSize);
|
291
232
|
|
292
233
|
this.opts.secs.forEach((sec) => {
|
293
|
-
|
294
|
-
|
234
|
+
if (sec.level > this.opts.toc_level) return;
|
235
|
+
let title = sec.title.replace(/_/g, " ");
|
236
|
+
title = this.parse_all_commands(title, commands);
|
237
|
+
const link = this.titleId(title);
|
295
238
|
|
296
239
|
let __line =
|
297
240
|
hor.repeat(Math.max(sec.level - 1, 0)) +
|
298
241
|
beg +
|
299
242
|
`[${title}](#${link})`;
|
243
|
+
|
300
244
|
__blob.push(__line);
|
301
245
|
});
|
302
246
|
return __blob.join("\n");
|
303
247
|
}
|
304
248
|
|
249
|
+
line_num_from_index(index: number) {
|
250
|
+
return this.raw.substring(0, index).split("\n").length + 1;
|
251
|
+
}
|
252
|
+
|
305
253
|
remove_double_blank_lines(blob) {
|
306
254
|
/* replace all triple newlines, and EOF by double newline */
|
307
255
|
blob = blob.replace(/(\r\n|\n){3,}/g, "\n\n");
|
@@ -310,25 +258,27 @@ class Parser {
|
|
310
258
|
}
|
311
259
|
|
312
260
|
/* output the parsed document to bundle */
|
313
|
-
to(bundleName,
|
261
|
+
to(bundleName: string, callback: (fileName: string) => void) {
|
314
262
|
const dir = path.dirname(bundleName);
|
315
263
|
var called = false;
|
316
|
-
if (
|
264
|
+
if (callback === undefined) callback = () => {};
|
317
265
|
|
318
266
|
if (!fs.existsSync(dir)) {
|
319
267
|
fs.mkdirSync(dir);
|
320
268
|
}
|
321
269
|
this.get(TargetType.MARKDOWN, (blob) => {
|
322
270
|
fs.writeFile(bundleName, blob, () => {
|
323
|
-
if (!
|
324
|
-
|
271
|
+
if (!this.opts.html) {
|
272
|
+
callback(bundleName);
|
273
|
+
called = true;
|
274
|
+
}
|
325
275
|
});
|
326
276
|
});
|
327
277
|
|
328
278
|
if (this.opts.html) {
|
329
279
|
const htmlFileName = bundleName.replace(".md", ".html");
|
330
280
|
fs.writeFile(htmlFileName, this.html(), () => {
|
331
|
-
if (!called)
|
281
|
+
if (!called) callback(htmlFileName);
|
332
282
|
called = true;
|
333
283
|
});
|
334
284
|
}
|
@@ -336,11 +286,18 @@ class Parser {
|
|
336
286
|
|
337
287
|
html() {
|
338
288
|
const htmlFormatted = marked(this.get(TargetType.HTML));
|
339
|
-
|
289
|
+
if (this.opts.watch) {
|
290
|
+
return (
|
291
|
+
`<script>w=new WebSocket("ws:localhost:7788");w.addEventListener("message",(e)=>{if(e.data=="refresh")location.reload();});</script>\n` +
|
292
|
+
htmlFormatted
|
293
|
+
);
|
294
|
+
}
|
340
295
|
return htmlFormatted;
|
341
296
|
}
|
342
297
|
|
343
|
-
get(targetType
|
298
|
+
get(targetType?: TargetType, callback?) {
|
299
|
+
/* If target type is undefined, markdown is the default */
|
300
|
+
if (targetType === undefined) targetType = TargetType.MARKDOWN;
|
344
301
|
if (this.blobs[targetType]) {
|
345
302
|
if (callback) {
|
346
303
|
callback(this.blobs[targetType]);
|
@@ -359,9 +316,13 @@ class Parser {
|
|
359
316
|
let p: Parser = this;
|
360
317
|
|
361
318
|
do {
|
362
|
-
|
363
|
-
p.
|
364
|
-
|
319
|
+
if (error instanceof MDMError)
|
320
|
+
traceback += `\n...on line ${p.line_num_from_index(
|
321
|
+
error.match.index,
|
322
|
+
)} in ${p.file}`.grey(15);
|
323
|
+
else
|
324
|
+
traceback +=
|
325
|
+
`\n...on line ${p.line_num} in ${p.file}`.grey(15);
|
365
326
|
if (p.parent) p = p.parent;
|
366
327
|
} while (p.parent);
|
367
328
|
|
@@ -377,6 +338,43 @@ class Parser {
|
|
377
338
|
}
|
378
339
|
}
|
379
340
|
|
341
|
+
export function splice(
|
342
|
+
str: string,
|
343
|
+
startIndex: number,
|
344
|
+
width: number,
|
345
|
+
newSubStr: string,
|
346
|
+
) {
|
347
|
+
const start = str.slice(0, startIndex);
|
348
|
+
const end = str.slice(startIndex + width);
|
349
|
+
return start + newSubStr + end;
|
350
|
+
}
|
351
|
+
|
352
|
+
/* add extention to marked */
|
353
|
+
marked.use({
|
354
|
+
renderer: {
|
355
|
+
blockquote(quote) {
|
356
|
+
/* find the ending, and if not, return the default */
|
357
|
+
const ending = quote.match(/\{(.+)\}\s*<\/p>/);
|
358
|
+
if (!ending) return `<blockquote>${quote}</blockquote>`;
|
359
|
+
|
360
|
+
const args = ending[1].split(" ");
|
361
|
+
|
362
|
+
const classes = args.filter((arg) => arg.startsWith("."));
|
363
|
+
const id = args.filter((arg) => arg.startsWith("#"));
|
364
|
+
|
365
|
+
const classNames = classes.map((c) => c.slice(1));
|
366
|
+
const classText =
|
367
|
+
classes.length > 0 ? `class="${classNames.join(" ")}"` : "";
|
368
|
+
const idText = id.length > 0 ? `id="${id[0].slice(1)}"` : "";
|
369
|
+
|
370
|
+
/* remove the ending from the quote */
|
371
|
+
quote = quote.replace(/\{(.+)\}\s*<\/p>/, "</p>");
|
372
|
+
|
373
|
+
return `<blockquote ${classText} ${idText}>\n${quote.trim()}</blockquote>`;
|
374
|
+
},
|
375
|
+
},
|
376
|
+
});
|
377
|
+
|
380
378
|
module.exports = Parser;
|
381
379
|
|
382
380
|
export default Parser;
|
@@ -0,0 +1,3 @@
|
|
1
|
+
const template = `<style>html {width: 100vw;height: 100vh;}.slide {padding: 5%;border-radius: 25px;margin: 0;}div > .slide-num {position: absolute;top: 12.5%;right: 15%;/* font-size: 150%; */}body {margin: 5% 15%;}img {max-width: 100%;max-height: 40vh;}</style><script>document.addEventListener("DOMContentLoaded", () => {let current_slide = 0;const all_slides = document.querySelectorAll("div.slide");const num_slides = all_slides.length;all_slides.forEach((slide) => {const num_elem = document.createElement("p");num_elem.classList.add("slide-num");slide.appendChild(num_elem);});onkeydown = (ev) => {if (ev.key == "ArrowRight" && current_slide < all_slides.length - 1)update_slide(++current_slide);else if (ev.key == "ArrowLeft" && current_slide > 0)update_slide(--current_slide);};const update_slide = (index) => {all_slides.forEach((slide) => (slide.style.display = "none"));all_slides[current_slide].style.display = "block";all_slides[current_slide].lastChild.textContent = \`\${current_slide + 1} / \${num_slides}\`;};update_slide(current_slide);});</script>`;
|
2
|
+
|
3
|
+
module.exports = template;
|
package/src/templates.ts
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
const templates: { [key: string]: string } = {};
|
2
|
+
|
3
|
+
/**
|
4
|
+
* Function to add a template to the templates object. Similar to definitions and variables, but reside as an extension.
|
5
|
+
* @param name The name of the template
|
6
|
+
* @param content The replacement string
|
7
|
+
*/
|
8
|
+
export function new_template(name: string, content: string) {
|
9
|
+
templates[name] = content;
|
10
|
+
}
|
11
|
+
|
12
|
+
/* initialize default templates */
|
13
|
+
const presentation_template = require("../src/templates/presentation.js");
|
14
|
+
const mathjax_template = require("../src/templates/mathjax.js");
|
15
|
+
new_template("presentation", presentation_template);
|
16
|
+
new_template("mathjax", mathjax_template);
|
17
|
+
|
18
|
+
export default templates;
|
@@ -0,0 +1,35 @@
|
|
1
|
+
const util = require("./tester.test.js");
|
2
|
+
|
3
|
+
util.put(
|
4
|
+
"module.exports = {main: (new_template, new_command) => {new_template('hi', 'hello'); new_command(/#test_cmd/, (t,p) => 'yeet', 0);}};",
|
5
|
+
"extensions.js"
|
6
|
+
);
|
7
|
+
|
8
|
+
describe("Use of templates", () => {
|
9
|
+
it("should import presentation template as expected", () => {
|
10
|
+
const output = new util.Parser("#mdtemplate<presentation>").get();
|
11
|
+
const template = `<style>html {width: 100vw;height: 100vh;}.slide {padding: 5%;border-radius: 25px;margin: 0;}div > .slide-num {position: absolute;top: 12.5%;right: 15%;/* font-size: 150%; */}body {margin: 5% 15%;}img {max-width: 100%;max-height: 40vh;}</style><script>document.addEventListener("DOMContentLoaded", () => {let current_slide = 0;const all_slides = document.querySelectorAll("div.slide");const num_slides = all_slides.length;all_slides.forEach((slide) => {const num_elem = document.createElement("p");num_elem.classList.add("slide-num");slide.appendChild(num_elem);});onkeydown = (ev) => {if (ev.key == "ArrowRight" && current_slide < all_slides.length - 1)update_slide(++current_slide);else if (ev.key == "ArrowLeft" && current_slide > 0)update_slide(--current_slide);};const update_slide = (index) => {all_slides.forEach((slide) => (slide.style.display = "none"));all_slides[current_slide].style.display = "block";all_slides[current_slide].lastChild.textContent = \`\${current_slide + 1} / \${num_slides}\`;};update_slide(current_slide);});</script>`;
|
12
|
+
|
13
|
+
util.assert.strictEqual(output, template + "\n\n");
|
14
|
+
});
|
15
|
+
|
16
|
+
it("should use custom templates from project extensions.js file", () => {
|
17
|
+
util.put("#mdtemplate<hi>", "sample1.md");
|
18
|
+
|
19
|
+
util.assert.strictEqual(
|
20
|
+
new util.Parser("test/test-files/sample1.md").get(),
|
21
|
+
"hello\n\n"
|
22
|
+
);
|
23
|
+
|
24
|
+
/* make sure to remove after, to not mess with future tests */
|
25
|
+
});
|
26
|
+
|
27
|
+
it("should use custom commands from project extensions.js file", () => {
|
28
|
+
util.put("#test_cmd", "sample1.md");
|
29
|
+
|
30
|
+
const parser = new util.Parser("test/test-files/sample1.md");
|
31
|
+
util.assert.strictEqual(parser.get(), "yeet\n\n");
|
32
|
+
|
33
|
+
/* make sure to remove after, to not mess with future tests */
|
34
|
+
});
|
35
|
+
});
|
package/test/basic.test.js
CHANGED
@@ -1,14 +1,6 @@
|
|
1
1
|
const util = require("./tester.test.js");
|
2
2
|
|
3
3
|
describe("Basic features", () => {
|
4
|
-
it("should raise an error if invalid token", () => {
|
5
|
-
util.assert.throws(
|
6
|
-
() => {
|
7
|
-
const output = new util.Parser("#mdNON<>").get();
|
8
|
-
},
|
9
|
-
SyntaxError
|
10
|
-
)
|
11
|
-
});
|
12
4
|
it("should join two files with include", () => {
|
13
5
|
util.put("hello\n#mdinclude<sample2.md>", "sample1.md");
|
14
6
|
util.put("there", "sample2.md");
|
@@ -19,32 +11,47 @@ describe("Basic features", () => {
|
|
19
11
|
util.assert.strictEqual(output, "hello\nthere\n\n");
|
20
12
|
});
|
21
13
|
it("should make a table of contents", () => {
|
22
|
-
const output = new util.Parser(
|
14
|
+
const output = new util.Parser(
|
15
|
+
"# yo\n## bruh nugget\n#mdmaketoc"
|
16
|
+
).get();
|
23
17
|
|
24
|
-
util.assert.strictEqual(
|
18
|
+
util.assert.strictEqual(
|
19
|
+
output,
|
20
|
+
"# yo\n## bruh nugget\n* [yo](#yo)\n * [bruh nugget](#bruh-nugget)\n\n"
|
21
|
+
);
|
25
22
|
});
|
26
23
|
it("should allow quotation marks in titles for toc", () => {
|
27
|
-
const parser = new util.Parser("# mac's farm\n#mdmaketoc")
|
24
|
+
const parser = new util.Parser("# mac's farm\n#mdmaketoc");
|
28
25
|
const markdown = parser.get();
|
29
26
|
|
30
|
-
util.assert.strictEqual(
|
27
|
+
util.assert.strictEqual(
|
28
|
+
markdown,
|
29
|
+
"# mac's farm\n* [mac's farm](#macs-farm)\n\n"
|
30
|
+
);
|
31
31
|
});
|
32
32
|
it("should allow variables in toc", () => {
|
33
|
-
const
|
33
|
+
const output = new util.Parser(
|
34
|
+
"#mddef<name= Foobar>\n# mr. #mdvar<name>\n#mdmaketoc<>"
|
35
|
+
).get();
|
34
36
|
|
35
|
-
util.assert.strictEqual(
|
37
|
+
util.assert.strictEqual(
|
38
|
+
output,
|
39
|
+
"\n# mr. Foobar\n* [mr. Foobar](#mr-foobar)\n\n"
|
40
|
+
);
|
36
41
|
});
|
37
42
|
it("should not exceed max include depth", () => {
|
38
43
|
util.put("#mdinclude<sample2.md>", "sample1.md");
|
39
44
|
util.put("yo.md>", "sample2.md");
|
40
45
|
|
41
|
-
util.assert.throws(
|
42
|
-
()
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
)
|
46
|
+
util.assert.throws(() => {
|
47
|
+
const parser = new util.Parser("test/test-files/sample1.md");
|
48
|
+
parser.opts.max_depth = 0;
|
49
|
+
parser.get();
|
50
|
+
}, Error);
|
51
|
+
});
|
52
|
+
it("should be able to reference toc elements, even if they are below toc-level", () => {
|
53
|
+
const parser = new util.Parser(`### Title\n#mdref<Title>`);
|
54
|
+
|
55
|
+
util.assert.strictEqual(parser.get(), "### Title\n[Title](#title)\n\n");
|
49
56
|
});
|
50
57
|
});
|
package/test/clargs.test.js
CHANGED
@@ -20,14 +20,14 @@ describe("Command Line Arguments", () => {
|
|
20
20
|
});
|
21
21
|
it("--allow-undef should not throw when variable is not defined", () => {
|
22
22
|
const output = new util.Parser("#mdvar<zum>", {
|
23
|
-
|
23
|
+
allow_undefined: true,
|
24
24
|
}).get();
|
25
25
|
|
26
26
|
util.assert.strictEqual(output, "<zum>\n\n");
|
27
27
|
});
|
28
28
|
describe("Conditional imports", () => {
|
29
29
|
it("should be able to conditionally import documents", () => {
|
30
|
-
util.put("hello\n#mdinclude<sample2.md,YES>", "sample1.md");
|
30
|
+
util.put("hello\n#mdinclude<sample2.md, YES>", "sample1.md");
|
31
31
|
util.put("there", "sample2.md");
|
32
32
|
|
33
33
|
const parser = new util.Parser("test/test-files/sample1.md");
|
package/test/errors.test.js
CHANGED
@@ -1,65 +1,26 @@
|
|
1
|
+
const { Parser } = require("marked");
|
1
2
|
const util = require("./tester.test.js");
|
2
3
|
|
3
4
|
describe("Error handling", () => {
|
4
|
-
it("should
|
5
|
-
util.put("
|
5
|
+
it("should dissallow undefined templates", () => {
|
6
|
+
util.put("#mdtemplate<UNDEF>", "sample1.md");
|
6
7
|
|
7
8
|
const parser = new util.Parser("test/test-files/sample1.md");
|
8
9
|
|
9
10
|
let e;
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
e = _e;
|
17
|
-
throw _e;
|
18
|
-
}
|
11
|
+
util.assert.throws(() => {
|
12
|
+
try {
|
13
|
+
parser.get();
|
14
|
+
} catch (_e) {
|
15
|
+
e = _e;
|
16
|
+
throw _e;
|
19
17
|
}
|
20
|
-
)
|
18
|
+
}, Error);
|
21
19
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
*/
|
26
|
-
util.assert.strictEqual(
|
27
|
-
e.message,
|
28
|
-
"Unknown token: #mdNON" +
|
29
|
-
"\n...on line 4 in test/test-files/sample1.md".grey(15)
|
30
|
-
)
|
20
|
+
let answer =
|
21
|
+
'Template "UNDEF" not found!' +
|
22
|
+
"\n...on line 1 in test/test-files/sample1.md".grey(15);
|
31
23
|
|
32
|
-
|
33
|
-
it("should traceback across file includes", () => {
|
34
|
-
util.put("\n#mdinclude<sample2.md>", "sample1.md");
|
35
|
-
util.put("#mdNON", "sample2.md");
|
36
|
-
|
37
|
-
const parser = new util.Parser("test/test-files/sample1.md");
|
38
|
-
|
39
|
-
let e;
|
40
|
-
|
41
|
-
/* should throw SyntaxError */
|
42
|
-
util.assert.throws(
|
43
|
-
/* run parser, but store error for further inspection */
|
44
|
-
() => {
|
45
|
-
try {
|
46
|
-
parser.get();
|
47
|
-
} catch (_e) {
|
48
|
-
e = _e;
|
49
|
-
throw _e;
|
50
|
-
}
|
51
|
-
},
|
52
|
-
SyntaxError
|
53
|
-
);
|
54
|
-
|
55
|
-
/* ...where the error message is the traceback on line 2 -> */
|
56
|
-
let answer = "Unknown token: #mdNON" +
|
57
|
-
"\n...on line 1 in test/test-files/sample2.md".grey(15) +
|
58
|
-
"\n...on line 2 in test/test-files/sample1.md".grey(15);
|
59
|
-
|
60
|
-
util.assert.strictEqual(
|
61
|
-
e.message.replace(/(\\)+/g, "/"),
|
62
|
-
answer
|
63
|
-
);
|
24
|
+
util.assert.strictEqual(e.message.replace(/(\\)+/g, "/"), answer);
|
64
25
|
});
|
65
26
|
});
|
package/test/html.test.js
CHANGED
@@ -7,7 +7,10 @@ describe("HTML Emitting", () => {
|
|
7
7
|
parser.opts.quiet = true;
|
8
8
|
|
9
9
|
parser.to("test/test-files/dist/bundle.md", () => {
|
10
|
-
util.assert.strictEqual(
|
10
|
+
util.assert.strictEqual(
|
11
|
+
util.fs.existsSync("test/test-files/dist/bundle.html"),
|
12
|
+
true
|
13
|
+
);
|
11
14
|
done();
|
12
15
|
});
|
13
16
|
});
|
@@ -17,7 +20,10 @@ describe("HTML Emitting", () => {
|
|
17
20
|
|
18
21
|
const output = parser.html();
|
19
22
|
|
20
|
-
util.assert.strictEqual(
|
23
|
+
util.assert.strictEqual(
|
24
|
+
output,
|
25
|
+
'<h1 id="cool-title">cool title</h1>\n<p>with a cool paragraph</p>\n'
|
26
|
+
);
|
21
27
|
});
|
22
28
|
it("should be able to include html documents, and not parse", () => {
|
23
29
|
util.put("#mdinclude<sample2.html>", "sample1.md");
|
@@ -28,4 +34,4 @@ describe("HTML Emitting", () => {
|
|
28
34
|
|
29
35
|
util.assert.strictEqual(output, "#mdvar<lul>\n\n");
|
30
36
|
});
|
31
|
-
})
|
37
|
+
});
|