Browse Source

[Beta] Don't rely on special MDX props (#4991)

main
dan 2 years ago
committed by GitHub
parent
commit
bc6310fd98
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      beta/package.json
  2. 8
      beta/src/components/Layout/MarkdownPage.tsx
  3. 4
      beta/src/components/MDX/Challenges/Challenges.tsx
  4. 6
      beta/src/components/MDX/CodeDiagram.tsx
  5. 1
      beta/src/components/MDX/Heading.tsx
  6. 2
      beta/src/components/MDX/Link.tsx
  7. 7
      beta/src/components/MDX/MDXComponents.tsx
  8. 4
      beta/src/components/MDX/PackageImport.tsx
  9. 2
      beta/src/components/MDX/Sandpack/createFileMap.ts
  10. 69
      beta/src/pages/[[...markdownPath]].js
  11. 14
      beta/src/utils/prepareMDX.js

1
beta/package.json

@ -26,7 +26,6 @@
"@docsearch/css": "3.0.0-alpha.41", "@docsearch/css": "3.0.0-alpha.41",
"@docsearch/react": "3.0.0-alpha.41", "@docsearch/react": "3.0.0-alpha.41",
"@headlessui/react": "^1.3.0", "@headlessui/react": "^1.3.0",
"@mdx-js/react": "^1.6.16",
"body-scroll-lock": "^3.1.3", "body-scroll-lock": "^3.1.3",
"classnames": "^2.2.6", "classnames": "^2.2.6",
"date-fns": "^2.16.1", "date-fns": "^2.16.1",

8
beta/src/components/Layout/MarkdownPage.tsx

@ -3,8 +3,6 @@
*/ */
import * as React from 'react'; import * as React from 'react';
// @ts-ignore
import {MDXContext} from '@mdx-js/react';
import {DocsPageFooter} from 'components/DocsFooter'; import {DocsPageFooter} from 'components/DocsFooter';
import {MDXComponents} from 'components/MDX/MDXComponents'; import {MDXComponents} from 'components/MDX/MDXComponents';
import {Seo} from 'components/Seo'; import {Seo} from 'components/Seo';
@ -45,11 +43,7 @@ export function MarkdownPage<
/> />
)} )}
<div className="px-5 sm:px-12"> <div className="px-5 sm:px-12">
<div className="max-w-7xl mx-auto"> <div className="max-w-7xl mx-auto">{children}</div>
<MDXContext.Provider value={MDXComponents}>
{children}
</MDXContext.Provider>
</div>
<DocsPageFooter <DocsPageFooter
route={route} route={route}
nextRoute={nextRoute} nextRoute={nextRoute}

4
beta/src/components/MDX/Challenges/Challenges.tsx

@ -37,8 +37,8 @@ const parseChallengeContents = (
let challenge: Partial<ChallengeContents> = {}; let challenge: Partial<ChallengeContents> = {};
let content: React.ReactElement[] = []; let content: React.ReactElement[] = [];
React.Children.forEach(children, (child) => { React.Children.forEach(children, (child) => {
const {props} = child; const {props, type} = child;
switch (props.mdxType) { switch ((type as any).mdxName) {
case 'Solution': { case 'Solution': {
challenge.solution = child; challenge.solution = child;
challenge.content = content; challenge.content = content;

6
beta/src/components/MDX/CodeDiagram.tsx

@ -12,10 +12,10 @@ interface CodeDiagramProps {
export function CodeDiagram({children, flip = false}: CodeDiagramProps) { export function CodeDiagram({children, flip = false}: CodeDiagramProps) {
const illustration = React.Children.toArray(children).filter((child: any) => { 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) => { const content = React.Children.toArray(children).map((child: any) => {
if (child.props?.mdxType === 'pre') { if (child.type?.mdxName === 'pre') {
return ( return (
<CodeBlock <CodeBlock
{...child.props.children.props} {...child.props.children.props}
@ -23,7 +23,7 @@ export function CodeDiagram({children, flip = false}: CodeDiagramProps) {
noMarkers={true} noMarkers={true}
/> />
); );
} else if (child.props?.mdxType === 'img') { } else if (child.type?.mdxName === 'img') {
return null; return null;
} else { } else {
return child; return child;

1
beta/src/components/MDX/Heading.tsx

@ -71,6 +71,7 @@ export const H2 = ({className, ...props}: HeadingProps) => (
{...props} {...props}
/> />
); );
export const H3 = ({className, ...props}: HeadingProps) => ( export const H3 = ({className, ...props}: HeadingProps) => (
<Heading <Heading
as="h3" as="h3"

2
beta/src/components/MDX/Link.tsx

@ -18,7 +18,7 @@ function Link({
'inline text-link dark:text-link-dark break-normal border-b border-link border-opacity-0 hover:border-opacity-100 duration-100 ease-in transition leading-normal'; 'inline text-link dark:text-link-dark break-normal border-b border-link border-opacity-0 hover:border-opacity-100 duration-100 ease-in transition leading-normal';
const modifiedChildren = React.Children.toArray(children).map( const modifiedChildren = React.Children.toArray(children).map(
(child: any) => { (child: any) => {
if (child.props?.mdxType && child.props?.mdxType === 'inlineCode') { if (child.type?.mdxName && child.type?.mdxName === 'inlineCode') {
return React.cloneElement(child, { return React.cloneElement(child, {
isLink: true, isLink: true,
}); });

7
beta/src/components/MDX/MDXComponents.tsx

@ -325,3 +325,10 @@ export const MDXComponents = {
Solution, Solution,
CodeStep, CodeStep,
}; };
for (let key in MDXComponents) {
if (MDXComponents.hasOwnProperty(key)) {
const MDXComponent: any = (MDXComponents as any)[key];
MDXComponent.mdxName = key;
}
}

4
beta/src/components/MDX/PackageImport.tsx

@ -11,10 +11,10 @@ interface PackageImportProps {
export function PackageImport({children}: PackageImportProps) { export function PackageImport({children}: PackageImportProps) {
const terminal = React.Children.toArray(children).filter((child: any) => { 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) => { const code = React.Children.toArray(children).map((child: any, i: number) => {
if (child.props?.mdxType === 'pre') { if (child.type?.mdxName === 'pre') {
return ( return (
<CodeBlock <CodeBlock
{...child.props.children.props} {...child.props.children.props}

2
beta/src/components/MDX/Sandpack/createFileMap.ts

@ -7,7 +7,7 @@ import type {SandpackFile} from '@codesandbox/sandpack-react';
export const createFileMap = (codeSnippets: any) => { export const createFileMap = (codeSnippets: any) => {
return codeSnippets.reduce( return codeSnippets.reduce(
(result: Record<string, SandpackFile>, codeSnippet: React.ReactElement) => { (result: Record<string, SandpackFile>, codeSnippet: React.ReactElement) => {
if (codeSnippet.props.mdxType !== 'pre') { if ((codeSnippet.type as any).mdxName !== 'pre') {
return result; return result;
} }
const {props} = codeSnippet.props.children; const {props} = codeSnippet.props.children;

69
beta/src/pages/[[...markdownPath]].js

@ -2,15 +2,17 @@
* Copyright (c) Facebook, Inc. and its affiliates. * 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 {MDXComponents} from 'components/MDX/MDXComponents';
import {MarkdownPage} from 'components/Layout/MarkdownPage'; import {MarkdownPage} from 'components/Layout/MarkdownPage';
import {Page} from 'components/Layout/Page'; import {Page} from 'components/Layout/Page';
import {mdx} from '@mdx-js/react';
import {prepareMDX} from '../utils/prepareMDX'; import {prepareMDX} from '../utils/prepareMDX';
export default function Layout({content, meta}) { 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( const {toc, children} = useMemo(
() => prepareMDX(decoded.props.children), () => prepareMDX(decoded.props.children),
[decoded] [decoded]
@ -24,20 +26,48 @@ export default function Layout({content, meta}) {
); );
} }
// Create a React tree from server JSON. // Deserialize a client React tree from JSON.
function reviveMDX(key, val) { function reviveNodeOnClient(key, val) {
if (val && val.$m) { if (Array.isArray(val) && val[0] == '$r') {
// This is an MDX node we need to revive. // Assume it's a React element.
let args = val.$m; let type = val[1];
if (args[0] == null) { let key = val[2];
// First argument to createElement() is a type. let props = val[3];
// If it didn't serialize, this is a custom MDX component. if (type === 'wrapper') {
args[0] = MDXComponents[args[1].mdxType]; type = Fragment;
if (args[0] == null) { props = {children: props.children};
throw Error('Unknown type: ' + args[1].mdxType); }
if (MDXComponents[type]) {
type = MDXComponents[type];
}
if (!type) {
console.error('Unknown type: ' + type);
type = Fragment;
}
return {
$$typeof: Symbol.for('react.element'),
type: type,
key: key,
ref: null,
props: props,
_owner: null,
};
} else {
return val;
} }
} }
return mdx.apply(null, args);
// 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 { } else {
return val; return val;
} }
@ -72,14 +102,11 @@ export async function getStaticProps(context) {
// Run it to get JSON for render output // Run it to get JSON for render output
const run = new Function('exports', 'mdx', js); const run = new Function('exports', 'mdx', js);
let outputExports = {}; let outputExports = {};
function createJSONNode(...args) { run(outputExports, createElement);
return {$m: args}; // Marker to turn this into createElement on the client const reactTree = outputExports.default({});
}
run(outputExports, createJSONNode);
const json = outputExports.default({});
return { return {
props: { props: {
content: JSON.stringify(json), content: JSON.stringify(reactTree, stringifyNodeOnServer),
meta, meta,
}, },
}; };

14
beta/src/utils/prepareMDX.js

@ -44,7 +44,7 @@ function wrapChildrenInMaxWidthContainers(children) {
wrapQueue.push(child); wrapQueue.push(child);
return; return;
} }
if (fullWidthTypes.includes(child.props.mdxType)) { if (fullWidthTypes.includes(child.type.mdxName)) {
flushWrapper(key); flushWrapper(key);
finalChildren.push(child); finalChildren.push(child);
} else { } else {
@ -59,22 +59,22 @@ function wrapChildrenInMaxWidthContainers(children) {
function getTableOfContents(children) { function getTableOfContents(children) {
const anchors = Children.toArray(children) const anchors = Children.toArray(children)
.filter((child) => { .filter((child) => {
if (child.props?.mdxType) { if (child.type?.mdxName) {
return ['h1', 'h2', 'h3', 'Challenges', 'Recap'].includes( return ['h1', 'h2', 'h3', 'Challenges', 'Recap'].includes(
child.props.mdxType child.type.mdxName
); );
} }
return false; return false;
}) })
.map((child) => { .map((child) => {
if (child.props.mdxType === 'Challenges') { if (child.type.mdxName === 'Challenges') {
return { return {
url: '#challenges', url: '#challenges',
depth: 0, depth: 0,
text: 'Challenges', text: 'Challenges',
}; };
} }
if (child.props.mdxType === 'Recap') { if (child.type.mdxName === 'Recap') {
return { return {
url: '#recap', url: '#recap',
depth: 0, depth: 0,
@ -84,8 +84,8 @@ function getTableOfContents(children) {
return { return {
url: '#' + child.props.id, url: '#' + child.props.id,
depth: depth:
(child.props?.mdxType && (child.type?.mdxName &&
parseInt(child.props.mdxType.replace('h', ''), 0)) ?? parseInt(child.type.mdxName.replace('h', ''), 0)) ??
0, 0,
text: child.props.children, text: child.props.children,
}; };

Loading…
Cancel
Save