fix: blockquote newline (#894)
This commit is contained in:
parent
d1cf12ec84
commit
69d11e831e
@ -1,7 +1,10 @@
|
||||
import { isNotNullish, isNullish } from './null.util';
|
||||
|
||||
export function isEmpty(value: string | null | undefined): value is null | undefined {
|
||||
return isNullish(value) || value === '';
|
||||
export function isEmpty(
|
||||
value: string | null | undefined,
|
||||
ignoreWhitespace?: boolean,
|
||||
): value is null | undefined {
|
||||
return isNullish(value) || (ignoreWhitespace ? value.trim() === '' : value === '');
|
||||
}
|
||||
|
||||
export function isNotEmpty(value: string | null | undefined): value is string {
|
||||
|
@ -2,5 +2,6 @@
|
||||
@apply border-l-2
|
||||
border-gray-400
|
||||
ml-2
|
||||
pl-2;
|
||||
pl-2
|
||||
my-2;
|
||||
}
|
||||
|
@ -6,7 +6,6 @@ import type { MdPlatePlugin } from '@staticcms/markdown';
|
||||
const softBreakPlugin: Partial<MdPlatePlugin<SoftBreakPlugin>> = {
|
||||
options: {
|
||||
rules: [
|
||||
{ hotkey: 'shift+enter' },
|
||||
{
|
||||
hotkey: 'enter',
|
||||
query: {
|
||||
|
@ -108,7 +108,9 @@ function serializeMarkdownNode(
|
||||
childrenHasLink = chunk.children.some(f => !isLeafNode(f) && f.type === NodeTypes.link);
|
||||
}
|
||||
|
||||
return serializeMarkdownNode(
|
||||
return {
|
||||
type: 'type' in c ? c.type : undefined,
|
||||
response: serializeMarkdownNode(
|
||||
{ ...c, parentType: type },
|
||||
{
|
||||
// WOAH.
|
||||
@ -132,9 +134,34 @@ function serializeMarkdownNode(
|
||||
index: childIndex,
|
||||
shortcodeConfigs,
|
||||
},
|
||||
);
|
||||
),
|
||||
};
|
||||
})
|
||||
.join(separator);
|
||||
.map(({ response, type }) => {
|
||||
if (selfIsBlockquote) {
|
||||
let serializedChild = response;
|
||||
|
||||
if (listDepth === 0) {
|
||||
serializedChild = serializedChild.replace(
|
||||
/(?<!(?:[ ]*(?:-|1.) [^\n]*)|[\n])[\n]{1}([^\n])/g,
|
||||
' \n$1',
|
||||
);
|
||||
}
|
||||
|
||||
return { response: serializedChild, type };
|
||||
}
|
||||
|
||||
return { response, type };
|
||||
})
|
||||
.reduce((acc, { response, type }, index) => {
|
||||
if (selfIsBlockquote) {
|
||||
if (type === NodeTypes.block_quote) {
|
||||
return index === 0 ? response : `${acc}${separator}\n${response}`;
|
||||
}
|
||||
}
|
||||
|
||||
return index === 0 ? response : `${acc}${separator}${response}`;
|
||||
}, '');
|
||||
}
|
||||
|
||||
// This is pretty fragile code, check the long comment where we iterate over children
|
||||
@ -152,7 +179,7 @@ function serializeMarkdownNode(
|
||||
}
|
||||
|
||||
if (children === '' && !VOID_ELEMENTS.find(k => NodeTypes[k] === type)) {
|
||||
return;
|
||||
return '\n';
|
||||
}
|
||||
|
||||
// Never allow decorating break tags with rich text formatting,
|
||||
@ -237,10 +264,11 @@ function serializeMarkdownNode(
|
||||
return `###### ${handleInBlockNewline(children)}\n`;
|
||||
|
||||
case NodeTypes.block_quote:
|
||||
return `${selfIsBlockquote && blockquoteDepth > 0 ? '\n' : ''}> ${children
|
||||
.replace(/^[\n]*|[\n]*$/gm, '')
|
||||
return `> ${children
|
||||
.replace(/[\n]+$/g, '')
|
||||
.split('\n')
|
||||
.join('\n> ')}\n`;
|
||||
.join('\n> ')
|
||||
.replace(/\n>[ \t]*\n/g, '\n>\n')}${selfIsBlockquote && blockquoteDepth === 0 ? '\n' : ''}`;
|
||||
|
||||
case NodeTypes.code_block:
|
||||
const codeBlock = chunk as MdCodeBlockElement;
|
||||
@ -319,7 +347,9 @@ ${bodyRows.join('\n')}`;
|
||||
|
||||
case NodeTypes.tableHeaderCell:
|
||||
case NodeTypes.tableCell:
|
||||
return isEmpty(children) ? ' ' : children.replace(/\|/g, '\\|').replace(/\n/g, BREAK_TAG);
|
||||
return isEmpty(children, true)
|
||||
? ' '
|
||||
: children.replace(/\|/g, '\\|').replace(/\n/g, BREAK_TAG);
|
||||
|
||||
case NodeTypes.shortcode:
|
||||
const shortcodeNode = chunk as MdShortcodeElement;
|
||||
|
@ -92,6 +92,8 @@ export interface Options {
|
||||
isInTable?: boolean;
|
||||
isInLink?: boolean;
|
||||
isInTableHeaderRow?: boolean;
|
||||
isInBlockquote?: boolean;
|
||||
isInList?: boolean;
|
||||
tableAlign?: (string | null)[];
|
||||
useMdx: boolean;
|
||||
shortcodeConfigs: Record<string, ShortcodeConfig>;
|
||||
@ -105,6 +107,8 @@ export default function deserializeMarkdown(node: MdastNode, options: Options) {
|
||||
isInTable = false,
|
||||
isInLink = false,
|
||||
isInTableHeaderRow = false,
|
||||
isInBlockquote = false,
|
||||
isInList = false,
|
||||
tableAlign,
|
||||
useMdx,
|
||||
shortcodeConfigs,
|
||||
@ -114,6 +118,8 @@ export default function deserializeMarkdown(node: MdastNode, options: Options) {
|
||||
const selfIsTable = node.type === 'table';
|
||||
const selfIsLink = node.type === 'link';
|
||||
const selfIsTableHeaderRow = node.type === 'tableRow' && index === 0;
|
||||
const selfIsBlockquote = node.type === 'blockquote';
|
||||
const selfIsList = node.type === 'list';
|
||||
|
||||
const nodeChildren = node.children;
|
||||
if (nodeChildren && Array.isArray(nodeChildren) && nodeChildren.length > 0) {
|
||||
@ -128,6 +134,8 @@ export default function deserializeMarkdown(node: MdastNode, options: Options) {
|
||||
isInTable: selfIsTable || isInTable,
|
||||
isInLink: selfIsLink || isInLink,
|
||||
isInTableHeaderRow: selfIsTableHeaderRow || isInTableHeaderRow,
|
||||
isInBlockquote: selfIsBlockquote || isInBlockquote,
|
||||
isInList: selfIsList || isInList,
|
||||
useMdx,
|
||||
shortcodeConfigs,
|
||||
index: childIndex,
|
||||
@ -181,7 +189,25 @@ export default function deserializeMarkdown(node: MdastNode, options: Options) {
|
||||
} as ListItemNode;
|
||||
|
||||
case 'paragraph':
|
||||
if ('ordered' in node) {
|
||||
if (isInBlockquote || isInList) {
|
||||
if (isInBlockquote && index > 0) {
|
||||
if (children.length > 0) {
|
||||
let firstChild = children[0];
|
||||
if ('text' in firstChild) {
|
||||
firstChild = { text: `\n\n${firstChild.text}` };
|
||||
}
|
||||
|
||||
if (children.length > 1) {
|
||||
const [_, ...rest] = children;
|
||||
return [firstChild, ...rest];
|
||||
}
|
||||
|
||||
return [firstChild];
|
||||
}
|
||||
|
||||
return children;
|
||||
}
|
||||
|
||||
return children;
|
||||
}
|
||||
|
||||
@ -212,7 +238,20 @@ export default function deserializeMarkdown(node: MdastNode, options: Options) {
|
||||
} as ImageNode;
|
||||
|
||||
case 'blockquote':
|
||||
return { type: NodeTypes.block_quote, children } as BlockQuoteNode;
|
||||
const blockquoteChildren = children.reduce((acc, n) => {
|
||||
const lastNode = acc.length > 0 ? acc[acc.length - 1] : null;
|
||||
if (lastNode && 'text' in lastNode && lastNode.text && 'text' in n && n.text) {
|
||||
acc[acc.length - 1] = {
|
||||
text: `${lastNode.text}${n.text}`,
|
||||
};
|
||||
} else {
|
||||
acc.push(n);
|
||||
}
|
||||
|
||||
return acc;
|
||||
}, [] as DeserializedNode[]);
|
||||
|
||||
return { type: NodeTypes.block_quote, children: blockquoteChildren } as BlockQuoteNode;
|
||||
|
||||
case 'code':
|
||||
return {
|
||||
@ -230,7 +269,7 @@ export default function deserializeMarkdown(node: MdastNode, options: Options) {
|
||||
children: [{ text: node.value?.replace(/<br>/g, '') || '' }],
|
||||
} as ParagraphNode;
|
||||
}
|
||||
return { type: 'p', children: [{ text: node.value || '' }] };
|
||||
return { type: 'p', children: [{ text: node.value ?? '' }] };
|
||||
|
||||
case 'emphasis':
|
||||
return {
|
||||
@ -285,7 +324,7 @@ export default function deserializeMarkdown(node: MdastNode, options: Options) {
|
||||
}
|
||||
}
|
||||
|
||||
return { text: node.value || '' };
|
||||
return { text: node.value ?? '' };
|
||||
|
||||
case 'mdxJsxTextElement':
|
||||
if ('name' in node && node.type === 'mdxJsxTextElement') {
|
||||
@ -344,26 +383,37 @@ export default function deserializeMarkdown(node: MdastNode, options: Options) {
|
||||
}
|
||||
}
|
||||
|
||||
return { text: node.value || '' };
|
||||
return { text: node.value ?? '' };
|
||||
|
||||
case 'break':
|
||||
return { text: '\n' };
|
||||
|
||||
case 'text':
|
||||
if (useMdx) {
|
||||
return { text: node.value || '' };
|
||||
return { text: (node.value ?? '').replace(/(?<![\n]|[ ]{2})[\n]{1}([^\n])/g, ' $1') };
|
||||
}
|
||||
|
||||
if (!node.value) {
|
||||
return { text: '' };
|
||||
}
|
||||
|
||||
const nodes = autoLinkToSlate(
|
||||
let nodes = autoLinkToSlate(
|
||||
processShortcodeConfigsToSlate(shortcodeConfigs, [node]),
|
||||
isInLink,
|
||||
);
|
||||
|
||||
return nodes.map(node => (node.type === 'text' ? { text: node.value ?? '' } : node));
|
||||
nodes = nodes.map(n => {
|
||||
if (n.type !== 'text') {
|
||||
return n;
|
||||
}
|
||||
|
||||
return { text: (n.value ?? '').replace(/(?<![\n]|[ ]{2})[\n]{1}([^\n])/g, ' $1') };
|
||||
});
|
||||
|
||||
return nodes;
|
||||
|
||||
default:
|
||||
console.warn('[StaticCMS] Unrecognized mdast node, proceeding as text', node);
|
||||
return { text: node.value || '' };
|
||||
return { text: node.value ?? '' };
|
||||
}
|
||||
}
|
||||
|
@ -75,14 +75,13 @@ const serializationTestData: SerializationTests = {
|
||||
},
|
||||
|
||||
'paragraph with line break': {
|
||||
markdown: `A line of text
|
||||
With another in the same paragraph`,
|
||||
markdown: `A line of text with another in the same paragraph`,
|
||||
slate: [
|
||||
{
|
||||
type: ELEMENT_PARAGRAPH,
|
||||
children: [
|
||||
{
|
||||
text: 'A line of text\nWith another in the same paragraph',
|
||||
text: 'A line of text with another in the same paragraph',
|
||||
},
|
||||
],
|
||||
},
|
||||
@ -234,20 +233,6 @@ And a completely new paragraph`,
|
||||
],
|
||||
},
|
||||
|
||||
'multiline blockquote': {
|
||||
markdown: '> I am a block quote\n> And another line',
|
||||
slate: [
|
||||
{
|
||||
type: ELEMENT_BLOCKQUOTE,
|
||||
children: [
|
||||
{
|
||||
text: 'I am a block quote\nAnd another line',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
'nested blockquote': {
|
||||
markdown: '> I am a block quote\n> > And another line',
|
||||
slate: [
|
||||
@ -269,6 +254,116 @@ And a completely new paragraph`,
|
||||
},
|
||||
] as MdValue,
|
||||
},
|
||||
|
||||
'multiline blockquote (double space and carrot each line)': {
|
||||
markdown: '> One line \n> Another line',
|
||||
slate: [
|
||||
{
|
||||
type: ELEMENT_BLOCKQUOTE,
|
||||
children: [
|
||||
{
|
||||
text: 'One line\nAnother line',
|
||||
},
|
||||
],
|
||||
},
|
||||
] as MdValue,
|
||||
},
|
||||
|
||||
'multiline blockquote (empty line carrot)': {
|
||||
markdown: '> One line\n>\n> Another line',
|
||||
slate: [
|
||||
{
|
||||
type: ELEMENT_BLOCKQUOTE,
|
||||
children: [
|
||||
{
|
||||
text: 'One line\n\nAnother line',
|
||||
},
|
||||
],
|
||||
},
|
||||
] as MdValue,
|
||||
},
|
||||
|
||||
'sequential blockquote': {
|
||||
markdown: `> I am a block quote
|
||||
|
||||
> And another block quote`,
|
||||
slate: [
|
||||
{
|
||||
type: ELEMENT_BLOCKQUOTE,
|
||||
children: [
|
||||
{
|
||||
text: 'I am a block quote',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
type: ELEMENT_BLOCKQUOTE,
|
||||
children: [
|
||||
{
|
||||
text: 'And another block quote',
|
||||
},
|
||||
],
|
||||
},
|
||||
] as MdValue,
|
||||
},
|
||||
|
||||
'blockquote with link': {
|
||||
// First line has double space
|
||||
markdown: `> I am a [block quote](https://example.com/). Another line
|
||||
>
|
||||
> Final Line`,
|
||||
slate: [
|
||||
{
|
||||
type: ELEMENT_BLOCKQUOTE,
|
||||
children: [
|
||||
{
|
||||
text: 'I am a ',
|
||||
},
|
||||
{
|
||||
type: ELEMENT_LINK,
|
||||
url: 'https://example.com/',
|
||||
children: [
|
||||
{
|
||||
text: 'block quote',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: '. Another line\n\nFinal Line',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
'blockquote with link (no punctuation)': {
|
||||
// First line has double space
|
||||
markdown: `> I am a [block quote](https://example.com/) and another line
|
||||
>
|
||||
> Final Line`,
|
||||
slate: [
|
||||
{
|
||||
type: ELEMENT_BLOCKQUOTE,
|
||||
children: [
|
||||
{
|
||||
text: 'I am a ',
|
||||
},
|
||||
{
|
||||
type: ELEMENT_LINK,
|
||||
url: 'https://example.com/',
|
||||
children: [
|
||||
{
|
||||
text: 'block quote',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: ' and another line\n\nFinal Line',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@ -1543,8 +1638,7 @@ label: 'Blog post content',
|
||||
widget: 'markdown',
|
||||
\`\`\`
|
||||
|
||||
> See the table below for default options
|
||||
> More API information can be found in the document
|
||||
> See the table below for default options \n> More API information can be found in the document
|
||||
|
||||
|Name|Type|Default|Description|
|
||||
|---|---|---|---|
|
||||
@ -2683,8 +2777,86 @@ Text ahead [youtube|p6h-rYSVX90] and behind and another {{< twitter 917359331535
|
||||
};
|
||||
|
||||
export const deserializationOnlyTestData: SerializationTests = {
|
||||
blcokquote: {
|
||||
both: {
|
||||
'blockquote with link': {
|
||||
// First line has double space
|
||||
markdown: `> I am a [block quote](https://example.com/).
|
||||
> Another line
|
||||
>
|
||||
> Final Line`,
|
||||
slate: [
|
||||
{
|
||||
type: ELEMENT_BLOCKQUOTE,
|
||||
children: [
|
||||
{
|
||||
text: 'I am a ',
|
||||
},
|
||||
{
|
||||
type: ELEMENT_LINK,
|
||||
url: 'https://example.com/',
|
||||
children: [
|
||||
{
|
||||
text: 'block quote',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
text: '. Another line\n\nFinal Line',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
'multiline blockquote (carrot each line)': {
|
||||
markdown: `> One line
|
||||
> Another line`,
|
||||
slate: [
|
||||
{
|
||||
type: ELEMENT_BLOCKQUOTE,
|
||||
children: [
|
||||
{
|
||||
text: 'One line Another line',
|
||||
},
|
||||
],
|
||||
},
|
||||
] as MdValue,
|
||||
},
|
||||
|
||||
'multiline blockquote (double space)': {
|
||||
markdown: '> One line \nAnother line',
|
||||
slate: [
|
||||
{
|
||||
type: ELEMENT_BLOCKQUOTE,
|
||||
children: [
|
||||
{
|
||||
text: 'One line\nAnother line',
|
||||
},
|
||||
],
|
||||
},
|
||||
] as MdValue,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
paragraph: {
|
||||
markdown: {
|
||||
'paragraph with line break': {
|
||||
markdown: `A line of text
|
||||
With another in the same paragraph`,
|
||||
slate: [
|
||||
{
|
||||
type: ELEMENT_PARAGRAPH,
|
||||
children: [
|
||||
{
|
||||
text: 'A line of text With another in the same paragraph',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
'paragraph with link': {
|
||||
markdown:
|
||||
'A line of text with a link https://www.youtube.com/watch?v=p6h-rYSVX90 and some more text',
|
||||
|
Loading…
x
Reference in New Issue
Block a user