Skip to content

Commit

Permalink
refactor(hiccup-markdown): update serializer
Browse files Browse the repository at this point in the history
- add support for nested blockquotes
- add support for link labels
- rename internal fns
- update tests
  • Loading branch information
postspectacular committed Feb 27, 2023
1 parent 0208a53 commit a489c0e
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 37 deletions.
89 changes: 55 additions & 34 deletions packages/hiccup-markdown/src/serialize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,27 +16,30 @@ interface SerializeState {
sep: string;
id?: number;
pre?: boolean;
blockquote?: boolean;
}

export const serialize = (tree: any, ctx: any) =>
_serialize(tree, ctx, { indent: 0, sep: "" }).replace(/\n{3,}/g, "\n\n");
__serialize(tree, ctx, { indent: 0, sep: "" })
.replace(/\n{3,}/g, "\n\n")
.trim();

const _serialize = (tree: any, ctx: any, state: SerializeState): string => {
const __serialize = (tree: any, ctx: any, state: SerializeState): string => {
if (tree == null) return "";
if (Array.isArray(tree)) {
if (!tree.length) {
return "";
}
let tag = tree[0];
if (isFunction(tag)) {
return _serialize(
return __serialize(
tag.apply(null, [ctx, ...tree.slice(1)]),
ctx,
state
);
}
if (implementsFunction(tag, "render")) {
return _serialize(
return __serialize(
tag.render.apply(null, [ctx, ...tree.slice(1)]),
ctx,
state
Expand All @@ -52,86 +55,96 @@ const _serialize = (tree: any, ctx: any, state: SerializeState): string => {
return serializeElement(tree, ctx, state);
}
if (isNotStringAndIterable(tree)) {
return serializeIter(tree, ctx, state);
return __serializeIter(tree, ctx, state);
}
illegalArgs(`invalid tree node: ${tree}`);
}
if (isFunction(tree)) {
return _serialize(tree(ctx), ctx, state);
return __serialize(tree(ctx), ctx, state);
}
if (implementsFunction(tree, "toHiccup")) {
return _serialize(tree.toHiccup(ctx), ctx, state);
return __serialize(tree.toHiccup(ctx), ctx, state);
}
if (implementsFunction(tree, "deref")) {
return _serialize(tree.deref(), ctx, state);
return __serialize(tree.deref(), ctx, state);
}
if (isNotStringAndIterable(tree)) {
return serializeIter(tree, ctx, state);
return __serializeIter(tree, ctx, state);
}
return tree.toString();
};

const serializeIter = (
const __serializeIter = (
iter: Iterable<any>,
ctx: any,
state: SerializeState
) => {
if (!iter) return "";
const res = [];
for (let i of iter) {
res.push(_serialize(i, ctx, state));
res.push(__serialize(i, ctx, state));
}
return res.join(state.sep);
};

const header =
const __heading =
(level: number) => (el: any[], ctx: any, state: SerializeState) =>
repeat("#", level) + " " + body(el, ctx, state) + "\n\n";
`\n${repeat("#", level)} ${__body(el, ctx, state)}\n`;

const body = (el: any[], ctx: any, state: SerializeState) =>
serializeIter(el[2], ctx, state);
const __body = (el: any[], ctx: any, state: SerializeState) =>
__serializeIter(el[2], ctx, state);

const __resolve = (x: any) => (isFunction(x) ? x() : x);

export const serializeElement = defmulti<any, any, SerializeState, string>(
(el) => el[0],
{
th: "strong",
},
{
[DEFAULT]: body,
h1: header(1),
h2: header(2),
h3: header(3),
h4: header(4),
h5: header(5),
h6: header(6),
[DEFAULT]: __body,
h1: __heading(1),
h2: __heading(2),
h3: __heading(3),
h4: __heading(4),
h5: __heading(5),
h6: __heading(6),

p: (el, ctx, state) => `\n${body(el, ctx, state)}\n`,
p: (el, ctx, state) => `\n${__body(el, ctx, state)}\n`,

img: (el) => `![${el[1].alt || ""}](${el[1].src})`,

a: (el, ctx, state) => `[${body(el, ctx, state)}](${el[1].href})`,
a: (el, ctx, state) => {
let { href, title } = el[1];
title = __resolve(title);
return `[${__body(el, ctx, state)}](${__resolve(href)}${
title ? ` "${title}"` : ""
})`;
},

em: (el, ctx, state) => `_${body(el, ctx, state)}_`,
em: (el, ctx, state) => `_${__body(el, ctx, state)}_`,

strong: (el, ctx, state) => `**${body(el, ctx, state)}**`,
strong: (el, ctx, state) => `**${__body(el, ctx, state)}**`,

pre: (el, ctx, state) =>
`\n\`\`\`${el[1].lang || ""}\n${body(el, ctx, {
`\n\`\`\`${el[1].lang || ""}\n${__body(el, ctx, {
...state,
pre: true,
sep: "\n",
})}\n\`\`\`\n`,

code: (el, ctx, state) =>
state.pre ? el[2][0] : `\`${body(el, ctx, state)}\``,
state.pre ? el[2][0] : `\`${__body(el, ctx, state)}\``,

ul: (el, ctx, state) => {
const cstate: SerializeState = {
...state,
indent: state.indent + 4,
sep: "\n",
};
return wrap(state.indent === 0 ? "\n" : "")(body(el, ctx, cstate));
return wrap(state.indent === 0 ? "\n" : "")(
__body(el, ctx, cstate)
);
},

ol: (el, ctx, state) => {
Expand All @@ -141,18 +154,26 @@ export const serializeElement = defmulti<any, any, SerializeState, string>(
id: 0,
sep: "\n",
};
return wrap(state.indent === 0 ? "\n" : "")(body(el, ctx, cstate));
return wrap(state.indent === 0 ? "\n" : "")(
__body(el, ctx, cstate)
);
},

li: (el, ctx, state) =>
repeat(" ", state.indent - 4) +
(state.id != null ? ++state.id + "." : "-") +
" " +
body(el, ctx, { ...state, sep: "" }),
__body(el, ctx, { ...state, sep: "" }),

blockquote: (el, ctx, state) => `\n> ${body(el, ctx, state)}\n`,
blockquote: (el, ctx, state) =>
`\n${repeat(">", state.indent + 1)} ${__body(el, ctx, {
...state,
indent: state.indent + 1,
blockquote: true,
})}\n`,

br: () => "\\\n",
br: (_, __, state) =>
state.blockquote ? `\\\n${repeat(">", state.indent)} ` : "\\\n",

hr: () => "\n---\n",

Expand Down Expand Up @@ -181,7 +202,7 @@ export const serializeElement = defmulti<any, any, SerializeState, string>(
tbody = rows(child[2]);
break;
case "caption":
caption = body(child, ctx, state);
caption = __body(child, ctx, state);
break;
default:
// TODO output warning?
Expand Down
5 changes: 2 additions & 3 deletions packages/hiccup-markdown/test/serialize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ group("hiccup-markdown", {
// link component for thi.ng URLs
const thingLink = (_: any, id: string, label: any) => [
"a",
{ href: `http://thi.ng/${id}` },
{ href: `https://thi.ng/${id}` },
label,
];

Expand Down Expand Up @@ -124,8 +124,7 @@ My magic number is: 42
_Table caption_
More info [here](http://thi.ng/hiccup-markdown).
`
More info [here](https://thi.ng/hiccup-markdown).`
);
},
});

0 comments on commit a489c0e

Please sign in to comment.