markdown-maker 1.9.2 → 1.10.1
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 +1 -1
- package/package.json +3 -2
- package/src/cltool.ts +11 -3
- package/src/commands.ts +98 -40
- package/src/parse.ts +45 -24
- package/src/templates/configTemplate.json +13 -0
- package/test/advanced.test.js +10 -8
- package/test/basic.test.js +10 -0
- package/test/errors.test.js +22 -1
- package/test/hooks.js +9 -0
- package/test/hooks.test.js +114 -0
- package/test/line.test.js +2 -4
- package/test/target.test.js +40 -32
- package/test/tester.test.js +15 -4
- package/test/vars.test.js +22 -30
- package/doc/.mdmconfig.json +0 -7
- package/doc/main.md +0 -2
- package/doc/other.md +0 -1
- package/re-test.js +0 -10
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "markdown-maker",
|
3
|
-
"version": "1.
|
3
|
+
"version": "1.10.1",
|
4
4
|
"description": "",
|
5
5
|
"main": "src/cltool.ts",
|
6
6
|
"bin": {
|
@@ -23,7 +23,7 @@
|
|
23
23
|
]
|
24
24
|
},
|
25
25
|
"scripts": {
|
26
|
-
"test": "mocha",
|
26
|
+
"test": "mocha --require test/hooks.js",
|
27
27
|
"test:yarn": "mocha",
|
28
28
|
"bundle": "tsc --project tsconfig.json",
|
29
29
|
"main": "node build/cltool.js document/main.md -o dist/bundle.md --debug",
|
@@ -52,6 +52,7 @@
|
|
52
52
|
"colors": "^1.4.0",
|
53
53
|
"colors.ts": "^1.0.20",
|
54
54
|
"marked": "^2.0.1",
|
55
|
+
"node-html-parser": "^6.1.13",
|
55
56
|
"require-runtime": "^2.0.0",
|
56
57
|
"ws": "^8.8.1"
|
57
58
|
},
|
package/src/cltool.ts
CHANGED
@@ -11,7 +11,7 @@ const { version } = require("../package.json"); /* package version number */
|
|
11
11
|
const choki = require("chokidar");
|
12
12
|
|
13
13
|
export const argParser = new ArgumentParser({
|
14
|
-
description: "Markdown bundler, with extra options",
|
14
|
+
description: "Markdown bundler, with extra options. Extension file is loaded from ./extensions.js, if it exists",
|
15
15
|
prog: "mdparse",
|
16
16
|
});
|
17
17
|
|
@@ -92,6 +92,14 @@ function main() {
|
|
92
92
|
clargs = argParser.parse_args();
|
93
93
|
}
|
94
94
|
|
95
|
+
/* if src is init, create config file and exit */
|
96
|
+
if (clargs.src == "init") {
|
97
|
+
const template = fs.readFileSync(path.join(__dirname, "..", "src", "templates", "configTemplate.json"));
|
98
|
+
fs.writeFileSync(configFileName, template);
|
99
|
+
fs.writeFileSync("main.md", "# Main\n");
|
100
|
+
return;
|
101
|
+
}
|
102
|
+
|
95
103
|
/* helper method for calling parser */
|
96
104
|
const compile = (source, output, cb?) => {
|
97
105
|
/* load data from file, if it exists,
|
@@ -140,9 +148,8 @@ function main() {
|
|
140
148
|
|
141
149
|
/* compile once */
|
142
150
|
if (!clargs.watch) compile(clargs.src, clargs.output);
|
143
|
-
|
144
151
|
/* watch the folder and recompile on change */
|
145
|
-
|
152
|
+
else {
|
146
153
|
const srcDirName = path.dirname(clargs.src);
|
147
154
|
console.log(`Watching ${srcDirName} for changes...`.yellow);
|
148
155
|
server = new WebSocketServer({ port: 7788 });
|
@@ -154,6 +161,7 @@ function main() {
|
|
154
161
|
console.log(e.message);
|
155
162
|
}
|
156
163
|
}
|
164
|
+
|
157
165
|
}
|
158
166
|
|
159
167
|
/* main entrypoint */
|
package/src/commands.ts
CHANGED
@@ -3,6 +3,7 @@ import Parser from "./parse";
|
|
3
3
|
import * as fs from "fs";
|
4
4
|
import templates, { new_template } from "./templates";
|
5
5
|
import requireRuntime from "require-runtime";
|
6
|
+
import * as nodeHtmlParser from "node-html-parser";
|
6
7
|
|
7
8
|
export class MDMError extends Error {
|
8
9
|
match: RegExpMatchArray;
|
@@ -42,7 +43,7 @@ export class Command {
|
|
42
43
|
constructor(
|
43
44
|
validator: RegExp,
|
44
45
|
acter: (match: RegExpMatchArray, parser: Parser) => string | void,
|
45
|
-
type: CommandType
|
46
|
+
type: CommandType
|
46
47
|
) {
|
47
48
|
this.type = type;
|
48
49
|
this.validator = validator;
|
@@ -71,7 +72,7 @@ export class Command {
|
|
71
72
|
new Command(
|
72
73
|
/(\s|^)<(.+)>/,
|
73
74
|
(match, parser) => `${match[1]}#mdvar<${match[2]}>`,
|
74
|
-
CommandType.PREPARSE
|
75
|
+
CommandType.PREPARSE
|
75
76
|
);
|
76
77
|
|
77
78
|
/* mddef */
|
@@ -80,7 +81,7 @@ new Command(
|
|
80
81
|
(match, parser) => {
|
81
82
|
parser.opts.defs[match[1]] = match[2].replace("_", " ");
|
82
83
|
},
|
83
|
-
CommandType.PARSE
|
84
|
+
CommandType.PARSE
|
84
85
|
);
|
85
86
|
|
86
87
|
/* mdvar */
|
@@ -92,7 +93,7 @@ new Command(
|
|
92
93
|
throw new Error(`Undefined variable: ${match[1]}`);
|
93
94
|
return (value = value || `<${match[1]}>`);
|
94
95
|
},
|
95
|
-
CommandType.PARSE
|
96
|
+
CommandType.PARSE
|
96
97
|
);
|
97
98
|
|
98
99
|
/** mdinclude */
|
@@ -107,17 +108,31 @@ new Command(
|
|
107
108
|
}
|
108
109
|
|
109
110
|
/* get the matching group */
|
110
|
-
|
111
|
+
let [_, name, condition] = match;
|
111
112
|
|
112
113
|
/* implement conditional imports */
|
113
114
|
if (condition && !parser.opts.args.includes(condition)) return;
|
114
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
|
+
|
115
130
|
const recursiveParser = new Parser(
|
116
131
|
path.join(parser.wd, name),
|
117
132
|
parser.opts,
|
118
133
|
{
|
119
134
|
parent: parser,
|
120
|
-
}
|
135
|
+
}
|
121
136
|
);
|
122
137
|
|
123
138
|
/* keep the options the same */
|
@@ -134,7 +149,7 @@ new Command(
|
|
134
149
|
parser.opts.depth--;
|
135
150
|
return blob;
|
136
151
|
},
|
137
|
-
CommandType.PARSE
|
152
|
+
CommandType.PARSE
|
138
153
|
);
|
139
154
|
|
140
155
|
/* mdlabel */
|
@@ -149,7 +164,7 @@ new Command(
|
|
149
164
|
parser.opts.secs.push({ level, title });
|
150
165
|
return `<span id="${link}"></span>`;
|
151
166
|
},
|
152
|
-
CommandType.PREPARSE
|
167
|
+
CommandType.PREPARSE
|
153
168
|
);
|
154
169
|
|
155
170
|
/* mdref */
|
@@ -163,7 +178,7 @@ new Command(
|
|
163
178
|
|
164
179
|
if (i === parser.opts.secs.length - 1)
|
165
180
|
throw new Error(
|
166
|
-
`Reference to [${match[1]}] could not be resolved
|
181
|
+
`Reference to [${match[1]}] could not be resolved!`
|
167
182
|
);
|
168
183
|
}
|
169
184
|
|
@@ -174,12 +189,12 @@ new Command(
|
|
174
189
|
else if (parser.opts.targetType === TargetType.MARKDOWN)
|
175
190
|
return `[${match[1]}](#${link})`;
|
176
191
|
},
|
177
|
-
CommandType.PARSE
|
192
|
+
CommandType.PARSE
|
178
193
|
);
|
179
194
|
|
180
195
|
/* mdtemplate */
|
181
196
|
new Command(
|
182
|
-
/#mdtemplate<(
|
197
|
+
/#mdtemplate<(\w+?)>/,
|
183
198
|
(match, parser) => {
|
184
199
|
const template = match[1];
|
185
200
|
const replacement = templates[template];
|
@@ -190,49 +205,92 @@ new Command(
|
|
190
205
|
throw new MDMError(`Template \"${template}\" not found!`, match);
|
191
206
|
}
|
192
207
|
},
|
193
|
-
CommandType.PARSE
|
208
|
+
CommandType.PARSE
|
194
209
|
);
|
195
210
|
|
211
|
+
/* mdmaketoc */
|
196
212
|
new Command(
|
197
213
|
/#mdmaketoc(?:<>)?/,
|
198
214
|
(match, parser) => parser.gen_toc(),
|
199
|
-
CommandType.POSTPARSE
|
215
|
+
CommandType.POSTPARSE
|
200
216
|
);
|
201
217
|
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
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
|
227
|
+
);
|
228
|
+
|
229
|
+
/* mdadvhook */
|
230
|
+
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
|
268
|
+
);
|
269
|
+
|
270
|
+
const loaded_extentions: fs.PathLike[] = [];
|
271
|
+
|
272
|
+
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);
|
207
277
|
extensions.main(new_template, new_command);
|
208
278
|
|
209
279
|
if (parser.opts.verbose)
|
210
|
-
console.log(
|
211
|
-
`Loaded global extensions from ${global_extensions_path}`
|
212
|
-
.yellow,
|
213
|
-
);
|
280
|
+
console.log(`Loaded extensions from ${file}`.yellow);
|
214
281
|
} else if (parser.opts.debug) {
|
215
|
-
console.log(
|
216
|
-
`No global extensions found at ${global_extensions_path}`.red,
|
217
|
-
);
|
282
|
+
console.log(`No extensions found at ${file}`.red);
|
218
283
|
}
|
284
|
+
}
|
285
|
+
|
286
|
+
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);
|
219
290
|
|
220
291
|
/* project extention */
|
221
292
|
const project_extensions_path = path.join(parser.wd_full, "extensions.js");
|
222
|
-
|
223
|
-
const extensions = requireRuntime(project_extensions_path);
|
224
|
-
extensions.main(new_template, new_command);
|
225
|
-
|
226
|
-
if (parser.opts.verbose)
|
227
|
-
console.log(
|
228
|
-
`Loaded project extensions from ${project_extensions_path}`
|
229
|
-
.yellow,
|
230
|
-
);
|
231
|
-
} else if (parser.opts.debug) {
|
232
|
-
console.log(
|
233
|
-
`No project extensions found at ${project_extensions_path}!`.red,
|
234
|
-
);
|
235
|
-
}
|
293
|
+
load_extension(parser, project_extensions_path);
|
236
294
|
}
|
237
295
|
|
238
296
|
/**
|
@@ -244,7 +302,7 @@ export function load_extensions(parser: Parser) {
|
|
244
302
|
export function new_command(
|
245
303
|
regex: RegExp,
|
246
304
|
acter: (match: RegExpMatchArray, parser: Parser) => string,
|
247
|
-
type?: CommandType
|
305
|
+
type?: CommandType
|
248
306
|
) {
|
249
307
|
new Command(regex, acter, type || CommandType.PARSE);
|
250
308
|
}
|
package/src/parse.ts
CHANGED
@@ -1,9 +1,10 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
import fs from "fs"; /* for handling reading of files */
|
2
|
+
import path from "path"; /* for handling file paths */
|
3
3
|
|
4
4
|
import Colors = require("colors.ts"); /* for adding colours to strings */
|
5
5
|
Colors.enable();
|
6
|
-
|
6
|
+
import marked from "marked";
|
7
|
+
|
7
8
|
import { Command, commands, load_extensions, MDMError } from "./commands";
|
8
9
|
|
9
10
|
enum TargetType {
|
@@ -43,6 +44,13 @@ class Parser {
|
|
43
44
|
targetType: TargetType | undefined;
|
44
45
|
only_warn: boolean;
|
45
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
|
+
};
|
46
54
|
isFileCallback: (s: string) => false | string;
|
47
55
|
};
|
48
56
|
raw: string;
|
@@ -89,6 +97,8 @@ class Parser {
|
|
89
97
|
targetType: undefined,
|
90
98
|
only_warn: false,
|
91
99
|
parent: undefined,
|
100
|
+
hooks: {},
|
101
|
+
adv_hooks: {},
|
92
102
|
isFileCallback: (f) => {
|
93
103
|
if (!fs.existsSync(f)) return false;
|
94
104
|
return fs.readFileSync(f, "utf-8") + "\n";
|
@@ -237,13 +247,27 @@ class Parser {
|
|
237
247
|
const link = this.titleId(title);
|
238
248
|
|
239
249
|
let __line =
|
240
|
-
hor.repeat(Math.max(sec.level - 1, 0)) +
|
250
|
+
hor.repeat(Math.max(sec.level - 1, 0)) +
|
251
|
+
beg +
|
252
|
+
`[${title}](#${link})`;
|
241
253
|
|
242
254
|
__blob.push(__line);
|
243
255
|
});
|
244
256
|
return __blob.join("\n");
|
245
257
|
}
|
246
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
|
+
|
247
271
|
line_num_from_index(index: number) {
|
248
272
|
return this.raw.substring(0, index).split("\n").length + 1;
|
249
273
|
}
|
@@ -258,27 +282,21 @@ class Parser {
|
|
258
282
|
/* output the parsed document to bundle */
|
259
283
|
to(bundleName: string, callback: (fileName: string) => void) {
|
260
284
|
const dir = path.dirname(bundleName);
|
261
|
-
var called = false;
|
262
285
|
if (callback === undefined) callback = () => {};
|
263
286
|
|
264
287
|
if (!fs.existsSync(dir)) {
|
265
288
|
fs.mkdirSync(dir, { recursive: true });
|
266
289
|
}
|
267
|
-
this.get(TargetType.MARKDOWN, (blob) => {
|
268
|
-
fs.writeFile(bundleName, blob, () => {
|
269
|
-
if (!this.opts.html) {
|
270
|
-
callback(bundleName);
|
271
|
-
called = true;
|
272
|
-
}
|
273
|
-
});
|
274
|
-
});
|
275
290
|
|
276
|
-
if (this.opts.html) {
|
277
|
-
|
278
|
-
|
279
|
-
if (!called) callback(htmlFileName);
|
280
|
-
called = true;
|
291
|
+
if (!this.opts.html) {
|
292
|
+
this.get(TargetType.MARKDOWN, (blob) => {
|
293
|
+
fs.writeFile(bundleName, blob, () => callback(bundleName));
|
281
294
|
});
|
295
|
+
} else {
|
296
|
+
const htmlFileName = bundleName.replace(".md", ".html");
|
297
|
+
fs.writeFile(htmlFileName, this.html(), () =>
|
298
|
+
callback(htmlFileName)
|
299
|
+
);
|
282
300
|
}
|
283
301
|
}
|
284
302
|
|
@@ -293,7 +311,7 @@ class Parser {
|
|
293
311
|
return htmlFormatted;
|
294
312
|
}
|
295
313
|
|
296
|
-
get(targetType?: TargetType, callback
|
314
|
+
get(targetType?: TargetType, callback?: (blob: string) => void): string {
|
297
315
|
/* If target type is undefined, markdown is the default */
|
298
316
|
if (targetType === undefined) targetType = TargetType.MARKDOWN;
|
299
317
|
if (this.blobs[targetType]) {
|
@@ -309,8 +327,8 @@ class Parser {
|
|
309
327
|
if (callback) callback(blob);
|
310
328
|
return blob;
|
311
329
|
} catch (error) {
|
330
|
+
/* Compile a traceback of error */
|
312
331
|
let traceback = "";
|
313
|
-
|
314
332
|
let p: Parser = this;
|
315
333
|
|
316
334
|
do {
|
@@ -318,13 +336,15 @@ class Parser {
|
|
318
336
|
traceback += `\n...on line ${p.line_num_from_index(
|
319
337
|
error.match.index
|
320
338
|
)} in ${p.file}`.grey(15);
|
321
|
-
else
|
339
|
+
else
|
340
|
+
traceback +=
|
341
|
+
`\n...on line ${p.line_num} in ${p.file}`.grey(15);
|
322
342
|
if (p.parent) p = p.parent;
|
323
343
|
} while (p.parent);
|
324
344
|
|
325
345
|
error.message += traceback;
|
326
346
|
|
327
|
-
/* only interested in stacktrace
|
347
|
+
/* only interested in node stacktrace when debugging */
|
328
348
|
if (!this.opts.debug) error.stack = "";
|
329
349
|
|
330
350
|
if (this.opts.only_warn) console.error(error);
|
@@ -345,7 +365,7 @@ export function splice(
|
|
345
365
|
return start + newSubStr + end;
|
346
366
|
}
|
347
367
|
|
348
|
-
/* add extention to marked */
|
368
|
+
/* add extention to marked for classed blockquotes*/
|
349
369
|
marked.use({
|
350
370
|
renderer: {
|
351
371
|
blockquote(quote) {
|
@@ -359,7 +379,8 @@ marked.use({
|
|
359
379
|
const id = args.filter((arg) => arg.startsWith("#"));
|
360
380
|
|
361
381
|
const classNames = classes.map((c) => c.slice(1));
|
362
|
-
const classText =
|
382
|
+
const classText =
|
383
|
+
classes.length > 0 ? `class="${classNames.join(" ")}"` : "";
|
363
384
|
const idText = id.length > 0 ? `id="${id[0].slice(1)}"` : "";
|
364
385
|
|
365
386
|
/* remove the ending from the quote */
|
package/test/advanced.test.js
CHANGED
@@ -1,12 +1,16 @@
|
|
1
1
|
const util = require("./tester.test.js");
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
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
|
+
};
|
7
9
|
|
8
10
|
describe("Use of templates", () => {
|
11
|
+
beforeEach(putTemplate);
|
9
12
|
it("should import presentation template as expected", () => {
|
13
|
+
putTemplate();
|
10
14
|
const output = new util.Parser("#mdtemplate<presentation>").get();
|
11
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>`;
|
12
16
|
|
@@ -14,22 +18,20 @@ describe("Use of templates", () => {
|
|
14
18
|
});
|
15
19
|
|
16
20
|
it("should use custom templates from project extensions.js file", () => {
|
21
|
+
putTemplate();
|
17
22
|
util.put("#mdtemplate<hi>", "sample1.md");
|
18
23
|
|
19
24
|
util.assert.strictEqual(
|
20
25
|
new util.Parser("test/test-files/sample1.md").get(),
|
21
26
|
"hello\n\n"
|
22
27
|
);
|
23
|
-
|
24
|
-
/* make sure to remove after, to not mess with future tests */
|
25
28
|
});
|
26
29
|
|
27
30
|
it("should use custom commands from project extensions.js file", () => {
|
31
|
+
putTemplate();
|
28
32
|
util.put("#test_cmd", "sample1.md");
|
29
33
|
|
30
34
|
const parser = new util.Parser("test/test-files/sample1.md");
|
31
35
|
util.assert.strictEqual(parser.get(), "yeet\n\n");
|
32
|
-
|
33
|
-
/* make sure to remove after, to not mess with future tests */
|
34
36
|
});
|
35
37
|
});
|
package/test/basic.test.js
CHANGED
@@ -54,4 +54,14 @@ describe("Basic features", () => {
|
|
54
54
|
|
55
55
|
util.assert.strictEqual(parser.get(), "### Title\n[Title](#title)\n\n");
|
56
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
|
+
});
|
57
67
|
});
|
package/test/errors.test.js
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
const { Parser } = require("marked");
|
2
1
|
const util = require("./tester.test.js");
|
3
2
|
|
4
3
|
describe("Error handling", () => {
|
@@ -23,4 +22,26 @@ describe("Error handling", () => {
|
|
23
22
|
|
24
23
|
util.assert.strictEqual(e.message.replace(/(\\)+/g, "/"), answer);
|
25
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
|
+
});
|
26
47
|
});
|
package/test/hooks.js
ADDED
@@ -0,0 +1,114 @@
|
|
1
|
+
const util = require("./tester.test.js");
|
2
|
+
|
3
|
+
describe("Use of markdown hooks for SSR", () => {
|
4
|
+
it("should allow a simple hook to be used", () => {
|
5
|
+
util.put("#mdhook<test>", "sample1.md");
|
6
|
+
|
7
|
+
const parser = new util.Parser("test/test-files/sample1.md");
|
8
|
+
parser.add_hook("test", () => "hello");
|
9
|
+
const output = parser.get();
|
10
|
+
|
11
|
+
util.assert.strictEqual(output, "hello\n\n");
|
12
|
+
});
|
13
|
+
it("should allow advanced hooks to be used", () => {
|
14
|
+
util.put(
|
15
|
+
"#mdadvhook<test>\n<b>Bold</b>\n<p>Paragraph</p>\n#mdendhook",
|
16
|
+
"sample1.md"
|
17
|
+
);
|
18
|
+
|
19
|
+
const replacer = (arg) => {
|
20
|
+
const elem = new util.html.HTMLElement("p", {});
|
21
|
+
elem.set_content("complete");
|
22
|
+
return elem;
|
23
|
+
};
|
24
|
+
|
25
|
+
const parser = new util.Parser("test/test-files/sample1.md");
|
26
|
+
parser.opts.allow_undefined = true;
|
27
|
+
parser.add_adv_hook("test", replacer);
|
28
|
+
const output = parser.get();
|
29
|
+
|
30
|
+
util.assert.strictEqual(output, "<p>complete</p>\n\n");
|
31
|
+
});
|
32
|
+
it("should allow for hooks to be used in HTML", () => {
|
33
|
+
util.put("<html><body>#mdhook<test></body></html>", "sample1.html");
|
34
|
+
|
35
|
+
const parser = new util.Parser("test/test-files/sample1.html");
|
36
|
+
parser.opts.allow_undefined = true;
|
37
|
+
parser.add_hook("test", () => "hello");
|
38
|
+
const output = parser.get();
|
39
|
+
|
40
|
+
util.assert.strictEqual(output, "<html><body>hello</body></html>\n\n");
|
41
|
+
});
|
42
|
+
it("should allow for hooks to be used in HTML with advanced hooks", () => {
|
43
|
+
util.put(
|
44
|
+
"<html><body>#mdadvhook<test>\n<b>Bold</b>\n<p>Paragraph</p>\n#mdendhook</body></html>",
|
45
|
+
"sample1.html"
|
46
|
+
);
|
47
|
+
|
48
|
+
const replacer = (arg) => {
|
49
|
+
const elem = new util.html.HTMLElement("p", {});
|
50
|
+
elem.set_content("complete");
|
51
|
+
return elem;
|
52
|
+
};
|
53
|
+
|
54
|
+
const parser = new util.Parser("test/test-files/sample1.html");
|
55
|
+
parser.opts.allow_undefined = true;
|
56
|
+
parser.add_adv_hook("test", replacer);
|
57
|
+
const output = parser.get();
|
58
|
+
|
59
|
+
util.assert.strictEqual(
|
60
|
+
output,
|
61
|
+
"<html><body><p>complete</p></body></html>\n\n"
|
62
|
+
);
|
63
|
+
});
|
64
|
+
it("should allow for extracting a node from the document as a template manually with ids", () => {
|
65
|
+
util.put(
|
66
|
+
`<html><body>#mdadvhook<template><name id="b"></name><class id="p"></class>#mdendhook</body></html>`,
|
67
|
+
"sample1.html"
|
68
|
+
);
|
69
|
+
|
70
|
+
const replacer = (elem) => {
|
71
|
+
const nameElem = elem.getElementsByTagName("name")[0];
|
72
|
+
nameElem.tagName = nameElem.id;
|
73
|
+
nameElem.removeAttribute("id");
|
74
|
+
nameElem.set_content("bold");
|
75
|
+
const classElem = elem.getElementsByTagName("class")[0];
|
76
|
+
classElem.tagName = classElem.id;
|
77
|
+
classElem.removeAttribute("id");
|
78
|
+
classElem.set_content("paragraph");
|
79
|
+
return elem;
|
80
|
+
};
|
81
|
+
|
82
|
+
const parser = new util.Parser("test/test-files/sample1.html");
|
83
|
+
parser.opts.allow_undefined = true;
|
84
|
+
parser.add_adv_hook("template", replacer);
|
85
|
+
const output = parser.get();
|
86
|
+
|
87
|
+
util.assert.strictEqual(
|
88
|
+
output,
|
89
|
+
"<html><body><b>bold</b><p>paragraph</p></body></html>\n\n"
|
90
|
+
);
|
91
|
+
});
|
92
|
+
it("should allow for extracting a node from the document as a template using map and data-tags", () => {
|
93
|
+
util.put(
|
94
|
+
`<html><body>#mdadvhook<template><name data-tag="b"></name><class data-tag="p"></class>#mdendhook</body></html>`,
|
95
|
+
"sample1.html"
|
96
|
+
);
|
97
|
+
|
98
|
+
const replacer = (elem, map) => {
|
99
|
+
map["name"].set_content("bold");
|
100
|
+
map["class"].set_content("paragraph");
|
101
|
+
return elem;
|
102
|
+
};
|
103
|
+
|
104
|
+
const parser = new util.Parser("test/test-files/sample1.html");
|
105
|
+
parser.opts.allow_undefined = true;
|
106
|
+
parser.add_adv_hook("template", replacer);
|
107
|
+
const output = parser.get();
|
108
|
+
|
109
|
+
util.assert.strictEqual(
|
110
|
+
output,
|
111
|
+
"<html><body><b>bold</b><p>paragraph</p></body></html>\n\n"
|
112
|
+
);
|
113
|
+
});
|
114
|
+
});
|
package/test/line.test.js
CHANGED
@@ -3,7 +3,7 @@ const util = require("./tester.test");
|
|
3
3
|
describe("Managing blank lines", () => {
|
4
4
|
it("should always end with 2 blank lines, even with no input", () => {
|
5
5
|
const output = new util.Parser("").get();
|
6
|
-
util.assert.strictEqual(output, "\n\n")
|
6
|
+
util.assert.strictEqual(output, "\n\n");
|
7
7
|
});
|
8
8
|
|
9
9
|
it("should reduce blank lines to 2", () => {
|
@@ -16,8 +16,6 @@ describe("Managing blank lines", () => {
|
|
16
16
|
|
17
17
|
it("should allow words when removing blank lines", () => {
|
18
18
|
const output = new util.Parser("hii\n\n\n").get();
|
19
|
-
util.assert.strictEqual(output, "hii\n\n")
|
19
|
+
util.assert.strictEqual(output, "hii\n\n");
|
20
20
|
});
|
21
21
|
});
|
22
|
-
|
23
|
-
|
package/test/target.test.js
CHANGED
@@ -1,35 +1,43 @@
|
|
1
1
|
const util = require("./tester.test.js");
|
2
2
|
|
3
3
|
describe("Target specific functionality", () => {
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
4
|
+
describe("HTML", () => {
|
5
|
+
it("Should include `#mdlabel` command, when compiling HTML", () => {
|
6
|
+
const parser = new util.Parser("#mdlabel<0,Cool!>");
|
7
|
+
const html = parser.get(util.TargetType.HTML);
|
8
|
+
|
9
|
+
util.assert.strictEqual(html, '<span id="cool"></span>\n\n');
|
10
|
+
});
|
11
|
+
|
12
|
+
it("Should link to sections with #mdref", () => {
|
13
|
+
const parser = new util.Parser(
|
14
|
+
"#mdlabel<0,Cool!>\n#mdlabel<1,coolzz>\n#mdref<Cool!>"
|
15
|
+
);
|
16
|
+
const html = parser.get(util.TargetType.HTML);
|
17
|
+
|
18
|
+
util.assert.strictEqual(
|
19
|
+
html,
|
20
|
+
'<span id="cool"></span>\n<span id="coolzz"></span>\n<a href="#cool">Cool!</a>\n\n'
|
21
|
+
);
|
22
|
+
});
|
23
|
+
});
|
24
|
+
|
25
|
+
describe("Markdown", () => {
|
26
|
+
it("Should not include `#mdlabel` command, when compiling Markdown", () => {
|
27
|
+
const parser = new util.Parser("#mdlabel<0,Cool!>");
|
28
|
+
|
29
|
+
const md = parser.get(util.TargetType.MARKDOWN);
|
30
|
+
util.assert.strictEqual(md, "\n\n");
|
31
|
+
});
|
32
|
+
it("Should include #mdref to title elements in markdown", () => {
|
33
|
+
const output = new util.Parser(
|
34
|
+
"# Some Title!\n#mdref<Some Title!>"
|
35
|
+
).get();
|
36
|
+
|
37
|
+
util.assert.strictEqual(
|
38
|
+
output,
|
39
|
+
"# Some Title!\n[Some Title!](#some-title)\n\n"
|
40
|
+
);
|
41
|
+
});
|
42
|
+
});
|
43
|
+
});
|
package/test/tester.test.js
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
const fs = require("fs");
|
2
2
|
const assert = require("assert");
|
3
3
|
const Parser = require("../build/parse");
|
4
|
-
|
5
|
-
|
4
|
+
const path = require("path");
|
5
|
+
const html = require("node-html-parser");
|
6
6
|
|
7
7
|
/* make folder for temporary files, if it doesn't exist */
|
8
8
|
if (
|
@@ -12,8 +12,16 @@ if (
|
|
12
12
|
fs.mkdirSync("test/test-files");
|
13
13
|
}
|
14
14
|
|
15
|
+
/**
|
16
|
+
* Create a new file under `test/test-files` with the given content.
|
17
|
+
* @param {string} text
|
18
|
+
* @param {string} file
|
19
|
+
*/
|
15
20
|
function put(text, file) {
|
16
|
-
fs.writeFileSync("test
|
21
|
+
fs.writeFileSync(path.join("test", "test-files", file), text);
|
22
|
+
}
|
23
|
+
function putDir(name) {
|
24
|
+
fs.mkdirSync(path.join("test", "test-files", name));
|
17
25
|
}
|
18
26
|
|
19
27
|
const TargetType = {
|
@@ -22,9 +30,12 @@ const TargetType = {
|
|
22
30
|
};
|
23
31
|
|
24
32
|
module.exports = {
|
25
|
-
fs,
|
26
33
|
assert,
|
34
|
+
fs,
|
35
|
+
html,
|
36
|
+
path,
|
27
37
|
Parser,
|
28
38
|
put,
|
39
|
+
putDir,
|
29
40
|
TargetType,
|
30
41
|
};
|
package/test/vars.test.js
CHANGED
@@ -1,57 +1,49 @@
|
|
1
|
-
const util = require("./tester.test");
|
1
|
+
const util = require("./tester.test.js");
|
2
2
|
|
3
3
|
describe("Use variables", () => {
|
4
|
-
it(
|
4
|
+
it("should replace var with the value", () => {
|
5
5
|
const output = new util.Parser("#mddef<hi=yo>\n#mdvar<hi>").get();
|
6
6
|
|
7
|
-
util.assert.strictEqual(output, "\nyo\n\n")
|
7
|
+
util.assert.strictEqual(output, "\nyo\n\n");
|
8
8
|
});
|
9
|
-
it(
|
9
|
+
it("should use variable shorthand", () => {
|
10
10
|
const output = new util.Parser("#mddef<hi=yo>\n<hi>").get();
|
11
11
|
|
12
|
-
util.assert.strictEqual(output, "\nyo\n\n")
|
12
|
+
util.assert.strictEqual(output, "\nyo\n\n");
|
13
13
|
});
|
14
|
-
it(
|
14
|
+
it("should use variables across files", () => {
|
15
15
|
util.put("#mddef<hi=yo>\n#mdinclude<sample2.md>", "sample1.md");
|
16
16
|
util.put("<hi>", "sample2.md");
|
17
17
|
|
18
18
|
const output = new util.Parser("test/test-files/sample1.md").get();
|
19
19
|
|
20
|
-
util.assert.strictEqual(output, "\nyo\n\n")
|
21
|
-
})
|
22
|
-
it(
|
23
|
-
|
20
|
+
util.assert.strictEqual(output, "\nyo\n\n");
|
21
|
+
});
|
22
|
+
it("should throw if undefined variable", () => {
|
24
23
|
const parser = new util.Parser("<yo>");
|
25
24
|
|
26
25
|
let e;
|
27
26
|
/* should throw an error */
|
28
|
-
util.assert.throws(
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
throw _e;
|
35
|
-
}
|
27
|
+
util.assert.throws(() => {
|
28
|
+
try {
|
29
|
+
parser.get();
|
30
|
+
} catch (_e) {
|
31
|
+
e = _e;
|
32
|
+
throw _e;
|
36
33
|
}
|
37
|
-
);
|
38
|
-
|
39
|
-
|
40
|
-
|
34
|
+
});
|
41
35
|
});
|
42
36
|
|
43
|
-
it(
|
37
|
+
it("should preserve whatever comes after", () => {
|
44
38
|
const output = new util.Parser("#mddef<hi=yo>\n<hi>,").get();
|
45
39
|
util.assert.strictEqual(output, "\nyo,\n\n");
|
46
40
|
});
|
47
41
|
|
48
|
-
it(
|
49
|
-
const output = new util.Parser(
|
42
|
+
it("should replace underscore with space", () => {
|
43
|
+
const output = new util.Parser(
|
44
|
+
"#mddef<name=Mr_Sir>\n#mdvar<name>"
|
45
|
+
).get();
|
50
46
|
|
51
|
-
util.assert.strictEqual(output, "\nMr Sir\n\n")
|
47
|
+
util.assert.strictEqual(output, "\nMr Sir\n\n");
|
52
48
|
});
|
53
|
-
|
54
49
|
});
|
55
|
-
|
56
|
-
|
57
|
-
|
package/doc/.mdmconfig.json
DELETED
package/doc/main.md
DELETED
package/doc/other.md
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
From the other siiiiiide!
|