markdown-maker 1.10.1 → 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/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
- };