Skip to content

Commit

Permalink
fix(remark): prevent double pre wrappers for code blocks
Browse files Browse the repository at this point in the history
  • Loading branch information
angrybacon committed Mar 2, 2024
1 parent 2612569 commit 1057f14
Show file tree
Hide file tree
Showing 14 changed files with 73 additions and 59 deletions.
10 changes: 7 additions & 3 deletions src/components/Remark/Remark.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { accordionClasses, Box, tableClasses } from '@mui/material';
import { ReactNode, useEffect, type FunctionComponent } from 'react';
import ReactMarkdown from 'react-markdown';
import Markdown from 'react-markdown';
import remarkDirective from 'remark-directive';
import remarkGfm from 'remark-gfm';
import remarkSlug from 'remark-slug';
Expand Down Expand Up @@ -57,13 +57,17 @@ export const Remark: FunctionComponent<Props> = ({
];

const children: ReactNode = (
<ReactMarkdown
<Markdown
components={{ ...COMPONENTS, ...COMPONENTS_EXTRA }}
// NOTE Prevent double `pre` wrappers.
// https://github.com/remarkjs/react-markdown/issues/820
disallowedElements={['pre']}
remarkPlugins={[...basePlugins, ...customPlugins]}
skipHtml
unwrapDisallowed
>
{text}
</ReactMarkdown>
</Markdown>
);

useEffect(() => {
Expand Down
42 changes: 22 additions & 20 deletions src/components/Remark/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,27 +27,29 @@ import { RemarkTweet } from '@/components/Remark/renderers/RemarkTweet';
import { RemarkYoutube } from '@/components/Remark/renderers/RemarkYoutube';
import { SpoilsCalculator } from '@/components/SpoilsCalculator/SpoilsCalculator';

export const COMPONENTS: Components = {
a: RemarkLink,
export const COMPONENTS: Components =
// prettier-ignore
{
a: RemarkLink,
blockquote: RemarkQuote,
code: RemarkCode,
h1: RemarkHeading,
h2: RemarkHeading,
h3: RemarkHeading,
h4: RemarkHeading,
h5: RemarkHeading,
h6: RemarkHeading,
hr: RemarkDivider,
img: RemarkImage,
ol: RemarkList as Components['ol'],
p: RemarkParagraph,
table: RemarkTable,
tbody: RemarkTableBody,
td: RemarkTableCell,
th: RemarkTableCell,
thead: RemarkTableHead,
tr: RemarkTableRow,
ul: RemarkList as Components['ul'],
code: RemarkCode,
h1: RemarkHeading,
h2: RemarkHeading,
h3: RemarkHeading,
h4: RemarkHeading,
h5: RemarkHeading,
h6: RemarkHeading,
hr: RemarkDivider,
img: RemarkImage,
ol: RemarkList,
p: RemarkParagraph,
table: RemarkTable,
tbody: RemarkTableBody,
td: RemarkTableCell,
th: RemarkTableCell,
thead: RemarkTableHead,
tr: RemarkTableRow,
ul: RemarkList,
} as const;

export const COMPONENTS_EXTRA = {
Expand Down
4 changes: 2 additions & 2 deletions src/components/Remark/renderers/RemarkAccordion.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
} from '@mui/material';
import { alpha } from '@mui/material/styles';
import { type FunctionComponent } from 'react';
import { type Options as ReactMarkdownOptions } from 'react-markdown';
import { type Options as MarkdownOptions } from 'react-markdown';

// eslint-disable-next-line import/no-cycle
import { Remark } from '@/components/Remark/Remark';
Expand All @@ -22,7 +22,7 @@ import { type Partial } from '@/tools/markdown/types';
//
// Remark -> Remark/constants -> RemarkAccordion -> Remark

interface Props extends ReactMarkdownOptions {
interface Props extends MarkdownOptions {
decklists?: Decklists;
partial?: Partial;
}
Expand Down
4 changes: 2 additions & 2 deletions src/components/Remark/renderers/RemarkCard.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { type FunctionComponent } from 'react';
import { type Options as ReactMarkdownOptions } from 'react-markdown';
import { type Options as MarkdownOptions } from 'react-markdown';

import { Link } from '@/components/Link/Link';

const SCRYFALL_SEARCH = 'https://scryfall.com/search';

interface Props extends ReactMarkdownOptions {
interface Props extends MarkdownOptions {
name?: string;
}

Expand Down
35 changes: 21 additions & 14 deletions src/components/Remark/renderers/RemarkCode.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,20 @@
import { Box } from '@mui/material';
import { Box, type PaletteMode } from '@mui/material';
import { alpha, useTheme } from '@mui/material/styles';
import { type Component } from 'react';
import { type Components } from 'react-markdown';
import SyntaxHighlighter from 'react-syntax-highlighter';
import { Prism, type SyntaxHighlighterProps } from 'react-syntax-highlighter';
import {
atomOneDark,
atomOneLight,
} from 'react-syntax-highlighter/dist/cjs/styles/hljs';
oneDark as dark,
oneLight as light,
} from 'react-syntax-highlighter/dist/cjs/styles/prism';

// NOTE They don't support React 18 yet
const SyntaxHighlighter = Prism as typeof Component<SyntaxHighlighterProps>;

const THEMES: Record<PaletteMode, SyntaxHighlighterProps['style']> = {
dark,
light,
};

export const RemarkCode: Components['code'] = ({
children,
Expand Down Expand Up @@ -39,12 +48,16 @@ export const RemarkCode: Components['code'] = ({
);
}

// NOTE ReactMarkdown passes language through the class name
// NOTE Languages are passed down through the class name with `react-markdown`
const [, language] = className.split('-');

return (
<Box
component="div"
component={SyntaxHighlighter}
customStyle={{ borderRadius: undefined, margin: undefined }}
language={language || 'text'}
showLineNumbers
style={THEMES[theme.palette.mode]}
sx={[
({ mixins }) => ({
...mixins.barf,
Expand All @@ -56,13 +69,7 @@ export const RemarkCode: Components['code'] = ({
({ palette }) => ({ borderColor: palette.divider }),
]}
>
<SyntaxHighlighter
language={language}
showLineNumbers
style={theme.palette.mode === 'dark' ? atomOneDark : atomOneLight}
>
{children.trim()}
</SyntaxHighlighter>
{children.trim()}
</Box>
);
};
4 changes: 2 additions & 2 deletions src/components/Remark/renderers/RemarkDecklist.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { type FunctionComponent } from 'react';
import { type Options as ReactMarkdownOptions } from 'react-markdown';
import { type Options as MarkdownOptions } from 'react-markdown';

import { Decklist } from '@/components/Decklist/Decklist';
import {
type DecklistExtra,
type Decklist as DecklistModel,
} from '@/tools/decklists/types';

interface Props extends ReactMarkdownOptions {
interface Props extends MarkdownOptions {
decklist?: DecklistModel & DecklistExtra;
}

Expand Down
5 changes: 3 additions & 2 deletions src/components/Remark/renderers/RemarkList.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import Typography from '@mui/material/Typography';
import { FunctionComponent, PropsWithChildren } from 'react';
import { PropsWithChildren } from 'react';
import { type Components } from 'react-markdown';

// NOTE Typings for Components['ol'] and Components['ul']
interface Props extends PropsWithChildren {
ordered: boolean;
}

export const RemarkList: FunctionComponent<Props> = ({ children, ordered }) => (
export const RemarkList: Components['ul'] = ({ children, ordered }) => (
<Typography component={ordered ? 'ol' : 'ul'}>{children}</Typography>
);
4 changes: 2 additions & 2 deletions src/components/Remark/renderers/RemarkMana.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { type FunctionComponent } from 'react';
import { type Options as ReactMarkdownOptions } from 'react-markdown';
import { type Options as MarkdownOptions } from 'react-markdown';

import { Mana } from '@/components/Mana/Mana';

interface Props extends ReactMarkdownOptions {
interface Props extends MarkdownOptions {
pattern?: string;
}

Expand Down
6 changes: 3 additions & 3 deletions src/components/Remark/renderers/RemarkRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Box } from '@mui/material';
import { alpha, Theme } from '@mui/material/styles';
import { type SystemStyleObject } from '@mui/system';
import { type FunctionComponent } from 'react';
import { type Options as ReactMarkdownOptions } from 'react-markdown';
import { type Options as MarkdownOptions } from 'react-markdown';

import { Card } from '@/components/Card/Card';
import { gutters } from '@/theme/tools/gutters';
Expand Down Expand Up @@ -35,9 +35,9 @@ const variantStyles: Record<
}),
};

interface Props extends ReactMarkdownOptions {
interface Props extends MarkdownOptions {
variant?: string;
node: ReactMarkdownProps['node'] & {
node: MarkdownOptions['node'] & {
properties: {
cards?: { data: ScryCard[]; id?: string }[];
};
Expand Down
4 changes: 2 additions & 2 deletions src/components/Remark/renderers/RemarkSoundcloud.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { Box } from '@mui/material';
import { type FunctionComponent } from 'react';
import { type Options as ReactMarkdownOptions } from 'react-markdown';
import { type Options as MarkdownOptions } from 'react-markdown';

interface Props extends ReactMarkdownOptions {
interface Props extends MarkdownOptions {
url?: string;
}

Expand Down
4 changes: 2 additions & 2 deletions src/components/Remark/renderers/RemarkSpoiler.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { Box } from '@mui/material';
import { useState, type FunctionComponent } from 'react';
import { type Options as ReactMarkdownOptions } from 'react-markdown';
import { type Options as MarkdownOptions } from 'react-markdown';

export const RemarkSpoiler: FunctionComponent<ReactMarkdownOptions> = ({
export const RemarkSpoiler: FunctionComponent<MarkdownOptions> = ({
children,
}) => {
const [isSpoiled, setIsSpoiled] = useState(false);
Expand Down
4 changes: 2 additions & 2 deletions src/components/Remark/renderers/RemarkTweet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Box, CircularProgress, Typography } from '@mui/material';
import Script from 'next/script';
import { FunctionComponent, useRef, useState } from 'react';
import { useInView } from 'react-intersection-observer';
import { Options as ReactMarkdownOptions } from 'react-markdown';
import { Options as MarkdownOptions } from 'react-markdown';

type CreateTweet = (
id: string,
Expand All @@ -16,7 +16,7 @@ declare global {
}
}

interface Props extends ReactMarkdownOptions {
interface Props extends MarkdownOptions {
id?: string;
}

Expand Down
4 changes: 2 additions & 2 deletions src/components/Remark/renderers/RemarkYoutube.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { Box } from '@mui/material';
import { type FunctionComponent } from 'react';
import { type Options as ReactMarkdownOptions } from 'react-markdown';
import { type Options as MarkdownOptions } from 'react-markdown';

interface Props extends ReactMarkdownOptions {
interface Props extends MarkdownOptions {
id?: string;
}

Expand Down
2 changes: 1 addition & 1 deletion src/tools/remark/remarkBase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { Test, visit } from 'unist-util-visit';

/**
* Preliminary visit to mark directives by name for future remarkers as well as
* for `ReactMarkdown`.
* for `react-markdown`.
*/
export const remarkBase: Plugin = () => (tree) => {
const tests = ['containerDirective', 'leafDirective', 'textDirective'];
Expand Down

0 comments on commit 1057f14

Please sign in to comment.