diff --git a/beta/package.json b/beta/package.json index 57e9d23d..f38255fc 100644 --- a/beta/package.json +++ b/beta/package.json @@ -26,7 +26,6 @@ "@docsearch/css": "3.0.0-alpha.41", "@docsearch/react": "3.0.0-alpha.41", "@headlessui/react": "^1.3.0", - "@mdx-js/react": "^1.6.16", "body-scroll-lock": "^3.1.3", "classnames": "^2.2.6", "date-fns": "^2.16.1", diff --git a/beta/src/components/Layout/MarkdownPage.tsx b/beta/src/components/Layout/MarkdownPage.tsx index 937641fa..a02e254d 100644 --- a/beta/src/components/Layout/MarkdownPage.tsx +++ b/beta/src/components/Layout/MarkdownPage.tsx @@ -3,8 +3,6 @@ */ import * as React from 'react'; -// @ts-ignore -import {MDXContext} from '@mdx-js/react'; import {DocsPageFooter} from 'components/DocsFooter'; import {MDXComponents} from 'components/MDX/MDXComponents'; import {Seo} from 'components/Seo'; @@ -45,11 +43,7 @@ export function MarkdownPage< /> )}
-
- - {children} - -
+
{children}
= {}; let content: React.ReactElement[] = []; React.Children.forEach(children, (child) => { - const {props} = child; - switch (props.mdxType) { + const {props, type} = child; + switch ((type as any).mdxName) { case 'Solution': { challenge.solution = child; challenge.content = content; diff --git a/beta/src/components/MDX/CodeDiagram.tsx b/beta/src/components/MDX/CodeDiagram.tsx index d6ad640d..26a44333 100644 --- a/beta/src/components/MDX/CodeDiagram.tsx +++ b/beta/src/components/MDX/CodeDiagram.tsx @@ -12,10 +12,10 @@ interface CodeDiagramProps { export function CodeDiagram({children, flip = false}: CodeDiagramProps) { const illustration = React.Children.toArray(children).filter((child: any) => { - return child.props?.mdxType === 'img'; + return child.type?.mdxName === 'img'; }); const content = React.Children.toArray(children).map((child: any) => { - if (child.props?.mdxType === 'pre') { + if (child.type?.mdxName === 'pre') { return ( ); - } else if (child.props?.mdxType === 'img') { + } else if (child.type?.mdxName === 'img') { return null; } else { return child; diff --git a/beta/src/components/MDX/Heading.tsx b/beta/src/components/MDX/Heading.tsx index 6e29bc4e..1a337f88 100644 --- a/beta/src/components/MDX/Heading.tsx +++ b/beta/src/components/MDX/Heading.tsx @@ -71,6 +71,7 @@ export const H2 = ({className, ...props}: HeadingProps) => ( {...props} /> ); + export const H3 = ({className, ...props}: HeadingProps) => ( { - if (child.props?.mdxType && child.props?.mdxType === 'inlineCode') { + if (child.type?.mdxName && child.type?.mdxName === 'inlineCode') { return React.cloneElement(child, { isLink: true, }); diff --git a/beta/src/components/MDX/MDXComponents.tsx b/beta/src/components/MDX/MDXComponents.tsx index cbb758e2..f92fbc4d 100644 --- a/beta/src/components/MDX/MDXComponents.tsx +++ b/beta/src/components/MDX/MDXComponents.tsx @@ -325,3 +325,10 @@ export const MDXComponents = { Solution, CodeStep, }; + +for (let key in MDXComponents) { + if (MDXComponents.hasOwnProperty(key)) { + const MDXComponent: any = (MDXComponents as any)[key]; + MDXComponent.mdxName = key; + } +} diff --git a/beta/src/components/MDX/PackageImport.tsx b/beta/src/components/MDX/PackageImport.tsx index 83ae74f8..092b319f 100644 --- a/beta/src/components/MDX/PackageImport.tsx +++ b/beta/src/components/MDX/PackageImport.tsx @@ -11,10 +11,10 @@ interface PackageImportProps { export function PackageImport({children}: PackageImportProps) { const terminal = React.Children.toArray(children).filter((child: any) => { - return child.props?.mdxType !== 'pre'; + return child.type?.mdxName !== 'pre'; }); const code = React.Children.toArray(children).map((child: any, i: number) => { - if (child.props?.mdxType === 'pre') { + if (child.type?.mdxName === 'pre') { return ( { return codeSnippets.reduce( (result: Record, codeSnippet: React.ReactElement) => { - if (codeSnippet.props.mdxType !== 'pre') { + if ((codeSnippet.type as any).mdxName !== 'pre') { return result; } const {props} = codeSnippet.props.children; diff --git a/beta/src/pages/[[...markdownPath]].js b/beta/src/pages/[[...markdownPath]].js index 659256bc..07eff0dd 100644 --- a/beta/src/pages/[[...markdownPath]].js +++ b/beta/src/pages/[[...markdownPath]].js @@ -2,15 +2,17 @@ * Copyright (c) Facebook, Inc. and its affiliates. */ -import {createElement, Children, useMemo} from 'react'; +import {createElement, Children, Fragment, useMemo} from 'react'; import {MDXComponents} from 'components/MDX/MDXComponents'; import {MarkdownPage} from 'components/Layout/MarkdownPage'; import {Page} from 'components/Layout/Page'; -import {mdx} from '@mdx-js/react'; import {prepareMDX} from '../utils/prepareMDX'; export default function Layout({content, meta}) { - const decoded = useMemo(() => JSON.parse(content, reviveMDX), [content]); + const decoded = useMemo( + () => JSON.parse(content, reviveNodeOnClient), + [content] + ); const {toc, children} = useMemo( () => prepareMDX(decoded.props.children), [decoded] @@ -24,20 +26,48 @@ export default function Layout({content, meta}) { ); } -// Create a React tree from server JSON. -function reviveMDX(key, val) { - if (val && val.$m) { - // This is an MDX node we need to revive. - let args = val.$m; - if (args[0] == null) { - // First argument to createElement() is a type. - // If it didn't serialize, this is a custom MDX component. - args[0] = MDXComponents[args[1].mdxType]; - if (args[0] == null) { - throw Error('Unknown type: ' + args[1].mdxType); - } +// Deserialize a client React tree from JSON. +function reviveNodeOnClient(key, val) { + if (Array.isArray(val) && val[0] == '$r') { + // Assume it's a React element. + let type = val[1]; + let key = val[2]; + let props = val[3]; + if (type === 'wrapper') { + type = Fragment; + props = {children: props.children}; + } + if (MDXComponents[type]) { + type = MDXComponents[type]; + } + if (!type) { + console.error('Unknown type: ' + type); + type = Fragment; } - return mdx.apply(null, args); + return { + $$typeof: Symbol.for('react.element'), + type: type, + key: key, + ref: null, + props: props, + _owner: null, + }; + } else { + return val; + } +} + +// Serialize a server React tree node to JSON. +function stringifyNodeOnServer(key, val) { + if (val != null && val.$$typeof === Symbol.for('react.element')) { + // Remove fake MDX props. + const {mdxType, originalType, parentName, ...cleanProps} = val.props; + return [ + '$r', + typeof val.type === 'string' ? val.type : mdxType, + val.key, + cleanProps, + ]; } else { return val; } @@ -72,14 +102,11 @@ export async function getStaticProps(context) { // Run it to get JSON for render output const run = new Function('exports', 'mdx', js); let outputExports = {}; - function createJSONNode(...args) { - return {$m: args}; // Marker to turn this into createElement on the client - } - run(outputExports, createJSONNode); - const json = outputExports.default({}); + run(outputExports, createElement); + const reactTree = outputExports.default({}); return { props: { - content: JSON.stringify(json), + content: JSON.stringify(reactTree, stringifyNodeOnServer), meta, }, }; diff --git a/beta/src/utils/prepareMDX.js b/beta/src/utils/prepareMDX.js index 93c89372..efe64c9b 100644 --- a/beta/src/utils/prepareMDX.js +++ b/beta/src/utils/prepareMDX.js @@ -44,7 +44,7 @@ function wrapChildrenInMaxWidthContainers(children) { wrapQueue.push(child); return; } - if (fullWidthTypes.includes(child.props.mdxType)) { + if (fullWidthTypes.includes(child.type.mdxName)) { flushWrapper(key); finalChildren.push(child); } else { @@ -59,22 +59,22 @@ function wrapChildrenInMaxWidthContainers(children) { function getTableOfContents(children) { const anchors = Children.toArray(children) .filter((child) => { - if (child.props?.mdxType) { + if (child.type?.mdxName) { return ['h1', 'h2', 'h3', 'Challenges', 'Recap'].includes( - child.props.mdxType + child.type.mdxName ); } return false; }) .map((child) => { - if (child.props.mdxType === 'Challenges') { + if (child.type.mdxName === 'Challenges') { return { url: '#challenges', depth: 0, text: 'Challenges', }; } - if (child.props.mdxType === 'Recap') { + if (child.type.mdxName === 'Recap') { return { url: '#recap', depth: 0, @@ -84,8 +84,8 @@ function getTableOfContents(children) { return { url: '#' + child.props.id, depth: - (child.props?.mdxType && - parseInt(child.props.mdxType.replace('h', ''), 0)) ?? + (child.type?.mdxName && + parseInt(child.type.mdxName.replace('h', ''), 0)) ?? 0, text: child.props.children, };