From a4f192e090c53c8b7f76d2b8451f16597b2ae287 Mon Sep 17 00:00:00 2001 From: joostdecock Date: Sat, 20 May 2023 13:22:36 +0200 Subject: [PATCH] fix(dev): Load MDX dynamically to reduce the number of routes Prior to this commit we'd generate a page for each MDX document as that avoids having to load MDX dynamically (which can be tricky) or through static props (which causes issues with serialization). However, Vercel (which hosts for us) has an upper limit on the number of routes, and because of this extensive documentation, we blew passed it with this approach. This changes to a dynamic resolution of MDX content with an async import in the useEffect hook. This should drastically reduce the number of routes and make Vercel happy. I didn't do much digging into the effects of this on SSR. If it turns out it's causes issues, we'll deal with it at that time. --- .gitignore | 8 -- sites/dev/pages/[...slug].mjs | 105 ++++++++++++++++++++++ sites/shared/components/wrappers/mdx.mjs | 4 +- sites/shared/components/wrappers/page.mjs | 19 ++-- 4 files changed, 117 insertions(+), 19 deletions(-) create mode 100644 sites/dev/pages/[...slug].mjs diff --git a/.gitignore b/.gitignore index 5f52ff744c5..ff730ab991a 100644 --- a/.gitignore +++ b/.gitignore @@ -42,14 +42,6 @@ sites/lab/pages # but not the indexes !sites/lab/pages/*/index.js -# dev auto-generated pages -sites/dev/pages/* -# But not these -!sites/dev/pages/404.mjs -!sites/dev/pages/_app.mjs -!sites/dev/pages/contact.mjs -!sites/dev/pages/index.mjs - # org auto-generated pages sites/org/pages/docs/* sites/org/pages/new/pattern/* diff --git a/sites/dev/pages/[...slug].mjs b/sites/dev/pages/[...slug].mjs new file mode 100644 index 00000000000..066ed182b9f --- /dev/null +++ b/sites/dev/pages/[...slug].mjs @@ -0,0 +1,105 @@ +// Used in static paths +import mdxMeta from 'site/prebuild/mdx.en.js' +// Dependencies +import { serverSideTranslations } from 'next-i18next/serverSideTranslations' +// Hooks +import { useState, useEffect } from 'react' +// Components +import Head from 'next/head' +import { PageWrapper, ns } from 'shared/components/wrappers/page.mjs' +import { Spinner } from 'shared/components/spinner.mjs' +import { components } from 'shared/components/mdx/index.mjs' +import { MdxWrapper } from 'shared/components/wrappers/mdx.mjs' +//import { TocWrapper } from 'shared/components/wrappers/toc.mjs' + +/* + * This page is auto-generated by the prebuild script. + * Any changes you make will be overwritten on the next (pre)build. + * + * See the page-templates folder for more info. + */ +const DocsPage = ({ page, slug }) => { + const [frontmatter, setFrontmatter] = useState({ title: 'FreeSewing.dev' }) + const [MDX, setMDX] = useState() + + /* Load MDX dynamically */ + useEffect(() => { + const loadMDX = async () => { + import(`../../../markdown/dev/${slug}/en.md`).then((mod) => { + setFrontmatter(mod.frontmatter) + const Component = mod.default + setMDX() + }) + } + loadMDX() + }, [slug]) + + return ( + + + + + + + + + + + + + + {frontmatter.title} - FreeSewing.dev + +
+ {false && frontmatter.toc && ( +
+ {/* FIXME: Implement toc plugin to add it to the frontmatter */} + {/* */} +
+ )} + {MDX} +
+
+ ) +} + +export default DocsPage + +/* + * getStaticProps() is used to fetch data at build-time. + * To learn more, see: https://nextjs.org/docs/basic-features/data-fetching + */ +export async function getStaticProps({ params }) { + return { + props: { + ...(await serverSideTranslations('en', ['docs', ...ns])), + slug: params.slug.join('/'), + page: { + locale: 'en', + path: params.slug, + }, + }, + } +} + +/* + * getStaticPaths() is used to specify for which routes (think URLs) + * this page should be used to generate the result. + * + * On this page, it is returning a list of routes (think URLs) for all + * the mdx (markdown) content. + * That list comes from mdxMeta, which is build in the prebuild step + * and contains paths, titles, and intro for all markdown. + * + * To learn more, see: https://nextjs.org/docs/basic-features/data-fetching + */ +export async function getStaticPaths() { + return { + paths: Object.keys(mdxMeta).map((slug) => '/' + slug), + fallback: false, + } +} diff --git a/sites/shared/components/wrappers/mdx.mjs b/sites/shared/components/wrappers/mdx.mjs index 25dbc7c9e2e..f5948fad301 100644 --- a/sites/shared/components/wrappers/mdx.mjs +++ b/sites/shared/components/wrappers/mdx.mjs @@ -101,7 +101,7 @@ const MetaData = ({ authors = [], maintainers = [], updated = '20220825', locale ) -export const MdxWrapper = ({ MDX, frontmatter = {}, components = {} }) => { +export const MdxWrapper = ({ MDX = false, frontmatter = {}, components = {}, children = [] }) => { const { t } = useTranslation('docs') const allComponents = { ...baseComponents, ...components } const { locale, slug } = useContext(NavigationContext) @@ -116,7 +116,7 @@ export const MdxWrapper = ({ MDX, frontmatter = {}, components = {} }) => { lastUpdated={updates.lastUpdates} {...{ locale, slug, t }} /> - + {MDX ? : children} ) } diff --git a/sites/shared/components/wrappers/page.mjs b/sites/shared/components/wrappers/page.mjs index 9c6b9f04ab5..3643f46cb88 100644 --- a/sites/shared/components/wrappers/page.mjs +++ b/sites/shared/components/wrappers/page.mjs @@ -29,6 +29,12 @@ export const PageWrapper = (props) => { // Title is typically set in props.t but check props.title too const pageTitle = props.t ? props.t : props.title ? props.title : null + /* + * Contexts + */ + const { modalContent } = useContext(ModalContext) + const { setNavigation, slug } = useContext(NavigationContext) + /* * This forces a re-render upon initial bootstrap of the app * This is needed to avoid hydration errors because theme can't be set reliably in SSR @@ -38,25 +44,20 @@ export const PageWrapper = (props) => { const [navupdates, setNavupdates] = useState(0) useEffect(() => setCurrentTheme(theme), [currentTheme, theme]) - /* - * Contexts - */ - const { modalContent } = useContext(ModalContext) - const { setNavigation } = useContext(NavigationContext) - /* * Update navigation context with title and path */ useEffect(() => { - if (navupdates < 5) { + // Only update if a new page was loaded + if (path.join('/') !== 'slug') { setNavigation({ title: pageTitle, locale, path, }) setNavupdates(navupdates + 1) - } else console.warn('Suppressing navigation update in page wrapper to avoid render loop') - }, [path, pageTitle]) + } + }, [path, pageTitle, slug]) /* * Hotkeys (keyboard actions)