feat: link in shortcode (#707)

This commit is contained in:
Daniel Lautzenheiser 2023-04-19 00:19:38 -04:00 committed by GitHub
parent 6be11c749e
commit 0fae2ce73d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 664 additions and 4682 deletions

View File

@ -51,8 +51,8 @@
"@babel/runtime": "7.21.0",
"@codemirror/autocomplete": "6.4.2",
"@codemirror/commands": "6.2.2",
"@codemirror/language": "6.6.0",
"@codemirror/language-data": "6.2.0",
"@codemirror/language": "6.6.0",
"@codemirror/legacy-modes": "6.3.2",
"@codemirror/lint": "6.2.0",
"@codemirror/search": "6.3.0",
@ -79,19 +79,19 @@
"@styled-icons/fa-brands": "10.47.0",
"@styled-icons/fluentui-system-regular": "10.47.0",
"@styled-icons/heroicons-outline": "10.47.0",
"@styled-icons/material": "10.47.0",
"@styled-icons/material-outlined": "10.47.0",
"@styled-icons/material-rounded": "10.47.0",
"@styled-icons/material": "10.47.0",
"@styled-icons/remix-editor": "10.46.0",
"@styled-icons/simple-icons": "10.46.0",
"@udecode/plate": "20.6.0",
"@udecode/plate-juice": "20.4.0",
"@udecode/plate-serializer-md": "20.4.1",
"@udecode/plate": "20.6.0",
"@uiw/codemirror-extensions-langs": "4.19.11",
"@uiw/react-codemirror": "4.19.11",
"ajv": "8.12.0",
"ajv-errors": "3.0.0",
"ajv-keywords": "5.1.0",
"ajv": "8.12.0",
"buffer": "6.0.3",
"clean-stack": "5.2.0",
"codemirror": "6.0.1",
@ -107,8 +107,8 @@
"fuzzy": "0.1.3",
"globby": "13.1.3",
"gotrue-js": "0.9.29",
"graphql": "16.6.0",
"graphql-tag": "2.12.6",
"graphql": "16.6.0",
"gray-matter": "4.0.3",
"history": "5.3.0",
"immer": "9.0.21",
@ -119,16 +119,24 @@
"jwt-decode": "3.1.2",
"localforage": "1.10.0",
"lodash": "4.17.21",
"mdast-util-gfm-footnote": "1.0.2",
"mdast-util-gfm-strikethrough": "1.0.3",
"mdast-util-gfm-table": "1.0.7",
"mdast-util-gfm-task-list-item": "1.0.2",
"micromark-extension-gfm-footnote": "1.1.0",
"micromark-extension-gfm-strikethrough": "1.0.5",
"micromark-extension-gfm-table": "1.0.5",
"micromark-extension-gfm-task-list-item": "1.0.4",
"micromark-util-combine-extensions": "1.0.0",
"minimatch": "8.0.3",
"moment": "2.29.4",
"node-polyglot": "2.5.0",
"ol": "7.3.0",
"path-browserify": "1.0.1",
"prop-types": "15.8.1",
"react": "18.2.0",
"react-color": "2.19.3",
"react-dnd": "16.0.1",
"react-dnd-html5-backend": "16.0.1",
"react-dnd": "16.0.1",
"react-dom": "18.2.0",
"react-frame-component": "5.2.6",
"react-is": "18.2.0",
@ -140,29 +148,29 @@
"react-virtualized-auto-sizer": "1.0.11",
"react-waypoint": "10.3.0",
"react-window": "1.8.8",
"remark-gfm": "3.0.1",
"react": "18.2.0",
"remark-html": "15.0.2",
"remark-mdx": "2.3.0",
"remark-parse": "10.0.1",
"sanitize-filename": "1.6.3",
"scheduler": "0.23.0",
"semaphore": "1.1.0",
"slate": "0.93.0",
"slate-history": "0.93.0",
"slate-hyperscript": "0.77.0",
"slate-react": "0.93.0",
"slate": "0.93.0",
"stream-browserify": "3.0.0",
"styled-components": "5.3.9",
"symbol-observable": "4.0.0",
"unified": "10.1.2",
"unist-util-visit": "4.1.2",
"url": "0.11.0",
"url-join": "5.0.0",
"url": "0.11.0",
"uuid": "9.0.0",
"validate-color": "2.2.4",
"vfile": "5.3.7",
"vfile-message": "3.1.4",
"vfile-statistics": "2.0.1",
"vfile": "5.3.7",
"what-input": "5.2.12",
"what-the-diff": "0.6.0",
"yaml": "2.2.1"
@ -197,13 +205,13 @@
"@types/jwt-decode": "2.2.1",
"@types/lodash": "4.14.191",
"@types/minimatch": "5.1.2",
"@types/node": "18.15.11",
"@types/node-fetch": "2.6.3",
"@types/react": "18.0.33",
"@types/node": "18.15.11",
"@types/react-color": "3.0.6",
"@types/react-dom": "18.0.11",
"@types/react-virtualized-auto-sizer": "1.0.1",
"@types/react-window": "1.8.5",
"@types/react": "18.0.33",
"@types/styled-components": "5.1.26",
"@types/url-join": "4.0.1",
"@types/uuid": "9.0.1",
@ -222,25 +230,25 @@
"babel-plugin-transform-export-extensions": "6.22.0",
"babel-plugin-transform-inline-environment-variables": "0.4.4",
"cache-me-outside": "1.0.0",
"commonmark": "0.30.0",
"commonmark-spec": "0.30.0",
"commonmark": "0.30.0",
"cross-env": "7.0.3",
"css-loader": "6.7.3",
"dotenv": "16.0.3",
"eslint": "8.37.0",
"eslint-import-resolver-typescript": "3.5.4",
"eslint-plugin-cypress": "2.13.2",
"eslint-plugin-import": "2.27.5",
"eslint-plugin-prettier": "4.2.1",
"eslint-plugin-react": "7.32.2",
"eslint-plugin-react-hooks": "4.6.0",
"eslint-plugin-react": "7.32.2",
"eslint-plugin-unicorn": "46.0.0",
"eslint": "8.37.0",
"execa": "7.1.1",
"fs-extra": "11.1.1",
"gitlab": "14.2.2",
"http-server": "14.1.1",
"jest": "29.5.0",
"jest-environment-jsdom": "29.5.0",
"jest": "29.5.0",
"js-yaml": "4.1.0",
"mini-css-extract-plugin": "2.7.5",
"mockserver-client": "5.15.0",
@ -248,8 +256,8 @@
"ncp": "2.0.0",
"node-fetch": "3.3.1",
"npm-run-all": "4.1.5",
"postcss": "8.4.21",
"postcss-loader": "7.2.4",
"postcss": "8.4.21",
"prettier": "2.8.7",
"process": "0.11.10",
"react-refresh": "0.14.0",
@ -263,9 +271,9 @@
"ts-jest": "29.1.0",
"tsconfig-paths-webpack-plugin": "4.0.1",
"typescript": "5.0.3",
"webpack": "5.77.0",
"webpack-cli": "5.0.1",
"webpack-dev-server": "4.13.2"
"webpack-dev-server": "4.13.2",
"webpack": "5.77.0"
},
"peerDependencies": {
"react": "^18.2.0",

View File

@ -9,7 +9,10 @@ import { markdownToSlate } from '../useMarkdownToSlate';
import type { SerializationTestData } from '../../tests-util/serializationTests.util';
import type { UseMarkdownToSlateOptions } from '../useMarkdownToSlate';
jest.unmock('remark-gfm');
jest.unmock('mdast-util-gfm-footnote');
jest.unmock('mdast-util-gfm-table');
jest.unmock('mdast-util-gfm-task-list-item');
jest.unmock('micromark-extension-gfm');
jest.unmock('remark-mdx');
jest.unmock('remark-parse');
jest.unmock('unified');

View File

@ -1,11 +1,11 @@
import { ELEMENT_PARAGRAPH } from '@udecode/plate';
import { useEffect, useState } from 'react';
import gfm from 'remark-gfm';
import mdx from 'remark-mdx';
import markdown from 'remark-parse';
import { unified } from 'unified';
import { getShortcodes } from '../../../../lib/registry';
import gfm from '../serialization/gfm';
import toSlatePlugin from '../serialization/slate/toSlatePlugin';
import type { ShortcodeConfig } from '../../../../interface';

View File

@ -0,0 +1,61 @@
import { gfmFootnoteFromMarkdown, gfmFootnoteToMarkdown } from 'mdast-util-gfm-footnote';
import {
gfmStrikethroughFromMarkdown,
gfmStrikethroughToMarkdown,
} from 'mdast-util-gfm-strikethrough';
import { gfmTableFromMarkdown, gfmTableToMarkdown } from 'mdast-util-gfm-table';
import {
gfmTaskListItemFromMarkdown,
gfmTaskListItemToMarkdown,
} from 'mdast-util-gfm-task-list-item';
import { gfmFootnote } from 'micromark-extension-gfm-footnote';
import { gfmStrikethrough } from 'micromark-extension-gfm-strikethrough';
import { gfmTable } from 'micromark-extension-gfm-table';
import { gfmTaskListItem } from 'micromark-extension-gfm-task-list-item';
import { combineExtensions } from 'micromark-util-combine-extensions';
import type { Root } from 'mdast';
import type { Plugin, Processor } from 'unified';
function gfmFromMarkdown() {
return [
gfmFootnoteFromMarkdown(),
gfmStrikethroughFromMarkdown,
gfmTableFromMarkdown,
gfmTaskListItemFromMarkdown,
];
}
function gfmToMarkdown() {
return {
extensions: [
gfmFootnoteToMarkdown(),
gfmStrikethroughToMarkdown,
gfmTableToMarkdown({}),
gfmTaskListItemToMarkdown,
],
};
}
function gfm() {
return combineExtensions([gfmFootnote(), gfmStrikethrough({}), gfmTable, gfmTaskListItem]);
}
/**
* Plugin to support GFM (footnotes, strikethrough, tables, tasklists).
*/
const remarkGfm: Plugin<void[], Root> = function (this: Processor) {
const data = this.data();
add('micromarkExtensions', gfm());
add('fromMarkdownExtensions', gfmFromMarkdown());
add('toMarkdownExtensions', gfmToMarkdown());
function add(field: string, value: unknown) {
const list = (data[field] ? data[field] : (data[field] = [])) as unknown[];
list.push(value);
}
};
export default remarkGfm;

View File

@ -0,0 +1,166 @@
import { autoLinkToSlate } from '../autoLinkUrls';
import type { MdastNode } from '../ast-types';
describe('processShortcodeConfig', () => {
describe('autoLinkToSlate', () => {
it('converts url to anchor node', () => {
const nodes: MdastNode[] = [
{ type: 'text', value: 'https://www.youtube.com/watch?v=p6h-rYSVX90' },
];
const slate: MdastNode[] = [
{
type: 'a',
url: 'https://www.youtube.com/watch?v=p6h-rYSVX90',
children: [{ text: 'https://www.youtube.com/watch?v=p6h-rYSVX90' }],
},
];
expect(autoLinkToSlate(nodes)).toEqual(slate);
});
it('does not convert url in shortcode node', () => {
const nodes: MdastNode[] = [
{
type: 'shortcode',
shortcode: 'youtube',
args: ['https://www.youtube.com/watch?v=p6h-rYSVX90'],
children: [{ text: '' }],
},
{ type: 'text', value: 'https://www.youtube.com/watch?v=p6h-rYSVX90' },
];
const slate: MdastNode[] = [
{
type: 'shortcode',
shortcode: 'youtube',
args: ['https://www.youtube.com/watch?v=p6h-rYSVX90'],
children: [{ text: '' }],
},
{
type: 'a',
url: 'https://www.youtube.com/watch?v=p6h-rYSVX90',
children: [{ text: 'https://www.youtube.com/watch?v=p6h-rYSVX90' }],
},
];
expect(autoLinkToSlate(nodes)).toEqual(slate);
});
it('converts url with text before', () => {
const nodes: MdastNode[] = [
{ type: 'text', value: 'Text before https://www.youtube.com/watch?v=p6h-rYSVX90' },
];
const slate: MdastNode[] = [
{
type: 'text',
value: 'Text before ',
},
{
type: 'a',
url: 'https://www.youtube.com/watch?v=p6h-rYSVX90',
children: [{ text: 'https://www.youtube.com/watch?v=p6h-rYSVX90' }],
},
];
expect(autoLinkToSlate(nodes)).toEqual(slate);
});
it('converts url with text after', () => {
const nodes: MdastNode[] = [
{ type: 'text', value: 'https://www.youtube.com/watch?v=p6h-rYSVX90 and text after' },
];
const slate: MdastNode[] = [
{
type: 'a',
url: 'https://www.youtube.com/watch?v=p6h-rYSVX90',
children: [{ text: 'https://www.youtube.com/watch?v=p6h-rYSVX90' }],
},
{
type: 'text',
value: ' and text after',
},
];
expect(autoLinkToSlate(nodes)).toEqual(slate);
});
it('converts url with text before and after', () => {
const nodes: MdastNode[] = [
{
type: 'text',
value: 'Text before https://www.youtube.com/watch?v=p6h-rYSVX90 and text after',
},
];
const slate: MdastNode[] = [
{
type: 'text',
value: 'Text before ',
},
{
type: 'a',
url: 'https://www.youtube.com/watch?v=p6h-rYSVX90',
children: [{ text: 'https://www.youtube.com/watch?v=p6h-rYSVX90' }],
},
{
type: 'text',
value: ' and text after',
},
];
expect(autoLinkToSlate(nodes)).toEqual(slate);
});
it('converts multiple urls', () => {
const nodes: MdastNode[] = [
{
type: 'text',
value:
'Text before https://www.youtube.com/watch?v=p6h-rYSVX90 and https://www.youtube.com/watch?v=p6h-rYSVX90 text after',
},
];
const slate: MdastNode[] = [
{
type: 'text',
value: 'Text before ',
},
{
type: 'a',
url: 'https://www.youtube.com/watch?v=p6h-rYSVX90',
children: [{ text: 'https://www.youtube.com/watch?v=p6h-rYSVX90' }],
},
{
type: 'text',
value: ' and ',
},
{
type: 'a',
url: 'https://www.youtube.com/watch?v=p6h-rYSVX90',
children: [{ text: 'https://www.youtube.com/watch?v=p6h-rYSVX90' }],
},
{
type: 'text',
value: ' text after',
},
];
expect(autoLinkToSlate(nodes)).toEqual(slate);
});
it('does not convert plain text', () => {
const nodes: MdastNode[] = [
{
type: 'text',
value: 'Some text about something going on somewhere',
},
];
const slate: MdastNode[] = [
{
type: 'text',
value: 'Some text about something going on somewhere',
},
];
expect(autoLinkToSlate(nodes)).toEqual(slate);
});
});
});

View File

@ -1,7 +1,265 @@
import { processShortcodeConfigToMdx } from '../processShortcodeConfig';
import {
processShortcodeConfigToMdx,
processShortcodeConfigsToSlate,
} from '../processShortcodeConfig';
import { testShortcodeConfigs } from '../../../tests-util/serializationTests.util';
import type { MdastNode } from '../ast-types';
describe('processShortcodeConfig', () => {
describe('processShortcodeConfigsToSlate', () => {
it('converts shortcode', () => {
const nodes: MdastNode[] = [{ type: 'text', value: '[youtube|p6h-rYSVX90]' }];
const slate: MdastNode[] = [
{
type: 'shortcode',
shortcode: 'youtube',
args: ['p6h-rYSVX90'],
children: [{ text: '' }],
},
];
expect(processShortcodeConfigsToSlate(testShortcodeConfigs, nodes)).toEqual(slate);
});
it('converts shortcode with no args', () => {
const nodes: MdastNode[] = [{ type: 'text', value: '[youtube]' }];
const slate: MdastNode[] = [
{
type: 'shortcode',
shortcode: 'youtube',
args: [],
children: [{ text: '' }],
},
];
expect(processShortcodeConfigsToSlate(testShortcodeConfigs, nodes)).toEqual(slate);
});
it('converts shortcode with multiple args', () => {
const nodes: MdastNode[] = [
{ type: 'text', value: '[youtube|p6h-rYSVX90|somethingElse|andOneMore]' },
];
const slate: MdastNode[] = [
{
type: 'shortcode',
shortcode: 'youtube',
args: ['p6h-rYSVX90', 'somethingElse', 'andOneMore'],
children: [{ text: '' }],
},
];
expect(processShortcodeConfigsToSlate(testShortcodeConfigs, nodes)).toEqual(slate);
});
it('converts shortcode with text before', () => {
const nodes: MdastNode[] = [{ type: 'text', value: 'Text before [youtube|p6h-rYSVX90]' }];
const slate: MdastNode[] = [
{
type: 'text',
value: 'Text before ',
},
{
type: 'shortcode',
shortcode: 'youtube',
args: ['p6h-rYSVX90'],
children: [{ text: '' }],
},
];
expect(processShortcodeConfigsToSlate(testShortcodeConfigs, nodes)).toEqual(slate);
});
it('converts shortcode with text after', () => {
const nodes: MdastNode[] = [{ type: 'text', value: '[youtube|p6h-rYSVX90] and text after' }];
const slate: MdastNode[] = [
{
type: 'shortcode',
shortcode: 'youtube',
args: ['p6h-rYSVX90'],
children: [{ text: '' }],
},
{
type: 'text',
value: ' and text after',
},
];
expect(processShortcodeConfigsToSlate(testShortcodeConfigs, nodes)).toEqual(slate);
});
it('converts shortcode with text before and after', () => {
const nodes: MdastNode[] = [
{ type: 'text', value: 'Text before [youtube|p6h-rYSVX90] and text after' },
];
const slate: MdastNode[] = [
{
type: 'text',
value: 'Text before ',
},
{
type: 'shortcode',
shortcode: 'youtube',
args: ['p6h-rYSVX90'],
children: [{ text: '' }],
},
{
type: 'text',
value: ' and text after',
},
];
expect(processShortcodeConfigsToSlate(testShortcodeConfigs, nodes)).toEqual(slate);
});
it('converts multiple shortcodes', () => {
const nodes: MdastNode[] = [
{
type: 'text',
value: 'Text before [youtube|p6h-rYSVX90] and {{< twitter 917359331535966209 >}}',
},
];
const slate: MdastNode[] = [
{
type: 'text',
value: 'Text before ',
},
{
type: 'shortcode',
shortcode: 'youtube',
args: ['p6h-rYSVX90'],
children: [{ text: '' }],
},
{
type: 'text',
value: ' and ',
},
{
type: 'shortcode',
shortcode: 'twitter',
args: ['917359331535966209'],
children: [{ text: '' }],
},
];
expect(processShortcodeConfigsToSlate(testShortcodeConfigs, nodes)).toEqual(slate);
});
it('converts multiple of the same shortcodes', () => {
const nodes: MdastNode[] = [
{
type: 'text',
value:
'Text before [youtube|p6h-rYSVX90], [youtube|p6h-rYSVX90], {{< twitter 917359331535966209 >}} and [youtube|p6h-rYSVX90]',
},
];
const slate: MdastNode[] = [
{
type: 'text',
value: 'Text before ',
},
{
type: 'shortcode',
shortcode: 'youtube',
args: ['p6h-rYSVX90'],
children: [{ text: '' }],
},
{
type: 'text',
value: ', ',
},
{
type: 'shortcode',
shortcode: 'youtube',
args: ['p6h-rYSVX90'],
children: [{ text: '' }],
},
{
type: 'text',
value: ', ',
},
{
type: 'shortcode',
shortcode: 'twitter',
args: ['917359331535966209'],
children: [{ text: '' }],
},
{
type: 'text',
value: ' and ',
},
{
type: 'shortcode',
shortcode: 'youtube',
args: ['p6h-rYSVX90'],
children: [{ text: '' }],
},
];
expect(processShortcodeConfigsToSlate(testShortcodeConfigs, nodes)).toEqual(slate);
});
it('does not convert unrecognized shortcode', () => {
const nodes: MdastNode[] = [{ type: 'text', value: '[someOtherShortcode|andstuff]' }];
const slate: MdastNode[] = [
{
type: 'text',
value: '[someOtherShortcode|andstuff]',
},
];
expect(processShortcodeConfigsToSlate(testShortcodeConfigs, nodes)).toEqual(slate);
});
it('does not convert unrecognized shortcode surrounded by recognized shortcodes', () => {
const nodes: MdastNode[] = [
{
type: 'text',
value:
'Text before [youtube|p6h-rYSVX90], [someOtherShortcode|andstuff] and {{< twitter 917359331535966209 >}}',
},
];
const slate: MdastNode[] = [
{
type: 'text',
value: 'Text before ',
},
{
type: 'shortcode',
shortcode: 'youtube',
args: ['p6h-rYSVX90'],
children: [{ text: '' }],
},
{
type: 'text',
value: ', [someOtherShortcode|andstuff] and ',
},
{
type: 'shortcode',
shortcode: 'twitter',
args: ['917359331535966209'],
children: [{ text: '' }],
},
];
expect(processShortcodeConfigsToSlate(testShortcodeConfigs, nodes)).toEqual(slate);
});
it('does not convert plain text', () => {
const nodes: MdastNode[] = [
{ type: 'text', value: 'Some text about something going on somewhere' },
];
const slate: MdastNode[] = [
{
type: 'text',
value: 'Some text about something going on somewhere',
},
];
expect(processShortcodeConfigsToSlate(testShortcodeConfigs, nodes)).toEqual(slate);
});
});
describe('processShortcodeConfigToMdx', () => {
it('converts to mdx', () => {
const markdown = '[youtube|p6h-rYSVX90]';
@ -25,21 +283,21 @@ describe('processShortcodeConfig', () => {
expect(processShortcodeConfigToMdx(testShortcodeConfigs, markdown)).toBe(mdx);
});
it('shortcode with text before', () => {
it('converts shortcode with text before', () => {
const markdown = 'Text before [youtube|p6h-rYSVX90]';
const mdx = 'Text before <Shortcode shortcode="youtube" args={[\'p6h-rYSVX90\']} />';
expect(processShortcodeConfigToMdx(testShortcodeConfigs, markdown)).toBe(mdx);
});
it('shortcode with text after', () => {
it('converts shortcode with text after', () => {
const markdown = '[youtube|p6h-rYSVX90] and text after';
const mdx = '<Shortcode shortcode="youtube" args={[\'p6h-rYSVX90\']} /> and text after';
expect(processShortcodeConfigToMdx(testShortcodeConfigs, markdown)).toBe(mdx);
});
it('shortcode with text before and after', () => {
it('converts shortcode with text before and after', () => {
const markdown = 'Text before [youtube|p6h-rYSVX90] and text after';
const mdx =
'Text before <Shortcode shortcode="youtube" args={[\'p6h-rYSVX90\']} /> and text after';
@ -47,7 +305,7 @@ describe('processShortcodeConfig', () => {
expect(processShortcodeConfigToMdx(testShortcodeConfigs, markdown)).toBe(mdx);
});
it('multiple shortcodes', () => {
it('converts multiple shortcodes', () => {
const markdown = 'Text before [youtube|p6h-rYSVX90] and {{< twitter 917359331535966209 >}}';
const mdx =
'Text before <Shortcode shortcode="youtube" args={[\'p6h-rYSVX90\']} /> and <Shortcode shortcode="twitter" args={[\'917359331535966209\']} />';
@ -55,7 +313,7 @@ describe('processShortcodeConfig', () => {
expect(processShortcodeConfigToMdx(testShortcodeConfigs, markdown)).toBe(mdx);
});
it('multiple of the same shortcodes', () => {
it('converts multiple of the same shortcodes', () => {
const markdown =
'Text before [youtube|p6h-rYSVX90], [youtube|p6h-rYSVX90], {{< twitter 917359331535966209 >}} and [youtube|p6h-rYSVX90]';
const mdx =
@ -64,14 +322,14 @@ describe('processShortcodeConfig', () => {
expect(processShortcodeConfigToMdx(testShortcodeConfigs, markdown)).toBe(mdx);
});
it('unrecognized shortcode', () => {
it('does not convert unrecognized shortcode', () => {
const markdown = '[someOtherShortcode|andstuff]';
const mdx = '[someOtherShortcode|andstuff]';
expect(processShortcodeConfigToMdx(testShortcodeConfigs, markdown)).toBe(mdx);
});
it('unrecognized shortcode surrounded by recognized shortcodes', () => {
it('does not convert unrecognized shortcode surrounded by recognized shortcodes', () => {
const markdown =
'Text before [youtube|p6h-rYSVX90], [someOtherShortcode|andstuff] and {{< twitter 917359331535966209 >}}';
const mdx =
@ -80,7 +338,7 @@ describe('processShortcodeConfig', () => {
expect(processShortcodeConfigToMdx(testShortcodeConfigs, markdown)).toBe(mdx);
});
it('plain text', () => {
it('does not convert plain text', () => {
const markdown = 'Some text about something going on somewhere';
const mdx = 'Some text about something going on somewhere';

View File

@ -1,31 +0,0 @@
import {
deserializationOnlyTestData,
runSerializationTests,
testShortcodeConfigs as shortcodeConfigs,
} from '../../../tests-util/serializationTests.util';
import { slateCompiler } from '../toSlatePlugin';
import type { SerializationTestData } from '../../../tests-util/serializationTests.util';
import type { MdastNode } from '../ast-types';
async function expectNodes(
mdast: MdastNode,
useMdx: boolean,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
children: any[],
) {
const compiler = slateCompiler({ useMdx, shortcodeConfigs });
expect(compiler(mdast)).toEqual(children);
}
function testRunner(key: string, mode: 'markdown' | 'mdx' | 'both', data: SerializationTestData) {
it(`deserializes ${key}`, async () => {
await expectNodes(data.mdast, mode === 'mdx', data.slate);
});
}
describe('markdownToSlate', () => {
runSerializationTests(testRunner);
runSerializationTests(testRunner, deserializationOnlyTestData);
});

View File

@ -0,0 +1,45 @@
/* eslint-disable import/prefer-default-export */
import { isNotEmpty } from '@staticcms/core/lib/util/string.util';
import { NodeTypes } from './ast-types';
import type { BaseMdastNode, MdastNode } from './ast-types';
export function autoLinkToSlate(nodes: BaseMdastNode[]) {
const output: MdastNode[] = [];
for (const node of nodes) {
if (node.type === 'text' && node.value) {
const regex =
/([\w\W]*?)((?:http(?:s)?:\/\/.)?(?:www\.)?[-a-zA-Z0-9@:%._+~#=]{2,256}\.[a-z]{2,6}\b(?:[-a-zA-Z0-9@:%_+.~#?&//=]*))([\w\W]*)/g;
let matches: RegExpExecArray | null;
let rest = node.value;
while (isNotEmpty(rest) && (matches = regex.exec(rest)) !== null && matches.length === 4) {
if (isNotEmpty(matches[1])) {
output.push({
type: 'text',
value: matches[1],
});
}
output.push({
type: NodeTypes.link,
url: matches[2],
children: [{ text: matches[2] }],
});
rest = matches[3];
regex.lastIndex = 0;
}
if (isNotEmpty(rest)) {
output.push({
type: 'text',
value: rest,
});
}
continue;
}
output.push(node);
}
return output;
}

View File

@ -2,7 +2,8 @@
import { ELEMENT_PARAGRAPH } from '@udecode/plate';
import { LIST_TYPES, MarkNodeTypes, NodeTypes } from './ast-types';
import { processShortcodeConfigToSlate } from './processShortcodeConfig';
import { autoLinkToSlate } from './autoLinkUrls';
import { processShortcodeConfigsToSlate } from './processShortcodeConfig';
import type { ShortcodeConfig } from '@staticcms/core/interface';
import type { MdBlockElement } from '@staticcms/markdown';
@ -350,11 +351,7 @@ export default function deserializeMarkdown(node: MdastNode, options: Options) {
return { text: '' };
}
let nodes: MdastNode[] = [node];
for (const shortcode in shortcodeConfigs) {
nodes = processShortcodeConfigToSlate(shortcode, shortcodeConfigs[shortcode], nodes);
}
const nodes = autoLinkToSlate(processShortcodeConfigsToSlate(shortcodeConfigs, [node]));
return nodes.map(node => (node.type === 'text' ? { text: node.value ?? '' } : node));

View File

@ -19,7 +19,7 @@ function createShortcodeRegex(name: string, config: ShortcodeConfig) {
)}?([\\w\\W]*?)${cleanRegex(config.closeTag)}`;
}
export function processShortcodeConfigToSlate(
function processShortcodeConfigToSlate(
name: string,
config: ShortcodeConfig,
nodes: BaseMdastNode[],
@ -68,6 +68,19 @@ export function processShortcodeConfigToSlate(
return output;
}
export function processShortcodeConfigsToSlate(
configs: Record<string, ShortcodeConfig>,
nodes: BaseMdastNode[],
) {
let finalNodes: MdastNode[] = nodes;
for (const shortcode in configs) {
finalNodes = processShortcodeConfigToSlate(shortcode, configs[shortcode], finalNodes);
}
return finalNodes;
}
export function processShortcodeConfigToMdx(
configs: Record<string, ShortcodeConfig>,
markdown: string,

View File

@ -12779,7 +12779,7 @@ mdast-util-gfm-autolink-literal@^1.0.0:
mdast-util-find-and-replace "^2.0.0"
micromark-util-character "^1.0.0"
mdast-util-gfm-footnote@^1.0.0:
mdast-util-gfm-footnote@1.0.2, mdast-util-gfm-footnote@^1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-1.0.2.tgz#ce5e49b639c44de68d5bf5399877a14d5020424e"
integrity sha512-56D19KOGbE00uKVj3sgIykpwKL179QsVFwx/DCW0u/0+URsryacI4MAdNJl0dh+u2PSsD9FtxPFbHCzJ78qJFQ==
@ -12788,7 +12788,7 @@ mdast-util-gfm-footnote@^1.0.0:
mdast-util-to-markdown "^1.3.0"
micromark-util-normalize-identifier "^1.0.0"
mdast-util-gfm-strikethrough@^1.0.0:
mdast-util-gfm-strikethrough@1.0.3, mdast-util-gfm-strikethrough@^1.0.0:
version "1.0.3"
resolved "https://registry.yarnpkg.com/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-1.0.3.tgz#5470eb105b483f7746b8805b9b989342085795b7"
integrity sha512-DAPhYzTYrRcXdMjUtUjKvW9z/FNAMTdU0ORyMcbmkwYNbKocDpdk+PX1L1dQgOID/+vVs1uBQ7ElrBQfZ0cuiQ==
@ -12796,7 +12796,7 @@ mdast-util-gfm-strikethrough@^1.0.0:
"@types/mdast" "^3.0.0"
mdast-util-to-markdown "^1.3.0"
mdast-util-gfm-table@^1.0.0:
mdast-util-gfm-table@1.0.7, mdast-util-gfm-table@^1.0.0:
version "1.0.7"
resolved "https://registry.yarnpkg.com/mdast-util-gfm-table/-/mdast-util-gfm-table-1.0.7.tgz#3552153a146379f0f9c4c1101b071d70bbed1a46"
integrity sha512-jjcpmNnQvrmN5Vx7y7lEc2iIOEytYv7rTvu+MeyAsSHTASGCCRA79Igg2uKssgOs1i1po8s3plW0sTu1wkkLGg==
@ -12806,7 +12806,7 @@ mdast-util-gfm-table@^1.0.0:
mdast-util-from-markdown "^1.0.0"
mdast-util-to-markdown "^1.3.0"
mdast-util-gfm-task-list-item@^1.0.0:
mdast-util-gfm-task-list-item@1.0.2, mdast-util-gfm-task-list-item@^1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-1.0.2.tgz#b280fcf3b7be6fd0cc012bbe67a59831eb34097b"
integrity sha512-PFTA1gzfp1B1UaiJVyhJZA1rm0+Tzn690frc/L8vNX1Jop4STZgOE6bxUhnzdVSB+vm2GU1tIsuQcA9bxTQpMQ==
@ -13043,6 +13043,20 @@ micromark-extension-gfm-autolink-literal@^1.0.0:
micromark-util-types "^1.0.0"
uvu "^0.5.0"
micromark-extension-gfm-footnote@1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-1.1.0.tgz#73e3db823db9defef25f68074cb4cf4bb9cf6a8c"
integrity sha512-RWYce7j8+c0n7Djzv5NzGEGitNNYO3uj+h/XYMdS/JinH1Go+/Qkomg/rfxExFzYTiydaV6GLeffGO5qcJbMPA==
dependencies:
micromark-core-commonmark "^1.0.0"
micromark-factory-space "^1.0.0"
micromark-util-character "^1.0.0"
micromark-util-normalize-identifier "^1.0.0"
micromark-util-sanitize-uri "^1.0.0"
micromark-util-symbol "^1.0.0"
micromark-util-types "^1.0.0"
uvu "^0.5.0"
micromark-extension-gfm-footnote@^1.0.0:
version "1.0.4"
resolved "https://registry.yarnpkg.com/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-1.0.4.tgz#cbfd8873b983e820c494498c6dac0105920818d5"
@ -13057,7 +13071,7 @@ micromark-extension-gfm-footnote@^1.0.0:
micromark-util-types "^1.0.0"
uvu "^0.5.0"
micromark-extension-gfm-strikethrough@^1.0.0:
micromark-extension-gfm-strikethrough@1.0.5, micromark-extension-gfm-strikethrough@^1.0.0:
version "1.0.5"
resolved "https://registry.yarnpkg.com/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-1.0.5.tgz#4db40b87d674a6fe1d00d59ac91118e4f5960f12"
integrity sha512-X0oI5eYYQVARhiNfbETy7BfLSmSilzN1eOuoRnrf9oUNsPRrWOAe9UqSizgw1vNxQBfOwL+n2610S3bYjVNi7w==
@ -13069,7 +13083,7 @@ micromark-extension-gfm-strikethrough@^1.0.0:
micromark-util-types "^1.0.0"
uvu "^0.5.0"
micromark-extension-gfm-table@^1.0.0:
micromark-extension-gfm-table@1.0.5, micromark-extension-gfm-table@^1.0.0:
version "1.0.5"
resolved "https://registry.yarnpkg.com/micromark-extension-gfm-table/-/micromark-extension-gfm-table-1.0.5.tgz#7b708b728f8dc4d95d486b9e7a2262f9cddbcbb4"
integrity sha512-xAZ8J1X9W9K3JTJTUL7G6wSKhp2ZYHrFk5qJgY/4B33scJzE2kpfRL6oiw/veJTbt7jiM/1rngLlOKPWr1G+vg==
@ -13087,7 +13101,7 @@ micromark-extension-gfm-tagfilter@^1.0.0:
dependencies:
micromark-util-types "^1.0.0"
micromark-extension-gfm-task-list-item@^1.0.0:
micromark-extension-gfm-task-list-item@1.0.4, micromark-extension-gfm-task-list-item@^1.0.0:
version "1.0.4"
resolved "https://registry.yarnpkg.com/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-1.0.4.tgz#4b66d87847de40cef2b5ceddb9f9629a6dfe7472"
integrity sha512-9XlIUUVnYXHsFF2HZ9jby4h3npfX10S1coXTnV035QGPgrtNYQq3J6IfIvcCIUAJrrqBVi5BqA/LmaOMJqPwMQ==
@ -13261,7 +13275,7 @@ micromark-util-classify-character@^1.0.0:
micromark-util-symbol "^1.0.0"
micromark-util-types "^1.0.0"
micromark-util-combine-extensions@^1.0.0:
micromark-util-combine-extensions@1.0.0, micromark-util-combine-extensions@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/micromark-util-combine-extensions/-/micromark-util-combine-extensions-1.0.0.tgz#91418e1e74fb893e3628b8d496085639124ff3d5"
integrity sha512-J8H058vFBdo/6+AsjHp2NF7AJ02SZtWaVUjsayNFeAiydTxUwViQPxN0Hf8dp4FmCQi0UUFovFsEyRSUmFH3MA==