markdown-maker 1.10.1 → 1.10.3

Sign up to get free protection for your applications and to get access to all the features.
package/src/parse.ts DELETED
@@ -1,396 +0,0 @@
1
- import fs from "fs"; /* for handling reading of files */
2
- import path from "path"; /* for handling file paths */
3
-
4
- import Colors = require("colors.ts"); /* for adding colours to strings */
5
- Colors.enable();
6
- import marked from "marked";
7
-
8
- import { Command, commands, load_extensions, MDMError } from "./commands";
9
-
10
- enum TargetType {
11
- HTML,
12
- MARKDOWN,
13
- }
14
-
15
- /* parse some md
16
- * recursively with extra options */
17
- class Parser {
18
- file: string;
19
- parent?: Parser;
20
- line_num: number;
21
- wd: string;
22
- wd_full: string;
23
- blobs: {
24
- [key: number]: string | undefined;
25
- };
26
- opts: {
27
- defs: {
28
- [key: string]: string;
29
- };
30
- secs: {
31
- level: number;
32
- title: string;
33
- }[];
34
- args: string[];
35
- depth: number;
36
- verbose: boolean;
37
- debug: boolean;
38
- max_depth: number;
39
- use_underscore: boolean;
40
- toc_level: number;
41
- allow_undefined: boolean;
42
- html: boolean;
43
- watch: boolean;
44
- targetType: TargetType | undefined;
45
- only_warn: boolean;
46
- parent?: Parser;
47
- hooks: { [key: string]: () => string };
48
- adv_hooks: {
49
- [key: string]: (
50
- tree: HTMLElement,
51
- map: { [tag: string]: HTMLElement }
52
- ) => HTMLElement;
53
- };
54
- isFileCallback: (s: string) => false | string;
55
- };
56
- raw: string;
57
-
58
- static TOKEN = "#md";
59
-
60
- constructor(
61
- filename,
62
- clargs,
63
- opts?: {
64
- parent?: Parser;
65
- isFileCallback?: (s: string) => false | string;
66
- }
67
- ) {
68
- /* this.working_directory */
69
- this.file = filename;
70
-
71
- if (!opts) opts = {};
72
-
73
- /* the parent parser */
74
- this.parent = opts.parent;
75
-
76
- this.line_num = 0;
77
- this.wd = path.dirname(filename);
78
- this.wd_full = path.resolve(this.wd);
79
-
80
- /* finished blob */
81
- this.blobs = {};
82
-
83
- /* all options */
84
- this.opts = {
85
- defs: {},
86
- secs: [],
87
- args: [],
88
- depth: 0,
89
- verbose: false,
90
- debug: false,
91
- max_depth: 5,
92
- use_underscore: false,
93
- toc_level: 3,
94
- allow_undefined: false,
95
- html: false,
96
- watch: false,
97
- targetType: undefined,
98
- only_warn: false,
99
- parent: undefined,
100
- hooks: {},
101
- adv_hooks: {},
102
- isFileCallback: (f) => {
103
- if (!fs.existsSync(f)) return false;
104
- return fs.readFileSync(f, "utf-8") + "\n";
105
- },
106
- };
107
-
108
- if (!clargs) {
109
- clargs = {};
110
- }
111
-
112
- /* append all commandline arguments to this */
113
- Object.assign(this.opts, clargs);
114
- Object.assign(this.opts, opts);
115
-
116
- this.raw = this.opts.isFileCallback(filename) || filename;
117
- }
118
-
119
- /**
120
- * parse wrapper for handling
121
- * preprocessing, parsing and postprocess
122
- **/
123
- parse() {
124
- load_extensions(this);
125
- if (this.opts.verbose || this.opts.debug) {
126
- console.log(
127
- Colors.colors(
128
- "magenta",
129
- "parsing " + this.file + ": depth=" + this.opts.depth
130
- )
131
- );
132
- }
133
-
134
- if (this.opts.debug) {
135
- console.log("Parsing options:");
136
- console.log(this.opts);
137
- }
138
-
139
- /* reset sections for beginning parse */
140
- if (this.opts.depth === 0) this.opts.secs = [];
141
- let __blob;
142
-
143
- /* apply preproccessing to raw file */
144
- __blob = this.preprocess(this.raw);
145
-
146
- /* main parser instance call */
147
- __blob = this.mainparse(__blob);
148
-
149
- /**
150
- * apply postprocessing after */
151
- __blob = this.postprocess(__blob);
152
-
153
- return __blob;
154
- }
155
-
156
- mainparse(blob: string) {
157
- if (this.opts.verbose || this.opts.debug) {
158
- console.debug(`beginning mainparse of '${this.file}'`.blue);
159
- }
160
-
161
- /* main parser instance loop */
162
- blob.split("\n").forEach((line, lnum) => {
163
- this.line_num = lnum;
164
-
165
- /* if line looks like a title */
166
- const titleMatch = line.trim().match(/^(#+) (.+)$/);
167
-
168
- if (titleMatch) {
169
- if (this.opts.verbose || this.opts.debug)
170
- console.log("found toc element: " + line);
171
-
172
- /* implement toc level */
173
- let level = titleMatch[1].length;
174
- let title = titleMatch[2];
175
-
176
- this.opts.secs.push({ level, title });
177
-
178
- if (this.opts.debug) {
179
- console.log("updated sections:", { level, title });
180
- }
181
- }
182
- });
183
-
184
- return this.parse_commands(blob, commands.parse);
185
- }
186
-
187
- preprocess(blob: string) {
188
- if (this.opts.verbose || this.opts.debug) {
189
- console.debug(`beginning preprocess of '${this.file}'`.blue);
190
- }
191
-
192
- return this.parse_commands(blob, commands.preparse);
193
- }
194
-
195
- postprocess(blob: string) {
196
- if (this.opts.verbose || this.opts.debug) {
197
- console.debug(`beginning postprocess of '${this.file}'`.blue);
198
- }
199
-
200
- blob = this.parse_commands(blob, commands.postparse);
201
-
202
- /* remove double empty lines */
203
- blob = this.remove_double_blank_lines(blob);
204
- blob = blob.trimEnd() + "\n\n";
205
- return blob;
206
- }
207
-
208
- parse_commands(blob: string, commands: Command[]) {
209
- commands.forEach((command) => {
210
- /* Add global flag to RegExp */
211
- const re = new RegExp(
212
- command.validator.source,
213
- (command.validator.flags || "") + "g"
214
- );
215
- blob = blob.replace(re, (...args) => command.act(args, this) || "");
216
- });
217
- return blob;
218
- }
219
-
220
- parse_all_commands(blob: string, commands: { [key: string]: Command[] }) {
221
- Object.keys(commands).forEach((key) => {
222
- blob = this.parse_commands(blob, commands[key]);
223
- });
224
- return blob;
225
- }
226
-
227
- titleId(title: string) {
228
- const sep = this.opts.use_underscore ? "_" : "-";
229
-
230
- title = title
231
- .toLowerCase()
232
- .replace(/[^\w\s]+/g, "")
233
- .replace(/[\s_]+/g, sep);
234
- return title;
235
- }
236
-
237
- gen_toc() {
238
- let __blob = [];
239
- let tabSize = 2;
240
- const beg = "* ";
241
- const hor = " ".repeat(tabSize);
242
-
243
- this.opts.secs.forEach((sec) => {
244
- if (sec.level > this.opts.toc_level) return;
245
- let title = sec.title.replace(/_/g, " ");
246
- title = this.parse_all_commands(title, commands);
247
- const link = this.titleId(title);
248
-
249
- let __line =
250
- hor.repeat(Math.max(sec.level - 1, 0)) +
251
- beg +
252
- `[${title}](#${link})`;
253
-
254
- __blob.push(__line);
255
- });
256
- return __blob.join("\n");
257
- }
258
-
259
- add_hook(name: string, hook: () => string) {
260
- if (this.opts.hooks[name] != undefined)
261
- throw new Error(`Hook ${name} already exists!`);
262
- this.opts.hooks[name] = hook;
263
- }
264
-
265
- add_adv_hook(name: string, hook: (tree: HTMLElement) => HTMLElement) {
266
- if (this.opts.hooks[name] != undefined)
267
- throw new Error(`Hook ${name} already exists!`);
268
- this.opts.adv_hooks[name] = hook;
269
- }
270
-
271
- line_num_from_index(index: number) {
272
- return this.raw.substring(0, index).split("\n").length + 1;
273
- }
274
-
275
- remove_double_blank_lines(blob) {
276
- /* replace all triple newlines, and EOF by double newline */
277
- blob = blob.replace(/(\r\n|\n){3,}/g, "\n\n");
278
-
279
- return blob;
280
- }
281
-
282
- /* output the parsed document to bundle */
283
- to(bundleName: string, callback: (fileName: string) => void) {
284
- const dir = path.dirname(bundleName);
285
- if (callback === undefined) callback = () => {};
286
-
287
- if (!fs.existsSync(dir)) {
288
- fs.mkdirSync(dir, { recursive: true });
289
- }
290
-
291
- if (!this.opts.html) {
292
- this.get(TargetType.MARKDOWN, (blob) => {
293
- fs.writeFile(bundleName, blob, () => callback(bundleName));
294
- });
295
- } else {
296
- const htmlFileName = bundleName.replace(".md", ".html");
297
- fs.writeFile(htmlFileName, this.html(), () =>
298
- callback(htmlFileName)
299
- );
300
- }
301
- }
302
-
303
- html() {
304
- const htmlFormatted = marked(this.get(TargetType.HTML));
305
- if (this.opts.watch) {
306
- return (
307
- `<script>w=new WebSocket("ws:localhost:7788");w.addEventListener("message",(e)=>{if(e.data=="refresh")location.reload();});</script>\n` +
308
- htmlFormatted
309
- );
310
- }
311
- return htmlFormatted;
312
- }
313
-
314
- get(targetType?: TargetType, callback?: (blob: string) => void): string {
315
- /* If target type is undefined, markdown is the default */
316
- if (targetType === undefined) targetType = TargetType.MARKDOWN;
317
- if (this.blobs[targetType]) {
318
- if (callback) {
319
- callback(this.blobs[targetType]);
320
- }
321
- return this.blobs[targetType];
322
- } else {
323
- try {
324
- this.opts.targetType = targetType;
325
- let blob = this.parse();
326
- this.opts.targetType = undefined;
327
- if (callback) callback(blob);
328
- return blob;
329
- } catch (error) {
330
- /* Compile a traceback of error */
331
- let traceback = "";
332
- let p: Parser = this;
333
-
334
- do {
335
- if (error instanceof MDMError)
336
- traceback += `\n...on line ${p.line_num_from_index(
337
- error.match.index
338
- )} in ${p.file}`.grey(15);
339
- else
340
- traceback +=
341
- `\n...on line ${p.line_num} in ${p.file}`.grey(15);
342
- if (p.parent) p = p.parent;
343
- } while (p.parent);
344
-
345
- error.message += traceback;
346
-
347
- /* only interested in node stacktrace when debugging */
348
- if (!this.opts.debug) error.stack = "";
349
-
350
- if (this.opts.only_warn) console.error(error);
351
- else throw error;
352
- }
353
- }
354
- }
355
- }
356
-
357
- export function splice(
358
- str: string,
359
- startIndex: number,
360
- width: number,
361
- newSubStr: string
362
- ) {
363
- const start = str.slice(0, startIndex);
364
- const end = str.slice(startIndex + width);
365
- return start + newSubStr + end;
366
- }
367
-
368
- /* add extention to marked for classed blockquotes*/
369
- marked.use({
370
- renderer: {
371
- blockquote(quote) {
372
- /* find the ending, and if not, return the default */
373
- const ending = quote.match(/\{(.+)\}\s*<\/p>/);
374
- if (!ending) return `<blockquote>${quote}</blockquote>`;
375
-
376
- const args = ending[1].split(" ");
377
-
378
- const classes = args.filter((arg) => arg.startsWith("."));
379
- const id = args.filter((arg) => arg.startsWith("#"));
380
-
381
- const classNames = classes.map((c) => c.slice(1));
382
- const classText =
383
- classes.length > 0 ? `class="${classNames.join(" ")}"` : "";
384
- const idText = id.length > 0 ? `id="${id[0].slice(1)}"` : "";
385
-
386
- /* remove the ending from the quote */
387
- quote = quote.replace(/\{(.+)\}\s*<\/p>/, "</p>");
388
-
389
- return `<blockquote ${classText} ${idText}>\n${quote.trim()}</blockquote>`;
390
- },
391
- },
392
- });
393
-
394
- module.exports = Parser;
395
-
396
- export default Parser;
@@ -1,37 +0,0 @@
1
- const util = require("./tester.test.js");
2
-
3
- const putTemplate = () => {
4
- util.put(
5
- "module.exports = {main: (new_template, new_command) => {new_template('hi', 'hello'); new_command(/#test_cmd/, (t,p) => 'yeet', 0);}};",
6
- "extensions.js"
7
- );
8
- };
9
-
10
- describe("Use of templates", () => {
11
- beforeEach(putTemplate);
12
- it("should import presentation template as expected", () => {
13
- putTemplate();
14
- const output = new util.Parser("#mdtemplate<presentation>").get();
15
- 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>`;
16
-
17
- util.assert.strictEqual(output, template + "\n\n");
18
- });
19
-
20
- it("should use custom templates from project extensions.js file", () => {
21
- putTemplate();
22
- util.put("#mdtemplate<hi>", "sample1.md");
23
-
24
- util.assert.strictEqual(
25
- new util.Parser("test/test-files/sample1.md").get(),
26
- "hello\n\n"
27
- );
28
- });
29
-
30
- it("should use custom commands from project extensions.js file", () => {
31
- putTemplate();
32
- util.put("#test_cmd", "sample1.md");
33
-
34
- const parser = new util.Parser("test/test-files/sample1.md");
35
- util.assert.strictEqual(parser.get(), "yeet\n\n");
36
- });
37
- });
@@ -1,67 +0,0 @@
1
- const util = require("./tester.test.js");
2
-
3
- describe("Basic features", () => {
4
- it("should join two files with include", () => {
5
- util.put("hello\n#mdinclude<sample2.md>", "sample1.md");
6
- util.put("there", "sample2.md");
7
-
8
- const parser = new util.Parser("test/test-files/sample1.md");
9
- const output = parser.get();
10
-
11
- util.assert.strictEqual(output, "hello\nthere\n\n");
12
- });
13
- it("should make a table of contents", () => {
14
- const output = new util.Parser(
15
- "# yo\n## bruh nugget\n#mdmaketoc"
16
- ).get();
17
-
18
- util.assert.strictEqual(
19
- output,
20
- "# yo\n## bruh nugget\n* [yo](#yo)\n * [bruh nugget](#bruh-nugget)\n\n"
21
- );
22
- });
23
- it("should allow quotation marks in titles for toc", () => {
24
- const parser = new util.Parser("# mac's farm\n#mdmaketoc");
25
- const markdown = parser.get();
26
-
27
- util.assert.strictEqual(
28
- markdown,
29
- "# mac's farm\n* [mac's farm](#macs-farm)\n\n"
30
- );
31
- });
32
- it("should allow variables in toc", () => {
33
- const output = new util.Parser(
34
- "#mddef<name= Foobar>\n# mr. #mdvar<name>\n#mdmaketoc<>"
35
- ).get();
36
-
37
- util.assert.strictEqual(
38
- output,
39
- "\n# mr. Foobar\n* [mr. Foobar](#mr-foobar)\n\n"
40
- );
41
- });
42
- it("should not exceed max include depth", () => {
43
- util.put("#mdinclude<sample2.md>", "sample1.md");
44
- util.put("yo.md>", "sample2.md");
45
-
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");
56
- });
57
- it("should include file with same name as folder when including a folder", () => {
58
- util.put("#mdinclude<sample_fld>", "sample1.md");
59
- util.putDir("sample_fld");
60
- util.put("hello", util.path.join("sample_fld", "sample_fld.md"));
61
-
62
- const parser = new util.Parser("test/test-files/sample1.md");
63
- const output = parser.get();
64
-
65
- util.assert.strictEqual(output, "hello\n\n");
66
- });
67
- });
@@ -1,51 +0,0 @@
1
- const util = require("./tester.test.js");
2
-
3
- describe("Command Line Arguments", () => {
4
- it("--use-underscore should replace '-' with '_' in toc", () => {
5
- const output = new util.Parser("# foo bar\n#mdmaketoc", {
6
- use_underscore: true,
7
- }).get();
8
-
9
- util.assert.strictEqual(output, "# foo bar\n* [foo bar](#foo_bar)\n\n");
10
- });
11
- it("--toc-level should exclude subsection with lower level", () => {
12
- const output = new util.Parser("# foo bar\n### baz\n#mdmaketoc", {
13
- toc_level: 2,
14
- }).get();
15
-
16
- util.assert.strictEqual(
17
- output,
18
- "# foo bar\n### baz\n* [foo bar](#foo-bar)\n\n"
19
- );
20
- });
21
- it("--allow-undef should not throw when variable is not defined", () => {
22
- const output = new util.Parser("#mdvar<zum>", {
23
- allow_undefined: true,
24
- }).get();
25
-
26
- util.assert.strictEqual(output, "<zum>\n\n");
27
- });
28
- describe("Conditional imports", () => {
29
- it("should be able to conditionally import documents", () => {
30
- util.put("hello\n#mdinclude<sample2.md, YES>", "sample1.md");
31
- util.put("there", "sample2.md");
32
-
33
- const parser = new util.Parser("test/test-files/sample1.md");
34
- parser.opts.args.push("YES");
35
-
36
- const output = parser.get();
37
-
38
- util.assert.strictEqual(output, "hello\nthere\n\n");
39
- });
40
- it("shouldn't include a document when flag is unset", () => {
41
- util.put("hello\n#mdinclude<sample2.md,YES>", "sample1.md");
42
- util.put("there", "sample2.md");
43
-
44
- const parser = new util.Parser("test/test-files/sample1.md");
45
-
46
- const output = parser.get();
47
-
48
- util.assert.strictEqual(output, "hello\n\n");
49
- });
50
- });
51
- });
@@ -1,47 +0,0 @@
1
- const util = require("./tester.test.js");
2
-
3
- describe("Error handling", () => {
4
- it("should dissallow undefined templates", () => {
5
- util.put("#mdtemplate<UNDEF>", "sample1.md");
6
-
7
- const parser = new util.Parser("test/test-files/sample1.md");
8
-
9
- let e;
10
- util.assert.throws(() => {
11
- try {
12
- parser.get();
13
- } catch (_e) {
14
- e = _e;
15
- throw _e;
16
- }
17
- }, Error);
18
-
19
- let answer =
20
- 'Template "UNDEF" not found!' +
21
- "\n...on line 1 in test/test-files/sample1.md".grey(15);
22
-
23
- util.assert.strictEqual(e.message.replace(/(\\)+/g, "/"), answer);
24
- });
25
- it("should dissallow loading a folder without an entry file", () => {
26
- util.put("#mdinclude<sample_fld>", "sample1.md");
27
- util.putDir("sample_fld");
28
-
29
- const parser = new util.Parser("test/test-files/sample1.md");
30
-
31
- let e;
32
- util.assert.throws(() => {
33
- try {
34
- parser.get();
35
- } catch (_e) {
36
- e = _e;
37
- throw _e;
38
- }
39
- }, Error);
40
-
41
- let answer =
42
- 'No entry file found in folder "sample_fld". Looking for "sample_fld.md"' +
43
- "\n...on line 1 in test/test-files/sample1.md".grey(15);
44
-
45
- util.assert.strictEqual(e.message.replace(/(\\)+/g, "/"), answer);
46
- });
47
- });
package/test/hooks.js DELETED
@@ -1,9 +0,0 @@
1
- const fs = require("fs");
2
-
3
- exports.mochaHooks = {
4
- beforeEach(done) {
5
- fs.rmSync("test/test-files", { recursive: true });
6
- fs.mkdirSync("test/test-files");
7
- done();
8
- },
9
- };