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/commands.ts CHANGED
@@ -1,296 +1,290 @@
1
1
  import * as path from "path";
2
- import Parser from "./parse";
2
+ import Parser from "./parser";
3
3
  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.opts,
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
216
- );
217
-
218
- /* basic mdhook */
219
- 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
198
+ /#mdmaketoc(?:<>)?/,
199
+ (match, parser) => parser.get_toc(),
200
+ CommandType.POSTPARSE
227
201
  );
228
202
 
229
203
  /* mdadvhook */
230
204
  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
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
268
262
  );
269
263
 
270
264
  const loaded_extentions: fs.PathLike[] = [];
271
265
 
272
266
  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
- }
267
+ // if (loaded_extentions.includes(file)) return;
268
+ if (fs.existsSync(file)) {
269
+ const extensions = requireRuntime(file);
270
+ loaded_extentions.push(file);
271
+ extensions.main(new_template, new_command);
272
+
273
+ if (parser.opts.verbose)
274
+ console.log(`Loaded extensions from ${file}`.yellow);
275
+ } else if (parser.opts.debug) {
276
+ console.log(`No extensions found at ${file}`.red);
277
+ }
284
278
  }
285
279
 
286
280
  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);
281
+ /* global extention */
282
+ const global_extensions_path = path.join(__dirname, "extensions.js");
283
+ load_extension(parser, global_extensions_path);
290
284
 
291
- /* project extention */
292
- const project_extensions_path = path.join(parser.wd_full, "extensions.js");
293
- load_extension(parser, project_extensions_path);
285
+ /* project extention */
286
+ const project_extensions_path = path.join(parser.wd_full, "extensions.js");
287
+ load_extension(parser, project_extensions_path);
294
288
  }
295
289
 
296
290
  /**
@@ -300,11 +294,11 @@ export function load_extensions(parser: Parser) {
300
294
  * @param type When the command should be run. Can be `CommandType.PREPARSE`, `CommandType.PARSE`, or `CommandType.POSTPARSE`. Defaults to `CommandType.PARSE`.
301
295
  */
302
296
  export function new_command(
303
- regex: RegExp,
304
- acter: (match: RegExpMatchArray, parser: Parser) => string,
305
- type?: CommandType
297
+ regex: RegExp,
298
+ acter: (match: RegExpMatchArray, parser: Parser) => string,
299
+ type?: CommandType
306
300
  ) {
307
- new Command(regex, acter, type || CommandType.PARSE);
301
+ new Command(regex, acter, type || CommandType.PARSE);
308
302
  }
309
303
 
310
304
  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
+ }