153 lines
5.9 KiB
JavaScript
153 lines
5.9 KiB
JavaScript
|
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);
|
||
|
});
|