fix(richtext-lexical): ensure markdown normalization does not merge escaped code block lines (#10230)
This commit is contained in:
@@ -18,8 +18,10 @@ const UNORDERED_LIST_REGEX = /^(\s*)[-*+]\s/
|
||||
const CHECK_LIST_REGEX = /^(\s*)(?:-\s)?\s?(\[(\s|x)?\])\s/i
|
||||
const HEADING_REGEX = /^(#{1,6})\s/
|
||||
const QUOTE_REGEX = /^>\s/
|
||||
const CODE_START_REGEX = /^[ \t]*```(\w+)?/
|
||||
const CODE_END_REGEX = /[ \t]*```$/
|
||||
// Match start of ``` or escaped \`\`\` code blocks
|
||||
const CODE_START_REGEX = /^[ \t]*(\\`\\`\\`|```)(\w+)?/
|
||||
// Match end of ``` or escaped \`\`\` code blocks
|
||||
const CODE_END_REGEX = /[ \t]*(\\`\\`\\`|```)$/
|
||||
const CODE_SINGLE_LINE_REGEX = /^[ \t]*```[^`]+(?:(?:`{1,2}|`{4,})[^`]+)*```(?:[^`]|$)/
|
||||
const TABLE_ROW_REG_EXP = /^\|(.+)\|\s?$/
|
||||
const TABLE_ROW_DIVIDER_REG_EXP = /^(\| ?:?-*:? ?)+\|\s?$/
|
||||
@@ -41,15 +43,15 @@ export function normalizeMarkdown(input: string, shouldMergeAdjacentLines = fals
|
||||
continue
|
||||
}
|
||||
|
||||
if (
|
||||
(CODE_START_REGEX.test(line) && !inCodeBlock) ||
|
||||
(CODE_END_REGEX.test(line) && inCodeBlock)
|
||||
) {
|
||||
inCodeBlock = !inCodeBlock
|
||||
// Toggle inCodeBlock state when encountering start or end of a code block
|
||||
if (CODE_START_REGEX.test(line)) {
|
||||
inCodeBlock = true
|
||||
sanitizedLines.push(line)
|
||||
continue
|
||||
}
|
||||
|
||||
// Detect the start or end of a code block
|
||||
if (CODE_START_REGEX.test(line) || CODE_END_REGEX.test(line)) {
|
||||
if (CODE_END_REGEX.test(line)) {
|
||||
inCodeBlock = false
|
||||
sanitizedLines.push(line)
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ import { InlineCodeBlock } from '../../mdx/jsxBlocks/inlineCode.js'
|
||||
import { PackageInstallOptions } from '../../mdx/jsxBlocks/packageInstallOptions.js'
|
||||
import { TextContainerBlock } from '../../mdx/jsxBlocks/TextContainer.js'
|
||||
import { TextContainerNoTrimBlock } from '../../mdx/jsxBlocks/TextContainerNoTrim.js'
|
||||
import { RestExamplesBlock } from '../../mdx/jsxBlocks/restExamples/index.js'
|
||||
|
||||
export const postsSlug = 'posts'
|
||||
|
||||
@@ -72,6 +73,7 @@ export const PostsCollection: CollectionConfig = {
|
||||
PackageInstallOptions,
|
||||
TextContainerNoTrimBlock,
|
||||
TextContainerBlock,
|
||||
RestExamplesBlock,
|
||||
],
|
||||
inlineBlocks: [InlineCodeBlock],
|
||||
}),
|
||||
@@ -94,6 +96,7 @@ export const PostsCollection: CollectionConfig = {
|
||||
PackageInstallOptions,
|
||||
TextContainerNoTrimBlock,
|
||||
TextContainerBlock,
|
||||
RestExamplesBlock,
|
||||
],
|
||||
inlineBlocks: [InlineCodeBlock],
|
||||
}),
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
88
test/lexical-mdx/mdx/jsxBlocks/restExamples/index.ts
Normal file
88
test/lexical-mdx/mdx/jsxBlocks/restExamples/index.ts
Normal file
@@ -0,0 +1,88 @@
|
||||
import type { Block } from 'payload'
|
||||
|
||||
export const RestExamplesBlock: Block = {
|
||||
slug: 'restExamples',
|
||||
fields: [
|
||||
{
|
||||
name: 'data',
|
||||
type: 'array',
|
||||
fields: [
|
||||
{
|
||||
name: 'operation',
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
name: 'method',
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
name: 'path',
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
name: 'description',
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
name: 'example',
|
||||
type: 'group',
|
||||
fields: [
|
||||
{
|
||||
name: 'slug',
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
name: 'req',
|
||||
type: 'json',
|
||||
},
|
||||
{
|
||||
name: 'res',
|
||||
type: 'json',
|
||||
},
|
||||
{
|
||||
name: 'drawerContent',
|
||||
type: 'textarea',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
interfaceName: 'RestExamplesBlock',
|
||||
jsx: {
|
||||
export: ({ fields, lexicalToMarkdown }) => {
|
||||
return {
|
||||
props: {
|
||||
data: fields.data.map((item) => {
|
||||
return {
|
||||
...item,
|
||||
example: {
|
||||
...item.example,
|
||||
drawerContent:
|
||||
lexicalToMarkdown && item.example.drawerContent
|
||||
? item.example.drawerContent
|
||||
: undefined,
|
||||
},
|
||||
}
|
||||
}),
|
||||
},
|
||||
}
|
||||
},
|
||||
import: ({ children, markdownToLexical, props }) => {
|
||||
return {
|
||||
data: props.data.map((item) => {
|
||||
return {
|
||||
...item,
|
||||
example: {
|
||||
...item.example,
|
||||
drawerContent:
|
||||
markdownToLexical && item.example.drawerContent
|
||||
? item.example.drawerContent
|
||||
: undefined,
|
||||
},
|
||||
}
|
||||
}),
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
47
test/lexical-mdx/result.json
Normal file
47
test/lexical-mdx/result.json
Normal file
@@ -0,0 +1,47 @@
|
||||
{
|
||||
"editorState": {
|
||||
"root": {
|
||||
"children": [
|
||||
{
|
||||
"format": "",
|
||||
"type": "block",
|
||||
"version": 2,
|
||||
"fields": {
|
||||
"blockType": "restExamples",
|
||||
"data": [
|
||||
{
|
||||
"operation": "Find",
|
||||
"method": "GET",
|
||||
"path": "/api/{collection-slug}",
|
||||
"description": "Find paginated documents",
|
||||
"example": {
|
||||
"slug": "getCollection",
|
||||
"req": true,
|
||||
"res": {
|
||||
"paginated": true,
|
||||
"data": {
|
||||
"id": "644a5c24cc1383022535fc7c",
|
||||
"title": "Home",
|
||||
"content": "REST API examples",
|
||||
"slug": "home",
|
||||
"createdAt": "2023-04-27T11:27:32.419Z",
|
||||
"updatedAt": "2023-04-27T11:27:32.419Z"
|
||||
}
|
||||
},
|
||||
"drawerContent": "\n#### Heading\n\nOne `two` three\n\n- [sort](/docs/queries/overview#sort) - sort by field\n- [where](/docs/queries/overview) - pass a where query to constrain returned documents\n\n```ts\nconst a = 1\nconst b = 2\nconst c = 3\nconst d = 4\n```\n"
|
||||
}
|
||||
}
|
||||
],
|
||||
"id": "6771f28d009a850b8401645e"
|
||||
}
|
||||
}
|
||||
],
|
||||
"direction": null,
|
||||
"format": "",
|
||||
"indent": 0,
|
||||
"type": "root",
|
||||
"version": 1
|
||||
}
|
||||
},
|
||||
"frontMatter": []
|
||||
}
|
||||
1372
test/lexical-mdx/tests/default.test.ts
Normal file
1372
test/lexical-mdx/tests/default.test.ts
Normal file
File diff suppressed because it is too large
Load Diff
54
test/lexical-mdx/tests/restExamples.input.mdx
Normal file
54
test/lexical-mdx/tests/restExamples.input.mdx
Normal file
@@ -0,0 +1,54 @@
|
||||
#### Heading
|
||||
|
||||
One `two` three
|
||||
|
||||
- [sort](/docs/queries/overview#sort) - sort by field
|
||||
- [where](/docs/queries/overview) - pass a where query to constrain returned documents
|
||||
|
||||
```ts
|
||||
const a = 1
|
||||
const b = 2
|
||||
const c = 3
|
||||
const d = 4
|
||||
```
|
||||
|
||||
<RestExamples
|
||||
data={[
|
||||
{
|
||||
operation: "Find",
|
||||
method: "GET",
|
||||
path: "/api/{collection-slug}",
|
||||
description: "Find paginated documents",
|
||||
example: {
|
||||
slug: "getCollection",
|
||||
req: true,
|
||||
res: {
|
||||
paginated: true,
|
||||
data: {
|
||||
id: "644a5c24cc1383022535fc7c",
|
||||
title: "Home",
|
||||
content: "REST API examples",
|
||||
slug: "home",
|
||||
createdAt: "2023-04-27T11:27:32.419Z",
|
||||
updatedAt: "2023-04-27T11:27:32.419Z",
|
||||
},
|
||||
},
|
||||
drawerContent: `
|
||||
#### Heading
|
||||
|
||||
One \`two\` three
|
||||
|
||||
- [sort](/docs/queries/overview#sort) - sort by field
|
||||
- [where](/docs/queries/overview) - pass a where query to constrain returned documents
|
||||
|
||||
\`\`\`ts
|
||||
const a = 1
|
||||
const b = 2
|
||||
const c = 3
|
||||
const d = 4
|
||||
\`\`\`
|
||||
`
|
||||
},
|
||||
}
|
||||
]}
|
||||
/>
|
||||
215
test/lexical-mdx/tests/restExamples.output.json
Normal file
215
test/lexical-mdx/tests/restExamples.output.json
Normal file
@@ -0,0 +1,215 @@
|
||||
{
|
||||
"editorState": {
|
||||
"root": {
|
||||
"children": [
|
||||
{
|
||||
"children": [
|
||||
{
|
||||
"detail": 0,
|
||||
"format": 0,
|
||||
"mode": "normal",
|
||||
"style": "",
|
||||
"text": "Heading",
|
||||
"type": "text",
|
||||
"version": 1
|
||||
}
|
||||
],
|
||||
"direction": null,
|
||||
"format": "",
|
||||
"indent": 0,
|
||||
"type": "heading",
|
||||
"version": 1,
|
||||
"tag": "h4"
|
||||
},
|
||||
{
|
||||
"children": [
|
||||
{
|
||||
"detail": 0,
|
||||
"format": 0,
|
||||
"mode": "normal",
|
||||
"style": "",
|
||||
"text": "One ",
|
||||
"type": "text",
|
||||
"version": 1
|
||||
},
|
||||
{
|
||||
"detail": 0,
|
||||
"format": 16,
|
||||
"mode": "normal",
|
||||
"style": "",
|
||||
"text": "two",
|
||||
"type": "text",
|
||||
"version": 1
|
||||
},
|
||||
{
|
||||
"detail": 0,
|
||||
"format": 0,
|
||||
"mode": "normal",
|
||||
"style": "",
|
||||
"text": " three",
|
||||
"type": "text",
|
||||
"version": 1
|
||||
}
|
||||
],
|
||||
"direction": null,
|
||||
"format": "",
|
||||
"indent": 0,
|
||||
"type": "paragraph",
|
||||
"version": 1,
|
||||
"textFormat": 0,
|
||||
"textStyle": ""
|
||||
},
|
||||
{
|
||||
"children": [
|
||||
{
|
||||
"children": [
|
||||
{
|
||||
"children": [
|
||||
{
|
||||
"detail": 0,
|
||||
"format": 0,
|
||||
"mode": "normal",
|
||||
"style": "",
|
||||
"text": "sort",
|
||||
"type": "text",
|
||||
"version": 1
|
||||
}
|
||||
],
|
||||
"direction": null,
|
||||
"format": "",
|
||||
"indent": 0,
|
||||
"type": "link",
|
||||
"version": 3,
|
||||
"fields": {
|
||||
"doc": null,
|
||||
"linkType": "custom",
|
||||
"newTab": false,
|
||||
"url": "/docs/queries/overview#sort"
|
||||
},
|
||||
"id": "67708768dd53bf162cb1ae6a"
|
||||
},
|
||||
{
|
||||
"detail": 0,
|
||||
"format": 0,
|
||||
"mode": "normal",
|
||||
"style": "",
|
||||
"text": " - sort by field",
|
||||
"type": "text",
|
||||
"version": 1
|
||||
}
|
||||
],
|
||||
"direction": null,
|
||||
"format": "",
|
||||
"indent": 0,
|
||||
"type": "listitem",
|
||||
"version": 1,
|
||||
"value": 1
|
||||
},
|
||||
{
|
||||
"children": [
|
||||
{
|
||||
"children": [
|
||||
{
|
||||
"detail": 0,
|
||||
"format": 0,
|
||||
"mode": "normal",
|
||||
"style": "",
|
||||
"text": "where",
|
||||
"type": "text",
|
||||
"version": 1
|
||||
}
|
||||
],
|
||||
"direction": null,
|
||||
"format": "",
|
||||
"indent": 0,
|
||||
"type": "link",
|
||||
"version": 3,
|
||||
"fields": {
|
||||
"doc": null,
|
||||
"linkType": "custom",
|
||||
"newTab": false,
|
||||
"url": "/docs/queries/overview"
|
||||
},
|
||||
"id": "67708768dd53bf162cb1ae6b"
|
||||
},
|
||||
{
|
||||
"detail": 0,
|
||||
"format": 0,
|
||||
"mode": "normal",
|
||||
"style": "",
|
||||
"text": " - pass a where query to constrain returned documents",
|
||||
"type": "text",
|
||||
"version": 1
|
||||
}
|
||||
],
|
||||
"direction": null,
|
||||
"format": "",
|
||||
"indent": 0,
|
||||
"type": "listitem",
|
||||
"version": 1,
|
||||
"value": 2
|
||||
}
|
||||
],
|
||||
"direction": null,
|
||||
"format": "",
|
||||
"indent": 0,
|
||||
"type": "list",
|
||||
"version": 1,
|
||||
"listType": "bullet",
|
||||
"start": 1,
|
||||
"tag": "ul"
|
||||
},
|
||||
{
|
||||
"format": "",
|
||||
"type": "block",
|
||||
"version": 2,
|
||||
"fields": {
|
||||
"blockType": "Code",
|
||||
"language": "ts",
|
||||
"code": "const a = 1\nconst b = 2\nconst c = 3\nconst d = 4",
|
||||
"id": "67708768dd53bf162cb1ae6c"
|
||||
}
|
||||
},
|
||||
{
|
||||
"format": "",
|
||||
"type": "block",
|
||||
"version": 2,
|
||||
"fields": {
|
||||
"blockType": "restExamples",
|
||||
"data": [
|
||||
{
|
||||
"operation": "Find",
|
||||
"method": "GET",
|
||||
"path": "/api/{collection-slug}",
|
||||
"description": "Find paginated documents",
|
||||
"example": {
|
||||
"slug": "getCollection",
|
||||
"req": true,
|
||||
"res": {
|
||||
"paginated": true,
|
||||
"data": {
|
||||
"id": "644a5c24cc1383022535fc7c",
|
||||
"title": "Home",
|
||||
"content": "REST API examples",
|
||||
"slug": "home",
|
||||
"createdAt": "2023-04-27T11:27:32.419Z",
|
||||
"updatedAt": "2023-04-27T11:27:32.419Z"
|
||||
}
|
||||
},
|
||||
"drawerContent": "\n#### Heading\n\nOne `two` three\n\n- [sort](/docs/queries/overview#sort) - sort by field\n- [where](/docs/queries/overview) - pass a where query to constrain returned documents\n\n```ts\nconst a = 1\nconst b = 2\nconst c = 3\nconst d = 4\n```\n"
|
||||
}
|
||||
}
|
||||
],
|
||||
"id": "67708768dd53bf162cb1ae6d"
|
||||
}
|
||||
}
|
||||
],
|
||||
"direction": null,
|
||||
"format": "",
|
||||
"indent": 0,
|
||||
"type": "root",
|
||||
"version": 1
|
||||
}
|
||||
},
|
||||
"frontMatter": []
|
||||
}
|
||||
14
test/lexical-mdx/tests/restExamples.test.ts
Normal file
14
test/lexical-mdx/tests/restExamples.test.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { readFileSync } from 'fs'
|
||||
import type { Test } from '../int.spec.js'
|
||||
import { fileURLToPath } from 'url'
|
||||
import path from 'path'
|
||||
|
||||
const filename = fileURLToPath(import.meta.url)
|
||||
const dirname = path.dirname(filename)
|
||||
|
||||
export const restExamplesTest1: Test = {
|
||||
input: readFileSync(path.resolve(dirname, 'restExamples.input.mdx'), 'utf-8'),
|
||||
rootChildren: JSON.parse(readFileSync(path.resolve(dirname, 'restExamples.output.json'), 'utf-8'))
|
||||
.editorState.root.children,
|
||||
convertFromEditorJSON: false,
|
||||
}
|
||||
40
test/lexical-mdx/tests/restExamples2.input.mdx
Normal file
40
test/lexical-mdx/tests/restExamples2.input.mdx
Normal file
@@ -0,0 +1,40 @@
|
||||
<RestExamples
|
||||
data={[
|
||||
{
|
||||
operation: "Find",
|
||||
method: "GET",
|
||||
path: "/api/{collection-slug}",
|
||||
description: "Find paginated documents",
|
||||
example: {
|
||||
slug: "getCollection",
|
||||
req: true,
|
||||
res: {
|
||||
paginated: true,
|
||||
data: {
|
||||
id: "644a5c24cc1383022535fc7c",
|
||||
title: "Home",
|
||||
content: "REST API examples",
|
||||
slug: "home",
|
||||
createdAt: "2023-04-27T11:27:32.419Z",
|
||||
updatedAt: "2023-04-27T11:27:32.419Z",
|
||||
},
|
||||
},
|
||||
drawerContent: `
|
||||
#### Heading
|
||||
|
||||
One \`two\` three
|
||||
|
||||
- [sort](/docs/queries/overview#sort) - sort by field
|
||||
- [where](/docs/queries/overview) - pass a where query to constrain returned documents
|
||||
|
||||
\`\`\`ts
|
||||
const a = 1
|
||||
const b = 2
|
||||
const c = 3
|
||||
const d = 4
|
||||
\`\`\`
|
||||
`
|
||||
},
|
||||
}
|
||||
]}
|
||||
/>
|
||||
47
test/lexical-mdx/tests/restExamples2.output.json
Normal file
47
test/lexical-mdx/tests/restExamples2.output.json
Normal file
@@ -0,0 +1,47 @@
|
||||
{
|
||||
"editorState": {
|
||||
"root": {
|
||||
"children": [
|
||||
{
|
||||
"format": "",
|
||||
"type": "block",
|
||||
"version": 2,
|
||||
"fields": {
|
||||
"blockType": "restExamples",
|
||||
"data": [
|
||||
{
|
||||
"operation": "Find",
|
||||
"method": "GET",
|
||||
"path": "/api/{collection-slug}",
|
||||
"description": "Find paginated documents",
|
||||
"example": {
|
||||
"slug": "getCollection",
|
||||
"req": true,
|
||||
"res": {
|
||||
"paginated": true,
|
||||
"data": {
|
||||
"id": "644a5c24cc1383022535fc7c",
|
||||
"title": "Home",
|
||||
"content": "REST API examples",
|
||||
"slug": "home",
|
||||
"createdAt": "2023-04-27T11:27:32.419Z",
|
||||
"updatedAt": "2023-04-27T11:27:32.419Z"
|
||||
}
|
||||
},
|
||||
"drawerContent": "\n#### Heading\n\nOne `two` three\n\n- [sort](/docs/queries/overview#sort) - sort by field\n- [where](/docs/queries/overview) - pass a where query to constrain returned documents\n\n```ts\nconst a = 1\nconst b = 2\nconst c = 3\nconst d = 4\n```\n"
|
||||
}
|
||||
}
|
||||
],
|
||||
"id": "67708768dd53bf162cb1ae6d"
|
||||
}
|
||||
}
|
||||
],
|
||||
"direction": null,
|
||||
"format": "",
|
||||
"indent": 0,
|
||||
"type": "root",
|
||||
"version": 1
|
||||
}
|
||||
},
|
||||
"frontMatter": []
|
||||
}
|
||||
15
test/lexical-mdx/tests/restExamples2.test.ts
Normal file
15
test/lexical-mdx/tests/restExamples2.test.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { readFileSync } from 'fs'
|
||||
import type { Test } from '../int.spec.js'
|
||||
import { fileURLToPath } from 'url'
|
||||
import path from 'path'
|
||||
|
||||
const filename = fileURLToPath(import.meta.url)
|
||||
const dirname = path.dirname(filename)
|
||||
|
||||
export const restExamplesTest2: Test = {
|
||||
input: readFileSync(path.resolve(dirname, 'restExamples2.input.mdx'), 'utf-8'),
|
||||
rootChildren: JSON.parse(
|
||||
readFileSync(path.resolve(dirname, 'restExamples2.output.json'), 'utf-8'),
|
||||
).editorState.root.children,
|
||||
convertFromEditorJSON: false,
|
||||
}
|
||||
Reference in New Issue
Block a user