flinker-markdown 1.0.0 → 1.1.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/README.md CHANGED
@@ -1,13 +1,58 @@
1
1
  ## Intro
2
2
  __FlinkerMD__ (MD) is a TypeScript library for parsing markdown text into html.
3
3
 
4
- See demo:
5
- [](https://wiki.archlinux.org/title/Installation_guide)
6
4
 
7
5
  ## Install
8
6
  ```cli
9
7
  npm i flinker-markdown
10
8
  ```
11
9
 
10
+ ## Example 1
11
+ ```ts
12
+ import { md, MDGrammar, MDParser } from "flinker-markdown"
13
+ import { div, TextProps } from "flinker-dom"
14
+
15
+ interface MarkdownProps extends TextProps {
16
+ absolutePathPrefix?: string
17
+ showRawText?: boolean
18
+ }
19
+
20
+ const grammar = new MDGrammar()
21
+ const parser = new MDParser(grammar)
22
+ export const Markdown = () => {
23
+ return div<MarkdownProps>()
24
+ .map(s => {
25
+ if (!s.showRawText) {
26
+ s.htmlText = s.text ? md(parser, s.text, s.absolutePathPrefix) : ''
27
+ s.text = ''
28
+ }
29
+ })
30
+ }
31
+ ```
32
+
33
+ ## Example 2: Custom grammar rule
34
+ Parsing Bash code:
35
+ ```ts
36
+ const grammar = new MDGrammar()
37
+ const bash = new MDLineGrammarRule()
38
+ bash.matcher = [/^>>> /, '> '] // begin text with symbols >>> to tranform line to bash code
39
+ bash.postProccessing = highlightBashCode // define highlight-func: (v: string) => string
40
+ grammar.globalRule.childrenLineRules.splice(0, 0, bash)
41
+ grammar.ol.childrenLineRules.splice(0, 0, bash) // allow lists to include bash code
42
+ grammar.ul.childrenLineRules.splice(0, 0, bash)
43
+
44
+ const parser = new MDParser(grammar)
45
+ export const Markdown = () => {...}
46
+ ```
47
+
48
+ highlightBashCode-func transforms text from e.g.:
49
+ ```html
50
+ >>> npm run install
51
+ ```
52
+ to the tokenized form:
53
+ ```html
54
+ <pre class="bashcode-dark"><code class="md-dark"><span class="op">&gt; </span><span class="bash-cmd">npm </span><span class="bash-param">run </span><span class="bash-param">install</span></code></pre>
55
+ ```
56
+
12
57
  ## License
13
58
  MIT
package/dist/esm/index.js CHANGED
@@ -1,3 +1 @@
1
- export { StyleSheetProcessor, PseudoClass } from "./processor";
2
- export { buildClassName, buildRule } from "./core";
3
- export { UIComponent, vstack, vstackMapper, hstack, hstackMapper, Text, div, p, span, h1, h2, h3, h4, h5, h6, Button, btn, LinkBtn, link, switcher, observer, List, vlist, hlist, spacer, Image, image, Input, input, textarea } from "./components";
1
+ export { md, MDInlineGrammarRule, MDLineGrammarRule, MDMultilineGrammarRule, GlobalGrammarRule, MDGrammar, MDParser } from "./md";
package/dist/esm/md.js ADDED
@@ -0,0 +1,256 @@
1
+ export class MDInlineGrammarRule {
2
+ constructor() {
3
+ this.matcher = [new RegExp(''), ''];
4
+ this.childrenInlineRules = [];
5
+ }
6
+ }
7
+ export class MDLineGrammarRule {
8
+ constructor() {
9
+ this.matcher = [new RegExp(''), ''];
10
+ this.childrenInlineRules = [];
11
+ }
12
+ }
13
+ export class GlobalGrammarRule {
14
+ constructor() {
15
+ this.childrenMultilineRules = [];
16
+ this.childrenLineRules = [];
17
+ this.childrenInlineRules = [];
18
+ }
19
+ }
20
+ export class MDMultilineGrammarRule {
21
+ constructor() {
22
+ this.startMatcher = [new RegExp(''), ''];
23
+ this.endMatcher = [new RegExp(''), ''];
24
+ this.childrenMultilineRules = [];
25
+ this.childrenLineRules = [];
26
+ this.childrenInlineRules = [];
27
+ }
28
+ }
29
+ export class MDGrammar {
30
+ constructor() {
31
+ this.globalRule = new GlobalGrammarRule();
32
+ const defLinePreproccessing = (v) => {
33
+ let res = v;
34
+ res = res.replace(/</g, '&lt;');
35
+ //character escaping
36
+ res = res.replace(/\\`/gm, '&#x60;');
37
+ res = res.replace(/\\#/gm, '&#x23;');
38
+ res = res.replace(/\\_/g, '&#x5f;');
39
+ return res;
40
+ };
41
+ //
42
+ // INLINE GRAMMAR RULES
43
+ //
44
+ this.sup = new MDInlineGrammarRule();
45
+ this.sup.matcher = [/\^\{([^}]+)\}/g, '<sup>$1</sup>'];
46
+ this.sub = new MDInlineGrammarRule();
47
+ this.sub.matcher = [/_\{([^}]+)\}/g, '<sub>$1</sub>'];
48
+ this.strong = new MDInlineGrammarRule();
49
+ this.strong.matcher = [/\*([^*]+)\*/g, '<strong>$1</strong>'];
50
+ this.boldItalic = new MDInlineGrammarRule();
51
+ this.boldItalic.matcher = [/_{3,}([^_]+)_{3,}/g, '<b><i>$1</i></b>'];
52
+ this.bold = new MDInlineGrammarRule();
53
+ this.bold.matcher = [/_{2,}([^_]+)_{2,}/g, '<b>$1</b>'];
54
+ this.italic = new MDInlineGrammarRule();
55
+ this.italic.matcher = [/_([^_]+)_/g, '<i>$1</i>'];
56
+ this.em = new MDInlineGrammarRule();
57
+ this.em.matcher = [/`([^`]+)`/g, '<em>$1</em>'];
58
+ this.em.childrenInlineRules = [this.strong, this.bold, this.italic];
59
+ this.code = new MDInlineGrammarRule();
60
+ this.code.matcher = [/``([^`]+)``/g, '<code>$1</code>'];
61
+ this.figure = new MDInlineGrammarRule();
62
+ this.figure.matcher = [/!\[([^\]]+)\]\(([^)]+)\)\(([^)]+)\)/gm, '<figure><img alt="$1" src="$2"/><figcaption>$3</figcaption></figure>'];
63
+ this.img = new MDInlineGrammarRule();
64
+ this.img.matcher = [/!\[([^\]]+)\]\(([^)]+)\)/gm, '<img alt="$1" src="$2"/>'];
65
+ this.link = new MDInlineGrammarRule();
66
+ this.link.matcher = [/\[([^\]]*)\]\(([^)]+)\)/, (line, descr, url) => {
67
+ return '<a href="' + url + '">' + (descr || url) + '</a>';
68
+ }];
69
+ this.globalRule.childrenInlineRules = [this.code, this.figure, this.img, this.link, this.sub, this.sup, this.strong, this.boldItalic, this.bold, this.em, this.italic];
70
+ //
71
+ // LINE GRAMMAR RULES
72
+ //
73
+ this.header = new MDLineGrammarRule();
74
+ this.header.matcher = [/^\$?(#{1,6}) (.*)$/, (line, signs, header) => {
75
+ const count = signs.length;
76
+ return '<h' + count + '>' + header + '</h' + count + '>';
77
+ }];
78
+ this.header.childrenInlineRules = [this.strong, this.boldItalic, this.bold, this.italic];
79
+ this.header.preProccessing = defLinePreproccessing;
80
+ this.quote = new MDLineGrammarRule();
81
+ this.quote.matcher = [/^> (.*)$/, '<blockquote><p>$1</p></blockquote>'];
82
+ this.quote.childrenInlineRules = this.globalRule.childrenInlineRules;
83
+ this.quote.preProccessing = defLinePreproccessing;
84
+ this.alignRight = new MDLineGrammarRule();
85
+ this.alignRight.matcher = [/^==> (.*)$/, '<div class="md-right">$1</div>'];
86
+ this.alignRight.childrenInlineRules = this.globalRule.childrenInlineRules;
87
+ this.alignRight.preProccessing = defLinePreproccessing;
88
+ this.alignCenter = new MDLineGrammarRule();
89
+ this.alignCenter.matcher = [/^=> (.*)$/, '<div class="md-center">$1</div>'];
90
+ this.alignCenter.childrenInlineRules = this.globalRule.childrenInlineRules;
91
+ this.alignCenter.preProccessing = defLinePreproccessing;
92
+ this.stars = new MDLineGrammarRule();
93
+ this.stars.matcher = [/^(\*{3,})/, '<p class="md-delim">$1</p>'];
94
+ this.p = new MDLineGrammarRule();
95
+ this.p.matcher = [/^(.*)$/, '<p>$1</p>'];
96
+ this.p.childrenInlineRules = this.globalRule.childrenInlineRules;
97
+ this.p.preProccessing = defLinePreproccessing;
98
+ this.br = new MDLineGrammarRule();
99
+ this.br.matcher = [/^\n$/, '<br/>'];
100
+ this.oli = new MDLineGrammarRule();
101
+ this.oli.matcher = [/^\d+\. (.*)$/, '<li>$1</li>'];
102
+ this.oli.childrenInlineRules = this.globalRule.childrenInlineRules;
103
+ this.oli.preProccessing = defLinePreproccessing;
104
+ this.uli = new MDLineGrammarRule();
105
+ this.uli.matcher = [/^\+ (.*)$/, '<li>$1</li>'];
106
+ this.uli.childrenInlineRules = this.globalRule.childrenInlineRules;
107
+ this.uli.preProccessing = defLinePreproccessing;
108
+ this.globalRule.childrenLineRules = [this.header, this.quote, this.alignCenter, this.alignRight, this.stars, this.br, this.p];
109
+ //
110
+ // MULTILINE GRAMMAR RULES
111
+ //
112
+ this.ol = new MDMultilineGrammarRule();
113
+ this.ul = new MDMultilineGrammarRule();
114
+ this.ol.startMatcher = [/^```ol *$/, '<ol>'];
115
+ this.ol.endMatcher = [/^``` *$/, '</ol>'];
116
+ this.ol.childrenLineRules = [this.oli, this.br, this.p];
117
+ this.ol.childrenInlineRules = this.globalRule.childrenInlineRules;
118
+ this.ol.childrenMultilineRules = [this.ol, this.ul];
119
+ this.ul.startMatcher = [/^```ul *$/, '<ul>'];
120
+ this.ul.endMatcher = [/^``` *$/, '</ul>'];
121
+ this.ul.childrenLineRules = [this.uli, this.br, this.p];
122
+ this.ul.childrenInlineRules = this.globalRule.childrenInlineRules;
123
+ this.ul.childrenMultilineRules = [this.ol, this.ul];
124
+ this.table = new MDMultilineGrammarRule();
125
+ this.table.startMatcher = [/^```tbl *$/, '<table>'];
126
+ this.table.endMatcher = [/^``` *$/, '</table>'];
127
+ const tableRow = new MDLineGrammarRule();
128
+ tableRow.matcher = [/^(.*)$/, (line) => {
129
+ return '<tr>' + line.split(/,(?! )/).map(v => '<td>' + v + '</td>').join('') + '</tr>';
130
+ }];
131
+ tableRow.childrenInlineRules = this.globalRule.childrenInlineRules;
132
+ tableRow.preProccessing = defLinePreproccessing;
133
+ this.table.childrenLineRules = [tableRow];
134
+ this.div = new MDMultilineGrammarRule();
135
+ this.div.startMatcher = [/^```([a-zA-Z]+) */, '<div class="$1"><div>'];
136
+ this.div.endMatcher = [/^``` *$/, '</div></div>'];
137
+ this.div.childrenInlineRules = this.globalRule.childrenInlineRules;
138
+ this.div.childrenLineRules = [this.quote, this.alignCenter, this.alignRight, this.br, this.p];
139
+ this.div.childrenMultilineRules = [this.div];
140
+ this.globalRule.childrenMultilineRules = [this.ol, this.ul, this.table, this.div];
141
+ }
142
+ }
143
+ class MDParserContext {
144
+ constructor(text) {
145
+ this._rowIndex = -1;
146
+ this.rows = text.split('\n');
147
+ }
148
+ nextRow() {
149
+ this._rowIndex++;
150
+ return this._rowIndex < this.rows.length ? this.rows[this._rowIndex] : undefined;
151
+ }
152
+ }
153
+ export class MDParser {
154
+ constructor(grammar) {
155
+ this.globalRule = grammar.globalRule;
156
+ }
157
+ run(text) {
158
+ const ctx = new MDParserContext(text);
159
+ return this.parseMultiline(ctx, this.globalRule);
160
+ }
161
+ parseMultiline(ctx, rule) {
162
+ let res = '';
163
+ let exactlyMatched = false;
164
+ while (true) {
165
+ let row = ctx.nextRow();
166
+ if (row === undefined)
167
+ break;
168
+ if (!row)
169
+ row = '\n';
170
+ exactlyMatched = false;
171
+ if (!(rule instanceof GlobalGrammarRule) && row.match(rule.endMatcher[0])) {
172
+ res += row.replace(rule.endMatcher[0], rule.endMatcher[1]);
173
+ break;
174
+ }
175
+ for (let i = 0; i < rule.childrenMultilineRules.length; i++) {
176
+ const r = rule.childrenMultilineRules[i];
177
+ if (row.match(r.startMatcher[0])) {
178
+ exactlyMatched = true;
179
+ res += row.replace(r.startMatcher[0], r.startMatcher[1]);
180
+ res += this.parseMultiline(ctx, r);
181
+ break;
182
+ }
183
+ }
184
+ if (exactlyMatched)
185
+ continue;
186
+ for (let i = 0; i < rule.childrenLineRules.length; i++) {
187
+ const r = rule.childrenLineRules[i];
188
+ if (row.match(r.matcher[0])) {
189
+ exactlyMatched = true;
190
+ let line = r.preProccessing ? r.preProccessing(row) : row;
191
+ line = line.replace(r.matcher[0], r.matcher[1]);
192
+ line = this.parseLine(line, r.childrenInlineRules);
193
+ if (r.postProccessing)
194
+ line = r.postProccessing(line);
195
+ res += line;
196
+ break;
197
+ }
198
+ }
199
+ if (exactlyMatched)
200
+ continue;
201
+ console.warn('Line is not handled by any rule, line: <', row, '>', 'parent rule:', rule);
202
+ res += this.parseLine(row, rule.childrenInlineRules);
203
+ }
204
+ if (rule.postProccessing)
205
+ res = rule.postProccessing(res);
206
+ return res;
207
+ }
208
+ parseLine(text, inlineRules) {
209
+ if (!text || inlineRules.length === 0)
210
+ return text;
211
+ if (inlineRules.length === 1) {
212
+ return text.replace(inlineRules[0].matcher[0], inlineRules[0].matcher[1]);
213
+ }
214
+ const buffer = [];
215
+ let rules = [...inlineRules];
216
+ let value = text;
217
+ while (value) {
218
+ const candidateRules = [];
219
+ let matchedRule;
220
+ let matchedRuleMinSearchIndex = value.length;
221
+ for (let i = 0; i < rules.length; i++) {
222
+ const r = rules[i];
223
+ const si = value.search(r.matcher[0]);
224
+ if (si !== -1) {
225
+ candidateRules.push(r);
226
+ if (si < matchedRuleMinSearchIndex) {
227
+ matchedRuleMinSearchIndex = si;
228
+ matchedRule = r;
229
+ }
230
+ }
231
+ }
232
+ if (!matchedRule) {
233
+ buffer.push(value);
234
+ break;
235
+ }
236
+ buffer.push(value.substring(0, matchedRuleMinSearchIndex));
237
+ value = value.substring(matchedRuleMinSearchIndex);
238
+ let replacingSubstring = value.match(matchedRule.matcher[0])?.[0] ?? '';
239
+ value = value.substring(replacingSubstring.length);
240
+ replacingSubstring = replacingSubstring.replace(matchedRule.matcher[0], matchedRule.matcher[1]);
241
+ if (matchedRule.childrenInlineRules.length > 0)
242
+ replacingSubstring = this.parseLine(replacingSubstring, matchedRule.childrenInlineRules);
243
+ buffer.push(replacingSubstring);
244
+ rules = candidateRules;
245
+ }
246
+ return buffer.join('');
247
+ }
248
+ }
249
+ // const parser = new MDParser(new MDGrammar)
250
+ export const md = (parser, text, absolutePathPrefix) => {
251
+ let res = parser.run(text);
252
+ // allow images and links to have a relative path
253
+ if (absolutePathPrefix)
254
+ res = res.replace(/src="((?!https?:)[^"]+)"/gm, 'src="' + absolutePathPrefix + '$1"');
255
+ return res;
256
+ };
@@ -1,3 +1 @@
1
- export { StyleSheetProcessor, PseudoClassType, PseudoClass } from "./processor";
2
- export { RulePriority, buildClassName, buildRule, BorderStyle, PointerEventsStyle, FontWeight, UIComponentProps } from "./core";
3
- export { HtmlTag, AnyUIComponent, ObserveAffect, UIComponent, StackHAlign, StackVAlign, StackProps, vstack, vstackMapper, hstack, hstackMapper, TextProps, Text, div, p, span, h1, h2, h3, h4, h5, h6, ButtonProps, Button, btn, LinkProps, LinkBtn, link, SwitcherProps, switcher, observer, OnReceiver, List, vlist, hlist, SpacerProps, spacer, ImageProps, Image, image, InputType, InputMode, TurnType, InputProps, Input, input, textarea } from "./components";
1
+ export { md, MDInlineGrammarRule, MDLineGrammarRule, MDMultilineGrammarRule, GlobalGrammarRule, MDGrammar, MDParser } from "./md";
@@ -0,0 +1,60 @@
1
+ export declare class MDInlineGrammarRule {
2
+ matcher: [RegExp, any];
3
+ childrenInlineRules: MDInlineGrammarRule[];
4
+ }
5
+ export declare class MDLineGrammarRule {
6
+ matcher: [RegExp, any];
7
+ preProccessing?: (v: string) => string;
8
+ postProccessing?: (v: string) => string;
9
+ childrenInlineRules: MDInlineGrammarRule[];
10
+ }
11
+ export declare class GlobalGrammarRule {
12
+ postProccessing?: (v: string) => string;
13
+ childrenMultilineRules: MDMultilineGrammarRule[];
14
+ childrenLineRules: MDLineGrammarRule[];
15
+ childrenInlineRules: MDInlineGrammarRule[];
16
+ }
17
+ export declare class MDMultilineGrammarRule {
18
+ startMatcher: [RegExp, any];
19
+ endMatcher: [RegExp, any];
20
+ postProccessing?: (v: string) => string;
21
+ childrenMultilineRules: MDMultilineGrammarRule[];
22
+ childrenLineRules: MDLineGrammarRule[];
23
+ childrenInlineRules: MDInlineGrammarRule[];
24
+ }
25
+ export declare class MDGrammar {
26
+ readonly globalRule: GlobalGrammarRule;
27
+ readonly sup: MDInlineGrammarRule;
28
+ readonly sub: MDInlineGrammarRule;
29
+ readonly strong: MDInlineGrammarRule;
30
+ readonly bold: MDInlineGrammarRule;
31
+ readonly italic: MDInlineGrammarRule;
32
+ readonly boldItalic: MDInlineGrammarRule;
33
+ readonly em: MDInlineGrammarRule;
34
+ readonly code: MDInlineGrammarRule;
35
+ readonly figure: MDInlineGrammarRule;
36
+ readonly img: MDInlineGrammarRule;
37
+ readonly link: MDInlineGrammarRule;
38
+ readonly header: MDLineGrammarRule;
39
+ readonly quote: MDLineGrammarRule;
40
+ readonly alignRight: MDLineGrammarRule;
41
+ readonly alignCenter: MDLineGrammarRule;
42
+ readonly stars: MDLineGrammarRule;
43
+ readonly p: MDLineGrammarRule;
44
+ readonly br: MDLineGrammarRule;
45
+ readonly oli: MDLineGrammarRule;
46
+ readonly uli: MDLineGrammarRule;
47
+ readonly ol: MDMultilineGrammarRule;
48
+ readonly ul: MDMultilineGrammarRule;
49
+ readonly table: MDMultilineGrammarRule;
50
+ readonly div: MDMultilineGrammarRule;
51
+ constructor();
52
+ }
53
+ export declare class MDParser {
54
+ readonly globalRule: GlobalGrammarRule;
55
+ constructor(grammar: MDGrammar);
56
+ run(text: string): string;
57
+ private parseMultiline;
58
+ private parseLine;
59
+ }
60
+ export declare const md: (parser: MDParser, text: string, absolutePathPrefix?: string) => string;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "flinker-markdown",
3
3
  "description": "Free TypeScript library for parsing markdown text (customisable, not standardized).",
4
- "version": "1.0.0",
4
+ "version": "1.1.1",
5
5
  "repository": {
6
6
  "type": "git",
7
7
  "url": "git+https://github.com/Dittner/FlinkerMD.git"