@uiw/react-md-editor 4.1.6 → 4.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/README.md +1 -1
  2. package/dist/mdeditor.css +5 -0
  3. package/dist/mdeditor.js +2378 -1029
  4. package/dist/mdeditor.min.css +1 -1
  5. package/dist/mdeditor.min.js +1 -1
  6. package/esm/Editor.factory.js +60 -10
  7. package/esm/components/TextArea/Markdown.common.d.ts +3 -5
  8. package/esm/components/TextArea/Markdown.common.js +2 -56
  9. package/esm/components/TextArea/Markdown.d.ts +3 -5
  10. package/esm/components/TextArea/Markdown.factory.d.ts +6 -0
  11. package/esm/components/TextArea/Markdown.factory.js +138 -0
  12. package/esm/components/TextArea/Markdown.js +2 -63
  13. package/esm/components/TextArea/index.css +5 -0
  14. package/esm/components/TextArea/index.less +5 -0
  15. package/esm/components/TextArea/splitBlocks.d.ts +13 -0
  16. package/esm/components/TextArea/splitBlocks.js +111 -0
  17. package/esm/package.json +1 -0
  18. package/lib/Editor.factory.js +68 -11
  19. package/lib/components/TextArea/Markdown.common.d.ts +3 -5
  20. package/lib/components/TextArea/Markdown.common.js +3 -60
  21. package/lib/components/TextArea/Markdown.d.ts +3 -5
  22. package/lib/components/TextArea/Markdown.factory.d.ts +6 -0
  23. package/lib/components/TextArea/Markdown.factory.js +148 -0
  24. package/lib/components/TextArea/Markdown.js +3 -67
  25. package/lib/components/TextArea/index.less +5 -0
  26. package/lib/components/TextArea/splitBlocks.d.ts +13 -0
  27. package/lib/components/TextArea/splitBlocks.js +117 -0
  28. package/markdown-editor.css +5 -0
  29. package/package.json +18 -12
  30. package/src/Editor.factory.tsx +59 -4
  31. package/src/components/TextArea/Markdown.common.tsx +3 -46
  32. package/src/components/TextArea/Markdown.factory.tsx +132 -0
  33. package/src/components/TextArea/Markdown.tsx +3 -55
  34. package/src/components/TextArea/index.less +5 -0
  35. package/src/components/TextArea/splitBlocks.ts +114 -0
@@ -1,7 +1,8 @@
1
1
  import _extends from "@babel/runtime/helpers/extends";
2
2
  import _objectWithoutPropertiesLoose from "@babel/runtime/helpers/objectWithoutPropertiesLoose";
3
- var _excluded = ["prefixCls", "className", "value", "commands", "commandsFilter", "direction", "extraCommands", "height", "enableScroll", "visibleDragbar", "highlightEnable", "preview", "fullscreen", "overflow", "previewOptions", "textareaProps", "id", "name", "required", "disabled", "readOnly", "placeholder", "aria-label", "aria-labelledby", "aria-describedby", "aria-invalid", "aria-required", "aria-errormessage", "maxHeight", "minHeight", "autoFocus", "autoFocusEnd", "tabSize", "defaultTabEnable", "onChange", "onStatistics", "onHeightChange", "hideToolbar", "toolbarBottom", "components", "renderTextarea"];
4
- import React, { useEffect, useReducer, useMemo, useRef, useImperativeHandle } from 'react';
3
+ var _excluded = ["renderMsRef"],
4
+ _excluded2 = ["prefixCls", "className", "value", "commands", "commandsFilter", "direction", "extraCommands", "height", "enableScroll", "visibleDragbar", "highlightEnable", "preview", "fullscreen", "overflow", "previewOptions", "textareaProps", "id", "name", "required", "disabled", "readOnly", "placeholder", "aria-label", "aria-labelledby", "aria-describedby", "aria-invalid", "aria-required", "aria-errormessage", "maxHeight", "minHeight", "autoFocus", "autoFocusEnd", "tabSize", "defaultTabEnable", "onChange", "onStatistics", "onHeightChange", "hideToolbar", "toolbarBottom", "components", "renderTextarea"];
5
+ import React, { useEffect, useLayoutEffect, useReducer, useMemo, useRef, useImperativeHandle } from 'react';
5
6
  import { ToolbarVisibility } from "./components/Toolbar/index.js";
6
7
  import DragBar from "./components/DragBar/index.js";
7
8
  import { getCommands, getExtraCommands, TextAreaCommandOrchestrator } from "./commands/index.js";
@@ -16,11 +17,57 @@ function setGroupPopFalse(data) {
16
17
  });
17
18
  return data;
18
19
  }
20
+ var DEFAULT_PREVIEW_OPTIONS = {};
21
+
22
+ /**
23
+ * Lets the preview render after the textarea on React 18+, so typing in a large
24
+ * document stays responsive. Older React versions render it right away, as before.
25
+ */
26
+ var useDeferredValue = React.useDeferredValue || (value => value);
27
+ var useIsomorphicLayoutEffect = typeof window === 'undefined' ? useEffect : useLayoutEffect;
28
+ var SLOW_PREVIEW_MS = 50;
29
+ var MAX_PREVIEW_DELAY_MS = 500;
30
+
31
+ /**
32
+ * Keeps the preview in step with every keystroke while it renders fast. Once a preview
33
+ * render takes longer than SLOW_PREVIEW_MS, it waits for a pause in typing as long as
34
+ * that render (at most MAX_PREVIEW_DELAY_MS) before updating.
35
+ * https://github.com/uiwjs/react-md-editor/issues/620
36
+ */
37
+ function usePreviewSource(markdown, previewRenderMs) {
38
+ var _useReducer = useReducer(count => count + 1, 0),
39
+ rerender = _useReducer[1];
40
+ var shownRef = useRef(markdown);
41
+ if (previewRenderMs.current < SLOW_PREVIEW_MS) {
42
+ shownRef.current = markdown;
43
+ }
44
+ useEffect(() => {
45
+ if (shownRef.current === markdown) {
46
+ return;
47
+ }
48
+ var delay = Math.min(previewRenderMs.current, MAX_PREVIEW_DELAY_MS);
49
+ var timer = setTimeout(() => {
50
+ shownRef.current = markdown;
51
+ rerender();
52
+ }, delay);
53
+ return () => clearTimeout(timer);
54
+ }, [markdown, previewRenderMs]);
55
+ return shownRef.current;
56
+ }
19
57
  export function createMDEditor(options) {
20
58
  var MarkdownPreview = options.MarkdownPreview,
21
59
  TextArea = options.TextArea;
22
60
  var PreviewComponent = MarkdownPreview;
23
61
  var TextAreaComponent = TextArea;
62
+ function TimedPreview(props) {
63
+ var renderMsRef = props.renderMsRef,
64
+ previewProps = _objectWithoutPropertiesLoose(props, _excluded);
65
+ var renderStart = performance.now();
66
+ useIsomorphicLayoutEffect(() => {
67
+ renderMsRef.current = performance.now() - renderStart;
68
+ });
69
+ return /*#__PURE__*/_jsx(PreviewComponent, _extends({}, previewProps));
70
+ }
24
71
  var InternalMDEditor = /*#__PURE__*/React.forwardRef((props, ref) => {
25
72
  var _ref = props || {},
26
73
  _ref$prefixCls = _ref.prefixCls,
@@ -48,7 +95,7 @@ export function createMDEditor(options) {
48
95
  _ref$overflow = _ref.overflow,
49
96
  overflow = _ref$overflow === void 0 ? true : _ref$overflow,
50
97
  _ref$previewOptions = _ref.previewOptions,
51
- previewOptions = _ref$previewOptions === void 0 ? {} : _ref$previewOptions,
98
+ previewOptions = _ref$previewOptions === void 0 ? DEFAULT_PREVIEW_OPTIONS : _ref$previewOptions,
52
99
  textareaProps = _ref.textareaProps,
53
100
  id = _ref.id,
54
101
  name = _ref.name,
@@ -81,10 +128,10 @@ export function createMDEditor(options) {
81
128
  toolbarBottom = _ref$toolbarBottom === void 0 ? false : _ref$toolbarBottom,
82
129
  components = _ref.components,
83
130
  renderTextarea = _ref.renderTextarea,
84
- other = _objectWithoutPropertiesLoose(_ref, _excluded);
131
+ other = _objectWithoutPropertiesLoose(_ref, _excluded2);
85
132
  var cmds = commands.map(item => commandsFilter ? commandsFilter(item, false) : item).filter(Boolean);
86
133
  var extraCmds = extraCommands.map(item => commandsFilter ? commandsFilter(item, true) : item).filter(Boolean);
87
- var _useReducer = useReducer(reducer, {
134
+ var _useReducer2 = useReducer(reducer, {
88
135
  markdown: propsValue,
89
136
  preview: previewType,
90
137
  components,
@@ -100,8 +147,8 @@ export function createMDEditor(options) {
100
147
  fullscreen,
101
148
  barPopup: {}
102
149
  }),
103
- state = _useReducer[0],
104
- dispatch = _useReducer[1];
150
+ state = _useReducer2[0],
151
+ dispatch = _useReducer2[1];
105
152
  var container = useRef(null);
106
153
  var previewRef = useRef(null);
107
154
  var enableScrollRef = useRef(enableScroll);
@@ -207,14 +254,17 @@ export function createMDEditor(options) {
207
254
  };
208
255
  var previewClassName = prefixCls + "-preview " + (previewOptions.className || '');
209
256
  var handlePreviewScroll = e => handleScroll(e, 'preview');
257
+ var previewRenderMs = useRef(0);
258
+ var previewMarkdown = useDeferredValue(usePreviewSource(state.markdown || '', previewRenderMs));
210
259
  var mdPreview = useMemo(() => /*#__PURE__*/_jsx("div", {
211
260
  ref: previewRef,
212
261
  className: previewClassName,
213
- children: /*#__PURE__*/_jsx(PreviewComponent, _extends({}, previewOptions, {
262
+ children: /*#__PURE__*/_jsx(TimedPreview, _extends({}, previewOptions, {
263
+ renderMsRef: previewRenderMs,
214
264
  onScroll: handlePreviewScroll,
215
- source: state.markdown || ''
265
+ source: previewMarkdown
216
266
  }))
217
- }), [previewClassName, previewOptions, state.markdown]);
267
+ }), [previewClassName, previewOptions, previewMarkdown]);
218
268
  var preview = (components == null ? void 0 : components.preview) && (components == null ? void 0 : components.preview(state.markdown || '', state, dispatch));
219
269
  if (preview && /*#__PURE__*/React.isValidElement(preview)) {
220
270
  mdPreview = /*#__PURE__*/_jsx("div", {
@@ -1,5 +1,3 @@
1
- import React from 'react';
2
- import { type IProps } from '../../Types';
3
- export interface MarkdownProps extends IProps, React.HTMLAttributes<HTMLPreElement> {
4
- }
5
- export default function Markdown(props: MarkdownProps): import("react/jsx-runtime").JSX.Element;
1
+ export type { MarkdownProps } from './Markdown.factory';
2
+ declare const _default: (props: import("./Markdown.factory").MarkdownProps) => import("react/jsx-runtime").JSX.Element;
3
+ export default _default;
@@ -1,57 +1,3 @@
1
- import _taggedTemplateLiteralLoose from "@babel/runtime/helpers/taggedTemplateLiteralLoose";
2
- var _templateObject;
3
- import React, { useContext, useEffect } from 'react';
4
- import { rehype } from 'rehype';
5
1
  import rehypePrism from 'rehype-prism-plus/common';
6
- import { EditorContext } from "../../Context.js";
7
- import { jsx as _jsx } from "react/jsx-runtime";
8
- function html2Escape(sHtml) {
9
- return sHtml.replace(/[<&"]/g, c => ({
10
- '<': '&lt;',
11
- '>': '&gt;',
12
- '&': '&amp;',
13
- '"': '&quot;'
14
- })[c]);
15
- }
16
- export default function Markdown(props) {
17
- var prefixCls = props.prefixCls;
18
- var _useContext = useContext(EditorContext),
19
- _useContext$markdown = _useContext.markdown,
20
- markdown = _useContext$markdown === void 0 ? '' : _useContext$markdown,
21
- highlightEnable = _useContext.highlightEnable,
22
- dispatch = _useContext.dispatch;
23
- var preRef = /*#__PURE__*/React.createRef();
24
- useEffect(() => {
25
- if (preRef.current && dispatch) {
26
- dispatch({
27
- textareaPre: preRef.current
28
- });
29
- }
30
- // eslint-disable-next-line react-hooks/exhaustive-deps
31
- }, []);
32
- if (!markdown) {
33
- return /*#__PURE__*/_jsx("pre", {
34
- ref: preRef,
35
- className: prefixCls + "-text-pre wmde-markdown-color"
36
- });
37
- }
38
- var mdStr = "<pre class=\"language-markdown " + prefixCls + "-text-pre wmde-markdown-color\"><code class=\"language-markdown\">" + html2Escape(String.raw(_templateObject || (_templateObject = _taggedTemplateLiteralLoose(["", ""])), markdown)) + "\n</code></pre>";
39
- if (highlightEnable) {
40
- try {
41
- mdStr = rehype().data('settings', {
42
- fragment: true
43
- })
44
- // https://github.com/uiwjs/react-md-editor/issues/593
45
- // @ts-ignore
46
- .use(rehypePrism, {
47
- ignoreMissing: true
48
- }).processSync(mdStr).toString();
49
- } catch (error) {}
50
- }
51
- return /*#__PURE__*/React.createElement('div', {
52
- className: 'wmde-markdown-color',
53
- dangerouslySetInnerHTML: {
54
- __html: mdStr || ''
55
- }
56
- });
57
- }
2
+ import { createMarkdown } from './Markdown.factory';
3
+ export default createMarkdown(rehypePrism);
@@ -1,5 +1,3 @@
1
- import React from 'react';
2
- import { type IProps } from '../../Types';
3
- export interface MarkdownProps extends IProps, React.HTMLAttributes<HTMLPreElement> {
4
- }
5
- export default function Markdown(props: MarkdownProps): import("react/jsx-runtime").JSX.Element;
1
+ export type { MarkdownProps } from './Markdown.factory';
2
+ declare const _default: (props: import("./Markdown.factory").MarkdownProps) => import("react/jsx-runtime").JSX.Element;
3
+ export default _default;
@@ -0,0 +1,6 @@
1
+ import React from 'react';
2
+ import type rehypePrismPlus from 'rehype-prism-plus';
3
+ import { type IProps } from '../../Types';
4
+ export interface MarkdownProps extends IProps, React.HTMLAttributes<HTMLPreElement> {
5
+ }
6
+ export declare function createMarkdown(rehypePrism: typeof rehypePrismPlus): (props: MarkdownProps) => import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,138 @@
1
+ import React, { useContext, useEffect, useMemo, useRef } from 'react';
2
+ import { rehype } from 'rehype';
3
+ import { EditorContext } from "../../Context.js";
4
+ import { splitBlocks } from "./splitBlocks.js";
5
+ /**
6
+ * Prism's markdown grammar takes quadratic time on long lines, so a single base64 image
7
+ * line can block the editor for minutes. Blocks with such a line are shown uncoloured.
8
+ */
9
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
10
+ var MAX_HIGHLIGHT_LINE_LENGTH = 1000;
11
+ function html2Escape(sHtml) {
12
+ return sHtml.replace(/[<&"]/g, c => ({
13
+ '<': '&lt;',
14
+ '>': '&gt;',
15
+ '&': '&amp;',
16
+ '"': '&quot;'
17
+ })[c]);
18
+ }
19
+ function hasLongLine(text) {
20
+ var lineStart = 0;
21
+ while (lineStart <= text.length) {
22
+ var lineEnd = text.indexOf('\n', lineStart);
23
+ var end = lineEnd === -1 ? text.length : lineEnd;
24
+ if (end - lineStart > MAX_HIGHLIGHT_LINE_LENGTH) {
25
+ return true;
26
+ }
27
+ lineStart = end + 1;
28
+ }
29
+ return false;
30
+ }
31
+ function getCodeInnerHtml(html) {
32
+ var codeStart = html.indexOf('<code');
33
+ var contentStart = html.indexOf('>', codeStart) + 1;
34
+ var contentEnd = html.lastIndexOf('</code>');
35
+ return html.slice(contentStart, contentEnd);
36
+ }
37
+ var MarkdownBlock = /*#__PURE__*/React.memo(function MarkdownBlock(props) {
38
+ return /*#__PURE__*/React.createElement('span', {
39
+ dangerouslySetInnerHTML: {
40
+ __html: props.html
41
+ }
42
+ });
43
+ });
44
+ export function createMarkdown(rehypePrism) {
45
+ var processor = rehype().data('settings', {
46
+ fragment: true
47
+ })
48
+ // https://github.com/uiwjs/react-md-editor/issues/593
49
+ // @ts-ignore
50
+ .use(rehypePrism, {
51
+ ignoreMissing: true
52
+ });
53
+ function highlightBlock(block) {
54
+ var escaped = html2Escape(block);
55
+ if (hasLongLine(block)) {
56
+ return escaped;
57
+ }
58
+ try {
59
+ var html = processor.processSync("<pre><code class=\"language-markdown\">" + escaped + "</code></pre>").toString();
60
+ return getCodeInnerHtml(html);
61
+ } catch (error) {
62
+ return escaped;
63
+ }
64
+ }
65
+
66
+ /**
67
+ * Highlighting runs per block, and blocks whose text did not change reuse their cached
68
+ * HTML and DOM, so typing in a large document only re-highlights the edited block.
69
+ * https://github.com/uiwjs/react-md-editor/issues/620
70
+ */
71
+ return function Markdown(props) {
72
+ var prefixCls = props.prefixCls;
73
+ var _useContext = useContext(EditorContext),
74
+ _useContext$markdown = _useContext.markdown,
75
+ markdown = _useContext$markdown === void 0 ? '' : _useContext$markdown,
76
+ highlightEnable = _useContext.highlightEnable,
77
+ dispatch = _useContext.dispatch;
78
+ var preRef = useRef(null);
79
+ var cacheRef = useRef(new Map());
80
+ var nextIdRef = useRef(0);
81
+ useEffect(() => {
82
+ if (preRef.current && dispatch) {
83
+ dispatch({
84
+ textareaPre: preRef.current
85
+ });
86
+ }
87
+ // eslint-disable-next-line react-hooks/exhaustive-deps
88
+ }, []);
89
+ var blocks = useMemo(() => {
90
+ if (!highlightEnable) {
91
+ cacheRef.current = new Map();
92
+ return [];
93
+ }
94
+ var cache = new Map();
95
+ var occurrences = new Map();
96
+ var result = splitBlocks(markdown).map(block => {
97
+ var highlighted = cacheRef.current.get(block) || cache.get(block);
98
+ if (!highlighted) {
99
+ highlighted = {
100
+ id: nextIdRef.current++,
101
+ html: highlightBlock(block)
102
+ };
103
+ }
104
+ cache.set(block, highlighted);
105
+ var occurrence = occurrences.get(block) || 0;
106
+ occurrences.set(block, occurrence + 1);
107
+ return {
108
+ key: highlighted.id + "-" + occurrence,
109
+ html: highlighted.html
110
+ };
111
+ });
112
+ cacheRef.current = cache;
113
+ return result;
114
+ }, [markdown, highlightEnable]);
115
+ if (!markdown) {
116
+ return /*#__PURE__*/_jsx("pre", {
117
+ ref: preRef,
118
+ className: prefixCls + "-text-pre wmde-markdown-color"
119
+ });
120
+ }
121
+ return /*#__PURE__*/_jsx("div", {
122
+ className: "wmde-markdown-color",
123
+ children: /*#__PURE__*/_jsx("pre", {
124
+ ref: preRef,
125
+ className: "language-markdown " + prefixCls + "-text-pre wmde-markdown-color",
126
+ children: highlightEnable ? /*#__PURE__*/_jsxs("code", {
127
+ className: "language-markdown code-highlight",
128
+ children: [blocks.map(block => /*#__PURE__*/_jsx(MarkdownBlock, {
129
+ html: block.html
130
+ }, block.key)), '\n']
131
+ }) : /*#__PURE__*/_jsx("code", {
132
+ className: "language-markdown",
133
+ children: markdown + "\n"
134
+ })
135
+ })
136
+ });
137
+ };
138
+ }
@@ -1,64 +1,3 @@
1
- import _taggedTemplateLiteralLoose from "@babel/runtime/helpers/taggedTemplateLiteralLoose";
2
- var _templateObject;
3
- import React, { useContext, useEffect } from 'react';
4
- import { rehype } from 'rehype';
5
1
  import rehypePrism from 'rehype-prism-plus';
6
- import { EditorContext } from "../../Context.js";
7
- import { jsx as _jsx } from "react/jsx-runtime";
8
- function html2Escape(sHtml) {
9
- return sHtml
10
- // .replace(/```(\w+)?([\s\S]*?)(\s.+)?```/g, (str: string) => {
11
- // return str.replace(
12
- // /[<&"]/g,
13
- // (c: string) => (({ '<': '&lt;', '>': '&gt;', '&': '&amp;', '"': '&quot;' } as Record<string, string>)[c]),
14
- // );
15
- // })
16
- .replace(/[<&"]/g, c => ({
17
- '<': '&lt;',
18
- '>': '&gt;',
19
- '&': '&amp;',
20
- '"': '&quot;'
21
- })[c]);
22
- }
23
- export default function Markdown(props) {
24
- var prefixCls = props.prefixCls;
25
- var _useContext = useContext(EditorContext),
26
- _useContext$markdown = _useContext.markdown,
27
- markdown = _useContext$markdown === void 0 ? '' : _useContext$markdown,
28
- highlightEnable = _useContext.highlightEnable,
29
- dispatch = _useContext.dispatch;
30
- var preRef = /*#__PURE__*/React.createRef();
31
- useEffect(() => {
32
- if (preRef.current && dispatch) {
33
- dispatch({
34
- textareaPre: preRef.current
35
- });
36
- }
37
- // eslint-disable-next-line react-hooks/exhaustive-deps
38
- }, []);
39
- if (!markdown) {
40
- return /*#__PURE__*/_jsx("pre", {
41
- ref: preRef,
42
- className: prefixCls + "-text-pre wmde-markdown-color"
43
- });
44
- }
45
- var mdStr = "<pre class=\"language-markdown " + prefixCls + "-text-pre wmde-markdown-color\"><code class=\"language-markdown\">" + html2Escape(String.raw(_templateObject || (_templateObject = _taggedTemplateLiteralLoose(["", ""])), markdown)) + "\n</code></pre>";
46
- if (highlightEnable) {
47
- try {
48
- mdStr = rehype().data('settings', {
49
- fragment: true
50
- })
51
- // https://github.com/uiwjs/react-md-editor/issues/593
52
- // @ts-ignore
53
- .use(rehypePrism, {
54
- ignoreMissing: true
55
- }).processSync(mdStr).toString();
56
- } catch (error) {}
57
- }
58
- return /*#__PURE__*/React.createElement('div', {
59
- className: 'wmde-markdown-color',
60
- dangerouslySetInnerHTML: {
61
- __html: mdStr || ''
62
- }
63
- });
64
- }
2
+ import { createMarkdown } from './Markdown.factory';
3
+ export default createMarkdown(rehypePrism);
@@ -113,6 +113,11 @@
113
113
  .w-md-editor-text-pre .token.bold {
114
114
  font-weight: unset !important;
115
115
  }
116
+ .w-md-editor-text-pre .token.list,
117
+ .w-md-editor-text-pre .token.table {
118
+ display: inline;
119
+ font-size: inherit;
120
+ }
116
121
  .w-md-editor-text-pre .token.title {
117
122
  line-height: unset !important;
118
123
  font-size: unset !important;
@@ -119,6 +119,11 @@
119
119
  .token.bold {
120
120
  font-weight: unset !important;
121
121
  }
122
+ .token.list,
123
+ .token.table {
124
+ display: inline;
125
+ font-size: inherit;
126
+ }
122
127
  .token.title {
123
128
  line-height: unset !important;
124
129
  font-size: unset !important;
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Splits markdown into blocks that Prism's markdown grammar highlights the same way
3
+ * on their own as it does in the full document, so unchanged blocks can be cached.
4
+ * Joining the returned blocks always yields the original string.
5
+ *
6
+ * A block ends where a run of blank lines starts, so each block keeps its leading blank
7
+ * lines (indented code needs them). No split happens inside a ``` fence, front matter or
8
+ * multiline markup or link references, or right before a `---` line, which Prism
9
+ * would read as front matter. Whitespace-only lines stay in their block because
10
+ * Prism's inline emphasis can span them.
11
+ * Fences follow Prism's rule, not CommonMark's, so the split matches what Prism sees.
12
+ */
13
+ export declare function splitBlocks(markdown: string): string[];
@@ -0,0 +1,111 @@
1
+ var FENCE_OPEN = /^```/;
2
+ var FENCE_CLOSE = /^```\r?\n?$/;
3
+ var BLANK_LINE = /^[ \t]*\r?\n?$/;
4
+ var FRONT_MATTER_FENCE = /^---\r?\n?$/;
5
+
6
+ /**
7
+ * Splits markdown into blocks that Prism's markdown grammar highlights the same way
8
+ * on their own as it does in the full document, so unchanged blocks can be cached.
9
+ * Joining the returned blocks always yields the original string.
10
+ *
11
+ * A block ends where a run of blank lines starts, so each block keeps its leading blank
12
+ * lines (indented code needs them). No split happens inside a ``` fence, front matter or
13
+ * multiline markup or link references, or right before a `---` line, which Prism
14
+ * would read as front matter. Whitespace-only lines stay in their block because
15
+ * Prism's inline emphasis can span them.
16
+ * Fences follow Prism's rule, not CommonMark's, so the split matches what Prism sees.
17
+ */
18
+ export function splitBlocks(markdown) {
19
+ var lines = markdown.match(/[^\n]*\n|[^\n]+$/g) || [];
20
+ var blocks = [];
21
+ var current = '';
22
+ var pendingSplit = -1;
23
+ var inFence = false;
24
+ var inFrontMatter = /^(?:\s*[\r\n])?---(?:\r?\n|$)/.test(markdown);
25
+ var frontMatterStarted = false;
26
+ var syntax = {
27
+ end: '',
28
+ quote: '',
29
+ brackets: 0,
30
+ subset: 0
31
+ };
32
+ var previousIsBlank = true;
33
+ lines.forEach(line => {
34
+ var isBlank = BLANK_LINE.test(line);
35
+ var canSplit = !inFence && !inFrontMatter && !syntax.end && !syntax.brackets;
36
+ if (/^\r?\n$/.test(line) && !previousIsBlank && canSplit) {
37
+ pendingSplit = current.length;
38
+ }
39
+ if (!isBlank && pendingSplit > 0) {
40
+ if (!FRONT_MATTER_FENCE.test(line)) {
41
+ blocks.push(current.slice(0, pendingSplit));
42
+ current = current.slice(pendingSplit);
43
+ }
44
+ pendingSplit = -1;
45
+ }
46
+ current += line;
47
+ previousIsBlank = isBlank;
48
+ if (inFrontMatter) {
49
+ if (FRONT_MATTER_FENCE.test(line)) {
50
+ inFrontMatter = !frontMatterStarted;
51
+ frontMatterStarted = true;
52
+ }
53
+ return;
54
+ }
55
+ if (inFence) {
56
+ inFence = !FENCE_CLOSE.test(line);
57
+ return;
58
+ }
59
+ if (!syntax.end && FENCE_OPEN.test(line)) {
60
+ inFence = true;
61
+ return;
62
+ }
63
+ updateSyntax(line, syntax);
64
+ });
65
+ if (current) {
66
+ blocks.push(current);
67
+ }
68
+ return blocks;
69
+ }
70
+
71
+ // Keep potential multiline Prism tokens together. Unclosed syntax conservatively
72
+ // keeps the rest of the document in one block until the user closes it.
73
+ function updateSyntax(line, state) {
74
+ var reference = false;
75
+ for (var i = 0; i < line.length; i++) {
76
+ var char = line[i];
77
+ if (state.end && state.end !== '>') {
78
+ var end = line.indexOf(state.end, i);
79
+ if (end === -1) return;
80
+ i = end + state.end.length - 1;
81
+ state.end = '';
82
+ continue;
83
+ }
84
+ if (state.end === '>') {
85
+ if (state.quote) {
86
+ if (char === state.quote) state.quote = '';
87
+ } else if (char === '"' || char === "'") {
88
+ state.quote = char;
89
+ } else if (char === '[') {
90
+ state.subset++;
91
+ } else if (char === ']' && state.subset) {
92
+ state.subset--;
93
+ } else if (char === '>' && !state.subset) {
94
+ state.end = '';
95
+ }
96
+ continue;
97
+ }
98
+ if (char === '\\') {
99
+ i++;
100
+ } else if (char === '<' && /[a-z!?/]/i.test(line[i + 1] || '')) {
101
+ state.end = line.startsWith('<!--', i) ? '-->' : line.startsWith('<![CDATA[', i) ? ']]>' : line.startsWith('<?', i) ? '?>' : '>';
102
+ } else if (char === '[') {
103
+ state.brackets++;
104
+ } else if (char === ']') {
105
+ state.brackets = Math.max(0, state.brackets - 1);
106
+ reference = line[i + 1] === ':';
107
+ } else if (reference && (char === '"' || char === "'" || char === '(')) {
108
+ state.end = char === '(' ? ')' : char;
109
+ }
110
+ }
111
+ }
@@ -0,0 +1 @@
1
+ {"type":"module"}