mirror of
https://github.com/TriliumNext/Notes.git
synced 2025-07-29 19:12:27 +08:00
feat(export/markdown): basic support for admonitions
This commit is contained in:
parent
b1e3ea4c80
commit
6d67e69e2f
@ -103,4 +103,94 @@ describe("Markdown export", () => {
|
|||||||
expect(markdownExportService.toMarkdown(html)).toBe(html);
|
expect(markdownExportService.toMarkdown(html)).toBe(html);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("exports admonitions properly", () => {
|
||||||
|
const html = trimIndentation`\
|
||||||
|
<p>
|
||||||
|
Before
|
||||||
|
</p>
|
||||||
|
<aside class="admonition note">
|
||||||
|
<p>
|
||||||
|
This is a note.
|
||||||
|
</p>
|
||||||
|
</aside>
|
||||||
|
<aside class="admonition tip">
|
||||||
|
<p>
|
||||||
|
This is a tip.
|
||||||
|
</p>
|
||||||
|
</aside>
|
||||||
|
<aside class="admonition important">
|
||||||
|
<p>
|
||||||
|
This is a very important information.
|
||||||
|
</p>
|
||||||
|
<figure class="table">
|
||||||
|
<table>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
1
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
2
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
3
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
4
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</figure>
|
||||||
|
</aside>
|
||||||
|
<aside class="admonition caution">
|
||||||
|
<p>
|
||||||
|
This is a caution.
|
||||||
|
</p>
|
||||||
|
</aside>
|
||||||
|
<aside class="admonition warning">
|
||||||
|
<h2>
|
||||||
|
Title goes here
|
||||||
|
</h2>
|
||||||
|
<p>
|
||||||
|
This is a warning.
|
||||||
|
</p>
|
||||||
|
</aside>
|
||||||
|
<p>
|
||||||
|
After
|
||||||
|
</p>
|
||||||
|
`;
|
||||||
|
|
||||||
|
const space = " "; // editor config trimming space.
|
||||||
|
const expected = trimIndentation`\
|
||||||
|
Before
|
||||||
|
|
||||||
|
> [!NOTE]
|
||||||
|
> This is a note.
|
||||||
|
|
||||||
|
> [!TIP]
|
||||||
|
> This is a tip.
|
||||||
|
|
||||||
|
> [!IMPORTANT]
|
||||||
|
> This is a very important information.
|
||||||
|
>${space}
|
||||||
|
> | | |
|
||||||
|
> | --- | --- |
|
||||||
|
> | 1 | 2 |
|
||||||
|
> | 3 | 4 |
|
||||||
|
|
||||||
|
> [!CAUTION]
|
||||||
|
> This is a caution.
|
||||||
|
|
||||||
|
> [!WARNING]
|
||||||
|
> ## Title goes here
|
||||||
|
>${space}
|
||||||
|
> This is a warning.
|
||||||
|
|
||||||
|
After`;
|
||||||
|
expect(markdownExportService.toMarkdown(html)).toBe(expected);
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -31,6 +31,7 @@ function toMarkdown(content: string) {
|
|||||||
// Filter is heavily based on: https://github.com/mixmark-io/turndown/issues/274#issuecomment-458730974
|
// Filter is heavily based on: https://github.com/mixmark-io/turndown/issues/274#issuecomment-458730974
|
||||||
instance.addRule("fencedCodeBlock", fencedCodeBlockFilter);
|
instance.addRule("fencedCodeBlock", fencedCodeBlockFilter);
|
||||||
instance.addRule("img", buildImageFilter());
|
instance.addRule("img", buildImageFilter());
|
||||||
|
instance.addRule("admonition", buildAdmonitionFilter());
|
||||||
instance.use(turndownPluginGfm.gfm);
|
instance.use(turndownPluginGfm.gfm);
|
||||||
instance.keep([ "kbd" ]);
|
instance.keep([ "kbd" ]);
|
||||||
}
|
}
|
||||||
@ -100,6 +101,57 @@ function buildImageFilter() {
|
|||||||
return imageFilter;
|
return imageFilter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function buildAdmonitionFilter() {
|
||||||
|
const admonitionTypeMappings: Record<string, string> = {
|
||||||
|
note: "NOTE",
|
||||||
|
tip: "TIP",
|
||||||
|
important: "IMPORTANT",
|
||||||
|
caution: "CAUTION",
|
||||||
|
warning: "WARNING"
|
||||||
|
};
|
||||||
|
|
||||||
|
const defaultAdmonitionType = admonitionTypeMappings.note;
|
||||||
|
|
||||||
|
function parseAdmonitionType(_node: Node) {
|
||||||
|
if (!("getAttribute" in _node)) {
|
||||||
|
return defaultAdmonitionType;
|
||||||
|
}
|
||||||
|
|
||||||
|
const node = _node as Element;
|
||||||
|
const classList = node.getAttribute("class")?.split(" ") ?? [];
|
||||||
|
|
||||||
|
for (const className of classList) {
|
||||||
|
if (className === "admonition") {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const mappedType = admonitionTypeMappings[className];
|
||||||
|
if (mappedType) {
|
||||||
|
return mappedType;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return defaultAdmonitionType;
|
||||||
|
}
|
||||||
|
|
||||||
|
const admonitionFilter: TurndownService.Rule = {
|
||||||
|
filter(node, options) {
|
||||||
|
return node.nodeName === "ASIDE" && node.classList.contains("admonition");
|
||||||
|
},
|
||||||
|
replacement(content, node) {
|
||||||
|
// Parse the admonition type.
|
||||||
|
const admonitionType = parseAdmonitionType(node);
|
||||||
|
|
||||||
|
content = content.replace(/^\n+|\n+$/g, '');
|
||||||
|
content = content.replace(/^/gm, '> ');
|
||||||
|
content = `> [!${admonitionType}]\n` + content;
|
||||||
|
|
||||||
|
return "\n\n" + content + "\n\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return admonitionFilter;
|
||||||
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
toMarkdown
|
toMarkdown
|
||||||
};
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user