Skip to content

Commit 06714ff

Browse files
Merge pull request #264 from eccenca/feature/upgrade-markdown-libs-CMEM-5567
Upgraded markdown libraries (CMEM-5567)
2 parents 8c549b3 + 13b2286 commit 06714ff

6 files changed

Lines changed: 2476 additions & 4619 deletions

File tree

CHANGELOG.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,13 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p
1818
- `delayDisplayChildren` property: set a time (in ms) to delay the actual rendering of elements inside the actions container. When enabled the containing `OverviewItem` can be displayed faster. Can be used e.g. to boost performance when rendering `OverviewItemActions` with `hiddenInteractions` set to `true`.
1919
- `delaySkeleton` property to set the placeholder/skeleton as long as the delayed display is waiting to get processed
2020
- `intent` property to `Button`, `FieldItem`, `FieldSet`, `Notification`, and `Spinner`
21+
- `<Button />`, `<FieldItem />`, `<FieldSet />`, `<Notification />`, `<Spinner />`
22+
- `intent` property: align intent state usage with other components
23+
24+
### Deprecated
25+
26+
- `<Markdown />`
27+
- `reHypePlugins` property now use `PluggableList` from the unified package. This may require changes if you previously used plugins not conforming to the stricter unified typings. Backward compatibility with the old plugin list type will be removed in the next major version.
2128

2229
### Fixed
2330

@@ -30,6 +37,22 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/) and this p
3037
- `IntentBlueprint`: BlueprintJS intent types, also available by `DefinitionsBlueprint`
3138
- `TableDataContainerProps`, `TableSimpleContainerProps`, `TableHeadProps`, `TableBodyProps`, `TableExpandedRowProps`, `TableHeaderProps` and `DataTableRenderProps` as interfaces for diverse table components
3239

40+
### Usage with old application bundlers
41+
42+
Old bundlers like webpack4 do not support the `exports` field from `package.json`, so it cannot resolve the correct files that need to be imported from the packages if they do not come with alternate configs like `modules` or `main`. Our latest markdown update introduced a few of those packages. So you need to extend your aliases (in webpack4 it is managed in `config.resolve.alias`) like:
43+
44+
```
45+
{
46+
"devlop": "devlop/lib/default.js",
47+
"unist-util-visit-parents/do-not-use-color": "unist-util-visit-parents/lib/color.js",
48+
"vfile/do-not-use-conditional-minpath": "vfile/lib/minpath.browser.js",
49+
"vfile/do-not-use-conditional-minproc": "vfile/lib/minproc.browser.js",
50+
"vfile/do-not-use-conditional-minurl": "vfile/lib/minurl.browser.js",
51+
}
52+
```
53+
54+
If you use Jest then you can use the same aliases for the `moduleNameMapper` config, if necessary.
55+
3356
## [24.1.0] - 2025-04-16
3457

3558
### Added

package.json

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -94,12 +94,14 @@
9494
"react-flow-renderer": "9.7.4",
9595
"react-flow-renderer-lts": "npm:react-flow-renderer@^10.3.17",
9696
"react-inlinesvg": "^3.0.3",
97-
"react-markdown": "^8.0.7",
97+
"react-markdown": "^10.1.0",
98+
"react-markdown-deprecated": "npm:react-markdown@^8.0.7",
9899
"react-syntax-highlighter": "^15.6.1",
99-
"rehype-raw": "^6.1.1",
100-
"remark-definition-list": "^1.2.0",
101-
"remark-gfm": "^3.0.1",
102-
"remark-parse": "^10.0.2",
100+
"rehype-external-links": "^3.0.0",
101+
"rehype-raw": "^7.0.0",
102+
"remark-definition-list": "^2.0.0",
103+
"remark-gfm": "^4.0.1",
104+
"remark-parse": "^11.0.0",
103105
"reset-css": "^5.0.2",
104106
"unified": "^11.0.5",
105107
"wicg-inert": "^3.1.3",
@@ -176,7 +178,8 @@
176178
},
177179
"resolutions": {
178180
"**/@types/react": "^17.0.85",
179-
"node-sass-package-importer/**/postcss": "^8.4.49"
181+
"node-sass-package-importer/**/postcss": "^8.4.49",
182+
"hast-util-from-parse5": "8.0.0"
180183
},
181184
"husky": {
182185
"hooks": {

src/cmem/markdown/Markdown.tsx

Lines changed: 25 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,21 @@
11
import React from "react";
22
import ReactMarkdown from "react-markdown";
3-
import { PluggableList } from "react-markdown/lib/react-markdown";
3+
/**
4+
* Recreate the old type to provide support until next major
5+
*/
6+
import { PluggableList as PluggableListDeprecated } from "react-markdown-deprecated/lib/react-markdown";
47
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
58
// @ts-ignore: No declaration file for module (TODO: should be @ts-expect-error but GUI elements is used inside project with `noImplicitAny=false`)
69
import remarkTypograf from "@mavrin/remark-typograf";
10+
import rehypeExternalLinks from "rehype-external-links";
711
import rehypeRaw from "rehype-raw";
812
import { remarkDefinitionList } from "remark-definition-list";
913
import remarkGfm from "remark-gfm";
14+
import { PluggableList as PluggableListUnified } from "unified";
1015

1116
import { CLASSPREFIX as eccgui } from "../../configuration/constants";
1217
import { HtmlContentBlock, HtmlContentBlockProps, TestableComponent } from "../../index";
18+
type PluggableList = PluggableListUnified | PluggableListDeprecated;
1319

1420
export interface MarkdownProps extends TestableComponent {
1521
children: string;
@@ -35,6 +41,7 @@ export interface MarkdownProps extends TestableComponent {
3541
/**
3642
* Additional reHype plugins to execute.
3743
* @see https://github.com/remarkjs/react-markdown#architecture
44+
* @deprecated (v25) this property won't support `PluggableList` from "react-markdown/lib/react-markdown" with the next major version, only the one from `unified` will be supported then.
3845
*/
3946
reHypePlugins?: PluggableList;
4047
/**
@@ -54,9 +61,9 @@ const configDefault = {
5461
@see https://github.com/remarkjs/react-markdown#api
5562
*/
5663
// @see https://github.com/remarkjs/remark/blob/main/doc/plugins.md#list-of-plugins
57-
remarkPlugins: [remarkGfm, remarkTypograf, remarkDefinitionList] as PluggableList,
64+
remarkPlugins: [remarkGfm, remarkTypograf, remarkDefinitionList] as PluggableListUnified,
5865
// @see https://github.com/rehypejs/rehype/blob/main/doc/plugins.md#list-of-plugins
59-
rehypePlugins: [] as PluggableList,
66+
rehypePlugins: [] as PluggableListUnified,
6067
allowedElements: [
6168
// default markdown
6269
"a",
@@ -110,6 +117,13 @@ export const Markdown = ({
110117
htmlContentBlockProps,
111118
...otherProps
112119
}: MarkdownProps) => {
120+
const configHtmlExternalLinks = {
121+
rel: ["nofollow"],
122+
target: linkTargetName,
123+
};
124+
125+
configDefault.rehypePlugins = configDefault.rehypePlugins.concat([[rehypeExternalLinks, configHtmlExternalLinks]]);
126+
113127
const configHtml = allowHtml
114128
? {
115129
rehypePlugins: [...configDefault.rehypePlugins].concat([rehypeRaw]),
@@ -132,14 +146,10 @@ export const Markdown = ({
132146
...configDefault,
133147
...configHtml,
134148
...configTextOnly,
135-
linkTarget: linkTargetName
136-
? (href: string, _children: any, _title: string) => {
137-
const linkTarget = href.charAt(0) !== "#" ? linkTargetName : "";
138-
return linkTarget as React.HTMLAttributeAnchorTarget;
139-
}
140-
: undefined,
141149
components: {
150+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
142151
code(props: any) {
152+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
143153
const { children, className, node, inline, ...rest } = props;
144154
const match = /language-(\w+)/.exec(className || "");
145155
return match ? (
@@ -159,14 +169,15 @@ export const Markdown = ({
159169
);
160170
},
161171
},
172+
allowedElements,
162173
};
163-
allowedElements && (reactMarkdownProperties.allowedElements = allowedElements);
164-
reHypePlugins &&
165-
reHypePlugins.forEach(
166-
(plugin) => (reactMarkdownProperties.rehypePlugins = [...reactMarkdownProperties.rehypePlugins, plugin])
174+
175+
if (reHypePlugins) {
176+
reactMarkdownProperties.rehypePlugins = reactMarkdownProperties.rehypePlugins.concat(
177+
reHypePlugins as PluggableListUnified
167178
);
179+
}
168180

169-
// @ts-ignore because against the lib spec it does not allow a function for linkTarget.
170181
const markdownDisplay = <ReactMarkdown {...reactMarkdownProperties} />;
171182
return inheritBlock && !(otherProps["data-test-id"] || htmlContentBlockProps) ? (
172183
markdownDisplay

src/cmem/markdown/highlightSearchWords.test.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,20 +9,26 @@ describe("Highlight search words reHype plugin", () => {
99
const highlightSearchWordsPlugin = markdownUtils.highlightSearchWordsPluginFactory(searchQuery);
1010
const highlightSearchWordTransformer = highlightSearchWordsPlugin();
1111
const textNode = (text: string): Text => ({ type: "text", value: text });
12-
const markNode = (text: string): Element => ({ type: "element", tagName: "mark", children: [textNode(text)] });
12+
const markNode = (text: string): Element => ({
13+
type: "element",
14+
tagName: "mark",
15+
properties: {},
16+
children: [textNode(text)],
17+
});
1318
const result = highlightSearchWordTransformer(
1419
{
1520
type: "root",
1621
children: [
1722
{
1823
type: "element",
1924
tagName: "p",
25+
properties: {},
2026
children: [textNode("Text with abc query words xyz.")],
2127
},
2228
],
2329
},
2430
new VFile(),
25-
// eslint-disable-next-line @typescript-eslint/no-empty-function
31+
2632
() => {}
2733
);
2834
const rootChildren = (result as Root).children;

src/cmem/markdown/highlightSearchWords.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,12 @@ const highlightSearchWordsPluginFactory = (searchQuery: string | undefined) => {
2525
let matchArray = multiWordRegex.exec(text);
2626
while (matchArray !== null) {
2727
result.push(createTextNode(text.slice(offset, matchArray.index)));
28-
result.push({ type: "element", tagName: "mark", children: [createTextNode(matchArray[0])] });
28+
result.push({
29+
type: "element",
30+
tagName: "mark",
31+
properties: {},
32+
children: [createTextNode(matchArray[0])],
33+
});
2934
offset = multiWordRegex.lastIndex;
3035
matchArray = multiWordRegex.exec(text);
3136
}

0 commit comments

Comments
 (0)