@tabnas/markdown 0.7.10 → 0.7.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
  A CommonMark parser for the [Tabnas](https://github.com/tabnas/parser) engine.
4
4
 
5
5
  **This parser is conformant to CommonMark 0.31.2**, all 652 examples, across all 26
6
- sections of the spec suite, in both runtimes. The suite is vendored in this repository, so
6
+ sections of the spec suite, in all three runtimes. The suite is vendored in this repository, so
7
7
  the claim is checkable: `npm run conformance` reports 652/652, with no build step and no
8
8
  engine installed. It runs with the GFM extensions off, which is what measuring CommonMark
9
9
  conformance means: with `gfm: true` the extensions deliberately change nine of those
package/dist/block.d.ts CHANGED
@@ -189,7 +189,7 @@ export declare class BlockParser {
189
189
  * Not simply `offset`: a JavaScript string index counts UTF-16 units, so an
190
190
  * astral character (emoji, rare CJK) would advance the column by two. A
191
191
  * column means a character, so `\u{1F600} *x*` must report the same columns
192
- * in both runtimes.
192
+ * in every runtime.
193
193
  *
194
194
  * Counted incrementally from the last offset already measured. `addChild`
195
195
  * runs once per container opened, so counting from the start of the line
package/dist/block.js CHANGED
@@ -1186,7 +1186,7 @@ class BlockParser {
1186
1186
  * Not simply `offset`: a JavaScript string index counts UTF-16 units, so an
1187
1187
  * astral character (emoji, rare CJK) would advance the column by two. A
1188
1188
  * column means a character, so `\u{1F600} *x*` must report the same columns
1189
- * in both runtimes.
1189
+ * in every runtime.
1190
1190
  *
1191
1191
  * Counted incrementally from the last offset already measured. `addChild`
1192
1192
  * runs once per container opened, so counting from the start of the line
@@ -26,4 +26,4 @@ export type { ParserOptions } from './options.ts';
26
26
  export { renderHTML } from './html.ts';
27
27
  export { translate } from './translate';
28
28
  export type { TranslationPart, TranslationParts } from './translate';
29
- declare const VERSION = "0.7.10";
29
+ declare const VERSION = "0.7.12";
package/dist/markdown.js CHANGED
@@ -161,6 +161,6 @@ Object.defineProperty(exports, "translate", { enumerable: true, get: function ()
161
161
  // VERSION is this package's version. It MUST equal package.json "version":
162
162
  // the release orchestrator rewrites both, and the version test fails the
163
163
  // build if they drift. Mirrors `const VERSION` in go/markdown.go.
164
- const VERSION = '0.7.10';
164
+ const VERSION = '0.7.12';
165
165
  exports.VERSION = VERSION;
166
166
  //# sourceMappingURL=markdown.js.map
@@ -5,6 +5,7 @@ type TranslationPart = Readonly<{
5
5
  type TranslationParts = Readonly<{
6
6
  manifest: string;
7
7
  lift?: TranslationPart;
8
+ embed?: TranslationPart;
8
9
  render?: TranslationPart;
9
10
  }>;
10
11
  declare function translate(): TranslationParts | undefined;
package/dist/translate.js CHANGED
@@ -41,15 +41,26 @@ const TRANSLATION = Object.freeze({
41
41
  "tree"
42
42
  ],
43
43
  "writes": "records",
44
+ "root": "array",
45
+ "schema": "markdown-ast",
44
46
  "lift": "alchemy/lift.alc",
45
47
  "render": "alchemy/render.alc",
46
48
  "loss": [
47
- "Only a document holding one table is read as records: a document with no table, or with more than one, is refused.",
48
- "Inline markup in a cell is flattened to its text when it is read, an image to its alt text, and a written cell is plain text, so text that spells inline markup reads back as that markup.",
49
+ "The rows are the elements of the root array, and a root of another kind is written as one row.",
50
+ "The columns are the first row's, an object's keys or an array's positions labelled 0, 1 and so on, so a member or position of a later row that the first row lacks is not written, and one the first row has and a later row lacks is an empty cell.",
51
+ "When the first row is a scalar there is one column, named value, and every row is written whole in it, an array or an object as its compact JSON text.",
52
+ "A table of no columns, such as the one an empty array or object makes, is written as the empty document, which reads back as the empty table.",
53
+ "Read as records, a document with no table is the empty table, of no columns and no rows.",
54
+ "Read as records, a document with several tables is its first table, and the tables after it are not read.",
55
+ "Inline markup in a cell is flattened to its text when it is read, an image to its alt text; a written cell escapes every character that can begin inline markup, so it reads back as the text it was.",
56
+ "A null and a missing cell are both written as an empty cell, so the two cannot be told apart, or from an empty string, when read back.",
57
+ "A nested array or object in a cell is written as its compact JSON text.",
58
+ "A number that is not finite is written as Infinity, -Infinity or NaN, and as null inside a nested array or object, since JSON has no spelling for one.",
59
+ "Every value is written as text, so a number or a boolean reads back as a string.",
49
60
  "Column alignment is not kept.",
50
61
  "A line break in a cell is written as a space.",
51
- "Leading and trailing spaces in a cell are not kept.",
52
- "A U+0000 in a cell is written as it is, and the reader replaces it with U+FFFD when the table is read back."
62
+ "Whitespace at either end of a cell is not kept, as the reader trims it: the space, the tab, U+00A0, U+FEFF, U+2028, U+2029 and the other Unicode space separators, though not U+0085.",
63
+ "A U+0000 in a cell is written as U+FFFD, the character a Markdown reader puts in its place, so it reads back as U+FFFD."
53
64
  ]
54
65
  }
55
66
  }
@@ -79,26 +90,36 @@ const TRANSLATION = Object.freeze({
79
90
  ; cell of text beside emphasis, a link, an image, inline HTML or an
80
91
  ; autolink is their texts run together, \`Bob *b*\` as \`Bob b\`.
81
92
  ;
82
- ; The document must hold exactly one table: a second table, or none by
83
- ; the end, is a failure (INPUT_INVALID) that says so, since a Markdown
84
- ; document is read as records through its table and nothing else.
93
+ ; A Markdown document is read as records through its table and nothing
94
+ ; else, so the first table is the one read: a table after it is not
95
+ ; read, its rows and cells passed over as any other node's. A document
96
+ ; with no table is read as the empty table, a schema of no columns, no
97
+ ; row and \`table-end\`, which the render writes back as the empty
98
+ ; document. The manifest's loss lines say both.
85
99
  ;
86
100
  ; The state is one vector, [stack phase seen in-cell runs cells]:
87
101
  ; stack the open containers, one marker each: \`:array\`, \`:node\` for
88
102
  ; an object whose type is not known yet or is any other node,
89
103
  ; and \`:table\`, \`:row\` or \`:cell\` once its \`type\` has named
90
- ; one, so that its end is known when its object ends;
104
+ ; one, or \`:later\` for a table after the first, so that its
105
+ ; end is known when its object ends;
91
106
  ; phase \`:before\` the table, \`:header\` inside it until its first
92
- ; row ends, \`:body\` once the schema is written, \`:after\` it;
107
+ ; row ends, \`:body\` once the schema is written, \`:after\` it,
108
+ ; and \`:later\` inside a table after it, which is not read;
93
109
  ; seen what the last key was, \`:type\`, \`:text\` (a \`value\` or an
94
- ; \`alt\`) or \`:other\`, so that the scalar after it is classified;
110
+ ; \`alt\`) or \`:other\`, so that the scalar after it is classified,
111
+ ; and \`:none\` before the first event, so that events with no
112
+ ; value at all are told from a document with no table;
95
113
  ; in-cell whether a cell is open, so that text under it is the cell's;
96
114
  ; runs the open cell's runs of text, in order;
97
115
  ; cells the open row's cells, as wide as one row.
98
116
  ; It grows with the document's nesting and with one row's width, never
99
- ; with the table's length: a row is emitted when its object ends and
100
- ; dropped. Events no tree has (a key outside an object, an end with
101
- ; nothing open) fail with INPUT_INVALID, saying so.
117
+ ; with the table's length or the document's: a row is emitted when its
118
+ ; object ends and dropped, and a later table keeps nothing. Events no
119
+ ; tree has (a key outside an object, an end with nothing open), and
120
+ ; events of a node where the reader's tree never puts one (a row or a
121
+ ; cell outside a table, a table inside one), fail with
122
+ ; PROTOCOL_ORDER_ERROR, saying so.
102
123
 
103
124
  ; A column record of the TableRows protocol, as the standard library's
104
125
  ; \`public-column\` makes one.
@@ -138,31 +159,35 @@ def markdown-mark [marker stack]
138
159
  push marker (pop stack)
139
160
 
140
161
  ; The scalar after \`type\`: the three nodes the lift reads leave their
141
- ; marker on the stack and move the phase; any other node is \`:node\`.
162
+ ; marker on the stack and move the phase; a table after the first leaves
163
+ ; \`:later\`, and its rows and cells, like any other node, stay \`:node\`.
142
164
  def markdown-node [name stack phase in-cell runs cells]
143
165
  match name
144
166
  case "table"
145
167
  match phase
146
168
  case :before (transition [(markdown-mark :table stack) :header :other in-cell runs cells] [])
147
- case :after (fail "the document holds a second table, and only a document of one table is read as records")
148
- case _ (fail "the events hold a table inside a table, which the reader's tree never has")
169
+ case :after (transition [(markdown-mark :later stack) :later :other in-cell runs cells] [])
170
+ case _ (fail :protocol-order "the events hold a table inside a table, which the reader's tree never has")
149
171
  case "tableRow"
150
172
  match phase
151
- case :before (fail "the events hold a table row outside a table, which the reader's tree never has")
152
- case :after (fail "the events hold a table row outside a table, which the reader's tree never has")
173
+ case :before (fail :protocol-order "the events hold a table row outside a table, which the reader's tree never has")
174
+ case :after (fail :protocol-order "the events hold a table row outside a table, which the reader's tree never has")
175
+ case :later (transition [stack phase :other in-cell runs cells] [])
153
176
  case _ (transition [(markdown-mark :row stack) phase :other in-cell runs []] [])
154
177
  case "tableCell"
155
178
  match phase
156
- case :before (fail "the events hold a table cell outside a table, which the reader's tree never has")
157
- case :after (fail "the events hold a table cell outside a table, which the reader's tree never has")
179
+ case :before (fail :protocol-order "the events hold a table cell outside a table, which the reader's tree never has")
180
+ case :after (fail :protocol-order "the events hold a table cell outside a table, which the reader's tree never has")
181
+ case :later (transition [stack phase :other in-cell runs cells] [])
158
182
  case _
159
183
  if in-cell
160
- fail "the events hold a table cell inside a cell, which the reader's tree never has"
184
+ fail :protocol-order "the events hold a table cell inside a cell, which the reader's tree never has"
161
185
  transition [(markdown-mark :cell stack) phase :other true [] cells] []
162
186
  case _ (transition [stack phase :other in-cell runs cells] [])
163
187
 
164
188
  ; An object's end: a cell joins its row, a row is the schema or a
165
- ; record, the table is done, and any other node is dropped.
189
+ ; record, the table is done, a later table is passed over, and any other
190
+ ; node is dropped.
166
191
  def markdown-close [stack phase in-cell runs cells]
167
192
  match (markdown-top stack)
168
193
  case :cell
@@ -175,9 +200,11 @@ def markdown-close [stack phase in-cell runs cells]
175
200
  transition [(pop stack) phase :other in-cell runs []] [(row cells)]
176
201
  case :table
177
202
  transition [(pop stack) :after :other in-cell runs cells] []
203
+ case :later
204
+ transition [(pop stack) :after :other in-cell runs cells] []
178
205
  case :node
179
206
  transition [(pop stack) phase :other in-cell runs cells] []
180
- case _ (fail "the events end an object that is not open, which a tree's never do")
207
+ case _ (fail :protocol-order "the events end an object that is not open, which a tree's never do")
181
208
 
182
209
  def markdown-event [stack phase seen in-cell runs cells event]
183
210
  match event
@@ -187,8 +214,8 @@ def markdown-event [stack phase seen in-cell runs cells event]
187
214
  transition [(push :array stack) phase :other in-cell runs cells] []
188
215
  case (key name)
189
216
  match (markdown-top stack)
190
- case :none (fail "the events hold a key outside an object, which a tree's never do")
191
- case :array (fail "the events hold a key inside an array, which a tree's never do")
217
+ case :none (fail :protocol-order "the events hold a key outside an object, which a tree's never do")
218
+ case :array (fail :protocol-order "the events hold a key inside an array, which a tree's never do")
192
219
  case _ (transition [stack phase (markdown-seen name) in-cell runs cells] [])
193
220
  case (scalar value)
194
221
  match seen
@@ -202,27 +229,34 @@ def markdown-event [stack phase seen in-cell runs cells event]
202
229
  case array-end
203
230
  match (markdown-top stack)
204
231
  case :array (transition [(pop stack) phase :other in-cell runs cells] [])
205
- case _ (fail "the events end an array that is not open, which a tree's never do")
232
+ case _ (fail :protocol-order "the events end an array that is not open, which a tree's never do")
206
233
 
207
234
  def markdown-lift-step [s event]
208
235
  match s
209
236
  case [stack phase seen in-cell runs cells]
210
237
  markdown-event stack phase seen in-cell runs cells event
211
238
 
239
+ ; What the end of the events adds once every container is closed: the
240
+ ; table's end, or, for a document with no table, the empty table.
241
+ def markdown-lift-end [stack outputs]
242
+ match (count stack)
243
+ case 0 outputs
244
+ case _ (fail :protocol-order "the events ended inside a container, which a tree's never do")
245
+
212
246
  def markdown-lift-finish [s]
213
247
  match s
214
248
  case [stack phase seen in-cell runs cells]
215
- match phase
216
- case :after
217
- match (count stack)
218
- case 0 [table-end]
219
- case _ (fail "the events ended inside a container, which a tree's never do")
220
- case :before (fail "the document holds no table, and only a document of one table is read as records")
221
- case _ (fail "the events ended inside the table, which a tree's never do")
249
+ match seen
250
+ case :none (fail :protocol-order "the events hold no value, where a tree's hold one")
251
+ case _
252
+ match phase
253
+ case :after (markdown-lift-end stack [table-end])
254
+ case :before (markdown-lift-end stack [(schema []) table-end])
255
+ case _ (fail :protocol-order "the events ended inside a table, which a tree's never do")
222
256
 
223
257
  ; The lift: the document's events in, the table's records out.
224
258
  def markdown-lift [input]
225
- scan-emit [[] :before :other false [] []] markdown-lift-step markdown-lift-finish (events input)
259
+ scan-emit [[] :before :none false [] []] markdown-lift-step markdown-lift-finish (events input)
226
260
  ` }),
227
261
  render: Object.freeze({ entry: "markdown-render", source: `; Markdown's render: a table's records, the TableRows protocol (one
228
262
  ; \`schema\`, a \`row\` per record and \`table-end\`), written as one GFM pipe
@@ -235,45 +269,69 @@ def markdown-lift [input]
235
269
  ; \`| --- | --- |\`, and a row per record, each line ended by a line feed
236
270
  ; and every cell between \`| \` and \` |\`. A cell is its value's text as
237
271
  ; the CSV renderer writes it (\`scalar-text\`: a string as it is, a number
238
- ; by its lexeme, a boolean by its name, null and a missing cell as
272
+ ; by its lexeme, or by its word, \`Infinity\`, \`-Infinity\` or \`NaN\`, when
273
+ ; it is not finite, a boolean by its name, null and a missing cell as
239
274
  ; nothing, a vector or a record as its compact JSON text), with every
240
- ; backslash doubled and every pipe escaped as \`\\|\`, which the reader
241
- ; turns back into the characters (the reader's split skips the byte
242
- ; after a backslash, so the pipe's escape holds only when a backslash
243
- ; before it is itself escaped), and every line break replaced by a
244
- ; space, since a row is one line. No other character is escaped: the
245
- ; render cannot look at one (alchemy has no character test), so a cell
246
- ; is plain text, and text that spells inline markup reads back as that
247
- ; markup. A U+0000 in a cell is written as it is for the same reason,
248
- ; and the reader replaces it with U+FFFD, as CommonMark asks of every
249
- ; reader; the loss declaration says so. Alignment is not written: every
250
- ; column's delimiter is \`---\`.
275
+ ; backslash doubled, and every character that can begin inline markup
276
+ ; escaped with a backslash, as CommonMark lets any ASCII punctuation be
277
+ ; escaped: the pipe, which the reader's split skips after a backslash
278
+ ; (so its escape holds only when a backslash before it is itself
279
+ ; escaped), the backtick of a code span, the \`*\` and \`_\` of emphasis,
280
+ ; the brackets of a link, an image or a footnote, the \`<\` of an autolink
281
+ ; or of raw HTML, the \`&\` of an entity or numeric character reference,
282
+ ; and the \`~\` of GFM's strikethrough. The reader turns each escape back
283
+ ; into its character, so a written cell reads back as the text it was;
284
+ ; a URL that GFM reads as an autolink reads back as its own text, which
285
+ ; is the link's. Every line break is replaced by a space, since a row is
286
+ ; one line, and a U+0000 is written as U+FFFD, the character CommonMark
287
+ ; has every reader put in its place; the loss declaration says both.
288
+ ; Alignment is not written: every column's delimiter is \`---\`.
289
+ ;
290
+ ; A table of no columns, the one an empty array or object makes, has no
291
+ ; pipe table, since a row needs at least one cell: it is written as the
292
+ ; empty document, nothing at all, and so are its rows, which have no
293
+ ; cells. The lift reads that document back as the empty table, of no
294
+ ; columns and no rows.
251
295
  ;
252
296
  ; The state is one marker: \`:start\` before the schema, the schema's
253
297
  ; width (a number) while rows may follow, and \`:ended\` after
254
298
  ; \`table-end\`, so that a stream which breaks the protocol (a row before
255
299
  ; the schema, a second schema, a row of another width, anything after
256
- ; the end, no end) fails with INPUT_INVALID, saying so, rather than
257
- ; being written. The check is this file's rather than \`csv-table\`'s,
258
- ; whose refusals speak of CSV. A schema of no columns fails too: a row
259
- ; needs at least one cell, so such a table has no Markdown form.
300
+ ; the end, no end) fails with PROTOCOL_ORDER_ERROR, saying so, rather
301
+ ; than being written. The check is this file's rather than
302
+ ; \`csv-table\`'s, whose refusals speak of CSV.
260
303
 
261
304
  ; What \`scalar-text\` reads: null and a missing cell are written as
262
305
  ; nothing, where the CSV renderer's \`csv-options\` map a missing cell to
263
- ; an error.
306
+ ; an error, and a number that is not finite as its word.
264
307
  def markdown-options
265
308
  record
266
309
  entry :null-text ""
267
310
  entry :missing ""
311
+ entry :non-finite :literal
268
312
 
269
- ; A cell's text, as it stands between the pipes.
313
+ ; A cell's text, as it stands between the pipes: the backslashes doubled
314
+ ; first, so that the escapes written after them stay single.
270
315
  def markdown-cell [cell]
271
316
  replace-text "\\r" " "
272
317
  replace-text "\\n" " "
273
318
  replace-text "\\r\\n" " "
274
- replace-text "|" "\\\\|"
275
- replace-text "\\\\" "\\\\\\\\"
276
- scalar-text markdown-options cell
319
+ replace-text "\\u0000" "\\ufffd"
320
+ markdown-escape
321
+ replace-text "\\\\" "\\\\\\\\"
322
+ scalar-text markdown-options cell
323
+
324
+ ; Every character that can begin inline markup, escaped with a backslash.
325
+ def markdown-escape [cell-text]
326
+ replace-text "~" "\\\\~"
327
+ replace-text "&" "\\\\&"
328
+ replace-text "<" "\\\\<"
329
+ replace-text "]" "\\\\]"
330
+ replace-text "[" "\\\\["
331
+ replace-text "_" "\\\\_"
332
+ replace-text "*" "\\\\*"
333
+ replace-text "\`" "\\\\\`"
334
+ replace-text "|" "\\\\|" cell-text
277
335
 
278
336
  def markdown-label [column]
279
337
  markdown-cell (get :label column)
@@ -291,38 +349,45 @@ def markdown-delimiter [width]
291
349
  repeat width "| --- "
292
350
  "|\\n"
293
351
 
352
+ ; A record's line in a table \`width\` columns wide, or none in a table of
353
+ ; no columns, which is written as the empty document.
354
+ def markdown-row [width cells]
355
+ match width
356
+ case 0 []
357
+ case _ [(markdown-line (map markdown-cell cells))]
358
+
294
359
  def markdown-render-step [s event]
295
360
  match event
296
361
  case (schema columns)
297
362
  match s
298
363
  case :start
299
364
  match (count columns)
300
- case 0 (fail "a table of no columns has no Markdown form: a row needs at least one cell")
365
+ case 0 (transition 0 [])
301
366
  case _
302
367
  transition (count columns)
303
368
  vector
304
369
  markdown-line (map markdown-label columns)
305
370
  markdown-delimiter (count columns)
306
- case _ (fail "the events hold a second schema, where a table's hold one")
371
+ case _ (fail :protocol-order "the events hold a second schema, where a table's hold one")
307
372
  case (row cells)
308
373
  match s
309
- case :start (fail "the events hold a row before the schema, which a table's never do")
310
- case :ended (fail "the events hold a row after table-end, which a table's never do")
374
+ case :start (fail :protocol-order "the events hold a row before the schema, which a table's never do")
375
+ case :ended (fail :protocol-order "the events hold a row after table-end, which a table's never do")
311
376
  case _
312
377
  match (compare (count cells) s)
313
- case :equal (transition s [(markdown-line (map markdown-cell cells))])
314
- case _ (fail "the events hold a row that is not as wide as the schema, which a table's never do")
378
+ case :equal (transition s (markdown-row s cells))
379
+ case _ (fail :protocol-order "the events hold a row that is not as wide as the schema, which a table's never do")
315
380
  case table-end
316
381
  match s
317
- case :start (fail "the events end before the schema, which a table's never do")
318
- case :ended (fail "the events hold a second table-end, which a table's never do")
382
+ case :start (fail :protocol-order "the events end before the schema, which a table's never do")
383
+ case :ended (fail :protocol-order "the events hold a second table-end, which a table's never do")
319
384
  case _ (transition :ended [])
320
385
 
321
386
  def markdown-render-finish [s]
322
387
  match s
323
388
  case :ended []
324
- case :start (fail "the events hold no schema, where a table's hold one first")
325
- case _ (fail "the events ended before table-end, which a table's never do")
389
+ case :start (fail :protocol-order "the events hold no schema, where a table's hold one first")
390
+ case _ (fail :protocol-order "the events ended before table-end, which a table's never do")
326
391
 
327
392
  ; The render: the table's records in, the pipe table's text out.
328
393
  def markdown-render [input]
@@ -1 +1 @@
1
- {"version":3,"file":"translate.js","sourceRoot":"","sources":["../src/translate.ts"],"names":[],"mappings":";AAAA,yDAAyD;;QA8VvD,SAAS;AA9UX,MAAM,WAAW,GAAqB,MAAM,CAAC,MAAM,CAAC;IAClD,QAAQ,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAkDX;IACC,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,eAAe,EAAE,MAAM,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAyKvD,EAAE,CAAC;IACF,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,iBAAiB,EAAE,MAAM,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAwG3D,EAAE,CAAC;CACH,CAAC,CAAA;AAEF,SAAS,SAAS;IAChB,OAAO,WAAW,CAAA;AACpB,CAAC"}
1
+ {"version":3,"file":"translate.js","sourceRoot":"","sources":["../src/translate.ts"],"names":[],"mappings":";AAAA,yDAAyD;;QAgavD,SAAS;AA/YX,MAAM,WAAW,GAAqB,MAAM,CAAC,MAAM,CAAC;IAClD,QAAQ,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA6DX;IACC,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,eAAe,EAAE,MAAM,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAgMvD,EAAE,CAAC;IACF,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,iBAAiB,EAAE,MAAM,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAuI3D,EAAE,CAAC;CACH,CAAC,CAAA;AAEF,SAAS,SAAS;IAChB,OAAO,WAAW,CAAA;AACpB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tabnas/markdown",
3
- "version": "0.7.10",
3
+ "version": "0.7.12",
4
4
  "description": "A Tabnas parser plugin for CommonMark/GFM Markdown — headings, lists, blockquotes, code, emphasis, links and more, into a JSON AST.",
5
5
  "main": "dist/markdown.js",
6
6
  "type": "commonjs",
package/src/block.ts CHANGED
@@ -1321,7 +1321,7 @@ export class BlockParser {
1321
1321
  * Not simply `offset`: a JavaScript string index counts UTF-16 units, so an
1322
1322
  * astral character (emoji, rare CJK) would advance the column by two. A
1323
1323
  * column means a character, so `\u{1F600} *x*` must report the same columns
1324
- * in both runtimes.
1324
+ * in every runtime.
1325
1325
  *
1326
1326
  * Counted incrementally from the last offset already measured. `addChild`
1327
1327
  * runs once per container opened, so counting from the start of the line
package/src/markdown.ts CHANGED
@@ -236,4 +236,4 @@ export type { TranslationPart, TranslationParts } from './translate'
236
236
  // VERSION is this package's version. It MUST equal package.json "version":
237
237
  // the release orchestrator rewrites both, and the version test fails the
238
238
  // build if they drift. Mirrors `const VERSION` in go/markdown.go.
239
- const VERSION = '0.7.10'
239
+ const VERSION = '0.7.12'
package/src/translate.ts CHANGED
@@ -11,6 +11,7 @@ type TranslationPart = Readonly<{
11
11
  type TranslationParts = Readonly<{
12
12
  manifest: string
13
13
  lift?: TranslationPart
14
+ embed?: TranslationPart
14
15
  render?: TranslationPart
15
16
  }>
16
17
 
@@ -53,15 +54,26 @@ const TRANSLATION: TranslationParts = Object.freeze({
53
54
  "tree"
54
55
  ],
55
56
  "writes": "records",
57
+ "root": "array",
58
+ "schema": "markdown-ast",
56
59
  "lift": "alchemy/lift.alc",
57
60
  "render": "alchemy/render.alc",
58
61
  "loss": [
59
- "Only a document holding one table is read as records: a document with no table, or with more than one, is refused.",
60
- "Inline markup in a cell is flattened to its text when it is read, an image to its alt text, and a written cell is plain text, so text that spells inline markup reads back as that markup.",
62
+ "The rows are the elements of the root array, and a root of another kind is written as one row.",
63
+ "The columns are the first row's, an object's keys or an array's positions labelled 0, 1 and so on, so a member or position of a later row that the first row lacks is not written, and one the first row has and a later row lacks is an empty cell.",
64
+ "When the first row is a scalar there is one column, named value, and every row is written whole in it, an array or an object as its compact JSON text.",
65
+ "A table of no columns, such as the one an empty array or object makes, is written as the empty document, which reads back as the empty table.",
66
+ "Read as records, a document with no table is the empty table, of no columns and no rows.",
67
+ "Read as records, a document with several tables is its first table, and the tables after it are not read.",
68
+ "Inline markup in a cell is flattened to its text when it is read, an image to its alt text; a written cell escapes every character that can begin inline markup, so it reads back as the text it was.",
69
+ "A null and a missing cell are both written as an empty cell, so the two cannot be told apart, or from an empty string, when read back.",
70
+ "A nested array or object in a cell is written as its compact JSON text.",
71
+ "A number that is not finite is written as Infinity, -Infinity or NaN, and as null inside a nested array or object, since JSON has no spelling for one.",
72
+ "Every value is written as text, so a number or a boolean reads back as a string.",
61
73
  "Column alignment is not kept.",
62
74
  "A line break in a cell is written as a space.",
63
- "Leading and trailing spaces in a cell are not kept.",
64
- "A U+0000 in a cell is written as it is, and the reader replaces it with U+FFFD when the table is read back."
75
+ "Whitespace at either end of a cell is not kept, as the reader trims it: the space, the tab, U+00A0, U+FEFF, U+2028, U+2029 and the other Unicode space separators, though not U+0085.",
76
+ "A U+0000 in a cell is written as U+FFFD, the character a Markdown reader puts in its place, so it reads back as U+FFFD."
65
77
  ]
66
78
  }
67
79
  }
@@ -91,26 +103,36 @@ const TRANSLATION: TranslationParts = Object.freeze({
91
103
  ; cell of text beside emphasis, a link, an image, inline HTML or an
92
104
  ; autolink is their texts run together, \`Bob *b*\` as \`Bob b\`.
93
105
  ;
94
- ; The document must hold exactly one table: a second table, or none by
95
- ; the end, is a failure (INPUT_INVALID) that says so, since a Markdown
96
- ; document is read as records through its table and nothing else.
106
+ ; A Markdown document is read as records through its table and nothing
107
+ ; else, so the first table is the one read: a table after it is not
108
+ ; read, its rows and cells passed over as any other node's. A document
109
+ ; with no table is read as the empty table, a schema of no columns, no
110
+ ; row and \`table-end\`, which the render writes back as the empty
111
+ ; document. The manifest's loss lines say both.
97
112
  ;
98
113
  ; The state is one vector, [stack phase seen in-cell runs cells]:
99
114
  ; stack the open containers, one marker each: \`:array\`, \`:node\` for
100
115
  ; an object whose type is not known yet or is any other node,
101
116
  ; and \`:table\`, \`:row\` or \`:cell\` once its \`type\` has named
102
- ; one, so that its end is known when its object ends;
117
+ ; one, or \`:later\` for a table after the first, so that its
118
+ ; end is known when its object ends;
103
119
  ; phase \`:before\` the table, \`:header\` inside it until its first
104
- ; row ends, \`:body\` once the schema is written, \`:after\` it;
120
+ ; row ends, \`:body\` once the schema is written, \`:after\` it,
121
+ ; and \`:later\` inside a table after it, which is not read;
105
122
  ; seen what the last key was, \`:type\`, \`:text\` (a \`value\` or an
106
- ; \`alt\`) or \`:other\`, so that the scalar after it is classified;
123
+ ; \`alt\`) or \`:other\`, so that the scalar after it is classified,
124
+ ; and \`:none\` before the first event, so that events with no
125
+ ; value at all are told from a document with no table;
107
126
  ; in-cell whether a cell is open, so that text under it is the cell's;
108
127
  ; runs the open cell's runs of text, in order;
109
128
  ; cells the open row's cells, as wide as one row.
110
129
  ; It grows with the document's nesting and with one row's width, never
111
- ; with the table's length: a row is emitted when its object ends and
112
- ; dropped. Events no tree has (a key outside an object, an end with
113
- ; nothing open) fail with INPUT_INVALID, saying so.
130
+ ; with the table's length or the document's: a row is emitted when its
131
+ ; object ends and dropped, and a later table keeps nothing. Events no
132
+ ; tree has (a key outside an object, an end with nothing open), and
133
+ ; events of a node where the reader's tree never puts one (a row or a
134
+ ; cell outside a table, a table inside one), fail with
135
+ ; PROTOCOL_ORDER_ERROR, saying so.
114
136
 
115
137
  ; A column record of the TableRows protocol, as the standard library's
116
138
  ; \`public-column\` makes one.
@@ -150,31 +172,35 @@ def markdown-mark [marker stack]
150
172
  push marker (pop stack)
151
173
 
152
174
  ; The scalar after \`type\`: the three nodes the lift reads leave their
153
- ; marker on the stack and move the phase; any other node is \`:node\`.
175
+ ; marker on the stack and move the phase; a table after the first leaves
176
+ ; \`:later\`, and its rows and cells, like any other node, stay \`:node\`.
154
177
  def markdown-node [name stack phase in-cell runs cells]
155
178
  match name
156
179
  case "table"
157
180
  match phase
158
181
  case :before (transition [(markdown-mark :table stack) :header :other in-cell runs cells] [])
159
- case :after (fail "the document holds a second table, and only a document of one table is read as records")
160
- case _ (fail "the events hold a table inside a table, which the reader's tree never has")
182
+ case :after (transition [(markdown-mark :later stack) :later :other in-cell runs cells] [])
183
+ case _ (fail :protocol-order "the events hold a table inside a table, which the reader's tree never has")
161
184
  case "tableRow"
162
185
  match phase
163
- case :before (fail "the events hold a table row outside a table, which the reader's tree never has")
164
- case :after (fail "the events hold a table row outside a table, which the reader's tree never has")
186
+ case :before (fail :protocol-order "the events hold a table row outside a table, which the reader's tree never has")
187
+ case :after (fail :protocol-order "the events hold a table row outside a table, which the reader's tree never has")
188
+ case :later (transition [stack phase :other in-cell runs cells] [])
165
189
  case _ (transition [(markdown-mark :row stack) phase :other in-cell runs []] [])
166
190
  case "tableCell"
167
191
  match phase
168
- case :before (fail "the events hold a table cell outside a table, which the reader's tree never has")
169
- case :after (fail "the events hold a table cell outside a table, which the reader's tree never has")
192
+ case :before (fail :protocol-order "the events hold a table cell outside a table, which the reader's tree never has")
193
+ case :after (fail :protocol-order "the events hold a table cell outside a table, which the reader's tree never has")
194
+ case :later (transition [stack phase :other in-cell runs cells] [])
170
195
  case _
171
196
  if in-cell
172
- fail "the events hold a table cell inside a cell, which the reader's tree never has"
197
+ fail :protocol-order "the events hold a table cell inside a cell, which the reader's tree never has"
173
198
  transition [(markdown-mark :cell stack) phase :other true [] cells] []
174
199
  case _ (transition [stack phase :other in-cell runs cells] [])
175
200
 
176
201
  ; An object's end: a cell joins its row, a row is the schema or a
177
- ; record, the table is done, and any other node is dropped.
202
+ ; record, the table is done, a later table is passed over, and any other
203
+ ; node is dropped.
178
204
  def markdown-close [stack phase in-cell runs cells]
179
205
  match (markdown-top stack)
180
206
  case :cell
@@ -187,9 +213,11 @@ def markdown-close [stack phase in-cell runs cells]
187
213
  transition [(pop stack) phase :other in-cell runs []] [(row cells)]
188
214
  case :table
189
215
  transition [(pop stack) :after :other in-cell runs cells] []
216
+ case :later
217
+ transition [(pop stack) :after :other in-cell runs cells] []
190
218
  case :node
191
219
  transition [(pop stack) phase :other in-cell runs cells] []
192
- case _ (fail "the events end an object that is not open, which a tree's never do")
220
+ case _ (fail :protocol-order "the events end an object that is not open, which a tree's never do")
193
221
 
194
222
  def markdown-event [stack phase seen in-cell runs cells event]
195
223
  match event
@@ -199,8 +227,8 @@ def markdown-event [stack phase seen in-cell runs cells event]
199
227
  transition [(push :array stack) phase :other in-cell runs cells] []
200
228
  case (key name)
201
229
  match (markdown-top stack)
202
- case :none (fail "the events hold a key outside an object, which a tree's never do")
203
- case :array (fail "the events hold a key inside an array, which a tree's never do")
230
+ case :none (fail :protocol-order "the events hold a key outside an object, which a tree's never do")
231
+ case :array (fail :protocol-order "the events hold a key inside an array, which a tree's never do")
204
232
  case _ (transition [stack phase (markdown-seen name) in-cell runs cells] [])
205
233
  case (scalar value)
206
234
  match seen
@@ -214,27 +242,34 @@ def markdown-event [stack phase seen in-cell runs cells event]
214
242
  case array-end
215
243
  match (markdown-top stack)
216
244
  case :array (transition [(pop stack) phase :other in-cell runs cells] [])
217
- case _ (fail "the events end an array that is not open, which a tree's never do")
245
+ case _ (fail :protocol-order "the events end an array that is not open, which a tree's never do")
218
246
 
219
247
  def markdown-lift-step [s event]
220
248
  match s
221
249
  case [stack phase seen in-cell runs cells]
222
250
  markdown-event stack phase seen in-cell runs cells event
223
251
 
252
+ ; What the end of the events adds once every container is closed: the
253
+ ; table's end, or, for a document with no table, the empty table.
254
+ def markdown-lift-end [stack outputs]
255
+ match (count stack)
256
+ case 0 outputs
257
+ case _ (fail :protocol-order "the events ended inside a container, which a tree's never do")
258
+
224
259
  def markdown-lift-finish [s]
225
260
  match s
226
261
  case [stack phase seen in-cell runs cells]
227
- match phase
228
- case :after
229
- match (count stack)
230
- case 0 [table-end]
231
- case _ (fail "the events ended inside a container, which a tree's never do")
232
- case :before (fail "the document holds no table, and only a document of one table is read as records")
233
- case _ (fail "the events ended inside the table, which a tree's never do")
262
+ match seen
263
+ case :none (fail :protocol-order "the events hold no value, where a tree's hold one")
264
+ case _
265
+ match phase
266
+ case :after (markdown-lift-end stack [table-end])
267
+ case :before (markdown-lift-end stack [(schema []) table-end])
268
+ case _ (fail :protocol-order "the events ended inside a table, which a tree's never do")
234
269
 
235
270
  ; The lift: the document's events in, the table's records out.
236
271
  def markdown-lift [input]
237
- scan-emit [[] :before :other false [] []] markdown-lift-step markdown-lift-finish (events input)
272
+ scan-emit [[] :before :none false [] []] markdown-lift-step markdown-lift-finish (events input)
238
273
  ` }),
239
274
  render: Object.freeze({ entry: "markdown-render", source: `; Markdown's render: a table's records, the TableRows protocol (one
240
275
  ; \`schema\`, a \`row\` per record and \`table-end\`), written as one GFM pipe
@@ -247,45 +282,69 @@ def markdown-lift [input]
247
282
  ; \`| --- | --- |\`, and a row per record, each line ended by a line feed
248
283
  ; and every cell between \`| \` and \` |\`. A cell is its value's text as
249
284
  ; the CSV renderer writes it (\`scalar-text\`: a string as it is, a number
250
- ; by its lexeme, a boolean by its name, null and a missing cell as
285
+ ; by its lexeme, or by its word, \`Infinity\`, \`-Infinity\` or \`NaN\`, when
286
+ ; it is not finite, a boolean by its name, null and a missing cell as
251
287
  ; nothing, a vector or a record as its compact JSON text), with every
252
- ; backslash doubled and every pipe escaped as \`\\|\`, which the reader
253
- ; turns back into the characters (the reader's split skips the byte
254
- ; after a backslash, so the pipe's escape holds only when a backslash
255
- ; before it is itself escaped), and every line break replaced by a
256
- ; space, since a row is one line. No other character is escaped: the
257
- ; render cannot look at one (alchemy has no character test), so a cell
258
- ; is plain text, and text that spells inline markup reads back as that
259
- ; markup. A U+0000 in a cell is written as it is for the same reason,
260
- ; and the reader replaces it with U+FFFD, as CommonMark asks of every
261
- ; reader; the loss declaration says so. Alignment is not written: every
262
- ; column's delimiter is \`---\`.
288
+ ; backslash doubled, and every character that can begin inline markup
289
+ ; escaped with a backslash, as CommonMark lets any ASCII punctuation be
290
+ ; escaped: the pipe, which the reader's split skips after a backslash
291
+ ; (so its escape holds only when a backslash before it is itself
292
+ ; escaped), the backtick of a code span, the \`*\` and \`_\` of emphasis,
293
+ ; the brackets of a link, an image or a footnote, the \`<\` of an autolink
294
+ ; or of raw HTML, the \`&\` of an entity or numeric character reference,
295
+ ; and the \`~\` of GFM's strikethrough. The reader turns each escape back
296
+ ; into its character, so a written cell reads back as the text it was;
297
+ ; a URL that GFM reads as an autolink reads back as its own text, which
298
+ ; is the link's. Every line break is replaced by a space, since a row is
299
+ ; one line, and a U+0000 is written as U+FFFD, the character CommonMark
300
+ ; has every reader put in its place; the loss declaration says both.
301
+ ; Alignment is not written: every column's delimiter is \`---\`.
302
+ ;
303
+ ; A table of no columns, the one an empty array or object makes, has no
304
+ ; pipe table, since a row needs at least one cell: it is written as the
305
+ ; empty document, nothing at all, and so are its rows, which have no
306
+ ; cells. The lift reads that document back as the empty table, of no
307
+ ; columns and no rows.
263
308
  ;
264
309
  ; The state is one marker: \`:start\` before the schema, the schema's
265
310
  ; width (a number) while rows may follow, and \`:ended\` after
266
311
  ; \`table-end\`, so that a stream which breaks the protocol (a row before
267
312
  ; the schema, a second schema, a row of another width, anything after
268
- ; the end, no end) fails with INPUT_INVALID, saying so, rather than
269
- ; being written. The check is this file's rather than \`csv-table\`'s,
270
- ; whose refusals speak of CSV. A schema of no columns fails too: a row
271
- ; needs at least one cell, so such a table has no Markdown form.
313
+ ; the end, no end) fails with PROTOCOL_ORDER_ERROR, saying so, rather
314
+ ; than being written. The check is this file's rather than
315
+ ; \`csv-table\`'s, whose refusals speak of CSV.
272
316
 
273
317
  ; What \`scalar-text\` reads: null and a missing cell are written as
274
318
  ; nothing, where the CSV renderer's \`csv-options\` map a missing cell to
275
- ; an error.
319
+ ; an error, and a number that is not finite as its word.
276
320
  def markdown-options
277
321
  record
278
322
  entry :null-text ""
279
323
  entry :missing ""
324
+ entry :non-finite :literal
280
325
 
281
- ; A cell's text, as it stands between the pipes.
326
+ ; A cell's text, as it stands between the pipes: the backslashes doubled
327
+ ; first, so that the escapes written after them stay single.
282
328
  def markdown-cell [cell]
283
329
  replace-text "\\r" " "
284
330
  replace-text "\\n" " "
285
331
  replace-text "\\r\\n" " "
286
- replace-text "|" "\\\\|"
287
- replace-text "\\\\" "\\\\\\\\"
288
- scalar-text markdown-options cell
332
+ replace-text "\\u0000" "\\ufffd"
333
+ markdown-escape
334
+ replace-text "\\\\" "\\\\\\\\"
335
+ scalar-text markdown-options cell
336
+
337
+ ; Every character that can begin inline markup, escaped with a backslash.
338
+ def markdown-escape [cell-text]
339
+ replace-text "~" "\\\\~"
340
+ replace-text "&" "\\\\&"
341
+ replace-text "<" "\\\\<"
342
+ replace-text "]" "\\\\]"
343
+ replace-text "[" "\\\\["
344
+ replace-text "_" "\\\\_"
345
+ replace-text "*" "\\\\*"
346
+ replace-text "\`" "\\\\\`"
347
+ replace-text "|" "\\\\|" cell-text
289
348
 
290
349
  def markdown-label [column]
291
350
  markdown-cell (get :label column)
@@ -303,38 +362,45 @@ def markdown-delimiter [width]
303
362
  repeat width "| --- "
304
363
  "|\\n"
305
364
 
365
+ ; A record's line in a table \`width\` columns wide, or none in a table of
366
+ ; no columns, which is written as the empty document.
367
+ def markdown-row [width cells]
368
+ match width
369
+ case 0 []
370
+ case _ [(markdown-line (map markdown-cell cells))]
371
+
306
372
  def markdown-render-step [s event]
307
373
  match event
308
374
  case (schema columns)
309
375
  match s
310
376
  case :start
311
377
  match (count columns)
312
- case 0 (fail "a table of no columns has no Markdown form: a row needs at least one cell")
378
+ case 0 (transition 0 [])
313
379
  case _
314
380
  transition (count columns)
315
381
  vector
316
382
  markdown-line (map markdown-label columns)
317
383
  markdown-delimiter (count columns)
318
- case _ (fail "the events hold a second schema, where a table's hold one")
384
+ case _ (fail :protocol-order "the events hold a second schema, where a table's hold one")
319
385
  case (row cells)
320
386
  match s
321
- case :start (fail "the events hold a row before the schema, which a table's never do")
322
- case :ended (fail "the events hold a row after table-end, which a table's never do")
387
+ case :start (fail :protocol-order "the events hold a row before the schema, which a table's never do")
388
+ case :ended (fail :protocol-order "the events hold a row after table-end, which a table's never do")
323
389
  case _
324
390
  match (compare (count cells) s)
325
- case :equal (transition s [(markdown-line (map markdown-cell cells))])
326
- case _ (fail "the events hold a row that is not as wide as the schema, which a table's never do")
391
+ case :equal (transition s (markdown-row s cells))
392
+ case _ (fail :protocol-order "the events hold a row that is not as wide as the schema, which a table's never do")
327
393
  case table-end
328
394
  match s
329
- case :start (fail "the events end before the schema, which a table's never do")
330
- case :ended (fail "the events hold a second table-end, which a table's never do")
395
+ case :start (fail :protocol-order "the events end before the schema, which a table's never do")
396
+ case :ended (fail :protocol-order "the events hold a second table-end, which a table's never do")
331
397
  case _ (transition :ended [])
332
398
 
333
399
  def markdown-render-finish [s]
334
400
  match s
335
401
  case :ended []
336
- case :start (fail "the events hold no schema, where a table's hold one first")
337
- case _ (fail "the events ended before table-end, which a table's never do")
402
+ case :start (fail :protocol-order "the events hold no schema, where a table's hold one first")
403
+ case _ (fail :protocol-order "the events ended before table-end, which a table's never do")
338
404
 
339
405
  ; The render: the table's records in, the pipe table's text out.
340
406
  def markdown-render [input]