markdown-maker 1.9.2 → 1.10.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -12,7 +12,7 @@ jobs:
12
12
 
13
13
  strategy:
14
14
  matrix:
15
- node-version: [12.x, 14.x, 15.x]
15
+ node-version: [16.x, 18.x, 20.x]
16
16
  # See supported Node.js release schedule at https://nodejs.org/en/about/releases/
17
17
 
18
18
  steps:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "markdown-maker",
3
- "version": "1.9.2",
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
- if (clargs.watch) {
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
- const [_, name, condition] = match;
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<([\w\W]+)>/,
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
- export function load_extensions(parser: Parser) {
203
- /* global extention */
204
- const global_extensions_path = path.join(process.cwd(), "extensions.js");
205
- if (fs.existsSync(global_extensions_path)) {
206
- const extensions = requireRuntime(global_extensions_path);
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
- if (fs.existsSync(project_extensions_path)) {
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
- const fs = require("fs"); /* for handling reading of files */
2
- const path = require("path"); /* for handling file paths */
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
- const marked = require("marked");
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)) + beg + `[${title}](#${link})`;
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
- const htmlFileName = bundleName.replace(".md", ".html");
278
- fs.writeFile(htmlFileName, this.html(), () => {
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 traceback += `\n...on line ${p.line_num} in ${p.file}`.grey(15);
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, when debugging */
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 = classes.length > 0 ? `class="${classNames.join(" ")}"` : "";
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 */
@@ -0,0 +1,13 @@
1
+ {
2
+ "opts": {
3
+ "src": "main.md",
4
+ "verbose": false,
5
+ "debug": false,
6
+ "output": "dist/bundle.md",
7
+ "max-depth": 3,
8
+ "toc-level": 3,
9
+ "watch": false,
10
+ "html": false,
11
+ "allow-undefined": false
12
+ }
13
+ }
@@ -1,12 +1,16 @@
1
1
  const util = require("./tester.test.js");
2
2
 
3
- util.put(
4
- "module.exports = {main: (new_template, new_command) => {new_template('hi', 'hello'); new_command(/#test_cmd/, (t,p) => 'yeet', 0);}};",
5
- "extensions.js"
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
  });
@@ -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
  });
@@ -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,9 @@
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
+ };
@@ -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
-
@@ -1,35 +1,43 @@
1
1
  const util = require("./tester.test.js");
2
2
 
3
3
  describe("Target specific functionality", () => {
4
-
5
- describe("HTML", () => {
6
- it("Should include `#mdlabel` command, when compiling HTML", () => {
7
- const parser = new util.Parser("#mdlabel<0,Cool!>");
8
- const html = parser.get(util.TargetType.HTML);
9
-
10
- util.assert.strictEqual(html, '<span id="cool"></span>\n\n')
11
- });
12
-
13
- it("Should link to sections with #mdref", () => {
14
- const parser = new util.Parser("#mdlabel<0,Cool!>\n#mdlabel<1,coolzz>\n#mdref<Cool!>");
15
- const html = parser.get(util.TargetType.HTML);
16
-
17
- util.assert.strictEqual(html, '<span id="cool"></span>\n<span id="coolzz"></span>\n<a href="#cool">Cool!</a>\n\n');
18
- })
19
- });
20
-
21
- describe("Markdown", () => {
22
- it("Should not include `#mdlabel` command, when compiling Markdown", () => {
23
- const parser = new util.Parser("#mdlabel<0,Cool!>");
24
-
25
- const md = parser.get(util.TargetType.MARKDOWN);
26
- util.assert.strictEqual(md, '\n\n')
27
- });
28
- it("Should include #mdref to title elements in markdown", () => {
29
- const output = new util.Parser("# Some Title!\n#mdref<Some Title!>").get();
30
-
31
- util.assert.strictEqual(output, '# Some Title!\n[Some Title!](#some-title)\n\n')
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
+ });
@@ -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/test-files/" + file, text);
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('should replace var with the value', () => {
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('should use variable shorthand', () => {
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('should use variables across files', () => {
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('should throw if undefined variable', () => {
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
- try {
31
- parser.get();
32
- } catch (_e) {
33
- e = _e;
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('should preserve whatever comes after', () => {
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('should replace underscore with space', () => {
49
- const output = new util.Parser("#mddef<name=Mr_Sir>\n#mdvar<name>").get();
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
-
@@ -1,7 +0,0 @@
1
- {
2
- "opts": {
3
- "src": "main.md",
4
- "use-underscore": true,
5
- "html": true
6
- }
7
- }
package/doc/main.md DELETED
@@ -1,2 +0,0 @@
1
- Hello there!
2
- #mdinclude<other.md>
package/doc/other.md DELETED
@@ -1 +0,0 @@
1
- From the other siiiiiide!
package/re-test.js DELETED
@@ -1,10 +0,0 @@
1
- const s = "hello1, hello2, hello3";
2
-
3
- const re = /hello(?<digit>\d)/g;
4
-
5
- let m = s.replace(re, (match, ...args) => {
6
- console.log(args);
7
- return "world";
8
- });
9
-
10
- console.log({ s, m });