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.
@@ -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 });