markdown-maker 1.7.10 → 1.9.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.
@@ -3,11 +3,7 @@
3
3
 
4
4
  name: Mocha
5
5
 
6
- on:
7
- push:
8
- branches: [ main ]
9
- pull_request:
10
- branches: [ main ]
6
+ on: [push, pull_request]
11
7
 
12
8
  jobs:
13
9
  build:
@@ -25,6 +21,15 @@ jobs:
25
21
  uses: actions/setup-node@v1
26
22
  with:
27
23
  node-version: ${{ matrix.node-version }}
28
- - run: npm ci
29
- - run: npm run build --if-present
30
- - run: npm test
24
+ - name: yarn install
25
+ uses: borales/actions-yarn@v3.0.0
26
+ with:
27
+ cmd: install # will run `yarn install` command
28
+ - name: yarn build
29
+ uses: borales/actions-yarn@v3.0.0
30
+ with:
31
+ cmd: build # will run `yarn build` command
32
+ - name: yarn test
33
+ uses: borales/actions-yarn@v3.0.0
34
+ with:
35
+ cmd: test # will run `yarn test` command
package/README.md CHANGED
@@ -14,6 +14,7 @@ Currently supports the following features
14
14
  * Automatic Table of Contents generation with `#mdmaketoc`
15
15
  * HTML emitting with custom styling
16
16
  * Easy extention of custom commands, see `src/commands.js` for implementations
17
+ * Usage of templates with the `#mdtemplate<...>` command.
17
18
 
18
19
  ## Usage
19
20
  Download the [latest release](https://github.com/blitzher/markdown-maker/releases), and write your document.
@@ -0,0 +1,7 @@
1
+ {
2
+ "opts": {
3
+ "src": "main.md",
4
+ "use-underscore": true,
5
+ "html": true
6
+ }
7
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "markdown-maker",
3
- "version": "1.7.10",
3
+ "version": "1.9.1",
4
4
  "description": "",
5
5
  "main": "src/cltool.ts",
6
6
  "bin": {
@@ -13,8 +13,8 @@
13
13
  "node_modules/colors/*",
14
14
  "node_modules/colors./*",
15
15
  "node_modules/marked/*",
16
- "node_modules/open/*",
17
- "build/cltool.js"
16
+ "build/cltool.js",
17
+ "src/templates/*"
18
18
  ],
19
19
  "targets": [
20
20
  "node12-macos-x64",
@@ -26,7 +26,7 @@
26
26
  "test": "mocha",
27
27
  "test:yarn": "mocha",
28
28
  "bundle": "tsc --project tsconfig.json",
29
- "main": "node build/cltool.js document/main.md -o dist/bundle.md",
29
+ "main": "node build/cltool.js document/main.md -o dist/bundle.md --debug",
30
30
  "debug": "node src/cltool.js -db document/main.md -o dist/bundle.md",
31
31
  "build": "npm run bundle && pkg --output bin/mdparse .",
32
32
  "build:yarn": "yarn bundle && pkg --output bin/mdparse .",
@@ -46,18 +46,20 @@
46
46
  "@types/chokidar": "^2.1.3",
47
47
  "@types/colors": "^1.2.1",
48
48
  "@types/node": "^15.6.1",
49
+ "@types/ws": "^8.5.3",
49
50
  "argparse": "^2.0.1",
50
51
  "chokidar": "^3.5.1",
51
52
  "colors": "^1.4.0",
52
53
  "colors.ts": "^1.0.20",
53
54
  "marked": "^2.0.1",
54
- "open": "^8.0.5"
55
+ "require-runtime": "^2.0.0",
56
+ "ws": "^8.8.1"
55
57
  },
56
58
  "devDependencies": {
57
59
  "cloc": "^2.7.0",
58
60
  "mocha": "^8.3.1",
61
+ "pkg": "^4.4.9",
59
62
  "prettier": "^2.3.0",
60
- "typescript": "^4.3.2",
61
- "pkg": "^4.4.9"
63
+ "typescript": "^4.3.2"
62
64
  }
63
65
  }
package/prettierrc.yaml CHANGED
@@ -1,4 +1,4 @@
1
- trailingComma: es5
1
+ trailingComma: all
2
2
  tabWidth: 4
3
3
  singleQuote: true
4
4
  arrowParens: always
package/re-test.js ADDED
@@ -0,0 +1,10 @@
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 });
package/src/cltool.ts CHANGED
@@ -3,6 +3,7 @@ const path = require("path"); /* for handling file paths */
3
3
 
4
4
  import Colors = require("colors.ts"); /* for adding colours to strings */
5
5
  import Parser from "./parse";
6
+ import { WebSocketServer } from "ws";
6
7
 
7
8
  Colors.enable();
8
9
  const { ArgumentParser } = require("argparse"); /* for parsing clargs */
@@ -11,8 +12,11 @@ const choki = require("chokidar");
11
12
 
12
13
  export const argParser = new ArgumentParser({
13
14
  description: "Markdown bundler, with extra options",
15
+ prog: "mdparse",
14
16
  });
15
17
 
18
+ const configFileName = ".mdmconfig.json";
19
+
16
20
  //#region command line args
17
21
  argParser.add_argument("src", {
18
22
  help: "file to be parsed. If this is a directory, it looks for entry point in the directory, see --entry",
@@ -22,7 +26,7 @@ argParser.add_argument("-v", "--verbose", {
22
26
  action: "store_true",
23
27
  help: "enable verbose output",
24
28
  });
25
- argParser.add_argument("-db", "--debug", {
29
+ argParser.add_argument("-D", "--debug", {
26
30
  action: "store_true",
27
31
  help: "enable debugging information",
28
32
  });
@@ -43,28 +47,50 @@ argParser.add_argument("-w", "--watch", {
43
47
  action: "store_true",
44
48
  help: "recompile after a change in target target file or directory.",
45
49
  });
46
- argParser.add_argument("-uu", "--use-underscore", {
50
+ argParser.add_argument("-u", "--use-underscore", {
47
51
  action: "store_true",
48
52
  help: "set the parser to use '_' as seperator in ids for Table of Content. If the links in the table does not work, this is likely to be the issue.",
49
53
  });
50
- argParser.add_argument("--toc-level", {
54
+ argParser.add_argument("-t", "--toc-level", {
51
55
  help: "the section level of the table of contents, by default is 3",
52
56
  default: 3,
53
57
  type: "int",
54
58
  });
55
- argParser.add_argument("--html", {
59
+ argParser.add_argument("-H", "--html", {
56
60
  action: "store_true",
57
61
  help: "compile HTML from the parsed markdown",
58
62
  });
59
- argParser.add_argument("--allow-undef", "-au", {
63
+ argParser.add_argument("--allow-undefined", "-A", {
60
64
  action: "store_true",
61
- help: "allow undefined variables. Mostly useful for typing inline html tags, and other non-strictly markdown related uses",
65
+ help: "allow the use of the \"<thing>\" syntax, without raising an error when 'thing' is not a variable. Mostly useful when writing inline html tags, and other non-strictly markdown related uses",
62
66
  });
63
67
  //#endregion
64
68
 
65
69
  function main() {
66
- // var server: refreshServer | undefined;
67
- const clargs = argParser.parse_args();
70
+ let clargs, fileargs;
71
+ let server: WebSocketServer | undefined;
72
+
73
+ /* Read config file or parse args from cmd-line */
74
+ if (fs.existsSync(configFileName)) {
75
+ let data: { [key: string]: string | boolean | number } = JSON.parse(fs.readFileSync(configFileName)).opts;
76
+
77
+ let args: (string | number)[] = [];
78
+ Object.entries(data).forEach(([key, value]) => {
79
+ if (key != "src" && value !== false) {
80
+ args.push("--" + key);
81
+ }
82
+ if (typeof value != "boolean") {
83
+ args.push(value);
84
+ }
85
+ });
86
+
87
+ /* We skip [0] and [1], as it is the binary and source file, even when compiled*/
88
+ for (let i = 2; i < process.argv.length; i++) args.push(process.argv[i]);
89
+
90
+ clargs = argParser.parse_args(args);
91
+ } else {
92
+ clargs = argParser.parse_args();
93
+ }
68
94
 
69
95
  /* helper method for calling parser */
70
96
  const compile = (source, output, cb?) => {
@@ -93,7 +119,10 @@ function main() {
93
119
 
94
120
  try {
95
121
  compile(clargs.src, clargs.output, () => {
96
- // if (server.refresh) server.refresh();
122
+ /* after compile, send refresh command to clients */
123
+ server.clients.forEach((client) => {
124
+ if (client.OPEN) client.send("refresh");
125
+ });
97
126
  });
98
127
  } catch (e) {
99
128
  console.log(e.message);
@@ -107,16 +136,16 @@ function main() {
107
136
  clargs.src = path.join(clargs.src, clargs.entry);
108
137
  }
109
138
 
110
- const srcDirName = path.dirname(clargs.src);
111
-
112
139
  if (clargs.debug) console.dir(clargs);
113
140
 
141
+ /* compile once */
114
142
  if (!clargs.watch) compile(clargs.src, clargs.output);
115
143
 
144
+ /* watch the folder and recompile on change */
116
145
  if (clargs.watch) {
117
- /* watch the folder of entry */
118
- // server = wsServer();
146
+ const srcDirName = path.dirname(clargs.src);
119
147
  console.log(`Watching ${srcDirName} for changes...`.yellow);
148
+ server = new WebSocketServer({ port: 7788 });
120
149
 
121
150
  const _watcher = choki.watch(srcDirName).on("all", watcher);
122
151
  try {
package/src/commands.ts CHANGED
@@ -1,36 +1,48 @@
1
1
  import * as path from "path";
2
- import { title } from "process";
3
2
  import Parser from "./parse";
3
+ import * as fs from "fs";
4
+ import templates, { new_template } from "./templates";
5
+ import requireRuntime from "require-runtime";
6
+
7
+ export class MDMError extends Error {
8
+ match: RegExpMatchArray;
9
+ constructor(message: string, match: RegExpMatchArray) {
10
+ super(message);
11
+ this.name = "MDMError";
12
+ this.match = match;
13
+ }
14
+ }
4
15
 
5
- const commands = {
16
+ export const commands: {
17
+ preparse: Command[];
18
+ parse: Command[];
19
+ postparse: Command[];
20
+ } = {
6
21
  preparse: [],
7
22
  parse: [],
8
23
  postparse: [],
9
24
  };
10
25
 
11
- const CommandType = {
12
- PREPARSE: 0,
13
- PARSE: 1,
14
- POSTPARSE: 2,
15
- };
26
+ export enum CommandType {
27
+ PREPARSE,
28
+ PARSE,
29
+ POSTPARSE,
30
+ }
16
31
 
17
- enum TargetType {
32
+ export enum TargetType {
18
33
  HTML,
19
34
  MARKDOWN,
20
35
  }
21
36
 
22
- class Command {
23
- type: number;
24
- validator: (token: string, parser: Parser) => boolean | RegExpMatchArray;
25
- acter: (token: string, parser: Parser) => string | void;
37
+ export class Command {
38
+ validator: RegExp;
39
+ acter: (match: RegExpMatchArray, parser: Parser) => string | void;
40
+ type: CommandType;
26
41
 
27
42
  constructor(
28
- type,
29
- validator: (
30
- token: string,
31
- parser: Parser
32
- ) => boolean | RegExpMatchArray,
33
- acter: (token: string, parser: Parser) => string | void
43
+ validator: RegExp,
44
+ acter: (match: RegExpMatchArray, parser: Parser) => string | void,
45
+ type: CommandType,
34
46
  ) {
35
47
  this.type = type;
36
48
  this.validator = validator;
@@ -50,131 +62,191 @@ class Command {
50
62
  }
51
63
  }
52
64
 
53
- valid(token, parser) {
54
- return this.validator(token, parser);
55
- }
56
-
57
- act(token, parser) {
58
- return this.acter(token, parser);
65
+ act(match, parser) {
66
+ return this.acter(match, parser);
59
67
  }
60
68
  }
61
69
 
62
70
  /* variable shorthand */
63
71
  new Command(
72
+ /(\s|^)<(.+)>/,
73
+ (match, parser) => `${match[1]}#mdvar<${match[2]}>`,
64
74
  CommandType.PREPARSE,
65
- (t, p) => t.match(/(?:\s|^)<\w+>/),
66
- (t, p) => `#mdvar` + t
67
75
  );
68
76
 
69
77
  /* mddef */
70
78
  new Command(
79
+ /#mddef< *(.+?) *= *(.+?) *>/ /* first .+ is lazy so as to not match following spaces */,
80
+ (match, parser) => {
81
+ parser.opts.defs[match[1]] = match[2].replace("_", " ");
82
+ },
71
83
  CommandType.PARSE,
72
- (t, p) => t.match(/^#mddef<(\w+)=(\w+)>/),
73
- (t, p) => {
74
- const m = t.match(/^#mddef<(\w+)=(\w+)>/);
75
- p.opts.defs[m[1]] = m[2];
76
- }
77
84
  );
78
85
 
79
86
  /* mdvar */
80
87
  new Command(
81
- CommandType.PARSE,
82
- (t, p) => t.match(/^#mdvar<(\w+)>/) || t.match(/^<(\w+)>/),
83
- (t, p) => {
84
- const match = t.match(/#mdvar<(\w+)>/);
85
- let value = p.opts.defs[match[1]];
86
- if (!value && !p.opts.allow_undef)
88
+ /#mdvar<(.+?)>/,
89
+ (match, parser) => {
90
+ let value = parser.opts.defs[match[1]];
91
+ if (!value && !parser.opts.allow_undefined)
87
92
  throw new Error(`Undefined variable: ${match[1]}`);
88
- value = value || `<${match[1]}>`;
89
- return t.replace(match[0], value.replace("_", " "));
90
- }
93
+ return (value = value || `<${match[1]}>`);
94
+ },
95
+ CommandType.PARSE,
91
96
  );
92
97
 
93
98
  /** mdinclude */
94
99
  new Command(
95
- CommandType.PARSE,
96
- (t, p) => t.match(/^#mdinclude<([\w.\/-]+)(?:[,\s]+([\w]+))?>/),
97
- (t, p) => {
100
+ /#mdinclude<([\w.\/-]+)(?:[,\s]+([\w]+))?>/,
101
+ (match, parser) => {
98
102
  /* increase the current recursive depth */
99
- p.opts.depth++;
103
+ parser.opts.depth++;
100
104
 
101
- if (p.opts.depth > p.opts.max_depth) {
105
+ if (parser.opts.depth > parser.opts.max_depth) {
102
106
  throw new Error("max depth exceeded!");
103
107
  }
104
108
 
105
109
  /* get the matching group */
106
- const match = t.match(/^#mdinclude<([\w.\/-]+)(?:[,\s]+([\w]+))?>/);
107
-
108
110
  const [_, name, condition] = match;
109
111
 
110
112
  /* implement conditional imports */
111
- if (condition && !p.opts.args.includes(condition)) return;
113
+ if (condition && !parser.opts.args.includes(condition)) return;
112
114
 
113
- const recursiveParser = new Parser(path.join(p.wd, name), p.opts, {
114
- parent: p,
115
- });
115
+ const recursiveParser = new Parser(
116
+ path.join(parser.wd, name),
117
+ parser.opts,
118
+ {
119
+ parent: parser,
120
+ },
121
+ );
116
122
 
117
123
  /* keep the options the same */
118
- recursiveParser.opts = p.opts;
119
- recursiveParser.parent = p;
124
+ recursiveParser.opts = parser.opts;
125
+ recursiveParser.parent = parser;
120
126
 
121
127
  const fileType = path.extname(recursiveParser.file);
122
128
 
123
129
  const blob =
124
130
  fileType === ".md"
125
- ? recursiveParser.get(p.opts.targetType)
131
+ ? recursiveParser.get(parser.opts.targetType)
126
132
  : recursiveParser.raw;
127
133
 
128
- p.opts.depth--;
134
+ parser.opts.depth--;
129
135
  return blob;
130
- }
136
+ },
137
+ CommandType.PARSE,
131
138
  );
132
139
 
140
+ /* mdlabel */
133
141
  new Command(
134
- CommandType.PREPARSE,
135
- (t, p) => t.match(/#mdlabel<(\d+),([\w\W]+)>/),
136
- (t, p) => {
137
- if (p.opts.targetType !== TargetType.HTML) return;
142
+ /#mdlabel<(\d+),\s?(.+)>/,
143
+ (match, parser) => {
144
+ if (parser.opts.targetType !== TargetType.HTML) return "";
138
145
 
139
- const match = t.match(/#mdlabel<([\d]+),([\w\W]+)>/);
140
146
  const level = Number.parseInt(match[1]);
141
147
  const title = match[2];
142
- const link = p.titleId(title);
143
- p.opts.secs.push({ level, title });
148
+ const link = parser.titleId(title);
149
+ parser.opts.secs.push({ level, title });
144
150
  return `<span id="${link}"></span>`;
145
- }
151
+ },
152
+ CommandType.PREPARSE,
146
153
  );
147
154
 
155
+ /* mdref */
148
156
  new Command(
149
- CommandType.PARSE,
150
- (t, p) => t.match(/#mdref<([\w\W]+)>/),
151
-
152
- (t, p) => {
153
- const match = t.match(/#mdref<([\w\W]+)>/);
157
+ /#mdref<(.+)>/,
154
158
 
155
- for (let i = 0; i < p.opts.secs.length; i++) {
156
- let { title } = p.opts.secs[i];
159
+ (match, parser) => {
160
+ for (let i = 0; i < parser.opts.secs.length; i++) {
161
+ let { title } = parser.opts.secs[i];
157
162
  if (title === match[1]) break;
158
163
 
159
- if (i === p.opts.secs.length - 1)
164
+ if (i === parser.opts.secs.length - 1)
160
165
  throw new Error(
161
- `Reference to [${match[1]}] could not be resolved!`
166
+ `Reference to [${match[1]}] could not be resolved!`,
162
167
  );
163
168
  }
164
169
 
165
170
  match[1] = match[1].replace("_", " ");
166
- const link = p.titleId(match[1]);
167
- if (p.opts.targetType === TargetType.HTML)
171
+ const link = parser.titleId(match[1]);
172
+ if (parser.opts.targetType === TargetType.HTML)
168
173
  return `<a href="#${link}">${match[1]}</a>`;
169
- else if (p.opts.targetType === TargetType.MARKDOWN)
174
+ else if (parser.opts.targetType === TargetType.MARKDOWN)
170
175
  return `[${match[1]}](#${link})`;
171
- }
176
+ },
177
+ CommandType.PARSE,
178
+ );
179
+
180
+ /* mdtemplate */
181
+ new Command(
182
+ /#mdtemplate<([\w\W]+)>/,
183
+ (match, parser) => {
184
+ const template = match[1];
185
+ const replacement = templates[template];
186
+
187
+ if (replacement !== undefined) {
188
+ return replacement;
189
+ } else {
190
+ throw new MDMError(`Template \"${template}\" not found!`, match);
191
+ }
192
+ },
193
+ CommandType.PARSE,
172
194
  );
173
195
 
174
196
  new Command(
197
+ /#mdmaketoc(?:<>)?/,
198
+ (match, parser) => parser.gen_toc(),
175
199
  CommandType.POSTPARSE,
176
- (t, p) => t.match(/#mdmaketoc/),
177
- (t, p) => p.gen_toc()
178
200
  );
179
201
 
180
- module.exports = commands;
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);
207
+ extensions.main(new_template, new_command);
208
+
209
+ if (parser.opts.verbose)
210
+ console.log(
211
+ `Loaded global extensions from ${global_extensions_path}`
212
+ .yellow,
213
+ );
214
+ } else if (parser.opts.debug) {
215
+ console.log(
216
+ `No global extensions found at ${global_extensions_path}`.red,
217
+ );
218
+ }
219
+
220
+ /* project extention */
221
+ 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
+ }
236
+ }
237
+
238
+ /**
239
+ *
240
+ * @param regex The regex to match the command
241
+ * @param acter The function called when a match is found. Takes two arguments, `match` and `parser`. `match` is the result of the regex match, and `parser` is the parser instance. The function should return the replacement string.
242
+ * @param type When the command should be run. Can be `CommandType.PREPARSE`, `CommandType.PARSE`, or `CommandType.POSTPARSE`. Defaults to `CommandType.PARSE`.
243
+ */
244
+ export function new_command(
245
+ regex: RegExp,
246
+ acter: (match: RegExpMatchArray, parser: Parser) => string,
247
+ type?: CommandType,
248
+ ) {
249
+ new Command(regex, acter, type || CommandType.PARSE);
250
+ }
251
+
252
+ export default { commands, load_extensions };