1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
|
import fs from 'fs';
import { DOMSerializer } from 'prosemirror-model';
import jsYaml from 'js-yaml';
// TODO: DRY up duplication with spec/frontend/content_editor/services/markdown_serializer_spec.js
// See https://gitlab.com/groups/gitlab-org/-/epics/7719#plan
import Blockquote from '~/content_editor/extensions/blockquote';
import Bold from '~/content_editor/extensions/bold';
import BulletList from '~/content_editor/extensions/bullet_list';
import Code from '~/content_editor/extensions/code';
import CodeBlockHighlight from '~/content_editor/extensions/code_block_highlight';
import DescriptionItem from '~/content_editor/extensions/description_item';
import DescriptionList from '~/content_editor/extensions/description_list';
import Details from '~/content_editor/extensions/details';
import DetailsContent from '~/content_editor/extensions/details_content';
import Division from '~/content_editor/extensions/division';
import Emoji from '~/content_editor/extensions/emoji';
import Figure from '~/content_editor/extensions/figure';
import FigureCaption from '~/content_editor/extensions/figure_caption';
import FootnoteDefinition from '~/content_editor/extensions/footnote_definition';
import FootnoteReference from '~/content_editor/extensions/footnote_reference';
import FootnotesSection from '~/content_editor/extensions/footnotes_section';
import HardBreak from '~/content_editor/extensions/hard_break';
import Heading from '~/content_editor/extensions/heading';
import HorizontalRule from '~/content_editor/extensions/horizontal_rule';
import Image from '~/content_editor/extensions/image';
import InlineDiff from '~/content_editor/extensions/inline_diff';
import Italic from '~/content_editor/extensions/italic';
import Link from '~/content_editor/extensions/link';
import ListItem from '~/content_editor/extensions/list_item';
import OrderedList from '~/content_editor/extensions/ordered_list';
import Strike from '~/content_editor/extensions/strike';
import Table from '~/content_editor/extensions/table';
import TableCell from '~/content_editor/extensions/table_cell';
import TableHeader from '~/content_editor/extensions/table_header';
import TableRow from '~/content_editor/extensions/table_row';
import TaskItem from '~/content_editor/extensions/task_item';
import TaskList from '~/content_editor/extensions/task_list';
import createMarkdownDeserializer from '~/content_editor/services/remark_markdown_deserializer';
import { createTestEditor } from 'jest/content_editor/test_utils';
import { setTestTimeout } from 'jest/__helpers__/timeout';
const tiptapEditor = createTestEditor({
extensions: [
Blockquote,
Bold,
BulletList,
Code,
CodeBlockHighlight,
DescriptionItem,
DescriptionList,
Details,
DetailsContent,
Division,
Emoji,
FootnoteDefinition,
FootnoteReference,
FootnotesSection,
Figure,
FigureCaption,
HardBreak,
Heading,
HorizontalRule,
Image,
InlineDiff,
Italic,
Link,
ListItem,
OrderedList,
Strike,
Table,
TableCell,
TableHeader,
TableRow,
TaskItem,
TaskList,
],
});
async function renderMarkdownToHTMLAndJSON(markdown, schema, deserializer) {
let prosemirrorDocument;
try {
const { document } = await deserializer.deserialize({ schema, content: markdown });
prosemirrorDocument = document;
} catch (e) {
const errorMsg = `Error - check implementation:\n${e.message}`;
return {
html: errorMsg,
json: errorMsg,
};
}
const documentFragment = DOMSerializer.fromSchema(schema).serializeFragment(
prosemirrorDocument.content,
);
const htmlString = documentFragment.firstChild.outerHTML;
const json = prosemirrorDocument.toJSON();
const jsonString = JSON.stringify(json, null, 2);
return { html: htmlString, json: jsonString };
}
function renderHtmlAndJsonForAllExamples(markdownExamples) {
const { schema } = tiptapEditor;
const deserializer = createMarkdownDeserializer();
const exampleNames = Object.keys(markdownExamples);
return exampleNames.reduce(async (promisedExamples, exampleName) => {
const markdown = markdownExamples[exampleName];
const htmlAndJson = await renderMarkdownToHTMLAndJSON(markdown, schema, deserializer);
const examples = await promisedExamples;
examples[exampleName] = htmlAndJson;
return examples;
}, Promise.resolve({}));
}
/* eslint-disable no-undef */
jest.mock('~/emoji');
// The purpose of this file is to deserialize markdown examples
// to WYSIWYG HTML and to prosemirror documents in JSON form, using
// the logic implemented as part of the Content Editor.
//
// It reads an input YAML file containing all the markdown examples,
// and outputs a YAML files containing the rendered HTML and JSON
// corresponding each markdown example.
//
// The input and output file paths are provides as command line arguments.
//
// Although it is implemented as a Jest test, it is not a unit test. We use
// Jest because that is the simplest environment in which to execute the
// relevant Content Editor logic.
//
//
// This script should be invoked via jest with the a command similar to the following:
// yarn jest --testMatch '**/render_wysiwyg_html_and_json.js' ./scripts/lib/glfm/render_wysiwyg_html_and_json.js
it('serializes html to prosemirror json', async () => {
setTestTimeout(20000);
const inputMarkdownTempfilePath = process.env.INPUT_MARKDOWN_YML_PATH;
expect(inputMarkdownTempfilePath).not.toBeUndefined();
const outputWysiwygHtmlAndJsonTempfilePath =
process.env.OUTPUT_WYSIWYG_HTML_AND_JSON_TEMPFILE_PATH;
expect(outputWysiwygHtmlAndJsonTempfilePath).not.toBeUndefined();
/* eslint-enable no-undef */
const markdownExamples = jsYaml.safeLoad(fs.readFileSync(inputMarkdownTempfilePath), {});
const htmlAndJsonExamples = await renderHtmlAndJsonForAllExamples(markdownExamples);
const htmlAndJsonExamplesYamlString = jsYaml.safeDump(htmlAndJsonExamples, {});
fs.writeFileSync(outputWysiwygHtmlAndJsonTempfilePath, htmlAndJsonExamplesYamlString);
});
|