@thi.ng/hiccup-markdown 3.1.0 → 3.2.0

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/CHANGELOG.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Change Log
2
2
 
3
- - **Last updated**: 2023-03-05T16:02:40Z
3
+ - **Last updated**: 2023-03-07T14:54:11Z
4
4
  - **Generator**: [thi.ng/monopub](https://thi.ng/monopub)
5
5
 
6
6
  All notable changes to this project will be documented in this file.
@@ -9,6 +9,31 @@ See [Conventional Commits](https://conventionalcommits.org/) for commit guidelin
9
9
  **Note:** Unlisted _patch_ versions only involve non-code or otherwise excluded changes
10
10
  and/or version bumps of transitive dependencies.
11
11
 
12
+ ## [3.2.0](https://github.com/thi-ng/umbrella/tree/@thi.ng/hiccup-markdown@3.2.0) (2023-03-07)
13
+
14
+ #### 🚀 Features
15
+
16
+ - update parse grammar ([e0be40a](https://github.com/thi-ng/umbrella/commit/e0be40a))
17
+ - char escapes now usable in almost all situs
18
+ (excl. metadata for code/custom blocks)
19
+ - add support for these HTML tags:
20
+ - comments `<!-- ... -->`
21
+ - `<sub>` / `<sup>`
22
+ - add tests
23
+ - update parse grammar & handlers ([7bd4edb](https://github.com/thi-ng/umbrella/commit/7bd4edb))
24
+ - update TransformCtx, add .column & .align fields
25
+ - update table row/cell handlers
26
+ - update olitem handler (index now a string)
27
+ - this is to support alphabetical ordered lists
28
+ - add `<sub>`/`<sup>` handlers
29
+ - update tests
30
+ - update docs
31
+ - update char escapes & inline format nesting rules ([2fde69b](https://github.com/thi-ng/umbrella/commit/2fde69b))
32
+ - allow nested kbd/sub/sup elements
33
+ - update char escape rules for inline code, codeblocks, custom blocks & meta blocks
34
+ - update __trimBody() helper to remove isolate trailing WS
35
+ - add/update tests
36
+
12
37
  ## [3.1.0](https://github.com/thi-ng/umbrella/tree/@thi.ng/hiccup-markdown@3.1.0) (2023-03-05)
13
38
 
14
39
  #### 🚀 Features
package/README.md CHANGED
@@ -13,11 +13,15 @@ This project is part of the
13
13
  - [Parser](#parser)
14
14
  - [Basic features](#basic-features)
15
15
  - [Additional syntax & parser features/restrictions](#additional-syntax--parser-featuresrestrictions)
16
- - [Formatting](#formatting)
17
- - [Code block headers](#code-block-headers)
16
+ - [Code blocks](#code-blocks)
18
17
  - [Custom blocks](#custom-blocks)
18
+ - [Escaping control characters](#escaping-control-characters)
19
+ - [Formatting](#formatting)
19
20
  - [Headings with anchor IDs](#headings-with-anchor-ids)
20
21
  - [Heading with anchor {#custom-id-123}](#heading-with-anchor-custom-id-123)
22
+ - [Images](#images)
23
+ - [Link formats](#link-formats)
24
+ - [Lists](#lists)
21
25
  - [Metadata](#metadata)
22
26
  - [Customizing tag transforms](#customizing-tag-transforms)
23
27
  - [Serializing to HTML](#serializing-to-html)
@@ -26,6 +30,7 @@ This project is part of the
26
30
  - [Behaviors](#behaviors)
27
31
  - [Usage examples](#usage-examples)
28
32
  - [Status](#status)
33
+ - [Related packages](#related-packages)
29
34
  - [Installation](#installation)
30
35
  - [Dependencies](#dependencies)
31
36
  - [Usage examples](#usage-examples)
@@ -37,7 +42,8 @@ This project is part of the
37
42
 
38
43
  Markdown parser & serializer from/to Hiccup format. This is a support package for [@thi.ng/hiccup](https://github.com/thi-ng/umbrella/tree/develop/packages/hiccup).
39
44
 
40
- **⚠️ IMPORTANT: With v3.0.0 the parser implementation underwent a complete rewrite (with breaking changes, but lots of improvements). ⚠️**
45
+ **⚠️ IMPORTANT: With v3.0.0 the parser implementation underwent a complete
46
+ rewrite (with breaking changes, but lots of improvements). ⚠️**
41
47
 
42
48
  This package provides both a customizable
43
49
  [Markdown](https://en.wikipedia.org/wiki/Markdown)-to-[Hiccup](https://github.com/thi-ng/umbrella/tree/develop/packages/hiccup)
@@ -45,48 +51,47 @@ parser and an extensible Hiccup-to-Markdown converter.
45
51
 
46
52
  ## Parser
47
53
 
54
+ Sadly, none of the available Markdown flavors have ever been designed with much
55
+ consistency and/or ease-of-implementation/parsing aspects in mind. The result is
56
+ a proliferation of Markdown-ish flavors, even though there've been attempts to
57
+ standardize the syntax.
58
+
48
59
  ### Basic features
49
60
 
50
- The parser itself is not aimed at supporting **all** of Markdown's
51
- (CommonMark's) quirky syntax features, but restricts itself to a sane subset of
52
- features and some useful [additional
61
+ The parser provided here is _not_ aimed at supporting **all** of Markdown's (or
62
+ CommonMark's) quirky syntax features, but restricts itself to a large _sane
63
+ subset_ of features and some useful [additional
53
64
  features](#additional-syntax--parser-featuresrestrictions) not part of the
54
- standard syntax.
55
-
56
- | Feature | Comments |
57
- |---------------|--------------------------------------------------------------------------------------------------------------------------------------------|
58
- | Blockquotes | Nestable, support for inline formatting and forced line breaks (trailing `\`) |
59
- | Code blocks | GFM style only (triple backtick prefix), w/ mandatory language hint & optional extra headers information |
60
- | Formatting | Nestable **bold**, _italic_, `code`, ~~strike~~, <kbd>Key</kbd> supported in paragraphs, headings, link labels, lists, blockquotes, tables |
61
- | Footnotes | Supported and stored separately in parse context |
62
- | Headings | ATX-style only (`#` line prefix), optional custom ID attrib (via `{#custom-id}` suffix), levels 1-6 then fallback to paragraph |
63
- | Horiz. Rulers | Only dash supported (e.g. `---`), min 2 chars required, length retained for downstream transformations |
64
- | HTML elements | Unsupported |
65
- | Images | Alt text is required, image can be used in link labels |
66
- | Links | Supports `[label](target)`, `[label][ref]`, `[[page id]]` or `[[page id|label]]` style links, inline formats in label |
67
- | Lists | Ordered & unordered, nestable, inline formatting, line breaks, GFM task list items |
68
- | Paragraphs | Support for forced line breaks (trailing `\`) |
69
- | Tables | Support for column alignments, nestable inline formatting |
65
+ standard/common syntax.
66
+
67
+ | Feature | Comments |
68
+ |---------------|-------------------------------------------------------------------------------------------------------------------------|
69
+ | Blockquotes | Nestable, support for inline formatting and forced line breaks (trailing backslash) |
70
+ | Code blocks | GFM style only (triple backtick prefix), w/ mandatory language hint & optional extra headers information |
71
+ | Escaping | Uniformly escape MD control characters via backslash, e.g. `\*` |
72
+ | Formatting | Nestable inline formats supported in paragraphs, headings, link labels, lists, blockquotes, tables: |
73
+ | | **bold**, _italic_, `code`, ~~strike~~ |
74
+ | | <kbd>Key</kbd>, <sub>subscript</sub> and <sup>super</sup> |
75
+ | Footnotes | Supported and stored separately in parse context for further processing |
76
+ | Headings | ATX-style only (`#` line prefix), optional anchor ID (via `{#custom-id}` suffix), levels 1-6 then fallback to paragraph |
77
+ | Horiz. Rulers | Only dash supported (e.g. `---`), min 3 chars required, length retained for downstream transformations |
78
+ | HTML elements | Only `<kbd>`, `<sub>` `<sup>` |
79
+ | Images | Alt text is required, image can be used in link labels, optional title suffix |
80
+ | Links | Supports `[label](target)`, `[label][ref]`, `[[page id]]` or `[[page id\|label]]` style links, inline formats in label |
81
+ | Lists | Ordered & unordered, nestable, inline formatting, line breaks, GFM todo list items |
82
+ | Paragraphs | Support for forced line breaks (trailing backslash) |
83
+ | Tables | Support for column alignments, nestable inline formatting (no nested block elements) |
84
+
85
+ **Please visit the [interactive Markdown parser/editor
86
+ playground](https://demo.thi.ng/umbrella/markdown/) for further
87
+ details/examples...**
70
88
 
71
89
  ### Additional syntax & parser features/restrictions
72
90
 
73
- #### Formatting
74
-
75
- To avoid ambiguity and simplify nesting, only the following formatting syntax is
76
- supported for bold & italic:
77
-
78
- - `**bold**`
79
- - `_italic_`
80
-
81
- `code` (\`) and ~~strikethrough~~ (`~~`) as usual...
82
-
83
- For keyboard commands `<kbd>` can be used, e.g.:\
84
- `<kbd>Control</kbd> + <kbd>R</kbd>`
91
+ #### Code blocks
85
92
 
86
- #### Code block headers
87
-
88
- In addition to the mandatory language hint, code blocks support optional user
89
- defined headers/metadata. Items will be separated by spaces (e.g. see
93
+ In addition to the **mandatory language hint**, code blocks support optional
94
+ user defined headers/metadata. Items will be separated by spaces (e.g. see
90
95
  [@thi.ng/tangle](https://github.com/thi-ng/umbrella/tree/develop/packages/tangle)
91
96
  for concrete use cases).
92
97
 
@@ -101,8 +106,8 @@ layout breakage)
101
106
 
102
107
  #### Custom blocks
103
108
 
104
- Since the parser does not directly transform Markdown into HTML, blocks of custom
105
- freeform content can be used to define arbitrary data structures (e.g. UI
109
+ Since the parser does not directly transform Markdown into HTML, blocks of
110
+ custom freeform content can be used to define arbitrary data structures (e.g. UI
106
111
  components, diagrams/visualizations etc.). Similarly to code blocks, custom
107
112
  blocks are wrapped with `:::` and a type specifier:
108
113
 
@@ -129,7 +134,34 @@ transformer. The default handler merely creates an element like this:
129
134
  **Tip:** Use a
130
135
  [`defmulti()`](https://github.com/thi-ng/umbrella/tree/develop/packages/defmulti)
131
136
  polymorphic function as tag transformer to elegantly handle multiple types of
132
- custom blocks (in an extensible manner).
137
+ custom blocks (in an easily extensible manner).
138
+
139
+ #### Escaping control characters
140
+
141
+ Unlike the weird & hard-to-memorize escape rules in "standard" Markdown, here
142
+ we're taking a more uniform approach of exclusively using backslash escapes
143
+ (e.g. `\*`) to ensure various Markdown control characters are used verbatim.
144
+ Only the following minor exceptions apply:
145
+
146
+ 1. In inline code sections only backticks (`\``) need to be escaped and
147
+ backslashes _can_ be escaped. All others chars are used as is.
148
+ 2. In fenced code blocks only backticks can be escaped (e.g. if escaping the
149
+ triple-backtick block fence itself). Backslashes and others chars are used as is.
150
+ 3. In custom blocks only colons (`:`) can be escaped (e.g. if escaping the
151
+ triple-colon block fence itself). All other chars are used as is.
152
+ 4. In metadata blocks only `}` can be escaped. All other chars are used as is.
153
+
154
+ #### Formatting
155
+
156
+ To avoid ambiguity and simplify nesting, only the following formatting syntax is
157
+ supported for bold & italic:
158
+
159
+ - `**bold**`
160
+ - `_italic_`
161
+ - `code` (\`) and ~~strikethrough~~ (`~~`) as usual...
162
+ - `<kbd>` for keyboard shortcuts (e.g. <kbd>Control</kbd>)
163
+ - `<sub>` for <sub>subscript</sub>
164
+ - `<sup>` for <sup>superscript</sup>
133
165
 
134
166
  #### Headings with anchor IDs
135
167
 
@@ -160,6 +192,35 @@ Results in:
160
192
  // ]
161
193
  ```
162
194
 
195
+ ### Images
196
+
197
+ **Alt text for images is required**. Optional `title` attribute (e.g. for hover
198
+ tooltip or caption) can be given in quotes after the image URL. For example:
199
+
200
+ ```markdown
201
+ ![alt text](url "title text")
202
+ ```
203
+
204
+ ### Link formats
205
+
206
+ The following link formats are supported:
207
+
208
+ 1. `[label](target)`
209
+ 2. `[label](target "title")`
210
+ 3. `[label][ref-id]` - the reference ID will have to provided somewhere else in
211
+ the document or pre-defined via options given to the parser
212
+ 4. `[[page name]]` - Wiki-style page reference, non-standard Markdown
213
+ 5. `[[page name|label]]` - like 4., but with added link label
214
+
215
+ ### Lists
216
+
217
+ - Ordered and unordered lists are supported
218
+ - Fully nestable
219
+ - Ordered lists start with a `1.` (digit or letter followed by a dot) prefix
220
+ - Unordered lists **must** use a `-` line prefix
221
+ - [ ] TODO list items
222
+ - [x] ...are supported as well
223
+
163
224
  #### Metadata
164
225
 
165
226
  Arbitrary metadata can be assigned to _any_ block level element:
@@ -450,6 +511,10 @@ More info [here](http://thi.ng/hiccup-markdown).
450
511
 
451
512
  [Search or submit any issues for this package](https://github.com/thi-ng/umbrella/issues?q=%5Bhiccup-markdown%5D+in%3Atitle)
452
513
 
514
+ ## Related packages
515
+
516
+ - [@thi.ng/markdown-table](https://github.com/thi-ng/umbrella/tree/develop/packages/markdown-table) - Markdown table formatter/generator with support for column alignments
517
+
453
518
  ## Installation
454
519
 
455
520
  ```bash
@@ -470,7 +535,7 @@ For Node.js REPL:
470
535
  const hiccupMarkdown = await import("@thi.ng/hiccup-markdown");
471
536
  ```
472
537
 
473
- Package sizes (brotli'd, pre-treeshake): ESM: 4.42 KB
538
+ Package sizes (brotli'd, pre-treeshake): ESM: 4.62 KB
474
539
 
475
540
  ## Dependencies
476
541
 
package/api.d.ts CHANGED
@@ -189,7 +189,8 @@ export interface TagTransforms {
189
189
  ol(ctx: TransformCtx, items: any[], meta?: any): any;
190
190
  /**
191
191
  * Handler for a single list item in an ordered list. The `index` arg is the
192
- * parsed integer index specified in the MD source code for that item.
192
+ * raw item index specified in the MD source code for that item (e.g. a
193
+ * digit or letter).
193
194
  *
194
195
  * @remarks
195
196
  * If the `attribs` object has a `__todo` attrib, the item is a task list
@@ -200,7 +201,7 @@ export interface TagTransforms {
200
201
  * @param index
201
202
  * @param body
202
203
  */
203
- olitem(ctx: TransformCtx, attribs: TodoAttribs, index: number, ...body: any[]): any;
204
+ olitem(ctx: TransformCtx, attribs: TodoAttribs, index: string, body: any[]): any;
204
205
  /**
205
206
  * Handler for a paragraph of body content.
206
207
  *
@@ -217,36 +218,54 @@ export interface TagTransforms {
217
218
  */
218
219
  strike(ctx: TransformCtx, body: any[]): any;
219
220
  /**
220
- * Handler for a table container.
221
+ * Handler for subscript inline content.
222
+ *
223
+ * @param ctx
224
+ * @param body
225
+ */
226
+ sub(ctx: TransformCtx, body: string): any;
227
+ /**
228
+ * Handler for superscript inline content.
229
+ *
230
+ * @param ctx
231
+ * @param body
232
+ */
233
+ sup(ctx: TransformCtx, body: string): any;
234
+ /**
235
+ * Handler for a table container. The alignment settings for each column can
236
+ * be accessed via given context's {@link TransformCtx.align} array.
221
237
  *
222
238
  * @param ctx
223
- * @param align
224
239
  * @param head
225
240
  * @param rows
226
241
  * @param meta
227
242
  */
228
- table(ctx: TransformCtx, align: ColumnAlign[], head: any[], rows: any[], meta?: any): any;
243
+ table(ctx: TransformCtx, head: any[], rows: any[], meta?: any): any;
229
244
  /**
230
- * Handler for a single table cell in a non-header row.
245
+ * Handler for a single table cell in a non-header row. The current column index can
246
+ * be accessed via given context's {@link TransformCtx.column} property.
231
247
  *
232
248
  * @param ctx
233
249
  * @param body
234
250
  */
235
251
  tableCell(ctx: TransformCtx, body: any[]): any;
236
252
  /**
237
- * Handler for a single table cell in the header row.
253
+ * Handler for a single table cell in the header row. The current column index can
254
+ * be accessed via given context's {@link TransformCtx.column} property.
238
255
  *
239
256
  * @param ctx
240
257
  * @param body
241
258
  */
242
259
  tableHead(ctx: TransformCtx, body: any[]): any;
243
260
  /**
244
- * Handler for a single table row. The header row will have `index=0`.
261
+ * Handler for a single table row. The current row index can be accessed via
262
+ * given context's {@link TransformCtx.row} property. The header row will
263
+ * have `index=0`. The first data row `index=1`.
245
264
  *
246
265
  * @param ctx
247
266
  * @param body
248
267
  */
249
- tableRow(ctx: TransformCtx, index: number, cells: any[]): any;
268
+ tableRow(ctx: TransformCtx, cells: any[]): any;
250
269
  /**
251
270
  * Handler for an unordered list wrapper.
252
271
  *
@@ -266,7 +285,7 @@ export interface TagTransforms {
266
285
  * @param attribs
267
286
  * @param body
268
287
  */
269
- ulitem(ctx: TransformCtx, attribs: TodoAttribs, ...body: any[]): any;
288
+ ulitem(ctx: TransformCtx, attribs: TodoAttribs, body: any[]): any;
270
289
  /**
271
290
  * Handler for `[[page name]]` or `[[page name|label]]-style links.
272
291
  *
@@ -291,6 +310,8 @@ export interface TransformCtx {
291
310
  }[];
292
311
  hasFootnotes: boolean;
293
312
  meta?: any;
313
+ align: ColumnAlign[];
314
+ column: number;
294
315
  row: number;
295
316
  opts: ParseOpts;
296
317
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thi.ng/hiccup-markdown",
3
- "version": "3.1.0",
3
+ "version": "3.2.0",
4
4
  "description": "Markdown parser & serializer from/to Hiccup format",
5
5
  "type": "module",
6
6
  "module": "./index.js",
@@ -40,7 +40,7 @@
40
40
  "@thi.ng/defmulti": "^2.1.31",
41
41
  "@thi.ng/emoji": "^0.1.0",
42
42
  "@thi.ng/errors": "^2.2.12",
43
- "@thi.ng/hiccup": "^4.2.35",
43
+ "@thi.ng/hiccup": "^4.2.36",
44
44
  "@thi.ng/logger": "^1.4.9",
45
45
  "@thi.ng/parse": "^2.2.28",
46
46
  "@thi.ng/strings": "^3.4.1",
@@ -93,7 +93,10 @@
93
93
  },
94
94
  "thi.ng": {
95
95
  "parent": "@thi.ng/hiccup",
96
+ "related": [
97
+ "markdown-table"
98
+ ],
96
99
  "year": 2018
97
100
  },
98
- "gitHead": "8342900eedc77bb09edb8c544804578b71f8acc6\n"
101
+ "gitHead": "31dd56eba72edd73a3ced5002c0f5145ce3d42d6\n"
99
102
  }
package/parse.js CHANGED
@@ -1,5 +1,5 @@
1
- // Downloaded @ 2023-03-05T09:08:51.082Z
2
- // Source: https://demo.thi.ng/umbrella/parse-playground/#ldoNwUROTDE6IDxETkw-KyA9PiBkaXNjYXJkIDsKRE5MMjogPE5MPnsyLH0gOwpsYnI6ICdcXCchIDxOTD4hIDsKZXNjOiAnXFwnISAoICc8JyB8ICdbJyB8ICdfJyB8ICcqJyB8ICdgJyB8ICd-JyB8ICc6JyApIDsKaW5saW5lZGVsaW06ICggIiFbIiB8ICdbJyB8ICIqKiIgfCAnXycgfCAnYCcgfCAnPCcgfCAnXFwnIHwgIn5-IiB8ICIgOiIgfCA8bGJyPiApIDsKZGVsaW06ICggPGlubGluZWRlbGltPiB8IDxETkwyPiApIDsKZGVsaW0xOiAoIDxpbmxpbmVkZWxpbT4gfCA8Tkw-ICkgOwpib2R5OiAuKD8tPGRlbGltPiEpID0-IGpvaW4gOwpib2R5MTogLig_LTxkZWxpbTE-ISkgPT4gam9pbiA7Cgp3aWtpcmVmOiAiW1siISAuKD8rIl1dIiEpID0-IGpvaW4gOwpmbnJlZjogIlteIiEgPGxhYmVsPiA7CmZub3RlOiA8TFNUQVJUPiAiW14iISA8Zm5sYWJlbD4gPFdTMT4gPHBhcmE-IDsKZm5sYWJlbDogLig_KyJdOiIhKSA9PiBqb2luIDsKbGFiZWw6IC4oPysnXSchKSA9PiBqb2luIDsKdGFyZ2V0OiAuKD8rJyknISkgPT4gam9pbiA7Cmxpbms6ICdbJyEgPGxpbmtsYWJlbD4gJygnISA8dGFyZ2V0PiA7CmxpbmtyZWY6ICdbJyEgPGxpbmtsYWJlbD4gJ1snISA8bGFiZWw-IDsKbGlua2RlZjogPExTVEFSVD4gJ1snISA8bGFiZWw-ICc6JyEgPFdTMT4gPGxkdGFyZ2V0PiA7CmxkdGFyZ2V0OiAuKD8rPEROTDE-KSA9PiBqb2luIDsKbGlua2xhYmVsOiAoPGltZz4gfCA8Ym9sZD4gfCA8aXRhbGljPiB8IDxzdHJpa2U-IHwgPGNvZGU-IHwgPGVtb2ppPiB8IDxsaW5rYm9keT4pKiAnXSchIDsKbGlua2RlbGltOiAoICIhWyIgfCAnWycgfCAiKioiIHwgJ18nIHwgIn5-IiB8ICdgJyB8ICddJykgOwpsaW5rYm9keTogLig_LTxsaW5rZGVsaW0-ISkgPT4gam9pbiA7CmltZzogIiFbIiEgPGxhYmVsPiAnKCchIDx0YXJnZXQ-IDsKYm9sZDogIioqIiEgKDx3aWtpcmVmPiB8IDxpbWc-IHwgPGZucmVmPiB8IDxsaW5rcmVmPiB8IDxsaW5rPiB8IDxpdGFsaWM-IHwgPHN0cmlrZT4gfCA8Y29kZT4gfCA8ZW1vamk-IHwgPGJvZHkxPikqICIqKiIhIDsKaXRhbGljOiAnXychICg8d2lraXJlZj4gfCA8aW1nPiB8IDxmbnJlZj4gfCA8bGlua3JlZj4gfCA8bGluaz4gfCA8Ym9sZD4gfCA8c3RyaWtlPiB8IDxjb2RlPiB8IDxlbW9qaT4gfCA8Ym9keTE-KSogJ18nISA7CnN0cmlrZTogIn5-IiEgKDx3aWtpcmVmPiB8IDxpbWc-IHwgPGZucmVmPiB8IDxsaW5rcmVmPiB8IDxsaW5rPiB8IDxib2xkPiB8IDxpdGFsaWM-IHwgPGNvZGU-IHwgPGVtb2ppPiB8IDxib2R5MT4pKiAifn4iISA7CmNvZGU6ICdgJyEgLig_KydgJyEpID0-IGpvaW4gOwprYmQ6ICI8a2JkPiIhIC4oPysiPC9rYmQ-IiEpID0-IGpvaW4gOwplbW9qaTogJyAnPyAnOichICg8QUxQSEFfTlVNPiB8ICcrJyB8ICctJykoPysnOichKSA9PiBqb2luIDsKcGFyYTogKDx3aWtpcmVmPiB8IDxpbWc-IHwgPGZucmVmPiB8IDxsaW5rcmVmPiB8IDxsaW5rPiB8IDxib2xkPiB8IDxpdGFsaWM-IHwgPHN0cmlrZT4gfCA8Y29kZT4gfCA8a2JkPiB8IDxlbW9qaT4gfCA8bGJyPiB8IDxlc2M-IHwgPGJvZHk-KSogPEROTDI-ISA7CgpoZGxldmVsOiAnIycrID0-IGNvdW50IDsKaGRpZDogInsjIiEgKDxBTFBIQV9OVU0-IHwgJy0nKSsgJ30nISA9PiBqb2luIDsKaGRkZWxpbTogKCA8ZGVsaW0xPiB8ICJ7IyIgKSA7CmhkYm9keTogLig_LTxoZGRlbGltPiEpID0-IGpvaW4gOwpoZDogPExTVEFSVD4gPGhkbGV2ZWw-IDxXUzA-CiAgICAoPHdpa2lyZWY-IHwgPGltZz4gfCA8Zm5yZWY-IHwgPGxpbms-IHwgPGJvbGQ-IHwgPGl0YWxpYz4gfCA8c3RyaWtlPiB8IDxjb2RlPiB8IDxlbW9qaT4gfCA8aGRib2R5PiApKiA8aGRpZD4_IDxETkwxPiA7CgpsaWxldmVsOiAnICcqID0-IGNvdW50IDsKdWludDogPERJR0lUPisgPT4gaW50IDsKdWxpZDogPEROTD4gPFdTMD4gJy0nISA7Cm9saWQ6IDxETkw-IDxXUzA-IDxESUdJVD4rISAnLichIDsKbGlkZWxpbTogKCA8ZGVsaW0-IHwgPHVsaWQ-IHwgPG9saWQ-ICkgOwpsaWJvZHk6IC4oPy08bGlkZWxpbT4hKSA9PiBqb2luIDsKdG9kbzogJ1snISBbIHhYXSAnXSchIDxXUzE-ID0-IGhvaXN0UiA7CnVsaXRlbTogPExTVEFSVD4gPGxpbGV2ZWw-ICItICIhIDx0b2RvPj8KICAgICAgICAoPHdpa2lyZWY-IHwgPGltZz4gfCA8Zm5yZWY-IHwgPGxpbms-IHwgPGJvbGQ-IHwgPGl0YWxpYz4gfCA8c3RyaWtlPiB8IDxjb2RlPiB8IDxlbW9qaT4gfCA8bGlib2R5PiApKiA8RE5MPiA7Cm9saXRlbTogPExTVEFSVD4gPGxpbGV2ZWw-IDx1aW50PiAiLiAiISA8dG9kbz4_CiAgICAgICAgKDx3aWtpcmVmPiB8IDxpbWc-IHwgPGZucmVmPiB8IDxsaW5rPiB8IDxib2xkPiB8IDxpdGFsaWM-IHwgPHN0cmlrZT4gfCA8Y29kZT4gfCA8ZW1vamk-IHwgPGxpYm9keT4gKSogPEROTD4gOwpsaXN0OiAoPHVsaXRlbT4gfCA8b2xpdGVtPikrIDxETkwxPiA7CgpjYmRlbGltOiA8TFNUQVJUPiAiYGBgIiEgOwpjb2RlYmxvY2s6IDxjYmRlbGltPiEgPGNvZGVtZXRhPiA8Y29kZWJvZHk-IDxETkwxPiA7CmNvZGVtZXRhOiAuKD8rPE5MPiEpID0-IGpvaW4gOwpjb2RlYm9keTogLig_KzxjYmRlbGltPikgPT4gam9pbiA7CgpjdXN0b21kZWxpbTogPExTVEFSVD4gIjo6OiIhIDsKY3VzdG9tYmxvY2s6IDxjdXN0b21kZWxpbT4hIDxjdXN0b21tZXRhPiA8Y3VzdG9tYm9keT4gPEROTDE-IDsKY3VzdG9tbWV0YTogLig_KzxOTD4hKSA9PiBqb2luIDsKY3VzdG9tYm9keTogLig_KzxjdXN0b21kZWxpbT4pID0-IGpvaW4gOwoKbWV0YWJsb2NrOiA8TFNUQVJUPiAie3t7IiEgPG1ldGFib2R5PiA8RE5MMT4gOwptZXRhYm9keTogLig_KzxtZXRhZW5kPiEpID0-IGpvaW4gOwptZXRhZW5kOiAifX19IiA8TEVORD4gOwoKYnFsZXZlbDogJz4nKyA9PiBjb3VudCA7CmJxbGluZTogPExTVEFSVD4gPGJxbGV2ZWw-ICcgJz8hCiAgICAgICAgKDx3aWtpcmVmPiB8IDxpbWc-IHwgPGZucmVmPiB8IDxsaW5rPiB8IDxib2xkPiB8IDxpdGFsaWM-IHwgPHN0cmlrZT4gfCA8Y29kZT4gfCA8ZW1vamk-IHwgPGJxbGJyPiB8IDxib2R5MT4pKiA8RE5MPiA7CmJxbGJyOiAnXFwnISg_LTxETkw-KSA7CmJxdW90ZTogPGJxbGluZT4rIDxETkwxPiA7Cgp0ZGVsaW06ICg8aW5saW5lZGVsaW0-IHwgJ3wnICkgOwp0Ym9keTogLig_LTx0ZGVsaW0-ISkgPT4gam9pbiA7CnRjZWxsOiA8V1MwPiAoPHdpa2lyZWY-IHwgPGltZz4gfCA8Zm5yZWY-IHwgPGxpbms-IHwgPGJvbGQ-IHwgPGl0YWxpYz4gfCA8c3RyaWtlPiB8IDxjb2RlPiB8IDxlbW9qaT4gfCA8dGJvZHk-ICkqICd8JyEgOwp0cm93OiA8TFNUQVJUPiAnfCchIDx0Y2VsbD4oPys8RE5MPikgOwp0YWJsZTogPHRyb3c-KyA8RE5MMT4gOwoKaHI6ICItLSIgJy0nKyA8RE5MMT4gPT4gam9pbiA7CgptYWluOiA8V1MwPiAoPGhkPiB8IDxsaXN0PiB8IDxicXVvdGU-IHwgPGNvZGVibG9jaz4gfCA8Y3VzdG9tYmxvY2s-IHwgPG1ldGFibG9jaz4gfCA8dGFibGU-IHwgPGhyPiB8IDxmbm90ZT4gfCA8bGlua2RlZj4gfCA8cGFyYT4pKiA7pG1haW7aAqgjIEhlbGxvIDp3b3JsZDo6c21pbGU6W15hYmNdIHsjaGQxfQoKIyMgYWJjIHsjaGQyfQojIyBkZWYKCmEgXDwgYgoKTGV0IGBleHAyKHgpID0gMioqeGAKClByZXNzIDxrYmQ-Q29udHJvbDwva2JkPiArIDxrYmQ-Ujwva2JkPiB0byByZWxvYWQuCgo6OjphbGVydApUZXN0IFtsYWJlbF1bcmVmcmVmXQo6OjoKCi0tLQoKe3t7IHsiaWQiOiAxMjMsICJwcm90ZWN0ZWQiOiB0cnVlIH0gfX19ClNvIFtbc21pbGVfZm9vMTJdXSDwn5iNIHdoYXQncyBbdGhpc10oLi90aGlzLm1kKVteMl0gdGhlbjoKCkEgW2xpbmsgX3dpdGggKipuZXN0ZWQgfn5mb3JtYXR-fioqIGFuZCBgcmVmZXJlbmNlYF9dW3JlZnJlZl0uLi4KCmBgYHRzIHRhbmdsZTpubyBsbjp5ZXMKY29uc3QgZm9vID0gNDI7Ci8vIHRoZSBlbmQKYGBgCgoxLiBmb28KICA0LiBiYXIKICAgICBiYXogYW5kIHNvIG9uCiAgICAgbXVsdGlsaW5lCjIuIFsgXSB0YXNrCjMuIFt4XSAqKmRvbmUqKgoKPiBibG9ja3F1b3RlCj4gbXVsdGlsaW5lIHRvb1wKPj4gbmVzdGVkCj4-PiBsZXZlbCAzCj4gLS0gc291cmNlCgp8IGZvbyB8IGJhciB8Cnw6LXwtOnwKfCBbW3JlZiMyMl1dIHwgYG9uYCAqKm9mZioqIHwKClteYWJjXTogRm9vIF9iYXJfIGJhegoKW14yXTogVE9ETwoKW3JlZnJlZl06IGh0dHA6Ly90aGkubmcvCltmb29dOiAuL2Zvby5tZCNiYXIKCtk1MTEuIGFiYwogIC0geHl6CjIxLiB4eHgKICAxLiBmb28KICAgIDEuIGJhcgozMS4geXl5CgrZVj4gKipsZXZlbCAxYSoqCj4-IGxldmVsIDJhCj4-IGxldmVsIDJiCj4KPiBsZXZlbCAxYgoKWyoqZm9vKipdKGJhcikKCioqIVtmb29dKGJhcikqKgoK
1
+ // Downloaded @ 2023-03-07T13:30:37.046Z
2
+ // Source: https://demo.thi.ng/umbrella/parse-playground/#l9oQo0ROTDE6IDxETkw-KyA9PiBkaXNjYXJkIDsKRE5MMjogPE5MPnsyLH0gOwpsYnI6ICdcXCchIDxOTD4hIDsKZXNjOiAnXFwnISAoJ18nIHwgJyonIHwgJ2AnIHwgJ34nIHwgJzonIHwgJy0nIHwgJzwnIHwgJz4nIHwgJ1snIHwgJ10nIHwgJygnIHwgJyknIHwgJ3snIHwgJ30nIHwgJyMnIHwgJy4nIHwgJ3wnIHwgJ1xcJykgOwpjZXNjOiAnXFwnISAoJ2AnfCAnXFwnKSA7CmNiZXNjOiAnXFwnISAnYCcgOwpjdWVzYzogJ1xcJyEgJzonIDsKbWVzYzogJ1xcJyEgJ30nIDsKaWRlbGltOiAoIiFbIiB8ICdbJyB8ICIqKiIgfCAnXycgfCAnYCcgfCAnPCcgfCAifn4iIHwgIiA6IiB8IDxsYnI-KSA7CmRlbGltOiAoPGlkZWxpbT4gfCA8RE5MMj4pIDsKZWJvZHk6ICg8ZXNjPnwuKSA7CmNib2R5OiAoPGNlc2M-fC4pIDsKY2Jib2R5OiAoPGNiZXNjPnwuKSA7CmN1Ym9keTogKDxjdWVzYz58LikgOwptYm9keTogKDxtZXNjPnwuKSA7CmJvZHk6IDxlYm9keT4oPy08ZGVsaW0-ISkgPT4gam9pbiA7Cmlib2R5OiA8ZWJvZHk-KD8tPGlkZWxpbT4hKSA9PiBqb2luIDsKCmNvbW1lbnQ6ICI8IS0tIiEgLig_KyItLT4iKSA9PiBkaXNjYXJkIDsKd2lraXJlZjogIltbIiEgPGVib2R5Pig_KyJdXSIhKSA9PiBqb2luIDsKZm5yZWY6ICJbXiIhIDxsYWJlbD4gOwpmbm90ZTogPExTVEFSVD4gIlteIiEgPGZubGFiZWw-IDxXUzE-IDxwYXJhPiA7CmZubGFiZWw6IDxlYm9keT4oPysiXToiISkgPT4gam9pbiA7CmxhYmVsOiA8ZWJvZHk-KD8rJ10nISkgPT4gam9pbiA7CnRhcmdldDogPGVib2R5Pig_KycpJyEpID0-IGpvaW4gOwpsaW5rOiAnWychIDxsaW5rbGFiZWw-ICcoJyEgPHRhcmdldD4gOwpsaW5rcmVmOiAnWychIDxsaW5rbGFiZWw-ICdbJyEgPGxhYmVsPiA7CmxpbmtkZWY6IDxMU1RBUlQ-ICdbJyEgPGxhYmVsPiAnOichIDxXUzE-IDxsZHRhcmdldD4gOwpsZHRhcmdldDogPGVib2R5Pig_KzxETkwxPikgPT4gam9pbiA7CmxpbmtsYWJlbDogKDxpbWc-IHwgPGJvbGQ-IHwgPGl0YWxpYz4gfCA8c3RyaWtlPiB8IDxjb2RlPiB8IDxlbW9qaT4gfCA8a2JkPiB8IDxzdWI-IHwgPHN1cD4gfCA8bGlua2JvZHk-KSogJ10nISA7CmxpbmtkZWxpbTogKCIhWyIgfCAnWycgfCAiKioiIHwgJ18nIHwgIn5-IiB8ICdgJyB8ICddJykgOwpsaW5rYm9keTogPGVib2R5Pig_LTxsaW5rZGVsaW0-ISkgPT4gam9pbiA7CmltZzogIiFbIiEgPGxhYmVsPiAnKCchIDx0YXJnZXQ-IDsKYm9sZDogIioqIiEgKDx3aWtpcmVmPiB8IDxpbWc-IHwgPGZucmVmPiB8IDxsaW5rcmVmPiB8IDxsaW5rPiB8IDxpdGFsaWM-IHwgPHN0cmlrZT4gfCA8Y29kZT4gfCA8ZW1vamk-IHwgPGtiZD4gfCA8c3ViPiB8IDxzdXA-IHwgPGlib2R5PikqICIqKiIhIDsKaXRhbGljOiAnXychICg8d2lraXJlZj4gfCA8aW1nPiB8IDxmbnJlZj4gfCA8bGlua3JlZj4gfCA8bGluaz4gfCA8Ym9sZD4gfCA8c3RyaWtlPiB8IDxjb2RlPiB8IDxlbW9qaT4gfCA8a2JkPiB8IDxzdWI-IHwgPHN1cD4gfCA8aWJvZHk-KSogJ18nISA7CnN0cmlrZTogIn5-IiEgKDx3aWtpcmVmPiB8IDxpbWc-IHwgPGZucmVmPiB8IDxsaW5rcmVmPiB8IDxsaW5rPiB8IDxib2xkPiB8IDxpdGFsaWM-IHwgPGNvZGU-IHwgPGVtb2ppPiB8IDxrYmQ-IHwgPHN1Yj4gfCA8c3VwPiB8IDxpYm9keT4pKiAifn4iISA7CmNvZGU6ICdgJyEgPGNib2R5Pig_KydgJyEpID0-IGpvaW4gOwprYmQ6ICI8a2JkPiIhIDxlYm9keT4oPysiPC9rYmQ-IiEpID0-IGpvaW4gOwpzdWI6ICI8c3ViPiIhIDxlYm9keT4oPysiPC9zdWI-IiEpID0-IGpvaW4gOwpzdXA6ICI8c3VwPiIhIDxlYm9keT4oPysiPC9zdXA-IiEpID0-IGpvaW4gOwplbW9qaTogJyAnPyAnOichICg8QUxQSEFfTlVNPiB8ICcrJykoPysnOichKSA9PiBqb2luIDsKcGFyYTogKDx3aWtpcmVmPiB8IDxpbWc-IHwgPGZucmVmPiB8IDxsaW5rcmVmPiB8IDxsaW5rPiB8IDxib2xkPiB8IDxpdGFsaWM-IHwgPHN0cmlrZT4gfCA8Y29kZT4gfCA8a2JkPiB8IDxzdWI-IHwgPHN1cD4gfCA8ZW1vamk-IHwgPGxicj4gfCA8Y29tbWVudD4gfCA8Ym9keT4pKiA8RE5MMj4hIDsKCmhkbGV2ZWw6ICcjJysgPT4gY291bnQgOwpoZGlkOiAieyMiISAoPEFMUEhBX05VTT4gfCAnLScpKyAnfSchID0-IGpvaW4gOwpoZGRlbGltOiAoPGlkZWxpbT4gfCA8Tkw-IHwgInsjIikgOwpoZGJvZHk6IDxlYm9keT4oPy08aGRkZWxpbT4hKSA9PiBqb2luIDsKaGQ6IDxMU1RBUlQ-IDxoZGxldmVsPiA8V1MwPgogICAgKDx3aWtpcmVmPiB8IDxpbWc-IHwgPGZucmVmPiB8IDxsaW5rPiB8IDxib2xkPiB8IDxpdGFsaWM-IHwgPHN0cmlrZT4gfCA8Y29kZT4gfCA8ZW1vamk-IHwgPGtiZD4gfCA8c3ViPiB8IDxzdXA-IHwgPGhkYm9keT4pKiA8aGRpZD4_IDxETkwxPiA7CgpsaWxldmVsOiAnICcqID0-IGNvdW50IDsKb2xpZDogKDxBTFBIQT4rfDxESUdJVD4rKSA9PiBqb2luIDsKdWxpbml0OiA8RE5MPiA8V1MwPiAnLSchIDsKb2xpbml0OiA8RE5MPiA8V1MwPiA8b2xpZD4hICcuJyEgOwpsaWRlbGltOiAoPGRlbGltPiB8IDx1bGluaXQ-IHwgPG9saW5pdD4pIDsKbGlib2R5OiA8ZWJvZHk-KD8tPGxpZGVsaW0-ISkgPT4gam9pbiA7CnRvZG86ICdbJyEgWyB4WF0gJ10nISA8V1MxPiA9PiBob2lzdFIgOwp1bGl0ZW06IDxMU1RBUlQ-IDxsaWxldmVsPiAiLSAiISA8dG9kbz4_CiAgICAgICAgKDx3aWtpcmVmPiB8IDxpbWc-IHwgPGZucmVmPiB8IDxsaW5rPiB8IDxib2xkPiB8IDxpdGFsaWM-IHwgPHN0cmlrZT4gfCA8Y29kZT4gfCA8ZW1vamk-IHwgPGtiZD4gfCA8c3ViPiB8IDxzdXA-IHwgPGxpYm9keT4gKSogPEROTD4gOwpvbGl0ZW06IDxMU1RBUlQ-IDxsaWxldmVsPiA8b2xpZD4gIi4gIiEgPHRvZG8-PwogICAgICAgICg8d2lraXJlZj4gfCA8aW1nPiB8IDxmbnJlZj4gfCA8bGluaz4gfCA8Ym9sZD4gfCA8aXRhbGljPiB8IDxzdHJpa2U-IHwgPGNvZGU-IHwgPGVtb2ppPiB8IDxrYmQ-IHwgPHN1Yj4gfCA8c3VwPiB8IDxsaWJvZHk-ICkqIDxETkw-IDsKbGlzdDogKDx1bGl0ZW0-IHwgPG9saXRlbT4pKyA8RE5MMT4gOwoKY2JkZWxpbTogPExTVEFSVD4gImBgYCIhIDsKY29kZWJsb2NrOiA8Y2JkZWxpbT4hIDxjb2RlbWV0YT4gPGNvZGVib2R5PiA8RE5MMT4gOwpjb2RlbWV0YTogLig_KzxOTD4hKSA9PiBqb2luIDsKY29kZWJvZHk6IDxjYmJvZHk-KD8rPGNiZGVsaW0-KSA9PiBqb2luIDsKCmN1ZGVsaW06IDxMU1RBUlQ-ICI6OjoiISA7CmN1c3RvbWJsb2NrOiA8Y3VkZWxpbT4hIDxjdXN0b21tZXRhPiA8Y3VzdG9tYm9keT4gPEROTDE-IDsKY3VzdG9tbWV0YTogLig_KzxOTD4hKSA9PiBqb2luIDsKY3VzdG9tYm9keTogPGN1Ym9keT4oPys8Y3VkZWxpbT4pID0-IGpvaW4gOwoKbWV0YWJsb2NrOiA8TFNUQVJUPiAie3t7IiEgPG1ldGFib2R5PiA8RE5MMT4gOwptZXRhYm9keTogPG1ib2R5Pig_KzxtZXRhZW5kPiEpID0-IGpvaW4gOwptZXRhZW5kOiAifX19IiA8TEVORD4gOwoKYnFsZXZlbDogJz4nKyA9PiBjb3VudCA7CmJxZGVsaW06ICg8aWRlbGltPiB8IDxOTD4pIDsKYnFib2R5OiA8ZWJvZHk-KD8tPGJxZGVsaW0-ISkgPT4gam9pbiA7CmJxbGluZTogPExTVEFSVD4gPGJxbGV2ZWw-ICcgJz8hCiAgICAgICAgKDx3aWtpcmVmPiB8IDxpbWc-IHwgPGZucmVmPiB8IDxsaW5rPiB8IDxib2xkPiB8IDxpdGFsaWM-IHwgPHN0cmlrZT4gfCA8Y29kZT4gfCA8ZW1vamk-IHwgPGtiZD4gfCA8c3ViPiB8IDxzdXA-IHwgPGJxbGJyPiB8IDxicWJvZHk-KSogPEROTD4gOwpicWxicjogJ1xcJyEoPy08RE5MPikgOwpicXVvdGU6IDxicWxpbmU-KyA8RE5MMT4gOwoKdGRlbGltOiAoPGlkZWxpbT4gfCAnfCcgKSA7CnRib2R5OiA8ZWJvZHk-KD8tPHRkZWxpbT4hKSA9PiBqb2luIDsKdGNlbGw6IDxXUzA-ICg8d2lraXJlZj4gfCA8aW1nPiB8IDxmbnJlZj4gfCA8bGluaz4gfCA8Ym9sZD4gfCA8aXRhbGljPiB8IDxzdHJpa2U-IHwgPGNvZGU-IHwgPGVtb2ppPiB8IDxrYmQ-IHwgPHN1Yj4gfCA8c3VwPiB8IDx0Ym9keT4gKSogJ3wnISA7CnRyb3c6IDxMU1RBUlQ-ICd8JyEgPHRjZWxsPig_KzxETkw-KSA7CnRhYmxlOiA8dHJvdz4rIDxETkwxPiA7CgpocjogIi0tIiAnLScrIDxETkwxPiA9PiBqb2luIDsKCm1haW46IDxXUzA-ICg8aGQ-IHwgPGxpc3Q-IHwgPGJxdW90ZT4gfCA8Y29kZWJsb2NrPiB8IDxjdXN0b21ibG9jaz4gfCA8bWV0YWJsb2NrPiB8IDx0YWJsZT4gfCA8aHI-IHwgPGZub3RlPiB8IDxsaW5rZGVmPiB8IDxjb21tZW50PiB8IDxwYXJhPiApKiA7pG1haW7aAtQjIEhlbGxvIFwhXFsgOndvcmxkOjpzbWlsZTpbXmFiY10geyNoZDF9CgojIyBhYmMgeyNoZDJ9CiMjIGRlZiA8c3VwPkNvbnRyb2w8L3N1cD4KCmEgXDwgYgoKTGV0IGBleHAyKHgpID0gMioqeGAKClByZXNzIDxrYmQ-Q29udHJvbDwva2JkPiArIDxrYmQ-Ujwva2JkPiB0byA8IS0tc2tpcC0tPiByZWxvYWQuCgo6OjphbGVydApUZXN0IFtsYWJlbF1bcmVmcmVmXQo6OjoKCi0tLQoKe3t7IHsiaWQiOiAxMjMsICJwcm90ZWN0ZWQiOiB0cnVlIH0gfX19ClNvIFtbc21pbGVfZm9vMTJdXSDwn5iNIHdoYXQncyBbdGhpc10oLi90aGlzLm1kKVteMl0gdGhlbjoKCkEgW2xpbmsgX3dpdGggKipuZXN0ZWQgfn5mb3JtYXR-fioqIGFuZCBgcmVmZXJlbmNlYF9dW3JlZnJlZl0uLi4KCmBgYHRzIHRhbmdsZTpubyBsbjp5ZXMKY29uc3QgcmUgPSAvW1xuXHJdKy9nOwovLyB0aGUgZW5kCmBgYAoKMS4gZm9vCiAgNC4gYmFyCiAgICAgYmF6IGFuZCBzbyBvbgogICAgIG11bHRpbGluZQoyLiBbIF0gdGFzawozLiBbeF0gKipkb25lKioKCj4gYmxvY2txdW90ZQo-IG11bHRpbGluZSB0b29cCj4-IG5lc3RlZAo-Pj4gbGV2ZWwgMwo-IC0tIHNvdXJjZQoKfCBmb28gfCBiYXIgfAp8Oi06fC06fAp8IFtbcmVmIzIyXV0gfCBgb25gICoqb2ZmKiogfAoKW15hYmNdOiBGb28gX2Jhcl8gYmF6CgpbXjJdOiBUT0RPCgpbcmVmcmVmXTogaHR0cDovL3RoaS5uZy8KW2Zvb106IC4vZm9vLm1kI2JhcgoK2VA8IS0tCmlnbm9yZQptdWx0aWxpbmUKLS0-CgoxMS4gYWJjCiAgLSB4eXoKMjEuIHh4eAogIGEuIGZvbwogICAgYi4gYmFyCjMxLiB5eXkKCtlSfCBjb2wxIHwgY29sMiB8CnwgLS0tLSB8IC0tLS06fAp8IDxrYmQ-Q3RybDwva2JkPiB8IDxzdXA-Ujwvc3VwPiBgW1thYmNcfGlkXV1gIHwKCrkqKjp3YXJuOiBhYmMKZGVmIDpvazoqKgoK2Ug-IGxpbmUxCj4-ICoqbGluZSAyKipcCj4-IGxpbmUgMmEKPj4gbGluZSAyYgo-PiAKPj4gbGluZSAyYwo-Pj4gbGluZSAzCgo
3
3
  import { peek } from "@thi.ng/arrays/peek";
4
4
  import { isArray } from "@thi.ng/checks/is-array";
5
5
  import { isPlainObject } from "@thi.ng/checks/is-plain-object";
@@ -15,84 +15,97 @@ export const GRAMMAR = defGrammar(`
15
15
  DNL1: <DNL>+ => discard ;
16
16
  DNL2: <NL>{2,} ;
17
17
  lbr: '\\\\'! <NL>! ;
18
- esc: '\\\\'! ( '<' | '[' | '_' | '*' | '\`' | '~' | ':' ) ;
19
- inlinedelim: ( "![" | '[' | "**" | '_' | '\`' | '<' | '\\\\' | "~~" | " :" | <lbr> ) ;
20
- delim: ( <inlinedelim> | <DNL2> ) ;
21
- delim1: ( <inlinedelim> | <NL> ) ;
22
- body: .(?-<delim>!) => join ;
23
- body1: .(?-<delim1>!) => join ;
18
+ esc: '\\\\'! ('_' | '*' | '\`' | '~' | ':' | '-' | '<' | '>' | '[' | ']' | '(' | ')' | '{' | '}' | '#' | '.' | '|' | '\\\\') ;
19
+ cesc: '\\\\'! ('\`'| '\\\\') ;
20
+ cbesc: '\\\\'! '\`' ;
21
+ cuesc: '\\\\'! ':' ;
22
+ mesc: '\\\\'! '}' ;
23
+ idelim: ("![" | '[' | "**" | '_' | '\`' | '<' | "~~" | " :" | <lbr>) ;
24
+ delim: (<idelim> | <DNL2>) ;
25
+ ebody: (<esc>|.) ;
26
+ cbody: (<cesc>|.) ;
27
+ cbbody: (<cbesc>|.) ;
28
+ cubody: (<cuesc>|.) ;
29
+ mbody: (<mesc>|.) ;
30
+ body: <ebody>(?-<delim>!) => join ;
31
+ ibody: <ebody>(?-<idelim>!) => join ;
24
32
 
25
- wikiref: "[["! .(?+"]]"!) => join ;
33
+ comment: "<!--"! .(?+"-->") => discard ;
34
+ wikiref: "[["! <ebody>(?+"]]"!) => join ;
26
35
  fnref: "[^"! <label> ;
27
36
  fnote: <LSTART> "[^"! <fnlabel> <WS1> <para> ;
28
- fnlabel: .(?+"]:"!) => join ;
29
- label: .(?+']'!) => join ;
30
- target: .(?+')'!) => join ;
37
+ fnlabel: <ebody>(?+"]:"!) => join ;
38
+ label: <ebody>(?+']'!) => join ;
39
+ target: <ebody>(?+')'!) => join ;
31
40
  link: '['! <linklabel> '('! <target> ;
32
41
  linkref: '['! <linklabel> '['! <label> ;
33
42
  linkdef: <LSTART> '['! <label> ':'! <WS1> <ldtarget> ;
34
- ldtarget: .(?+<DNL1>) => join ;
35
- linklabel: (<img> | <bold> | <italic> | <strike> | <code> | <emoji> | <linkbody>)* ']'! ;
36
- linkdelim: ( "![" | '[' | "**" | '_' | "~~" | '\`' | ']') ;
37
- linkbody: .(?-<linkdelim>!) => join ;
43
+ ldtarget: <ebody>(?+<DNL1>) => join ;
44
+ linklabel: (<img> | <bold> | <italic> | <strike> | <code> | <emoji> | <kbd> | <sub> | <sup> | <linkbody>)* ']'! ;
45
+ linkdelim: ("![" | '[' | "**" | '_' | "~~" | '\`' | ']') ;
46
+ linkbody: <ebody>(?-<linkdelim>!) => join ;
38
47
  img: "!["! <label> '('! <target> ;
39
- bold: "**"! (<wikiref> | <img> | <fnref> | <linkref> | <link> | <italic> | <strike> | <code> | <emoji> | <body1>)* "**"! ;
40
- italic: '_'! (<wikiref> | <img> | <fnref> | <linkref> | <link> | <bold> | <strike> | <code> | <emoji> | <body1>)* '_'! ;
41
- strike: "~~"! (<wikiref> | <img> | <fnref> | <linkref> | <link> | <bold> | <italic> | <code> | <emoji> | <body1>)* "~~"! ;
42
- code: '\`'! .(?+'\`'!) => join ;
43
- kbd: "<kbd>"! .(?+"</kbd>"!) => join ;
44
- emoji: ' '? ':'! (<ALPHA_NUM> | '+' | '-')(?+':'!) => join ;
45
- para: (<wikiref> | <img> | <fnref> | <linkref> | <link> | <bold> | <italic> | <strike> | <code> | <kbd> | <emoji> | <lbr> | <esc> | <body>)* <DNL2>! ;
48
+ bold: "**"! (<wikiref> | <img> | <fnref> | <linkref> | <link> | <italic> | <strike> | <code> | <emoji> | <kbd> | <sub> | <sup> | <ibody>)* "**"! ;
49
+ italic: '_'! (<wikiref> | <img> | <fnref> | <linkref> | <link> | <bold> | <strike> | <code> | <emoji> | <kbd> | <sub> | <sup> | <ibody>)* '_'! ;
50
+ strike: "~~"! (<wikiref> | <img> | <fnref> | <linkref> | <link> | <bold> | <italic> | <code> | <emoji> | <kbd> | <sub> | <sup> | <ibody>)* "~~"! ;
51
+ code: '\`'! <cbody>(?+'\`'!) => join ;
52
+ kbd: "<kbd>"! <ebody>(?+"</kbd>"!) => join ;
53
+ sub: "<sub>"! <ebody>(?+"</sub>"!) => join ;
54
+ sup: "<sup>"! <ebody>(?+"</sup>"!) => join ;
55
+ emoji: ' '? ':'! (<ALPHA_NUM> | '+')(?+':'!) => join ;
56
+ para: (<wikiref> | <img> | <fnref> | <linkref> | <link> | <bold> | <italic> | <strike> | <code> | <kbd> | <sub> | <sup> | <emoji> | <lbr> | <comment> | <body>)* <DNL2>! ;
46
57
 
47
58
  hdlevel: '#'+ => count ;
48
59
  hdid: "{#"! (<ALPHA_NUM> | '-')+ '}'! => join ;
49
- hddelim: ( <delim1> | "{#" ) ;
50
- hdbody: .(?-<hddelim>!) => join ;
60
+ hddelim: (<idelim> | <NL> | "{#") ;
61
+ hdbody: <ebody>(?-<hddelim>!) => join ;
51
62
  hd: <LSTART> <hdlevel> <WS0>
52
- (<wikiref> | <img> | <fnref> | <link> | <bold> | <italic> | <strike> | <code> | <emoji> | <hdbody> )* <hdid>? <DNL1> ;
63
+ (<wikiref> | <img> | <fnref> | <link> | <bold> | <italic> | <strike> | <code> | <emoji> | <kbd> | <sub> | <sup> | <hdbody>)* <hdid>? <DNL1> ;
53
64
 
54
65
  lilevel: ' '* => count ;
55
- uint: <DIGIT>+ => int ;
56
- ulid: <DNL> <WS0> '-'! ;
57
- olid: <DNL> <WS0> <DIGIT>+! '.'! ;
58
- lidelim: ( <delim> | <ulid> | <olid> ) ;
59
- libody: .(?-<lidelim>!) => join ;
66
+ olid: (<ALPHA>+|<DIGIT>+) => join ;
67
+ ulinit: <DNL> <WS0> '-'! ;
68
+ olinit: <DNL> <WS0> <olid>! '.'! ;
69
+ lidelim: (<delim> | <ulinit> | <olinit>) ;
70
+ libody: <ebody>(?-<lidelim>!) => join ;
60
71
  todo: '['! [ xX] ']'! <WS1> => hoistR ;
61
72
  ulitem: <LSTART> <lilevel> "- "! <todo>?
62
- (<wikiref> | <img> | <fnref> | <link> | <bold> | <italic> | <strike> | <code> | <emoji> | <libody> )* <DNL> ;
63
- olitem: <LSTART> <lilevel> <uint> ". "! <todo>?
64
- (<wikiref> | <img> | <fnref> | <link> | <bold> | <italic> | <strike> | <code> | <emoji> | <libody> )* <DNL> ;
73
+ (<wikiref> | <img> | <fnref> | <link> | <bold> | <italic> | <strike> | <code> | <emoji> | <kbd> | <sub> | <sup> | <libody> )* <DNL> ;
74
+ olitem: <LSTART> <lilevel> <olid> ". "! <todo>?
75
+ (<wikiref> | <img> | <fnref> | <link> | <bold> | <italic> | <strike> | <code> | <emoji> | <kbd> | <sub> | <sup> | <libody> )* <DNL> ;
65
76
  list: (<ulitem> | <olitem>)+ <DNL1> ;
66
77
 
67
78
  cbdelim: <LSTART> "\`\`\`"! ;
68
79
  codeblock: <cbdelim>! <codemeta> <codebody> <DNL1> ;
69
80
  codemeta: .(?+<NL>!) => join ;
70
- codebody: .(?+<cbdelim>) => join ;
81
+ codebody: <cbbody>(?+<cbdelim>) => join ;
71
82
 
72
- customdelim: <LSTART> ":::"! ;
73
- customblock: <customdelim>! <custommeta> <custombody> <DNL1> ;
83
+ cudelim: <LSTART> ":::"! ;
84
+ customblock: <cudelim>! <custommeta> <custombody> <DNL1> ;
74
85
  custommeta: .(?+<NL>!) => join ;
75
- custombody: .(?+<customdelim>) => join ;
86
+ custombody: <cubody>(?+<cudelim>) => join ;
76
87
 
77
88
  metablock: <LSTART> "{{{"! <metabody> <DNL1> ;
78
- metabody: .(?+<metaend>!) => join ;
89
+ metabody: <mbody>(?+<metaend>!) => join ;
79
90
  metaend: "}}}" <LEND> ;
80
91
 
81
92
  bqlevel: '>'+ => count ;
93
+ bqdelim: (<idelim> | <NL>) ;
94
+ bqbody: <ebody>(?-<bqdelim>!) => join ;
82
95
  bqline: <LSTART> <bqlevel> ' '?!
83
- (<wikiref> | <img> | <fnref> | <link> | <bold> | <italic> | <strike> | <code> | <emoji> | <bqlbr> | <body1>)* <DNL> ;
96
+ (<wikiref> | <img> | <fnref> | <link> | <bold> | <italic> | <strike> | <code> | <emoji> | <kbd> | <sub> | <sup> | <bqlbr> | <bqbody>)* <DNL> ;
84
97
  bqlbr: '\\\\'!(?-<DNL>) ;
85
98
  bquote: <bqline>+ <DNL1> ;
86
99
 
87
- tdelim: (<inlinedelim> | '|' ) ;
88
- tbody: .(?-<tdelim>!) => join ;
89
- tcell: <WS0> (<wikiref> | <img> | <fnref> | <link> | <bold> | <italic> | <strike> | <code> | <emoji> | <tbody> )* '|'! ;
100
+ tdelim: (<idelim> | '|' ) ;
101
+ tbody: <ebody>(?-<tdelim>!) => join ;
102
+ tcell: <WS0> (<wikiref> | <img> | <fnref> | <link> | <bold> | <italic> | <strike> | <code> | <emoji> | <kbd> | <sub> | <sup> | <tbody> )* '|'! ;
90
103
  trow: <LSTART> '|'! <tcell>(?+<DNL>) ;
91
104
  table: <trow>+ <DNL1> ;
92
105
 
93
106
  hr: "--" '-'+ <DNL1> => join ;
94
107
 
95
- main: <WS0> (<hd> | <list> | <bquote> | <codeblock> | <customblock> | <metablock> | <table> | <hr> | <fnote> | <linkdef> | <para>)* ;
108
+ main: <WS0> (<hd> | <list> | <bquote> | <codeblock> | <customblock> | <metablock> | <table> | <hr> | <fnote> | <linkdef> | <comment> | <para> )* ;
96
109
  `);
97
110
  export const DEFAULT_TAG_TRANSFORMS = {
98
111
  bold: (_, body) => ["strong", {}, ...body],
@@ -157,15 +170,17 @@ export const DEFAULT_TAG_TRANSFORMS = {
157
170
  ol: (_, items, meta) => ["ol", withMeta({}, meta), ...items],
158
171
  para: (_, body, meta) => ["p", withMeta({}, meta), ...body],
159
172
  strike: (_, body) => ["s", {}, ...body],
160
- table: (_, __align, head, rows, meta) => [
173
+ sub: (_, body) => ["sub", {}, body],
174
+ sup: (_, body) => ["sup", {}, body],
175
+ table: (ctx, head, rows, meta) => [
161
176
  "table",
162
- withMeta({ __align }, meta),
177
+ withMeta({ __align: ctx.align }, meta),
163
178
  ["thead", {}, head],
164
179
  ["tbody", {}, ...rows],
165
180
  ],
166
181
  tableCell: (_, body) => ["td", {}, ...body],
167
182
  tableHead: (_, body) => ["th", {}, ...body],
168
- tableRow: (_, __, cells) => ["tr", {}, ...cells],
183
+ tableRow: (_, cells) => ["tr", {}, ...cells],
169
184
  ul: (_, items, meta) => ["ul", withMeta({}, meta), ...items],
170
185
  ulitem: (_, attribs, body) => ["li", attribs, ...body],
171
186
  wikiref: (_, id, label) => [
@@ -240,6 +255,8 @@ export const defTransformContext = (tags, opts, linkRefs, logger) => ({
240
255
  hasFootnotes: false,
241
256
  logger,
242
257
  meta: null,
258
+ align: [],
259
+ column: 0,
243
260
  row: 0,
244
261
  opts: {
245
262
  escape: false,
@@ -259,10 +276,11 @@ export const transformScope = defmulti((x, ctx) => {
259
276
  ctx.logger && ctx.logger.debug(x);
260
277
  return x.id;
261
278
  }, {
262
- body1: "body",
279
+ bqbody: "body",
263
280
  bqlbr: "lbr",
264
281
  bqline: "repeat0",
265
282
  hdbody: "body",
283
+ ibody: "body",
266
284
  label: "body",
267
285
  libody: "body",
268
286
  linkbody: "body",
@@ -340,7 +358,6 @@ export const transformScope = defmulti((x, ctx) => {
340
358
  }
341
359
  __collect(acc, ctx.tags.emoji(ctx, result));
342
360
  },
343
- esc: (scope, ctx, acc) => acc.push(__escape(ctx, scope.children[0].result)),
344
361
  fnote: ({ children }, ctx) => {
345
362
  const body = [];
346
363
  const id = children[0].result;
@@ -369,7 +386,7 @@ export const transformScope = defmulti((x, ctx) => {
369
386
  },
370
387
  img: ({ children }, ctx, acc) => __collect(acc, ctx.tags.img(ctx, __escape(ctx, children[0].result.trim()), ...__linkTitle(children[1].result))),
371
388
  italic: (scope, ctx, acc) => __collect(acc, ctx.tags.italic(ctx, __children(ctx, scope.children))),
372
- kbd: (scope, ctx, acc) => __collect(acc, ctx.tags.kbd(ctx, scope.result)),
389
+ kbd: (scope, ctx, acc) => __collectPrim(scope, ctx, acc, "kbd"),
373
390
  lbr: (_, ctx, acc) => __collect(acc, ctx.tags.br(ctx)),
374
391
  link: ({ children }, ctx, acc) => __collect(acc, ctx.tags.link(ctx, ...__linkTitle(children[1].result), __children(ctx, children[0].children))),
375
392
  linkdef: ({ children }, ctx) => {
@@ -435,28 +452,37 @@ export const transformScope = defmulti((x, ctx) => {
435
452
  ctx.meta = null;
436
453
  },
437
454
  strike: (scope, ctx, acc) => __collect(acc, ctx.tags.strike(ctx, __children(ctx, scope.children))),
455
+ sub: (scope, ctx, acc) => __collectPrim(scope, ctx, acc, "sub"),
456
+ sup: (scope, ctx, acc) => __collectPrim(scope, ctx, acc, "sup"),
438
457
  table: (scope, ctx, acc) => {
439
458
  const children = scope.children[0].children;
440
459
  const head = [];
441
460
  const rows = [];
442
461
  ctx.row = 0;
443
- transformScope(children[0], ctx, head);
444
- let align;
445
462
  if (children.length > 1) {
446
- align = __columnAlignments(children[1].children[0].children);
463
+ ctx.align = __columnAlignments(children[1].children[0].children);
447
464
  for (let i = 2, n = children.length; i < n; i++) {
448
465
  ctx.row = i - 1;
449
466
  transformScope(children[i], ctx, rows);
450
467
  }
451
468
  }
452
469
  else {
453
- align = new Array(children[0].children[0].children.length).fill("left");
470
+ ctx.align = new Array(children[0].children[0].children.length).fill("left");
454
471
  }
455
- __collect(acc, ctx.tags.table(ctx, align, head[0], rows, ctx.meta));
472
+ ctx.row = 0;
473
+ transformScope(children[0], ctx, head);
474
+ __collect(acc, ctx.tags.table(ctx, head[0], rows, ctx.meta));
456
475
  ctx.meta = null;
457
476
  },
458
477
  tcell: ({ children }, ctx, acc) => __collect(acc, (ctx.row > 0 ? ctx.tags.tableCell : ctx.tags.tableHead)(ctx, __trimBody(__children(ctx, children)))),
459
- trow: ({ children }, ctx, acc) => __collect(acc, ctx.tags.tableRow(ctx, ctx.row, __children(ctx, children[0].children))),
478
+ trow: (scope, ctx, acc) => {
479
+ const cells = [];
480
+ scope.children[0].children.forEach((c, i) => {
481
+ ctx.column = i;
482
+ transformScope(c, ctx, cells);
483
+ });
484
+ __collect(acc, ctx.tags.tableRow(ctx, cells));
485
+ },
460
486
  ulitem: ({ children }, ctx, acc) => {
461
487
  const body = [];
462
488
  transformScope(children[2], ctx, body);
@@ -468,7 +494,7 @@ export const transformScope = defmulti((x, ctx) => {
468
494
  },
469
495
  wikiref: (scope, ctx, acc) => {
470
496
  const [id, label] = scope.result.split("|");
471
- __collect(acc, ctx.tags.wikiref(ctx, id, label));
497
+ __collect(acc, ctx.tags.wikiref(ctx, id.trim(), label?.trim()));
472
498
  },
473
499
  });
474
500
  /**
@@ -501,6 +527,7 @@ export const extractBody = (body, acc = []) => {
501
527
  };
502
528
  /** @internal */
503
529
  const __collect = (acc, x) => x != null && acc.push(x);
530
+ const __collectPrim = (scope, ctx, acc, tag) => __collect(acc, ctx.tags[tag](ctx, scope.result));
504
531
  /** @internal */
505
532
  const __children = (ctx, children, acc = []) => {
506
533
  for (let c of children)
@@ -520,6 +547,11 @@ const __listItemAttribs = (scope) => scope?.id === "todo"
520
547
  const __trimBody = (body) => {
521
548
  if (body.length === 1 && isString(body[0]))
522
549
  body[0] = body[0].trim();
550
+ else {
551
+ const last = peek(body);
552
+ if (isString(last) && /^\s+$/.test(last))
553
+ body.pop();
554
+ }
523
555
  return body;
524
556
  };
525
557
  /** @internal */