feat(widget-markdown): allow registering remark plugins (#5633)
This commit is contained in:
3
packages/netlify-cms-core/index.d.ts
vendored
3
packages/netlify-cms-core/index.d.ts
vendored
@ -2,6 +2,7 @@
|
||||
declare module 'netlify-cms-core' {
|
||||
import type { ComponentType } from 'react';
|
||||
import type { List, Map } from 'immutable';
|
||||
import type { Pluggable } from 'unified';
|
||||
|
||||
export type CmsBackendType =
|
||||
| 'azure'
|
||||
@ -543,6 +544,7 @@ declare module 'netlify-cms-core' {
|
||||
export interface CMS {
|
||||
getBackend: (name: string) => CmsRegistryBackend | undefined;
|
||||
getEditorComponents: () => Map<string, ComponentType<any>>;
|
||||
getRemarkPlugins: () => Array<Pluggable>;
|
||||
getLocale: (locale: string) => CmsLocalePhrases | undefined;
|
||||
getMediaLibrary: (name: string) => CmsMediaLibrary | undefined;
|
||||
getPreviewStyles: () => PreviewStyle[];
|
||||
@ -552,6 +554,7 @@ declare module 'netlify-cms-core' {
|
||||
init: (options?: InitOptions) => void;
|
||||
registerBackend: (name: string, backendClass: CmsBackendClass) => void;
|
||||
registerEditorComponent: (options: EditorComponentOptions) => void;
|
||||
registerRemarkPlugin: (plugin: Pluggable) => void;
|
||||
registerEventListener: (
|
||||
eventListener: CmsEventListener,
|
||||
options?: CmsEventListenerOptions,
|
||||
|
@ -4,6 +4,7 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import { Map, List } from 'immutable';
|
||||
import { oneLine } from 'common-tags';
|
||||
|
||||
import { getRemarkPlugins } from '../../../lib/registry';
|
||||
import ValidationErrorTypes from '../../../constants/validationErrorTypes';
|
||||
|
||||
function truthy() {
|
||||
@ -326,6 +327,7 @@ export default class Widget extends Component {
|
||||
resolveWidget,
|
||||
widget,
|
||||
getEditorComponents,
|
||||
getRemarkPlugins,
|
||||
query,
|
||||
queryHits,
|
||||
clearSearch,
|
||||
|
@ -7,7 +7,12 @@ import Frame, { FrameContextConsumer } from 'react-frame-component';
|
||||
import { lengths } from 'netlify-cms-ui-default';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { resolveWidget, getPreviewTemplate, getPreviewStyles } from '../../../lib/registry';
|
||||
import {
|
||||
resolveWidget,
|
||||
getPreviewTemplate,
|
||||
getPreviewStyles,
|
||||
getRemarkPlugins,
|
||||
} from '../../../lib/registry';
|
||||
import { ErrorBoundary } from '../../UI';
|
||||
import { selectTemplateName, selectInferedField, selectField } from '../../../reducers/collections';
|
||||
import { boundGetAsset } from '../../../actions/media';
|
||||
@ -45,6 +50,7 @@ export class PreviewPane extends React.Component {
|
||||
entry={entry}
|
||||
fieldsMetaData={metadata}
|
||||
resolveWidget={resolveWidget}
|
||||
getRemarkPlugins={getRemarkPlugins}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
@ -26,6 +26,7 @@ const registry = {
|
||||
previewStyles: [],
|
||||
widgets: {},
|
||||
editorComponents: Map(),
|
||||
remarkPlugins: [],
|
||||
widgetValueSerializers: {},
|
||||
mediaLibraries: [],
|
||||
locales: {},
|
||||
@ -43,6 +44,8 @@ export default {
|
||||
resolveWidget,
|
||||
registerEditorComponent,
|
||||
getEditorComponents,
|
||||
registerRemarkPlugin,
|
||||
getRemarkPlugins,
|
||||
registerWidgetValueSerializer,
|
||||
getWidgetValueSerializer,
|
||||
registerBackend,
|
||||
@ -163,6 +166,19 @@ export function getEditorComponents() {
|
||||
return registry.editorComponents;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remark plugins
|
||||
*/
|
||||
/** @typedef {import('unified').Pluggable} RemarkPlugin */
|
||||
/** @type {(plugin: RemarkPlugin) => void} */
|
||||
export function registerRemarkPlugin(plugin) {
|
||||
registry.remarkPlugins.push(plugin);
|
||||
}
|
||||
/** @type {() => Array<RemarkPlugin>} */
|
||||
export function getRemarkPlugins() {
|
||||
return registry.remarkPlugins;
|
||||
}
|
||||
|
||||
/**
|
||||
* Widget Serializers
|
||||
*/
|
||||
|
@ -45,8 +45,8 @@ function createEmptyRawDoc() {
|
||||
return { nodes: [emptyBlock] };
|
||||
}
|
||||
|
||||
function createSlateValue(rawValue, { voidCodeBlock }) {
|
||||
const rawDoc = rawValue && markdownToSlate(rawValue, { voidCodeBlock });
|
||||
function createSlateValue(rawValue, { voidCodeBlock, remarkPlugins }) {
|
||||
const rawDoc = rawValue && markdownToSlate(rawValue, { voidCodeBlock, remarkPlugins });
|
||||
const rawDocHasNodes = !isEmpty(get(rawDoc, 'nodes'));
|
||||
const document = Document.fromJSON(rawDocHasNodes ? rawDoc : createEmptyRawDoc());
|
||||
return Value.create({ document });
|
||||
@ -95,6 +95,8 @@ export default class Editor extends React.Component {
|
||||
? editorComponents
|
||||
: editorComponents.set('code-block', { label: 'Code Block', type: 'code-block' });
|
||||
|
||||
this.remarkPlugins = props.getRemarkPlugins();
|
||||
|
||||
mergeMediaConfig(this.editorComponents, this.props.field);
|
||||
this.renderBlock = renderBlock({
|
||||
classNameWrapper: props.className,
|
||||
@ -108,9 +110,13 @@ export default class Editor extends React.Component {
|
||||
getAsset: props.getAsset,
|
||||
resolveWidget: props.resolveWidget,
|
||||
t: props.t,
|
||||
remarkPlugins: this.remarkPlugins,
|
||||
});
|
||||
this.state = {
|
||||
value: createSlateValue(this.props.value, { voidCodeBlock: !!this.codeBlockComponent }),
|
||||
value: createSlateValue(this.props.value, {
|
||||
voidCodeBlock: !!this.codeBlockComponent,
|
||||
remarkPlugins: this.remarkPlugins,
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
@ -123,14 +129,20 @@ export default class Editor extends React.Component {
|
||||
value: PropTypes.string,
|
||||
field: ImmutablePropTypes.map.isRequired,
|
||||
getEditorComponents: PropTypes.func.isRequired,
|
||||
getRemarkPlugins: PropTypes.func.isRequired,
|
||||
isShowModeToggle: PropTypes.bool.isRequired,
|
||||
t: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
shouldComponentUpdate(nextProps, nextState) {
|
||||
if (!this.state.value.equals(nextState.value)) return true;
|
||||
|
||||
const raw = nextState.value.document.toJS();
|
||||
const markdown = slateToMarkdown(raw, { voidCodeBlock: this.codeBlockComponent });
|
||||
return !this.state.value.equals(nextState.value) || nextProps.value !== markdown;
|
||||
const markdown = slateToMarkdown(raw, {
|
||||
voidCodeBlock: this.codeBlockComponent,
|
||||
remarkPlugins: this.remarkPlugins,
|
||||
});
|
||||
return nextProps.value !== markdown;
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
@ -143,7 +155,10 @@ export default class Editor extends React.Component {
|
||||
componentDidUpdate(prevProps) {
|
||||
if (prevProps.value !== this.props.value) {
|
||||
this.setState({
|
||||
value: createSlateValue(this.props.value, { voidCodeBlock: !!this.codeBlockComponent }),
|
||||
value: createSlateValue(this.props.value, {
|
||||
voidCodeBlock: !!this.codeBlockComponent,
|
||||
remarkPlugins: this.remarkPlugins,
|
||||
}),
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -183,7 +198,10 @@ export default class Editor extends React.Component {
|
||||
handleDocumentChange = debounce(editor => {
|
||||
const { onChange } = this.props;
|
||||
const raw = editor.value.document.toJS();
|
||||
const markdown = slateToMarkdown(raw, { voidCodeBlock: this.codeBlockComponent });
|
||||
const markdown = slateToMarkdown(raw, {
|
||||
voidCodeBlock: this.codeBlockComponent,
|
||||
remarkPlugins: this.remarkPlugins,
|
||||
});
|
||||
onChange(markdown);
|
||||
}, 150);
|
||||
|
||||
|
@ -76,6 +76,7 @@ export default class MarkdownControl extends React.Component {
|
||||
classNameWrapper,
|
||||
field,
|
||||
getEditorComponents,
|
||||
getRemarkPlugins,
|
||||
resolveWidget,
|
||||
t,
|
||||
isDisabled,
|
||||
@ -95,6 +96,7 @@ export default class MarkdownControl extends React.Component {
|
||||
value={value}
|
||||
field={field}
|
||||
getEditorComponents={getEditorComponents}
|
||||
getRemarkPlugins={getRemarkPlugins}
|
||||
resolveWidget={resolveWidget}
|
||||
pendingFocus={pendingFocus && this.setFocusReceived}
|
||||
t={t}
|
||||
|
@ -5,10 +5,10 @@ import isHotkey from 'is-hotkey';
|
||||
|
||||
import { slateToMarkdown, markdownToSlate, htmlToSlate, markdownToHtml } from '../../serializers';
|
||||
|
||||
function CopyPasteVisual({ getAsset, resolveWidget }) {
|
||||
function CopyPasteVisual({ getAsset, resolveWidget, remarkPlugins }) {
|
||||
function handleCopy(event, editor) {
|
||||
const markdown = slateToMarkdown(editor.value.fragment.toJS());
|
||||
const html = markdownToHtml(markdown, { getAsset, resolveWidget });
|
||||
const markdown = slateToMarkdown(editor.value.fragment.toJS(), { remarkPlugins });
|
||||
const html = markdownToHtml(markdown, { getAsset, resolveWidget, remarkPlugins });
|
||||
setEventTransfer(event, 'text', markdown);
|
||||
setEventTransfer(event, 'html', html);
|
||||
setEventTransfer(event, 'fragment', base64.serializeNode(editor.value.fragment));
|
||||
@ -28,7 +28,9 @@ function CopyPasteVisual({ getAsset, resolveWidget }) {
|
||||
}
|
||||
|
||||
const html = data.types.includes('text/html') && data.getData('text/html');
|
||||
const ast = html ? htmlToSlate(html) : markdownToSlate(data.getData('text/plain'));
|
||||
const ast = html
|
||||
? htmlToSlate(html)
|
||||
: markdownToSlate(data.getData('text/plain'), { remarkPlugins });
|
||||
const doc = Document.fromJSON(ast);
|
||||
return editor.insertFragment(doc);
|
||||
},
|
||||
|
@ -15,7 +15,7 @@ import Shortcode from './Shortcode';
|
||||
import { SLATE_DEFAULT_BLOCK_TYPE as defaultType } from '../../types';
|
||||
import Hotkey, { HOT_KEY_MAP } from './Hotkey';
|
||||
|
||||
function plugins({ getAsset, resolveWidget, t }) {
|
||||
function plugins({ getAsset, resolveWidget, t, remarkPlugins }) {
|
||||
return [
|
||||
{
|
||||
onKeyDown(event, editor, next) {
|
||||
@ -51,7 +51,7 @@ function plugins({ getAsset, resolveWidget, t }) {
|
||||
CloseBlock({ defaultType }),
|
||||
SelectAll(),
|
||||
ForceInsert({ defaultType }),
|
||||
CopyPasteVisual({ getAsset, resolveWidget }),
|
||||
CopyPasteVisual({ getAsset, resolveWidget, remarkPlugins }),
|
||||
Shortcode({ defaultType }),
|
||||
];
|
||||
}
|
||||
|
@ -12,12 +12,12 @@ class MarkdownPreview extends React.Component {
|
||||
};
|
||||
|
||||
render() {
|
||||
const { value, getAsset, resolveWidget, field } = this.props;
|
||||
const { value, getAsset, resolveWidget, field, getRemarkPlugins } = this.props;
|
||||
if (value === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const html = markdownToHtml(value, { getAsset, resolveWidget });
|
||||
const html = markdownToHtml(value, { getAsset, resolveWidget }, getRemarkPlugins?.());
|
||||
const toRender = field?.get('sanitize_preview', false) ? DOMPurify.sanitize(html) : html;
|
||||
|
||||
return <WidgetPreviewContainer dangerouslySetInnerHTML={{ __html: toRender }} />;
|
||||
|
@ -0,0 +1,299 @@
|
||||
import visit from 'unist-util-visit';
|
||||
|
||||
import { markdownToRemark, remarkToMarkdown } from '..';
|
||||
|
||||
describe('registered remark plugins', () => {
|
||||
function withNetlifyLinks() {
|
||||
return function transformer(tree) {
|
||||
visit(tree, 'link', function onLink(node) {
|
||||
node.url = 'https://netlify.com';
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
it('should use remark transformer plugins when converting mdast to markdown', () => {
|
||||
const plugins = [withNetlifyLinks];
|
||||
const result = remarkToMarkdown(
|
||||
{
|
||||
type: 'root',
|
||||
children: [
|
||||
{
|
||||
type: 'paragraph',
|
||||
children: [
|
||||
{
|
||||
type: 'text',
|
||||
value: 'Some ',
|
||||
},
|
||||
{
|
||||
type: 'emphasis',
|
||||
children: [
|
||||
{
|
||||
type: 'text',
|
||||
value: 'important',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
value: ' text with ',
|
||||
},
|
||||
{
|
||||
type: 'link',
|
||||
title: null,
|
||||
url: 'https://this-value-should-be-replaced.com',
|
||||
children: [
|
||||
{
|
||||
type: 'text',
|
||||
value: 'a link',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
value: ' in it.',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
plugins,
|
||||
);
|
||||
expect(result).toMatchInlineSnapshot(
|
||||
`"Some *important* text with [a link](https://netlify.com) in it."`,
|
||||
);
|
||||
});
|
||||
|
||||
it('should use remark transformer plugins when converting markdown to mdast', () => {
|
||||
const plugins = [withNetlifyLinks];
|
||||
const result = markdownToRemark(
|
||||
'Some text with [a link](https://this-value-should-be-replaced.com) in it.',
|
||||
plugins,
|
||||
);
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"children": Array [
|
||||
Object {
|
||||
"children": Array [
|
||||
Object {
|
||||
"children": Array [],
|
||||
"position": Position {
|
||||
"end": Object {
|
||||
"column": 16,
|
||||
"line": 1,
|
||||
"offset": 15,
|
||||
},
|
||||
"indent": Array [],
|
||||
"start": Object {
|
||||
"column": 1,
|
||||
"line": 1,
|
||||
"offset": 0,
|
||||
},
|
||||
},
|
||||
"type": "text",
|
||||
"value": "Some text with ",
|
||||
},
|
||||
Object {
|
||||
"children": Array [
|
||||
Object {
|
||||
"children": Array [],
|
||||
"position": Position {
|
||||
"end": Object {
|
||||
"column": 23,
|
||||
"line": 1,
|
||||
"offset": 22,
|
||||
},
|
||||
"indent": Array [],
|
||||
"start": Object {
|
||||
"column": 17,
|
||||
"line": 1,
|
||||
"offset": 16,
|
||||
},
|
||||
},
|
||||
"type": "text",
|
||||
"value": "a link",
|
||||
},
|
||||
],
|
||||
"position": Position {
|
||||
"end": Object {
|
||||
"column": 67,
|
||||
"line": 1,
|
||||
"offset": 66,
|
||||
},
|
||||
"indent": Array [],
|
||||
"start": Object {
|
||||
"column": 16,
|
||||
"line": 1,
|
||||
"offset": 15,
|
||||
},
|
||||
},
|
||||
"title": null,
|
||||
"type": "link",
|
||||
"url": "https://netlify.com",
|
||||
},
|
||||
Object {
|
||||
"children": Array [],
|
||||
"position": Position {
|
||||
"end": Object {
|
||||
"column": 74,
|
||||
"line": 1,
|
||||
"offset": 73,
|
||||
},
|
||||
"indent": Array [],
|
||||
"start": Object {
|
||||
"column": 67,
|
||||
"line": 1,
|
||||
"offset": 66,
|
||||
},
|
||||
},
|
||||
"type": "text",
|
||||
"value": " in it.",
|
||||
},
|
||||
],
|
||||
"position": Position {
|
||||
"end": Object {
|
||||
"column": 74,
|
||||
"line": 1,
|
||||
"offset": 73,
|
||||
},
|
||||
"indent": Array [],
|
||||
"start": Object {
|
||||
"column": 1,
|
||||
"line": 1,
|
||||
"offset": 0,
|
||||
},
|
||||
},
|
||||
"type": "paragraph",
|
||||
},
|
||||
],
|
||||
"position": Object {
|
||||
"end": Object {
|
||||
"column": 74,
|
||||
"line": 1,
|
||||
"offset": 73,
|
||||
},
|
||||
"start": Object {
|
||||
"column": 1,
|
||||
"line": 1,
|
||||
"offset": 0,
|
||||
},
|
||||
},
|
||||
"type": "root",
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
it('should use remark serializer plugins when converting mdast to markdown', () => {
|
||||
function withEscapedLessThanChar() {
|
||||
if (this.Compiler) {
|
||||
this.Compiler.prototype.visitors.text = node => {
|
||||
return node.value.replace(/</g, '<');
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const plugins = [withEscapedLessThanChar];
|
||||
const result = remarkToMarkdown(
|
||||
{
|
||||
type: 'root',
|
||||
children: [
|
||||
{
|
||||
type: 'paragraph',
|
||||
children: [
|
||||
{
|
||||
type: 'text',
|
||||
value: '<3 Netlify',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
plugins,
|
||||
);
|
||||
expect(result).toMatchInlineSnapshot(`"<3 Netlify"`);
|
||||
});
|
||||
|
||||
it('should use remark preset with settings when converting mdast to markdown', () => {
|
||||
const settings = {
|
||||
emphasis: '_',
|
||||
bullet: '-',
|
||||
};
|
||||
|
||||
const plugins = [{ settings }];
|
||||
const result = remarkToMarkdown(
|
||||
{
|
||||
type: 'root',
|
||||
children: [
|
||||
{
|
||||
type: 'paragraph',
|
||||
children: [
|
||||
{
|
||||
type: 'text',
|
||||
value: 'Some ',
|
||||
},
|
||||
{
|
||||
type: 'emphasis',
|
||||
children: [
|
||||
{
|
||||
type: 'text',
|
||||
value: 'important',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
value: ' points:',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'list',
|
||||
ordered: false,
|
||||
start: null,
|
||||
spread: false,
|
||||
children: [
|
||||
{
|
||||
type: 'listItem',
|
||||
spread: false,
|
||||
checked: null,
|
||||
children: [
|
||||
{
|
||||
type: 'paragraph',
|
||||
children: [
|
||||
{
|
||||
type: 'text',
|
||||
value: 'One',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: 'listItem',
|
||||
spread: false,
|
||||
checked: null,
|
||||
children: [
|
||||
{
|
||||
type: 'paragraph',
|
||||
children: [
|
||||
{
|
||||
type: 'text',
|
||||
value: 'Two',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
plugins,
|
||||
);
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
"Some _important_ points:
|
||||
|
||||
- One
|
||||
- Two"
|
||||
`);
|
||||
});
|
||||
});
|
@ -59,21 +59,24 @@ import { getEditorComponents } from '../MarkdownControl';
|
||||
/**
|
||||
* Deserialize a Markdown string to an MDAST.
|
||||
*/
|
||||
export function markdownToRemark(markdown) {
|
||||
/**
|
||||
* Parse the Markdown string input to an MDAST.
|
||||
*/
|
||||
const parsed = unified()
|
||||
export function markdownToRemark(markdown, remarkPlugins) {
|
||||
const processor = unified()
|
||||
.use(markdownToRemarkPlugin, { fences: true, commonmark: true })
|
||||
.use(markdownToRemarkRemoveTokenizers, { inlineTokenizers: ['url'] })
|
||||
.use(remarkParseShortcodes, { plugins: getEditorComponents() })
|
||||
.use(remarkAllowHtmlEntities)
|
||||
.parse(markdown);
|
||||
.use(remarkSquashReferences)
|
||||
.use(remarkPlugins);
|
||||
|
||||
/**
|
||||
* Parse the Markdown string input to an MDAST.
|
||||
*/
|
||||
const parsed = processor.parse(markdown);
|
||||
|
||||
/**
|
||||
* Further transform the MDAST with plugins.
|
||||
*/
|
||||
const result = unified().use(remarkSquashReferences).runSync(parsed);
|
||||
const result = processor.runSync(parsed);
|
||||
|
||||
return result;
|
||||
}
|
||||
@ -91,7 +94,7 @@ function markdownToRemarkRemoveTokenizers({ inlineTokenizers }) {
|
||||
/**
|
||||
* Serialize an MDAST to a Markdown string.
|
||||
*/
|
||||
export function remarkToMarkdown(obj) {
|
||||
export function remarkToMarkdown(obj, remarkPlugins) {
|
||||
/**
|
||||
* Rewrite the remark-stringify text visitor to simply return the text value,
|
||||
* without encoding or escaping any characters. This means we're completely
|
||||
@ -123,20 +126,24 @@ export function remarkToMarkdown(obj) {
|
||||
rule: '-',
|
||||
};
|
||||
|
||||
const processor = unified()
|
||||
.use({ settings: remarkToMarkdownPluginOpts })
|
||||
.use(remarkEscapeMarkdownEntities)
|
||||
.use(remarkStripTrailingBreaks)
|
||||
.use(remarkToMarkdownPlugin)
|
||||
.use(remarkAllowAllText)
|
||||
.use(createRemarkShortcodeStringifier({ plugins: getEditorComponents() }))
|
||||
.use(remarkPlugins);
|
||||
|
||||
/**
|
||||
* Transform the MDAST with plugins.
|
||||
*/
|
||||
const processedMdast = unified()
|
||||
.use(remarkEscapeMarkdownEntities)
|
||||
.use(remarkStripTrailingBreaks)
|
||||
.runSync(mdast);
|
||||
const processedMdast = processor.runSync(mdast);
|
||||
|
||||
const markdown = unified()
|
||||
.use(remarkToMarkdownPlugin, remarkToMarkdownPluginOpts)
|
||||
.use(remarkAllowAllText)
|
||||
.use(createRemarkShortcodeStringifier({ plugins: getEditorComponents() }))
|
||||
.stringify(processedMdast)
|
||||
.replace(/\r?/g, '');
|
||||
/**
|
||||
* Serialize the MDAST to markdown.
|
||||
*/
|
||||
const markdown = processor.stringify(processedMdast).replace(/\r?/g, '');
|
||||
|
||||
/**
|
||||
* Return markdown with trailing whitespace removed.
|
||||
@ -147,8 +154,8 @@ export function remarkToMarkdown(obj) {
|
||||
/**
|
||||
* Convert Markdown to HTML.
|
||||
*/
|
||||
export function markdownToHtml(markdown, { getAsset, resolveWidget } = {}) {
|
||||
const mdast = markdownToRemark(markdown);
|
||||
export function markdownToHtml(markdown, { getAsset, resolveWidget, remarkPlugins = [] } = {}) {
|
||||
const mdast = markdownToRemark(markdown, remarkPlugins);
|
||||
|
||||
const hast = unified()
|
||||
.use(remarkToRehypeShortcodes, { plugins: getEditorComponents(), getAsset, resolveWidget })
|
||||
@ -192,8 +199,8 @@ export function htmlToSlate(html) {
|
||||
/**
|
||||
* Convert Markdown to Slate's Raw AST.
|
||||
*/
|
||||
export function markdownToSlate(markdown, { voidCodeBlock } = {}) {
|
||||
const mdast = markdownToRemark(markdown);
|
||||
export function markdownToSlate(markdown, { voidCodeBlock, remarkPlugins = [] } = {}) {
|
||||
const mdast = markdownToRemark(markdown, remarkPlugins);
|
||||
|
||||
const slateRaw = unified()
|
||||
.use(remarkWrapHtml)
|
||||
@ -212,8 +219,8 @@ export function markdownToSlate(markdown, { voidCodeBlock } = {}) {
|
||||
* MDAST. The conversion is manual because Unified can only operate on Unist
|
||||
* trees.
|
||||
*/
|
||||
export function slateToMarkdown(raw, { voidCodeBlock } = {}) {
|
||||
export function slateToMarkdown(raw, { voidCodeBlock, remarkPlugins = [] } = {}) {
|
||||
const mdast = slateToRemark(raw, { voidCodeBlock });
|
||||
const markdown = remarkToMarkdown(mdast);
|
||||
const markdown = remarkToMarkdown(mdast, remarkPlugins);
|
||||
return markdown;
|
||||
}
|
||||
|
Reference in New Issue
Block a user