@tabnas/markdown 0.7.11 → 0.7.13

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.
@@ -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.11";
29
+ declare const VERSION = "0.7.13";
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.11';
164
+ const VERSION = '0.7.13';
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,16 +41,28 @@ 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."
53
- ]
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."
64
+ ],
65
+ "whole": "A link or an image may name a link reference definition that comes later in the document, so a document's tree is complete only at its end: it is read whole before anything is written."
54
66
  }
55
67
  }
56
68
  `,
@@ -79,26 +91,36 @@ const TRANSLATION = Object.freeze({
79
91
  ; cell of text beside emphasis, a link, an image, inline HTML or an
80
92
  ; autolink is their texts run together, \`Bob *b*\` as \`Bob b\`.
81
93
  ;
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.
94
+ ; A Markdown document is read as records through its table and nothing
95
+ ; else, so the first table is the one read: a table after it is not
96
+ ; read, its rows and cells passed over as any other node's. A document
97
+ ; with no table is read as the empty table, a schema of no columns, no
98
+ ; row and \`table-end\`, which the render writes back as the empty
99
+ ; document. The manifest's loss lines say both.
85
100
  ;
86
101
  ; The state is one vector, [stack phase seen in-cell runs cells]:
87
102
  ; stack the open containers, one marker each: \`:array\`, \`:node\` for
88
103
  ; an object whose type is not known yet or is any other node,
89
104
  ; and \`:table\`, \`:row\` or \`:cell\` once its \`type\` has named
90
- ; one, so that its end is known when its object ends;
105
+ ; one, or \`:later\` for a table after the first, so that its
106
+ ; end is known when its object ends;
91
107
  ; phase \`:before\` the table, \`:header\` inside it until its first
92
- ; row ends, \`:body\` once the schema is written, \`:after\` it;
108
+ ; row ends, \`:body\` once the schema is written, \`:after\` it,
109
+ ; and \`:later\` inside a table after it, which is not read;
93
110
  ; seen what the last key was, \`:type\`, \`:text\` (a \`value\` or an
94
- ; \`alt\`) or \`:other\`, so that the scalar after it is classified;
111
+ ; \`alt\`) or \`:other\`, so that the scalar after it is classified,
112
+ ; and \`:none\` before the first event, so that events with no
113
+ ; value at all are told from a document with no table;
95
114
  ; in-cell whether a cell is open, so that text under it is the cell's;
96
115
  ; runs the open cell's runs of text, in order;
97
116
  ; cells the open row's cells, as wide as one row.
98
117
  ; 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.
118
+ ; with the table's length or the document's: a row is emitted when its
119
+ ; object ends and dropped, and a later table keeps nothing. Events no
120
+ ; tree has (a key outside an object, an end with nothing open), and
121
+ ; events of a node where the reader's tree never puts one (a row or a
122
+ ; cell outside a table, a table inside one), fail with
123
+ ; PROTOCOL_ORDER_ERROR, saying so.
102
124
 
103
125
  ; A column record of the TableRows protocol, as the standard library's
104
126
  ; \`public-column\` makes one.
@@ -138,31 +160,35 @@ def markdown-mark [marker stack]
138
160
  push marker (pop stack)
139
161
 
140
162
  ; 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\`.
163
+ ; marker on the stack and move the phase; a table after the first leaves
164
+ ; \`:later\`, and its rows and cells, like any other node, stay \`:node\`.
142
165
  def markdown-node [name stack phase in-cell runs cells]
143
166
  match name
144
167
  case "table"
145
168
  match phase
146
169
  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")
170
+ case :after (transition [(markdown-mark :later stack) :later :other in-cell runs cells] [])
171
+ case _ (fail :protocol-order "the events hold a table inside a table, which the reader's tree never has")
149
172
  case "tableRow"
150
173
  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")
174
+ case :before (fail :protocol-order "the events hold a table row outside a table, which the reader's tree never has")
175
+ case :after (fail :protocol-order "the events hold a table row outside a table, which the reader's tree never has")
176
+ case :later (transition [stack phase :other in-cell runs cells] [])
153
177
  case _ (transition [(markdown-mark :row stack) phase :other in-cell runs []] [])
154
178
  case "tableCell"
155
179
  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")
180
+ case :before (fail :protocol-order "the events hold a table cell outside a table, which the reader's tree never has")
181
+ case :after (fail :protocol-order "the events hold a table cell outside a table, which the reader's tree never has")
182
+ case :later (transition [stack phase :other in-cell runs cells] [])
158
183
  case _
159
184
  if in-cell
160
- fail "the events hold a table cell inside a cell, which the reader's tree never has"
185
+ fail :protocol-order "the events hold a table cell inside a cell, which the reader's tree never has"
161
186
  transition [(markdown-mark :cell stack) phase :other true [] cells] []
162
187
  case _ (transition [stack phase :other in-cell runs cells] [])
163
188
 
164
189
  ; 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.
190
+ ; record, the table is done, a later table is passed over, and any other
191
+ ; node is dropped.
166
192
  def markdown-close [stack phase in-cell runs cells]
167
193
  match (markdown-top stack)
168
194
  case :cell
@@ -175,9 +201,11 @@ def markdown-close [stack phase in-cell runs cells]
175
201
  transition [(pop stack) phase :other in-cell runs []] [(row cells)]
176
202
  case :table
177
203
  transition [(pop stack) :after :other in-cell runs cells] []
204
+ case :later
205
+ transition [(pop stack) :after :other in-cell runs cells] []
178
206
  case :node
179
207
  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")
208
+ case _ (fail :protocol-order "the events end an object that is not open, which a tree's never do")
181
209
 
182
210
  def markdown-event [stack phase seen in-cell runs cells event]
183
211
  match event
@@ -187,8 +215,8 @@ def markdown-event [stack phase seen in-cell runs cells event]
187
215
  transition [(push :array stack) phase :other in-cell runs cells] []
188
216
  case (key name)
189
217
  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")
218
+ case :none (fail :protocol-order "the events hold a key outside an object, which a tree's never do")
219
+ case :array (fail :protocol-order "the events hold a key inside an array, which a tree's never do")
192
220
  case _ (transition [stack phase (markdown-seen name) in-cell runs cells] [])
193
221
  case (scalar value)
194
222
  match seen
@@ -202,27 +230,34 @@ def markdown-event [stack phase seen in-cell runs cells event]
202
230
  case array-end
203
231
  match (markdown-top stack)
204
232
  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")
233
+ case _ (fail :protocol-order "the events end an array that is not open, which a tree's never do")
206
234
 
207
235
  def markdown-lift-step [s event]
208
236
  match s
209
237
  case [stack phase seen in-cell runs cells]
210
238
  markdown-event stack phase seen in-cell runs cells event
211
239
 
240
+ ; What the end of the events adds once every container is closed: the
241
+ ; table's end, or, for a document with no table, the empty table.
242
+ def markdown-lift-end [stack outputs]
243
+ match (count stack)
244
+ case 0 outputs
245
+ case _ (fail :protocol-order "the events ended inside a container, which a tree's never do")
246
+
212
247
  def markdown-lift-finish [s]
213
248
  match s
214
249
  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")
250
+ match seen
251
+ case :none (fail :protocol-order "the events hold no value, where a tree's hold one")
252
+ case _
253
+ match phase
254
+ case :after (markdown-lift-end stack [table-end])
255
+ case :before (markdown-lift-end stack [(schema []) table-end])
256
+ case _ (fail :protocol-order "the events ended inside a table, which a tree's never do")
222
257
 
223
258
  ; The lift: the document's events in, the table's records out.
224
259
  def markdown-lift [input]
225
- scan-emit [[] :before :other false [] []] markdown-lift-step markdown-lift-finish (events input)
260
+ scan-emit [[] :before :none false [] []] markdown-lift-step markdown-lift-finish (events input)
226
261
  ` }),
227
262
  render: Object.freeze({ entry: "markdown-render", source: `; Markdown's render: a table's records, the TableRows protocol (one
228
263
  ; \`schema\`, a \`row\` per record and \`table-end\`), written as one GFM pipe
@@ -235,45 +270,69 @@ def markdown-lift [input]
235
270
  ; \`| --- | --- |\`, and a row per record, each line ended by a line feed
236
271
  ; and every cell between \`| \` and \` |\`. A cell is its value's text as
237
272
  ; 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
273
+ ; by its lexeme, or by its word, \`Infinity\`, \`-Infinity\` or \`NaN\`, when
274
+ ; it is not finite, a boolean by its name, null and a missing cell as
239
275
  ; 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 \`---\`.
276
+ ; backslash doubled, and every character that can begin inline markup
277
+ ; escaped with a backslash, as CommonMark lets any ASCII punctuation be
278
+ ; escaped: the pipe, which the reader's split skips after a backslash
279
+ ; (so its escape holds only when a backslash before it is itself
280
+ ; escaped), the backtick of a code span, the \`*\` and \`_\` of emphasis,
281
+ ; the brackets of a link, an image or a footnote, the \`<\` of an autolink
282
+ ; or of raw HTML, the \`&\` of an entity or numeric character reference,
283
+ ; and the \`~\` of GFM's strikethrough. The reader turns each escape back
284
+ ; into its character, so a written cell reads back as the text it was;
285
+ ; a URL that GFM reads as an autolink reads back as its own text, which
286
+ ; is the link's. Every line break is replaced by a space, since a row is
287
+ ; one line, and a U+0000 is written as U+FFFD, the character CommonMark
288
+ ; has every reader put in its place; the loss declaration says both.
289
+ ; Alignment is not written: every column's delimiter is \`---\`.
290
+ ;
291
+ ; A table of no columns, the one an empty array or object makes, has no
292
+ ; pipe table, since a row needs at least one cell: it is written as the
293
+ ; empty document, nothing at all, and so are its rows, which have no
294
+ ; cells. The lift reads that document back as the empty table, of no
295
+ ; columns and no rows.
251
296
  ;
252
297
  ; The state is one marker: \`:start\` before the schema, the schema's
253
298
  ; width (a number) while rows may follow, and \`:ended\` after
254
299
  ; \`table-end\`, so that a stream which breaks the protocol (a row before
255
300
  ; 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.
301
+ ; the end, no end) fails with PROTOCOL_ORDER_ERROR, saying so, rather
302
+ ; than being written. The check is this file's rather than
303
+ ; \`csv-table\`'s, whose refusals speak of CSV.
260
304
 
261
305
  ; What \`scalar-text\` reads: null and a missing cell are written as
262
306
  ; nothing, where the CSV renderer's \`csv-options\` map a missing cell to
263
- ; an error.
307
+ ; an error, and a number that is not finite as its word.
264
308
  def markdown-options
265
309
  record
266
310
  entry :null-text ""
267
311
  entry :missing ""
312
+ entry :non-finite :literal
268
313
 
269
- ; A cell's text, as it stands between the pipes.
314
+ ; A cell's text, as it stands between the pipes: the backslashes doubled
315
+ ; first, so that the escapes written after them stay single.
270
316
  def markdown-cell [cell]
271
317
  replace-text "\\r" " "
272
318
  replace-text "\\n" " "
273
319
  replace-text "\\r\\n" " "
274
- replace-text "|" "\\\\|"
275
- replace-text "\\\\" "\\\\\\\\"
276
- scalar-text markdown-options cell
320
+ replace-text "\\u0000" "\\ufffd"
321
+ markdown-escape
322
+ replace-text "\\\\" "\\\\\\\\"
323
+ scalar-text markdown-options cell
324
+
325
+ ; Every character that can begin inline markup, escaped with a backslash.
326
+ def markdown-escape [cell-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 "\`" "\\\\\`"
335
+ replace-text "|" "\\\\|" cell-text
277
336
 
278
337
  def markdown-label [column]
279
338
  markdown-cell (get :label column)
@@ -291,38 +350,45 @@ def markdown-delimiter [width]
291
350
  repeat width "| --- "
292
351
  "|\\n"
293
352
 
353
+ ; A record's line in a table \`width\` columns wide, or none in a table of
354
+ ; no columns, which is written as the empty document.
355
+ def markdown-row [width cells]
356
+ match width
357
+ case 0 []
358
+ case _ [(markdown-line (map markdown-cell cells))]
359
+
294
360
  def markdown-render-step [s event]
295
361
  match event
296
362
  case (schema columns)
297
363
  match s
298
364
  case :start
299
365
  match (count columns)
300
- case 0 (fail "a table of no columns has no Markdown form: a row needs at least one cell")
366
+ case 0 (transition 0 [])
301
367
  case _
302
368
  transition (count columns)
303
369
  vector
304
370
  markdown-line (map markdown-label columns)
305
371
  markdown-delimiter (count columns)
306
- case _ (fail "the events hold a second schema, where a table's hold one")
372
+ case _ (fail :protocol-order "the events hold a second schema, where a table's hold one")
307
373
  case (row cells)
308
374
  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")
375
+ case :start (fail :protocol-order "the events hold a row before the schema, which a table's never do")
376
+ case :ended (fail :protocol-order "the events hold a row after table-end, which a table's never do")
311
377
  case _
312
378
  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")
379
+ case :equal (transition s (markdown-row s cells))
380
+ case _ (fail :protocol-order "the events hold a row that is not as wide as the schema, which a table's never do")
315
381
  case table-end
316
382
  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")
383
+ case :start (fail :protocol-order "the events end before the schema, which a table's never do")
384
+ case :ended (fail :protocol-order "the events hold a second table-end, which a table's never do")
319
385
  case _ (transition :ended [])
320
386
 
321
387
  def markdown-render-finish [s]
322
388
  match s
323
389
  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")
390
+ case :start (fail :protocol-order "the events hold no schema, where a table's hold one first")
391
+ case _ (fail :protocol-order "the events ended before table-end, which a table's never do")
326
392
 
327
393
  ; The render: the table's records in, the pipe table's text out.
328
394
  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;;QAiavD,SAAS;AAhZX,MAAM,WAAW,GAAqB,MAAM,CAAC,MAAM,CAAC;IAClD,QAAQ,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA8DX;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.11",
3
+ "version": "0.7.13",
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/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.11'
239
+ const VERSION = '0.7.13'
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,16 +54,28 @@ 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."
65
- ]
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."
77
+ ],
78
+ "whole": "A link or an image may name a link reference definition that comes later in the document, so a document's tree is complete only at its end: it is read whole before anything is written."
66
79
  }
67
80
  }
68
81
  `,
@@ -91,26 +104,36 @@ const TRANSLATION: TranslationParts = Object.freeze({
91
104
  ; cell of text beside emphasis, a link, an image, inline HTML or an
92
105
  ; autolink is their texts run together, \`Bob *b*\` as \`Bob b\`.
93
106
  ;
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.
107
+ ; A Markdown document is read as records through its table and nothing
108
+ ; else, so the first table is the one read: a table after it is not
109
+ ; read, its rows and cells passed over as any other node's. A document
110
+ ; with no table is read as the empty table, a schema of no columns, no
111
+ ; row and \`table-end\`, which the render writes back as the empty
112
+ ; document. The manifest's loss lines say both.
97
113
  ;
98
114
  ; The state is one vector, [stack phase seen in-cell runs cells]:
99
115
  ; stack the open containers, one marker each: \`:array\`, \`:node\` for
100
116
  ; an object whose type is not known yet or is any other node,
101
117
  ; and \`:table\`, \`:row\` or \`:cell\` once its \`type\` has named
102
- ; one, so that its end is known when its object ends;
118
+ ; one, or \`:later\` for a table after the first, so that its
119
+ ; end is known when its object ends;
103
120
  ; phase \`:before\` the table, \`:header\` inside it until its first
104
- ; row ends, \`:body\` once the schema is written, \`:after\` it;
121
+ ; row ends, \`:body\` once the schema is written, \`:after\` it,
122
+ ; and \`:later\` inside a table after it, which is not read;
105
123
  ; seen what the last key was, \`:type\`, \`:text\` (a \`value\` or an
106
- ; \`alt\`) or \`:other\`, so that the scalar after it is classified;
124
+ ; \`alt\`) or \`:other\`, so that the scalar after it is classified,
125
+ ; and \`:none\` before the first event, so that events with no
126
+ ; value at all are told from a document with no table;
107
127
  ; in-cell whether a cell is open, so that text under it is the cell's;
108
128
  ; runs the open cell's runs of text, in order;
109
129
  ; cells the open row's cells, as wide as one row.
110
130
  ; 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.
131
+ ; with the table's length or the document's: a row is emitted when its
132
+ ; object ends and dropped, and a later table keeps nothing. Events no
133
+ ; tree has (a key outside an object, an end with nothing open), and
134
+ ; events of a node where the reader's tree never puts one (a row or a
135
+ ; cell outside a table, a table inside one), fail with
136
+ ; PROTOCOL_ORDER_ERROR, saying so.
114
137
 
115
138
  ; A column record of the TableRows protocol, as the standard library's
116
139
  ; \`public-column\` makes one.
@@ -150,31 +173,35 @@ def markdown-mark [marker stack]
150
173
  push marker (pop stack)
151
174
 
152
175
  ; 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\`.
176
+ ; marker on the stack and move the phase; a table after the first leaves
177
+ ; \`:later\`, and its rows and cells, like any other node, stay \`:node\`.
154
178
  def markdown-node [name stack phase in-cell runs cells]
155
179
  match name
156
180
  case "table"
157
181
  match phase
158
182
  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")
183
+ case :after (transition [(markdown-mark :later stack) :later :other in-cell runs cells] [])
184
+ case _ (fail :protocol-order "the events hold a table inside a table, which the reader's tree never has")
161
185
  case "tableRow"
162
186
  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")
187
+ case :before (fail :protocol-order "the events hold a table row outside a table, which the reader's tree never has")
188
+ case :after (fail :protocol-order "the events hold a table row outside a table, which the reader's tree never has")
189
+ case :later (transition [stack phase :other in-cell runs cells] [])
165
190
  case _ (transition [(markdown-mark :row stack) phase :other in-cell runs []] [])
166
191
  case "tableCell"
167
192
  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")
193
+ case :before (fail :protocol-order "the events hold a table cell outside a table, which the reader's tree never has")
194
+ case :after (fail :protocol-order "the events hold a table cell outside a table, which the reader's tree never has")
195
+ case :later (transition [stack phase :other in-cell runs cells] [])
170
196
  case _
171
197
  if in-cell
172
- fail "the events hold a table cell inside a cell, which the reader's tree never has"
198
+ fail :protocol-order "the events hold a table cell inside a cell, which the reader's tree never has"
173
199
  transition [(markdown-mark :cell stack) phase :other true [] cells] []
174
200
  case _ (transition [stack phase :other in-cell runs cells] [])
175
201
 
176
202
  ; 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.
203
+ ; record, the table is done, a later table is passed over, and any other
204
+ ; node is dropped.
178
205
  def markdown-close [stack phase in-cell runs cells]
179
206
  match (markdown-top stack)
180
207
  case :cell
@@ -187,9 +214,11 @@ def markdown-close [stack phase in-cell runs cells]
187
214
  transition [(pop stack) phase :other in-cell runs []] [(row cells)]
188
215
  case :table
189
216
  transition [(pop stack) :after :other in-cell runs cells] []
217
+ case :later
218
+ transition [(pop stack) :after :other in-cell runs cells] []
190
219
  case :node
191
220
  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")
221
+ case _ (fail :protocol-order "the events end an object that is not open, which a tree's never do")
193
222
 
194
223
  def markdown-event [stack phase seen in-cell runs cells event]
195
224
  match event
@@ -199,8 +228,8 @@ def markdown-event [stack phase seen in-cell runs cells event]
199
228
  transition [(push :array stack) phase :other in-cell runs cells] []
200
229
  case (key name)
201
230
  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")
231
+ case :none (fail :protocol-order "the events hold a key outside an object, which a tree's never do")
232
+ case :array (fail :protocol-order "the events hold a key inside an array, which a tree's never do")
204
233
  case _ (transition [stack phase (markdown-seen name) in-cell runs cells] [])
205
234
  case (scalar value)
206
235
  match seen
@@ -214,27 +243,34 @@ def markdown-event [stack phase seen in-cell runs cells event]
214
243
  case array-end
215
244
  match (markdown-top stack)
216
245
  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")
246
+ case _ (fail :protocol-order "the events end an array that is not open, which a tree's never do")
218
247
 
219
248
  def markdown-lift-step [s event]
220
249
  match s
221
250
  case [stack phase seen in-cell runs cells]
222
251
  markdown-event stack phase seen in-cell runs cells event
223
252
 
253
+ ; What the end of the events adds once every container is closed: the
254
+ ; table's end, or, for a document with no table, the empty table.
255
+ def markdown-lift-end [stack outputs]
256
+ match (count stack)
257
+ case 0 outputs
258
+ case _ (fail :protocol-order "the events ended inside a container, which a tree's never do")
259
+
224
260
  def markdown-lift-finish [s]
225
261
  match s
226
262
  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")
263
+ match seen
264
+ case :none (fail :protocol-order "the events hold no value, where a tree's hold one")
265
+ case _
266
+ match phase
267
+ case :after (markdown-lift-end stack [table-end])
268
+ case :before (markdown-lift-end stack [(schema []) table-end])
269
+ case _ (fail :protocol-order "the events ended inside a table, which a tree's never do")
234
270
 
235
271
  ; The lift: the document's events in, the table's records out.
236
272
  def markdown-lift [input]
237
- scan-emit [[] :before :other false [] []] markdown-lift-step markdown-lift-finish (events input)
273
+ scan-emit [[] :before :none false [] []] markdown-lift-step markdown-lift-finish (events input)
238
274
  ` }),
239
275
  render: Object.freeze({ entry: "markdown-render", source: `; Markdown's render: a table's records, the TableRows protocol (one
240
276
  ; \`schema\`, a \`row\` per record and \`table-end\`), written as one GFM pipe
@@ -247,45 +283,69 @@ def markdown-lift [input]
247
283
  ; \`| --- | --- |\`, and a row per record, each line ended by a line feed
248
284
  ; and every cell between \`| \` and \` |\`. A cell is its value's text as
249
285
  ; 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
286
+ ; by its lexeme, or by its word, \`Infinity\`, \`-Infinity\` or \`NaN\`, when
287
+ ; it is not finite, a boolean by its name, null and a missing cell as
251
288
  ; 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 \`---\`.
289
+ ; backslash doubled, and every character that can begin inline markup
290
+ ; escaped with a backslash, as CommonMark lets any ASCII punctuation be
291
+ ; escaped: the pipe, which the reader's split skips after a backslash
292
+ ; (so its escape holds only when a backslash before it is itself
293
+ ; escaped), the backtick of a code span, the \`*\` and \`_\` of emphasis,
294
+ ; the brackets of a link, an image or a footnote, the \`<\` of an autolink
295
+ ; or of raw HTML, the \`&\` of an entity or numeric character reference,
296
+ ; and the \`~\` of GFM's strikethrough. The reader turns each escape back
297
+ ; into its character, so a written cell reads back as the text it was;
298
+ ; a URL that GFM reads as an autolink reads back as its own text, which
299
+ ; is the link's. Every line break is replaced by a space, since a row is
300
+ ; one line, and a U+0000 is written as U+FFFD, the character CommonMark
301
+ ; has every reader put in its place; the loss declaration says both.
302
+ ; Alignment is not written: every column's delimiter is \`---\`.
303
+ ;
304
+ ; A table of no columns, the one an empty array or object makes, has no
305
+ ; pipe table, since a row needs at least one cell: it is written as the
306
+ ; empty document, nothing at all, and so are its rows, which have no
307
+ ; cells. The lift reads that document back as the empty table, of no
308
+ ; columns and no rows.
263
309
  ;
264
310
  ; The state is one marker: \`:start\` before the schema, the schema's
265
311
  ; width (a number) while rows may follow, and \`:ended\` after
266
312
  ; \`table-end\`, so that a stream which breaks the protocol (a row before
267
313
  ; 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.
314
+ ; the end, no end) fails with PROTOCOL_ORDER_ERROR, saying so, rather
315
+ ; than being written. The check is this file's rather than
316
+ ; \`csv-table\`'s, whose refusals speak of CSV.
272
317
 
273
318
  ; What \`scalar-text\` reads: null and a missing cell are written as
274
319
  ; nothing, where the CSV renderer's \`csv-options\` map a missing cell to
275
- ; an error.
320
+ ; an error, and a number that is not finite as its word.
276
321
  def markdown-options
277
322
  record
278
323
  entry :null-text ""
279
324
  entry :missing ""
325
+ entry :non-finite :literal
280
326
 
281
- ; A cell's text, as it stands between the pipes.
327
+ ; A cell's text, as it stands between the pipes: the backslashes doubled
328
+ ; first, so that the escapes written after them stay single.
282
329
  def markdown-cell [cell]
283
330
  replace-text "\\r" " "
284
331
  replace-text "\\n" " "
285
332
  replace-text "\\r\\n" " "
286
- replace-text "|" "\\\\|"
287
- replace-text "\\\\" "\\\\\\\\"
288
- scalar-text markdown-options cell
333
+ replace-text "\\u0000" "\\ufffd"
334
+ markdown-escape
335
+ replace-text "\\\\" "\\\\\\\\"
336
+ scalar-text markdown-options cell
337
+
338
+ ; Every character that can begin inline markup, escaped with a backslash.
339
+ def markdown-escape [cell-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 "\`" "\\\\\`"
348
+ replace-text "|" "\\\\|" cell-text
289
349
 
290
350
  def markdown-label [column]
291
351
  markdown-cell (get :label column)
@@ -303,38 +363,45 @@ def markdown-delimiter [width]
303
363
  repeat width "| --- "
304
364
  "|\\n"
305
365
 
366
+ ; A record's line in a table \`width\` columns wide, or none in a table of
367
+ ; no columns, which is written as the empty document.
368
+ def markdown-row [width cells]
369
+ match width
370
+ case 0 []
371
+ case _ [(markdown-line (map markdown-cell cells))]
372
+
306
373
  def markdown-render-step [s event]
307
374
  match event
308
375
  case (schema columns)
309
376
  match s
310
377
  case :start
311
378
  match (count columns)
312
- case 0 (fail "a table of no columns has no Markdown form: a row needs at least one cell")
379
+ case 0 (transition 0 [])
313
380
  case _
314
381
  transition (count columns)
315
382
  vector
316
383
  markdown-line (map markdown-label columns)
317
384
  markdown-delimiter (count columns)
318
- case _ (fail "the events hold a second schema, where a table's hold one")
385
+ case _ (fail :protocol-order "the events hold a second schema, where a table's hold one")
319
386
  case (row cells)
320
387
  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")
388
+ case :start (fail :protocol-order "the events hold a row before the schema, which a table's never do")
389
+ case :ended (fail :protocol-order "the events hold a row after table-end, which a table's never do")
323
390
  case _
324
391
  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")
392
+ case :equal (transition s (markdown-row s cells))
393
+ case _ (fail :protocol-order "the events hold a row that is not as wide as the schema, which a table's never do")
327
394
  case table-end
328
395
  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")
396
+ case :start (fail :protocol-order "the events end before the schema, which a table's never do")
397
+ case :ended (fail :protocol-order "the events hold a second table-end, which a table's never do")
331
398
  case _ (transition :ended [])
332
399
 
333
400
  def markdown-render-finish [s]
334
401
  match s
335
402
  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")
403
+ case :start (fail :protocol-order "the events hold no schema, where a table's hold one first")
404
+ case _ (fail :protocol-order "the events ended before table-end, which a table's never do")
338
405
 
339
406
  ; The render: the table's records in, the pipe table's text out.
340
407
  def markdown-render [input]