@thi.ng/hiccup-markdown 3.0.1 → 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-02T18:09:03Z
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,41 @@ 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
+
37
+ ## [3.1.0](https://github.com/thi-ng/umbrella/tree/@thi.ng/hiccup-markdown@3.1.0) (2023-03-05)
38
+
39
+ #### 🚀 Features
40
+
41
+ - add anchor ID syntax support for headings ([86ce79e](https://github.com/thi-ng/umbrella/commit/86ce79e))
42
+ - update parse grammar & hd parser
43
+ - support for `{#id}` suffixes for defining heading anchor IDs
44
+ - only auto-generate anchor IDs if not explicitly specified
45
+ - update tests
46
+
12
47
  # [3.0.0](https://github.com/thi-ng/umbrella/tree/@thi.ng/hiccup-markdown@3.0.0) (2023-02-27)
13
48
 
14
49
  #### 🛑 Breaking changes
package/README.md CHANGED
@@ -13,10 +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)
21
+ - [Heading with anchor {#custom-id-123}](#heading-with-anchor-custom-id-123)
22
+ - [Images](#images)
23
+ - [Link formats](#link-formats)
24
+ - [Lists](#lists)
20
25
  - [Metadata](#metadata)
21
26
  - [Customizing tag transforms](#customizing-tag-transforms)
22
27
  - [Serializing to HTML](#serializing-to-html)
@@ -25,6 +30,7 @@ This project is part of the
25
30
  - [Behaviors](#behaviors)
26
31
  - [Usage examples](#usage-examples)
27
32
  - [Status](#status)
33
+ - [Related packages](#related-packages)
28
34
  - [Installation](#installation)
29
35
  - [Dependencies](#dependencies)
30
36
  - [Usage examples](#usage-examples)
@@ -36,7 +42,8 @@ This project is part of the
36
42
 
37
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).
38
44
 
39
- **⚠️ 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). ⚠️**
40
47
 
41
48
  This package provides both a customizable
42
49
  [Markdown](https://en.wikipedia.org/wiki/Markdown)-to-[Hiccup](https://github.com/thi-ng/umbrella/tree/develop/packages/hiccup)
@@ -44,48 +51,47 @@ parser and an extensible Hiccup-to-Markdown converter.
44
51
 
45
52
  ## Parser
46
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
+
47
59
  ### Basic features
48
60
 
49
- The parser itself is not aimed at supporting **all** of Markdown's
50
- (CommonMark's) quirky syntax features, but restricts itself to a sane subset of
51
- 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
52
64
  features](#additional-syntax--parser-featuresrestrictions) not part of the
53
- standard syntax.
54
-
55
- | Feature | Comments |
56
- |---------------|--------------------------------------------------------------------------------------------------------------------------------------------|
57
- | Blockquotes | Nestable, support for inline formatting and forced line breaks (trailing `\`) |
58
- | Code blocks | GFM style only (triple backtick prefix), w/ mandatory language hint & optional extra headers information |
59
- | Formatting | Nestable **bold**, _italic_, `code`, ~~strike~~, <kbd>Key</kbd> supported in paragraphs, headings, link labels, lists, blockquotes, tables |
60
- | Footnotes | Supported and stored separately in parse context |
61
- | Headings | ATX-style only (`#` line prefix), any level |
62
- | Horiz. Rulers | Only dash supported (e.g. `---`), min 2 chars required, length retained for downstream transformations |
63
- | HTML elements | Unsupported |
64
- | Images | Alt text is required, image can be used in link labels |
65
- | Links | Supports `[label](target)`, `[label][ref]`, `[[page id]]` or `[[page id|label]]` style links, inline formats in label |
66
- | Lists | Ordered & unordered, nestable, inline formatting, line breaks, GFM task list items |
67
- | Paragraphs | Support for forced line breaks (trailing `\`) |
68
- | 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...**
69
88
 
70
89
  ### Additional syntax & parser features/restrictions
71
90
 
72
- #### Formatting
73
-
74
- To avoid ambiguity and simplify nesting, only the following formatting syntax is
75
- supported for bold & italic:
76
-
77
- - `**bold**`
78
- - `_italic_`
91
+ #### Code blocks
79
92
 
80
- `code` (\`) and ~~strikethrough~~ (`~~`) as usual...
81
-
82
- For keyboard commands `<kbd>` can be used, e.g.:\
83
- `<kbd>Control</kbd> + <kbd>R</kbd>`
84
-
85
- #### Code block headers
86
-
87
- In addition to the mandatory language hint, code blocks support optional user
88
- 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
89
95
  [@thi.ng/tangle](https://github.com/thi-ng/umbrella/tree/develop/packages/tangle)
90
96
  for concrete use cases).
91
97
 
@@ -100,8 +106,8 @@ layout breakage)
100
106
 
101
107
  #### Custom blocks
102
108
 
103
- Since the parser does not directly transform Markdown into HTML, blocks of custom
104
- 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
105
111
  components, diagrams/visualizations etc.). Similarly to code blocks, custom
106
112
  blocks are wrapped with `:::` and a type specifier:
107
113
 
@@ -128,30 +134,93 @@ transformer. The default handler merely creates an element like this:
128
134
  **Tip:** Use a
129
135
  [`defmulti()`](https://github.com/thi-ng/umbrella/tree/develop/packages/defmulti)
130
136
  polymorphic function as tag transformer to elegantly handle multiple types of
131
- 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>
132
165
 
133
166
  #### Headings with anchor IDs
134
167
 
135
- The default tag transform for headlines auto-generates ID attributes using that
136
- headline's body and
137
- [slugifying](https://docs.thi.ng/umbrella/strings/functions/slugifyGH.html) it
138
- (Github readme compatible):
168
+ The parser supports `{#custom-id}`-style line suffixes for headings, which are
169
+ passed as separate `anchorID` param to the element handlers. If not specified in
170
+ the Markdown source, the parser auto-generates this ID (with no uniqueness
171
+ guarantee) based on
172
+ [slugifying](https://docs.thi.ng/umbrella/strings/functions/slugifyGH.html) the
173
+ heading's body content (Github readme compatible):
139
174
 
140
175
  ```text
141
176
  # The **beautiful `code`**
177
+
178
+ ## Heading with anchor {#custom-id-123}
142
179
  ```
143
180
 
144
181
  Results in:
145
182
 
146
183
  ```js
147
184
  // [
148
- // "h1",
149
- // { id: "the-beautiful-code" },
150
- // "The ",
151
- // [ "strong", {}, "beautiful ", [ "code", {}, "code" ] ]
185
+ // [
186
+ // "h1",
187
+ // { id: "the-beautiful-code" },
188
+ // "The ",
189
+ // [ "strong", {}, "beautiful ", [ "code", {}, "code" ] ]
190
+ // ],
191
+ // [ "h2", { id: "custom-id-123" }, "Heading with anchor" ]
152
192
  // ]
153
193
  ```
154
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
+
155
224
  #### Metadata
156
225
 
157
226
  Arbitrary metadata can be assigned to _any_ block level element:
@@ -442,6 +511,10 @@ More info [here](http://thi.ng/hiccup-markdown).
442
511
 
443
512
  [Search or submit any issues for this package](https://github.com/thi-ng/umbrella/issues?q=%5Bhiccup-markdown%5D+in%3Atitle)
444
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
+
445
518
  ## Installation
446
519
 
447
520
  ```bash
@@ -462,7 +535,7 @@ For Node.js REPL:
462
535
  const hiccupMarkdown = await import("@thi.ng/hiccup-markdown");
463
536
  ```
464
537
 
465
- Package sizes (brotli'd, pre-treeshake): ESM: 4.35 KB
538
+ Package sizes (brotli'd, pre-treeshake): ESM: 4.62 KB
466
539
 
467
540
  ## Dependencies
468
541
 
package/api.d.ts CHANGED
@@ -110,11 +110,11 @@ export interface TagTransforms {
110
110
  *
111
111
  * @param ctx
112
112
  * @param level
113
- * @param id
113
+ * @param anchorID
114
114
  * @param body
115
115
  * @param meta
116
116
  */
117
- heading(ctx: TransformCtx, level: number, id: string, body: any[], meta?: any): any;
117
+ heading(ctx: TransformCtx, level: number, anchorID: string, body: any[], meta?: any): any;
118
118
  /**
119
119
  * Horizontal rule handler. Also receives number of dashes used (e.g. to
120
120
  * create different representations/styling)
@@ -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.0.1",
3
+ "version": "3.2.0",
4
4
  "description": "Markdown parser & serializer from/to Hiccup format",
5
5
  "type": "module",
6
6
  "module": "./index.js",
@@ -35,16 +35,16 @@
35
35
  },
36
36
  "dependencies": {
37
37
  "@thi.ng/api": "^8.7.3",
38
- "@thi.ng/arrays": "^2.5.6",
38
+ "@thi.ng/arrays": "^2.5.7",
39
39
  "@thi.ng/checks": "^3.3.9",
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.34",
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",
47
- "@thi.ng/text-canvas": "^2.4.31"
47
+ "@thi.ng/text-canvas": "^2.4.32"
48
48
  },
49
49
  "devDependencies": {
50
50
  "@microsoft/api-extractor": "^7.34.2",
@@ -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": "a2915dee637c1b8cd2e11a78a23bd035e4f750e7\n"
101
+ "gitHead": "31dd56eba72edd73a3ced5002c0f5145ce3d42d6\n"
99
102
  }
package/parse.js CHANGED
@@ -1,5 +1,5 @@
1
- // Downloaded @ 2023-02-26T13:59:47.034Z
2
- // Source: https://demo.thi.ng/umbrella/parse-playground/#ldoNR0ROTDE6IDxETkw-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-IGNvdW50IDsKaGQ6IDxMU1RBUlQ-IDxoZGxldmVsPiA8V1MwPgogICAgKDx3aWtpcmVmPiB8IDxpbWc-IHwgPGZucmVmPiB8IDxsaW5rPiB8IDxib2xkPiB8IDxpdGFsaWM-IHwgPHN0cmlrZT4gfCA8Y29kZT4gfCA8ZW1vamk-IHwgPGJvZHkxPiApKiA8RE5MMT4gOwoKbGlsZXZlbDogJyAnKiA9PiBjb3VudCA7CnVpbnQ6IDxESUdJVD4rID0-IGludCA7CnVsaWQ6IDxETkw-IDxXUzA-ICctJyEgOwpvbGlkOiA8RE5MPiA8V1MwPiA8RElHSVQ-KyEgJy4nISA7CmxpZGVsaW06ICggPGRlbGltPiB8IDx1bGlkPiB8IDxvbGlkPiApIDsKbGlib2R5OiAuKD8tPGxpZGVsaW0-ISkgPT4gam9pbiA7CnRvZG86ICdbJyEgWyB4WF0gJ10nISA8V1MxPiA9PiBob2lzdFIgOwp1bGl0ZW06IDxMU1RBUlQ-IDxsaWxldmVsPiAiLSAiISA8dG9kbz4_CiAgICAgICAgKDx3aWtpcmVmPiB8IDxpbWc-IHwgPGZucmVmPiB8IDxsaW5rPiB8IDxib2xkPiB8IDxpdGFsaWM-IHwgPHN0cmlrZT4gfCA8Y29kZT4gfCA8ZW1vamk-IHwgPGxpYm9keT4gKSogPEROTD4gOwpvbGl0ZW06IDxMU1RBUlQ-IDxsaWxldmVsPiA8dWludD4gIi4gIiEgPHRvZG8-PwogICAgICAgICg8d2lraXJlZj4gfCA8aW1nPiB8IDxmbnJlZj4gfCA8bGluaz4gfCA8Ym9sZD4gfCA8aXRhbGljPiB8IDxzdHJpa2U-IHwgPGNvZGU-IHwgPGVtb2ppPiB8IDxsaWJvZHk-ICkqIDxETkw-IDsKbGlzdDogKDx1bGl0ZW0-IHwgPG9saXRlbT4pKyA8RE5MMT4gOwoKY2JkZWxpbTogPExTVEFSVD4gImBgYCIhIDsKY29kZWJsb2NrOiA8Y2JkZWxpbT4hIDxjb2RlbWV0YT4gPGNvZGVib2R5PiA8RE5MMT4gOwpjb2RlbWV0YTogLig_KzxOTD4hKSA9PiBqb2luIDsKY29kZWJvZHk6IC4oPys8Y2JkZWxpbT4pID0-IGpvaW4gOwoKY3VzdG9tZGVsaW06IDxMU1RBUlQ-ICI6OjoiISA7CmN1c3RvbWJsb2NrOiA8Y3VzdG9tZGVsaW0-ISA8Y3VzdG9tbWV0YT4gPGN1c3RvbWJvZHk-IDxETkwxPiA7CmN1c3RvbW1ldGE6IC4oPys8Tkw-ISkgPT4gam9pbiA7CmN1c3RvbWJvZHk6IC4oPys8Y3VzdG9tZGVsaW0-KSA9PiBqb2luIDsKCm1ldGFibG9jazogPExTVEFSVD4gInt7eyIhIDxtZXRhYm9keT4gPEROTDE-IDsKbWV0YWJvZHk6IC4oPys8bWV0YWVuZD4hKSA9PiBqb2luIDsKbWV0YWVuZDogIn19fSIgPExFTkQ-IDsKCmJxbGV2ZWw6ICc-JysgPT4gY291bnQgOwpicWxpbmU6IDxMU1RBUlQ-IDxicWxldmVsPiAnICc_IQogICAgICAgICg8d2lraXJlZj4gfCA8aW1nPiB8IDxmbnJlZj4gfCA8bGluaz4gfCA8Ym9sZD4gfCA8aXRhbGljPiB8IDxzdHJpa2U-IHwgPGNvZGU-IHwgPGVtb2ppPiB8IDxicWxicj4gfCA8Ym9keTE-KSogPEROTD4gOwpicWxicjogJ1xcJyEoPy08RE5MPikgOwpicXVvdGU6IDxicWxpbmU-KyA8RE5MMT4gOwoKdGRlbGltOiAoPGlubGluZWRlbGltPiB8ICd8JyApIDsKdGJvZHk6IC4oPy08dGRlbGltPiEpID0-IGpvaW4gOwp0Y2VsbDogPFdTMD4gKDx3aWtpcmVmPiB8IDxpbWc-IHwgPGZucmVmPiB8IDxsaW5rPiB8IDxib2xkPiB8IDxpdGFsaWM-IHwgPHN0cmlrZT4gfCA8Y29kZT4gfCA8ZW1vamk-IHwgPHRib2R5PiApKiAnfCchIDsKdHJvdzogPExTVEFSVD4gJ3wnISA8dGNlbGw-KD8rPEROTD4pIDsKdGFibGU6IDx0cm93PisgPEROTDE-IDsKCmhyOiAiLS0iICctJysgPEROTDE-ID0-IGpvaW4gOwoKbWFpbjogPFdTMD4gKDxoZD4gfCA8bGlzdD4gfCA8YnF1b3RlPiB8IDxjb2RlYmxvY2s-IHwgPGN1c3RvbWJsb2NrPiB8IDxtZXRhYmxvY2s-IHwgPHRhYmxlPiB8IDxocj4gfCA8Zm5vdGU-IHwgPGxpbmtkZWY-IHwgPHBhcmE-KSogO6RtYWlu2gKLIyBIZWxsbyA6d29ybGQ6OnNtaWxlOlteYWJjXQoKYSBcPCBiCgpMZXQgYGV4cDIoeCkgPSAyKip4YAoKUHJlc3MgPGtiZD5Db250cm9sPC9rYmQ-ICsgPGtiZD5SPC9rYmQ-IHRvIHJlbG9hZC4KCjo6OmFsZXJ0ClRlc3QgW2xhYmVsXVtyZWZyZWZdCjo6OgoKLS0tCgp7e3sgeyJpZCI6IDEyMywgInByb3RlY3RlZCI6IHRydWUgfSB9fX0KU28gW1tzbWlsZV9mb28xMl1dIPCfmI0gd2hhdCdzIFt0aGlzXSguL3RoaXMubWQpW14yXSB0aGVuOgoKQSBbbGluayBfd2l0aCAqKm5lc3RlZCB-fmZvcm1hdH5-KiogYW5kIGByZWZlcmVuY2VgX11bcmVmcmVmXS4uLgoKYGBgdHMgdGFuZ2xlOm5vIGxuOnllcwpjb25zdCBmb28gPSA0MjsKLy8gdGhlIGVuZApgYGAKCjEuIGZvbwogIDQuIGJhcgogICAgIGJheiBhbmQgc28gb24KICAgICBtdWx0aWxpbmUKMi4gWyBdIHRhc2sKMy4gW3hdICoqZG9uZSoqCgo-IGJsb2NrcXVvdGUKPiBtdWx0aWxpbmUgdG9vXAo-PiBuZXN0ZWQKPj4-IGxldmVsIDMKPiAtLSBzb3VyY2UKCnwgZm9vIHwgYmFyIHwKfDotfC06fAp8IFtbcmVmIzIyXV0gfCBgb25gICoqb2ZmKiogfAoKW15hYmNdOiBGb28gX2Jhcl8gYmF6CgpbXjJdOiBUT0RPCgpbcmVmcmVmXTogaHR0cDovL3RoaS5uZy8KW2Zvb106IC4vZm9vLm1kI2JhcgoK2TUxMS4gYWJjCiAgLSB4eXoKMjEuIHh4eAogIDEuIGZvbwogICAgMS4gYmFyCjMxLiB5eXkKCtlWPiAqKmxldmVsIDFhKioKPj4gbGV2ZWwgMmEKPj4gbGV2ZWwgMmIKPgo-IGxldmVsIDFiCgpbKipmb28qKl0oYmFyKQoKKiohW2Zvb10oYmFyKSoqCgo
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,81 +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 ;
59
+ hdid: "{#"! (<ALPHA_NUM> | '-')+ '}'! => join ;
60
+ hddelim: (<idelim> | <NL> | "{#") ;
61
+ hdbody: <ebody>(?-<hddelim>!) => join ;
48
62
  hd: <LSTART> <hdlevel> <WS0>
49
- (<wikiref> | <img> | <fnref> | <link> | <bold> | <italic> | <strike> | <code> | <emoji> | <body1> )* <DNL1> ;
63
+ (<wikiref> | <img> | <fnref> | <link> | <bold> | <italic> | <strike> | <code> | <emoji> | <kbd> | <sub> | <sup> | <hdbody>)* <hdid>? <DNL1> ;
50
64
 
51
65
  lilevel: ' '* => count ;
52
- uint: <DIGIT>+ => int ;
53
- ulid: <DNL> <WS0> '-'! ;
54
- olid: <DNL> <WS0> <DIGIT>+! '.'! ;
55
- lidelim: ( <delim> | <ulid> | <olid> ) ;
56
- 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 ;
57
71
  todo: '['! [ xX] ']'! <WS1> => hoistR ;
58
72
  ulitem: <LSTART> <lilevel> "- "! <todo>?
59
- (<wikiref> | <img> | <fnref> | <link> | <bold> | <italic> | <strike> | <code> | <emoji> | <libody> )* <DNL> ;
60
- olitem: <LSTART> <lilevel> <uint> ". "! <todo>?
61
- (<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> ;
62
76
  list: (<ulitem> | <olitem>)+ <DNL1> ;
63
77
 
64
78
  cbdelim: <LSTART> "\`\`\`"! ;
65
79
  codeblock: <cbdelim>! <codemeta> <codebody> <DNL1> ;
66
80
  codemeta: .(?+<NL>!) => join ;
67
- codebody: .(?+<cbdelim>) => join ;
81
+ codebody: <cbbody>(?+<cbdelim>) => join ;
68
82
 
69
- customdelim: <LSTART> ":::"! ;
70
- customblock: <customdelim>! <custommeta> <custombody> <DNL1> ;
83
+ cudelim: <LSTART> ":::"! ;
84
+ customblock: <cudelim>! <custommeta> <custombody> <DNL1> ;
71
85
  custommeta: .(?+<NL>!) => join ;
72
- custombody: .(?+<customdelim>) => join ;
86
+ custombody: <cubody>(?+<cudelim>) => join ;
73
87
 
74
88
  metablock: <LSTART> "{{{"! <metabody> <DNL1> ;
75
- metabody: .(?+<metaend>!) => join ;
89
+ metabody: <mbody>(?+<metaend>!) => join ;
76
90
  metaend: "}}}" <LEND> ;
77
91
 
78
92
  bqlevel: '>'+ => count ;
93
+ bqdelim: (<idelim> | <NL>) ;
94
+ bqbody: <ebody>(?-<bqdelim>!) => join ;
79
95
  bqline: <LSTART> <bqlevel> ' '?!
80
- (<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> ;
81
97
  bqlbr: '\\\\'!(?-<DNL>) ;
82
98
  bquote: <bqline>+ <DNL1> ;
83
99
 
84
- tdelim: (<inlinedelim> | '|' ) ;
85
- tbody: .(?-<tdelim>!) => join ;
86
- 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> )* '|'! ;
87
103
  trow: <LSTART> '|'! <tcell>(?+<DNL>) ;
88
104
  table: <trow>+ <DNL1> ;
89
105
 
90
106
  hr: "--" '-'+ <DNL1> => join ;
91
107
 
92
- 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> )* ;
93
109
  `);
94
110
  export const DEFAULT_TAG_TRANSFORMS = {
95
111
  bold: (_, body) => ["strong", {}, ...body],
@@ -154,15 +170,17 @@ export const DEFAULT_TAG_TRANSFORMS = {
154
170
  ol: (_, items, meta) => ["ol", withMeta({}, meta), ...items],
155
171
  para: (_, body, meta) => ["p", withMeta({}, meta), ...body],
156
172
  strike: (_, body) => ["s", {}, ...body],
157
- table: (_, __align, head, rows, meta) => [
173
+ sub: (_, body) => ["sub", {}, body],
174
+ sup: (_, body) => ["sup", {}, body],
175
+ table: (ctx, head, rows, meta) => [
158
176
  "table",
159
- withMeta({ __align }, meta),
177
+ withMeta({ __align: ctx.align }, meta),
160
178
  ["thead", {}, head],
161
179
  ["tbody", {}, ...rows],
162
180
  ],
163
181
  tableCell: (_, body) => ["td", {}, ...body],
164
182
  tableHead: (_, body) => ["th", {}, ...body],
165
- tableRow: (_, __, cells) => ["tr", {}, ...cells],
183
+ tableRow: (_, cells) => ["tr", {}, ...cells],
166
184
  ul: (_, items, meta) => ["ul", withMeta({}, meta), ...items],
167
185
  ulitem: (_, attribs, body) => ["li", attribs, ...body],
168
186
  wikiref: (_, id, label) => [
@@ -237,6 +255,8 @@ export const defTransformContext = (tags, opts, linkRefs, logger) => ({
237
255
  hasFootnotes: false,
238
256
  logger,
239
257
  meta: null,
258
+ align: [],
259
+ column: 0,
240
260
  row: 0,
241
261
  opts: {
242
262
  escape: false,
@@ -256,9 +276,11 @@ export const transformScope = defmulti((x, ctx) => {
256
276
  ctx.logger && ctx.logger.debug(x);
257
277
  return x.id;
258
278
  }, {
259
- body1: "body",
279
+ bqbody: "body",
260
280
  bqlbr: "lbr",
261
281
  bqline: "repeat0",
282
+ hdbody: "body",
283
+ ibody: "body",
262
284
  label: "body",
263
285
  libody: "body",
264
286
  linkbody: "body",
@@ -336,7 +358,6 @@ export const transformScope = defmulti((x, ctx) => {
336
358
  }
337
359
  __collect(acc, ctx.tags.emoji(ctx, result));
338
360
  },
339
- esc: (scope, ctx, acc) => acc.push(__escape(ctx, scope.children[0].result)),
340
361
  fnote: ({ children }, ctx) => {
341
362
  const body = [];
342
363
  const id = children[0].result;
@@ -353,9 +374,10 @@ export const transformScope = defmulti((x, ctx) => {
353
374
  const body = [];
354
375
  const level = children[0].result;
355
376
  transformScope(children[1], ctx, body);
356
- ctx.headings.push({ level, body });
357
377
  __trimBody(body);
358
- __collect(acc, ctx.tags.heading(ctx, level, slugifyGH(extractBody(body).join("")), body, ctx.meta));
378
+ const slug = children[2]?.result || slugifyGH(extractBody(body).join(""));
379
+ ctx.headings.push({ level, body });
380
+ __collect(acc, ctx.tags.heading(ctx, level, slug, body, ctx.meta));
359
381
  ctx.meta = null;
360
382
  },
361
383
  hr: (scope, ctx, acc) => {
@@ -364,7 +386,7 @@ export const transformScope = defmulti((x, ctx) => {
364
386
  },
365
387
  img: ({ children }, ctx, acc) => __collect(acc, ctx.tags.img(ctx, __escape(ctx, children[0].result.trim()), ...__linkTitle(children[1].result))),
366
388
  italic: (scope, ctx, acc) => __collect(acc, ctx.tags.italic(ctx, __children(ctx, scope.children))),
367
- kbd: (scope, ctx, acc) => __collect(acc, ctx.tags.kbd(ctx, scope.result)),
389
+ kbd: (scope, ctx, acc) => __collectPrim(scope, ctx, acc, "kbd"),
368
390
  lbr: (_, ctx, acc) => __collect(acc, ctx.tags.br(ctx)),
369
391
  link: ({ children }, ctx, acc) => __collect(acc, ctx.tags.link(ctx, ...__linkTitle(children[1].result), __children(ctx, children[0].children))),
370
392
  linkdef: ({ children }, ctx) => {
@@ -430,28 +452,37 @@ export const transformScope = defmulti((x, ctx) => {
430
452
  ctx.meta = null;
431
453
  },
432
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"),
433
457
  table: (scope, ctx, acc) => {
434
458
  const children = scope.children[0].children;
435
459
  const head = [];
436
460
  const rows = [];
437
461
  ctx.row = 0;
438
- transformScope(children[0], ctx, head);
439
- let align;
440
462
  if (children.length > 1) {
441
- align = __columnAlignments(children[1].children[0].children);
463
+ ctx.align = __columnAlignments(children[1].children[0].children);
442
464
  for (let i = 2, n = children.length; i < n; i++) {
443
465
  ctx.row = i - 1;
444
466
  transformScope(children[i], ctx, rows);
445
467
  }
446
468
  }
447
469
  else {
448
- align = new Array(children[0].children[0].children.length).fill("left");
470
+ ctx.align = new Array(children[0].children[0].children.length).fill("left");
449
471
  }
450
- __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));
451
475
  ctx.meta = null;
452
476
  },
453
477
  tcell: ({ children }, ctx, acc) => __collect(acc, (ctx.row > 0 ? ctx.tags.tableCell : ctx.tags.tableHead)(ctx, __trimBody(__children(ctx, children)))),
454
- 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
+ },
455
486
  ulitem: ({ children }, ctx, acc) => {
456
487
  const body = [];
457
488
  transformScope(children[2], ctx, body);
@@ -463,7 +494,7 @@ export const transformScope = defmulti((x, ctx) => {
463
494
  },
464
495
  wikiref: (scope, ctx, acc) => {
465
496
  const [id, label] = scope.result.split("|");
466
- __collect(acc, ctx.tags.wikiref(ctx, id, label));
497
+ __collect(acc, ctx.tags.wikiref(ctx, id.trim(), label?.trim()));
467
498
  },
468
499
  });
469
500
  /**
@@ -496,6 +527,7 @@ export const extractBody = (body, acc = []) => {
496
527
  };
497
528
  /** @internal */
498
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));
499
531
  /** @internal */
500
532
  const __children = (ctx, children, acc = []) => {
501
533
  for (let c of children)
@@ -515,6 +547,11 @@ const __listItemAttribs = (scope) => scope?.id === "todo"
515
547
  const __trimBody = (body) => {
516
548
  if (body.length === 1 && isString(body[0]))
517
549
  body[0] = body[0].trim();
550
+ else {
551
+ const last = peek(body);
552
+ if (isString(last) && /^\s+$/.test(last))
553
+ body.pop();
554
+ }
518
555
  return body;
519
556
  };
520
557
  /** @internal */