|
1 | 1 | import type { MDXComponents } from "mdx/types";
|
2 |
| -import React, { ReactNode } from "react"; |
| 2 | +import React from "react"; |
3 | 3 | import { CodeExample } from "./components/code-example";
|
4 | 4 | import Link from "next/link";
|
5 | 5 |
|
| 6 | +declare module "mdx/types" { |
| 7 | + // Augment the MDX types to make it understand React. |
| 8 | + namespace JSX { |
| 9 | + type Element = React.JSX.Element; |
| 10 | + type ElementClass = React.JSX.ElementClass; |
| 11 | + type ElementType = React.JSX.ElementType; |
| 12 | + type IntrinsicElements = React.JSX.IntrinsicElements; |
| 13 | + } |
| 14 | +} |
| 15 | + |
6 | 16 | function getTextContent(node: React.ReactNode): string {
|
7 | 17 | if (typeof node === "string" || typeof node === "number") {
|
8 | 18 | return String(node);
|
@@ -51,64 +61,70 @@ function createHeading(level: 1 | 2 | 3 | 4 | 5 | 6) {
|
51 | 61 | };
|
52 | 62 | }
|
53 | 63 |
|
| 64 | +const components = { |
| 65 | + // Allows customizing built-in components, e.g. to add styling. |
| 66 | + // h1: ({ children }) => <h1 style={{ fontSize: "100px" }}>{children}</h1>, |
| 67 | + |
| 68 | + h2: createHeading(2), |
| 69 | + h3: createHeading(3), |
| 70 | + h4: createHeading(4), |
| 71 | + h5: createHeading(5), |
| 72 | + h6: createHeading(6), |
| 73 | + |
| 74 | + a(props) { |
| 75 | + return <Link {...(props as React.ComponentProps<typeof Link>)} />; |
| 76 | + }, |
| 77 | + |
| 78 | + code({ children }) { |
| 79 | + if (typeof children !== "string") { |
| 80 | + return <code>{children}</code>; |
| 81 | + } |
| 82 | + |
| 83 | + if (children.startsWith("<")) { |
| 84 | + return <code>{children}</code>; |
| 85 | + } |
| 86 | + |
| 87 | + return ( |
| 88 | + <code> |
| 89 | + {children |
| 90 | + .split(/(<[^>]+>)/g) |
| 91 | + .map((part, i) => (part.startsWith("<") && part.endsWith(">") ? <var key={i}>{part}</var> : part))} |
| 92 | + </code> |
| 93 | + ); |
| 94 | + }, |
| 95 | + |
| 96 | + pre(props) { |
| 97 | + let child = React.Children.only(props.children) as React.ReactElement; |
| 98 | + if (!child) return null; |
| 99 | + |
| 100 | + // @ts-ignore |
| 101 | + let { className, children: code } = child.props; |
| 102 | + let lang = className ? className.replace("language-", "") : ""; |
| 103 | + let filename = undefined; |
| 104 | + |
| 105 | + // Extract `[!code filename:…]` directives from the first line of code |
| 106 | + let lines = code.split("\n"); |
| 107 | + let filenameRegex = /\[\!code filename\:(.+)\]/; |
| 108 | + let match = lines[0].match(filenameRegex); |
| 109 | + if (match) { |
| 110 | + filename = match[1]; |
| 111 | + code = lines.splice(1).join("\n"); |
| 112 | + } |
| 113 | + |
| 114 | + return ( |
| 115 | + <div> |
| 116 | + <CodeExample example={{ lang, code }} className="not-prose" filename={filename} /> |
| 117 | + </div> |
| 118 | + ); |
| 119 | + }, |
| 120 | +} satisfies MDXComponents; |
| 121 | + |
| 122 | +declare global { |
| 123 | + // Provide type-safety of provided components inside MDX files. |
| 124 | + type MDXProvidedComponents = typeof components; |
| 125 | +} |
| 126 | + |
54 | 127 | // This file is required to use MDX in `app` directory.
|
55 |
| -export function useMDXComponents(components: MDXComponents): MDXComponents { |
56 |
| - return { |
57 |
| - // Allows customizing built-in components, e.g. to add styling. |
58 |
| - // h1: ({ children }) => <h1 style={{ fontSize: "100px" }}>{children}</h1>, |
59 |
| - ...components, |
60 |
| - |
61 |
| - h2: createHeading(2), |
62 |
| - h3: createHeading(3), |
63 |
| - h4: createHeading(4), |
64 |
| - h5: createHeading(5), |
65 |
| - h6: createHeading(6), |
66 |
| - |
67 |
| - a(props: any) { |
68 |
| - return <Link {...props} />; |
69 |
| - }, |
70 |
| - |
71 |
| - code({ children }: { children: string | ReactNode }) { |
72 |
| - if (typeof children !== "string") { |
73 |
| - return <code>{children}</code>; |
74 |
| - } |
75 |
| - |
76 |
| - if (children.startsWith("<")) { |
77 |
| - return <code>{children}</code>; |
78 |
| - } |
79 |
| - |
80 |
| - return ( |
81 |
| - <code> |
82 |
| - {children |
83 |
| - .split(/(<[^>]+>)/g) |
84 |
| - .map((part, i) => (part.startsWith("<") && part.endsWith(">") ? <var key={i}>{part}</var> : part))} |
85 |
| - </code> |
86 |
| - ); |
87 |
| - }, |
88 |
| - |
89 |
| - pre(props) { |
90 |
| - let child = React.Children.only(props.children) as React.ReactElement; |
91 |
| - if (!child) return null; |
92 |
| - |
93 |
| - // @ts-ignore |
94 |
| - let { className, children: code } = child.props; |
95 |
| - let lang = className ? className.replace("language-", "") : ""; |
96 |
| - let filename = undefined; |
97 |
| - |
98 |
| - // Extract `[!code filename:…]` directives from the first line of code |
99 |
| - let lines = code.split("\n"); |
100 |
| - let filenameRegex = /\[\!code filename\:(.+)\]/; |
101 |
| - let match = lines[0].match(filenameRegex); |
102 |
| - if (match) { |
103 |
| - filename = match[1]; |
104 |
| - code = lines.splice(1).join("\n"); |
105 |
| - } |
106 |
| - |
107 |
| - return ( |
108 |
| - <div> |
109 |
| - <CodeExample example={{ lang, code }} className="not-prose" filename={filename} /> |
110 |
| - </div> |
111 |
| - ); |
112 |
| - }, |
113 |
| - }; |
| 128 | +export function useMDXComponents(): MDXProvidedComponents { |
| 129 | + return components; |
114 | 130 | }
|
0 commit comments