Skip to content
代码片段 群组 项目
提交 d8affb73 编辑于 作者: Simon Knox's avatar Simon Knox
浏览文件

Merge branch '355787-setup-preserve-unchanged-markdown-feature-flag' into 'master'

Rename default Markdown deserializer in the Content Editor

See merge request gitlab-org/gitlab!85551
No related branches found
No related tags found
无相关合并请求
显示 25 个添加46 个删除
...@@ -2,7 +2,7 @@ import { Extension } from '@tiptap/core'; ...@@ -2,7 +2,7 @@ import { Extension } from '@tiptap/core';
import { Plugin, PluginKey } from 'prosemirror-state'; import { Plugin, PluginKey } from 'prosemirror-state';
import { __ } from '~/locale'; import { __ } from '~/locale';
import { VARIANT_DANGER } from '~/flash'; import { VARIANT_DANGER } from '~/flash';
import createMarkdownDeserializer from '../services/markdown_deserializer'; import createMarkdownDeserializer from '../services/gl_api_markdown_deserializer';
import { import {
ALERT_EVENT, ALERT_EVENT,
LOADING_CONTENT_EVENT, LOADING_CONTENT_EVENT,
......
...@@ -36,16 +36,6 @@ const codeBlockLanguageLoader = { ...@@ -36,16 +36,6 @@ const codeBlockLanguageLoader = {
return this.lowlight.registered(language); return this.lowlight.registered(language);
}, },
loadLanguagesFromDOM(domTree) {
const languages = [];
domTree.querySelectorAll('pre').forEach((preElement) => {
languages.push(preElement.getAttribute('lang'));
});
return this.loadLanguages(languages);
},
loadLanguageFromInputRule(match) { loadLanguageFromInputRule(match) {
const { syntax } = this.findLanguageBySyntax(match[1]); const { syntax } = this.findLanguageBySyntax(match[1]);
......
...@@ -52,9 +52,9 @@ export class ContentEditor { ...@@ -52,9 +52,9 @@ export class ContentEditor {
}); });
if (Object.keys(result).length !== 0) { if (Object.keys(result).length !== 0) {
const { document, dom } = result; const { document, languages } = result;
await languageLoader.loadLanguagesFromDOM(dom); await languageLoader.loadLanguages(languages);
tr.setSelection(selection) tr.setSelection(selection)
.replaceSelectionWith(document, false) .replaceSelectionWith(document, false)
......
...@@ -58,7 +58,7 @@ import Video from '../extensions/video'; ...@@ -58,7 +58,7 @@ import Video from '../extensions/video';
import WordBreak from '../extensions/word_break'; import WordBreak from '../extensions/word_break';
import { ContentEditor } from './content_editor'; import { ContentEditor } from './content_editor';
import createMarkdownSerializer from './markdown_serializer'; import createMarkdownSerializer from './markdown_serializer';
import createMarkdownDeserializer from './markdown_deserializer'; import createGlApiMarkdownDeserializer from './gl_api_markdown_deserializer';
import trackInputRulesAndShortcuts from './track_input_rules_and_shortcuts'; import trackInputRulesAndShortcuts from './track_input_rules_and_shortcuts';
import languageLoader from './code_block_language_loader'; import languageLoader from './code_block_language_loader';
...@@ -146,7 +146,7 @@ export const createContentEditor = ({ ...@@ -146,7 +146,7 @@ export const createContentEditor = ({
const trackedExtensions = allExtensions.map(trackInputRulesAndShortcuts); const trackedExtensions = allExtensions.map(trackInputRulesAndShortcuts);
const tiptapEditor = createTiptapEditor({ extensions: trackedExtensions, ...tiptapOptions }); const tiptapEditor = createTiptapEditor({ extensions: trackedExtensions, ...tiptapOptions });
const serializer = createMarkdownSerializer({ serializerConfig }); const serializer = createMarkdownSerializer({ serializerConfig });
const deserializer = createMarkdownDeserializer({ render: renderMarkdown }); const deserializer = createGlApiMarkdownDeserializer({ render: renderMarkdown });
return new ContentEditor({ tiptapEditor, serializer, eventHub, deserializer, languageLoader }); return new ContentEditor({ tiptapEditor, serializer, eventHub, deserializer, languageLoader });
}; };
...@@ -18,6 +18,7 @@ export default ({ render }) => { ...@@ -18,6 +18,7 @@ export default ({ render }) => {
return { return {
deserialize: async ({ schema, content }) => { deserialize: async ({ schema, content }) => {
const html = await render(content); const html = await render(content);
const languages = [];
if (!html) return {}; if (!html) return {};
...@@ -27,7 +28,11 @@ export default ({ render }) => { ...@@ -27,7 +28,11 @@ export default ({ render }) => {
// append original source as a comment that nodes can access // append original source as a comment that nodes can access
body.append(document.createComment(content)); body.append(document.createComment(content));
return { document: ProseMirrorDOMParser.fromSchema(schema).parse(body), dom: body }; body.querySelectorAll('pre').forEach((preElement) => {
languages.push(preElement.getAttribute('lang'));
});
return { document: ProseMirrorDOMParser.fromSchema(schema).parse(body), languages };
}, },
}; };
}; };
...@@ -76,24 +76,6 @@ describe('content_editor/services/code_block_language_loader', () => { ...@@ -76,24 +76,6 @@ describe('content_editor/services/code_block_language_loader', () => {
}); });
}); });
describe('loadLanguagesFromDOM', () => {
it('loads highlight.js language packages identified by pre tags in a DOM fragment', async () => {
const parser = new DOMParser();
const { body } = parser.parseFromString(
`
<pre lang="javascript"></pre>
<pre lang="ruby"></pre>
`,
'text/html',
);
await languageLoader.loadLanguagesFromDOM(body);
expect(lowlight.registerLanguage).toHaveBeenCalledWith('javascript', expect.any(Function));
expect(lowlight.registerLanguage).toHaveBeenCalledWith('ruby', expect.any(Function));
});
});
describe('loadLanguageFromInputRule', () => { describe('loadLanguageFromInputRule', () => {
it('loads highlight.js language packages identified from the input rule', async () => { it('loads highlight.js language packages identified from the input rule', async () => {
const match = new RegExp(backtickInputRegex).exec('```js '); const match = new RegExp(backtickInputRegex).exec('```js ');
......
...@@ -28,7 +28,7 @@ describe('content_editor/services/content_editor', () => { ...@@ -28,7 +28,7 @@ describe('content_editor/services/content_editor', () => {
serializer = { deserialize: jest.fn() }; serializer = { deserialize: jest.fn() };
deserializer = { deserialize: jest.fn() }; deserializer = { deserialize: jest.fn() };
languageLoader = { loadLanguagesFromDOM: jest.fn() }; languageLoader = { loadLanguages: jest.fn() };
eventHub = eventHubFactory(); eventHub = eventHubFactory();
contentEditor = new ContentEditor({ contentEditor = new ContentEditor({
tiptapEditor, tiptapEditor,
...@@ -51,12 +51,12 @@ describe('content_editor/services/content_editor', () => { ...@@ -51,12 +51,12 @@ describe('content_editor/services/content_editor', () => {
describe('when setSerializedContent succeeds', () => { describe('when setSerializedContent succeeds', () => {
let document; let document;
const dom = {}; const languages = ['javascript'];
const testMarkdown = '**bold text**'; const testMarkdown = '**bold text**';
beforeEach(() => { beforeEach(() => {
document = doc(p('document')); document = doc(p('document'));
deserializer.deserialize.mockResolvedValueOnce({ document, dom }); deserializer.deserialize.mockResolvedValueOnce({ document, languages });
}); });
it('emits loadingContent and loadingSuccess event in the eventHub', () => { it('emits loadingContent and loadingSuccess event in the eventHub', () => {
...@@ -81,7 +81,7 @@ describe('content_editor/services/content_editor', () => { ...@@ -81,7 +81,7 @@ describe('content_editor/services/content_editor', () => {
it('passes deserialized DOM document to language loader', async () => { it('passes deserialized DOM document to language loader', async () => {
await contentEditor.setSerializedContent(testMarkdown); await contentEditor.setSerializedContent(testMarkdown);
expect(languageLoader.loadLanguagesFromDOM).toHaveBeenCalledWith(dom); expect(languageLoader.loadLanguages).toHaveBeenCalledWith(languages);
}); });
}); });
......
import createMarkdownDeserializer from '~/content_editor/services/markdown_deserializer'; import createMarkdownDeserializer from '~/content_editor/services/gl_api_markdown_deserializer';
import Bold from '~/content_editor/extensions/bold'; import Bold from '~/content_editor/extensions/bold';
import { createTestEditor, createDocBuilder } from '../test_utils'; import { createTestEditor, createDocBuilder } from '../test_utils';
describe('content_editor/services/markdown_deserializer', () => { describe('content_editor/services/gl_api_markdown_deserializer', () => {
let renderMarkdown; let renderMarkdown;
let doc; let doc;
let p; let p;
...@@ -32,7 +32,9 @@ describe('content_editor/services/markdown_deserializer', () => { ...@@ -32,7 +32,9 @@ describe('content_editor/services/markdown_deserializer', () => {
beforeEach(async () => { beforeEach(async () => {
const deserializer = createMarkdownDeserializer({ render: renderMarkdown }); const deserializer = createMarkdownDeserializer({ render: renderMarkdown });
renderMarkdown.mockResolvedValueOnce(`<p><strong>${text}</strong></p>`); renderMarkdown.mockResolvedValueOnce(
`<p><strong>${text}</strong></p><pre lang="javascript"></pre>`,
);
result = await deserializer.deserialize({ result = await deserializer.deserialize({
content: 'content', content: 'content',
...@@ -40,13 +42,13 @@ describe('content_editor/services/markdown_deserializer', () => { ...@@ -40,13 +42,13 @@ describe('content_editor/services/markdown_deserializer', () => {
}); });
}); });
it('transforms HTML returned by render function to a ProseMirror document', async () => { it('transforms HTML returned by render function to a ProseMirror document', async () => {
const expectedDoc = doc(p(bold(text))); const document = doc(p(bold(text)));
expect(result.document.toJSON()).toEqual(expectedDoc.toJSON()); expect(result.document.toJSON()).toEqual(document.toJSON());
}); });
it('returns parsed HTML as a DOM object', () => { it('returns languages of code blocks found in the document', () => {
expect(result.dom.innerHTML).toEqual(`<p><strong>${text}</strong></p><!--content-->`); expect(result.languages).toEqual(['javascript']);
}); });
}); });
......
...@@ -2,7 +2,7 @@ import { Extension } from '@tiptap/core'; ...@@ -2,7 +2,7 @@ import { Extension } from '@tiptap/core';
import BulletList from '~/content_editor/extensions/bullet_list'; import BulletList from '~/content_editor/extensions/bullet_list';
import ListItem from '~/content_editor/extensions/list_item'; import ListItem from '~/content_editor/extensions/list_item';
import Paragraph from '~/content_editor/extensions/paragraph'; import Paragraph from '~/content_editor/extensions/paragraph';
import markdownDeserializer from '~/content_editor/services/markdown_deserializer'; import markdownDeserializer from '~/content_editor/services/gl_api_markdown_deserializer';
import { getMarkdownSource, getFullSource } from '~/content_editor/services/markdown_sourcemap'; import { getMarkdownSource, getFullSource } from '~/content_editor/services/markdown_sourcemap';
import { createTestEditor, createDocBuilder } from '../test_utils'; import { createTestEditor, createDocBuilder } from '../test_utils';
......
0% 加载中 .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册