From 2836e4735d8dc7a83003d9652588881903dc6195 Mon Sep 17 00:00:00 2001
From: jaywcjlove <398188662@qq.com>
Date: Wed, 1 Nov 2023 12:19:56 +0800
Subject: [PATCH] feat: add no highlight component.
https://github.com/uiwjs/react-md-editor/issues/586
---
core/README.md | 61 ++++++++++++++++++++
core/nohighlight.d.ts | 32 +++++++++++
core/package.json | 14 +++++
core/src/index.tsx | 115 +++----------------------------------
core/src/nohighlight.tsx | 18 ++++++
core/src/preview.tsx | 83 ++++++++++++++++++++++++++
core/src/rehypePlugins.tsx | 27 +++++++++
website/package.json | 6 +-
website/src/index.tsx | 3 +-
9 files changed, 250 insertions(+), 109 deletions(-)
create mode 100644 core/nohighlight.d.ts
create mode 100644 core/src/nohighlight.tsx
create mode 100644 core/src/preview.tsx
create mode 100644 core/src/rehypePlugins.tsx
diff --git a/core/README.md b/core/README.md
index f2bdd948..1838e4b2 100644
--- a/core/README.md
+++ b/core/README.md
@@ -147,6 +147,67 @@ export default function Demo() {
}
```
+## Code Highlight
+
+```jsx mdx:preview
+import React from 'react';
+import MarkdownPreview from '@uiw/react-markdown-preview';
+
+const source = `
+\`\`\`js
+function () {
+ console.log('hello hello hello hello hello hello hello hello hello hello hello hello hello hello hello hello')
+}
+\`\`\`
+\`\`\`js
+function () {
+ console.log('hello ')
+}
+\`\`\`
+`;
+
+export default function Demo() {
+ return (
+
+ );
+}
+```
+
+## Remove Code Highlight
+
+The following example can help you _exclude code highlighting code_ from being included in the bundle. `@uiw/react-markdown-preview/nohighlight` component does not contain the `rehype-prism-plus` code highlighting package, `showLineNumbers` and `highlight line` functions will no longer work. ([#586](https://github.com/uiwjs/react-md-editor/issues/586))
+
+```jsx mdx:preview
+import React from 'react';
+import MarkdownPreview from '@uiw/react-markdown-preview/nohighlight';
+
+const source = `
+\`\`\`js showLineNumbers
+function () {
+ console.log('hello hello hello hello hello hello hello hello hello hello hello hello hello hello hello hello')
+}
+\`\`\`
+\`\`\`js showLineNumbers {2}
+function () {
+ console.log('hello ')
+}
+\`\`\`
+`;
+
+export default function Demo() {
+ return (
+ {
+ if (node.tagName === "a" && parent && /^h(1|2|3|4|5|6)/.test(parent.tagName)) {
+ parent.children = parent.children.slice(1)
+ }
+ }}
+ />
+ );
+}
+```
+
## Ignore
Ignore content display via HTML comments, Shown in GitHub readme, excluded in HTML.
diff --git a/core/nohighlight.d.ts b/core/nohighlight.d.ts
new file mode 100644
index 00000000..a6a937a2
--- /dev/null
+++ b/core/nohighlight.d.ts
@@ -0,0 +1,32 @@
+declare module '@uiw/react-markdown-preview/nohighlight' {
+ import React from 'react';
+ import { Options } from 'react-markdown';
+ import { PluggableList } from 'unified';
+ import { RehypeRewriteOptions } from 'rehype-rewrite';
+ export interface MarkdownPreviewProps extends Omit {
+ prefixCls?: string;
+ className?: string;
+ source?: string;
+ disableCopy?: boolean;
+ style?: React.CSSProperties;
+ pluginsFilter?: (type: 'rehype' | 'remark', plugin: PluggableList) => PluggableList;
+ wrapperElement?: React.DetailedHTMLProps, HTMLDivElement> & {
+ 'data-color-mode'?: 'light' | 'dark';
+ };
+ /**
+ * Please use wrapperElement, Will be removed in v5 release.
+ * @deprecated
+ */
+ warpperElement?: React.DetailedHTMLProps, HTMLDivElement> & {
+ 'data-color-mode'?: 'light' | 'dark';
+ };
+ onScroll?: (e: React.UIEvent) => void;
+ onMouseOver?: (e: React.MouseEvent) => void;
+ rehypeRewrite?: RehypeRewriteOptions['rewrite'];
+ }
+ export interface MarkdownPreviewRef extends MarkdownPreviewProps {
+ mdp: React.RefObject;
+ }
+ const _default: React.ForwardRefExoticComponent>;
+ export default _default;
+}
diff --git a/core/package.json b/core/package.json
index faecb6da..37b511f4 100644
--- a/core/package.json
+++ b/core/package.json
@@ -5,6 +5,20 @@
"homepage": "https://uiwjs.github.io/react-markdown-preview",
"main": "lib/index.js",
"module": "esm/index.js",
+ "exports": {
+ "./README.md": "./README.md",
+ "./package.json": "./package.json",
+ ".": {
+ "import": "./esm/index.js",
+ "types": "./lib/index.d.ts",
+ "require": "./lib/index.js"
+ },
+ "./nohighlight": {
+ "import": "./esm/nohighlight.js",
+ "types": "./lib/nohighlight.d.ts",
+ "require": "./lib/nohighlight.js"
+ }
+ },
"scripts": {
"css:build": "compile-less -d src -o esm",
"css:watch": "compile-less -d src -o esm --watch",
diff --git a/core/src/index.tsx b/core/src/index.tsx
index 51b9a762..5eecf20c 100644
--- a/core/src/index.tsx
+++ b/core/src/index.tsx
@@ -1,117 +1,20 @@
-import React, { useImperativeHandle } from 'react';
-import ReactMarkdown, { Options } from 'react-markdown';
-import { Element } from 'hast';
+import React from 'react';
+import MarkdownPreview, { type MarkdownPreviewProps, type MarkdownPreviewRef } from './preview';
+import rehypePrism from 'rehype-prism-plus';
import { PluggableList } from 'unified';
-import gfm from 'remark-gfm';
-import raw from 'rehype-raw';
-import slug from 'rehype-slug';
-import headings from 'rehype-autolink-headings';
+import rehypeRewrite from 'rehype-rewrite';
import rehypeAttrs from 'rehype-attr';
-import rehypeIgnore from 'rehype-ignore';
-import rehypePrism from 'rehype-prism-plus';
-import rehypeRewrite, { getCodeString, RehypeRewriteOptions } from 'rehype-rewrite';
-import { octiconLink } from './nodes/octiconLink';
-import { copyElement } from './nodes/copy';
-import { useCopied } from './plugins/useCopied';
-import './styles/markdown.less';
-
import { reservedMeta } from './plugins/reservedMeta';
-
-export interface MarkdownPreviewProps extends Omit {
- prefixCls?: string;
- className?: string;
- source?: string;
- disableCopy?: boolean;
- style?: React.CSSProperties;
- pluginsFilter?: (type: 'rehype' | 'remark', plugin: PluggableList) => PluggableList;
- wrapperElement?: React.DetailedHTMLProps, HTMLDivElement> & {
- 'data-color-mode'?: 'light' | 'dark';
- };
- /**
- * Please use wrapperElement, Will be removed in v5 release.
- * @deprecated
- */
- warpperElement?: React.DetailedHTMLProps, HTMLDivElement> & {
- 'data-color-mode'?: 'light' | 'dark';
- };
- onScroll?: (e: React.UIEvent) => void;
- onMouseOver?: (e: React.MouseEvent) => void;
- rehypeRewrite?: RehypeRewriteOptions['rewrite'];
-}
-
-export interface MarkdownPreviewRef extends MarkdownPreviewProps {
- mdp: React.RefObject;
-}
+import { rehypeRewriteHandle, defaultRehypePlugins } from './rehypePlugins';
export default React.forwardRef((props, ref) => {
- const {
- prefixCls = 'wmde-markdown wmde-markdown-color',
- className,
- source,
- style,
- disableCopy = false,
- skipHtml = true,
- onScroll,
- onMouseOver,
- pluginsFilter,
- rehypeRewrite: rewrite,
- wrapperElement = {},
- warpperElement = {},
- ...other
- } = props;
- const mdp = React.useRef(null);
- useImperativeHandle(ref, () => ({ ...props, mdp }), [mdp, props]);
- const cls = `${prefixCls || ''} ${className || ''}`;
- useCopied(mdp);
-
- const rehypeRewriteHandle: RehypeRewriteOptions['rewrite'] = (node, index, parent) => {
- if (node.type === 'element' && parent && parent.type === 'root' && /h(1|2|3|4|5|6)/.test(node.tagName)) {
- const child = node.children && (node.children[0] as Element);
- if (child && child.properties && child.properties.ariaHidden === 'true') {
- child.properties = { class: 'anchor', ...child.properties };
- child.children = [octiconLink];
- }
- }
- if (node.type === 'element' && node.tagName === 'pre' && !disableCopy) {
- const code = getCodeString(node.children);
- node.children.push(copyElement(code));
- }
- rewrite && rewrite(node, index, parent);
- };
-
const rehypePlugins: PluggableList = [
reservedMeta,
[rehypePrism, { ignoreMissing: true }],
- slug,
- headings,
- rehypeIgnore,
- [rehypeRewrite, { rewrite: rehypeRewriteHandle }],
+ ...defaultRehypePlugins,
+ [rehypeRewrite, { rewrite: rehypeRewriteHandle(props.disableCopy ?? false, props.rehypeRewrite) }],
[rehypeAttrs, { properties: 'attr' }],
- ...(other.rehypePlugins || []),
+ ...(props.rehypePlugins || []),
];
- const customProps: MarkdownPreviewProps = {
- allowElement: (element, index, parent) => {
- if (other.allowElement) {
- return other.allowElement(element, index, parent);
- }
- return /^[A-Za-z0-9]+$/.test(element.tagName);
- },
- };
- if (skipHtml) {
- rehypePlugins.push(raw);
- }
- const remarkPlugins = [...(other.remarkPlugins || []), gfm];
- const wrapperProps = { ...warpperElement, ...wrapperElement };
- return (
-
-
-
- );
+ return ;
});
diff --git a/core/src/nohighlight.tsx b/core/src/nohighlight.tsx
new file mode 100644
index 00000000..dba8bd47
--- /dev/null
+++ b/core/src/nohighlight.tsx
@@ -0,0 +1,18 @@
+import React from 'react';
+import MarkdownPreview, { type MarkdownPreviewProps, type MarkdownPreviewRef } from './preview';
+import { PluggableList } from 'unified';
+import rehypeRewrite from 'rehype-rewrite';
+import { reservedMeta } from './plugins/reservedMeta';
+import rehypeAttrs from 'rehype-attr';
+import { rehypeRewriteHandle, defaultRehypePlugins } from './rehypePlugins';
+
+export default React.forwardRef((props, ref) => {
+ const rehypePlugins: PluggableList = [
+ reservedMeta,
+ ...defaultRehypePlugins,
+ [rehypeRewrite, { rewrite: rehypeRewriteHandle(props.disableCopy ?? false, props.rehypeRewrite) }],
+ [rehypeAttrs, { properties: 'attr' }],
+ ...(props.rehypePlugins || []),
+ ];
+ return ;
+});
diff --git a/core/src/preview.tsx b/core/src/preview.tsx
new file mode 100644
index 00000000..4d406464
--- /dev/null
+++ b/core/src/preview.tsx
@@ -0,0 +1,83 @@
+import React, { useImperativeHandle } from 'react';
+import ReactMarkdown, { Options } from 'react-markdown';
+import { PluggableList } from 'unified';
+import gfm from 'remark-gfm';
+import raw from 'rehype-raw';
+import { type RehypeRewriteOptions } from 'rehype-rewrite';
+import { useCopied } from './plugins/useCopied';
+import './styles/markdown.less';
+
+export interface MarkdownPreviewProps extends Omit {
+ prefixCls?: string;
+ className?: string;
+ source?: string;
+ disableCopy?: boolean;
+ style?: React.CSSProperties;
+ pluginsFilter?: (type: 'rehype' | 'remark', plugin: PluggableList) => PluggableList;
+ wrapperElement?: React.DetailedHTMLProps, HTMLDivElement> & {
+ 'data-color-mode'?: 'light' | 'dark';
+ };
+ /**
+ * Please use wrapperElement, Will be removed in v5 release.
+ * @deprecated
+ */
+ warpperElement?: React.DetailedHTMLProps, HTMLDivElement> & {
+ 'data-color-mode'?: 'light' | 'dark';
+ };
+ onScroll?: (e: React.UIEvent) => void;
+ onMouseOver?: (e: React.MouseEvent) => void;
+ rehypeRewrite?: RehypeRewriteOptions['rewrite'];
+}
+
+export interface MarkdownPreviewRef extends MarkdownPreviewProps {
+ mdp: React.RefObject;
+}
+
+export default React.forwardRef((props, ref) => {
+ const {
+ prefixCls = 'wmde-markdown wmde-markdown-color',
+ className,
+ source,
+ style,
+ disableCopy = false,
+ skipHtml = true,
+ onScroll,
+ onMouseOver,
+ pluginsFilter,
+ rehypeRewrite: rewrite,
+ wrapperElement = {},
+ warpperElement = {},
+ ...other
+ } = props;
+ const mdp = React.useRef(null);
+ useImperativeHandle(ref, () => ({ ...props, mdp }), [mdp, props]);
+ const cls = `${prefixCls || ''} ${className || ''}`;
+ useCopied(mdp);
+
+ const rehypePlugins: PluggableList = [...(other.rehypePlugins || [])];
+ const customProps: MarkdownPreviewProps = {
+ allowElement: (element, index, parent) => {
+ if (other.allowElement) {
+ return other.allowElement(element, index, parent);
+ }
+ return /^[A-Za-z0-9]+$/.test(element.tagName);
+ },
+ };
+ if (skipHtml) {
+ rehypePlugins.push(raw);
+ }
+ const remarkPlugins = [...(other.remarkPlugins || []), gfm];
+ const wrapperProps = { ...warpperElement, ...wrapperElement };
+ return (
+
+
+
+ );
+});
diff --git a/core/src/rehypePlugins.tsx b/core/src/rehypePlugins.tsx
new file mode 100644
index 00000000..6e2c6d21
--- /dev/null
+++ b/core/src/rehypePlugins.tsx
@@ -0,0 +1,27 @@
+import { PluggableList } from 'unified';
+import slug from 'rehype-slug';
+import headings from 'rehype-autolink-headings';
+import rehypeIgnore from 'rehype-ignore';
+import { getCodeString, RehypeRewriteOptions } from 'rehype-rewrite';
+import { octiconLink } from './nodes/octiconLink';
+import { copyElement } from './nodes/copy';
+import { Root, Element, RootContent } from 'hast';
+
+export const rehypeRewriteHandle =
+ (disableCopy: boolean, rewrite?: RehypeRewriteOptions['rewrite']) =>
+ (node: Root | RootContent, index: number | null, parent: Root | Element | null) => {
+ if (node.type === 'element' && parent && parent.type === 'root' && /h(1|2|3|4|5|6)/.test(node.tagName)) {
+ const child = node.children && (node.children[0] as Element);
+ if (child && child.properties && child.properties.ariaHidden === 'true') {
+ child.properties = { class: 'anchor', ...child.properties };
+ child.children = [octiconLink];
+ }
+ }
+ if (node.type === 'element' && node.tagName === 'pre' && !disableCopy) {
+ const code = getCodeString(node.children);
+ node.children.push(copyElement(code));
+ }
+ rewrite && rewrite(node, index, parent);
+ };
+
+export const defaultRehypePlugins: PluggableList = [slug, headings, rehypeIgnore];
diff --git a/website/package.json b/website/package.json
index 1a53280e..659260bd 100644
--- a/website/package.json
+++ b/website/package.json
@@ -4,7 +4,8 @@
"private": true,
"scripts": {
"build": "kkt build",
- "start": "kkt start"
+ "start": "kkt start",
+ "map": "source-map-explorer build/static/js/*.js --html build/website-result.html"
},
"dependencies": {
"@uiw/react-markdown-preview-example": "^1.5.5",
@@ -21,7 +22,8 @@
"markdown-react-code-preview-loader": "^2.1.5",
"prettier": "^2.8.4",
"pretty-quick": "^3.1.3",
- "react-test-renderer": "^18.2.0"
+ "react-test-renderer": "^18.2.0",
+ "source-map-explorer": "~2.5.2"
},
"eslintConfig": {
"extends": "react-app"
diff --git a/website/src/index.tsx b/website/src/index.tsx
index 3fbec8a9..83265db2 100644
--- a/website/src/index.tsx
+++ b/website/src/index.tsx
@@ -1,6 +1,7 @@
import { createRoot } from 'react-dom/client';
import MarkdownPreviewExample from '@uiw/react-markdown-preview-example';
import data from '@uiw/react-markdown-preview/README.md';
+import pkg from '@uiw/react-markdown-preview/package.json';
import { Footer, Example } from './App';
const container = document.getElementById('root');
@@ -17,7 +18,7 @@ root.render(
}
description="React component preview markdown text in web browser. The minimal amount of CSS to replicate the GitHub
Markdown style."
- version={`v${VERSION}`}
+ version={`v${pkg.version}`}
>