@thi.ng/hiccup-markdown 2.1.49 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/parser.d.ts ADDED
@@ -0,0 +1,65 @@
1
+ import type { Fn3, IObjectOf } from "@thi.ng/api";
2
+ import type { ILogger } from "@thi.ng/logger";
3
+ import type { ContextOpts, ParseScope } from "@thi.ng/parse";
4
+ export declare const GRAMMAR: import("@thi.ng/parse").Language | undefined;
5
+ export declare const parseRaw: (src: string, opts?: Partial<ContextOpts>) => {
6
+ result: boolean;
7
+ ctx: import("@thi.ng/parse").ParseContext<string>;
8
+ };
9
+ export interface TagTransforms {
10
+ bold(ctx: MDParseContext, body: string): any;
11
+ bquote(ctx: MDParseContext, body: any[], meta?: any): any;
12
+ code(ctx: MDParseContext, body: string): any;
13
+ codeblock(ctx: MDParseContext, lang: string, head: string, body: string, meta?: any): any;
14
+ custom(ctx: MDParseContext, kind: string, body: string, meta?: any): any;
15
+ emoji(ctx: MDParseContext, id: string): any;
16
+ footnote(ctx: MDParseContext, id: string, body: any[], meta?: any): any;
17
+ footnoteRef(ctx: MDParseContext, id: string): any;
18
+ footnoteWrapper(ctx: MDParseContext, notes: IObjectOf<any>): any;
19
+ hd(ctx: MDParseContext, level: number, body: any[], meta?: any): any;
20
+ hr(ctx: MDParseContext, length: number): any;
21
+ img(ctx: MDParseContext, src: string, alt: string): any;
22
+ italic(ctx: MDParseContext, body: string): any;
23
+ link(ctx: MDParseContext, target: string, body: any[]): any;
24
+ linkRef(ctx: MDParseContext, refID: string, body: any[]): any;
25
+ meta(ctx: MDParseContext, body: string): any;
26
+ ol(ctx: MDParseContext, items: any[], meta?: any): any;
27
+ olitem(ctx: MDParseContext, attribs: TodoAttribs, index: number, ...body: any[]): any;
28
+ para(ctx: MDParseContext, body: any[], meta?: any): any;
29
+ strike(ctx: MDParseContext, body: string): any;
30
+ table(ctx: MDParseContext, align: ColumnAlign[], head: any[], rows: any[], meta?: any): any;
31
+ tableCell(ctx: MDParseContext, body: any[]): any;
32
+ tableRow(ctx: MDParseContext, cells: any[]): any;
33
+ ul(ctx: MDParseContext, items: any[], meta?: any): any;
34
+ ulitem(ctx: MDParseContext, attribs: TodoAttribs, ...body: any[]): any;
35
+ }
36
+ export interface MDParseContext {
37
+ logger?: ILogger;
38
+ tags: TagTransforms;
39
+ linkRefs: IObjectOf<string>;
40
+ footnotes: IObjectOf<any>;
41
+ headings: {
42
+ level: number;
43
+ body: any[];
44
+ }[];
45
+ hasFootnotes: boolean;
46
+ meta?: any;
47
+ opts: MDParseOpts;
48
+ }
49
+ export interface MDParseOpts {
50
+ /**
51
+ * Line break representation in blockquotes. E.g. Use `["br",{}]` for actual
52
+ * line breaks.
53
+ *
54
+ * @defaultValue `" "`
55
+ */
56
+ bqLineBreak: any;
57
+ }
58
+ export type TodoAttribs = Partial<{
59
+ __todo: true;
60
+ __done: boolean;
61
+ }>;
62
+ export type ColumnAlign = "center" | "left" | "right";
63
+ export declare const defMDContext: (tags?: Partial<TagTransforms>, opts?: Partial<MDParseOpts>) => MDParseContext;
64
+ export declare const walk: Fn3<ParseScope<string>, MDParseContext, any[], void>;
65
+ //# sourceMappingURL=parser.d.ts.map
package/parser.js ADDED
@@ -0,0 +1,379 @@
1
+ // thing:no-export
2
+ import { peek } from "@thi.ng/arrays/peek";
3
+ import { delay } from "@thi.ng/compose/delay";
4
+ import { DEFAULT, defmulti } from "@thi.ng/defmulti";
5
+ import { EMOJI } from "@thi.ng/emoji/emoji";
6
+ import { defContext } from "@thi.ng/parse/context";
7
+ import { defGrammar } from "@thi.ng/parse/grammar";
8
+ export const GRAMMAR = defGrammar(`
9
+ DNL1: <DNL>+ => discard ;
10
+ DNL2: <NL>{2,} ;
11
+ inlinedelim: ( "![" | '[' | "**" | '_' | "~~" | '\`' | " :" ) ;
12
+ delim: ( <inlinedelim> | <DNL2> ) ;
13
+ delim1: ( <inlinedelim> | <NL> ) ;
14
+ body: .(?-<delim>!) => join ;
15
+ body1: .(?-<delim1>!) => join ;
16
+
17
+ ref: "[["! .(?+"]]"!) => join ;
18
+ fnref: "[^"! <label> ;
19
+ fnote: <LSTART> "[^"! <fnlabel> <WS1> <para> ;
20
+ fnlabel: .(?+"]:"!) => join ;
21
+ label: .(?+']'!) => join ;
22
+ target: .(?+')'!) => join ;
23
+ link: '['! <label> '('! <target> ;
24
+ linkref: '['! <label> '['! <label> ;
25
+ linkdef: <LSTART> '['! <label> ':'! <WS1> <ldtarget> ;
26
+ ldtarget: .(?+<DNL1>) => join ;
27
+ img: "!["! <label> '('! <target> ;
28
+ bold: "**"! .(?+"**"!) => join ;
29
+ italic: "_"! .(?+"_"!) => join ;
30
+ code: '\`'! .(?+'\`'!) => join ;
31
+ strike: "~~"! .(?+"~~"!) => join ;
32
+ emoji: ' '? ':'! <ALPHA_NUM>(?+':'!) => join ;
33
+ para: (<ref> | <img> | <fnref> | <linkref> | <link> | <bold> | <italic> | <strike> | <code> | <emoji> | <body>)* <DNL2>! ;
34
+
35
+ hdlevel: '#'+ => count ;
36
+ hd: <LSTART> <hdlevel> <WS0>
37
+ (<ref> | <img> | <fnref> | <link> | <bold> | <italic> | <strike> | <code> | <emoji> | <body1> )* <DNL1> ;
38
+
39
+ lilevel: ' '* => count ;
40
+ uint: <DIGIT>+ => int ;
41
+ ulid: <DNL> <WS0> '-'! ;
42
+ olid: <DNL> <WS0> <DIGIT>+! '.'! ;
43
+ lidelim: ( <delim> | <ulid> | <olid> ) ;
44
+ libody: .(?-<lidelim>!) => join ;
45
+ todo: '['! [ xX] ']'! <WS1> => hoistR ;
46
+ ulitem: <LSTART> <lilevel> "- "! <todo>?
47
+ (<ref> | <img> | <fnref> | <link> | <bold> | <italic> | <strike> | <code> | <emoji> | <libody> )* <DNL> ;
48
+ olitem: <LSTART> <lilevel> <uint> ". "! <todo>?
49
+ (<ref> | <img> | <fnref> | <link> | <bold> | <italic> | <strike> | <code> | <emoji> | <libody> )* <DNL> ;
50
+ list: (<ulitem> | <olitem>)+ <DNL1> ;
51
+
52
+ cbdelim: <LSTART> "\`\`\`"! ;
53
+ codeblock: <cbdelim>! <codemeta> <codebody> <DNL1> ;
54
+ codemeta: .(?+<NL>!) => join ;
55
+ codebody: .(?+<cbdelim>) => join ;
56
+
57
+ customdelim: <LSTART> ":::"! ;
58
+ customblock: <customdelim>! <custommeta> <custombody> <DNL1> ;
59
+ custommeta: .(?+<NL>!) => join ;
60
+ custombody: .(?+<customdelim>) => join ;
61
+
62
+ metablock: <LSTART> "{{{"! <metabody> <DNL1> ;
63
+ metabody: .(?+<metaend>!) => join ;
64
+ metaend: "}}}" <LEND> ;
65
+
66
+ bqline: <LSTART> "> "!
67
+ (<ref> | <img> | <fnref> | <link> | <bold> | <italic> | <strike> | <code> | <emoji> | <body1>)* <DNL> ;
68
+ bquote: <bqline>+ <DNL1> ;
69
+
70
+ tdelim: (<inlinedelim> | '|' ) ;
71
+ tbody: .(?-<tdelim>!) => join ;
72
+ tcell: <WS0> (<ref> | <img> | <fnref> | <link> | <bold> | <italic> | <strike> | <code> | <emoji> | <tbody> )* '|'! ;
73
+ trow: <LSTART> '|'! <tcell>(?+<DNL>) ;
74
+ table: <trow>+ <DNL1> ;
75
+
76
+ hr: "--" ('-'(?-<NL>!))* <DNL1> => join ;
77
+
78
+ main: <WS0> (<hd> | <list> | <bquote> | <codeblock> | <customblock> | <metablock> | <table> | <hr> | <fnote> | <linkdef> | <para>)* ;
79
+ `);
80
+ export const parseRaw = (src, opts) => {
81
+ const ctx = defContext(src, opts);
82
+ return { result: GRAMMAR.rules.main(ctx), ctx };
83
+ };
84
+ export const defMDContext = (tags, opts) => ({
85
+ footnotes: {},
86
+ headings: [],
87
+ linkRefs: {},
88
+ hasFootnotes: false,
89
+ meta: null,
90
+ opts: {
91
+ bqLineBreak: " ",
92
+ ...opts,
93
+ },
94
+ tags: {
95
+ bold: (_, body) => ["strong", {}, body],
96
+ bquote: (_, body, meta) => [
97
+ "blockquote",
98
+ __withMeta({}, meta),
99
+ ...body,
100
+ ],
101
+ code: (_, body) => ["code", {}, body],
102
+ codeblock: (_, lang, __head, body, meta) => [
103
+ "pre",
104
+ __withMeta({ data: { lang }, __head }, meta),
105
+ ["code", {}, body],
106
+ ],
107
+ custom: (_, kind, body, meta) => [kind, __withMeta({}, meta), body],
108
+ emoji: (_, id) => EMOJI[id] || id,
109
+ footnote: (_, id, body, meta) => [
110
+ "li",
111
+ __withMeta({ id: `fn-${id}` }, meta),
112
+ ["sup", {}, `[${id}] `],
113
+ ...body,
114
+ " ",
115
+ ["a", { href: `#fnref-${id}` }, "⏎"],
116
+ ],
117
+ footnoteRef: (_, id) => [
118
+ "sup",
119
+ {},
120
+ ["a", { id: `fnref-${id}`, href: `#fn-${id}` }, `[${id}]`],
121
+ ],
122
+ footnoteWrapper: (_, notes) => [
123
+ "ul",
124
+ { id: "footnotes" },
125
+ ...Object.keys(notes)
126
+ .sort()
127
+ .map((id) => notes[id]),
128
+ ],
129
+ hd: (_, level, body, meta) => [
130
+ level < 7 ? `h${level}` : "p",
131
+ __withMeta({}, meta),
132
+ ...body,
133
+ ],
134
+ hr: (_, __length) => ["hr", { __length }],
135
+ img: (_, src, alt) => ["img", { src, alt }],
136
+ italic: (_, body) => ["em", {}, body],
137
+ link: (_, href, body) => ["a", { href }, ...body],
138
+ linkRef: (ctx, refID, body) => [
139
+ "a",
140
+ { href: delay(() => ctx.linkRefs[refID]) },
141
+ ...body,
142
+ ],
143
+ meta: (_, body) => body,
144
+ olitem: (_, attribs, index, body) => [
145
+ "li",
146
+ { ...attribs, __index: index },
147
+ ...body,
148
+ ],
149
+ ol: (_, items, meta) => ["ol", __withMeta({}, meta), ...items],
150
+ para: (_, body, meta) => ["p", __withMeta({}, meta), ...body],
151
+ strike: (_, body) => ["s", {}, body],
152
+ table: (_, __align, head, rows, meta) => [
153
+ "table",
154
+ __withMeta({ __align }, meta),
155
+ ["thead", {}, head],
156
+ ["tbody", {}, ...rows],
157
+ ],
158
+ tableCell: (_, body) => ["td", {}, ...body],
159
+ tableRow: (_, cells) => ["tr", {}, ...cells],
160
+ ul: (_, items, meta) => ["ul", __withMeta({}, meta), ...items],
161
+ ulitem: (_, attribs, body) => ["li", attribs, ...body],
162
+ ...tags,
163
+ },
164
+ });
165
+ export const walk = defmulti((x, ctx) => {
166
+ ctx.logger && ctx.logger.debug(x);
167
+ return x.id;
168
+ }, {
169
+ body1: "body",
170
+ bqline: "repeat0",
171
+ label: "body",
172
+ libody: "body",
173
+ main: "root",
174
+ repeat1: "repeat0",
175
+ tbody: "body",
176
+ }, {
177
+ [DEFAULT]: (scope, ctx) => {
178
+ throw new Error(`unknown ID: ${scope.id}, ctx: ${JSON.stringify(ctx)}`);
179
+ },
180
+ root: ({ children }, ctx, acc) => {
181
+ walk(children[0], ctx, acc);
182
+ if (ctx.hasFootnotes) {
183
+ __collect(acc, ctx.tags.footnoteWrapper(ctx, ctx.footnotes));
184
+ }
185
+ },
186
+ main: ({ children }, ctx, acc) => walk(children[0], ctx, acc),
187
+ repeat0: ({ children }, ctx, acc) => {
188
+ for (let c of children)
189
+ walk(c, ctx, acc);
190
+ },
191
+ body: ({ result }, _, acc) => __collect(acc, result),
192
+ bold: ({ result }, ctx, acc) => __collect(acc, ctx.tags.bold(ctx, result)),
193
+ bquote: (scope, ctx, acc) => {
194
+ const body = [];
195
+ const children = scope.children[0].children;
196
+ for (let i = 0, n = children.length - 1; i <= n; i++) {
197
+ walk(children[i].children[0], ctx, body);
198
+ if (i < n)
199
+ body.push(ctx.opts.bqLineBreak);
200
+ }
201
+ __collect(acc, ctx.tags.bquote(ctx, body, ctx.meta));
202
+ ctx.meta = null;
203
+ },
204
+ code: ({ result }, ctx, acc) => __collect(acc, ctx.tags.code(ctx, result)),
205
+ codeblock: ({ children }, ctx, acc) => {
206
+ const [lang, ...head] = children[0].result.split(" ");
207
+ __collect(acc, ctx.tags.codeblock(ctx, lang, head, children[1].result.trim(), ctx.meta));
208
+ ctx.meta = null;
209
+ },
210
+ customblock: ({ children }, ctx, acc) => {
211
+ __collect(acc, ctx.tags.custom(children[0].result, children[1].result.trim(), ctx.meta));
212
+ ctx.meta = null;
213
+ },
214
+ emoji: ({ result }, ctx, acc) => {
215
+ if (result[0] === " ") {
216
+ __collect(acc, " ");
217
+ result = result.substring(1);
218
+ }
219
+ __collect(acc, ctx.tags.emoji(ctx, result));
220
+ },
221
+ fnote: ({ children }, ctx) => {
222
+ const body = [];
223
+ const id = children[0].result;
224
+ walk(children[1].children[0], ctx, body);
225
+ const res = ctx.tags.footnote(ctx, id, body, ctx.meta);
226
+ if (res != null) {
227
+ ctx.hasFootnotes = true;
228
+ ctx.footnotes[id] = res;
229
+ }
230
+ ctx.meta = null;
231
+ },
232
+ fnref: ({ children }, ctx, acc) => __collect(acc, ctx.tags.footnoteRef(ctx, children[0].result)),
233
+ hd: ({ children }, ctx, acc) => {
234
+ const body = [];
235
+ const level = children[0].result;
236
+ walk(children[1], ctx, body);
237
+ ctx.headings.push({ level, body });
238
+ __collect(acc, ctx.tags.hd(ctx, level, body, ctx.meta));
239
+ ctx.meta = null;
240
+ },
241
+ hr: ({ result }, ctx, acc) => __collect(acc, ctx.tags.hr(ctx, result.length)),
242
+ img: ({ children }, ctx, acc) => __collect(acc, ctx.tags.img(ctx, children[1].result, children[0].result)),
243
+ italic: ({ result }, ctx, acc) => __collect(acc, ctx.tags.italic(ctx, result)),
244
+ link: ({ children }, ctx, acc) => {
245
+ const body = [];
246
+ walk(children[0], ctx, body);
247
+ __collect(acc, ctx.tags.link(ctx, children[1].result, body));
248
+ },
249
+ linkdef: ({ children }, ctx) => {
250
+ ctx.linkRefs[children[0].result] = children[1].result;
251
+ },
252
+ linkref: ({ children }, ctx, acc) => {
253
+ const body = [];
254
+ walk(children[0], ctx, body);
255
+ __collect(acc, ctx.tags.linkRef(ctx, children[1].result, body));
256
+ },
257
+ list: (scope, ctx, acc) => {
258
+ const children = scope.children[0].children;
259
+ const stack = [
260
+ [children[0].id === "ulitem" ? "ul" : "ol"],
261
+ ];
262
+ const levels = [0];
263
+ for (let item of children) {
264
+ const currLevel = item.children[0].result;
265
+ if (currLevel > peek(levels)) {
266
+ const sublist = [item.id === "ulitem" ? "ul" : "ol"];
267
+ const parent = peek(stack);
268
+ parent.length > 1
269
+ ? peek(parent).push(sublist)
270
+ : parent.push([
271
+ parent[0] === "ul" ? "ulitem" : "olitem",
272
+ {},
273
+ sublist,
274
+ ]);
275
+ stack.push(sublist);
276
+ levels.push(currLevel);
277
+ }
278
+ else if (currLevel < peek(levels)) {
279
+ while (currLevel < peek(levels)) {
280
+ stack.pop();
281
+ levels.pop();
282
+ }
283
+ }
284
+ walk(item, ctx, peek(stack));
285
+ }
286
+ const $list = (root, isRoot = false) => ctx.tags[root[0]](ctx, root.slice(1).map($item), isRoot ? ctx.meta : null);
287
+ const $item = (item) => {
288
+ let last = item[item.length - 1];
289
+ if (last[0] === "ul" || last[0] === "ol")
290
+ item[item.length - 1] = $list(last);
291
+ return item[0] === "ulitem"
292
+ ? ctx.tags.ulitem(ctx, item[1], item.slice(2))
293
+ : ctx.tags.olitem(ctx, item[1], item[2], item.slice(3));
294
+ };
295
+ __collect(acc, $list(stack[0], true));
296
+ ctx.meta = null;
297
+ },
298
+ metablock: ({ children }, ctx) => {
299
+ ctx.meta = ctx.tags.meta(ctx, children[0].result.trim());
300
+ },
301
+ olitem: ({ children }, ctx, acc) => {
302
+ const body = [];
303
+ walk(children[3], ctx, body);
304
+ __collect(acc, [
305
+ "olitem",
306
+ __listItemAttribs(children[2]),
307
+ children[1].result,
308
+ ...body,
309
+ ]);
310
+ },
311
+ para: ({ children }, ctx, acc) => {
312
+ const body = [];
313
+ for (let c of children)
314
+ walk(c, ctx, body);
315
+ __collect(acc, ctx.tags.para(ctx, body, ctx.meta));
316
+ ctx.meta = null;
317
+ },
318
+ strike: ({ result }, ctx, acc) => __collect(acc, ctx.tags.strike(ctx, result)),
319
+ table: (scope, ctx, acc) => {
320
+ const children = scope.children[0].children;
321
+ const head = [];
322
+ const rows = [];
323
+ walk(children[0], ctx, head);
324
+ let align = [];
325
+ if (children.length > 1) {
326
+ for (let c of children[1].children[0].children) {
327
+ const raw = (c.children[0].children[0].result.trim());
328
+ align.push(raw.startsWith(":-")
329
+ ? raw.endsWith("-:")
330
+ ? "center"
331
+ : "left"
332
+ : raw.endsWith("-:")
333
+ ? "right"
334
+ : "left");
335
+ }
336
+ for (let c of children.slice(2))
337
+ walk(c, ctx, rows);
338
+ }
339
+ else {
340
+ align = new Array(children[0].children[0].children.length).fill("left");
341
+ }
342
+ __collect(acc, ctx.tags.table(ctx, align, head[0], rows, ctx.meta));
343
+ ctx.meta = null;
344
+ },
345
+ tcell: ({ children }, ctx, acc) => {
346
+ const body = [];
347
+ for (let c of children)
348
+ walk(c, ctx, body);
349
+ __collect(acc, ctx.tags.tableCell(ctx, body));
350
+ },
351
+ trow: ({ children }, ctx, acc) => {
352
+ const cols = [];
353
+ for (let c of children[0].children)
354
+ walk(c, ctx, cols);
355
+ __collect(acc, ctx.tags.tableRow(ctx, cols));
356
+ },
357
+ ulitem: ({ children }, ctx, acc) => {
358
+ const body = [];
359
+ walk(children[2], ctx, body);
360
+ __collect(acc, [
361
+ "ulitem",
362
+ __listItemAttribs(children[1]),
363
+ ...body,
364
+ ]);
365
+ },
366
+ });
367
+ const __collect = (acc, x) => x != null && acc.push(x);
368
+ const __withMeta = (target, meta) => {
369
+ if (meta != null) {
370
+ target.__meta = meta;
371
+ }
372
+ return target;
373
+ };
374
+ const __listItemAttribs = (scope) => scope?.id === "todo"
375
+ ? {
376
+ __todo: true,
377
+ __done: scope.result === "x",
378
+ }
379
+ : {};
package/serialize.d.ts CHANGED
@@ -3,8 +3,9 @@ interface SerializeState {
3
3
  sep: string;
4
4
  id?: number;
5
5
  pre?: boolean;
6
+ blockquote?: boolean;
6
7
  }
7
8
  export declare const serialize: (tree: any, ctx: any) => string;
8
- export declare const serializeElement: import("@thi.ng/defmulti/api").MultiFn3<any, any, SerializeState, string>;
9
+ export declare const serializeElement: import("@thi.ng/defmulti").MultiFn3<any, any, SerializeState, string>;
9
10
  export {};
10
11
  //# sourceMappingURL=serialize.d.ts.map
package/serialize.js CHANGED
@@ -10,8 +10,10 @@ import { wrap } from "@thi.ng/strings/wrap";
10
10
  import { Border } from "@thi.ng/text-canvas/api";
11
11
  import { formatCanvas } from "@thi.ng/text-canvas/format";
12
12
  import { tableCanvas } from "@thi.ng/text-canvas/table";
13
- export const serialize = (tree, ctx) => _serialize(tree, ctx, { indent: 0, sep: "" }).replace(/\n{3,}/g, "\n\n");
14
- const _serialize = (tree, ctx, state) => {
13
+ export const serialize = (tree, ctx) => __serialize(tree, ctx, { indent: 0, sep: "" })
14
+ .replace(/\n{3,}/g, "\n\n")
15
+ .trim();
16
+ const __serialize = (tree, ctx, state) => {
15
17
  if (tree == null)
16
18
  return "";
17
19
  if (Array.isArray(tree)) {
@@ -20,10 +22,10 @@ const _serialize = (tree, ctx, state) => {
20
22
  }
21
23
  let tag = tree[0];
22
24
  if (isFunction(tag)) {
23
- return _serialize(tag.apply(null, [ctx, ...tree.slice(1)]), ctx, state);
25
+ return __serialize(tag.apply(null, [ctx, ...tree.slice(1)]), ctx, state);
24
26
  }
25
27
  if (implementsFunction(tag, "render")) {
26
- return _serialize(tag.render.apply(null, [ctx, ...tree.slice(1)]), ctx, state);
28
+ return __serialize(tag.render.apply(null, [ctx, ...tree.slice(1)]), ctx, state);
27
29
  }
28
30
  if (isString(tag)) {
29
31
  tree = normalize(tree);
@@ -35,63 +37,68 @@ const _serialize = (tree, ctx, state) => {
35
37
  return serializeElement(tree, ctx, state);
36
38
  }
37
39
  if (isNotStringAndIterable(tree)) {
38
- return serializeIter(tree, ctx, state);
40
+ return __serializeIter(tree, ctx, state);
39
41
  }
40
42
  illegalArgs(`invalid tree node: ${tree}`);
41
43
  }
42
44
  if (isFunction(tree)) {
43
- return _serialize(tree(ctx), ctx, state);
45
+ return __serialize(tree(ctx), ctx, state);
44
46
  }
45
47
  if (implementsFunction(tree, "toHiccup")) {
46
- return _serialize(tree.toHiccup(ctx), ctx, state);
48
+ return __serialize(tree.toHiccup(ctx), ctx, state);
47
49
  }
48
50
  if (implementsFunction(tree, "deref")) {
49
- return _serialize(tree.deref(), ctx, state);
51
+ return __serialize(tree.deref(), ctx, state);
50
52
  }
51
53
  if (isNotStringAndIterable(tree)) {
52
- return serializeIter(tree, ctx, state);
54
+ return __serializeIter(tree, ctx, state);
53
55
  }
54
56
  return tree.toString();
55
57
  };
56
- const serializeIter = (iter, ctx, state) => {
58
+ const __serializeIter = (iter, ctx, state) => {
57
59
  if (!iter)
58
60
  return "";
59
61
  const res = [];
60
62
  for (let i of iter) {
61
- res.push(_serialize(i, ctx, state));
63
+ res.push(__serialize(i, ctx, state));
62
64
  }
63
65
  return res.join(state.sep);
64
66
  };
65
- const header = (level) => (el, ctx, state) => repeat("#", level) + " " + body(el, ctx, state) + "\n\n";
66
- const body = (el, ctx, state) => serializeIter(el[2], ctx, state);
67
+ const __heading = (level) => (el, ctx, state) => `\n${repeat("#", level)} ${__body(el, ctx, state)}\n`;
68
+ const __body = (el, ctx, state) => __serializeIter(el[2], ctx, state);
69
+ const __resolve = (x) => (isFunction(x) ? x() : x);
67
70
  export const serializeElement = defmulti((el) => el[0], {
68
71
  th: "strong",
69
72
  }, {
70
- [DEFAULT]: body,
71
- h1: header(1),
72
- h2: header(2),
73
- h3: header(3),
74
- h4: header(4),
75
- h5: header(5),
76
- h6: header(6),
77
- p: (el, ctx, state) => `\n${body(el, ctx, state)}\n`,
73
+ [DEFAULT]: __body,
74
+ h1: __heading(1),
75
+ h2: __heading(2),
76
+ h3: __heading(3),
77
+ h4: __heading(4),
78
+ h5: __heading(5),
79
+ h6: __heading(6),
80
+ p: (el, ctx, state) => `\n${__body(el, ctx, state)}\n`,
78
81
  img: (el) => `![${el[1].alt || ""}](${el[1].src})`,
79
- a: (el, ctx, state) => `[${body(el, ctx, state)}](${el[1].href})`,
80
- em: (el, ctx, state) => `_${body(el, ctx, state)}_`,
81
- strong: (el, ctx, state) => `**${body(el, ctx, state)}**`,
82
- pre: (el, ctx, state) => `\n\`\`\`${el[1].lang || ""}\n${body(el, ctx, {
82
+ a: (el, ctx, state) => {
83
+ let { href, title } = el[1];
84
+ title = __resolve(title);
85
+ return `[${__body(el, ctx, state)}](${__resolve(href)}${title ? ` "${title}"` : ""})`;
86
+ },
87
+ em: (el, ctx, state) => `_${__body(el, ctx, state)}_`,
88
+ strong: (el, ctx, state) => `**${__body(el, ctx, state)}**`,
89
+ pre: (el, ctx, state) => `\n\`\`\`${el[1].lang || ""}\n${__body(el, ctx, {
83
90
  ...state,
84
91
  pre: true,
85
92
  sep: "\n",
86
93
  })}\n\`\`\`\n`,
87
- code: (el, ctx, state) => state.pre ? el[2][0] : `\`${body(el, ctx, state)}\``,
94
+ code: (el, ctx, state) => state.pre ? el[2][0] : `\`${__body(el, ctx, state)}\``,
88
95
  ul: (el, ctx, state) => {
89
96
  const cstate = {
90
97
  ...state,
91
98
  indent: state.indent + 4,
92
99
  sep: "\n",
93
100
  };
94
- return wrap(state.indent === 0 ? "\n" : "")(body(el, ctx, cstate));
101
+ return wrap(state.indent === 0 ? "\n" : "")(__body(el, ctx, cstate));
95
102
  },
96
103
  ol: (el, ctx, state) => {
97
104
  const cstate = {
@@ -100,14 +107,18 @@ export const serializeElement = defmulti((el) => el[0], {
100
107
  id: 0,
101
108
  sep: "\n",
102
109
  };
103
- return wrap(state.indent === 0 ? "\n" : "")(body(el, ctx, cstate));
110
+ return wrap(state.indent === 0 ? "\n" : "")(__body(el, ctx, cstate));
104
111
  },
105
112
  li: (el, ctx, state) => repeat(" ", state.indent - 4) +
106
113
  (state.id != null ? ++state.id + "." : "-") +
107
114
  " " +
108
- body(el, ctx, { ...state, sep: "" }),
109
- blockquote: (el, ctx, state) => `\n> ${body(el, ctx, state)}\n`,
110
- br: () => "\\\n",
115
+ __body(el, ctx, { ...state, sep: "" }),
116
+ blockquote: (el, ctx, state) => `\n${repeat(">", state.indent + 1)} ${__body(el, ctx, {
117
+ ...state,
118
+ indent: state.indent + 1,
119
+ blockquote: true,
120
+ })}\n`,
121
+ br: (_, __, state) => state.blockquote ? `\\\n${repeat(">", state.indent)} ` : "\\\n",
111
122
  hr: () => "\n---\n",
112
123
  table: (el, ctx, state) => {
113
124
  let caption = "";
@@ -129,7 +140,7 @@ export const serializeElement = defmulti((el) => el[0], {
129
140
  tbody = rows(child[2]);
130
141
  break;
131
142
  case "caption":
132
- caption = body(child, ctx, state);
143
+ caption = __body(child, ctx, state);
133
144
  break;
134
145
  default:
135
146
  // TODO output warning?