chore: Move editor into codebase (#2930)
This commit is contained in:
129
server/editor/__snapshots__/renderToHtml.test.ts.snap
Normal file
129
server/editor/__snapshots__/renderToHtml.test.ts.snap
Normal file
@@ -0,0 +1,129 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`renders blockquote 1`] = `
|
||||
"<blockquote>
|
||||
<p>blockquote</p>
|
||||
</blockquote>"
|
||||
`;
|
||||
|
||||
exports[`renders bold marks 1`] = `"<p>this is <strong>bold</strong> text</p>"`;
|
||||
|
||||
exports[`renders bullet list 1`] = `
|
||||
"<ul>
|
||||
<li>item one</li>
|
||||
<li>item two
|
||||
<ul>
|
||||
<li>nested item</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>"
|
||||
`;
|
||||
|
||||
exports[`renders checkbox list 1`] = `
|
||||
"<ul>
|
||||
<li class=\\"checkbox-list-item\\"><span class=\\"checkbox \\">[ ]</span>unchecked</li>
|
||||
<li class=\\"checkbox-list-item\\"><span class=\\"checkbox checked\\">[x]</span>checked</li>
|
||||
</ul>"
|
||||
`;
|
||||
|
||||
exports[`renders code block 1`] = `
|
||||
"<pre><code>this is indented code
|
||||
</code></pre>"
|
||||
`;
|
||||
|
||||
exports[`renders code fence 1`] = `
|
||||
"<pre><code class=\\"language-javascript\\">this is code
|
||||
</code></pre>"
|
||||
`;
|
||||
|
||||
exports[`renders code marks 1`] = `"<p>this is <code>inline code</code> text</p>"`;
|
||||
|
||||
exports[`renders headings 1`] = `
|
||||
"<h1>Heading 1</h1>
|
||||
<h2>Heading 2</h2>
|
||||
<h3>Heading 3</h3>
|
||||
<h4>Heading 4</h4>"
|
||||
`;
|
||||
|
||||
exports[`renders highlight marks 1`] = `"<p>this is <span class=\\"highlight\\">highlighted</span> text</p>"`;
|
||||
|
||||
exports[`renders horizontal rule 1`] = `"<hr>"`;
|
||||
|
||||
exports[`renders image 1`] = `"<p><img src=\\"https://lorempixel.com/200/200\\" alt=\\"caption\\"></p>"`;
|
||||
|
||||
exports[`renders image with alignment 1`] = `"<p><img src=\\"https://lorempixel.com/200/200\\" alt=\\"caption\\" title=\\"left-40\\"></p>"`;
|
||||
|
||||
exports[`renders info notice 1`] = `
|
||||
"<div class=\\"notice notice-info\\">
|
||||
<p>content of notice</p>
|
||||
</div>"
|
||||
`;
|
||||
|
||||
exports[`renders italic marks 1`] = `"<p>this is <em>italic</em> text</p>"`;
|
||||
|
||||
exports[`renders italic marks 2`] = `"<p>this is <em>also italic</em> text</p>"`;
|
||||
|
||||
exports[`renders link marks 1`] = `"<p>this is <a href=\\"https://www.example.com\\">linked</a> text</p>"`;
|
||||
|
||||
exports[`renders ordered list 1`] = `
|
||||
"<ol>
|
||||
<li>item one</li>
|
||||
<li>item two</li>
|
||||
</ol>"
|
||||
`;
|
||||
|
||||
exports[`renders ordered list 2`] = `
|
||||
"<ol>
|
||||
<li>item one</li>
|
||||
<li>item two</li>
|
||||
</ol>"
|
||||
`;
|
||||
|
||||
exports[`renders plain text as paragraph 1`] = `"<p>plain text</p>"`;
|
||||
|
||||
exports[`renders table 1`] = `
|
||||
"<table>
|
||||
<tr>
|
||||
<th>
|
||||
<p>heading</p></th>
|
||||
<th style=\\"text-align:center\\">
|
||||
<p>centered</p></th>
|
||||
<th style=\\"text-align:right\\">
|
||||
<p>right aligned</p></th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<p></p></td>
|
||||
<td style=\\"text-align:center\\">
|
||||
<p>center</p></td>
|
||||
<td style=\\"text-align:right\\">
|
||||
<p></p></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<p></p></td>
|
||||
<td style=\\"text-align:center\\">
|
||||
<p></p></td>
|
||||
<td style=\\"text-align:right\\">
|
||||
<p>bottom r</p></td>
|
||||
</tr>
|
||||
</table>"
|
||||
`;
|
||||
|
||||
exports[`renders template placeholder marks 1`] = `"<p>this is <span class=\\"placeholder\\">a placeholder</span></p>"`;
|
||||
|
||||
exports[`renders tip notice 1`] = `
|
||||
"<div class=\\"notice notice-tip\\">
|
||||
<p>content of notice</p>
|
||||
</div>"
|
||||
`;
|
||||
|
||||
exports[`renders underline marks 1`] = `"<p>this is <underline>underlined</underline> text</p>"`;
|
||||
|
||||
exports[`renders underline marks 2`] = `"<p>this is <s>strikethrough</s> text</p>"`;
|
||||
|
||||
exports[`renders warning notice 1`] = `
|
||||
"<div class=\\"notice notice-warning\\">
|
||||
<p>content of notice</p>
|
||||
</div>"
|
||||
`;
|
||||
10
server/editor/index.test.ts
Normal file
10
server/editor/index.test.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { parser } from ".";
|
||||
|
||||
test("renders an empty doc", () => {
|
||||
const ast = parser.parse("");
|
||||
|
||||
expect(ast.toJSON()).toEqual({
|
||||
content: [{ type: "paragraph" }],
|
||||
type: "doc",
|
||||
});
|
||||
});
|
||||
85
server/editor/index.ts
Normal file
85
server/editor/index.ts
Normal file
@@ -0,0 +1,85 @@
|
||||
import { Schema } from "prosemirror-model";
|
||||
import ExtensionManager from "@shared/editor/lib/ExtensionManager";
|
||||
|
||||
// marks
|
||||
import Bold from "@shared/editor/marks/Bold";
|
||||
import Code from "@shared/editor/marks/Code";
|
||||
import Highlight from "@shared/editor/marks/Highlight";
|
||||
import Italic from "@shared/editor/marks/Italic";
|
||||
import Link from "@shared/editor/marks/Link";
|
||||
import TemplatePlaceholder from "@shared/editor/marks/Placeholder";
|
||||
import Strikethrough from "@shared/editor/marks/Strikethrough";
|
||||
import Underline from "@shared/editor/marks/Underline";
|
||||
|
||||
// nodes
|
||||
import Blockquote from "@shared/editor/nodes/Blockquote";
|
||||
import BulletList from "@shared/editor/nodes/BulletList";
|
||||
import CheckboxItem from "@shared/editor/nodes/CheckboxItem";
|
||||
import CheckboxList from "@shared/editor/nodes/CheckboxList";
|
||||
import CodeBlock from "@shared/editor/nodes/CodeBlock";
|
||||
import CodeFence from "@shared/editor/nodes/CodeFence";
|
||||
import Doc from "@shared/editor/nodes/Doc";
|
||||
import Embed from "@shared/editor/nodes/Embed";
|
||||
import Emoji from "@shared/editor/nodes/Emoji";
|
||||
import HardBreak from "@shared/editor/nodes/HardBreak";
|
||||
import Heading from "@shared/editor/nodes/Heading";
|
||||
import HorizontalRule from "@shared/editor/nodes/HorizontalRule";
|
||||
import Image from "@shared/editor/nodes/Image";
|
||||
import ListItem from "@shared/editor/nodes/ListItem";
|
||||
import Notice from "@shared/editor/nodes/Notice";
|
||||
import OrderedList from "@shared/editor/nodes/OrderedList";
|
||||
import Paragraph from "@shared/editor/nodes/Paragraph";
|
||||
import Table from "@shared/editor/nodes/Table";
|
||||
import TableCell from "@shared/editor/nodes/TableCell";
|
||||
import TableHeadCell from "@shared/editor/nodes/TableHeadCell";
|
||||
import TableRow from "@shared/editor/nodes/TableRow";
|
||||
import Text from "@shared/editor/nodes/Text";
|
||||
import render from "./renderToHtml";
|
||||
|
||||
const extensions = new ExtensionManager([
|
||||
new Doc(),
|
||||
new Text(),
|
||||
new HardBreak(),
|
||||
new Paragraph(),
|
||||
new Blockquote(),
|
||||
new Emoji(),
|
||||
new BulletList(),
|
||||
new CodeBlock(),
|
||||
new CodeFence(),
|
||||
new CheckboxList(),
|
||||
new CheckboxItem(),
|
||||
new Embed(),
|
||||
new ListItem(),
|
||||
new Notice(),
|
||||
new Heading(),
|
||||
new HorizontalRule(),
|
||||
new Image(),
|
||||
new Table(),
|
||||
new TableCell(),
|
||||
new TableHeadCell(),
|
||||
new TableRow(),
|
||||
new Bold(),
|
||||
new Code(),
|
||||
new Highlight(),
|
||||
new Italic(),
|
||||
new Link(),
|
||||
new Strikethrough(),
|
||||
new TemplatePlaceholder(),
|
||||
new Underline(),
|
||||
new OrderedList(),
|
||||
]);
|
||||
|
||||
export const schema = new Schema({
|
||||
nodes: extensions.nodes,
|
||||
marks: extensions.marks,
|
||||
});
|
||||
|
||||
export const parser = extensions.parser({
|
||||
schema,
|
||||
plugins: extensions.rulePlugins,
|
||||
});
|
||||
|
||||
export const serializer = extensions.serializer();
|
||||
|
||||
export const renderToHtml = (markdown: string): string =>
|
||||
render(markdown, extensions.rulePlugins);
|
||||
154
server/editor/renderToHtml.test.ts
Normal file
154
server/editor/renderToHtml.test.ts
Normal file
@@ -0,0 +1,154 @@
|
||||
import renderToHtml from "./renderToHtml";
|
||||
|
||||
test("renders an empty string", () => {
|
||||
expect(renderToHtml("")).toBe("");
|
||||
});
|
||||
|
||||
test("renders plain text as paragraph", () => {
|
||||
expect(renderToHtml("plain text")).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test("renders blockquote", () => {
|
||||
expect(renderToHtml("> blockquote")).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test("renders code block", () => {
|
||||
expect(
|
||||
renderToHtml(`
|
||||
this is indented code
|
||||
`)
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test("renders code fence", () => {
|
||||
expect(
|
||||
renderToHtml(`\`\`\`javascript
|
||||
this is code
|
||||
\`\`\``)
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test("renders checkbox list", () => {
|
||||
expect(
|
||||
renderToHtml(`- [ ] unchecked
|
||||
- [x] checked`)
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test("renders bullet list", () => {
|
||||
expect(
|
||||
renderToHtml(`- item one
|
||||
- item two
|
||||
- nested item`)
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test("renders info notice", () => {
|
||||
expect(
|
||||
renderToHtml(`:::info
|
||||
content of notice
|
||||
:::`)
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test("renders warning notice", () => {
|
||||
expect(
|
||||
renderToHtml(`:::warning
|
||||
content of notice
|
||||
:::`)
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test("renders tip notice", () => {
|
||||
expect(
|
||||
renderToHtml(`:::tip
|
||||
content of notice
|
||||
:::`)
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test("renders headings", () => {
|
||||
expect(
|
||||
renderToHtml(`# Heading 1
|
||||
|
||||
## Heading 2
|
||||
|
||||
### Heading 3
|
||||
|
||||
#### Heading 4`)
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test("renders horizontal rule", () => {
|
||||
expect(renderToHtml(`---`)).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test("renders image", () => {
|
||||
expect(
|
||||
renderToHtml(``)
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test("renders image with alignment", () => {
|
||||
expect(
|
||||
renderToHtml(``)
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test("renders table", () => {
|
||||
expect(
|
||||
renderToHtml(`
|
||||
| heading | centered | right aligned |
|
||||
|---------|:--------:|--------------:|
|
||||
| | center | |
|
||||
| | | bottom r |
|
||||
`)
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test("renders bold marks", () => {
|
||||
expect(renderToHtml(`this is **bold** text`)).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test("renders code marks", () => {
|
||||
expect(renderToHtml(`this is \`inline code\` text`)).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test("renders highlight marks", () => {
|
||||
expect(renderToHtml(`this is ==highlighted== text`)).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test("renders italic marks", () => {
|
||||
expect(renderToHtml(`this is *italic* text`)).toMatchSnapshot();
|
||||
expect(renderToHtml(`this is _also italic_ text`)).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test("renders template placeholder marks", () => {
|
||||
expect(renderToHtml(`this is !!a placeholder!!`)).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test("renders underline marks", () => {
|
||||
expect(renderToHtml(`this is __underlined__ text`)).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test("renders link marks", () => {
|
||||
expect(
|
||||
renderToHtml(`this is [linked](https://www.example.com) text`)
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test("renders underline marks", () => {
|
||||
expect(renderToHtml(`this is ~~strikethrough~~ text`)).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test("renders ordered list", () => {
|
||||
expect(
|
||||
renderToHtml(`1. item one
|
||||
1. item two`)
|
||||
).toMatchSnapshot();
|
||||
|
||||
expect(
|
||||
renderToHtml(`1. item one
|
||||
2. item two`)
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
29
server/editor/renderToHtml.ts
Normal file
29
server/editor/renderToHtml.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import { PluginSimple } from "markdown-it";
|
||||
import createMarkdown from "@shared/editor/lib/markdown/rules";
|
||||
import breakRule from "@shared/editor/rules/breaks";
|
||||
import checkboxRule from "@shared/editor/rules/checkboxes";
|
||||
import embedsRule from "@shared/editor/rules/embeds";
|
||||
import emojiRule from "@shared/editor/rules/emoji";
|
||||
import markRule from "@shared/editor/rules/mark";
|
||||
import noticesRule from "@shared/editor/rules/notices";
|
||||
import tablesRule from "@shared/editor/rules/tables";
|
||||
import underlinesRule from "@shared/editor/rules/underlines";
|
||||
|
||||
const defaultRules = [
|
||||
embedsRule([]),
|
||||
breakRule,
|
||||
checkboxRule,
|
||||
markRule({ delim: "==", mark: "highlight" }),
|
||||
markRule({ delim: "!!", mark: "placeholder" }),
|
||||
underlinesRule,
|
||||
tablesRule,
|
||||
noticesRule,
|
||||
emojiRule,
|
||||
];
|
||||
|
||||
export default function renderToHtml(
|
||||
markdown: string,
|
||||
rulePlugins: PluginSimple[] = defaultRules
|
||||
): string {
|
||||
return createMarkdown({ plugins: rulePlugins }).render(markdown).trim();
|
||||
}
|
||||
Reference in New Issue
Block a user