diff --git a/src/mdx-components.tsx b/src/mdx-components.tsx index 2186c4997..3409981ba 100644 --- a/src/mdx-components.tsx +++ b/src/mdx-components.tsx @@ -1,8 +1,18 @@ import type { MDXComponents } from "mdx/types"; -import React, { ReactNode } from "react"; +import React from "react"; import { CodeExample } from "./components/code-example"; import Link from "next/link"; +declare module "mdx/types" { + // Augment the MDX types to make it understand React. + namespace JSX { + type Element = React.JSX.Element; + type ElementClass = React.JSX.ElementClass; + type ElementType = React.JSX.ElementType; + type IntrinsicElements = React.JSX.IntrinsicElements; + } +} + function getTextContent(node: React.ReactNode): string { if (typeof node === "string" || typeof node === "number") { return String(node); @@ -51,64 +61,70 @@ function createHeading(level: 1 | 2 | 3 | 4 | 5 | 6) { }; } +const components = { + // Allows customizing built-in components, e.g. to add styling. + // h1: ({ children }) =>

{children}

, + + h2: createHeading(2), + h3: createHeading(3), + h4: createHeading(4), + h5: createHeading(5), + h6: createHeading(6), + + a(props) { + return )} />; + }, + + code({ children }) { + if (typeof children !== "string") { + return {children}; + } + + if (children.startsWith("<")) { + return {children}; + } + + return ( + + {children + .split(/(<[^>]+>)/g) + .map((part, i) => (part.startsWith("<") && part.endsWith(">") ? {part} : part))} + + ); + }, + + pre(props) { + let child = React.Children.only(props.children) as React.ReactElement; + if (!child) return null; + + // @ts-ignore + let { className, children: code } = child.props; + let lang = className ? className.replace("language-", "") : ""; + let filename = undefined; + + // Extract `[!code filename:…]` directives from the first line of code + let lines = code.split("\n"); + let filenameRegex = /\[\!code filename\:(.+)\]/; + let match = lines[0].match(filenameRegex); + if (match) { + filename = match[1]; + code = lines.splice(1).join("\n"); + } + + return ( +
+ +
+ ); + }, +} satisfies MDXComponents; + +declare global { + // Provide type-safety of provided components inside MDX files. + type MDXProvidedComponents = typeof components; +} + // This file is required to use MDX in `app` directory. -export function useMDXComponents(components: MDXComponents): MDXComponents { - return { - // Allows customizing built-in components, e.g. to add styling. - // h1: ({ children }) =>

{children}

, - ...components, - - h2: createHeading(2), - h3: createHeading(3), - h4: createHeading(4), - h5: createHeading(5), - h6: createHeading(6), - - a(props: any) { - return ; - }, - - code({ children }: { children: string | ReactNode }) { - if (typeof children !== "string") { - return {children}; - } - - if (children.startsWith("<")) { - return {children}; - } - - return ( - - {children - .split(/(<[^>]+>)/g) - .map((part, i) => (part.startsWith("<") && part.endsWith(">") ? {part} : part))} - - ); - }, - - pre(props) { - let child = React.Children.only(props.children) as React.ReactElement; - if (!child) return null; - - // @ts-ignore - let { className, children: code } = child.props; - let lang = className ? className.replace("language-", "") : ""; - let filename = undefined; - - // Extract `[!code filename:…]` directives from the first line of code - let lines = code.split("\n"); - let filenameRegex = /\[\!code filename\:(.+)\]/; - let match = lines[0].match(filenameRegex); - if (match) { - filename = match[1]; - code = lines.splice(1).join("\n"); - } - - return ( -
- -
- ); - }, - }; +export function useMDXComponents(): MDXProvidedComponents { + return components; } diff --git a/tsconfig.json b/tsconfig.json index c1334095f..886a8f675 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -22,6 +22,6 @@ "@/*": ["./src/*"] } }, - "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], + "include": ["next-env.d.ts", "**/*.mdx", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], "exclude": ["node_modules"] }