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/.github/workflows/node.js.yml +21 -26
- package/.vscode/extensions.json +3 -0
- package/.vscode/launch.json +4 -22
- package/.vscode/settings.json +8 -3
- package/.vscode/snippets.code-snippets +6 -8
- package/jest.config.js +7 -0
- package/package.json +41 -64
- package/src/cltool.ts +119 -135
- package/src/commands.ts +238 -244
- package/src/errors.ts +26 -0
- package/src/main.ts +123 -0
- package/src/parser.ts +398 -0
- package/src/templates.ts +5 -1
- package/src/types.ts +33 -0
- package/tests/_test-util.ts +44 -0
- package/tests/advanced.spec.ts +92 -0
- package/tests/basic.spec.ts +68 -0
- package/tests/clargs.spec.ts +50 -0
- package/tests/errors.spec.ts +64 -0
- package/tests/html.spec.ts +23 -0
- package/tests/line.spec.ts +21 -0
- package/tests/marked.spec.ts +40 -0
- package/tests/target.spec.ts +41 -0
- package/tests/vars.spec.ts +45 -0
- package/tsconfig.json +66 -64
- package/prettierrc.yaml +0 -4
- package/src/parse.ts +0 -396
- package/test/advanced.test.js +0 -37
- package/test/basic.test.js +0 -67
- package/test/clargs.test.js +0 -51
- package/test/errors.test.js +0 -47
- package/test/hooks.js +0 -9
- package/test/hooks.test.js +0 -114
- package/test/html.test.js +0 -37
- package/test/line.test.js +0 -21
- package/test/marked.test.js +0 -43
- package/test/target.test.js +0 -43
- package/test/tester.test.js +0 -41
- package/test/vars.test.js +0 -49
package/src/commands.ts
CHANGED
@@ -1,296 +1,290 @@
|
|
1
1
|
import * as path from "path";
|
2
|
-
import Parser from "./
|
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
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
}
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
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
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
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
|
-
|
74
|
-
|
75
|
-
|
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
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
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
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
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
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
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
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
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
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
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
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
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
|
-
|
214
|
-
|
215
|
-
|
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
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
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
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
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
|
-
|
288
|
-
|
289
|
-
|
281
|
+
/* global extention */
|
282
|
+
const global_extensions_path = path.join(__dirname, "extensions.js");
|
283
|
+
load_extension(parser, global_extensions_path);
|
290
284
|
|
291
|
-
|
292
|
-
|
293
|
-
|
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
|
-
|
304
|
-
|
305
|
-
|
297
|
+
regex: RegExp,
|
298
|
+
acter: (match: RegExpMatchArray, parser: Parser) => string,
|
299
|
+
type?: CommandType
|
306
300
|
) {
|
307
|
-
|
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
|
+
}
|