diff --git a/docusaurus.config.js b/docusaurus.config.js index ee4e113b6a2..39135d06150 100755 --- a/docusaurus.config.js +++ b/docusaurus.config.js @@ -1,5 +1,6 @@ import path from 'path'; import remarkNpm2Yarn from '@docusaurus/remark-plugin-npm2yarn'; +import rehypeCodeblockMeta from './src/plugins/rehype-codeblock-meta.mjs'; export default { title: 'React Navigation', @@ -146,6 +147,9 @@ export default { includeCurrentVersion: false, lastVersion: '6.x', remarkPlugins: [[remarkNpm2Yarn, { sync: true }]], + rehypePlugins: [ + [rehypeCodeblockMeta, { match: { snack: true } }], + ], }, blog: { remarkPlugins: [[remarkNpm2Yarn, { sync: true }]], diff --git a/src/components/Pre.js b/src/components/Pre.js new file mode 100644 index 00000000000..8c39db7f679 --- /dev/null +++ b/src/components/Pre.js @@ -0,0 +1,219 @@ +import { useColorMode } from '@docusaurus/theme-common'; +import MDXPre from '@theme-original/MDXComponents/Pre'; +import CodeBlock from '@theme-original/CodeBlock'; +import React from 'react'; + +const peers = { + 'react-native-safe-area-context': '*', + 'react-native-screens': '*', +}; + +const versions = { + 7: { + '@react-navigation/bottom-tabs': ['7.0.0-alpha.7', peers], + '@react-navigation/core': '7.0.0-alpha.6', + '@react-navigation/native': '7.0.0-alpha.6', + '@react-navigation/drawer': [ + '7.0.0-alpha.7', + { + ...peers, + 'react-native-reanimated': '*', + }, + ], + '@react-navigation/elements': ['2.0.0-alpha.4', peers], + '@react-navigation/material-top-tabs': [ + '7.0.0-alpha.6', + { + ...peers, + 'react-native-pager-view': '*', + }, + ], + '@react-navigation/native-stack': ['7.0.0-alpha.7', peers], + '@react-navigation/routers': '7.0.0-alpha.4', + '@react-navigation/stack': [ + '7.0.0-alpha.7', + { + ...peers, + 'react-native-gesture-handler': '*', + }, + ], + 'react-native-drawer-layout': [ + '4.0.0-alpha.3', + { + 'react-native-gesture-handler': '*', + 'react-native-reanimated': '*', + }, + ], + 'react-native-tab-view': [ + '4.0.0-alpha.2', + { + 'react-native-pager-view': '*', + }, + ], + }, +}; + +export default function Pre({ + children, + 'data-name': name, + 'data-snack': snack, + 'data-version': version, + 'data-dependencies': deps, + ...rest +}) { + const { colorMode } = useColorMode(); + + if (snack) { + const code = React.Children.only(children).props.children; + + if (typeof code !== 'string') { + throw new Error( + 'Playground code must be a string, but received ' + typeof code + ); + } + + const dependencies = deps + ? Object.fromEntries(deps.split(',').map((entry) => entry.split('@'))) + : {}; + + Object.assign( + dependencies, + Object.entries(versions[version]).reduce((acc, [key, value]) => { + if (code.includes(`from '${key}'`)) { + if (Array.isArray(value)) { + const [version, peers] = value; + + Object.assign(acc, { + [key]: version, + ...peers, + }); + } else { + acc[key] = value; + } + } + + return acc; + }, {}) + ); + + // FIXME: use staging for now since react-navigation fails to build on prod + const url = new URL('https://staging-snack.expo.dev'); + + if (name) { + url.searchParams.set('name', name); + } + + url.searchParams.set( + 'code', + // Remove highlight and codeblock focus comments from code + code + .split('\n') + .filter((line) => + [ + '// highlight-start', + '// highlight-end', + '// highlight-next-line', + '// codeblock-focus-start', + '// codeblock-focus-end', + ].every((comment) => line.trim() !== comment) + ) + .join('\n') + ); + + url.searchParams.set( + 'dependencies', + Object.entries(dependencies) + .map(([key, value]) => `${key}@${value}`) + .join(',') + ); + + url.searchParams.set('platform', 'web'); + url.searchParams.set('supportedPlatforms', 'ios,android,web'); + url.searchParams.set('preview', 'true'); + url.searchParams.set('hideQueryParams', 'true'); + + if (snack === 'embed') { + url.searchParams.set('theme', colorMode === 'dark' ? 'dark' : 'light'); + url.pathname = 'embedded'; + + return ( +