diff --git a/beta/src/pages/[[...markdownPath]].js b/beta/src/pages/[[...markdownPath]].js
index 07eff0dd..1c8ad6fe 100644
--- a/beta/src/pages/[[...markdownPath]].js
+++ b/beta/src/pages/[[...markdownPath]].js
@@ -2,25 +2,21 @@
* Copyright (c) Facebook, Inc. and its affiliates.
*/
-import {createElement, Children, Fragment, useMemo} from 'react';
+import {Fragment, useMemo} from 'react';
import {MDXComponents} from 'components/MDX/MDXComponents';
import {MarkdownPage} from 'components/Layout/MarkdownPage';
import {Page} from 'components/Layout/Page';
-import {prepareMDX} from '../utils/prepareMDX';
-export default function Layout({content, meta}) {
- const decoded = useMemo(
+export default function Layout({content, toc, meta}) {
+ const parsedContent = useMemo(
() => JSON.parse(content, reviveNodeOnClient),
[content]
);
- const {toc, children} = useMemo(
- () => prepareMDX(decoded.props.children),
- [decoded]
- );
+ const parsedToc = useMemo(() => JSON.parse(toc, reviveNodeOnClient), [toc]);
return (
-
- {children}
+
+ {parsedContent}
);
@@ -82,7 +78,7 @@ export async function getStaticProps(context) {
const {remarkPlugins} = require('../../plugins/markdownToHtml');
const rootDir = process.cwd() + '/src/content/';
- // Read MDX and make JS out of it
+ // Read MDX from the file. Parse Frontmatter data out of it.
let path = (context.params.markdownPath || []).join('/') || 'index';
let mdxWithFrontmatter;
try {
@@ -91,22 +87,43 @@ export async function getStaticProps(context) {
mdxWithFrontmatter = fs.readFileSync(rootDir + path + '/index.md', 'utf8');
}
const {content: mdx, data: meta} = fm(mdxWithFrontmatter);
- const jsx = await compileMdx(mdx, {
+
+ // Turn the MDX we just read into some JS we can execute.
+ let mdxWithFakeImports = '';
+ for (let key in MDXComponents) {
+ if (MDXComponents.hasOwnProperty(key)) {
+ // If we don't add these fake imports, the MDX compiler
+ // will insert a bunch of opaque components we can't introspect.
+ // This will break the prepareMDX() call below.
+ mdxWithFakeImports += 'import ' + key + ' from "' + key + '";\n';
+ }
+ }
+ mdxWithFakeImports += '\n' + mdx;
+ const jsxCode = await compileMdx(mdxWithFakeImports, {
remarkPlugins,
});
- const js = transform(jsx, {
+ const jsCode = transform(jsxCode, {
plugins: ['@babel/plugin-transform-modules-commonjs'],
presets: ['@babel/preset-react'],
}).code;
- // Run it to get JSON for render output
- const run = new Function('exports', 'mdx', js);
- let outputExports = {};
- run(outputExports, createElement);
- const reactTree = outputExports.default({});
+ // Prepare environment for MDX and then eval it.
+ let fakeExports = {};
+ // For each fake MDX import, give back the string component name.
+ // It will get serialized later.
+ const fakeRequire = (key) => key;
+ const evalJSCode = new Function('require', 'exports', 'mdx', jsCode);
+ const createElement = require('react').createElement;
+ evalJSCode(fakeRequire, fakeExports, createElement);
+ const reactTree = fakeExports.default({});
+
+ // Pre-process MDX output and serialize it.
+ const {prepareMDX} = require('../utils/prepareMDX');
+ const {toc, children} = prepareMDX(reactTree.props.children);
return {
props: {
- content: JSON.stringify(reactTree, stringifyNodeOnServer),
+ content: JSON.stringify(children, stringifyNodeOnServer),
+ toc: JSON.stringify(toc, stringifyNodeOnServer),
meta,
},
};
diff --git a/beta/src/utils/prepareMDX.js b/beta/src/utils/prepareMDX.js
index efe64c9b..296b66af 100644
--- a/beta/src/utils/prepareMDX.js
+++ b/beta/src/utils/prepareMDX.js
@@ -7,7 +7,7 @@ import {MDXComponents} from 'components/MDX/MDXComponents';
const {MaxWidth} = MDXComponents;
-// TODO: This logic should be in MDX plugins instead.
+// TODO: This logic could be in MDX plugins instead.
export function prepareMDX(rawChildren) {
const toc = getTableOfContents(rawChildren);
@@ -32,7 +32,8 @@ function wrapChildrenInMaxWidthContainers(children) {
let finalChildren = [];
function flushWrapper(key) {
if (wrapQueue.length > 0) {
- finalChildren.push({wrapQueue});
+ const Wrapper = 'MaxWidth';
+ finalChildren.push({wrapQueue});
wrapQueue = [];
}
}
@@ -44,7 +45,7 @@ function wrapChildrenInMaxWidthContainers(children) {
wrapQueue.push(child);
return;
}
- if (fullWidthTypes.includes(child.type.mdxName)) {
+ if (fullWidthTypes.includes(child.type)) {
flushWrapper(key);
finalChildren.push(child);
} else {
@@ -59,22 +60,20 @@ function wrapChildrenInMaxWidthContainers(children) {
function getTableOfContents(children) {
const anchors = Children.toArray(children)
.filter((child) => {
- if (child.type?.mdxName) {
- return ['h1', 'h2', 'h3', 'Challenges', 'Recap'].includes(
- child.type.mdxName
- );
+ if (child.type) {
+ return ['h1', 'h2', 'h3', 'Challenges', 'Recap'].includes(child.type);
}
return false;
})
.map((child) => {
- if (child.type.mdxName === 'Challenges') {
+ if (child.type === 'Challenges') {
return {
url: '#challenges',
depth: 0,
text: 'Challenges',
};
}
- if (child.type.mdxName === 'Recap') {
+ if (child.type === 'Recap') {
return {
url: '#recap',
depth: 0,
@@ -83,10 +82,7 @@ function getTableOfContents(children) {
}
return {
url: '#' + child.props.id,
- depth:
- (child.type?.mdxName &&
- parseInt(child.type.mdxName.replace('h', ''), 0)) ??
- 0,
+ depth: (child.type && parseInt(child.type.replace('h', ''), 0)) ?? 0,
text: child.props.children,
};
});