markdown-maker 1.10.2 → 1.10.4

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.
Files changed (68) hide show
  1. package/.github/workflows/node.js.yml +21 -26
  2. package/.vscode/extensions.json +3 -0
  3. package/.vscode/settings.json +8 -3
  4. package/.vscode/snippets.code-snippets +6 -8
  5. package/bundle/cltool.d.ts +86 -0
  6. package/bundle/cltool.js +63 -0
  7. package/bundle/cltool.js.map +1 -0
  8. package/bundle/commands.d.ts +23 -0
  9. package/bundle/commands.js +303 -0
  10. package/bundle/commands.js.map +1 -0
  11. package/bundle/errors.d.ts +11 -0
  12. package/bundle/errors.js +51 -0
  13. package/bundle/errors.js.map +1 -0
  14. package/bundle/main.d.ts +6 -0
  15. package/bundle/main.js +152 -0
  16. package/bundle/main.js.map +1 -0
  17. package/bundle/parser.d.ts +35 -0
  18. package/bundle/parser.js +330 -0
  19. package/bundle/parser.js.map +1 -0
  20. package/bundle/templates/mathjax.d.ts +2 -0
  21. package/bundle/templates/mathjax.js +3 -0
  22. package/bundle/templates/mathjax.js.map +1 -0
  23. package/bundle/templates/presentation.d.ts +2 -0
  24. package/bundle/templates/presentation.js +3 -0
  25. package/bundle/templates/presentation.js.map +1 -0
  26. package/bundle/templates.d.ts +10 -0
  27. package/bundle/templates.js +23 -0
  28. package/bundle/templates.js.map +1 -0
  29. package/bundle/types.d.ts +28 -0
  30. package/bundle/types.js +15 -0
  31. package/bundle/types.js.map +1 -0
  32. package/bundle/version.d.ts +1 -0
  33. package/bundle/version.js +5 -0
  34. package/bundle/version.js.map +1 -0
  35. package/jest.config.js +7 -0
  36. package/package.json +42 -40
  37. package/src/cltool.ts +115 -72
  38. package/src/commands.ts +257 -241
  39. package/src/errors.ts +26 -0
  40. package/src/main.ts +114 -109
  41. package/src/parser.ts +378 -350
  42. package/src/templates.ts +5 -1
  43. package/src/types.ts +33 -0
  44. package/src/version.ts +1 -0
  45. package/tests/_test-util.ts +44 -0
  46. package/tests/advanced.spec.ts +102 -0
  47. package/tests/basic.spec.ts +68 -0
  48. package/tests/clargs.spec.ts +50 -0
  49. package/tests/errors.spec.ts +64 -0
  50. package/tests/html.spec.ts +23 -0
  51. package/tests/line.spec.ts +21 -0
  52. package/tests/marked.spec.ts +40 -0
  53. package/tests/target.spec.ts +41 -0
  54. package/tests/vars.spec.ts +45 -0
  55. package/tsconfig.json +66 -64
  56. package/prettierrc.yaml +0 -4
  57. package/test/advanced.test.js +0 -34
  58. package/test/basic.test.js +0 -67
  59. package/test/clargs.test.js +0 -51
  60. package/test/errors.test.js +0 -47
  61. package/test/hooks.js +0 -9
  62. package/test/hooks.test.js +0 -114
  63. package/test/html.test.js +0 -37
  64. package/test/line.test.js +0 -21
  65. package/test/marked.test.js +0 -43
  66. package/test/target.test.js +0 -43
  67. package/test/tester.test.js +0 -41
  68. package/test/vars.test.js +0 -49
package/src/commands.ts CHANGED
@@ -4,293 +4,309 @@ import * as fs from "fs";
4
4
  import templates, { new_template } from "./templates";
5
5
  import requireRuntime from "require-runtime";
6
6
  import * as nodeHtmlParser from "node-html-parser";
7
-
8
- export class MDMError extends Error {
9
- match: RegExpMatchArray;
10
- constructor(message: string, match: RegExpMatchArray) {
11
- super(message);
12
- this.name = "MDMError";
13
- this.match = match;
14
- }
15
- }
16
-
17
- export const commands: {
18
- preparse: Command[];
19
- parse: Command[];
20
- postparse: Command[];
21
- } = {
22
- preparse: [],
23
- parse: [],
24
- postparse: [],
7
+ import { HTMLElement } from "node-html-parser";
8
+ import crypto from "crypto";
9
+ import {
10
+ CommandGroupType,
11
+ CommandType,
12
+ TaggedElement,
13
+ TargetType,
14
+ } from "./types";
15
+ import { MDMError } from "./errors";
16
+
17
+ const salt = crypto.randomBytes(2).toString("hex");
18
+
19
+ export const commands: CommandGroupType = {
20
+ preparse: [],
21
+ parse: [],
22
+ postparse: [],
25
23
  };
26
24
 
27
- export enum CommandType {
28
- PREPARSE,
29
- PARSE,
30
- POSTPARSE,
31
- }
32
-
33
- export enum TargetType {
34
- HTML,
35
- MARKDOWN,
36
- }
37
-
38
25
  export class Command {
39
- validator: RegExp;
40
- acter: (match: RegExpMatchArray, parser: Parser) => string | void;
41
- type: CommandType;
42
-
43
- constructor(
44
- validator: RegExp,
45
- acter: (match: RegExpMatchArray, parser: Parser) => string | void,
46
- type: CommandType
47
- ) {
48
- this.type = type;
49
- this.validator = validator;
50
- this.acter = acter;
51
-
52
- /* add this function to appropriate file */
53
- switch (type) {
54
- case CommandType.PARSE:
55
- commands.parse.push(this);
56
- break;
57
- case CommandType.PREPARSE:
58
- commands.preparse.push(this);
59
- break;
60
- case CommandType.POSTPARSE:
61
- commands.postparse.push(this);
62
- break;
63
- }
64
- }
65
-
66
- act(match, parser) {
67
- return this.acter(match, parser);
68
- }
26
+ validator: RegExp;
27
+ acter: (match: RegExpExecArray, parser: Parser) => string | void;
28
+ type: CommandType;
29
+
30
+ constructor(
31
+ validator: RegExp,
32
+ acter: (match: RegExpExecArray, parser: Parser) => string | void,
33
+ type: CommandType
34
+ ) {
35
+ validator = new RegExp(validator.source, validator.flags);
36
+ this.validator = validator;
37
+ this.acter = acter;
38
+ this.type = type;
39
+
40
+ /* add this function to appropriate file */
41
+ switch (type) {
42
+ case CommandType.PARSE:
43
+ commands.parse.push(this);
44
+ break;
45
+ case CommandType.PREPARSE:
46
+ commands.preparse.push(this);
47
+ break;
48
+ case CommandType.POSTPARSE:
49
+ commands.postparse.push(this);
50
+ break;
51
+ }
52
+ }
53
+
54
+ act(match: RegExpExecArray, parser: Parser) {
55
+ return this.acter(match, parser);
56
+ }
69
57
  }
70
58
 
71
59
  /* variable shorthand */
72
60
  new Command(
73
- /(\s|^)<(.+)>/,
74
- (match, parser) => `${match[1]}#mdvar<${match[2]}>`,
75
- CommandType.PREPARSE
61
+ /(\s|^)!<(.+)>/,
62
+ (match, parser) => `${match[1]}#mdvar<${match[2]}>`,
63
+ CommandType.PREPARSE
76
64
  );
77
65
 
78
66
  /* mddef */
79
67
  new Command(
80
- /#mddef< *(.+?) *= *(.+?) *>/ /* first .+ is lazy so as to not match following spaces */,
81
- (match, parser) => {
82
- parser.opts.defs[match[1]] = match[2].replace("_", " ");
83
- },
84
- CommandType.PARSE
68
+ /#mddef< *(.+?) *= *(.+?) *>/ /* first .+ is lazy so as to not match following spaces */,
69
+ (match, parser) => {
70
+ parser.opts.defs[match[1]] = match[2].replace("_", " ");
71
+ },
72
+ CommandType.PARSE
85
73
  );
86
74
 
87
75
  /* mdvar */
88
76
  new Command(
89
- /#mdvar<(.+?)>/,
90
- (match, parser) => {
91
- let value = parser.opts.defs[match[1]];
92
- if (!value && !parser.opts.allow_undefined)
93
- throw new Error(`Undefined variable: ${match[1]}`);
94
- return (value = value || `<${match[1]}>`);
95
- },
96
- CommandType.PARSE
77
+ /#mdvar<(.+?)>/,
78
+ (match, parser) => {
79
+ let value = parser.opts.defs[match[1]];
80
+ if (!value && !parser.opts.allow_undefined)
81
+ throw new MDMError(`Undefined variable: ${match[1]}`, match);
82
+ return (value = value || `<${match[1]}>`);
83
+ },
84
+ CommandType.PARSE
97
85
  );
98
86
 
99
87
  /** mdinclude */
100
88
  new Command(
101
- /#mdinclude<([\w.\/-]+)(?:[,\s]+([\w]+))?>/,
102
- (match, parser) => {
103
- /* increase the current recursive depth */
104
- parser.opts.depth++;
105
-
106
- if (parser.opts.depth > parser.opts.max_depth) {
107
- throw new Error("max depth exceeded!");
108
- }
109
-
110
- /* get the matching group */
111
- let [_, name, condition] = match;
112
-
113
- /* implement conditional imports */
114
- if (condition && !parser.opts.args.includes(condition)) return;
115
-
116
- const fsstat = fs.lstatSync(path.join(parser.wd, name));
117
- if (fsstat.isDirectory()) {
118
- /* check if a file with the same name of the
119
- * exists in the folder */
120
-
121
- if (fs.existsSync(path.join(parser.wd, name, `${name}.md`))) {
122
- name = path.join(name, `${name}.md`);
123
- } else {
124
- throw new Error(
125
- `No entry file found in folder "${name}". Looking for "${name}.md"`
126
- );
127
- }
128
- }
129
-
130
- const recursiveParser = new Parser(
131
- path.join(parser.wd, name),
132
- parser.clargs,
133
- {
134
- parent: parser,
135
- }
136
- );
137
-
138
- /* keep the options the same */
139
- recursiveParser.opts = parser.opts;
140
- recursiveParser.parent = parser;
141
-
142
- const fileType = path.extname(recursiveParser.file);
143
-
144
- const blob =
145
- fileType === ".md"
146
- ? recursiveParser.get(parser.opts.targetType)
147
- : recursiveParser.raw;
148
-
149
- parser.opts.depth--;
150
- return blob;
151
- },
152
- CommandType.PARSE
89
+ /#mdinclude<([\w.\/-]+)(?:[,\s]+([\w]+))?>/,
90
+ (match, parser) => {
91
+ /* increment the current recursion depth */
92
+ parser.opts.depth++;
93
+
94
+ if (parser.opts.depth > parser.opts.max_depth) {
95
+ throw new MDMError("max depth exceeded!", match);
96
+ }
97
+
98
+ /* get the matching group */
99
+ let [_, name, condition] = match;
100
+
101
+ /* implement conditional imports */
102
+ if (condition && !parser.opts.args.includes(condition)) return;
103
+
104
+ const fsstat = fs.lstatSync(path.join(parser.wd, name));
105
+ if (fsstat.isDirectory()) {
106
+ /* check if a file with the same name of the
107
+ * exists in the folder */
108
+
109
+ const entry = path.join(parser.wd, name, `${name}.md`);
110
+ if (fs.existsSync(entry)) {
111
+ name = path.join(name, `${name}.md`);
112
+ } else {
113
+ throw new MDMError(
114
+ `No entry file found in folder "${name}". Looking for "${entry}"`,
115
+ match
116
+ );
117
+ }
118
+ }
119
+
120
+ const recursiveParser = parser.createChild(path.join(parser.wd, name));
121
+
122
+ /* keep the options the same */
123
+ recursiveParser.opts = parser.opts;
124
+ recursiveParser.parent = parser;
125
+
126
+ const fileType = path.extname(recursiveParser.file);
127
+
128
+ const blob =
129
+ fileType === ".md"
130
+ ? recursiveParser.get(parser.opts.targetType)
131
+ : recursiveParser.raw;
132
+
133
+ parser.opts.depth--;
134
+ return blob;
135
+ },
136
+ CommandType.PARSE
153
137
  );
154
138
 
155
139
  /* mdlabel */
156
140
  new Command(
157
- /#mdlabel<(\d+),\s?(.+)>/,
158
- (match, parser) => {
159
- if (parser.opts.targetType !== TargetType.HTML) return "";
160
-
161
- const level = Number.parseInt(match[1]);
162
- const title = match[2];
163
- const link = parser.titleId(title);
164
- parser.opts.secs.push({ level, title });
165
- return `<span id="${link}"></span>`;
166
- },
167
- CommandType.PREPARSE
141
+ /#mdlabel<(\d+),\s?(.+)>/,
142
+ (match, parser) => {
143
+ if (parser.opts.targetType !== TargetType.HTML) return "";
144
+
145
+ const level = Number.parseInt(match[1]);
146
+ const title = match[2];
147
+ const link = parser.titleId(title);
148
+ parser.opts.secs.push({ level, title });
149
+ return `<span id="${link}"></span>`;
150
+ },
151
+ CommandType.PREPARSE
168
152
  );
169
153
 
170
154
  /* mdref */
171
155
  new Command(
172
- /#mdref<(.+)>/,
173
-
174
- (match, parser) => {
175
- for (let i = 0; i < parser.opts.secs.length; i++) {
176
- let { title } = parser.opts.secs[i];
177
- if (title === match[1]) break;
178
-
179
- if (i === parser.opts.secs.length - 1)
180
- throw new Error(
181
- `Reference to [${match[1]}] could not be resolved!`
182
- );
183
- }
184
-
185
- match[1] = match[1].replace("_", " ");
186
- const link = parser.titleId(match[1]);
187
- if (parser.opts.targetType === TargetType.HTML)
188
- return `<a href="#${link}">${match[1]}</a>`;
189
- else if (parser.opts.targetType === TargetType.MARKDOWN)
190
- return `[${match[1]}](#${link})`;
191
- },
192
- CommandType.PARSE
156
+ /#mdref<(.+)>/,
157
+
158
+ (match, parser) => {
159
+ for (let i = 0; i < parser.opts.secs.length; i++) {
160
+ let { title } = parser.opts.secs[i];
161
+ if (title === match[1]) break;
162
+
163
+ if (i === parser.opts.secs.length - 1)
164
+ throw new MDMError(
165
+ `Reference to [${match[1]}] could not be resolved!`,
166
+ match
167
+ );
168
+ }
169
+
170
+ match[1] = match[1].replace("_", " ");
171
+ const link = parser.titleId(match[1]);
172
+ if (parser.opts.targetType === TargetType.HTML)
173
+ return `<a href="#${link}">${match[1]}</a>`;
174
+ else if (parser.opts.targetType === TargetType.MARKDOWN)
175
+ return `[${match[1]}](#${link})`;
176
+ },
177
+ CommandType.PARSE
193
178
  );
194
179
 
195
180
  /* mdtemplate */
196
181
  new Command(
197
- /#mdtemplate<(\w+?)>/,
198
- (match, parser) => {
199
- const template = match[1];
200
- const replacement = templates[template];
201
-
202
- if (replacement !== undefined) {
203
- return replacement;
204
- } else {
205
- throw new MDMError(`Template \"${template}\" not found!`, match);
206
- }
207
- },
208
- CommandType.PARSE
182
+ /#mdtemplate<(\w+?)>/,
183
+ (match, parser) => {
184
+ const template = match[1];
185
+ const replacement = templates[template];
186
+
187
+ if (replacement !== undefined) {
188
+ return replacement;
189
+ } else {
190
+ throw new MDMError(`Template \"${template}\" not found!`, match);
191
+ }
192
+ },
193
+ CommandType.PARSE
209
194
  );
210
195
 
211
196
  /* mdmaketoc */
212
197
  new Command(
213
- /#mdmaketoc(?:<>)?/,
214
- (match, parser) => parser.gen_toc(),
215
- CommandType.POSTPARSE
198
+ /#mdmaketoc(?:<>)?/,
199
+ (match, parser) => parser.get_toc(),
200
+ CommandType.POSTPARSE
216
201
  );
217
202
 
218
- /* basic mdhook */
203
+ /* mdadvhook */
219
204
  new Command(
220
- /#mdhook<(\w+)>/,
221
- (match, parser) => {
222
- if (parser.opts.hooks[match[1]]) {
223
- return parser.opts.hooks[match[1]]();
224
- }
225
- },
226
- CommandType.POSTPARSE
205
+ /\#mdhook<(\w+)>([\w\W]+)\#mdendhook<\1>/m,
206
+ (match, parser) => {
207
+ if (!parser.opts.hooks[match[1]])
208
+ throw new MDMError(`No advanced hook found for ${match[1]}`, match);
209
+
210
+ const innerElements = match[2].trim();
211
+
212
+ /* Run the inner elements through the parser itself */
213
+ const innerParser = parser.createChild(innerElements);
214
+
215
+ /* parse the inner */
216
+ let innerBlob = innerParser.get(parser.opts.targetType).trim();
217
+
218
+ /* Find all tagged elements in inner */
219
+ /* and construct helper map */
220
+ const re = /<(\w+)::(\w+)([\s=\w\"\'-]*)>/g;
221
+ const map: { [key: string]: TaggedElement } = {};
222
+ for (let match of innerBlob.matchAll(re)) {
223
+ const args = Array.from(match[3].matchAll(/(\w+)=["'](.+?)["']/g));
224
+ const argsMap = Object.fromEntries(args.map((x) => x.slice(1)));
225
+ map[match[2]] = {
226
+ "html-tag": match[1],
227
+ "var-tag": match[2],
228
+ node: new nodeHtmlParser.HTMLElement(match[1], {}),
229
+ args: Object.fromEntries(
230
+ match[3]
231
+ .trim()
232
+ .split(" ")
233
+ .map((x) => x.split("="))
234
+ ),
235
+ _raw: match[0],
236
+ };
237
+ }
238
+
239
+ /* Replace tagged elements in inner blob with their uuids */
240
+ for (let taggedElement of Object.values(map)) {
241
+ innerBlob = innerBlob.replace(
242
+ taggedElement._raw,
243
+ salt + taggedElement["var-tag"] + salt
244
+ );
245
+ }
246
+
247
+ /* Run the hook on the map*/
248
+ parser.opts.hooks[match[1]](map);
249
+
250
+ /* Replace the tagged elements with the modified nodes */
251
+ let output = innerBlob;
252
+ for (let taggedElement of Object.values(map)) {
253
+ output = output.replace(
254
+ salt + taggedElement["var-tag"] + salt,
255
+ taggedElement.node.toString()
256
+ );
257
+ }
258
+
259
+ return output;
260
+ },
261
+ CommandType.POSTPARSE
227
262
  );
228
263
 
229
- /* mdadvhook */
230
264
  new Command(
231
- /\#mdadvhook<(\w+)>([\w\W]+?)\#mdendhook/,
232
- (match, parser) => {
233
- if (parser.opts.adv_hooks[match[1]]) {
234
- const innerElements = match[2].trim();
235
- /* Find tagNames in innerElements */
236
- const re = /<(\w+)[\s=\w\"\'-]*>/g;
237
- const tags = [];
238
- innerElements.match(re)?.forEach((tag) => {
239
- tags.push(tag.slice(1, -1).split(" ")[0]);
240
- });
241
- /* Evil type hack */
242
- const root = nodeHtmlParser.parse(innerElements, {
243
- voidTag: {
244
- tags,
245
- closingSlash: true,
246
- },
247
- }) as any as HTMLElement;
248
-
249
- const helper = (node: HTMLElement) => {
250
- /* */
251
- const map: { [tag: string]: HTMLElement } = {};
252
- for (let tag of tags) {
253
- const el = node.getElementsByTagName(tag)[0];
254
- const dataTag = el.toString().match(/data-tag="([\w-]+)"/);
255
- if (!dataTag || dataTag[1] == undefined) continue;
256
- el.tagName = dataTag[1];
257
- el.removeAttribute("data-tag");
258
- map[tag] = el;
259
- }
260
- return map;
261
- };
262
-
263
- const hooked = parser.opts.adv_hooks[match[1]](root, helper(root));
264
- return hooked.toString();
265
- }
266
- },
267
- CommandType.POSTPARSE
265
+ /<(\w+)([\#\.\w]+)\n([\w\W]*?)>/,
266
+ (match, _) => {
267
+ const tag = match[1];
268
+ const tagline = match[2];
269
+ const id = tagline.match(/#(\w+)/)
270
+ ? `id="${tagline.match(/#(\w+)/)[1]}"`
271
+ : "";
272
+ const cls = tagline.match(/\.(.+)/);
273
+ let cls_str = "";
274
+ if (cls) {
275
+ cls_str =
276
+ cls.length > 1 ? `class="${cls[1].replace(".", " ")}"` : "";
277
+ }
278
+
279
+ const content = match[3];
280
+
281
+ return `<${tag} ${id} ${cls_str}>${content.trim()}</${tag}>`;
282
+ },
283
+ CommandType.POSTPARSE
268
284
  );
269
285
 
270
286
  const loaded_extentions: fs.PathLike[] = [];
271
287
 
272
288
  function load_extension(parser: Parser, file: fs.PathLike) {
273
- if (loaded_extentions.includes(file)) return;
274
- if (fs.existsSync(file)) {
275
- const extensions = requireRuntime(file);
276
- loaded_extentions.push(file);
277
- extensions.main(new_template, new_command);
278
-
279
- if (parser.opts.verbose)
280
- console.log(`Loaded extensions from ${file}`.yellow);
281
- } else if (parser.opts.debug) {
282
- console.log(`No extensions found at ${file}`.red);
283
- }
289
+ // if (loaded_extentions.includes(file)) return;
290
+ if (fs.existsSync(file)) {
291
+ const extensions = requireRuntime(file);
292
+ loaded_extentions.push(file);
293
+ extensions.main(new_template, new_command);
294
+
295
+ if (parser.opts.verbose)
296
+ console.log(`Loaded extensions from ${file}`.yellow);
297
+ } else if (parser.opts.debug) {
298
+ console.log(`No extensions found at ${file}`.red);
299
+ }
284
300
  }
285
301
 
286
302
  export function load_extensions(parser: Parser) {
287
- /* global extention */
288
- const global_extensions_path = path.join(__dirname, "extensions.js");
289
- load_extension(parser, global_extensions_path);
303
+ /* global extention */
304
+ const global_extensions_path = path.join(__dirname, "extensions.js");
305
+ load_extension(parser, global_extensions_path);
290
306
 
291
- /* project extention */
292
- const project_extensions_path = path.join(parser.wd_full, "extensions.js");
293
- load_extension(parser, project_extensions_path);
307
+ /* project extention */
308
+ const project_extensions_path = path.join(parser.wd_full, "extensions.js");
309
+ load_extension(parser, project_extensions_path);
294
310
  }
295
311
 
296
312
  /**
@@ -300,11 +316,11 @@ export function load_extensions(parser: Parser) {
300
316
  * @param type When the command should be run. Can be `CommandType.PREPARSE`, `CommandType.PARSE`, or `CommandType.POSTPARSE`. Defaults to `CommandType.PARSE`.
301
317
  */
302
318
  export function new_command(
303
- regex: RegExp,
304
- acter: (match: RegExpMatchArray, parser: Parser) => string,
305
- type?: CommandType
319
+ regex: RegExp,
320
+ acter: (match: RegExpMatchArray, parser: Parser) => string,
321
+ type?: CommandType
306
322
  ) {
307
- new Command(regex, acter, type || CommandType.PARSE);
323
+ new Command(regex, acter, type || CommandType.PARSE);
308
324
  }
309
325
 
310
326
  export default { commands, load_extensions };
package/src/errors.ts ADDED
@@ -0,0 +1,26 @@
1
+ export class MDMNonParserError extends Error {
2
+ constructor(message: string) {
3
+ super(message);
4
+ Object.setPrototypeOf(this, MDMNonParserError.prototype);
5
+ }
6
+ }
7
+
8
+ export class MDMError extends Error {
9
+ match: RegExpMatchArray;
10
+ constructor(message: string, match: RegExpMatchArray) {
11
+ super(message);
12
+ Object.setPrototypeOf(this, MDMError.prototype);
13
+
14
+ this.match = match;
15
+ }
16
+ }
17
+
18
+ export class MDMWarning extends Error {
19
+ match?: RegExpMatchArray;
20
+ constructor(message: string, match?: RegExpMatchArray) {
21
+ super(message);
22
+ Object.setPrototypeOf(this, MDMWarning.prototype);
23
+
24
+ this.match = match;
25
+ }
26
+ }